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