Coverage Report

Created: 2026-02-14 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/smbd/sec_ctx.c
Line
Count
Source
1
/* 
2
   Unix SMB/CIFS implementation.
3
   uid/user handling
4
   Copyright (C) Tim Potter 2000
5
6
   This program is free software; you can redistribute it and/or modify
7
   it under the terms of the GNU General Public License as published by
8
   the Free Software Foundation; either version 3 of the License, or
9
   (at your option) any later version.
10
11
   This program is distributed in the hope that it will be useful,
12
   but WITHOUT ANY WARRANTY; without even the implied warranty of
13
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
   GNU General Public License for more details.
15
16
   You should have received a copy of the GNU General Public License
17
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
*/
19
20
#include "includes.h"
21
#include "system/passwd.h"
22
#include "smbd/smbd.h"
23
#include "smbd/globals.h"
24
#include "libcli/security/security_token.h"
25
#include "auth.h"
26
#include "smbprofile.h"
27
#include "../lib/util/setid.h"
28
29
extern struct current_user current_user;
30
31
/****************************************************************************
32
 Are two UNIX tokens equal ?
33
****************************************************************************/
34
35
bool unix_token_equal(const struct security_unix_token *t1, const struct security_unix_token *t2)
36
0
{
37
0
  if (t1->uid != t2->uid || t1->gid != t2->gid ||
38
0
      t1->ngroups != t2->ngroups) {
39
0
    return false;
40
0
  }
41
0
  if (memcmp(t1->groups, t2->groups,
42
0
      t1->ngroups*sizeof(gid_t)) != 0) {
43
0
    return false;
44
0
  }
45
0
  return true;
46
0
}
47
48
/****************************************************************************
49
 Become the specified uid.
50
****************************************************************************/
51
52
static bool become_uid(uid_t uid)
53
0
{
54
  /* Check for dodgy uid values */
55
56
0
  if (uid == (uid_t)-1 || 
57
0
      ((sizeof(uid_t) == 2) && (uid == (uid_t)65535))) {
58
0
    if (!become_uid_done) {
59
0
      DEBUG(1,("WARNING: using uid %d is a security risk\n",
60
0
         (int)uid));
61
0
      become_uid_done = true;
62
0
    }
63
0
  }
64
65
  /* Set effective user id */
66
67
0
  set_effective_uid(uid);
68
69
0
  return True;
70
0
}
71
72
/****************************************************************************
73
 Become the specified gid.
74
****************************************************************************/
75
76
static bool become_gid(gid_t gid)
77
0
{
78
  /* Check for dodgy gid values */
79
80
0
  if (gid == (gid_t)-1 || ((sizeof(gid_t) == 2) && 
81
0
         (gid == (gid_t)65535))) {
82
0
    if (!become_gid_done) {
83
0
      DEBUG(1,("WARNING: using gid %d is a security risk\n",
84
0
         (int)gid));  
85
0
      become_gid_done = true;
86
0
    }
87
0
  }
88
89
  /* Set effective group id */
90
91
0
  set_effective_gid(gid);
92
0
  return True;
93
0
}
94
95
/****************************************************************************
96
 Drop back to root privileges in order to change to another user.
97
****************************************************************************/
98
99
static void gain_root(void)
100
0
{
101
0
  if (non_root_mode()) {
102
0
    return;
103
0
  }
104
105
0
  if (geteuid() != 0) {
106
0
    set_effective_uid(0);
107
108
0
    if (geteuid() != 0) {
109
0
      DEBUG(0,
110
0
            ("Warning: You appear to have a trapdoor "
111
0
             "uid system\n"));
112
0
    }
113
0
  }
114
115
0
  if (getegid() != 0) {
116
0
    set_effective_gid(0);
117
118
0
    if (getegid() != 0) {
119
0
      DEBUG(0,
120
0
            ("Warning: You appear to have a trapdoor "
121
0
             "gid system\n"));
122
0
    }
123
0
  }
124
0
}
125
126
/****************************************************************************
127
 Get the list of current groups.
128
****************************************************************************/
129
130
static int get_current_groups(gid_t gid, uint32_t *p_ngroups, gid_t **p_groups)
131
0
{
132
0
  int i;
133
0
  int ngroups;
134
0
  gid_t *groups = NULL;
135
136
0
  (*p_ngroups) = 0;
137
0
  (*p_groups) = NULL;
138
139
  /* this looks a little strange, but is needed to cope with
140
     systems that put the current egid in the group list
141
     returned from getgroups() (tridge) */
142
0
  save_re_gid();
143
0
  set_effective_gid(gid);
144
0
  samba_setgid(gid);
145
146
0
  ngroups = sys_getgroups(0, NULL);
147
0
  if (ngroups <= 0) {
148
0
    goto fail;
149
0
  }
150
151
0
  if((groups = SMB_MALLOC_ARRAY(gid_t, ngroups+1)) == NULL) {
152
0
    DEBUG(0,("setup_groups malloc fail !\n"));
153
0
    goto fail;
154
0
  }
155
156
0
  if ((ngroups = sys_getgroups(ngroups,groups)) == -1) {
157
0
    goto fail;
158
0
  }
159
160
0
  restore_re_gid();
161
162
0
  (*p_ngroups) = ngroups;
163
0
  (*p_groups) = groups;
164
165
0
  DEBUG( 4, ( "get_current_groups: user is in %u groups: ", ngroups));
166
0
  for (i = 0; i < ngroups; i++ ) {
167
0
    DEBUG( 4, ( "%s%d", (i ? ", " : ""), (int)groups[i] ) );
168
0
  }
169
0
  DEBUG( 4, ( "\n" ) );
170
171
0
  return ngroups;
172
173
0
fail:
174
0
  SAFE_FREE(groups);
175
0
  restore_re_gid();
176
0
  return -1;
177
0
}
178
179
/****************************************************************************
180
 Create a new security context on the stack.  It is the same as the old
181
 one.  User changes are done using the set_sec_ctx() function.
182
****************************************************************************/
183
184
bool push_sec_ctx(void)
185
0
{
186
0
  struct sec_ctx *ctx_p;
187
188
0
  START_PROFILE(push_sec_ctx);
189
190
  /* Check we don't overflow our stack */
191
192
0
  if (sec_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
193
0
    DEBUG(0, ("Security context stack overflow!\n"));
194
0
    smb_panic("Security context stack overflow!");
195
0
  }
196
197
  /* Store previous user context */
198
199
0
  sec_ctx_stack_ndx++;
200
201
0
  ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
202
203
0
  ctx_p->ut.uid = geteuid();
204
0
  ctx_p->ut.gid = getegid();
205
206
0
  DEBUG(4, ("push_sec_ctx(%u, %u) : sec_ctx_stack_ndx = %d\n", 
207
0
      (unsigned int)ctx_p->ut.uid, (unsigned int)ctx_p->ut.gid, sec_ctx_stack_ndx ));
208
209
0
  ctx_p->token = security_token_duplicate(NULL,
210
0
            sec_ctx_stack[sec_ctx_stack_ndx-1].token);
211
212
0
  ctx_p->ut.ngroups = sys_getgroups(0, NULL);
213
214
0
  if (ctx_p->ut.ngroups != 0) {
215
0
    if (!(ctx_p->ut.groups = SMB_MALLOC_ARRAY(gid_t, ctx_p->ut.ngroups))) {
216
0
      DEBUG(0, ("Out of memory in push_sec_ctx()\n"));
217
0
      TALLOC_FREE(ctx_p->token);
218
0
      return False;
219
0
    }
220
221
0
    sys_getgroups(ctx_p->ut.ngroups, ctx_p->ut.groups);
222
0
  } else {
223
0
    ctx_p->ut.groups = NULL;
224
0
  }
225
226
0
  END_PROFILE(push_sec_ctx);
227
228
0
  return True;
229
0
}
230
231
#ifndef HAVE_DARWIN_INITGROUPS
232
/****************************************************************************
233
 Become the specified uid and gid.
234
****************************************************************************/
235
236
static bool become_id(uid_t uid, gid_t gid)
237
0
{
238
0
  return become_gid(gid) && become_uid(uid);
239
0
}
240
241
/****************************************************************************
242
 Change UNIX security context. Calls panic if not successful so no return value.
243
****************************************************************************/
244
/* Normal credential switch path. */
245
246
static void set_unix_security_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups)
247
0
{
248
  /* Start context switch */
249
0
  gain_root();
250
0
#ifdef HAVE_SETGROUPS
251
0
  if (sys_setgroups(gid, ngroups, groups) != 0 && !non_root_mode()) {
252
0
    smb_panic("sys_setgroups failed");
253
0
  }
254
0
#endif
255
0
  become_id(uid, gid);
256
  /* end context switch */
257
0
}
258
259
#else /* HAVE_DARWIN_INITGROUPS */
260
261
/* The Darwin groups implementation is a little unusual. The list of
262
* groups in the kernel credential is not exhaustive, but more like
263
* a cache. The full group list is held in userspace and checked
264
* dynamically.
265
*
266
* This is an optional mechanism, and setgroups(2) opts out
267
* of it. That is, if you call setgroups, then the list of groups you
268
* set are the only groups that are ever checked. This is not what we
269
* want. We want to opt in to the dynamic resolution mechanism, so we
270
* need to specify the uid of the user whose group list (cache) we are
271
* setting.
272
*
273
* The Darwin rules are:
274
*  1. Thou shalt setegid, initgroups and seteuid IN THAT ORDER
275
*  2. Thou shalt not pass more that NGROUPS_MAX to initgroups
276
*  3. Thou shalt leave the first entry in the groups list well alone
277
*/
278
279
#include <sys/syscall.h>
280
281
static void set_unix_security_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups)
282
{
283
  int max = NGROUPS_MAX;
284
285
  /* Start context switch */
286
  gain_root();
287
288
  become_gid(gid);
289
290
291
  if (syscall(SYS_initgroups, (ngroups > max) ? max : ngroups,
292
      groups, uid) == -1 && !non_root_mode()) {
293
    DEBUG(0, ("WARNING: failed to set group list "
294
      "(%d groups) for UID %d: %s\n",
295
      ngroups, uid, strerror(errno)));
296
    smb_panic("sys_setgroups failed");
297
  }
298
299
  become_uid(uid);
300
  /* end context switch */
301
}
302
303
#endif /* HAVE_DARWIN_INITGROUPS */
304
305
/****************************************************************************
306
 Set the current security context to a given user.
307
****************************************************************************/
308
309
static void set_sec_ctx_internal(uid_t uid, gid_t gid,
310
         int ngroups, gid_t *groups,
311
         const struct security_token *token)
312
0
{
313
0
  struct sec_ctx *ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
314
315
  /* Set the security context */
316
317
0
  DEBUG(4, ("setting sec ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", 
318
0
    (unsigned int)uid, (unsigned int)gid, sec_ctx_stack_ndx));
319
320
0
  security_token_debug(DBGC_CLASS, 5, token);
321
0
  debug_unix_user_token(DBGC_CLASS, 5, uid, gid, ngroups, groups);
322
323
  /* Change uid, gid and supplementary group list. */
324
0
  set_unix_security_ctx(uid, gid, ngroups, groups);
325
326
0
  ctx_p->ut.ngroups = ngroups;
327
328
0
  SAFE_FREE(ctx_p->ut.groups);
329
0
  if (token && (token == ctx_p->token)) {
330
0
    smb_panic("DUPLICATE_TOKEN");
331
0
  }
332
333
0
  TALLOC_FREE(ctx_p->token);
334
335
0
  if (ngroups) {
336
0
    ctx_p->ut.groups = (gid_t *)smb_xmemdup(groups,
337
0
                    sizeof(gid_t) * ngroups);
338
0
  } else {
339
0
    ctx_p->ut.groups = NULL;
340
0
  }
341
342
0
  if (token) {
343
0
    ctx_p->token = security_token_duplicate(NULL, token);
344
0
    if (!ctx_p->token) {
345
0
      smb_panic("security_token_duplicate failed");
346
0
    }
347
0
  } else {
348
0
    ctx_p->token = NULL;
349
0
  }
350
351
0
  ctx_p->ut.uid = uid;
352
0
  ctx_p->ut.gid = gid;
353
354
  /* Update current_user stuff */
355
356
0
  current_user.ut.uid = uid;
357
0
  current_user.ut.gid = gid;
358
0
  current_user.ut.ngroups = ngroups;
359
0
  current_user.ut.groups = groups;
360
0
  current_user.nt_user_token = ctx_p->token;
361
362
  /*
363
   * Delete any ChDir cache. We can't assume
364
   * the new uid has access to current working
365
   * directory.
366
   * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14682
367
   */
368
0
  SAFE_FREE(LastDir);
369
0
}
370
371
void set_sec_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups, const struct security_token *token)
372
0
{
373
0
  START_PROFILE(set_sec_ctx);
374
0
  set_sec_ctx_internal(uid, gid, ngroups, groups, token);
375
0
  END_PROFILE(set_sec_ctx);
376
0
}
377
378
/****************************************************************************
379
 Become root context.
380
****************************************************************************/
381
382
void set_root_sec_ctx(void)
383
0
{
384
  /* May need to worry about supplementary groups at some stage */
385
386
0
  START_PROFILE(set_root_sec_ctx);
387
0
  set_sec_ctx_internal(0, 0, 0, NULL, NULL);
388
0
  END_PROFILE(set_root_sec_ctx);
389
0
}
390
391
/****************************************************************************
392
 Pop a security context from the stack.
393
****************************************************************************/
394
395
bool pop_sec_ctx(void)
396
0
{
397
0
  struct sec_ctx *ctx_p;
398
0
  struct sec_ctx *prev_ctx_p;
399
400
0
  START_PROFILE(pop_sec_ctx);
401
402
  /* Check for stack underflow */
403
404
0
  if (sec_ctx_stack_ndx == 0) {
405
0
    DEBUG(0, ("Security context stack underflow!\n"));
406
0
    smb_panic("Security context stack underflow!");
407
0
  }
408
409
0
  ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
410
411
  /* Clear previous user info */
412
413
0
  ctx_p->ut.uid = (uid_t)-1;
414
0
  ctx_p->ut.gid = (gid_t)-1;
415
416
0
  SAFE_FREE(ctx_p->ut.groups);
417
0
  ctx_p->ut.ngroups = 0;
418
419
0
  TALLOC_FREE(ctx_p->token);
420
421
  /* Pop back previous user */
422
423
0
  sec_ctx_stack_ndx--;
424
425
0
  prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
426
427
  /* Change uid, gid and supplementary group list. */
428
0
  set_unix_security_ctx(prev_ctx_p->ut.uid,
429
0
      prev_ctx_p->ut.gid,
430
0
      prev_ctx_p->ut.ngroups,
431
0
      prev_ctx_p->ut.groups);
432
433
  /* Update current_user stuff */
434
435
0
  current_user.ut.uid = prev_ctx_p->ut.uid;
436
0
  current_user.ut.gid = prev_ctx_p->ut.gid;
437
0
  current_user.ut.ngroups = prev_ctx_p->ut.ngroups;
438
0
  current_user.ut.groups = prev_ctx_p->ut.groups;
439
0
  current_user.nt_user_token = prev_ctx_p->token;
440
441
0
  END_PROFILE(pop_sec_ctx);
442
443
0
  DEBUG(4, ("pop_sec_ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", 
444
0
    (unsigned int)geteuid(), (unsigned int)getegid(), sec_ctx_stack_ndx));
445
446
0
  return True;
447
0
}
448
449
/* Initialise the security context system */
450
451
void init_sec_ctx(void)
452
0
{
453
0
  int i;
454
0
  struct sec_ctx *ctx_p;
455
456
  /* Initialise security context stack */
457
458
0
  memset(sec_ctx_stack, 0, sizeof(struct sec_ctx) * MAX_SEC_CTX_DEPTH);
459
460
0
  for (i = 0; i < MAX_SEC_CTX_DEPTH; i++) {
461
0
    sec_ctx_stack[i].ut.uid = (uid_t)-1;
462
0
    sec_ctx_stack[i].ut.gid = (gid_t)-1;
463
0
  }
464
465
  /* Initialise first level of stack.  It is the current context */
466
0
  ctx_p = &sec_ctx_stack[0];
467
468
0
  ctx_p->ut.uid = geteuid();
469
0
  ctx_p->ut.gid = getegid();
470
471
0
  get_current_groups(ctx_p->ut.gid, &ctx_p->ut.ngroups, &ctx_p->ut.groups);
472
473
0
  ctx_p->token = NULL; /* Maps to guest user. */
474
475
  /* Initialise current_user global */
476
477
0
  current_user.ut.uid = ctx_p->ut.uid;
478
0
  current_user.ut.gid = ctx_p->ut.gid;
479
0
  current_user.ut.ngroups = ctx_p->ut.ngroups;
480
0
  current_user.ut.groups = ctx_p->ut.groups;
481
482
  /* The conn and vuid are usually taken care of by other modules.
483
     We initialise them here. */
484
485
0
  current_user.conn = NULL;
486
0
  current_user.vuid = UID_FIELD_INVALID;
487
0
  current_user.nt_user_token = NULL;
488
0
}
489
490
/*************************************************************
491
 Called when we're inside a become_root() temporary escalation
492
 of privileges and the nt_user_token is NULL. Return the last
493
 active token on the context stack. We know there is at least
494
 one valid non-NULL token on the stack so panic if we underflow.
495
*************************************************************/
496
497
const struct security_token *sec_ctx_active_token(void)
498
0
{
499
0
  int stack_index = sec_ctx_stack_ndx;
500
0
  struct sec_ctx *ctx_p = &sec_ctx_stack[stack_index];
501
502
0
  while (ctx_p->token == NULL) {
503
0
    stack_index--;
504
0
    if (stack_index < 0) {
505
0
      DEBUG(0, ("Security context active token "
506
0
          "stack underflow!\n"));
507
0
      smb_panic("Security context active token "
508
0
          "stack underflow!");
509
0
    }
510
0
    ctx_p = &sec_ctx_stack[stack_index];
511
0
  }
512
0
  return ctx_p->token;
513
0
}