Coverage Report

Created: 2026-06-30 07:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/server/util.c
Line
Count
Source
1
/*
2
 * util.c Various utility functions.
3
 *
4
 * Version:     $Id: 32c720404e74d8c5f1beb14bd6ffaf0ca6c85120 $
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 2 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, write to the Free Software
18
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19
 *
20
21
 */
22
23
RCSID("$Id: 32c720404e74d8c5f1beb14bd6ffaf0ca6c85120 $")
24
25
#include <freeradius-devel/server/base.h>
26
27
#include <freeradius-devel/util/base16.h>
28
#include <freeradius-devel/util/skip.h>
29
#include <freeradius-devel/util/perm.h>
30
#include <freeradius-devel/util/cap.h>
31
32
33
#include <fcntl.h>
34
35
static bool suid_down_permanent = false;  //!< Record whether we've permanently dropped privilledges
36
37
/*
38
 *  The signal() function in Solaris 2.5.1 sets SA_NODEFER in
39
 *  sa_flags, which causes grief if signal() is called in the
40
 *  handler before the cause of the signal has been cleared.
41
 *  (Infinite recursion).
42
 *
43
 *  The same problem appears on HPUX, so we avoid it, if we can.
44
 *
45
 *  Using sigaction() to reset the signal handler fixes the problem,
46
 *  so where available, we prefer that solution.
47
 */
48
49
void (*reset_signal(int signo, void (*func)(int)))(int)
50
0
{
51
0
#ifdef HAVE_SIGACTION
52
0
  struct sigaction act, oact;
53
54
0
  memset(&act, 0, sizeof(act));
55
0
  act.sa_handler = func;
56
0
  sigemptyset(&act.sa_mask);
57
0
  act.sa_flags = 0;
58
0
#ifdef  SA_INTERRUPT    /* SunOS */
59
0
  act.sa_flags |= SA_INTERRUPT;
60
0
#endif
61
0
  if (sigaction(signo, &act, &oact) < 0)
62
0
    return SIG_ERR;
63
0
  return oact.sa_handler;
64
#else
65
66
  /*
67
   *  re-set by calling the 'signal' function, which
68
   *  may cause infinite recursion and core dumps due to
69
   *  stack growth.
70
   *
71
   *  However, the system is too dumb to implement sigaction(),
72
   *  so we don't have a choice.
73
   */
74
  signal(signo, func);
75
76
  return NULL;
77
#endif
78
0
}
79
80
/** Ensures that a filename cannot walk up the directory structure
81
 *
82
 * Also sanitizes control chars.
83
 *
84
 * @param out Output buffer.
85
 * @param in string to escape.
86
 * @param len Size of the output buffer.
87
 */
88
static ssize_t rad_filename_make_safe(char *out, char const *in, size_t len)
89
0
{
90
0
  char const *q, *end;
91
0
  char *p = out;
92
93
0
  q = in;
94
0
  end = in + len;
95
96
  /*
97
   *  Escape '.foo', as it could be used to either read the local directory, walk back up the
98
   *  directory hierarchy, or else to create "dot" files.
99
   */
100
0
  if (len > 1) {
101
0
    if (*q == '.') {
102
0
      *(p++) = '_';
103
0
      q++;
104
0
    }
105
0
  }
106
107
0
  while (q < end) {
108
0
    if (*q < ' ') {
109
0
      *(p++) = '_';
110
0
      q++;
111
0
      continue;
112
0
    }
113
114
0
    if (*q != '/') {
115
0
      *(p++) = *(q++);
116
0
      continue;
117
0
    }
118
119
    /*
120
     *  Mash slashes, too.
121
     */
122
0
    *(p++) = '_';
123
0
    q++;
124
0
    continue;
125
0
  }
126
0
  *p = '\0';
127
128
0
  return (p - out);
129
0
}
130
131
int rad_filename_box_make_safe(fr_value_box_t *vb, UNUSED void *uxtc)
132
{
133
  char      *escaped;
134
  size_t      len;
135
136
  if (vb->vb_length == 0) return 0;
137
138
  if (fr_value_box_is_safe_for(vb, rad_filename_box_make_safe)) return 1;
139
140
  /*
141
   *  Allocate an output buffer, only ever the same or shorter than the input
142
   */
143
  MEM(escaped = talloc_array(vb, char, vb->vb_length + 1));
144
145
  len = rad_filename_make_safe(escaped, vb->vb_strvalue, vb->vb_length);
146
147
  fr_value_box_strdup_shallow_replace(vb, escaped, len);
148
149
  fr_value_box_mark_safe_for(vb, rad_filename_box_make_safe);
150
151
  return 1;
152
}
153
154
/** Escapes the raw string such that it should be safe to use as part of a file path
155
 *
156
 * This function is designed to produce a string that's still readable but portable
157
 * across the majority of file systems.
158
 *
159
 * For security reasons it cannot remove characters from the name, and must not allow
160
 * collisions to occur between different strings.
161
 *
162
 * With that in mind '-' has been chosen as the escape character, and will be double
163
 * escaped '-' -> '--' to avoid collisions.
164
 *
165
 * Escaping should be reversible if the original string needs to be extracted.
166
 *
167
 * @note function takes additional arguments so that it may be used as an xlat escape
168
 *  function but it's fine to call it directly.
169
 *
170
 * @note OSX/Unix/NTFS/VFAT have a max filename size of 255 bytes.
171
 *
172
 * @param request Current request (may be NULL).
173
 * @param out Output buffer.
174
 * @param outlen Size of the output buffer.
175
 * @param in string to escape.
176
 * @param arg Context arguments (unused, should be NULL).
177
 */
178
ssize_t rad_filename_escape(UNUSED request_t *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
179
0
{
180
0
  size_t freespace = outlen;
181
182
0
  while (*in != '\0') {
183
0
    size_t utf8_len;
184
185
    /*
186
     *  Encode multibyte UTF8 chars
187
     */
188
0
    utf8_len = fr_utf8_char((uint8_t const *) in, -1);
189
0
    if (utf8_len > 1) {
190
0
      if (freespace <= (utf8_len * 3)) break;
191
192
0
      switch (utf8_len) {
193
0
      case 2:
194
0
        snprintf(out, freespace, "-%02x-%02x", (uint8_t)in[0], (uint8_t)in[1]);
195
0
        break;
196
197
0
      case 3:
198
0
        snprintf(out, freespace, "-%02x-%02x-%02x", (uint8_t)in[0], (uint8_t)in[1], (uint8_t)in[2]);
199
0
        break;
200
201
0
      case 4:
202
0
        snprintf(out, freespace, "-%02x-%02x-%02x-%02x", (uint8_t)in[0], (uint8_t)in[1], (uint8_t)in[2], (uint8_t)in[3]);
203
0
        break;
204
0
      }
205
206
0
      freespace -= (utf8_len * 3);
207
0
      out += (utf8_len * 3);
208
0
      in += utf8_len;
209
210
0
      continue;
211
0
    }
212
213
    /*
214
     *  Safe chars
215
     */
216
0
    if (((*in >= 'A') && (*in <= 'Z')) ||
217
0
        ((*in >= 'a') && (*in <= 'z')) ||
218
0
        ((*in >= '0') && (*in <= '9')) ||
219
0
        (*in == '_')) {
220
0
          if (freespace <= 1) break;
221
222
0
      *out++ = *in++;
223
0
      freespace--;
224
0
      continue;
225
0
    }
226
0
    if (freespace <= 2) break;
227
228
    /*
229
     *  Double escape '-' (like \\)
230
     */
231
0
    if (*in == '-') {
232
0
      *out++ = '-';
233
0
      *out++ = '-';
234
235
0
      freespace -= 2;
236
0
      in++;
237
0
      continue;
238
0
    }
239
240
    /*
241
     *  Unsafe chars get escaped as -XX.
242
     */
243
0
    snprintf(out, freespace, "-%02x", (uint8_t) in[0]);
244
0
    in++;
245
0
    out += 3;
246
0
    freespace -= 3;
247
0
  }
248
0
  *out = '\0';
249
250
0
  return outlen - freespace;
251
0
}
252
253
int rad_filename_box_escape(fr_value_box_t *vb, UNUSED void *uxtc)
254
0
{
255
0
  char      *escaped;
256
0
  size_t      len;
257
258
0
  if (vb->vb_length == 0) return 0;
259
260
0
  fr_assert(!fr_value_box_is_safe_for(vb, rad_filename_box_escape));
261
262
  /*
263
   *  Allocate an output buffer, if every character is escaped,
264
   *  it will be 3 times the input
265
   */
266
0
  MEM(escaped = talloc_array(vb, char, vb->vb_length * 3 + 1));
267
268
0
  len = rad_filename_escape(NULL, escaped, (vb->vb_length * 3 + 1), vb->vb_strvalue, NULL);
269
270
  /*
271
   *  If the escaped length == input length, no changes were done.
272
   */
273
0
  if (len == vb->vb_length) {
274
0
    talloc_free(escaped);
275
0
    return 0;
276
0
  }
277
278
0
  fr_value_box_strdup_shallow_replace(vb, escaped, len);
279
280
0
  return 0;
281
0
}
282
283
284
/*
285
 *  Copy a quoted string.
286
 */
287
static int rad_copy_string(char *to, char const *from)
288
0
{
289
0
  int length = 0;
290
0
  char quote = *from;
291
292
0
  do {
293
0
    if (*from == '\\') {
294
0
      *(to++) = *(from++);
295
0
      length++;
296
0
    }
297
0
    *(to++) = *(from++);
298
0
    length++;
299
0
  } while (*from && (*from != quote));
300
301
0
  if (*from != quote) return -1; /* not properly quoted */
302
303
0
  *(to++) = quote;
304
0
  length++;
305
0
  *to = '\0';
306
307
0
  return length;
308
0
}
309
310
/*
311
 *  Copy a quoted string but without the quotes. The length
312
 *  returned is the number of chars written; the number of
313
 *  characters consumed is 2 more than this.
314
 */
315
static int rad_copy_string_bare(char *to, char const *from)
316
0
{
317
0
  int length = 0;
318
0
  char quote = *from;
319
320
0
  from++;
321
0
  while (*from && (*from != quote)) {
322
0
    if (*from == '\\') {
323
0
      *(to++) = *(from++);
324
0
      length++;
325
0
    }
326
0
    *(to++) = *(from++);
327
0
    length++;
328
0
  }
329
330
0
  if (*from != quote) return -1; /* not properly quoted */
331
332
0
  *to = '\0';
333
334
0
  return length;
335
0
}
336
337
338
/*
339
 *  Copy a %{} string.
340
 */
341
static int rad_copy_variable(char *to, char const *from)
342
0
{
343
0
  int length = 0;
344
0
  int sublen;
345
346
0
  *(to++) = *(from++);
347
0
  length++;
348
349
0
  while (*from) {
350
0
    switch (*from) {
351
0
    case '"':
352
0
    case '\'':
353
0
      sublen = rad_copy_string(to, from);
354
0
      if (sublen < 0) return sublen;
355
0
      from += sublen;
356
0
      to += sublen;
357
0
      length += sublen;
358
0
      break;
359
360
0
    case '}': /* end of variable expansion */
361
0
      *(to++) = *(from++);
362
0
      *to = '\0';
363
0
      length++;
364
0
      return length; /* proper end of variable */
365
366
0
    case '\\':
367
0
      *(to++) = *(from++);
368
0
      *(to++) = *(from++);
369
0
      length += 2;
370
0
      break;
371
372
0
    case '%': /* start of variable expansion */
373
0
      if (from[1] == '{') {
374
0
        *(to++) = *(from++);
375
0
        length++;
376
377
0
        sublen = rad_copy_variable(to, from);
378
0
        if (sublen < 0) return sublen;
379
0
        from += sublen;
380
0
        to += sublen;
381
0
        length += sublen;
382
0
        break;
383
0
      } /* else FIXME: catch %%{ ?*/
384
385
0
      FALL_THROUGH;
386
0
    default:
387
0
      *(to++) = *(from++);
388
0
      length++;
389
0
      break;
390
0
    }
391
0
  } /* loop over the input string */
392
393
  /*
394
   *  We ended the string before a trailing '}'
395
   */
396
397
0
  return -1;
398
0
}
399
400
uint32_t rad_pps(uint32_t *past, uint32_t *present, time_t *then, struct timeval *now)
401
0
{
402
0
  uint32_t pps;
403
404
0
  if (*then != now->tv_sec) {
405
0
    *then = now->tv_sec;
406
0
    *past = *present;
407
0
    *present = 0;
408
0
  }
409
410
  /*
411
   *  Bootstrap PPS by looking at a percentage of
412
   *  the previous PPS.  This lets us take a moving
413
   *  count, without doing a moving average.  If
414
   *  we're a fraction "f" (0..1) into the current
415
   *  second, we can get a good guess for PPS by
416
   *  doing:
417
   *
418
   *  PPS = pps_now + pps_old * (1 - f)
419
   *
420
   *  It's an instantaneous measurement, rather than
421
   *  a moving average.  This will hopefully let it
422
   *  respond better to sudden spikes.
423
   *
424
   *  Doing the calculations by thousands allows us
425
   *  to not overflow 2^32, AND to not underflow
426
   *  when we divide by USEC.
427
   */
428
0
  pps = USEC - now->tv_usec; /* useconds left in previous second */
429
0
  pps /= 1000;       /* scale to milliseconds */
430
0
  pps *= *past;      /* multiply by past count to get fraction */
431
0
  pps /= 1000;       /* scale to usec again */
432
0
  pps += *present;     /* add in current count */
433
434
0
  return pps;
435
0
}
436
437
/** Split string into words and expand each one
438
 *
439
 * @param request Current request.
440
 * @param cmd string to split.
441
 * @param max_argc the maximum number of arguments to split into.
442
 * @param argv Where to write the pointers into argv_buf.
443
 * @param can_fail If false, stop processing if any of the xlat expansions fail.
444
 * @param argv_buflen size of argv_buf.
445
 * @param argv_buf temporary buffer we used to mangle/expand cmd.
446
 *  Pointers to offsets of this buffer will be written to argv.
447
 * @return argc or -1 on failure.
448
 */
449
450
int rad_expand_xlat(request_t *request, char const *cmd,
451
        int max_argc, char const *argv[], bool can_fail,
452
        size_t argv_buflen, char *argv_buf)
453
0
{
454
0
  char const *from;
455
0
  char *to;
456
0
  int argc = -1;
457
0
  int i;
458
0
  int left;
459
0
  size_t len = strlen(cmd);
460
461
0
  if (len > (argv_buflen - 1)) {
462
0
    fr_strerror_const("Expansion string is too long for output buffer");
463
0
    return -1;
464
0
  }
465
466
  /*
467
   *  Check for bad escapes.
468
   */
469
0
  if ((len > 0) && (cmd[len - 1] == '\\')) {
470
0
    fr_strerror_const("Expansion string ends with a trailing backslash - invalid escape sequence");
471
0
    return -1;
472
0
  }
473
474
0
  strlcpy(argv_buf, cmd, argv_buflen);
475
476
  /*
477
   *  Split the string into argv's BEFORE doing xlat_eval...
478
   */
479
0
  from = cmd;
480
0
  to = argv_buf;
481
0
  argc = 0;
482
0
  while (*from) {
483
0
    int length;
484
485
0
    fr_skip_whitespace(from);
486
487
0
    argv[argc] = to;
488
0
    argc++;
489
490
0
    if (argc >= (max_argc - 1)) break;
491
492
    /*
493
     *  Copy the argv over to our buffer.
494
     */
495
0
    while (*from && (*from != ' ') && (*from != '\t')) {
496
0
      if (to >= argv_buf + argv_buflen - 1) {
497
0
        fr_strerror_const("Expansion string is too long for output buffer");
498
0
        return -1;
499
0
      }
500
501
0
      switch (*from) {
502
0
      case '"':
503
0
      case '\'':
504
0
        length = rad_copy_string_bare(to, from);
505
0
        if (length < 0) {
506
0
          fr_strerror_const("Invalid quoted string in expansion");
507
0
          return -1;
508
0
        }
509
0
        from += length+2;
510
0
        to += length;
511
0
        break;
512
513
0
      case '%':
514
0
        if (from[1] == '{') {
515
0
          *(to++) = *(from++);
516
517
0
          length = rad_copy_variable(to, from);
518
0
          if (length < 0) {
519
0
            fr_strerror_const("Invalid variable in expansion");
520
0
            return -1;
521
0
          }
522
0
          from += length;
523
0
          to += length;
524
0
        } else { /* FIXME: catch %%{ ? */
525
0
          *(to++) = *(from++);
526
0
        }
527
0
        break;
528
529
0
      case '\\':
530
0
        if (from[1] == ' ') from++;
531
0
        FALL_THROUGH;
532
533
0
      default:
534
0
        *(to++) = *(from++);
535
0
      }
536
0
    } /* end of string, or found a space */
537
538
0
    *(to++) = '\0'; /* terminate the string */
539
0
  }
540
541
  /*
542
   *  We have to have SOMETHING, at least.
543
   */
544
0
  if (argc <= 0) {
545
0
    fr_strerror_const("Expansion string is empty");
546
0
    return -1;
547
0
  }
548
549
  /*
550
   *  Expand each string, as appropriate.
551
   */
552
0
  left = argv_buf + argv_buflen - to;
553
0
  for (i = 0; i < argc; i++) {
554
0
    int sublen;
555
556
    /*
557
     *  Don't touch argv's which won't be translated.
558
     */
559
0
    if (strchr(argv[i], '%') == NULL) continue;
560
561
0
    if (!request) continue;
562
563
0
    sublen = xlat_eval(to, left - 1, request, argv[i], NULL, NULL);
564
0
    if (sublen <= 0) {
565
0
      if (can_fail) {
566
        /*
567
         *  Fail to be backwards compatible.
568
         *
569
         *  It's yucky, but it won't break anything,
570
         *  and it won't cause security problems.
571
         */
572
0
        sublen = 0;
573
0
      } else {
574
0
        fr_strerror_const("Failed expanding substring");
575
0
        return -1;
576
0
      }
577
0
    }
578
579
0
    argv[i] = to;
580
0
    to += sublen;
581
0
    *(to++) = '\0';
582
0
    left -= sublen;
583
0
    left--;
584
585
0
    if (left <= 0) {
586
0
      fr_strerror_const("Ran out of space while expanding arguments");
587
0
      return -1;
588
0
    }
589
0
  }
590
0
  argv[argc] = NULL;
591
592
0
  return argc;
593
0
}
594
595
#ifdef HAVE_SETUID
596
static bool doing_setuid = false;
597
static uid_t suid_down_uid = (uid_t)-1;
598
599
/** Set the uid and gid used when dropping privileges
600
 *
601
 * @note if this function hasn't been called, rad_suid_down will have no effect.
602
 *
603
 * @param uid to drop down to.
604
 */
605
void rad_suid_set_down_uid(uid_t uid)
606
0
{
607
0
  suid_down_uid = uid;
608
0
  doing_setuid = true;
609
0
}
610
611
#  if defined(HAVE_SETRESUID) && defined (HAVE_GETRESUID)
612
void rad_suid_up(void)
613
0
{
614
0
  uid_t ruid, euid, suid;
615
616
0
  if (getresuid(&ruid, &euid, &suid) < 0) {
617
0
    ERROR("Failed getting saved UID's");
618
0
    fr_exit_now(EXIT_FAILURE);
619
0
  }
620
621
0
  if (setresuid(-1, suid, -1) < 0) {
622
0
    ERROR("Failed switching to privileged user");
623
0
    fr_exit_now(EXIT_FAILURE);
624
0
  }
625
626
0
  if (geteuid() != suid) {
627
0
    ERROR("Switched to unknown UID");
628
0
    fr_exit_now(EXIT_FAILURE);
629
0
  }
630
0
}
631
632
void rad_suid_down(void)
633
0
{
634
0
  if (!doing_setuid) return;
635
636
0
  if (setresuid(-1, suid_down_uid, geteuid()) < 0) {
637
0
    struct passwd *passwd;
638
0
    char const *name;
639
640
0
    name = (fr_perm_getpwuid(NULL, &passwd, suid_down_uid) < 0) ? "unknown" : passwd->pw_name;
641
0
    ERROR("Failed switching to uid %s: %s", name, fr_syserror(errno));
642
0
    talloc_free(passwd);
643
0
    fr_exit_now(EXIT_FAILURE);
644
0
  }
645
646
0
  if (geteuid() != suid_down_uid) {
647
0
    ERROR("Failed switching uid: UID is incorrect");
648
0
    fr_exit_now(EXIT_FAILURE);
649
0
  }
650
651
0
  fr_reset_dumpable();
652
0
}
653
654
void rad_suid_down_permanent(void)
655
{
656
  if (!doing_setuid) return;
657
658
  if (setresuid(suid_down_uid, suid_down_uid, suid_down_uid) < 0) {
659
    struct passwd *passwd;
660
    char const *name;
661
662
    name = (fr_perm_getpwuid(NULL, &passwd, suid_down_uid) < 0) ? "unknown" : passwd->pw_name;
663
    ERROR("Failed in permanent switch to uid %s: %s", name, fr_syserror(errno));
664
    talloc_free(passwd);
665
    fr_exit_now(EXIT_FAILURE);
666
  }
667
668
  if (geteuid() != suid_down_uid) {
669
    ERROR("Switched to unknown uid");
670
    fr_exit_now(EXIT_FAILURE);
671
  }
672
673
  /*
674
   *  Shut down most of the interesting things which might get abused.
675
   */
676
  if ((fr_cap_disable(CAP_SETUID, CAP_EFFECTIVE) < 0) ||
677
      (fr_cap_disable(CAP_SETUID, CAP_INHERITABLE) < 0) ||
678
      (fr_cap_disable(CAP_SETUID, CAP_PERMITTED) < 0)) {
679
    ERROR("Failed disabling CAP_SUID");
680
    fr_exit_now(EXIT_FAILURE);
681
  }
682
683
#ifdef HAVE_GRP_H
684
  if ((fr_cap_disable(CAP_SETGID, CAP_EFFECTIVE) < 0) ||
685
      (fr_cap_disable(CAP_SETGID, CAP_INHERITABLE) < 0) ||
686
      (fr_cap_disable(CAP_SETGID, CAP_PERMITTED) < 0)) {
687
    ERROR("Failed disabling CAP_SGID");
688
    fr_exit_now(EXIT_FAILURE);
689
  }
690
#endif
691
692
  fr_reset_dumpable();
693
694
  suid_down_permanent = true;
695
}
696
#  else
697
/*
698
 *  Much less secure...
699
 */
700
void rad_suid_up(void)
701
{
702
  if (!doing_setuid) return;
703
704
  if (seteuid(0) < 0) {
705
    ERROR("Failed switching up to euid 0: %s", fr_syserror(errno));
706
    fr_exit_now(EXIT_FAILURE);
707
  }
708
709
}
710
711
void rad_suid_down(void)
712
{
713
  if (!doing_setuid) return;
714
715
  if (geteuid() == suid_down_uid) return;
716
717
  if (seteuid(suid_down_uid) < 0) {
718
    struct passwd *passwd;
719
    char const *name;
720
721
    name = (fr_perm_getpwuid(NULL, &passwd, suid_down_uid) < 0) ? "unknown": passwd->pw_name;
722
    ERROR("Failed switching to euid %s: %s", name, fr_syserror(errno));
723
    talloc_free(passwd);
724
    fr_exit_now(EXIT_FAILURE);
725
  }
726
727
  fr_reset_dumpable();
728
}
729
730
void rad_suid_down_permanent(void)
731
{
732
  if (!doing_setuid) return;
733
734
  /*
735
   *  Already done.  Don't do anything else.
736
   */
737
  if (getuid() == suid_down_uid) return;
738
739
  /*
740
   *  We're root, but running as a normal user.  Fix that,
741
   *  so we can call setuid().
742
   */
743
  if (geteuid() == suid_down_uid) {
744
    rad_suid_up();
745
  }
746
747
  if (setuid(suid_down_uid) < 0) {
748
    struct passwd *passwd;
749
    char const *name;
750
751
    name = (fr_perm_getpwuid(NULL, &passwd, suid_down_uid) < 0) ? "unknown": passwd->pw_name;
752
    ERROR("Failed switching permanently to uid %s: %s", name, fr_syserror(errno));
753
    talloc_free(passwd);
754
    fr_exit_now(EXIT_FAILURE);
755
  }
756
757
  fr_reset_dumpable();
758
759
  suid_down_permanent = true;
760
}
761
#  endif /* HAVE_SETRESUID && HAVE_GETRESUID */
762
#else  /* HAVE_SETUID */
763
void rad_suid_set_down_uid(uid_t uid)
764
{
765
}
766
767
void rad_suid_up(void)
768
{
769
}
770
771
void rad_suid_down(void)
772
{
773
  fr_reset_dumpable();
774
}
775
776
void rad_suid_down_permanent(void)
777
{
778
  fr_reset_dumpable();
779
}
780
#endif /* HAVE_SETUID */
781
782
/** Return whether we've permanently dropped root privileges
783
 *
784
 * @return
785
 *  - true if root privileges have been dropped.
786
 *  - false if root privileges have not been dropped.
787
 */
788
bool rad_suid_is_down_permanent(void)
789
0
{
790
0
  return suid_down_permanent;
791
0
}
792
793
/** Alter the effective user id
794
 *
795
 * @param uid to set
796
 * @return
797
 *  - 0 on success.
798
 *  - -1 on failure.
799
 */
800
int rad_seuid(uid_t uid)
801
0
{
802
0
  if (seteuid(uid) < 0) {
803
0
    int sete_errno = errno;  /* errno sets overwritten by fr_perm_getpwuid */
804
0
    struct passwd *passwd;
805
806
0
    if (fr_perm_getpwuid(NULL, &passwd, uid) < 0) return -1;
807
0
    fr_strerror_printf("%s", fr_syserror(sete_errno));
808
0
    talloc_free(passwd);
809
810
0
    return -1;
811
0
  }
812
0
  return 0;
813
0
}
814
815
/** Alter the effective user id
816
 *
817
 * @param gid to set
818
 * @return
819
 *  - 0 on success.
820
 *  - -1 on failure.
821
 */
822
int rad_segid(gid_t gid)
823
0
{
824
0
  if (setegid(gid) < 0) {
825
0
    int sete_errno = errno;  /* errno sets overwritten by fr_perm_getgrgid */
826
0
    struct group *group;
827
828
0
    if (fr_perm_getgrgid(NULL, &group, gid) < 0) return -1;
829
0
    fr_strerror_printf("%s", fr_syserror(sete_errno));
830
0
    talloc_free(group);
831
832
0
    return -1;
833
0
  }
834
0
  return 0;
835
0
}