1 /*
2 * util.c Various utility functions.
3 *
4 * Version: $Id: b216cc9a5170f90a4ae0d02f0fb551d5bf928189 $
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 * Copyright 2000,2006 The FreeRADIUS server project
21 */
22
23 RCSID("$Id: b216cc9a5170f90a4ae0d02f0fb551d5bf928189 $")
24
25 #include <freeradius-devel/radiusd.h>
26 #include <freeradius-devel/rad_assert.h>
27
28 #include <ctype.h>
29
30 #include <sys/stat.h>
31 #include <fcntl.h>
32
33 /*
34 * The signal() function in Solaris 2.5.1 sets SA_NODEFER in
35 * sa_flags, which causes grief if signal() is called in the
36 * handler before the cause of the signal has been cleared.
37 * (Infinite recursion).
38 *
39 * The same problem appears on HPUX, so we avoid it, if we can.
40 *
41 * Using sigaction() to reset the signal handler fixes the problem,
42 * so where available, we prefer that solution.
43 */
44
reset_signal(int signo,void (* func)(int))45 void (*reset_signal(int signo, void (*func)(int)))(int)
46 {
47 #ifdef HAVE_SIGACTION
48 struct sigaction act, oact;
49
50 memset(&act, 0, sizeof(act));
51 act.sa_handler = func;
52 sigemptyset(&act.sa_mask);
53 act.sa_flags = 0;
54 #ifdef SA_INTERRUPT /* SunOS */
55 act.sa_flags |= SA_INTERRUPT;
56 #endif
57 if (sigaction(signo, &act, &oact) < 0)
58 return SIG_ERR;
59 return oact.sa_handler;
60 #else
61
62 /*
63 * re-set by calling the 'signal' function, which
64 * may cause infinite recursion and core dumps due to
65 * stack growth.
66 *
67 * However, the system is too dumb to implement sigaction(),
68 * so we don't have a choice.
69 */
70 signal(signo, func);
71
72 return NULL;
73 #endif
74 }
75
76 /*
77 * Per-request data, added by modules...
78 */
79 struct request_data_t {
80 request_data_t *next;
81
82 void *unique_ptr;
83 int unique_int;
84 void *opaque;
85 bool free_opaque;
86 };
87
88 /*
89 * Add opaque data (with a "free" function) to a REQUEST.
90 *
91 * The unique ptr is meant to be a module configuration,
92 * and the unique integer allows the caller to have multiple
93 * opaque data associated with a REQUEST.
94 */
request_data_add(REQUEST * request,void * unique_ptr,int unique_int,void * opaque,bool free_opaque)95 int request_data_add(REQUEST *request, void *unique_ptr, int unique_int, void *opaque, bool free_opaque)
96 {
97 request_data_t *this, **last, *next;
98
99 /*
100 * Some simple sanity checks.
101 */
102 if (!request || !opaque) return -1;
103
104 this = next = NULL;
105 for (last = &(request->data);
106 *last != NULL;
107 last = &((*last)->next)) {
108 if (((*last)->unique_ptr == unique_ptr) &&
109 ((*last)->unique_int == unique_int)) {
110 this = *last;
111 next = this->next;
112
113 /*
114 * If caller requires custom behaviour on free
115 * they must set a destructor.
116 */
117 if (this->opaque && this->free_opaque) talloc_free(this->opaque);
118
119 break; /* replace the existing entry */
120 }
121 }
122
123 /*
124 * Only alloc new memory if we're not replacing
125 * an existing entry.
126 */
127 if (!this) this = talloc_zero(request, request_data_t);
128 if (!this) return -1;
129
130 this->next = next;
131 this->unique_ptr = unique_ptr;
132 this->unique_int = unique_int;
133 this->opaque = opaque;
134 this->free_opaque = free_opaque;
135
136 *last = this;
137
138 return 0;
139 }
140
141 /*
142 * Get opaque data from a request.
143 */
request_data_get(REQUEST * request,void * unique_ptr,int unique_int)144 void *request_data_get(REQUEST *request, void *unique_ptr, int unique_int)
145 {
146 request_data_t **last;
147
148 if (!request) return NULL;
149
150 for (last = &(request->data);
151 *last != NULL;
152 last = &((*last)->next)) {
153 if (((*last)->unique_ptr == unique_ptr) &&
154 ((*last)->unique_int == unique_int)) {
155 request_data_t *this;
156 void *ptr;
157
158 this = *last;
159 ptr = this->opaque;
160
161 /*
162 * Remove the entry from the list, and free it.
163 */
164 *last = this->next;
165 talloc_free(this);
166
167 return ptr; /* don't free it, the caller does that */
168 }
169 }
170
171 return NULL; /* wasn't found, too bad... */
172 }
173
174 /*
175 * Get opaque data from a request without removing it.
176 */
request_data_reference(REQUEST * request,void * unique_ptr,int unique_int)177 void *request_data_reference(REQUEST *request, void *unique_ptr, int unique_int)
178 {
179 request_data_t **last;
180
181 for (last = &(request->data);
182 *last != NULL;
183 last = &((*last)->next)) {
184 if (((*last)->unique_ptr == unique_ptr) &&
185 ((*last)->unique_int == unique_int)) {
186 return (*last)->opaque;
187 }
188 }
189
190 return NULL; /* wasn't found, too bad... */
191 }
192
193 /** Create possibly many directories.
194 *
195 * @note that the input directory name is NOT treated as a constant. This is so that
196 * if an error is returned, the 'directory' ptr points to the name of the file
197 * which caused the error.
198 *
199 * @param dir path to directory to create.
200 * @param mode for new directories.
201 * @param uid to set on new directories, may be -1 to use effective uid.
202 * @param gid to set on new directories, may be -1 to use effective gid.
203 * @return 0 on success, -1 on error. Error available as errno.
204 */
rad_mkdir(char * dir,mode_t mode,uid_t uid,gid_t gid)205 int rad_mkdir(char *dir, mode_t mode, uid_t uid, gid_t gid)
206 {
207 int rcode, fd;
208 char *p;
209
210 /*
211 * Try to make the dir. If it exists, chmod it.
212 * If a path doesn't exist, that's OK. Otherwise
213 * return with an error.
214 *
215 * Directories permissions are initially set so
216 * that only we should have access. This prevents
217 * an attacker removing them and swapping them
218 * out for a link to somewhere else.
219 * We change them to the correct permissions later.
220 */
221 rcode = mkdir(dir, 0700);
222 if (rcode < 0) {
223 switch (errno) {
224 case EEXIST:
225 return 0; /* don't change permissions */
226
227 case ENOENT:
228 break;
229
230 default:
231 return rcode;
232 }
233
234 /*
235 * A component in the dir path doesn't
236 * exist. Look for the LAST dir name. Try
237 * to create that. If there's an error, we leave
238 * the dir path as the one at which the
239 * error occured.
240 */
241 p = strrchr(dir, FR_DIR_SEP);
242 if (!p || (p == dir)) return -1;
243
244 *p = '\0';
245 rcode = rad_mkdir(dir, mode, uid, gid);
246 if (rcode < 0) return rcode;
247
248 /*
249 * Reset the dir path, and try again to
250 * make the dir.
251 */
252 *p = FR_DIR_SEP;
253 rcode = mkdir(dir, 0700);
254 if (rcode < 0) return rcode;
255 } /* else we successfully created the dir */
256
257 /*
258 * Set the permissions on the directory we created
259 * this should never fail unless there's a race.
260 */
261 fd = open(dir, O_DIRECTORY);
262 if (fd < 0) return -1;
263
264 rcode = fchmod(fd, mode);
265 if (rcode < 0) {
266 close(fd);
267 return rcode;
268 }
269
270 if ((uid != (uid_t)-1) || (gid != (gid_t)-1)) {
271 rad_suid_up();
272 rcode = fchown(fd, uid, gid);
273 rad_suid_down();
274 }
275 close(fd);
276
277 return rcode;
278 }
279
280 /** Ensures that a filename cannot walk up the directory structure
281 *
282 * Also sanitizes control chars.
283 *
284 * @param request Current request (may be NULL).
285 * @param out Output buffer.
286 * @param outlen Size of the output buffer.
287 * @param in string to escape.
288 * @param arg Context arguments (unused, should be NULL).
289 */
rad_filename_make_safe(UNUSED REQUEST * request,char * out,size_t outlen,char const * in,UNUSED void * arg)290 size_t rad_filename_make_safe(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
291 {
292 char const *q = in;
293 char *p = out;
294 size_t left = outlen;
295
296 while (*q) {
297 if (*q != '/') {
298 if (left < 2) break;
299
300 /*
301 * Smash control characters and spaces to
302 * something simpler.
303 */
304 if (*q < ' ') {
305 *(p++) = '_';
306 q++;
307 continue;
308 }
309
310 *(p++) = *(q++);
311 left--;
312 continue;
313 }
314
315 /*
316 * For now, allow slashes in the expanded
317 * filename. This allows the admin to set
318 * attributes which create sub-directories.
319 * Unfortunately, it also allows users to send
320 * attributes which *may* end up creating
321 * sub-directories.
322 */
323 if (left < 2) break;
324 *(p++) = *(q++);
325
326 /*
327 * Get rid of ////../.././///.///..//
328 */
329 redo:
330 /*
331 * Get rid of ////
332 */
333 if (*q == '/') {
334 q++;
335 goto redo;
336 }
337
338 /*
339 * Get rid of /./././
340 */
341 if ((q[0] == '.') &&
342 (q[1] == '/')) {
343 q += 2;
344 goto redo;
345 }
346
347 /*
348 * Get rid of /../../../
349 */
350 if ((q[0] == '.') && (q[1] == '.') &&
351 (q[2] == '/')) {
352 q += 3;
353 goto redo;
354 }
355 }
356 *p = '\0';
357
358 return (p - out);
359 }
360
361 /** Escapes the raw string such that it should be safe to use as part of a file path
362 *
363 * This function is designed to produce a string that's still readable but portable
364 * across the majority of file systems.
365 *
366 * For security reasons it cannot remove characters from the name, and must not allow
367 * collisions to occur between different strings.
368 *
369 * With that in mind '-' has been chosen as the escape character, and will be double
370 * escaped '-' -> '--' to avoid collisions.
371 *
372 * Escaping should be reversible if the original string needs to be extracted.
373 *
374 * @note function takes additional arguments so that it may be used as an xlat escape
375 * function but it's fine to call it directly.
376 *
377 * @note OSX/Unix/NTFS/VFAT have a max filename size of 255 bytes.
378 *
379 * @param request Current request (may be NULL).
380 * @param out Output buffer.
381 * @param outlen Size of the output buffer.
382 * @param in string to escape.
383 * @param arg Context arguments (unused, should be NULL).
384 */
rad_filename_escape(UNUSED REQUEST * request,char * out,size_t outlen,char const * in,UNUSED void * arg)385 size_t rad_filename_escape(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
386 {
387 size_t freespace = outlen;
388
389 while (*in != '\0') {
390 size_t utf8_len;
391
392 /*
393 * Encode multibyte UTF8 chars
394 */
395 utf8_len = fr_utf8_char((uint8_t const *) in, -1);
396 if (utf8_len > 1) {
397 if (freespace <= (utf8_len * 3)) break;
398
399 switch (utf8_len) {
400 case 2:
401 snprintf(out, freespace, "-%x-%x", in[0], in[1]);
402 break;
403
404 case 3:
405 snprintf(out, freespace, "-%x-%x-%x", in[0], in[1], in[2]);
406 break;
407
408 case 4:
409 snprintf(out, freespace, "-%x-%x-%x-%x", in[0], in[1], in[2], in[3]);
410 break;
411 }
412
413 freespace -= (utf8_len * 3);
414 out += (utf8_len * 3);
415 in += utf8_len;
416
417 continue;
418 }
419
420 /*
421 * Safe chars
422 */
423 if (((*in >= 'A') && (*in <= 'Z')) ||
424 ((*in >= 'a') && (*in <= 'z')) ||
425 ((*in >= '0') && (*in <= '9')) ||
426 (*in == '_')) {
427 if (freespace <= 1) break;
428
429 *out++ = *in++;
430 freespace--;
431 continue;
432 }
433 if (freespace <= 2) break;
434
435 /*
436 * Double escape '-' (like \\)
437 */
438 if (*in == '-') {
439 *out++ = '-';
440 *out++ = '-';
441
442 freespace -= 2;
443 in++;
444 continue;
445 }
446
447 /*
448 * Unsafe chars
449 */
450 *out++ = '-';
451 fr_bin2hex(out, (uint8_t const *)in++, 1);
452 out += 2;
453 freespace -= 3;
454 }
455 *out = '\0';
456
457 return outlen - freespace;
458 }
459
460 /** Converts data stored in a file name back to its original form
461 *
462 * @param out Where to write the unescaped string (may be the same as in).
463 * @param outlen Length of the output buffer.
464 * @param in Input filename.
465 * @param inlen Length of input.
466 * @return number of bytes written to output buffer, or offset where parse error
467 * occurred on failure.
468 */
rad_filename_unescape(char * out,size_t outlen,char const * in,size_t inlen)469 ssize_t rad_filename_unescape(char *out, size_t outlen, char const *in, size_t inlen)
470 {
471 char const *p, *end = in + inlen;
472 size_t freespace = outlen;
473
474 for (p = in; p < end; p++) {
475 if (freespace <= 1) break;
476
477 if (((*p >= 'A') && (*p <= 'Z')) ||
478 ((*p >= 'a') && (*p <= 'z')) ||
479 ((*p >= '0') && (*p <= '9')) ||
480 (*p == '_')) {
481 *out++ = *p;
482 freespace--;
483 continue;
484 }
485
486 if (p[0] == '-') {
487 /*
488 * End of input, '-' needs at least one extra char after
489 * it to be valid.
490 */
491 if ((end - p) < 2) return in - p;
492 if (p[1] == '-') {
493 p++;
494 *out++ = '-';
495 freespace--;
496 continue;
497 }
498
499 /*
500 * End of input, '-' must be followed by <hex><hex>
501 * but there aren't enough chars left
502 */
503 if ((end - p) < 3) return in - p;
504
505 /*
506 * If hex2bin returns 0 the next two chars weren't hexits.
507 */
508 if (fr_hex2bin((uint8_t *) out, 1, in, 1) == 0) return in - (p + 1);
509 in += 2;
510 out++;
511 freespace--;
512 }
513
514 return in - p; /* offset we found the bad char at */
515 }
516 *out = '\0';
517
518 return outlen - freespace; /* how many bytes were written */
519 }
520
521 /*
522 * Allocate memory, or exit.
523 *
524 * This call ALWAYS succeeds!
525 */
rad_malloc(size_t size)526 void *rad_malloc(size_t size)
527 {
528 void *ptr = malloc(size);
529
530 if (ptr == NULL) {
531 ERROR("no memory");
532 fr_exit(1);
533 }
534
535 return ptr;
536 }
537
538
rad_const_free(void const * ptr)539 void rad_const_free(void const *ptr)
540 {
541 void *tmp;
542 if (!ptr) return;
543
544 memcpy(&tmp, &ptr, sizeof(tmp));
545 talloc_free(tmp);
546 }
547
548
549 /*
550 * Logs an error message and aborts the program
551 *
552 */
553
rad_assert_fail(char const * file,unsigned int line,char const * expr)554 void NEVER_RETURNS rad_assert_fail(char const *file, unsigned int line, char const *expr)
555 {
556 ERROR("ASSERT FAILED %s[%u]: %s", file, line, expr);
557 fr_fault(SIGABRT);
558 fr_exit_now(1);
559 }
560
561 /*
562 * Free a REQUEST struct.
563 */
_request_free(REQUEST * request)564 static int _request_free(REQUEST *request)
565 {
566 rad_assert(!request->in_request_hash);
567 #ifdef WITH_PROXY
568 rad_assert(!request->in_proxy_hash);
569 #endif
570 rad_assert(!request->ev);
571
572 #ifdef WITH_COA
573 rad_assert(request->coa == NULL);
574 #endif
575
576 #ifndef NDEBUG
577 request->magic = 0x01020304; /* set the request to be nonsense */
578 #endif
579 request->client = NULL;
580 #ifdef WITH_PROXY
581 request->home_server = NULL;
582 #endif
583
584 /*
585 * This is parented separately.
586 */
587 if (request->state_ctx) {
588 talloc_free(request->state_ctx);
589 }
590
591 return 0;
592 }
593
594 /*
595 * Create a new REQUEST data structure.
596 */
request_alloc(TALLOC_CTX * ctx)597 REQUEST *request_alloc(TALLOC_CTX *ctx)
598 {
599 REQUEST *request;
600
601 request = talloc_zero(ctx, REQUEST);
602 if (!request) return NULL;
603 talloc_set_destructor(request, _request_free);
604 #ifndef NDEBUG
605 request->magic = REQUEST_MAGIC;
606 #endif
607 #ifdef WITH_PROXY
608 request->proxy = NULL;
609 #endif
610 request->reply = NULL;
611 #ifdef WITH_PROXY
612 request->proxy_reply = NULL;
613 #endif
614 request->config = NULL;
615 request->username = NULL;
616 request->password = NULL;
617 request->timestamp = time(NULL);
618 request->log.lvl = rad_debug_lvl; /* Default to global debug level */
619
620 request->module = "";
621 request->component = "<core>";
622 request->log.func = vradlog_request;
623
624 request->state_ctx = talloc_init("session-state");
625
626 return request;
627 }
628
629
630 /*
631 * Create a new REQUEST, based on an old one.
632 *
633 * This function allows modules to inject fake requests
634 * into the server, for tunneled protocols like TTLS & PEAP.
635 */
request_alloc_fake(REQUEST * request)636 REQUEST *request_alloc_fake(REQUEST *request)
637 {
638 REQUEST *fake;
639
640 fake = request_alloc(request);
641 if (!fake) return NULL;
642
643 fake->number = request->number;
644 #ifdef HAVE_PTHREAD_H
645 fake->child_pid = request->child_pid;
646 #endif
647 fake->parent = request;
648 fake->root = request->root;
649 fake->client = request->client;
650
651 /*
652 * For new server support.
653 *
654 * FIXME: Key instead off of a "virtual server" data structure.
655 *
656 * FIXME: Permit different servers for inner && outer sessions?
657 */
658 fake->server = request->server;
659
660 fake->packet = rad_alloc(fake, true);
661 if (!fake->packet) {
662 talloc_free(fake);
663 return NULL;
664 }
665
666 fake->reply = rad_alloc(fake, false);
667 if (!fake->reply) {
668 talloc_free(fake);
669 return NULL;
670 }
671
672 fake->master_state = REQUEST_ACTIVE;
673 fake->child_state = REQUEST_RUNNING;
674
675 /*
676 * Fill in the fake request.
677 */
678 fake->packet->sockfd = -1;
679 fake->packet->src_ipaddr = request->packet->src_ipaddr;
680 fake->packet->src_port = request->packet->src_port;
681 fake->packet->dst_ipaddr = request->packet->dst_ipaddr;
682 fake->packet->dst_port = 0;
683
684 /*
685 * This isn't STRICTLY required, as the fake request MUST NEVER
686 * be put into the request list. However, it's still reasonable
687 * practice.
688 */
689 fake->packet->id = fake->number & 0xff;
690 fake->packet->code = request->packet->code;
691 fake->timestamp = request->timestamp;
692 fake->packet->timestamp = request->packet->timestamp;
693
694 /*
695 * Required for new identity support
696 */
697 fake->listener = request->listener;
698
699 /*
700 * Fill in the fake reply, based on the fake request.
701 */
702 fake->reply->sockfd = fake->packet->sockfd;
703 fake->reply->src_ipaddr = fake->packet->dst_ipaddr;
704 fake->reply->src_port = fake->packet->dst_port;
705 fake->reply->dst_ipaddr = fake->packet->src_ipaddr;
706 fake->reply->dst_port = fake->packet->src_port;
707 fake->reply->id = fake->packet->id;
708 fake->reply->code = 0; /* UNKNOWN code */
709
710 /*
711 * Copy debug information.
712 */
713 memcpy(&(fake->log), &(request->log), sizeof(fake->log));
714 fake->log.indent = 0; /* Apart from the indent which we reset */
715
716 return fake;
717 }
718
719 #ifdef WITH_COA
null_handler(UNUSED REQUEST * request)720 static int null_handler(UNUSED REQUEST *request)
721 {
722 return 0;
723 }
724
request_alloc_coa(REQUEST * request)725 REQUEST *request_alloc_coa(REQUEST *request)
726 {
727 if (!request || request->coa) return NULL;
728
729 /*
730 * Originate CoA requests only when necessary.
731 */
732 if ((request->packet->code != PW_CODE_ACCESS_REQUEST) &&
733 (request->packet->code != PW_CODE_ACCOUNTING_REQUEST)) return NULL;
734
735 request->coa = request_alloc_fake(request);
736 if (!request->coa) return NULL;
737
738 request->coa->handle = null_handler;
739 request->coa->options = RAD_REQUEST_OPTION_COA; /* is a CoA packet */
740 request->coa->packet->code = 0; /* unknown, as of yet */
741 request->coa->child_state = REQUEST_RUNNING;
742 request->coa->proxy = rad_alloc(request->coa, false);
743 if (!request->coa->proxy) {
744 TALLOC_FREE(request->coa);
745 return NULL;
746 }
747
748 return request->coa;
749 }
750 #endif
751
752 /*
753 * Copy a quoted string.
754 */
rad_copy_string(char * to,char const * from)755 int rad_copy_string(char *to, char const *from)
756 {
757 int length = 0;
758 char quote = *from;
759
760 do {
761 if (*from == '\\') {
762 *(to++) = *(from++);
763 length++;
764 }
765 *(to++) = *(from++);
766 length++;
767 } while (*from && (*from != quote));
768
769 if (*from != quote) return -1; /* not properly quoted */
770
771 *(to++) = quote;
772 length++;
773 *to = '\0';
774
775 return length;
776 }
777
778 /*
779 * Copy a quoted string but without the quotes. The length
780 * returned is the number of chars written; the number of
781 * characters consumed is 2 more than this.
782 */
rad_copy_string_bare(char * to,char const * from)783 int rad_copy_string_bare(char *to, char const *from)
784 {
785 int length = 0;
786 char quote = *from;
787
788 from++;
789 while (*from && (*from != quote)) {
790 if (*from == '\\') {
791 *(to++) = *(from++);
792 length++;
793 }
794 *(to++) = *(from++);
795 length++;
796 }
797
798 if (*from != quote) return -1; /* not properly quoted */
799
800 *to = '\0';
801
802 return length;
803 }
804
805
806 /*
807 * Copy a %{} string.
808 */
rad_copy_variable(char * to,char const * from)809 int rad_copy_variable(char *to, char const *from)
810 {
811 int length = 0;
812 int sublen;
813
814 *(to++) = *(from++);
815 length++;
816
817 while (*from) {
818 switch (*from) {
819 case '"':
820 case '\'':
821 sublen = rad_copy_string(to, from);
822 if (sublen < 0) return sublen;
823 from += sublen;
824 to += sublen;
825 length += sublen;
826 break;
827
828 case '}': /* end of variable expansion */
829 *(to++) = *(from++);
830 *to = '\0';
831 length++;
832 return length; /* proper end of variable */
833
834 case '\\':
835 *(to++) = *(from++);
836 *(to++) = *(from++);
837 length += 2;
838 break;
839
840 case '%': /* start of variable expansion */
841 if (from[1] == '{') {
842 *(to++) = *(from++);
843 length++;
844
845 sublen = rad_copy_variable(to, from);
846 if (sublen < 0) return sublen;
847 from += sublen;
848 to += sublen;
849 length += sublen;
850 break;
851 } /* else FIXME: catch %%{ ?*/
852
853 /* FALL-THROUGH */
854 default:
855 *(to++) = *(from++);
856 length++;
857 break;
858 }
859 } /* loop over the input string */
860
861 /*
862 * We ended the string before a trailing '}'
863 */
864
865 return -1;
866 }
867
868 #ifndef USEC
869 #define USEC 1000000
870 #endif
871
rad_pps(uint32_t * past,uint32_t * present,time_t * then,struct timeval * now)872 uint32_t rad_pps(uint32_t *past, uint32_t *present, time_t *then, struct timeval *now)
873 {
874 uint32_t pps;
875
876 if (*then != now->tv_sec) {
877 *then = now->tv_sec;
878 *past = *present;
879 *present = 0;
880 }
881
882 /*
883 * Bootstrap PPS by looking at a percentage of
884 * the previous PPS. This lets us take a moving
885 * count, without doing a moving average. If
886 * we're a fraction "f" (0..1) into the current
887 * second, we can get a good guess for PPS by
888 * doing:
889 *
890 * PPS = pps_now + pps_old * (1 - f)
891 *
892 * It's an instantaneous measurement, rather than
893 * a moving average. This will hopefully let it
894 * respond better to sudden spikes.
895 *
896 * Doing the calculations by thousands allows us
897 * to not overflow 2^32, AND to not underflow
898 * when we divide by USEC.
899 */
900 pps = USEC - now->tv_usec; /* useconds left in previous second */
901 pps /= 1000; /* scale to milliseconds */
902 pps *= *past; /* multiply by past count to get fraction */
903 pps /= 1000; /* scale to usec again */
904 pps += *present; /* add in current count */
905
906 return pps;
907 }
908
909 /** Split string into words and expand each one
910 *
911 * @param request Current request.
912 * @param cmd string to split.
913 * @param max_argc the maximum number of arguments to split into.
914 * @param argv Where to write the pointers into argv_buf.
915 * @param can_fail If false, stop processing if any of the xlat expansions fail.
916 * @param argv_buflen size of argv_buf.
917 * @param argv_buf temporary buffer we used to mangle/expand cmd.
918 * Pointers to offsets of this buffer will be written to argv.
919 * @return argc or -1 on failure.
920 */
921
rad_expand_xlat(REQUEST * request,char const * cmd,int max_argc,char const * argv[],bool can_fail,size_t argv_buflen,char * argv_buf)922 int rad_expand_xlat(REQUEST *request, char const *cmd,
923 int max_argc, char const *argv[], bool can_fail,
924 size_t argv_buflen, char *argv_buf)
925 {
926 char const *from;
927 char *to;
928 int argc = -1;
929 int i;
930 int left;
931
932 if (strlen(cmd) > (argv_buflen - 1)) {
933 ERROR("rad_expand_xlat: Command line is too long");
934 return -1;
935 }
936
937 /*
938 * Check for bad escapes.
939 */
940 if (cmd[strlen(cmd) - 1] == '\\') {
941 ERROR("rad_expand_xlat: Command line has final backslash, without a following character");
942 return -1;
943 }
944
945 strlcpy(argv_buf, cmd, argv_buflen);
946
947 /*
948 * Split the string into argv's BEFORE doing radius_xlat...
949 */
950 from = cmd;
951 to = argv_buf;
952 argc = 0;
953 while (*from) {
954 int length;
955
956 /*
957 * Skip spaces.
958 */
959 if ((*from == ' ') || (*from == '\t')) {
960 from++;
961 continue;
962 }
963
964 argv[argc] = to;
965 argc++;
966
967 if (argc >= (max_argc - 1)) break;
968
969 /*
970 * Copy the argv over to our buffer.
971 */
972 while (*from && (*from != ' ') && (*from != '\t')) {
973 if (to >= argv_buf + argv_buflen - 1) {
974 ERROR("rad_expand_xlat: Ran out of space in command line");
975 return -1;
976 }
977
978 switch (*from) {
979 case '"':
980 case '\'':
981 length = rad_copy_string_bare(to, from);
982 if (length < 0) {
983 ERROR("rad_expand_xlat: Invalid string passed as argument");
984 return -1;
985 }
986 from += length+2;
987 to += length;
988 break;
989
990 case '%':
991 if (from[1] == '{') {
992 *(to++) = *(from++);
993
994 length = rad_copy_variable(to, from);
995 if (length < 0) {
996 ERROR("rad_expand_xlat: Invalid variable expansion passed as argument");
997 return -1;
998 }
999 from += length;
1000 to += length;
1001 } else { /* FIXME: catch %%{ ? */
1002 *(to++) = *(from++);
1003 }
1004 break;
1005
1006 case '\\':
1007 if (from[1] == ' ') from++;
1008 /* FALL-THROUGH */
1009
1010 default:
1011 *(to++) = *(from++);
1012 }
1013 } /* end of string, or found a space */
1014
1015 *(to++) = '\0'; /* terminate the string */
1016 }
1017
1018 /*
1019 * We have to have SOMETHING, at least.
1020 */
1021 if (argc <= 0) {
1022 ERROR("rad_expand_xlat: Empty command line");
1023 return -1;
1024 }
1025
1026 /*
1027 * Expand each string, as appropriate.
1028 */
1029 left = argv_buf + argv_buflen - to;
1030 for (i = 0; i < argc; i++) {
1031 int sublen;
1032
1033 /*
1034 * Don't touch argv's which won't be translated.
1035 */
1036 if (strchr(argv[i], '%') == NULL) continue;
1037
1038 if (!request) continue;
1039
1040 sublen = radius_xlat(to, left - 1, request, argv[i], NULL, NULL);
1041 if (sublen <= 0) {
1042 if (can_fail) {
1043 /*
1044 * Fail to be backwards compatible.
1045 *
1046 * It's yucky, but it won't break anything,
1047 * and it won't cause security problems.
1048 */
1049 sublen = 0;
1050 } else {
1051 ERROR("rad_expand_xlat: xlat failed");
1052 return -1;
1053 }
1054 }
1055
1056 argv[i] = to;
1057 to += sublen;
1058 *(to++) = '\0';
1059 left -= sublen;
1060 left--;
1061
1062 if (left <= 0) {
1063 ERROR("rad_expand_xlat: Ran out of space while expanding arguments");
1064 return -1;
1065 }
1066 }
1067 argv[argc] = NULL;
1068
1069 return argc;
1070 }
1071
1072 #ifndef NDEBUG
1073 /*
1074 * Verify a packet.
1075 */
verify_packet(char const * file,int line,REQUEST * request,RADIUS_PACKET * packet,char const * name)1076 static void verify_packet(char const *file, int line, REQUEST *request, RADIUS_PACKET *packet, char const *name)
1077 {
1078 TALLOC_CTX *parent;
1079
1080 if (!packet) {
1081 fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%i]: RADIUS_PACKET %s pointer was NULL", file, line, name);
1082 fr_assert(0);
1083 fr_exit_now(0);
1084 }
1085
1086 parent = talloc_parent(packet);
1087 if (parent != request) {
1088 ERROR("CONSISTENCY CHECK FAILED %s[%i]: Expected RADIUS_PACKET %s to be parented by %p (%s), "
1089 "but parented by %p (%s)", file, line, name, request, talloc_get_name(request),
1090 parent, parent ? talloc_get_name(parent) : "NULL");
1091
1092 fr_log_talloc_report(packet);
1093 if (parent) fr_log_talloc_report(parent);
1094
1095 rad_assert(0);
1096 }
1097
1098 VERIFY_PACKET(packet);
1099
1100 if (!packet->vps) return;
1101
1102 #ifdef WITH_VERIFY_PTR
1103 fr_pair_list_verify(file, line, packet, packet->vps, name);
1104 #endif
1105 }
1106 /*
1107 * Catch horrible talloc errors.
1108 */
verify_request(char const * file,int line,REQUEST * request)1109 void verify_request(char const *file, int line, REQUEST *request)
1110 {
1111 if (!request) {
1112 fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%i]: REQUEST pointer was NULL", file, line);
1113 fr_assert(0);
1114 fr_exit_now(0);
1115 }
1116
1117 (void) talloc_get_type_abort(request, REQUEST);
1118
1119 #ifdef WITH_VERIFY_PTR
1120 fr_pair_list_verify(file, line, request, request->config, "config");
1121 fr_pair_list_verify(file, line, request->state_ctx, request->state, "state");
1122 #endif
1123
1124 if (request->packet) verify_packet(file, line, request, request->packet, "request");
1125 if (request->reply) verify_packet(file, line, request, request->reply, "reply");
1126 #ifdef WITH_PROXY
1127 if (request->proxy) verify_packet(file, line, request, request->proxy, "proxy-request");
1128 if (request->proxy_reply) verify_packet(file, line, request, request->proxy_reply, "proxy-reply");
1129 #endif
1130
1131 #ifdef WITH_COA
1132 if (request->coa) {
1133 void *parent;
1134
1135 (void) talloc_get_type_abort(request->coa, REQUEST);
1136 parent = talloc_parent(request->coa);
1137
1138 rad_assert(parent == request);
1139
1140 verify_request(file, line, request->coa);
1141 }
1142 #endif
1143 }
1144 #endif
1145
1146 /** Convert mode_t into humanly readable permissions flags
1147 *
1148 * @author Jonathan Leffler.
1149 *
1150 * @param mode to convert.
1151 * @param out Where to write the string to, must be exactly 10 bytes long.
1152 */
rad_mode_to_str(char out[10],mode_t mode)1153 void rad_mode_to_str(char out[10], mode_t mode)
1154 {
1155 static char const *rwx[] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"};
1156
1157 strcpy(&out[0], rwx[(mode >> 6) & 0x07]);
1158 strcpy(&out[3], rwx[(mode >> 3) & 0x07]);
1159 strcpy(&out[6], rwx[(mode & 7)]);
1160 if (mode & S_ISUID) out[2] = (mode & 0100) ? 's' : 'S';
1161 if (mode & S_ISGID) out[5] = (mode & 0010) ? 's' : 'l';
1162 if (mode & S_ISVTX) out[8] = (mode & 0100) ? 't' : 'T';
1163 out[9] = '\0';
1164 }
1165
rad_mode_to_oct(char out[5],mode_t mode)1166 void rad_mode_to_oct(char out[5], mode_t mode)
1167 {
1168 out[0] = '0' + ((mode >> 9) & 0x07);
1169 out[1] = '0' + ((mode >> 6) & 0x07);
1170 out[2] = '0' + ((mode >> 3) & 0x07);
1171 out[3] = '0' + (mode & 0x07);
1172 out[4] = '\0';
1173 }
1174
1175 /** Resolve a uid to a passwd entry
1176 *
1177 * Resolves a uid to a passwd entry. The memory to hold the
1178 * passwd entry is talloced under ctx, and must be freed when no
1179 * longer required.
1180 *
1181 * @param ctx to allocate passwd entry in.
1182 * @param out Where to write pointer to entry.
1183 * @param uid to resolve.
1184 * @return 0 on success, -1 on error.
1185 */
rad_getpwuid(TALLOC_CTX * ctx,struct passwd ** out,uid_t uid)1186 int rad_getpwuid(TALLOC_CTX *ctx, struct passwd **out, uid_t uid)
1187 {
1188 static size_t len;
1189 uint8_t *buff;
1190 int ret;
1191
1192 *out = NULL;
1193
1194 /*
1195 * We assume this won't change between calls,
1196 * and that the value is the same, so races don't
1197 * matter.
1198 */
1199 if (len == 0) {
1200 #ifdef _SC_GETPW_R_SIZE_MAX
1201 long int sc_len;
1202
1203 sc_len = sysconf(_SC_GETPW_R_SIZE_MAX);
1204 if (sc_len <= 0) sc_len = 1024;
1205 len = (size_t)sc_len;
1206 #else
1207 len = 1024;
1208 #endif
1209 }
1210
1211 buff = talloc_array(ctx, uint8_t, sizeof(struct passwd) + len);
1212 if (!buff) return -1;
1213
1214 /*
1215 * In some cases we may need to dynamically
1216 * grow the string buffer.
1217 */
1218 while ((ret = getpwuid_r(uid, (struct passwd *)buff, (char *)(buff + sizeof(struct passwd)),
1219 talloc_array_length(buff) - sizeof(struct passwd), out)) == ERANGE) {
1220 buff = talloc_realloc_size(ctx, buff, talloc_array_length(buff) * 2);
1221 if (!buff) {
1222 talloc_free(buff);
1223 return -1;
1224 }
1225 }
1226
1227 if ((ret != 0) || !*out) {
1228 fr_strerror_printf("Failed resolving UID: %s", fr_syserror(ret));
1229 talloc_free(buff);
1230 errno = ret;
1231 return -1;
1232 }
1233
1234 talloc_set_type(buff, struct passwd);
1235 *out = (struct passwd *)buff;
1236
1237 return 0;
1238 }
1239
1240 /** Resolve a username to a passwd entry
1241 *
1242 * Resolves a username to a passwd entry. The memory to hold the
1243 * passwd entry is talloced under ctx, and must be freed when no
1244 * longer required.
1245 *
1246 * @param ctx to allocate passwd entry in.
1247 * @param out Where to write pointer to entry.
1248 * @param name to resolve.
1249 * @return 0 on success, -1 on error.
1250 */
rad_getpwnam(TALLOC_CTX * ctx,struct passwd ** out,char const * name)1251 int rad_getpwnam(TALLOC_CTX *ctx, struct passwd **out, char const *name)
1252 {
1253 static size_t len;
1254 uint8_t *buff;
1255 int ret;
1256
1257 *out = NULL;
1258
1259 /*
1260 * We assume this won't change between calls,
1261 * and that the value is the same, so races don't
1262 * matter.
1263 */
1264 if (len == 0) {
1265 #ifdef _SC_GETPW_R_SIZE_MAX
1266 long int sc_len;
1267
1268 sc_len = sysconf(_SC_GETPW_R_SIZE_MAX);
1269 if (sc_len <= 0) sc_len = 1024;
1270 len = (size_t)sc_len;
1271 #else
1272 sc_len = 1024;
1273 #endif
1274 }
1275
1276 buff = talloc_array(ctx, uint8_t, sizeof(struct passwd) + len);
1277 if (!buff) return -1;
1278
1279 /*
1280 * In some cases we may need to dynamically
1281 * grow the string buffer.
1282 */
1283 while ((ret = getpwnam_r(name, (struct passwd *)buff, (char *)(buff + sizeof(struct passwd)),
1284 talloc_array_length(buff) - sizeof(struct passwd), out)) == ERANGE) {
1285 buff = talloc_realloc_size(ctx, buff, talloc_array_length(buff) * 2);
1286 if (!buff) {
1287 talloc_free(buff);
1288 return -1;
1289 }
1290 }
1291
1292 if ((ret != 0) || !*out) {
1293 fr_strerror_printf("Failed resolving UID: %s", fr_syserror(ret));
1294 talloc_free(buff);
1295 errno = ret;
1296 return -1;
1297 }
1298
1299 talloc_set_type(buff, struct passwd);
1300 *out = (struct passwd *)buff;
1301
1302 return 0;
1303 }
1304
1305 /** Resolve a gid to a group database entry
1306 *
1307 * Resolves a gid to a group database entry. The memory to hold the
1308 * group entry is talloced under ctx, and must be freed when no
1309 * longer required.
1310 *
1311 * @param ctx to allocate passwd entry in.
1312 * @param out Where to write pointer to entry.
1313 * @param gid to resolve.
1314 * @return 0 on success, -1 on error.
1315 */
rad_getgrgid(TALLOC_CTX * ctx,struct group ** out,gid_t gid)1316 int rad_getgrgid(TALLOC_CTX *ctx, struct group **out, gid_t gid)
1317 {
1318 static size_t len;
1319 uint8_t *buff;
1320 int ret;
1321
1322 *out = NULL;
1323
1324 /*
1325 * We assume this won't change between calls,
1326 * and that the value is the same, so races don't
1327 * matter.
1328 */
1329 if (len == 0) {
1330 #ifdef _SC_GETGR_R_SIZE_MAX
1331 long int sc_len;
1332
1333 sc_len = sysconf(_SC_GETGR_R_SIZE_MAX);
1334 if (sc_len <= 0) sc_len = 1024;
1335 len = (size_t)sc_len;
1336 #else
1337 sc_len = 1024;
1338 #endif
1339 }
1340
1341 buff = talloc_array(ctx, uint8_t, sizeof(struct group) + len);
1342 if (!buff) return -1;
1343
1344 /*
1345 * In some cases we may need to dynamically
1346 * grow the string buffer.
1347 */
1348 while ((ret = getgrgid_r(gid, (struct group *)buff, (char *)(buff + sizeof(struct group)),
1349 talloc_array_length(buff) - sizeof(struct group), out)) == ERANGE) {
1350 buff = talloc_realloc_size(ctx, buff, talloc_array_length(buff) * 2);
1351 if (!buff) {
1352 talloc_free(buff);
1353 return -1;
1354 }
1355 }
1356
1357 if ((ret != 0) || !*out) {
1358 fr_strerror_printf("Failed resolving GID: %s", fr_syserror(ret));
1359 talloc_free(buff);
1360 errno = ret;
1361 return -1;
1362 }
1363
1364 talloc_set_type(buff, struct group);
1365 *out = (struct group *)buff;
1366
1367 return 0;
1368 }
1369
1370 /** Resolve a group name to a group database entry
1371 *
1372 * Resolves a group name to a group database entry.
1373 * The memory to hold the group entry is talloced under ctx,
1374 * and must be freed when no longer required.
1375 *
1376 * @param ctx to allocate passwd entry in.
1377 * @param out Where to write pointer to entry.
1378 * @param name to resolve.
1379 * @return 0 on success, -1 on error.
1380 */
rad_getgrnam(TALLOC_CTX * ctx,struct group ** out,char const * name)1381 int rad_getgrnam(TALLOC_CTX *ctx, struct group **out, char const *name)
1382 {
1383 static size_t len;
1384 uint8_t *buff;
1385 int ret;
1386
1387 *out = NULL;
1388
1389 /*
1390 * We assume this won't change between calls,
1391 * and that the value is the same, so races don't
1392 * matter.
1393 */
1394 if (len == 0) {
1395 #ifdef _SC_GETGR_R_SIZE_MAX
1396 long int sc_len;
1397
1398 sc_len = sysconf(_SC_GETGR_R_SIZE_MAX);
1399 if (sc_len <= 0) sc_len = 1024;
1400 len = (size_t)sc_len;
1401 #else
1402 len = 1024;
1403 #endif
1404 }
1405
1406 buff = talloc_array(ctx, uint8_t, sizeof(struct group) + len);
1407 if (!buff) return -1;
1408
1409 /*
1410 * In some cases we may need to dynamically
1411 * grow the string buffer.
1412 */
1413 while ((ret = getgrnam_r(name, (struct group *)buff, (char *)(buff + sizeof(struct group)),
1414 talloc_array_length(buff) - sizeof(struct group), out)) == ERANGE) {
1415 buff = talloc_realloc_size(ctx, buff, talloc_array_length(buff) * 2);
1416 if (!buff) {
1417 talloc_free(buff);
1418 return -1;
1419 }
1420 }
1421
1422 if ((ret != 0) || !*out) {
1423 fr_strerror_printf("Failed resolving GID: %s", fr_syserror(ret));
1424 talloc_free(buff);
1425 errno = ret;
1426 return -1;
1427 }
1428
1429 talloc_set_type(buff, struct group);
1430 *out = (struct group *)buff;
1431
1432 return 0;
1433 }
1434
1435 /** Resolve a group name to a GID
1436 *
1437 * @param ctx TALLOC_CTX for temporary allocations.
1438 * @param name of group.
1439 * @param out where to write gid.
1440 * @return 0 on success, -1 on error;
1441 */
rad_getgid(TALLOC_CTX * ctx,gid_t * out,char const * name)1442 int rad_getgid(TALLOC_CTX *ctx, gid_t *out, char const *name)
1443 {
1444 int ret;
1445 struct group *result;
1446
1447 ret = rad_getgrnam(ctx, &result, name);
1448 if (ret < 0) return -1;
1449
1450 *out = result->gr_gid;
1451 talloc_free(result);
1452 return 0;
1453 }
1454
1455 /** Print uid to a string
1456 *
1457 * @note The reason for taking a fixed buffer is pure laziness.
1458 * It means the caller doesn't have to free the string.
1459 *
1460 * @note Will always \0 terminate the buffer, even on error.
1461 *
1462 * @param ctx TALLOC_CTX for temporary allocations.
1463 * @param out Where to write the uid string.
1464 * @param outlen length of output buffer.
1465 * @param uid to resolve.
1466 * @return 0 on success, -1 on failure.
1467 */
rad_prints_uid(TALLOC_CTX * ctx,char * out,size_t outlen,uid_t uid)1468 int rad_prints_uid(TALLOC_CTX *ctx, char *out, size_t outlen, uid_t uid)
1469 {
1470 struct passwd *result;
1471
1472 rad_assert(outlen > 0);
1473
1474 *out = '\0';
1475
1476 if (rad_getpwuid(ctx, &result, uid) < 0) return -1;
1477 strlcpy(out, result->pw_name, outlen);
1478 talloc_free(result);
1479
1480 return 0;
1481 }
1482
1483 /** Print gid to a string
1484 *
1485 * @note The reason for taking a fixed buffer is pure laziness.
1486 * It means the caller doesn't have to free the string.
1487 *
1488 * @note Will always \0 terminate the buffer, even on error.
1489 *
1490 * @param ctx TALLOC_CTX for temporary allocations.
1491 * @param out Where to write the uid string.
1492 * @param outlen length of output buffer.
1493 * @param gid to resolve.
1494 * @return 0 on success, -1 on failure.
1495 */
rad_prints_gid(TALLOC_CTX * ctx,char * out,size_t outlen,gid_t gid)1496 int rad_prints_gid(TALLOC_CTX *ctx, char *out, size_t outlen, gid_t gid)
1497 {
1498 struct group *result;
1499
1500 rad_assert(outlen > 0);
1501
1502 *out = '\0';
1503
1504 if (rad_getgrgid(ctx, &result, gid) < 0) return -1;
1505 strlcpy(out, result->gr_name, outlen);
1506 talloc_free(result);
1507
1508 return 0;
1509 }
1510
1511 #ifdef HAVE_SETUID
1512 static bool doing_setuid = false;
1513 static uid_t suid_down_uid = (uid_t)-1;
1514
1515 /** Set the uid and gid used when dropping privileges
1516 *
1517 * @note if this function hasn't been called, rad_suid_down will have no effect.
1518 *
1519 * @param uid to drop down to.
1520 */
rad_suid_set_down_uid(uid_t uid)1521 void rad_suid_set_down_uid(uid_t uid)
1522 {
1523 suid_down_uid = uid;
1524 doing_setuid = true;
1525 }
1526
1527 # if defined(HAVE_SETRESUID) && defined (HAVE_GETRESUID)
rad_suid_up(void)1528 void rad_suid_up(void)
1529 {
1530 uid_t ruid, euid, suid;
1531
1532 if (getresuid(&ruid, &euid, &suid) < 0) {
1533 ERROR("Failed getting saved UID's");
1534 fr_exit_now(1);
1535 }
1536
1537 if (setresuid(-1, suid, -1) < 0) {
1538 ERROR("Failed switching to privileged user");
1539 fr_exit_now(1);
1540 }
1541
1542 if (geteuid() != suid) {
1543 ERROR("Switched to unknown UID");
1544 fr_exit_now(1);
1545 }
1546 }
1547
rad_suid_down(void)1548 void rad_suid_down(void)
1549 {
1550 if (!doing_setuid) return;
1551
1552 if (setresuid(-1, suid_down_uid, geteuid()) < 0) {
1553 struct passwd *passwd;
1554 char const *name;
1555
1556 name = (rad_getpwuid(NULL, &passwd, suid_down_uid) < 0) ? "unknown" : passwd->pw_name;
1557 ERROR("Failed switching to uid %s: %s", name, fr_syserror(errno));
1558 talloc_free(passwd);
1559 fr_exit_now(1);
1560 }
1561
1562 if (geteuid() != suid_down_uid) {
1563 ERROR("Failed switching uid: UID is incorrect");
1564 fr_exit_now(1);
1565 }
1566
1567 fr_reset_dumpable();
1568 }
1569
rad_suid_down_permanent(void)1570 void rad_suid_down_permanent(void)
1571 {
1572 if (!doing_setuid) return;
1573
1574 if (setresuid(suid_down_uid, suid_down_uid, suid_down_uid) < 0) {
1575 struct passwd *passwd;
1576 char const *name;
1577
1578 name = (rad_getpwuid(NULL, &passwd, suid_down_uid) < 0) ? "unknown" : passwd->pw_name;
1579 ERROR("Failed in permanent switch to uid %s: %s", name, fr_syserror(errno));
1580 talloc_free(passwd);
1581 fr_exit_now(1);
1582 }
1583
1584 if (geteuid() != suid_down_uid) {
1585 ERROR("Switched to unknown uid");
1586 fr_exit_now(1);
1587 }
1588
1589 fr_reset_dumpable();
1590 }
1591 # else
1592 /*
1593 * Much less secure...
1594 */
rad_suid_up(void)1595 void rad_suid_up(void)
1596 {
1597 if (!doing_setuid) return;
1598
1599 if (seteuid(0) < 0) {
1600 ERROR("Failed switching up to euid 0: %s", fr_syserror(errno));
1601 fr_exit_now(1);
1602 }
1603
1604 }
1605
rad_suid_down(void)1606 void rad_suid_down(void)
1607 {
1608 if (!doing_setuid) return;
1609
1610 if (geteuid() == suid_down_uid) return;
1611
1612 if (seteuid(suid_down_uid) < 0) {
1613 struct passwd *passwd;
1614 char const *name;
1615
1616 name = (rad_getpwuid(NULL, &passwd, suid_down_uid) < 0) ? "unknown": passwd->pw_name;
1617 ERROR("Failed switching to euid %s: %s", name, fr_syserror(errno));
1618 talloc_free(passwd);
1619 fr_exit_now(1);
1620 }
1621
1622 fr_reset_dumpable();
1623 }
1624
rad_suid_down_permanent(void)1625 void rad_suid_down_permanent(void)
1626 {
1627 if (!doing_setuid) return;
1628
1629 /*
1630 * Already done. Don't do anything else.
1631 */
1632 if (getuid() == suid_down_uid) return;
1633
1634 /*
1635 * We're root, but running as a normal user. Fix that,
1636 * so we can call setuid().
1637 */
1638 if (geteuid() == suid_down_uid) {
1639 rad_suid_up();
1640 }
1641
1642 if (setuid(suid_down_uid) < 0) {
1643 struct passwd *passwd;
1644 char const *name;
1645
1646 name = (rad_getpwuid(NULL, &passwd, suid_down_uid) < 0) ? "unknown": passwd->pw_name;
1647 ERROR("Failed switching permanently to uid %s: %s", name, fr_syserror(errno));
1648 talloc_free(passwd);
1649 fr_exit_now(1);
1650 }
1651
1652 fr_reset_dumpable();
1653 }
1654 # endif /* HAVE_SETRESUID && HAVE_GETRESUID */
1655 #else /* HAVE_SETUID */
rad_suid_set_down_uid(uid_t uid)1656 void rad_suid_set_down_uid(uid_t uid)
1657 {
1658 }
rad_suid_up(void)1659 void rad_suid_up(void)
1660 {
1661 }
rad_suid_down(void)1662 void rad_suid_down(void)
1663 {
1664 fr_reset_dumpable();
1665 }
rad_suid_down_permanent(void)1666 void rad_suid_down_permanent(void)
1667 {
1668 fr_reset_dumpable();
1669 }
1670 #endif /* HAVE_SETUID */
1671
1672 /** Alter the effective user id
1673 *
1674 * @param uid to set
1675 * @return 0 on success -1 on failure.
1676 */
rad_seuid(uid_t uid)1677 int rad_seuid(uid_t uid)
1678 {
1679 if (seteuid(uid) < 0) {
1680 struct passwd *passwd;
1681
1682 if (rad_getpwuid(NULL, &passwd, uid) < 0) return -1;
1683 fr_strerror_printf("Failed setting euid to %s", passwd->pw_name);
1684 talloc_free(passwd);
1685
1686 return -1;
1687 }
1688 return 0;
1689 }
1690
1691 /** Alter the effective user id
1692 *
1693 * @param gid to set
1694 * @return 0 on success -1 on failure.
1695 */
rad_segid(gid_t gid)1696 int rad_segid(gid_t gid)
1697 {
1698 if (setegid(gid) < 0) {
1699 struct group *group;
1700
1701 if (rad_getgrgid(NULL, &group, gid) < 0) return -1;
1702 fr_strerror_printf("Failed setting egid to %s", group->gr_name);
1703 talloc_free(group);
1704
1705 return -1;
1706 }
1707 return 0;
1708 }
1709
1710 /** Determine the elapsed time between two timevals
1711 *
1712 * @param end timeval nearest to the present
1713 * @param start timeval furthest from the present
1714 * @param elapsed Where to write the elapsed time
1715 */
rad_tv_sub(struct timeval const * end,struct timeval const * start,struct timeval * elapsed)1716 void rad_tv_sub(struct timeval const *end, struct timeval const *start, struct timeval *elapsed)
1717 {
1718 elapsed->tv_sec = end->tv_sec - start->tv_sec;
1719 if (elapsed->tv_sec > 0) {
1720 elapsed->tv_sec--;
1721 elapsed->tv_usec = USEC;
1722 } else {
1723 elapsed->tv_usec = 0;
1724 }
1725 elapsed->tv_usec += end->tv_usec;
1726 elapsed->tv_usec -= start->tv_usec;
1727
1728 if (elapsed->tv_usec >= USEC) {
1729 elapsed->tv_usec -= USEC;
1730 elapsed->tv_sec++;
1731 }
1732 }
1733