Coverage Report

Created: 2026-06-07 07:07

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