Coverage Report

Created: 2025-11-16 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/lib/util_sec.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   Copyright (C) Jeremy Allison 1998.
4
   rewritten for version 2.0.6 by Tridge
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
#ifndef AUTOCONF_TEST
21
#include "includes.h"
22
#include "system/passwd.h" /* uid_wrapper */
23
#include "../lib/util/setid.h"
24
25
#else
26
/* we are running this code in autoconf test mode to see which type of setuid
27
   function works */
28
#if defined(HAVE_UNISTD_H)
29
#include <unistd.h>
30
#endif
31
#if defined(HAVE_GRP_H)
32
#include <grp.h> /* setgroups() */
33
#endif
34
#include <stdbool.h>
35
#include <stdlib.h>
36
#include <stdio.h>
37
#include <sys/types.h>
38
#include <errno.h>
39
40
#ifdef HAVE_SYS_PRIV_H
41
#include <sys/priv.h>
42
#endif
43
#ifdef HAVE_SYS_ID_H
44
#include <sys/id.h>
45
#endif
46
47
#define DEBUG(x, y) printf y
48
#define smb_panic(x) exit(1)
49
#endif
50
51
/* are we running as non-root? This is used by the regression test code,
52
   and potentially also for sites that want non-root smbd */
53
static uid_t initial_uid;
54
static gid_t initial_gid;
55
56
/****************************************************************************
57
remember what uid we got started as - this allows us to run correctly
58
as non-root while catching trapdoor systems
59
****************************************************************************/
60
61
void sec_init(void)
62
0
{
63
0
  static int initialized;
64
65
0
  if (!initialized) {
66
67
0
#ifndef AUTOCONF_TEST
68
0
    if (uid_wrapper_enabled()) {
69
0
      setenv("UID_WRAPPER_MYUID", "1", 1);
70
0
    }
71
0
#endif
72
73
0
    initial_uid = geteuid();
74
0
    initial_gid = getegid();
75
76
0
#ifndef AUTOCONF_TEST
77
0
    if (uid_wrapper_enabled()) {
78
0
      unsetenv("UID_WRAPPER_MYUID");
79
0
    }
80
0
#endif
81
82
0
    initialized = 1;
83
0
  }
84
0
}
85
86
/****************************************************************************
87
some code (eg. winbindd) needs to know what uid we started as
88
****************************************************************************/
89
uid_t sec_initial_uid(void)
90
0
{
91
0
  return initial_uid;
92
0
}
93
94
/****************************************************************************
95
some code (eg. winbindd, profiling shm) needs to know what gid we started as
96
****************************************************************************/
97
gid_t sec_initial_gid(void)
98
0
{
99
0
  return initial_gid;
100
0
}
101
102
/**
103
 * @brief Check if we are running in root mode.
104
 *
105
 * @return Return whether Samba has root privileges
106
 */
107
bool root_mode(void)
108
0
{
109
0
  uid_t euid;
110
111
0
  euid = geteuid();
112
113
0
#ifndef AUTOCONF_TEST
114
0
  if (uid_wrapper_enabled()) {
115
0
    return (euid == initial_uid || euid == (uid_t)0);
116
0
  }
117
0
#endif
118
119
0
  return (initial_uid == euid);
120
0
}
121
122
/****************************************************************************
123
are we running in non-root mode?
124
****************************************************************************/
125
bool non_root_mode(void)
126
0
{
127
0
  return (initial_uid != (uid_t)0);
128
0
}
129
130
/****************************************************************************
131
abort if we haven't set the uid correctly
132
****************************************************************************/
133
static void assert_uid(uid_t ruid, uid_t euid)
134
0
{
135
0
  if ((euid != (uid_t)-1 && geteuid() != euid) ||
136
0
      (ruid != (uid_t)-1 && getuid() != ruid)) {
137
0
    if (!non_root_mode()) {
138
0
      DEBUG(0,("Failed to set uid privileges to (%d,%d) now set to (%d,%d)\n",
139
0
         (int)ruid, (int)euid,
140
0
         (int)getuid(), (int)geteuid()));
141
0
      smb_panic("failed to set uid\n");
142
0
      exit(1);
143
0
    }
144
0
  }
145
0
}
146
147
/****************************************************************************
148
abort if we haven't set the gid correctly
149
****************************************************************************/
150
static void assert_gid(gid_t rgid, gid_t egid)
151
0
{
152
0
  if ((egid != (gid_t)-1 && getegid() != egid) ||
153
0
      (rgid != (gid_t)-1 && getgid() != rgid)) {
154
0
    if (!non_root_mode()) {
155
0
      DEBUG(0,("Failed to set gid privileges to (%d,%d) now set to (%d,%d) uid=(%d,%d)\n",
156
0
         (int)rgid, (int)egid,
157
0
         (int)getgid(), (int)getegid(),
158
0
         (int)getuid(), (int)geteuid()));
159
0
      smb_panic("failed to set gid\n");
160
0
      exit(1);
161
0
    }
162
0
  }
163
0
}
164
165
/****************************************************************************
166
 Gain root privilege before doing something. 
167
 We want to end up with ruid==euid==0
168
****************************************************************************/
169
void gain_root_privilege(void)
170
0
{ 
171
0
#if defined(USE_SETRESUID) || defined(HAVE_LINUX_THREAD_CREDENTIALS)
172
0
  samba_setresuid(0,0,0);
173
0
#endif
174
    
175
#if USE_SETEUID
176
  samba_seteuid(0);
177
#endif
178
179
#if USE_SETREUID
180
  samba_setreuid(0, 0);
181
#endif
182
183
#if USE_SETUIDX
184
  samba_setuidx(ID_EFFECTIVE, 0);
185
  samba_setuidx(ID_REAL, 0);
186
#endif
187
188
  /* this is needed on some systems */
189
0
  samba_setuid(0);
190
191
0
  assert_uid(0, 0);
192
0
}
193
194
195
/****************************************************************************
196
 Ensure our real and effective groups are zero.
197
 we want to end up with rgid==egid==0
198
****************************************************************************/
199
void gain_root_group_privilege(void)
200
0
{
201
0
#if defined(USE_SETRESUID) || defined(HAVE_LINUX_THREAD_CREDENTIALS)
202
0
  samba_setresgid(0,0,0);
203
0
#endif
204
205
#if USE_SETREUID
206
  samba_setregid(0,0);
207
#endif
208
209
#if USE_SETEUID
210
  samba_setegid(0);
211
#endif
212
213
#if USE_SETUIDX
214
  samba_setgidx(ID_EFFECTIVE, 0);
215
  samba_setgidx(ID_REAL, 0);
216
#endif
217
218
0
  samba_setgid(0);
219
220
0
  assert_gid(0, 0);
221
0
}
222
223
224
/****************************************************************************
225
 Set effective uid, and possibly the real uid too.
226
 We want to end up with either:
227
  
228
   ruid==uid and euid==uid
229
230
 or
231
232
   ruid==0 and euid==uid
233
234
 depending on what the local OS will allow us to regain root from.
235
****************************************************************************/
236
void set_effective_uid(uid_t uid)
237
0
{
238
0
#if defined(USE_SETRESUID) || defined(HAVE_LINUX_THREAD_CREDENTIALS)
239
        /* Set the effective as well as the real uid. */
240
0
  if (samba_setresuid(uid,uid,-1) == -1) {
241
0
    if (errno == EAGAIN) {
242
0
      DEBUG(0, ("samba_setresuid failed with EAGAIN. uid(%d) "
243
0
          "might be over its NPROC limit\n",
244
0
          (int)uid));
245
0
    }
246
0
  }
247
0
#endif
248
249
#if USE_SETREUID
250
  samba_setreuid(-1,uid);
251
#endif
252
253
#if USE_SETEUID
254
  samba_seteuid(uid);
255
#endif
256
257
#if USE_SETUIDX
258
  samba_setuidx(ID_EFFECTIVE, uid);
259
#endif
260
261
0
  assert_uid(-1, uid);
262
0
}
263
264
/****************************************************************************
265
 Set *only* the effective gid.
266
 we want to end up with rgid==0 and egid==gid
267
****************************************************************************/
268
void set_effective_gid(gid_t gid)
269
0
{
270
0
#if defined(USE_SETRESUID) || defined(HAVE_LINUX_THREAD_CREDENTIALS)
271
0
  samba_setresgid(-1,gid,-1);
272
0
#endif
273
274
#if USE_SETREUID
275
  samba_setregid(-1,gid);
276
#endif
277
278
#if USE_SETEUID
279
  samba_setegid(gid);
280
#endif
281
282
#if USE_SETUIDX
283
  samba_setgidx(ID_EFFECTIVE, gid);
284
#endif
285
286
0
  assert_gid(-1, gid);
287
0
}
288
289
static uid_t saved_euid, saved_ruid;
290
static gid_t saved_egid, saved_rgid;
291
292
/****************************************************************************
293
 save the real and effective uid for later restoration. Used by the quotas
294
 code
295
****************************************************************************/
296
void save_re_uid(void)
297
0
{
298
0
  saved_ruid = getuid();
299
0
  saved_euid = geteuid();
300
0
}
301
302
303
/****************************************************************************
304
 and restore them!
305
****************************************************************************/
306
307
void restore_re_uid_fromroot(void)
308
0
{
309
0
#if defined(USE_SETRESUID) || defined(HAVE_LINUX_THREAD_CREDENTIALS)
310
0
  samba_setresuid(saved_ruid, saved_euid, -1);
311
#elif USE_SETREUID
312
  samba_setreuid(saved_ruid, -1);
313
  samba_setreuid(-1,saved_euid);
314
#elif USE_SETUIDX
315
  samba_setuidx(ID_REAL, saved_ruid);
316
  samba_setuidx(ID_EFFECTIVE, saved_euid);
317
#else
318
  set_effective_uid(saved_euid);
319
  if (getuid() != saved_ruid)
320
    samba_setuid(saved_ruid);
321
  set_effective_uid(saved_euid);
322
#endif
323
324
0
  assert_uid(saved_ruid, saved_euid);
325
0
}
326
327
void restore_re_uid(void)
328
0
{
329
0
  set_effective_uid(0);
330
0
  restore_re_uid_fromroot();
331
0
}
332
333
/****************************************************************************
334
 save the real and effective gid for later restoration. Used by the 
335
 getgroups code
336
****************************************************************************/
337
void save_re_gid(void)
338
0
{
339
0
  saved_rgid = getgid();
340
0
  saved_egid = getegid();
341
0
}
342
343
/****************************************************************************
344
 and restore them!
345
****************************************************************************/
346
void restore_re_gid(void)
347
0
{
348
0
#if defined(USE_SETRESUID) || defined(HAVE_LINUX_THREAD_CREDENTIALS)
349
0
  samba_setresgid(saved_rgid, saved_egid, -1);
350
#elif USE_SETREUID
351
  samba_setregid(saved_rgid, -1);
352
  samba_setregid(-1,saved_egid);
353
#elif USE_SETUIDX
354
  samba_setgidx(ID_REAL, saved_rgid);
355
  samba_setgidx(ID_EFFECTIVE, saved_egid);
356
#else
357
  set_effective_gid(saved_egid);
358
  if (getgid() != saved_rgid)
359
    samba_setgid(saved_rgid);
360
  set_effective_gid(saved_egid);
361
#endif
362
363
0
  assert_gid(saved_rgid, saved_egid);
364
0
}
365
366
367
/****************************************************************************
368
 set the real AND effective uid to the current effective uid in a way that
369
 allows root to be regained.
370
 This is only possible on some platforms.
371
****************************************************************************/
372
int set_re_uid(void)
373
0
{
374
0
  uid_t uid = geteuid();
375
376
0
#if defined(USE_SETRESUID) || defined(HAVE_LINUX_THREAD_CREDENTIALS)
377
0
  samba_setresuid(uid, uid, -1);
378
0
#endif
379
380
#if USE_SETREUID
381
  samba_setreuid(0, 0);
382
  samba_setreuid(uid, -1);
383
  samba_setreuid(-1, uid);
384
#endif
385
386
#if USE_SETEUID
387
  /* can't be done */
388
  return -1;
389
#endif
390
391
#if USE_SETUIDX
392
  /* can't be done */
393
  return -1;
394
#endif
395
396
0
  assert_uid(uid, uid);
397
0
  return 0;
398
0
}
399
400
401
/****************************************************************************
402
 Become the specified uid and gid - permanently !
403
 there should be no way back if possible
404
****************************************************************************/
405
void become_user_permanently(uid_t uid, gid_t gid)
406
0
{
407
  /*
408
   * First - gain root privilege. We do this to ensure
409
   * we can lose it again.
410
   */
411
412
0
  gain_root_privilege();
413
0
  gain_root_group_privilege();
414
415
0
#if defined(USE_SETRESUID) || defined(HAVE_LINUX_THREAD_CREDENTIALS)
416
0
  samba_setresgid(gid,gid,gid);
417
0
  samba_setgid(gid);
418
0
  samba_setresuid(uid,uid,uid);
419
0
  samba_setuid(uid);
420
0
#endif
421
422
#if USE_SETREUID
423
  samba_setregid(gid,gid);
424
  samba_setgid(gid);
425
  samba_setreuid(uid,uid);
426
  samba_setuid(uid);
427
#endif
428
429
#if USE_SETEUID
430
  samba_setegid(gid);
431
  samba_setgid(gid);
432
  samba_setuid(uid);
433
  samba_seteuid(uid);
434
  samba_setuid(uid);
435
#endif
436
437
#if USE_SETUIDX
438
  samba_setgidx(ID_REAL, gid);
439
  samba_setgidx(ID_EFFECTIVE, gid);
440
  samba_setgid(gid);
441
  samba_setuidx(ID_REAL, uid);
442
  samba_setuidx(ID_EFFECTIVE, uid);
443
  samba_setuid(uid);
444
#endif
445
  
446
0
  assert_uid(uid, uid);
447
0
  assert_gid(gid, gid);
448
0
}
449
450
#if defined(HAVE_LINUX_THREAD_CREDENTIALS) && defined(HAVE___THREAD)
451
  struct set_thread_credentials_cache {
452
    bool active;
453
    uid_t uid;
454
    gid_t gid;
455
    size_t setlen;
456
    uintptr_t gidset;
457
  };
458
static __thread struct set_thread_credentials_cache cache;
459
#endif
460
461
/**********************************************************
462
 Function to set thread specific credentials. Leave
463
 saved-set uid/gid alone.Must be thread-safe code.
464
**********************************************************/
465
466
int set_thread_credentials(uid_t uid,
467
      gid_t gid,
468
      size_t setlen,
469
      const gid_t *gidset)
470
0
{
471
0
#if defined(HAVE_LINUX_THREAD_CREDENTIALS)
472
  /*
473
   * With Linux thread-specific credentials
474
   * we know we have setresuid/setresgid
475
   * available.
476
   */
477
0
#ifdef HAVE___THREAD
478
0
  if (cache.active &&
479
0
      cache.uid == uid &&
480
0
      cache.gid == gid &&
481
0
      cache.setlen == setlen &&
482
0
      (const gid_t *)cache.gidset == gidset)
483
0
  {
484
0
    return 0;
485
0
  }
486
0
#endif /* HAVE___THREAD */
487
488
  /* Become root. */
489
  /* Set ru=0, eu=0 */
490
0
  if (samba_setresuid(0, 0, -1) != 0) {
491
0
    return -1;
492
0
  }
493
  /* Set our primary gid. */
494
  /* Set rg=gid, eg=gid */
495
0
  if (samba_setresgid(gid, gid, -1) != 0) {
496
0
    return -1;
497
0
  }
498
  /* Set extra groups list. */
499
0
  if (samba_setgroups(setlen, gidset) != 0) {
500
0
    return -1;
501
0
  }
502
  /* Become the requested user. */
503
  /* Set ru=uid, eu=uid */
504
0
  if (samba_setresuid(uid, uid, -1) != 0) {
505
0
    return -1;
506
0
  }
507
0
  if (geteuid() != uid || getuid() != uid ||
508
0
      getegid() != gid || getgid() != gid) {
509
0
    smb_panic("set_thread_credentials failed\n");
510
0
    return -1;
511
0
  }
512
513
0
#ifdef HAVE___THREAD
514
0
  cache.active = true;
515
0
  cache.uid = uid;
516
0
  cache.gid = gid;
517
0
  cache.setlen = setlen;
518
0
  cache.gidset = (uintptr_t)gidset;
519
0
#endif /* HAVE___THREAD */
520
521
0
  return 0;
522
#else
523
  errno = ENOSYS;
524
  return -1;
525
#endif
526
0
}
527
528
#ifdef AUTOCONF_TEST
529
530
/****************************************************************************
531
this function just checks that we don't get ENOSYS back
532
****************************************************************************/
533
static int have_syscall(void)
534
{
535
  errno = 0;
536
537
#if defined(USE_SETRESUID) || defined(HAVE_LINUX_THREAD_CREDENTIALS)
538
  samba_setresuid(-1,-1,-1);
539
#endif
540
541
#if USE_SETREUID
542
  samba_setreuid(-1,-1);
543
#endif
544
545
#if USE_SETEUID
546
  samba_seteuid(-1);
547
#endif
548
549
#if USE_SETUIDX
550
  samba_setuidx(ID_EFFECTIVE, -1);
551
#endif
552
553
  if (errno == ENOSYS) {
554
    return -1;
555
  }
556
  return 0;
557
}
558
559
int main(void)
560
{
561
        if (getuid() != 0) {
562
#if (defined(AIX) && defined(USE_SETREUID))
563
    /* setreuid is badly broken on AIX 4.1, we avoid it completely */
564
                fprintf(stderr,"avoiding possibly broken setreuid\n");
565
    exit(1);
566
#endif
567
568
    /* if not running as root then at least check to see if we get ENOSYS - this 
569
       handles Linux 2.0.x with glibc 2.1 */
570
                fprintf(stderr,"not running as root: checking for ENOSYS\n");
571
    exit(have_syscall());
572
  }
573
574
  gain_root_privilege();
575
  gain_root_group_privilege();
576
  set_effective_gid(1);
577
  set_effective_uid(1);
578
  save_re_uid();
579
  restore_re_uid();
580
  gain_root_privilege();
581
  gain_root_group_privilege();
582
  become_user_permanently(1, 1);
583
  samba_setuid(0);
584
  if (getuid() == 0) {
585
    fprintf(stderr,"uid not set permanently\n");
586
    exit(1);
587
  }
588
589
  printf("OK\n");
590
591
  exit(0);
592
}
593
#endif
594
595
/****************************************************************************
596
Check if we are setuid root.  Used in libsmb and smbpasswd paranoia checks.
597
****************************************************************************/
598
bool is_setuid_root(void) 
599
0
{
600
0
  return (geteuid() == (uid_t)0) && (getuid() != (uid_t)0);
601
0
}