1 /*
2  * mainconf.c	Handle the server's configuration.
3  *
4  * Version:	$Id: 09799da0e2077050c2b581d8c750723677c907e0 $
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 2002,2006-2007  The FreeRADIUS server project
21  * Copyright 2002  Alan DeKok <aland@ox.org>
22  */
23 
24 RCSID("$Id: 09799da0e2077050c2b581d8c750723677c907e0 $")
25 
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/modules.h>
28 #include <freeradius-devel/modpriv.h>
29 #include <freeradius-devel/rad_assert.h>
30 
31 #include <sys/stat.h>
32 #include <pwd.h>
33 #include <grp.h>
34 
35 #ifdef HAVE_SYSLOG_H
36 #  include <syslog.h>
37 #endif
38 
39 #ifdef HAVE_FCNTL_H
40 #  include <fcntl.h>
41 #endif
42 
43 #ifdef HAVE_SYSTEMD
44 #  include <systemd/sd-daemon.h>
45 #endif
46 
47 main_config_t		main_config;				//!< Main server configuration.
48 extern fr_cond_t	*debug_condition;
49 fr_cond_t		*debug_condition = NULL;			//!< Condition used to mark packets up for checking.
50 bool			event_loop_started = false;		//!< Whether the main event loop has been started yet.
51 
52 typedef struct cached_config_t {
53 	struct cached_config_t *next;
54 	time_t		created;
55 	CONF_SECTION	*cs;
56 } cached_config_t;
57 
58 static cached_config_t	*cs_cache = NULL;
59 
60 /*
61  *	Temporary local variables for parsing the configuration
62  *	file.
63  */
64 #ifdef HAVE_SETUID
65 /*
66  *	Systems that have set/getresuid also have setuid.
67  */
68 static uid_t server_uid = 0;
69 static gid_t server_gid = 0;
70 static char const *uid_name = NULL;
71 static char const *gid_name = NULL;
72 #endif
73 static char const *chroot_dir = NULL;
74 static bool allow_core_dumps = false;
75 static char const *radlog_dest = NULL;
76 
77 /*
78  *	These are not used anywhere else..
79  */
80 static char const	*localstatedir = NULL;
81 static char const	*prefix = NULL;
82 static char const	*my_name = NULL;
83 static char const	*sbindir = NULL;
84 static char const	*run_dir = NULL;
85 static char const	*syslog_facility = NULL;
86 static bool		do_colourise = false;
87 
88 static char const	*radius_dir = NULL;	//!< Path to raddb directory
89 
90 /**********************************************************************
91  *
92  *	We need to figure out where the logs go, before doing anything
93  *	else.  This is so that the log messages go to the correct
94  *	place.
95  *
96  *	BUT, we want the settings from the command line to over-ride
97  *	the ones in the configuration file.  So, these items are
98  *	parsed ONLY if there is no "-l foo" on the command line.
99  *
100  **********************************************************************/
101 
102 /*
103  *	Log destinations
104  */
105 static const CONF_PARSER startup_log_config[] = {
106 	{ "destination",  FR_CONF_POINTER(PW_TYPE_STRING, &radlog_dest), "files" },
107 	{ "syslog_facility",  FR_CONF_POINTER(PW_TYPE_STRING, &syslog_facility), STRINGIFY(0) },
108 
109 	{ "localstatedir", FR_CONF_POINTER(PW_TYPE_STRING, &localstatedir), "${prefix}/var"},
110 	{ "logdir", FR_CONF_POINTER(PW_TYPE_STRING, &radlog_dir), "${localstatedir}/log"},
111 	{ "file",  FR_CONF_POINTER(PW_TYPE_STRING, &main_config.log_file), "${logdir}/radius.log" },
112 	{ "requests",  FR_CONF_POINTER(PW_TYPE_STRING | PW_TYPE_DEPRECATED, &default_log.file), NULL },
113 	CONF_PARSER_TERMINATOR
114 };
115 
116 
117 /*
118  *	Basic configuration for the server.
119  */
120 static const CONF_PARSER startup_server_config[] = {
121 	{ "log",  FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) startup_log_config },
122 
123 	{ "name", FR_CONF_POINTER(PW_TYPE_STRING, &my_name), "radiusd"},
124 	{ "prefix", FR_CONF_POINTER(PW_TYPE_STRING, &prefix), "/usr/local"},
125 
126 	{ "log_file",  FR_CONF_POINTER(PW_TYPE_STRING, &main_config.log_file), NULL },
127 	{ "log_destination", FR_CONF_POINTER(PW_TYPE_STRING, &radlog_dest), NULL },
128 	{ "use_utc", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &log_dates_utc), NULL },
129 	CONF_PARSER_TERMINATOR
130 };
131 
132 
133 /**********************************************************************
134  *
135  *	Now that we've parsed the log destination, AND the security
136  *	items, we can parse the rest of the configuration items.
137  *
138  **********************************************************************/
139 static const CONF_PARSER log_config[] = {
140 	{ "stripped_names", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &log_stripped_names),"no" },
141 	{ "auth", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.log_auth), "no" },
142 	{ "auth_accept", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.log_accept), NULL},
143 	{ "auth_reject", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.log_reject), NULL},
144 	{ "auth_badpass", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.log_auth_badpass), "no" },
145 	{ "auth_goodpass", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.log_auth_goodpass), "no" },
146 	{ "msg_badpass", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.auth_badpass_msg), NULL},
147 	{ "msg_goodpass", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.auth_goodpass_msg), NULL},
148 	{ "colourise",FR_CONF_POINTER(PW_TYPE_BOOLEAN, &do_colourise), NULL },
149 	{ "use_utc", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &log_dates_utc), NULL },
150 	{ "msg_denied", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.denied_msg), "You are already logged in - access denied" },
151 	{ "suppress_secrets", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.suppress_secrets), NULL },
152 	CONF_PARSER_TERMINATOR
153 };
154 
155 
156 /*
157  *  Security configuration for the server.
158  */
159 static const CONF_PARSER security_config[] = {
160 	{ "max_attributes",  FR_CONF_POINTER(PW_TYPE_INTEGER, &fr_max_attributes), STRINGIFY(0) },
161 	{ "reject_delay",  FR_CONF_POINTER(PW_TYPE_TIMEVAL, &main_config.reject_delay), STRINGIFY(0) },
162 	{ "status_server", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.status_server), "no"},
163 #ifdef ENABLE_OPENSSL_VERSION_CHECK
164 	{ "allow_vulnerable_openssl", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.allow_vulnerable_openssl), "no"},
165 #endif
166 	CONF_PARSER_TERMINATOR
167 };
168 
169 static const CONF_PARSER resources[] = {
170 	/*
171 	 *	Don't set a default here.  It's set in the code, below.  This means that
172 	 *	the config item will *not* get printed out in debug mode, so that no one knows
173 	 *	it exists.
174 	 */
175 	{ "talloc_pool_size", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.talloc_pool_size), NULL },
176 	CONF_PARSER_TERMINATOR
177 };
178 
179 static const CONF_PARSER server_config[] = {
180 	/*
181 	 *	FIXME: 'prefix' is the ONLY one which should be
182 	 *	configured at compile time.  Hard-coding it here is
183 	 *	bad.  It will be cleaned up once we clean up the
184 	 *	hard-coded defines for the locations of the various
185 	 *	files.
186 	 */
187 	{ "name", FR_CONF_POINTER(PW_TYPE_STRING, &my_name), "radiusd"},
188 	{ "prefix", FR_CONF_POINTER(PW_TYPE_STRING, &prefix), "/usr/local"},
189 	{ "localstatedir", FR_CONF_POINTER(PW_TYPE_STRING, &localstatedir), "${prefix}/var"},
190 	{ "sbindir", FR_CONF_POINTER(PW_TYPE_STRING, &sbindir), "${prefix}/sbin"},
191 	{ "logdir", FR_CONF_POINTER(PW_TYPE_STRING, &radlog_dir), "${localstatedir}/log"},
192 	{ "run_dir", FR_CONF_POINTER(PW_TYPE_STRING, &run_dir), "${localstatedir}/run/${name}"},
193 	{ "libdir", FR_CONF_POINTER(PW_TYPE_STRING, &radlib_dir), "${prefix}/lib"},
194 	{ "radacctdir", FR_CONF_POINTER(PW_TYPE_STRING, &radacct_dir), "${logdir}/radacct" },
195 	{ "panic_action", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.panic_action), NULL},
196 	{ "hostname_lookups", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &fr_dns_lookups), "no" },
197 	{ "max_request_time", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.max_request_time), STRINGIFY(MAX_REQUEST_TIME) },
198 	{ "cleanup_delay", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.cleanup_delay), STRINGIFY(CLEANUP_DELAY) },
199 	{ "max_requests", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.max_requests), STRINGIFY(MAX_REQUESTS) },
200 	{ "postauth_client_lost", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.postauth_client_lost), "no" },
201 	{ "pidfile", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.pid_file), "${run_dir}/radiusd.pid"},
202 	{ "checkrad", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.checkrad), "${sbindir}/checkrad" },
203 
204 	{ "debug_level", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.debug_level), "0"},
205 
206 #ifdef WITH_PROXY
207 	{ "proxy_requests", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.proxy_requests), "yes" },
208 #endif
209 	{ "log", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) log_config },
210 
211 	{ "resources", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) resources },
212 
213 	/*
214 	 *	People with old configs will have these.  They are listed
215 	 *	AFTER the "log" section, so if they exist in radiusd.conf,
216 	 *	it will prefer "log_foo = bar" to "log { foo = bar }".
217 	 *	They're listed with default values of NULL, so that if they
218 	 *	DON'T exist in radiusd.conf, then the previously parsed
219 	 *	values for "log { foo = bar}" will be used.
220 	 */
221 	{ "log_auth", FR_CONF_POINTER(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, &main_config.log_auth), NULL },
222 	{ "log_auth_badpass", FR_CONF_POINTER(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, &main_config.log_auth_badpass), NULL },
223 	{ "log_auth_goodpass", FR_CONF_POINTER(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, &main_config.log_auth_goodpass), NULL },
224 	{ "log_stripped_names", FR_CONF_POINTER(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, &log_stripped_names), NULL },
225 
226 	{  "security", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) security_config },
227 	CONF_PARSER_TERMINATOR
228 };
229 
230 
231 /**********************************************************************
232  *
233  *	The next few items are here as a "bootstrap" for security.
234  *	They allow the server to switch users, chroot, while still
235  *	opening the various output files with the correct permission.
236  *
237  *	It's rare (or impossible) to have parse errors here, so we
238  *	don't worry too much about that.  In contrast, when we parse
239  *	the rest of the configuration, we CAN get parse errors.  We
240  *	want THOSE parse errors to go to the log file, and we want the
241  *	log file to have the correct permissions.
242  *
243  **********************************************************************/
244 static const CONF_PARSER bootstrap_security_config[] = {
245 #ifdef HAVE_SETUID
246 	{ "user",  FR_CONF_POINTER(PW_TYPE_STRING, &uid_name), NULL },
247 	{ "group", FR_CONF_POINTER(PW_TYPE_STRING, &gid_name), NULL },
248 #endif
249 	{ "chroot",  FR_CONF_POINTER(PW_TYPE_STRING, &chroot_dir), NULL },
250 	{ "allow_core_dumps", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &allow_core_dumps), "no" },
251 	CONF_PARSER_TERMINATOR
252 };
253 
254 static const CONF_PARSER bootstrap_config[] = {
255 	{  "security", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) bootstrap_security_config },
256 
257 	{ "name", FR_CONF_POINTER(PW_TYPE_STRING, &my_name), "radiusd"},
258 	{ "prefix", FR_CONF_POINTER(PW_TYPE_STRING, &prefix), "/usr/local"},
259 	{ "localstatedir", FR_CONF_POINTER(PW_TYPE_STRING, &localstatedir), "${prefix}/var"},
260 
261 	{ "logdir", FR_CONF_POINTER(PW_TYPE_STRING, &radlog_dir), "${localstatedir}/log"},
262 	{ "run_dir", FR_CONF_POINTER(PW_TYPE_STRING, &run_dir), "${localstatedir}/run/${name}"},
263 
264 	/*
265 	 *	For backwards compatibility.
266 	 */
267 #ifdef HAVE_SETUID
268 	{ "user",  FR_CONF_POINTER(PW_TYPE_STRING | PW_TYPE_DEPRECATED, &uid_name), NULL },
269 	{ "group",  FR_CONF_POINTER(PW_TYPE_STRING | PW_TYPE_DEPRECATED, &gid_name), NULL },
270 #endif
271 	{ "chroot",  FR_CONF_POINTER(PW_TYPE_STRING | PW_TYPE_DEPRECATED, &chroot_dir), NULL },
272 	{ "allow_core_dumps", FR_CONF_POINTER(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, &allow_core_dumps), NULL },
273 	CONF_PARSER_TERMINATOR
274 };
275 
276 
config_escape_func(UNUSED REQUEST * request,char * out,size_t outlen,char const * in,UNUSED void * arg)277 static size_t config_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
278 {
279 	size_t len = 0;
280 	static char const disallowed[] = "%{}\\'\"`";
281 
282 	while (in[0]) {
283 		/*
284 		 *	Non-printable characters get replaced with their
285 		 *	mime-encoded equivalents.
286 		 */
287 		if ((in[0] < 32)) {
288 			if (outlen <= 3) break;
289 
290 			snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
291 			in++;
292 			out += 3;
293 			outlen -= 3;
294 			len += 3;
295 			continue;
296 
297 		} else if (strchr(disallowed, *in) != NULL) {
298 			if (outlen <= 2) break;
299 
300 			out[0] = '\\';
301 			out[1] = *in;
302 			in++;
303 			out += 2;
304 			outlen -= 2;
305 			len += 2;
306 			continue;
307 		}
308 
309 		/*
310 		 *	Only one byte left.
311 		 */
312 		if (outlen <= 1) {
313 			break;
314 		}
315 
316 		/*
317 		 *	Allowed character.
318 		 */
319 		*out = *in;
320 		out++;
321 		in++;
322 		outlen--;
323 		len++;
324 	}
325 	*out = '\0';
326 	return len;
327 }
328 
329 /*
330  *	Xlat for %{config:section.subsection.attribute}
331  */
xlat_config(UNUSED void * instance,REQUEST * request,char const * fmt,char * out,size_t outlen)332 static ssize_t xlat_config(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen)
333 {
334 	char const *value;
335 	CONF_PAIR *cp;
336 	CONF_ITEM *ci;
337 	char buffer[1024];
338 
339 	/*
340 	 *	Expand it safely.
341 	 */
342 	if (radius_xlat(buffer, sizeof(buffer), request, fmt, config_escape_func, NULL) < 0) {
343 		return 0;
344 	}
345 
346 	ci = cf_reference_item(request->root->config,
347 			       request->root->config, buffer);
348 	if (!ci || !cf_item_is_pair(ci)) {
349 		REDEBUG("Config item \"%s\" does not exist", fmt);
350 		*out = '\0';
351 		return -1;
352 	}
353 
354 	cp = cf_item_to_pair(ci);
355 
356 	/*
357 	 *  Ensure that we only copy what's necessary.
358 	 *
359 	 *  If 'outlen' is too small, then the output is chopped to fit.
360 	 */
361 	value = cf_pair_value(cp);
362 	if (!value) {
363 		out[0] = '\0';
364 		return 0;
365 	}
366 
367 	if (outlen > strlen(value)) {
368 		outlen = strlen(value) + 1;
369 	}
370 
371 	strlcpy(out, value, outlen);
372 
373 	return strlen(out);
374 }
375 
376 
377 /*
378  *	Xlat for %{client:foo}
379  */
xlat_client(UNUSED void * instance,REQUEST * request,char const * fmt,char * out,size_t outlen)380 static ssize_t xlat_client(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen)
381 {
382 	char const *value = NULL;
383 	CONF_PAIR *cp;
384 
385 	if (!fmt || !out || (outlen < 1)) return 0;
386 
387 	if (!request->client) {
388 		RWDEBUG("No client associated with this request");
389 		*out = '\0';
390 		return 0;
391 	}
392 
393 	cp = cf_pair_find(request->client->cs, fmt);
394 	if (!cp || !(value = cf_pair_value(cp))) {
395 		if (strcmp(fmt, "shortname") == 0 && request->client->shortname) {
396 			value = request->client->shortname;
397 		}
398 		else if (strcmp(fmt, "nas_type") == 0 && request->client->nas_type) {
399 			value = request->client->nas_type;
400 		} else {
401 			*out = '\0';
402 			return 0;
403 		}
404 	}
405 
406 	strlcpy(out, value, outlen);
407 
408 	return strlen(out);
409 }
410 
411 /*
412  *	Xlat for %{getclient:<ipaddr>.foo}
413  */
xlat_getclient(UNUSED void * instance,REQUEST * request,char const * fmt,char * out,size_t outlen)414 static ssize_t xlat_getclient(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen)
415 {
416 	char const *value = NULL;
417 	char buffer[INET6_ADDRSTRLEN], *q;
418 	char const *p = fmt;
419 	fr_ipaddr_t ip;
420 	CONF_PAIR *cp;
421 	RADCLIENT *client = NULL;
422 
423 	if (!fmt || !out || (outlen < 1)) return 0;
424 
425 	q = strrchr(p, '.');
426 	if (!q || (q == p) || (((size_t)(q - p)) > sizeof(buffer))) {
427 		REDEBUG("Invalid client string");
428 		goto error;
429 	}
430 
431 	strlcpy(buffer, p, (q + 1) - p);
432 	if (fr_pton(&ip, buffer, -1, AF_UNSPEC, false) < 0) {
433 		REDEBUG("\"%s\" is not a valid IPv4 or IPv6 address", buffer);
434 		goto error;
435 	}
436 
437 	fmt = q + 1;
438 
439 	client = client_find(NULL, &ip, IPPROTO_IP);
440 	if (!client) {
441 		RDEBUG("No client found with IP \"%s\"", buffer);
442 		*out = '\0';
443 		return 0;
444 	}
445 
446 	cp = cf_pair_find(client->cs, fmt);
447 	if (!cp || !(value = cf_pair_value(cp))) {
448 		if (strcmp(fmt, "shortname") == 0) {
449 			strlcpy(out, request->client->shortname, outlen);
450 			return strlen(out);
451 		}
452 		*out = '\0';
453 		return 0;
454 	}
455 
456 	strlcpy(out, value, outlen);
457 	return strlen(out);
458 
459 	error:
460 	*out = '\0';
461 	return -1;
462 }
463 
464 /*
465  *	Common xlat for listeners
466  */
xlat_listen_common(REQUEST * request,rad_listen_t * listen,char const * fmt,char * out,size_t outlen)467 static ssize_t xlat_listen_common(REQUEST *request, rad_listen_t *listen,
468 				  char const *fmt, char *out, size_t outlen)
469 {
470 	char const *value = NULL;
471 	CONF_PAIR *cp;
472 
473 	if (!fmt || !out || (outlen < 1)) return 0;
474 
475 	if (!listen) {
476 		RWDEBUG("No listener associated with this request");
477 		*out = '\0';
478 		return 0;
479 	}
480 
481 	/*
482 	 *	When TLS is configured, we *require* the use of TLS.
483 	 */
484 	if (strcmp(fmt, "tls") == 0) {
485 #ifdef WITH_TLS
486 		if (listen->tls) {
487 			strlcpy(out, "yes", outlen);
488 			return strlen(out);
489 		}
490 #endif
491 
492 		strlcpy(out, "no", outlen);
493 		return strlen(out);
494 	}
495 
496 #ifdef WITH_TLS
497 	/*
498 	 *	Look for TLS certificate data.
499 	 */
500 	if (strncmp(fmt, "TLS-", 4) == 0) {
501 		VALUE_PAIR *vp;
502 		listen_socket_t *sock = listen->data;
503 
504 		for (vp = sock->certs; vp != NULL; vp = vp->next) {
505 			if (strcmp(fmt, vp->da->name) == 0) {
506 				return vp_prints_value(out, outlen, vp, 0);
507 			}
508 		}
509 	}
510 #endif
511 
512 #ifdef WITH_COA_TUNNEL
513 	/*
514 	 *      Look for RADSEC CoA tunnel key.
515 	 */
516 	if (listen->key && (strcmp(fmt, "Originating-Realm-Key") == 0)) {
517 		strlcpy(out, listen->key, outlen);
518 		return strlen(out);
519 	}
520 #endif
521 
522 	cp = cf_pair_find(listen->cs, fmt);
523 	if (!cp || !(value = cf_pair_value(cp))) {
524 		RDEBUG("Listener does not contain config item \"%s\"", fmt);
525 		*out = '\0';
526 		return 0;
527 	}
528 
529 	strlcpy(out, value, outlen);
530 
531 	return strlen(out);
532 }
533 
534 
535 /*
536  *	Xlat for %{listen:foo}
537  */
xlat_listen(UNUSED void * instance,REQUEST * request,char const * fmt,char * out,size_t outlen)538 static ssize_t xlat_listen(UNUSED void *instance, REQUEST *request,
539 			   char const *fmt, char *out, size_t outlen)
540 {
541 	return xlat_listen_common(request, request->listener, fmt, out, outlen);
542 }
543 
544 /*
545  *	Xlat for %{proxy_listen:foo}
546  */
xlat_proxy_listen(UNUSED void * instance,REQUEST * request,char const * fmt,char * out,size_t outlen)547 static ssize_t xlat_proxy_listen(UNUSED void *instance, REQUEST *request,
548 				 char const *fmt, char *out, size_t outlen)
549 {
550 	if (!request->proxy_listener) {
551 		*out = '\0';
552 		return 0;
553 	}
554 
555 	return xlat_listen_common(request, request->proxy_listener, fmt, out, outlen);
556 }
557 
558 #ifdef HAVE_SETUID
559 /*
560  *  Do chroot, if requested.
561  *
562  *  Switch UID and GID to what is specified in the config file
563  */
switch_users(CONF_SECTION * cs)564 static int switch_users(CONF_SECTION *cs)
565 {
566 	bool do_suid = false;
567 	bool do_sgid = false;
568 
569 	/*
570 	 *	Get the current maximum for core files.  Do this
571 	 *	before anything else so as to ensure it's properly
572 	 *	initialized.
573 	 */
574 	if (fr_set_dumpable_init() < 0) {
575 		fr_perror("%s", main_config.name);
576 		return 0;
577 	}
578 
579 	/*
580 	 *	Don't do chroot/setuid/setgid if we're in debugging
581 	 *	as non-root.
582 	 */
583 	if (rad_debug_lvl && (getuid() != 0)) return 1;
584 
585 	if (cf_section_parse(cs, NULL, bootstrap_config) < 0) {
586 		fr_strerror_printf("%s: Error: Failed to parse user/group information.\n",
587 				   main_config.name);
588 		return 0;
589 	}
590 
591 #ifdef HAVE_GRP_H
592 	/*
593 	 *	Get the correct GID for the server.
594 	 */
595 	server_gid = getgid();
596 
597 	if (gid_name) {
598 		struct group *gr;
599 
600 		gr = getgrnam(gid_name);
601 		if (!gr) {
602 			fr_strerror_printf("%s: Cannot get ID for group %s: %s\n",
603 					   main_config.name, gid_name, fr_syserror(errno));
604 			return 0;
605 		}
606 
607 		if (server_gid != gr->gr_gid) {
608 			server_gid = gr->gr_gid;
609 			do_sgid = true;
610 		}
611 	}
612 #endif
613 
614 	/*
615 	 *	Get the correct UID for the server.
616 	 */
617 	server_uid = getuid();
618 
619 	if (uid_name) {
620 		struct passwd *user;
621 
622 		if (rad_getpwnam(cs, &user, uid_name) < 0) {
623 			fr_strerror_printf("%s: Cannot get passwd entry for user %s: %s\n",
624 					   main_config.name, uid_name, fr_strerror());
625 			return 0;
626 		}
627 
628 		/*
629 		 *	We're not the correct user.  Go set that.
630 		 */
631 		if (server_uid != user->pw_uid) {
632 			server_uid = user->pw_uid;
633 			do_suid = true;
634 #ifdef HAVE_INITGROUPS
635 			if (initgroups(uid_name, server_gid) < 0) {
636 				fr_strerror_printf("%s: Cannot initialize supplementary group list for user %s: %s\n",
637 						   main_config.name, uid_name, fr_syserror(errno));
638 				talloc_free(user);
639 				return 0;
640 			}
641 #endif
642 		}
643 
644 		talloc_free(user);
645 	}
646 
647 	/*
648 	 *	Do chroot BEFORE changing UIDs.
649 	 */
650 	if (chroot_dir) {
651 		if (chroot(chroot_dir) < 0) {
652 			fr_strerror_printf("%s: Failed to perform chroot %s: %s",
653 					   main_config.name, chroot_dir, fr_syserror(errno));
654 			return 0;
655 		}
656 
657 		/*
658 		 *	Note that we leave chdir alone.  It may be
659 		 *	OUTSIDE of the root.  This allows us to read
660 		 *	the configuration from "-d ./etc/raddb", with
661 		 *	the chroot as "./chroot/" for example.  After
662 		 *	the server has been loaded, it does a "cd
663 		 *	${logdir}" below, so that core files (if any)
664 		 *	go to a logging directory.
665 		 *
666 		 *	This also allows the configuration of the
667 		 *	server to be outside of the chroot.  If the
668 		 *	server is statically linked, then the only
669 		 *	things needed inside of the chroot are the
670 		 *	logging directories.
671 		 */
672 	}
673 
674 #ifdef HAVE_GRP_H
675 	/*
676 	 *	Set the GID.  Don't bother checking it.
677 	 */
678 	if (do_sgid) {
679 		if (setgid(server_gid) < 0){
680 			fr_strerror_printf("%s: Failed setting group to %s: %s",
681 					   main_config.name, gid_name, fr_syserror(errno));
682 			return 0;
683 		}
684 	}
685 #endif
686 
687 	/*
688 	 *	The directories for PID files and logs must exist.  We
689 	 *	need to create them if we're told to write files to
690 	 *	those directories.
691 	 *
692 	 *	Because this creation is new in 3.0.9, it's a soft
693 	 *	fail.
694 	 *
695 	 */
696 	if (main_config.write_pid) {
697 		char *my_dir;
698 
699 		my_dir = talloc_strdup(NULL, run_dir);
700 		if (rad_mkdir(my_dir, 0750, server_uid, server_gid) < 0) {
701 			DEBUG("Failed to create run_dir %s: %s",
702 			      my_dir, strerror(errno));
703 		}
704 		talloc_free(my_dir);
705 	}
706 
707 	if (default_log.dst == L_DST_FILES) {
708 		char *my_dir;
709 
710 		my_dir = talloc_strdup(NULL, radlog_dir);
711 		if (rad_mkdir(my_dir, 0750, server_uid, server_gid) < 0) {
712 			DEBUG("Failed to create logdir %s: %s",
713 			      my_dir, strerror(errno));
714 		}
715 		talloc_free(my_dir);
716 	}
717 
718 	/*
719 	 *	If we don't already have a log file open, open one
720 	 *	now.  We may not have been logging anything yet.  The
721 	 *	server normally starts up fairly quietly.
722 	 */
723 	if ((default_log.dst == L_DST_FILES) &&
724 	    (default_log.fd < 0)) {
725 		default_log.fd = open(main_config.log_file,
726 				      O_WRONLY | O_APPEND | O_CREAT, 0640);
727 		if (default_log.fd < 0) {
728 			fr_strerror_printf("%s: Failed to open log file %s: %s\n",
729 					   main_config.name, main_config.log_file, fr_syserror(errno));
730 			return 0;
731 		}
732 	}
733 
734 	/*
735 	 *	If we need to change UID, ensure that the log files
736 	 *	have the correct owner && group.
737 	 *
738 	 *	We have to do this because some log files MAY already
739 	 *	have been written as root.  We need to change them to
740 	 *	have the correct ownership before proceeding.
741 	 */
742 	if ((do_suid || do_sgid) &&
743 	    (default_log.dst == L_DST_FILES)) {
744 		if (fchown(default_log.fd, server_uid, server_gid) < 0) {
745 			fr_strerror_printf("%s: Cannot change ownership of log file %s: %s\n",
746 					   main_config.name, main_config.log_file, fr_syserror(errno));
747 			return 0;
748 		}
749 	}
750 
751 	/*
752 	 *	Once we're done with all of the privileged work,
753 	 *	permanently change the UID.
754 	 */
755 	if (do_suid) {
756 		rad_suid_set_down_uid(server_uid);
757 		rad_suid_down();
758 	}
759 
760 	/*
761 	 *	This also clears the dumpable flag if core dumps
762 	 *	aren't allowed.
763 	 */
764 	if (fr_set_dumpable(allow_core_dumps) < 0) {
765 		ERROR("%s", fr_strerror());
766 	}
767 
768 	if (allow_core_dumps) {
769 		INFO("Core dumps are enabled");
770 	}
771 
772 	return 1;
773 }
774 #endif	/* HAVE_SETUID */
775 
776 /** Set the global radius config directory.
777  *
778  * @param ctx Where to allocate the memory for the path string.
779  * @param path to config dir root e.g. /usr/local/etc/raddb
780  */
set_radius_dir(TALLOC_CTX * ctx,char const * path)781 void set_radius_dir(TALLOC_CTX *ctx, char const *path)
782 {
783 	if (radius_dir) {
784 		char *p;
785 
786 		memcpy(&p, &radius_dir, sizeof(p));
787 		talloc_free(p);
788 		radius_dir = NULL;
789 	}
790 	if (path) radius_dir = talloc_strdup(ctx, path);
791 }
792 
793 /** Get the global radius config directory.
794  *
795  * @return the global radius config directory.
796  */
get_radius_dir(void)797 char const *get_radius_dir(void)
798 {
799 	return radius_dir;
800 }
801 
_dlhandle_free(void ** dl_handle)802 static int _dlhandle_free(void **dl_handle)
803 {
804 	dlclose(*dl_handle);
805 	return 0;
806 }
807 
808 /*
809  *	Read config files.
810  *
811  *	This function can ONLY be called from the main server process.
812  */
main_config_init(void)813 int main_config_init(void)
814 {
815 	char const *p = NULL;
816 	CONF_SECTION *cs, *subcs;
817 	struct stat statbuf;
818 	cached_config_t *cc;
819 	char buffer[1024];
820 
821 	if (stat(radius_dir, &statbuf) < 0) {
822 		ERROR("Errors reading %s: %s",
823 		       radius_dir, fr_syserror(errno));
824 		return -1;
825 	}
826 
827 #ifdef S_IWOTH
828 	if ((statbuf.st_mode & S_IWOTH) != 0) {
829 		ERROR("Configuration directory %s is globally writable.  Refusing to start due to insecure configuration.",
830 		       radius_dir);
831 	  return -1;
832 	}
833 #endif
834 
835 #if 0 && defined(S_IROTH)
836 	if (statbuf.st_mode & S_IROTH != 0) {
837 		ERROR("Configuration directory %s is globally readable.  Refusing to start due to insecure configuration.",
838 		       radius_dir);
839 		return -1;
840 	}
841 #endif
842 	INFO("Starting - reading configuration files ...");
843 
844 	/*
845 	 *	We need to load the dictionaries before reading the
846 	 *	configuration files.  This is because of the
847 	 *	pre-compilation in conffile.c.  That should probably
848 	 *	be fixed to be done as a second stage.
849 	 */
850 	if (!main_config.dictionary_dir) {
851 		main_config.dictionary_dir = DICTDIR;
852 	}
853 
854 	/*
855 	 *	About sizeof(REQUEST) + sizeof(RADIUS_PACKET) * 2 + sizeof(VALUE_PAIR) * 400
856 	 *
857 	 *	Which should be enough for many configurations.
858 	 */
859 	main_config.talloc_pool_size = 8 * 1024; /* default */
860 
861 	/*
862 	 *	Read the distribution dictionaries first, then
863 	 *	the ones in raddb.
864 	 */
865 	DEBUG2("including dictionary file %s/%s", main_config.dictionary_dir, RADIUS_DICTIONARY);
866 	if (dict_init(main_config.dictionary_dir, RADIUS_DICTIONARY) != 0) {
867 		ERROR("Errors reading dictionary: %s",
868 		      fr_strerror());
869 		return -1;
870 	}
871 
872 #define DICT_READ_OPTIONAL(_d, _n) \
873 do {\
874 	switch (dict_read(_d, _n)) {\
875 	case -1:\
876 		ERROR("Errors reading %s/%s: %s", _d, _n, fr_strerror());\
877 		return -1;\
878 	case 0:\
879 		DEBUG2("including dictionary file %s/%s", _d,_n);\
880 		break;\
881 	default:\
882 		break;\
883 	}\
884 } while (0)
885 
886 	/*
887 	 *	Try to load protocol-specific dictionaries.  It's OK
888 	 *	if they don't exist.
889 	 */
890 #ifdef WITH_DHCP
891 	DICT_READ_OPTIONAL(main_config.dictionary_dir, "dictionary.dhcp");
892 #endif
893 
894 #ifdef WITH_VMPS
895 	DICT_READ_OPTIONAL(main_config.dictionary_dir, "dictionary.vqp");
896 #endif
897 
898 	/*
899 	 *	It's OK if this one doesn't exist.
900 	 */
901 	DICT_READ_OPTIONAL(radius_dir, RADIUS_DICTIONARY);
902 
903 	cs = cf_section_alloc(NULL, "main", NULL);
904 	if (!cs) return -1;
905 
906 	/*
907 	 *	Add a 'feature' subsection off the main config
908 	 *	We check if it's defined first, as the user may
909 	 *	have defined their own feature flags, or want
910 	 *	to manually override the ones set by modules
911 	 *	or the server.
912 	 */
913 	subcs = cf_section_sub_find(cs, "feature");
914 	if (!subcs) {
915 		subcs = cf_section_alloc(cs, "feature", NULL);
916 		if (!subcs) return -1;
917 
918 		cf_section_add(cs, subcs);
919 	}
920 	version_init_features(subcs);
921 
922 	/*
923 	 *	Add a 'version' subsection off the main config
924 	 *	We check if it's defined first, this is for
925 	 *	backwards compatibility.
926 	 */
927 	subcs = cf_section_sub_find(cs, "version");
928 	if (!subcs) {
929 		subcs = cf_section_alloc(cs, "version", NULL);
930 		if (!subcs) return -1;
931 		cf_section_add(cs, subcs);
932 	}
933 	version_init_numbers(subcs);
934 
935 	/* Read the configuration file */
936 	snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf", radius_dir, main_config.name);
937 	if (cf_file_read(cs, buffer) < 0) {
938 		ERROR("Errors reading or parsing %s", buffer);
939 	failure:
940 		talloc_free(cs);
941 		return -1;
942 	}
943 
944 	/*
945 	 *	Parse environment variables first.
946 	 */
947 	subcs = cf_section_sub_find(cs, "ENV");
948 	if (subcs) {
949 		char const *attr, *value;
950 		CONF_PAIR *cp;
951 		CONF_ITEM *ci;
952 
953 		for (ci = cf_item_find_next(subcs, NULL);
954 		     ci != NULL;
955 		     ci = cf_item_find_next(subcs, ci)) {
956 
957 			if (cf_item_is_data(ci)) continue;
958 
959 			if (!cf_item_is_pair(ci)) {
960 				cf_log_err(ci, "Unexpected item in ENV section");
961 				goto failure;
962 			}
963 
964 			cp = cf_item_to_pair(ci);
965 			if (cf_pair_operator(cp) != T_OP_EQ) {
966 				cf_log_err(ci, "Invalid operator for item in ENV section");
967 				goto failure;
968 			}
969 
970 			attr = cf_pair_attr(cp);
971 			value = cf_pair_value(cp);
972 			if (!value) {
973 				if (unsetenv(attr) < 0) {
974 					cf_log_err(ci, "Failed deleting environment variable %s: %s",
975 						   attr, fr_syserror(errno));
976 					goto failure;
977 				}
978 			} else {
979 				void *handle;
980 				void **handle_p;
981 
982 				if (setenv(attr, value, 1) < 0) {
983 					cf_log_err(ci, "Failed setting environment variable %s: %s",
984 						   attr, fr_syserror(errno));
985 					goto failure;
986 				}
987 
988 				/*
989 				 *	Hacks for LD_PRELOAD.
990 				 */
991 				if (strcmp(attr, "LD_PRELOAD") != 0) continue;
992 
993 				handle = dlopen(value, RTLD_NOW | RTLD_GLOBAL);
994 				if (!handle) {
995 					cf_log_err(ci, "Failed loading library %s: %s", value, dlerror());
996 					goto failure;
997 				}
998 
999 				/*
1000 				 *	Wrap the pointer, so we can set a destructor.
1001 				 */
1002 				MEM(handle_p = talloc(NULL, void *));
1003 				*handle_p = handle;
1004 				talloc_set_destructor(handle_p, _dlhandle_free);
1005 
1006 				(void) cf_data_add(subcs, value, handle, NULL);
1007 			}
1008 		} /* loop over pairs in ENV */
1009 	} /* there's an ENV subsection */
1010 
1011 	/*
1012 	 *	If there was no log destination set on the command line,
1013 	 *	set it now.
1014 	 */
1015 	if (default_log.dst == L_DST_NULL) {
1016 		default_log.dst = L_DST_STDERR;
1017 		default_log.fd = STDERR_FILENO;
1018 
1019 		if (cf_section_parse(cs, NULL, startup_server_config) == -1) {
1020 			fprintf(stderr, "%s: Error: Failed to parse log{} section.\n",
1021 				main_config.name);
1022 			cf_file_free(cs);
1023 			return -1;
1024 		}
1025 
1026 		if (!radlog_dest) {
1027 			fprintf(stderr, "%s: Error: No log destination specified.\n",
1028 				main_config.name);
1029 			cf_file_free(cs);
1030 			return -1;
1031 		}
1032 
1033 		default_log.fd = -1;
1034 		default_log.dst = fr_str2int(log_str2dst, radlog_dest,
1035 					      L_DST_NUM_DEST);
1036 		if (default_log.dst == L_DST_NUM_DEST) {
1037 			fprintf(stderr, "%s: Error: Unknown log_destination %s\n",
1038 				main_config.name, radlog_dest);
1039 			cf_file_free(cs);
1040 			return -1;
1041 		}
1042 
1043 		if (default_log.dst == L_DST_SYSLOG) {
1044 			/*
1045 			 *	Make sure syslog_facility isn't NULL
1046 			 *	before using it
1047 			 */
1048 			if (!syslog_facility) {
1049 				fprintf(stderr, "%s: Error: Syslog chosen but no facility was specified\n",
1050 					main_config.name);
1051 				cf_file_free(cs);
1052 				return -1;
1053 			}
1054 			main_config.syslog_facility = fr_str2int(syslog_facility_table, syslog_facility, -1);
1055 			if (main_config.syslog_facility < 0) {
1056 				fprintf(stderr, "%s: Error: Unknown syslog_facility %s\n",
1057 					main_config.name, syslog_facility);
1058 				cf_file_free(cs);
1059 				return -1;
1060 			}
1061 
1062 #ifdef HAVE_SYSLOG_H
1063 			/*
1064 			 *	Call openlog only once, when the
1065 			 *	program starts.
1066 			 */
1067 			openlog(main_config.name, LOG_PID, main_config.syslog_facility);
1068 #endif
1069 
1070 		} else if (default_log.dst == L_DST_FILES) {
1071 			if (!main_config.log_file) {
1072 				fprintf(stderr, "%s: Error: Specified \"files\" as a log destination, but no log filename was given!\n",
1073 					main_config.name);
1074 				cf_file_free(cs);
1075 				return -1;
1076 			}
1077 		}
1078 	}
1079 
1080 #ifdef HAVE_SETUID
1081 	/*
1082 	 *	Switch users as early as possible.
1083 	 */
1084 	if (!switch_users(cs)) fr_exit(1);
1085 #endif
1086 
1087 	/*
1088 	 *	This allows us to figure out where, relative to
1089 	 *	radiusd.conf, the other configuration files exist.
1090 	 */
1091 	if (cf_section_parse(cs, NULL, server_config) < 0) return -1;
1092 
1093 	/*
1094 	 *	Fix up log_auth, and log_accept and log_reject
1095 	 */
1096 	if (main_config.log_auth) {
1097 		main_config.log_accept = main_config.log_reject = true;
1098 	}
1099 
1100 	/*
1101 	 *	We ignore colourization of output until after the
1102 	 *	configuration files have been parsed.
1103 	 */
1104 	p = getenv("TERM");
1105 	if (do_colourise && p && isatty(default_log.fd) && strstr(p, "xterm")) {
1106 		default_log.colourise = true;
1107 	} else {
1108 		default_log.colourise = false;
1109 	}
1110 
1111 	/*
1112 	 *	Starting the server, WITHOUT "-x" on the
1113 	 *	command-line: use whatever is in the config
1114 	 *	file.
1115 	 */
1116 	if (rad_debug_lvl == 0) {
1117 		rad_debug_lvl = main_config.debug_level;
1118 	}
1119 	fr_debug_lvl = rad_debug_lvl;
1120 
1121 	FR_INTEGER_COND_CHECK("max_request_time", main_config.max_request_time,
1122 			      (main_config.max_request_time != 0), 100);
1123 
1124 	/*
1125 	 *	reject_delay can be zero.  OR 1 though 10.
1126 	 */
1127 	if ((main_config.reject_delay.tv_sec != 0) || (main_config.reject_delay.tv_usec != 0)) {
1128 		FR_TIMEVAL_BOUND_CHECK("reject_delay", &main_config.reject_delay, >=, 1, 0);
1129 	}
1130 	FR_TIMEVAL_BOUND_CHECK("reject_delay", &main_config.reject_delay, <=, 10, 0);
1131 
1132 	FR_INTEGER_BOUND_CHECK("cleanup_delay", main_config.cleanup_delay, <=, 30);
1133 
1134 	FR_INTEGER_BOUND_CHECK("resources.talloc_pool_size", main_config.talloc_pool_size, >=, 2 * 1024);
1135 	FR_INTEGER_BOUND_CHECK("resources.talloc_pool_size", main_config.talloc_pool_size, <=, 1024 * 1024);
1136 
1137 	/*
1138 	 * Set default initial request processing delay to 1/3 of a second.
1139 	 * Will be updated by the lowest response window across all home servers,
1140 	 * if it is less than this.
1141 	 */
1142 	main_config.init_delay.tv_sec = 0;
1143 	main_config.init_delay.tv_usec = 2* (1000000 / 3);
1144 
1145 	/*
1146 	 *	Free the old configuration items, and replace them
1147 	 *	with the new ones.
1148 	 *
1149 	 *	Note that where possible, we do atomic switch-overs,
1150 	 *	to ensure that the pointers are always valid.
1151 	 */
1152 	rad_assert(main_config.config == NULL);
1153 	root_config = main_config.config = cs;
1154 
1155 	DEBUG2("%s: #### Loading Realms and Home Servers ####", main_config.name);
1156 	if (!realms_init(cs)) {
1157 		return -1;
1158 	}
1159 
1160 	DEBUG2("%s: #### Loading Clients ####", main_config.name);
1161 	if (!client_list_parse_section(cs, false)) {
1162 		return -1;
1163 	}
1164 
1165 	/*
1166 	 *	Register the %{config:section.subsection} xlat function.
1167 	 */
1168 	xlat_register("config", xlat_config, NULL, NULL);
1169 	xlat_register("client", xlat_client, NULL, NULL);
1170 	xlat_register("getclient", xlat_getclient, NULL, NULL);
1171 	xlat_register("listen", xlat_listen, NULL, NULL);
1172 	xlat_register("proxy_listen", xlat_proxy_listen, NULL, NULL);
1173 
1174 	/*
1175 	 *  Go update our behaviour, based on the configuration
1176 	 *  changes.
1177 	 */
1178 
1179 	/*
1180 	 *	Sanity check the configuration for internal
1181 	 *	consistency.
1182 	 */
1183 	FR_TIMEVAL_BOUND_CHECK("reject_delay", &main_config.reject_delay, <=, main_config.cleanup_delay, 0);
1184 
1185 	if (chroot_dir) {
1186 		if (chdir(radlog_dir) < 0) {
1187 			ERROR("Failed to 'chdir %s' after chroot: %s",
1188 			       radlog_dir, fr_syserror(errno));
1189 			return -1;
1190 		}
1191 	}
1192 
1193 	cc = talloc_zero(NULL, cached_config_t);
1194 	if (!cc) return -1;
1195 
1196 	cc->cs = talloc_steal(cc ,cs);
1197 	rad_assert(cs_cache == NULL);
1198 	cs_cache = cc;
1199 
1200 	/* Clear any unprocessed configuration errors */
1201 	(void) fr_strerror();
1202 
1203 	return 0;
1204 }
1205 
1206 /*
1207  *	Free the configuration.  Called only when the server is exiting.
1208  */
main_config_free(void)1209 int main_config_free(void)
1210 {
1211 	virtual_servers_free(0);
1212 
1213 	/*
1214 	 *	Clean up the configuration data
1215 	 *	structures.
1216 	 */
1217 	client_list_free(NULL);
1218 	realms_free();
1219 	listen_free(&main_config.listen);
1220 
1221 	/*
1222 	 *	Frees current config and any previous configs.
1223 	 */
1224 	TALLOC_FREE(cs_cache);
1225 	dict_free();
1226 
1227 	return 0;
1228 }
1229 
hup_logfile(void)1230 void hup_logfile(void)
1231 {
1232 	int fd, old_fd;
1233 
1234 	if (default_log.dst != L_DST_FILES) return;
1235 
1236 	fd = open(main_config.log_file,
1237 		  O_WRONLY | O_APPEND | O_CREAT, 0640);
1238 	if (fd >= 0) {
1239 		/*
1240 		 *	Atomic swap. We'd like to keep the old
1241 		 *	FD around so that callers don't
1242 		 *	suddenly find the FD closed, and the
1243 		 *	writes go nowhere.  But that's hard to
1244 		 *	do.  So... we have the case where a
1245 		 *	log message *might* be lost on HUP.
1246 		 */
1247 		old_fd = default_log.fd;
1248 		default_log.fd = fd;
1249 		close(old_fd);
1250 	}
1251 }
1252 
hup_callback(void * ctx,void * data)1253 static int hup_callback(void *ctx, void *data)
1254 {
1255 	CONF_SECTION *modules = ctx;
1256 	CONF_SECTION *cs = data;
1257 	CONF_SECTION *parent;
1258 	char const *name;
1259 	module_instance_t *mi;
1260 
1261 	/*
1262 	 *	Files may be defined in sub-sections of a module
1263 	 *	config.  Walk up the tree until we find the module
1264 	 *	definition.
1265 	 */
1266 	parent = cf_item_parent(cf_section_to_item(cs));
1267 	while (parent != modules) {
1268 		cs = parent;
1269 		parent = cf_item_parent(cf_section_to_item(cs));
1270 
1271 		/*
1272 		 *	Something went wrong.  Oh well...
1273 		 */
1274 		if (!parent) return 0;
1275 	}
1276 
1277 	name = cf_section_name2(cs);
1278 	if (!name) name = cf_section_name1(cs);
1279 
1280 	mi = module_find(modules, name);
1281 	if (!mi) return 0;
1282 
1283 	if ((mi->entry->module->type & RLM_TYPE_HUP_SAFE) == 0) return 0;
1284 
1285 	if (!module_hup_module(mi->cs, mi, time(NULL))) return 0;
1286 
1287 	return 1;
1288 }
1289 
main_config_hup(void)1290 void main_config_hup(void)
1291 {
1292 	int rcode;
1293 	cached_config_t *cc;
1294 	CONF_SECTION *cs;
1295 	time_t when;
1296 	char buffer[1024];
1297 
1298 	static time_t last_hup = 0;
1299 
1300 	/*
1301 	 *	Re-open the log file.  If we can't, then keep logging
1302 	 *	to the old log file.
1303 	 *
1304 	 *	The "open log file" code is here rather than in log.c,
1305 	 *	because it makes that function MUCH simpler.
1306 	 */
1307 	hup_logfile();
1308 
1309 	/*
1310 	 *	Only check the config files every few seconds.
1311 	 */
1312 	when = time(NULL);
1313 	if ((last_hup + 2) >= when) {
1314 		INFO("HUP - Last HUP was too recent.  Ignoring");
1315 		return;
1316 	}
1317 	last_hup = when;
1318 
1319 	rcode = cf_file_changed(cs_cache->cs, hup_callback);
1320 	if (rcode == CF_FILE_NONE) {
1321 		INFO("HUP - No files changed.  Ignoring");
1322 		return;
1323 	}
1324 
1325 	if (rcode == CF_FILE_ERROR) {
1326 		INFO("HUP - Cannot read configuration files.  Ignoring");
1327 		return;
1328 	}
1329 
1330 	/*
1331 	 *	No config files have changed.
1332 	 */
1333 	if ((rcode & CF_FILE_CONFIG) == 0) {
1334 		if ((rcode & CF_FILE_MODULE) != 0) {
1335 			INFO("HUP - Files loaded by a module have changed.");
1336 
1337 			/*
1338 			 *	FIXME: reload the module.
1339 			 */
1340 
1341 		}
1342 		return;
1343 	}
1344 
1345 	cs = cf_section_alloc(NULL, "main", NULL);
1346 	if (!cs) return;
1347 
1348 #ifdef HAVE_SYSTEMD
1349 	sd_notify(0, "RELOADING=1");
1350 #endif
1351 
1352 	/* Read the configuration file */
1353 	snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf", radius_dir, main_config.name);
1354 
1355 	INFO("HUP - Re-reading configuration files");
1356 	if (cf_file_read(cs, buffer) < 0) {
1357 		ERROR("Failed to re-read or parse %s", buffer);
1358 		talloc_free(cs);
1359 		return;
1360 	}
1361 
1362 	cc = talloc_zero(cs_cache, cached_config_t);
1363 	if (!cc) {
1364 		ERROR("Out of memory");
1365 		return;
1366 	}
1367 
1368 	/*
1369 	 *	Save the current configuration.  Note that we do NOT
1370 	 *	free older ones.  We should probably do so at some
1371 	 *	point.  Doing so will require us to mark which modules
1372 	 *	are still in use, and which aren't.  Modules that
1373 	 *	can't be HUPed always use the original configuration.
1374 	 *	Modules that can be HUPed use one of the newer
1375 	 *	configurations.
1376 	 */
1377 	cc->created = time(NULL);
1378 	cc->cs = talloc_steal(cc, cs);
1379 	cc->next = cs_cache;
1380 	cs_cache = cc;
1381 
1382 	INFO("HUP - loading modules");
1383 
1384 	/*
1385 	 *	Prefer the new module configuration.
1386 	 */
1387 	modules_hup(cf_section_sub_find(cs, "modules"));
1388 
1389 	/*
1390 	 *	Load new servers BEFORE freeing old ones.
1391 	 */
1392 	virtual_servers_load(cs);
1393 
1394 	virtual_servers_free(cc->created - (main_config.max_request_time * 4));
1395 
1396 #ifdef HAVE_SYSTEMD
1397 	/*
1398 	 * If RELOADING=1 event is sent then it needed also a "READY=1" notification
1399 	 * when it completed reloading its configuration.
1400 	 */
1401 	sd_notify(0, "READY=1");
1402 #endif
1403 }
1404