The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
util.c
Go to the documentation of this file.
1 /*
2  * util.c Various utility functions.
3  *
4  * Version: $Id: 4285e85ecc4c6f1216b4a69abfa628c3fd92475a $
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: 4285e85ecc4c6f1216b4a69abfa628c3fd92475a $")
24 
25 #include <freeradius-devel/server/base.h>
26 #include <freeradius-devel/server/stats.h>
27 #include <freeradius-devel/server/util.h>
28 
29 #include <freeradius-devel/util/debug.h>
30 #include <freeradius-devel/util/base16.h>
31 #include <freeradius-devel/util/misc.h>
32 #include <freeradius-devel/util/perm.h>
33 
34 #include <ctype.h>
35 #include <fcntl.h>
36 #include <signal.h>
37 #include <sys/stat.h>
38 
39 static bool suid_down_permanent = false; //!< Record whether we've permanently dropped privilledges
40 
41 /*
42  * The signal() function in Solaris 2.5.1 sets SA_NODEFER in
43  * sa_flags, which causes grief if signal() is called in the
44  * handler before the cause of the signal has been cleared.
45  * (Infinite recursion).
46  *
47  * The same problem appears on HPUX, so we avoid it, if we can.
48  *
49  * Using sigaction() to reset the signal handler fixes the problem,
50  * so where available, we prefer that solution.
51  */
52 
53 void (*reset_signal(int signo, void (*func)(int)))(int)
54 {
55 #ifdef HAVE_SIGACTION
56  struct sigaction act, oact;
57 
58  memset(&act, 0, sizeof(act));
59  act.sa_handler = func;
60  sigemptyset(&act.sa_mask);
61  act.sa_flags = 0;
62 #ifdef SA_INTERRUPT /* SunOS */
63  act.sa_flags |= SA_INTERRUPT;
64 #endif
65  if (sigaction(signo, &act, &oact) < 0)
66  return SIG_ERR;
67  return oact.sa_handler;
68 #else
69 
70  /*
71  * re-set by calling the 'signal' function, which
72  * may cause infinite recursion and core dumps due to
73  * stack growth.
74  *
75  * However, the system is too dumb to implement sigaction(),
76  * so we don't have a choice.
77  */
78  signal(signo, func);
79 
80  return NULL;
81 #endif
82 }
83 
84 /** Ensures that a filename cannot walk up the directory structure
85  *
86  * Also sanitizes control chars.
87  *
88  * @param request Current request (may be NULL).
89  * @param out Output buffer.
90  * @param outlen Size of the output buffer.
91  * @param in string to escape.
92  * @param arg Context arguments (unused, should be NULL).
93  */
94 size_t rad_filename_make_safe(UNUSED request_t *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
95 {
96  char const *q = in;
97  char *p = out;
98  size_t left = outlen;
99 
100  while (*q) {
101  if (*q != '/') {
102  if (left < 2) break;
103 
104  /*
105  * Smash control characters and spaces to
106  * something simpler.
107  */
108  if (*q < ' ') {
109  *(p++) = '_';
110  q++;
111  continue;
112  }
113 
114  *(p++) = *(q++);
115  left--;
116  continue;
117  }
118 
119  /*
120  * For now, allow slashes in the expanded
121  * filename. This allows the admin to set
122  * attributes which create sub-directories.
123  * Unfortunately, it also allows users to send
124  * attributes which *may* end up creating
125  * sub-directories.
126  */
127  if (left < 2) break;
128  *(p++) = *(q++);
129 
130  /*
131  * Get rid of ////../.././///.///..//
132  */
133  redo:
134  /*
135  * Get rid of ////
136  */
137  if (*q == '/') {
138  q++;
139  goto redo;
140  }
141 
142  /*
143  * Get rid of /./././
144  */
145  if ((q[0] == '.') &&
146  (q[1] == '/')) {
147  q += 2;
148  goto redo;
149  }
150 
151  /*
152  * Get rid of /../../../
153  */
154  if ((q[0] == '.') && (q[1] == '.') &&
155  (q[2] == '/')) {
156  q += 3;
157  goto redo;
158  }
159  }
160  *p = '\0';
161 
162  return (p - out);
163 }
164 
166 {
167  char *escaped;
168  size_t len;
169  fr_value_box_entry_t entry;
170 
171  if (vb->vb_length == 0) return 0;
172  if (vb->safe_for == (fr_value_box_safe_for_t)rad_filename_box_make_safe) return 0;
173 
174  /*
175  * Allocate an output buffer, only ever the same or shorter than the input
176  */
177  MEM(escaped = talloc_array(vb, char, vb->vb_length + 1));
178 
179  len = rad_filename_make_safe(NULL, escaped, (vb->vb_length + 1), vb->vb_strvalue, NULL);
180 
181  entry = vb->entry;
183  fr_value_box_bstrndup(vb, vb, NULL, escaped, len, false);
184  vb->entry = entry;
185  talloc_free(escaped);
186 
187  return 0;
188 }
189 
190 /** Escapes the raw string such that it should be safe to use as part of a file path
191  *
192  * This function is designed to produce a string that's still readable but portable
193  * across the majority of file systems.
194  *
195  * For security reasons it cannot remove characters from the name, and must not allow
196  * collisions to occur between different strings.
197  *
198  * With that in mind '-' has been chosen as the escape character, and will be double
199  * escaped '-' -> '--' to avoid collisions.
200  *
201  * Escaping should be reversible if the original string needs to be extracted.
202  *
203  * @note function takes additional arguments so that it may be used as an xlat escape
204  * function but it's fine to call it directly.
205  *
206  * @note OSX/Unix/NTFS/VFAT have a max filename size of 255 bytes.
207  *
208  * @param request Current request (may be NULL).
209  * @param out Output buffer.
210  * @param outlen Size of the output buffer.
211  * @param in string to escape.
212  * @param arg Context arguments (unused, should be NULL).
213  */
214 size_t rad_filename_escape(UNUSED request_t *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
215 {
216  size_t freespace = outlen;
217 
218  while (*in != '\0') {
219  size_t utf8_len;
220 
221  /*
222  * Encode multibyte UTF8 chars
223  */
224  utf8_len = fr_utf8_char((uint8_t const *) in, -1);
225  if (utf8_len > 1) {
226  if (freespace <= (utf8_len * 3)) break;
227 
228  switch (utf8_len) {
229  case 2:
230  snprintf(out, freespace, "-%x-%x", (uint8_t)in[0], (uint8_t)in[1]);
231  break;
232 
233  case 3:
234  snprintf(out, freespace, "-%x-%x-%x", (uint8_t)in[0], (uint8_t)in[1], (uint8_t)in[2]);
235  break;
236 
237  case 4:
238  snprintf(out, freespace, "-%x-%x-%x-%x", (uint8_t)in[0], (uint8_t)in[1], (uint8_t)in[2], (uint8_t)in[3]);
239  break;
240  }
241 
242  freespace -= (utf8_len * 3);
243  out += (utf8_len * 3);
244  in += utf8_len;
245 
246  continue;
247  }
248 
249  /*
250  * Safe chars
251  */
252  if (((*in >= 'A') && (*in <= 'Z')) ||
253  ((*in >= 'a') && (*in <= 'z')) ||
254  ((*in >= '0') && (*in <= '9')) ||
255  (*in == '_')) {
256  if (freespace <= 1) break;
257 
258  *out++ = *in++;
259  freespace--;
260  continue;
261  }
262  if (freespace <= 2) break;
263 
264  /*
265  * Double escape '-' (like \\‍)
266  */
267  if (*in == '-') {
268  *out++ = '-';
269  *out++ = '-';
270 
271  freespace -= 2;
272  in++;
273  continue;
274  }
275 
276  /*
277  * Unsafe chars
278  */
279  *out++ = '-';
280  fr_base16_encode(&FR_SBUFF_OUT(out, freespace), &FR_DBUFF_TMP((uint8_t const *)in, 1));
281  in++;
282  out += 2;
283  freespace -= 3;
284  }
285  *out = '\0';
286 
287  return outlen - freespace;
288 }
289 
291 {
292  char *escaped;
293  size_t len;
294  fr_value_box_entry_t entry;
295 
296  if (vb->vb_length == 0) return 0;
297  if (vb->safe_for == (fr_value_box_safe_for_t)rad_filename_box_escape) return 0;
298 
299  /*
300  * Allocate an output buffer, if every character is escaped,
301  * it will be 3 times the input
302  */
303  MEM(escaped = talloc_array(vb, char, vb->vb_length * 3 + 1));
304 
305  len = rad_filename_escape(NULL, escaped, (vb->vb_length * 3 + 1), vb->vb_strvalue, NULL);
306 
307  /*
308  * If the escaped length == input length, no changes were done.
309  */
310  if (len == vb->vb_length) {
311  talloc_free(escaped);
312  return 0;
313  }
314 
315  entry = vb->entry;
317  fr_value_box_bstrndup(vb, vb, NULL, escaped, len, false);
318  vb->entry = entry;
319  talloc_free(escaped);
320 
321  return 0;
322 }
323 
324 /** Converts data stored in a file name back to its original form
325  *
326  * @param out Where to write the unescaped string (may be the same as in).
327  * @param outlen Length of the output buffer.
328  * @param in Input filename.
329  * @param inlen Length of input.
330  * @return
331  * - Number of bytes written to output buffer
332  * - offset where parse error occurred on failure.
333  */
334 ssize_t rad_filename_unescape(char *out, size_t outlen, char const *in, size_t inlen)
335 {
336  char const *p, *end = in + inlen;
337  size_t freespace = outlen;
338 
339  for (p = in; p < end; p++) {
340  if (freespace <= 1) break;
341 
342  if (((*p >= 'A') && (*p <= 'Z')) ||
343  ((*p >= 'a') && (*p <= 'z')) ||
344  ((*p >= '0') && (*p <= '9')) ||
345  (*p == '_')) {
346  *out++ = *p;
347  freespace--;
348  continue;
349  }
350 
351  if (p[0] == '-') {
352  /*
353  * End of input, '-' needs at least one extra char after
354  * it to be valid.
355  */
356  if ((end - p) < 2) return in - p;
357  if (p[1] == '-') {
358  p++;
359  *out++ = '-';
360  freespace--;
361  continue;
362  }
363 
364  /*
365  * End of input, '-' must be followed by <hex><hex>
366  * but there aren't enough chars left
367  */
368  if ((end - p) < 3) return in - p;
369 
370  /*
371  * If hex2bin returns 0 the next two chars weren't hexits.
372  */
373  if (fr_base16_decode(NULL,
374  &FR_DBUFF_TMP((uint8_t *) out, 1),
375  &FR_SBUFF_IN(in, 1), false) == 0) return in - (p + 1);
376  in += 2;
377  out++;
378  freespace--;
379  }
380 
381  return in - p; /* offset we found the bad char at */
382  }
383  *out = '\0';
384 
385  return outlen - freespace; /* how many bytes were written */
386 }
387 
388 /** talloc a buffer to hold the concatenated value of all elements of argv
389  *
390  * @param ctx to allocate buffer in.
391  * @param argv array of substrings.
392  * @param argc length of array.
393  * @param c separation character. Optional, may be '\0' for no separator.
394  * @return the concatenation of the elements of argv, separated by c.
395  */
396 char *rad_ajoin(TALLOC_CTX *ctx, char const **argv, int argc, char c)
397 {
398  char *buff, *p;
399  int i;
400  size_t total = 0, freespace;
401 
402  if (!*argv) {
403  goto null;
404  }
405 
406  for (i = 0; i < argc; i++) total += (strlen(argv[i]) + ((c == '\0') ? 0 : 1));
407  if (!total) {
408  null:
409  return talloc_zero_array(ctx, char, 1);
410  }
411 
412  if (c == '\0') total++;
413 
414  freespace = total;
415  buff = p = talloc_array(ctx, char, total);
416  for (i = 0; i < argc; i++) {
417  size_t len;
418 
419  len = strlcpy(p, argv[i], freespace);
420  p += len;
421  freespace -= len;
422 
423  *p++ = c;
424  freespace--;
425  }
426  buff[total] = '\0';
427 
428  return buff;
429 }
430 
431 /*
432  * Copy a quoted string.
433  */
434 static int rad_copy_string(char *to, char const *from)
435 {
436  int length = 0;
437  char quote = *from;
438 
439  do {
440  if (*from == '\\') {
441  *(to++) = *(from++);
442  length++;
443  }
444  *(to++) = *(from++);
445  length++;
446  } while (*from && (*from != quote));
447 
448  if (*from != quote) return -1; /* not properly quoted */
449 
450  *(to++) = quote;
451  length++;
452  *to = '\0';
453 
454  return length;
455 }
456 
457 /*
458  * Copy a quoted string but without the quotes. The length
459  * returned is the number of chars written; the number of
460  * characters consumed is 2 more than this.
461  */
462 static int rad_copy_string_bare(char *to, char const *from)
463 {
464  int length = 0;
465  char quote = *from;
466 
467  from++;
468  while (*from && (*from != quote)) {
469  if (*from == '\\') {
470  *(to++) = *(from++);
471  length++;
472  }
473  *(to++) = *(from++);
474  length++;
475  }
476 
477  if (*from != quote) return -1; /* not properly quoted */
478 
479  *to = '\0';
480 
481  return length;
482 }
483 
484 
485 /*
486  * Copy a %{} string.
487  */
488 static int rad_copy_variable(char *to, char const *from)
489 {
490  int length = 0;
491  int sublen;
492 
493  *(to++) = *(from++);
494  length++;
495 
496  while (*from) {
497  switch (*from) {
498  case '"':
499  case '\'':
500  sublen = rad_copy_string(to, from);
501  if (sublen < 0) return sublen;
502  from += sublen;
503  to += sublen;
504  length += sublen;
505  break;
506 
507  case '}': /* end of variable expansion */
508  *(to++) = *(from++);
509  *to = '\0';
510  length++;
511  return length; /* proper end of variable */
512 
513  case '\\':
514  *(to++) = *(from++);
515  *(to++) = *(from++);
516  length += 2;
517  break;
518 
519  case '%': /* start of variable expansion */
520  if (from[1] == '{') {
521  *(to++) = *(from++);
522  length++;
523 
524  sublen = rad_copy_variable(to, from);
525  if (sublen < 0) return sublen;
526  from += sublen;
527  to += sublen;
528  length += sublen;
529  break;
530  } /* else FIXME: catch %%{ ?*/
531 
532  FALL_THROUGH;
533  default:
534  *(to++) = *(from++);
535  length++;
536  break;
537  }
538  } /* loop over the input string */
539 
540  /*
541  * We ended the string before a trailing '}'
542  */
543 
544  return -1;
545 }
546 
547 uint32_t rad_pps(uint32_t *past, uint32_t *present, time_t *then, struct timeval *now)
548 {
549  uint32_t pps;
550 
551  if (*then != now->tv_sec) {
552  *then = now->tv_sec;
553  *past = *present;
554  *present = 0;
555  }
556 
557  /*
558  * Bootstrap PPS by looking at a percentage of
559  * the previous PPS. This lets us take a moving
560  * count, without doing a moving average. If
561  * we're a fraction "f" (0..1) into the current
562  * second, we can get a good guess for PPS by
563  * doing:
564  *
565  * PPS = pps_now + pps_old * (1 - f)
566  *
567  * It's an instantaneous measurement, rather than
568  * a moving average. This will hopefully let it
569  * respond better to sudden spikes.
570  *
571  * Doing the calculations by thousands allows us
572  * to not overflow 2^32, AND to not underflow
573  * when we divide by USEC.
574  */
575  pps = USEC - now->tv_usec; /* useconds left in previous second */
576  pps /= 1000; /* scale to milliseconds */
577  pps *= *past; /* multiply by past count to get fraction */
578  pps /= 1000; /* scale to usec again */
579  pps += *present; /* add in current count */
580 
581  return pps;
582 }
583 
584 /** Split string into words and expand each one
585  *
586  * @param request Current request.
587  * @param cmd string to split.
588  * @param max_argc the maximum number of arguments to split into.
589  * @param argv Where to write the pointers into argv_buf.
590  * @param can_fail If false, stop processing if any of the xlat expansions fail.
591  * @param argv_buflen size of argv_buf.
592  * @param argv_buf temporary buffer we used to mangle/expand cmd.
593  * Pointers to offsets of this buffer will be written to argv.
594  * @return argc or -1 on failure.
595  */
596 
597 int rad_expand_xlat(request_t *request, char const *cmd,
598  int max_argc, char const *argv[], bool can_fail,
599  size_t argv_buflen, char *argv_buf)
600 {
601  char const *from;
602  char *to;
603  int argc = -1;
604  int i;
605  int left;
606 
607  if (strlen(cmd) > (argv_buflen - 1)) {
608  fr_strerror_const("Expansion string is too long for output buffer");
609  return -1;
610  }
611 
612  /*
613  * Check for bad escapes.
614  */
615  if (cmd[strlen(cmd) - 1] == '\\') {
616  fr_strerror_const("Expansion string ends with a trailing backslash - invalid escape sequence");
617  return -1;
618  }
619 
620  strlcpy(argv_buf, cmd, argv_buflen);
621 
622  /*
623  * Split the string into argv's BEFORE doing xlat_eval...
624  */
625  from = cmd;
626  to = argv_buf;
627  argc = 0;
628  while (*from) {
629  int length;
630 
631  fr_skip_whitespace(from);
632 
633  argv[argc] = to;
634  argc++;
635 
636  if (argc >= (max_argc - 1)) break;
637 
638  /*
639  * Copy the argv over to our buffer.
640  */
641  while (*from && (*from != ' ') && (*from != '\t')) {
642  if (to >= argv_buf + argv_buflen - 1) {
643  fr_strerror_const("Expansion string is too long for output buffer");
644  return -1;
645  }
646 
647  switch (*from) {
648  case '"':
649  case '\'':
650  length = rad_copy_string_bare(to, from);
651  if (length < 0) {
652  fr_strerror_const("Invalid quoted string in expansion");
653  return -1;
654  }
655  from += length+2;
656  to += length;
657  break;
658 
659  case '%':
660  if (from[1] == '{') {
661  *(to++) = *(from++);
662 
663  length = rad_copy_variable(to, from);
664  if (length < 0) {
665  fr_strerror_const("Invalid variable in expansion");
666  return -1;
667  }
668  from += length;
669  to += length;
670  } else { /* FIXME: catch %%{ ? */
671  *(to++) = *(from++);
672  }
673  break;
674 
675  case '\\':
676  if (from[1] == ' ') from++;
677  FALL_THROUGH;
678 
679  default:
680  *(to++) = *(from++);
681  }
682  } /* end of string, or found a space */
683 
684  *(to++) = '\0'; /* terminate the string */
685  }
686 
687  /*
688  * We have to have SOMETHING, at least.
689  */
690  if (argc <= 0) {
691  fr_strerror_const("Expansion string is empty");
692  return -1;
693  }
694 
695  /*
696  * Expand each string, as appropriate.
697  */
698  left = argv_buf + argv_buflen - to;
699  for (i = 0; i < argc; i++) {
700  int sublen;
701 
702  /*
703  * Don't touch argv's which won't be translated.
704  */
705  if (strchr(argv[i], '%') == NULL) continue;
706 
707  if (!request) continue;
708 
709  sublen = xlat_eval(to, left - 1, request, argv[i], NULL, NULL);
710  if (sublen <= 0) {
711  if (can_fail) {
712  /*
713  * Fail to be backwards compatible.
714  *
715  * It's yucky, but it won't break anything,
716  * and it won't cause security problems.
717  */
718  sublen = 0;
719  } else {
720  fr_strerror_const("Failed expanding substring");
721  return -1;
722  }
723  }
724 
725  argv[i] = to;
726  to += sublen;
727  *(to++) = '\0';
728  left -= sublen;
729  left--;
730 
731  if (left <= 0) {
732  fr_strerror_const("Ran out of space while expanding arguments");
733  return -1;
734  }
735  }
736  argv[argc] = NULL;
737 
738  return argc;
739 }
740 
741 #ifdef HAVE_SETUID
742 static bool doing_setuid = false;
743 static uid_t suid_down_uid = (uid_t)-1;
744 
745 /** Set the uid and gid used when dropping privileges
746  *
747  * @note if this function hasn't been called, rad_suid_down will have no effect.
748  *
749  * @param uid to drop down to.
750  */
751 void rad_suid_set_down_uid(uid_t uid)
752 {
753  suid_down_uid = uid;
754  doing_setuid = true;
755 }
756 
757 # if defined(HAVE_SETRESUID) && defined (HAVE_GETRESUID)
758 void rad_suid_up(void)
759 {
760  uid_t ruid, euid, suid;
761 
762  if (getresuid(&ruid, &euid, &suid) < 0) {
763  ERROR("Failed getting saved UID's");
764  fr_exit_now(EXIT_FAILURE);
765  }
766 
767  if (setresuid(-1, suid, -1) < 0) {
768  ERROR("Failed switching to privileged user");
769  fr_exit_now(EXIT_FAILURE);
770  }
771 
772  if (geteuid() != suid) {
773  ERROR("Switched to unknown UID");
774  fr_exit_now(EXIT_FAILURE);
775  }
776 }
777 
778 void rad_suid_down(void)
779 {
780  if (!doing_setuid) return;
781 
782  if (setresuid(-1, suid_down_uid, geteuid()) < 0) {
783  struct passwd *passwd;
784  char const *name;
785 
786  name = (fr_perm_getpwuid(NULL, &passwd, suid_down_uid) < 0) ? "unknown" : passwd->pw_name;
787  ERROR("Failed switching to uid %s: %s", name, fr_syserror(errno));
788  talloc_free(passwd);
789  fr_exit_now(EXIT_FAILURE);
790  }
791 
792  if (geteuid() != suid_down_uid) {
793  ERROR("Failed switching uid: UID is incorrect");
794  fr_exit_now(EXIT_FAILURE);
795  }
796 
798 }
799 
800 void rad_suid_down_permanent(void)
801 {
802  if (!doing_setuid) return;
803 
804  if (setresuid(suid_down_uid, suid_down_uid, suid_down_uid) < 0) {
805  struct passwd *passwd;
806  char const *name;
807 
808  name = (fr_perm_getpwuid(NULL, &passwd, suid_down_uid) < 0) ? "unknown" : passwd->pw_name;
809  ERROR("Failed in permanent switch to uid %s: %s", name, fr_syserror(errno));
810  talloc_free(passwd);
811  fr_exit_now(EXIT_FAILURE);
812  }
813 
814  if (geteuid() != suid_down_uid) {
815  ERROR("Switched to unknown uid");
816  fr_exit_now(EXIT_FAILURE);
817  }
818 
820 
821  suid_down_permanent = true;
822 }
823 # else
824 /*
825  * Much less secure...
826  */
827 void rad_suid_up(void)
828 {
829  if (!doing_setuid) return;
830 
831  if (seteuid(0) < 0) {
832  ERROR("Failed switching up to euid 0: %s", fr_syserror(errno));
833  fr_exit_now(EXIT_FAILURE);
834  }
835 
836 }
837 
838 void rad_suid_down(void)
839 {
840  if (!doing_setuid) return;
841 
842  if (geteuid() == suid_down_uid) return;
843 
844  if (seteuid(suid_down_uid) < 0) {
845  struct passwd *passwd;
846  char const *name;
847 
848  name = (fr_perm_getpwuid(NULL, &passwd, suid_down_uid) < 0) ? "unknown": passwd->pw_name;
849  ERROR("Failed switching to euid %s: %s", name, fr_syserror(errno));
850  talloc_free(passwd);
851  fr_exit_now(EXIT_FAILURE);
852  }
853 
855 }
856 
857 void rad_suid_down_permanent(void)
858 {
859  if (!doing_setuid) return;
860 
861  /*
862  * Already done. Don't do anything else.
863  */
864  if (getuid() == suid_down_uid) return;
865 
866  /*
867  * We're root, but running as a normal user. Fix that,
868  * so we can call setuid().
869  */
870  if (geteuid() == suid_down_uid) {
871  rad_suid_up();
872  }
873 
874  if (setuid(suid_down_uid) < 0) {
875  struct passwd *passwd;
876  char const *name;
877 
878  name = (fr_perm_getpwuid(NULL, &passwd, suid_down_uid) < 0) ? "unknown": passwd->pw_name;
879  ERROR("Failed switching permanently to uid %s: %s", name, fr_syserror(errno));
880  talloc_free(passwd);
881  fr_exit_now(EXIT_FAILURE);
882  }
883 
885 
886  suid_down_permanent = true;
887 }
888 # endif /* HAVE_SETRESUID && HAVE_GETRESUID */
889 #else /* HAVE_SETUID */
890 void rad_suid_set_down_uid(uid_t uid)
891 {
892 }
893 
894 void rad_suid_up(void)
895 {
896 }
897 
898 void rad_suid_down(void)
899 {
901 }
902 
904 {
906 }
907 #endif /* HAVE_SETUID */
908 
909 /** Return whether we've permanently dropped root privileges
910  *
911  * @return
912  * - true if root privileges have been dropped.
913  * - false if root privileges have not been dropped.
914  */
916 {
917  return suid_down_permanent;
918 }
919 
920 /** Alter the effective user id
921  *
922  * @param uid to set
923  * @return
924  * - 0 on success.
925  * - -1 on failure.
926  */
927 int rad_seuid(uid_t uid)
928 {
929  if (seteuid(uid) < 0) {
930  int sete_errno = errno; /* errno sets overwritten by fr_perm_getpwuid */
931  struct passwd *passwd;
932 
933  if (fr_perm_getpwuid(NULL, &passwd, uid) < 0) return -1;
934  fr_strerror_printf("%s", fr_syserror(sete_errno));
935  talloc_free(passwd);
936 
937  return -1;
938  }
939  return 0;
940 }
941 
942 /** Alter the effective user id
943  *
944  * @param gid to set
945  * @return
946  * - 0 on success.
947  * - -1 on failure.
948  */
949 int rad_segid(gid_t gid)
950 {
951  if (setegid(gid) < 0) {
952  int sete_errno = errno; /* errno sets overwritten by fr_perm_getgrgid */
953  struct group *group;
954 
955  if (fr_perm_getgrgid(NULL, &group, gid) < 0) return -1;
956  fr_strerror_printf("%s", fr_syserror(sete_errno));
957  talloc_free(group);
958 
959  return -1;
960  }
961  return 0;
962 }
#define fr_base16_encode(_out, _in)
Definition: base16.h:57
#define fr_base16_decode(_err, _out, _in, _no_trailing)
Definition: base16.h:95
#define RCSID(id)
Definition: build.h:444
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition: build.h:320
#define UNUSED
Definition: build.h:313
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition: dbuff.h:509
int fr_reset_dumpable(void)
Reset dumpable state to previously configured value.
Definition: debug.c:873
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
Definition: debug.h:232
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
static fr_slen_t in
Definition: dict.h:645
void rad_suid_up(void)
Definition: util.c:894
bool rad_suid_is_down_permanent(void)
Return whether we've permanently dropped root privileges.
Definition: util.c:915
static int rad_copy_string(char *to, char const *from)
Definition: util.c:434
size_t rad_filename_make_safe(UNUSED request_t *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
Ensures that a filename cannot walk up the directory structure.
Definition: util.c:94
static int rad_copy_variable(char *to, char const *from)
Definition: util.c:488
void(*)(int) reset_signal(int signo, void(*func)(int))
Definition: util.c:53
int rad_segid(gid_t gid)
Alter the effective user id.
Definition: util.c:949
uint32_t rad_pps(uint32_t *past, uint32_t *present, time_t *then, struct timeval *now)
Definition: util.c:547
int rad_filename_box_escape(fr_value_box_t *vb, UNUSED void *uxtc)
Definition: util.c:290
char * rad_ajoin(TALLOC_CTX *ctx, char const **argv, int argc, char c)
talloc a buffer to hold the concatenated value of all elements of argv
Definition: util.c:396
ssize_t rad_filename_unescape(char *out, size_t outlen, char const *in, size_t inlen)
Converts data stored in a file name back to its original form.
Definition: util.c:334
static bool suid_down_permanent
Record whether we've permanently dropped privilledges.
Definition: util.c:39
void rad_suid_down(void)
Definition: util.c:898
int rad_filename_box_make_safe(fr_value_box_t *vb, UNUSED void *uxtc)
Definition: util.c:165
int rad_expand_xlat(request_t *request, char const *cmd, int max_argc, char const *argv[], bool can_fail, size_t argv_buflen, char *argv_buf)
Split string into words and expand each one.
Definition: util.c:597
size_t rad_filename_escape(UNUSED request_t *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
Escapes the raw string such that it should be safe to use as part of a file path.
Definition: util.c:214
void rad_suid_down_permanent(void)
Definition: util.c:903
static int rad_copy_string_bare(char *to, char const *from)
Definition: util.c:462
int rad_seuid(uid_t uid)
Alter the effective user id.
Definition: util.c:927
void rad_suid_set_down_uid(uid_t uid)
Definition: util.c:890
talloc_free(reap)
unsigned int uint32_t
Definition: merged_model.c:33
long int ssize_t
Definition: merged_model.c:24
ssize_t xlat_eval(char *out, size_t outlen, request_t *request, char const *fmt, xlat_escape_legacy_t escape, void const *escape_ctx)
Definition: merged_model.c:215
unsigned char uint8_t
Definition: merged_model.c:30
#define fr_skip_whitespace(_p)
Skip whitespace ('\t', '\n', '\v', '\f', '\r', ' ')
Definition: misc.h:59
int fr_perm_getgrgid(TALLOC_CTX *ctx, struct group **out, gid_t gid)
Resolve a gid to a group database entry.
Definition: perm.c:201
int fr_perm_getpwuid(TALLOC_CTX *ctx, struct passwd **out, uid_t uid)
Resolve a uid to a passwd entry.
Definition: perm.c:75
size_t fr_utf8_char(uint8_t const *str, ssize_t inlen)
Checks for utf-8, taken from http://www.w3.org/International/questions/qa-forms-utf-8.
Definition: print.c:39
static char const * name
#define FR_SBUFF_IN(_start, _len_or_end)
#define FR_SBUFF_OUT(_start, _len_or_end)
Signals that can be sent to a request.
static char buff[sizeof("18446744073709551615")+3]
Definition: size_tests.c:41
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:689
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition: strlcpy.c:34
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: syserror.c:243
#define USEC
Definition: time.h:378
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition: strerror.h:64
#define fr_strerror_const(_msg)
Definition: strerror.h:223
void fr_value_box_clear_value(fr_value_box_t *data)
Clear/free any existing value.
Definition: value.c:3630
int fr_value_box_bstrndup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Copy a string to to a fr_value_box_t.
Definition: value.c:4097
static size_t char fr_sbuff_t size_t inlen
Definition: value.h:984
uintptr_t fr_value_box_safe_for_t
Escaping that's been applied to a value box.
Definition: value.h:155
static size_t char ** out
Definition: value.h:984