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