Add panic action support to radiusd and unittest
[freeradius.git] / src / main / mainconfig.c
1 /*
2  * mainconf.c   Handle the server's configuration.
3  *
4  * Version:     $Id$
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$")
25
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/modules.h>
28 #include <freeradius-devel/rad_assert.h>
29
30 #include <sys/stat.h>
31
32 #ifdef HAVE_SYS_RESOURCE_H
33 #include <sys/resource.h>
34 #endif
35
36 #ifdef HAVE_PWD_H
37 #include <pwd.h>
38 #endif
39
40 #ifdef HAVE_GRP_H
41 #include <grp.h>
42 #endif
43
44 #ifdef HAVE_SYS_PRCTL_H
45 #include <sys/prctl.h>
46 #endif
47
48 #ifdef HAVE_SYSLOG_H
49 #       include <syslog.h>
50 #endif
51
52 #ifdef HAVE_SYS_STAT_H
53 #include <sys/stat.h>
54 #endif
55
56 #ifdef HAVE_FCNTL_H
57 #include <fcntl.h>
58 #endif
59
60 struct main_config_t mainconfig;
61 char *debug_condition = NULL;
62 extern int log_dates_utc;
63
64 typedef struct cached_config_t {
65         struct cached_config_t *next;
66         time_t          created;
67         CONF_SECTION    *cs;
68 } cached_config_t;
69
70 static cached_config_t  *cs_cache = NULL;
71
72 /*
73  *      Temporary local variables for parsing the configuration
74  *      file.
75  */
76 #ifdef HAVE_SETUID
77 /*
78  *      Systems that have set/getresuid also have setuid.
79  */
80 static uid_t server_uid = 0;
81 static gid_t server_gid = 0;
82 static char const *uid_name = NULL;
83 static char const *gid_name = NULL;
84 #endif
85 static char const *chroot_dir = NULL;
86 static bool allow_core_dumps = false;
87 static char const *radlog_dest = NULL;
88
89 /*
90  *      These are not used anywhere else..
91  */
92 static char const       *localstatedir = NULL;
93 static char const       *prefix = NULL;
94 static char             my_name;
95 static char const       *sbindir = NULL;
96 static char const       *run_dir = NULL;
97 static char             *syslog_facility = NULL;
98 static bool             do_colourise = false;
99
100
101 /*
102  *  Security configuration for the server.
103  */
104 static const CONF_PARSER security_config[] = {
105         { "max_attributes",  PW_TYPE_INTEGER, 0, &fr_max_attributes, STRINGIFY(0) },
106         { "reject_delay",  PW_TYPE_INTEGER, 0, &mainconfig.reject_delay, STRINGIFY(0) },
107         { "status_server", PW_TYPE_BOOLEAN, 0, &mainconfig.status_server, "no"},
108         { NULL, -1, 0, NULL, NULL }
109 };
110
111
112 /*
113  *      Logging configuration for the server.
114  */
115 static const CONF_PARSER logdest_config[] = {
116         { "destination",  PW_TYPE_STRING_PTR, 0, &radlog_dest, "files" },
117         { "syslog_facility",  PW_TYPE_STRING_PTR, 0, &syslog_facility, STRINGIFY(0) },
118
119         { "file", PW_TYPE_STRING_PTR, 0, &mainconfig.log_file, "${logdir}/radius.log" },
120         { "requests", PW_TYPE_STRING_PTR, 0, &default_log.file, NULL },
121         { NULL, -1, 0, NULL, NULL }
122 };
123
124
125 static const CONF_PARSER serverdest_config[] = {
126         { "log", PW_TYPE_SUBSECTION, 0, NULL, (void const *) logdest_config },
127         { "log_file", PW_TYPE_STRING_PTR, 0, &mainconfig.log_file, NULL },
128         { "log_destination", PW_TYPE_STRING_PTR, 0, &radlog_dest, NULL },
129         { "use_utc", PW_TYPE_BOOLEAN, 0, &log_dates_utc, NULL },
130         { NULL, -1, 0, NULL, NULL }
131 };
132
133
134 static const CONF_PARSER log_config_nodest[] = {
135         { "stripped_names", PW_TYPE_BOOLEAN, 0, &log_stripped_names,"no" },
136         { "auth", PW_TYPE_BOOLEAN, 0, &mainconfig.log_auth, "no" },
137         { "auth_badpass", PW_TYPE_BOOLEAN, 0, &mainconfig.log_auth_badpass, "no" },
138         { "auth_goodpass", PW_TYPE_BOOLEAN, 0, &mainconfig.log_auth_goodpass, "no" },
139         { "msg_badpass", PW_TYPE_STRING_PTR, 0, &mainconfig.auth_badpass_msg, NULL},
140         { "msg_goodpass", PW_TYPE_STRING_PTR, 0, &mainconfig.auth_goodpass_msg, NULL},
141         { "colourise", PW_TYPE_BOOLEAN, 0, &do_colourise, NULL },
142         { "use_utc", PW_TYPE_BOOLEAN, 0, &log_dates_utc, NULL },
143
144         { NULL, -1, 0, NULL, NULL }
145 };
146
147
148 /*
149  *  A mapping of configuration file names to internal variables
150  */
151 static const CONF_PARSER server_config[] = {
152         /*
153          *      FIXME: 'prefix' is the ONLY one which should be
154          *      configured at compile time.  Hard-coding it here is
155          *      bad.  It will be cleaned up once we clean up the
156          *      hard-coded defines for the locations of the various
157          *      files.
158          */
159         { "name",              PW_TYPE_STRING_PTR, 0, &my_name,   "radiusd"},
160         { "prefix",          PW_TYPE_STRING_PTR, 0, &prefix,        "/usr/local"},
161         { "localstatedir",      PW_TYPE_STRING_PTR, 0, &localstatedir,     "${prefix}/var"},
162         { "sbindir",        PW_TYPE_STRING_PTR, 0, &sbindir,        "${prefix}/sbin"},
163         { "logdir",          PW_TYPE_STRING_PTR, 0, &radlog_dir,        "${localstatedir}/log"},
164         { "run_dir",        PW_TYPE_STRING_PTR, 0, &run_dir,       "${localstatedir}/run/${name}"},
165         { "libdir",          PW_TYPE_STRING_PTR, 0, &radlib_dir,        "${prefix}/lib"},
166         { "dict_dir",           PW_TYPE_STRING_PTR | PW_TYPE_NO_OVERWRITE, 0,
167           &mainconfig.dictionary_dir, "${prefix}/share" },
168         { "radacctdir",  PW_TYPE_STRING_PTR, 0, &radacct_dir,       "${logdir}/radacct" },
169         { "panic_action", PW_TYPE_STRING_PTR, 0, &mainconfig.panic_action, NULL},
170         { "hostname_lookups",   PW_TYPE_BOOLEAN,    0, &fr_dns_lookups,      "no" },
171         { "max_request_time", PW_TYPE_INTEGER, 0, &mainconfig.max_request_time, STRINGIFY(MAX_REQUEST_TIME) },
172         { "cleanup_delay", PW_TYPE_INTEGER, 0, &mainconfig.cleanup_delay, STRINGIFY(CLEANUP_DELAY) },
173         { "max_requests", PW_TYPE_INTEGER, 0, &mainconfig.max_requests, STRINGIFY(MAX_REQUESTS) },
174 #ifdef DELETE_BLOCKED_REQUESTS
175         { "delete_blocked_requests", PW_TYPE_INTEGER, 0, &mainconfig.kill_unresponsive_children, STRINGIFY(false) },
176 #endif
177         { "pidfile", PW_TYPE_STRING_PTR, 0, &mainconfig.pid_file, "${run_dir}/radiusd.pid"},
178         { "checkrad", PW_TYPE_STRING_PTR, 0, &mainconfig.checkrad, "${sbindir}/checkrad" },
179
180         { "debug_level", PW_TYPE_INTEGER, 0, &mainconfig.debug_level, "0"},
181
182 #ifdef WITH_PROXY
183         { "proxy_requests", PW_TYPE_BOOLEAN, 0, &mainconfig.proxy_requests, "yes" },
184 #endif
185         { "log", PW_TYPE_SUBSECTION, 0, NULL, (void const *) log_config_nodest },
186
187         /*
188          *      People with old configs will have these.  They are listed
189          *      AFTER the "log" section, so if they exist in radiusd.conf,
190          *      it will prefer "log_foo = bar" to "log { foo = bar }".
191          *      They're listed with default values of NULL, so that if they
192          *      DON'T exist in radiusd.conf, then the previously parsed
193          *      values for "log { foo = bar}" will be used.
194          */
195         { "log_auth", PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, 0, &mainconfig.log_auth, NULL },
196         { "log_auth_badpass", PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, 0, &mainconfig.log_auth_badpass, NULL },
197         { "log_auth_goodpass", PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, 0, &mainconfig.log_auth_goodpass, NULL },
198         { "log_stripped_names", PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, 0, &log_stripped_names, NULL },
199
200         {  "security", PW_TYPE_SUBSECTION, 0, NULL, (void const *) security_config },
201
202         { NULL, -1, 0, NULL, NULL }
203 };
204
205 static const CONF_PARSER bootstrap_security_config[] = {
206 #ifdef HAVE_SETUID
207         { "user",  PW_TYPE_STRING_PTR, 0, &uid_name, NULL },
208         { "group",  PW_TYPE_STRING_PTR, 0, &gid_name, NULL },
209 #endif
210         { "chroot",  PW_TYPE_STRING_PTR, 0, &chroot_dir, NULL },
211         { "allow_core_dumps", PW_TYPE_BOOLEAN, 0, &allow_core_dumps, "no" },
212
213         { NULL, -1, 0, NULL, NULL }
214 };
215
216 static const CONF_PARSER bootstrap_config[] = {
217         {  "security", PW_TYPE_SUBSECTION, 0, NULL, (void const *) bootstrap_security_config },
218
219         /*
220          *      For backwards compatibility.
221          */
222 #ifdef HAVE_SETUID
223         { "user",  PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, 0, &uid_name, NULL },
224         { "group",  PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, 0, &gid_name, NULL },
225 #endif
226         { "chroot",  PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, 0, &chroot_dir, NULL },
227         { "allow_core_dumps", PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, 0, &allow_core_dumps, NULL },
228
229         { NULL, -1, 0, NULL, NULL }
230 };
231
232
233
234 #define MAX_ARGV (256)
235
236
237 static size_t config_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
238 {
239         size_t len = 0;
240         static char const *disallowed = "%{}\\'\"`";
241
242         while (in[0]) {
243                 /*
244                  *      Non-printable characters get replaced with their
245                  *      mime-encoded equivalents.
246                  */
247                 if ((in[0] < 32)) {
248                         if (outlen <= 3) break;
249
250                         snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
251                         in++;
252                         out += 3;
253                         outlen -= 3;
254                         len += 3;
255                         continue;
256
257                 } else if (strchr(disallowed, *in) != NULL) {
258                         if (outlen <= 2) break;
259
260                         out[0] = '\\';
261                         out[1] = *in;
262                         in++;
263                         out += 2;
264                         outlen -= 2;
265                         len += 2;
266                         continue;
267                 }
268
269                 /*
270                  *      Only one byte left.
271                  */
272                 if (outlen <= 1) {
273                         break;
274                 }
275
276                 /*
277                  *      Allowed character.
278                  */
279                 *out = *in;
280                 out++;
281                 in++;
282                 outlen--;
283                 len++;
284         }
285         *out = '\0';
286         return len;
287 }
288
289 /*
290  *      Xlat for %{config:section.subsection.attribute}
291  */
292 static ssize_t xlat_config(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen)
293 {
294         char const *value;
295         CONF_PAIR *cp;
296         CONF_ITEM *ci;
297         char buffer[1024];
298
299         /*
300          *      Expand it safely.
301          */
302         if (radius_xlat(buffer, sizeof(buffer), request, fmt, config_escape_func, NULL) < 0) {
303                 return 0;
304         }
305
306         ci = cf_reference_item(request->root->config,
307                                request->root->config, buffer);
308         if (!ci || !cf_item_is_pair(ci)) {
309                 REDEBUG("Config item \"%s\" does not exist", fmt);
310                 *out = '\0';
311                 return -1;
312         }
313
314         cp = cf_itemtopair(ci);
315
316         /*
317          *  Ensure that we only copy what's necessary.
318          *
319          *  If 'outlen' is too small, then the output is chopped to fit.
320          */
321         value = cf_pair_value(cp);
322         if (!value) {
323                 out[0] = '\0';
324                 return 0;
325         }
326
327         if (outlen > strlen(value)) {
328                 outlen = strlen(value) + 1;
329         }
330
331         strlcpy(out, value, outlen);
332
333         return strlen(out);
334 }
335
336
337 /*
338  *      Xlat for %{client:foo}
339  */
340 static ssize_t xlat_client(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen)
341 {
342         char const *value = NULL;
343         CONF_PAIR *cp;
344
345         if (!fmt || !out || (outlen < 1)) return 0;
346
347         if (!request || !request->client) {
348                 RWDEBUG("No client associated with this request");
349                 *out = '\0';
350                 return 0;
351         }
352
353         cp = cf_pair_find(request->client->cs, fmt);
354         if (!cp || !(value = cf_pair_value(cp))) {
355                 if (strcmp(fmt, "shortname") == 0) {
356                         strlcpy(out, request->client->shortname, outlen);
357                         return strlen(out);
358                 }
359                 RDEBUG("Client does not contain config item \"%s\"", fmt);
360                 *out = '\0';
361                 return 0;
362         }
363
364         strlcpy(out, value, outlen);
365
366         return strlen(out);
367 }
368
369 /*
370  *      Xlat for %{getclient:<ipaddr>.foo}
371  */
372 static ssize_t xlat_getclient(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen)
373 {
374         char const *value = NULL;
375         char buffer[INET6_ADDRSTRLEN], *q;
376         char const *p = fmt;
377         fr_ipaddr_t ip;
378         CONF_PAIR *cp;
379         RADCLIENT *client = NULL;
380
381         if (!fmt || !out || (outlen < 1)) return 0;
382
383         q = strrchr(p, '.');
384         if (!q || (q == p) || (((size_t)(q - p)) > sizeof(buffer))) {
385                 REDEBUG("Invalid client string");
386                 goto error;
387         }
388
389         strlcpy(buffer, p, (q + 1) - p);
390         if (ip_ptonx(buffer, &ip) <= 0) {
391                 REDEBUG("\"%s\" is not a valid IPv4 or IPv6 address", buffer);
392                 goto error;
393         }
394
395         fmt = q + 1;
396
397         client = client_find(NULL, &ip, IPPROTO_IP);
398         if (!client) {
399                 RDEBUG("No client found with IP \"%s\"", buffer);
400                 *out = '\0';
401                 return 0;
402         }
403
404         cp = cf_pair_find(client->cs, fmt);
405         if (!cp || !(value = cf_pair_value(cp))) {
406                 if (strcmp(fmt, "shortname") == 0) {
407                         strlcpy(out, request->client->shortname, outlen);
408                         return strlen(out);
409                 }
410                 RDEBUG("Client does not contain config item \"%s\"", fmt);
411                 *out = '\0';
412                 return 0;
413         }
414
415         strlcpy(out, value, outlen);
416         return strlen(out);
417
418         error:
419         *out = '\0';
420         return -1;
421 }
422
423 #ifdef HAVE_SYS_RESOURCE_H
424 static struct rlimit core_limits;
425 #endif
426
427 static void fr_set_dumpable(void)
428 {
429         /*
430          *      If configured, turn core dumps off.
431          */
432         if (!allow_core_dumps) {
433 #ifdef HAVE_SYS_RESOURCE_H
434                 struct rlimit no_core;
435
436
437                 no_core.rlim_cur = 0;
438                 no_core.rlim_max = 0;
439
440                 if (setrlimit(RLIMIT_CORE, &no_core) < 0) {
441                         ERROR("Failed disabling core dumps: %s",
442                                fr_syserror(errno));
443                 }
444 #endif
445                 return;
446         }
447
448         /*
449          *      Set or re-set the dumpable flag.
450          */
451 #ifdef HAVE_SYS_PRCTL_H
452 #ifdef PR_SET_DUMPABLE
453         if (prctl(PR_SET_DUMPABLE, 1) < 0) {
454                 ERROR("Cannot re-enable core dumps: prctl(PR_SET_DUMPABLE) failed: '%s'",
455                        fr_syserror(errno));
456         }
457 #endif
458 #endif
459
460         /*
461          *      Reset the core dump limits to their original value.
462          */
463 #ifdef HAVE_SYS_RESOURCE_H
464         if (setrlimit(RLIMIT_CORE, &core_limits) < 0) {
465                 ERROR("Cannot update core dump limit: %s",
466                        fr_syserror(errno));
467         }
468 #endif
469 }
470
471 #ifdef HAVE_SETUID
472 static int doing_setuid = false;
473
474 #if defined(HAVE_SETRESUID) && defined (HAVE_GETRESUID)
475 void fr_suid_up(void)
476 {
477         uid_t ruid, euid, suid;
478
479         if (getresuid(&ruid, &euid, &suid) < 0) {
480                 ERROR("Failed getting saved UID's");
481                 fr_exit_now(1);
482         }
483
484         if (setresuid(-1, suid, -1) < 0) {
485                 ERROR("Failed switching to privileged user");
486                 fr_exit_now(1);
487         }
488
489         if (geteuid() != suid) {
490                 ERROR("Switched to unknown UID");
491                 fr_exit_now(1);
492         }
493 }
494
495 void fr_suid_down(void)
496 {
497         if (!doing_setuid) return;
498
499         if (setresuid(-1, server_uid, geteuid()) < 0) {
500                 fprintf(stderr, "%s: Failed switching to uid %s: %s\n",
501                         progname, uid_name, fr_syserror(errno));
502                 fr_exit_now(1);
503         }
504
505         if (geteuid() != server_uid) {
506                 fprintf(stderr, "%s: Failed switching uid: UID is incorrect\n",
507                         progname);
508                 fr_exit_now(1);
509         }
510
511         fr_set_dumpable();
512 }
513
514 void fr_suid_down_permanent(void)
515 {
516         if (!doing_setuid) return;
517
518         if (setresuid(server_uid, server_uid, server_uid) < 0) {
519                 ERROR("Failed in permanent switch to uid %s: %s",
520                        uid_name, fr_syserror(errno));
521                 fr_exit_now(1);
522         }
523
524         if (geteuid() != server_uid) {
525                 ERROR("Switched to unknown uid");
526                 fr_exit_now(1);
527         }
528
529         fr_set_dumpable();
530 }
531 #else
532 /*
533  *      Much less secure...
534  */
535 void fr_suid_up(void)
536 {
537 }
538 void fr_suid_down(void)
539 {
540         if (!uid_name) return;
541
542         if (setuid(server_uid) < 0) {
543                 fprintf(stderr, "%s: Failed switching to uid %s: %s\n",
544                         progname, uid_name, fr_syserror(errno));
545                 fr_exit(1);
546         }
547
548         fr_set_dumpable();
549 }
550 void fr_suid_down_permanent(void)
551 {
552         fr_set_dumpable();
553 }
554 #endif /* HAVE_SETRESUID && HAVE_GETRESUID */
555 #else  /* HAVE_SETUID */
556 void fr_suid_up(void)
557 {
558 }
559 void fr_suid_down(void)
560 {
561         fr_set_dumpable();
562 }
563 void fr_suid_down_permanent(void)
564 {
565         fr_set_dumpable();
566 }
567 #endif /* HAVE_SETUID */
568
569 #ifdef HAVE_SETUID
570
571 /*
572  *  Do chroot, if requested.
573  *
574  *  Switch UID and GID to what is specified in the config file
575  */
576 static int switch_users(CONF_SECTION *cs)
577 {
578 #ifdef HAVE_SYS_RESOURCE_H
579         /*
580          *      Get the current maximum for core files.  Do this
581          *      before anything else so as to ensure it's properly
582          *      initialized.
583          */
584         if (getrlimit(RLIMIT_CORE, &core_limits) < 0) {
585                 ERROR("Failed to get current core limit:  %s", fr_syserror(errno));
586                 return 0;
587         }
588 #endif
589
590         /*
591          *      Don't do chroot/setuid/setgid if we're in debugging
592          *      as non-root.
593          */
594         if (debug_flag && (getuid() != 0)) return 1;
595
596         if (cf_section_parse(cs, NULL, bootstrap_config) < 0) {
597                 fprintf(stderr, "radiusd: Error: Failed to parse user/group information.\n");
598                 return 0;
599         }
600
601
602 #ifdef HAVE_GRP_H
603         /*  Set GID.  */
604         if (gid_name) {
605                 struct group *gr;
606
607                 gr = getgrnam(gid_name);
608                 if (gr == NULL) {
609                         fprintf(stderr, "%s: Cannot get ID for group %s: %s\n",
610                                 progname, gid_name, fr_syserror(errno));
611                         return 0;
612                 }
613                 server_gid = gr->gr_gid;
614         } else {
615                 server_gid = getgid();
616         }
617 #endif
618
619 #ifdef HAVE_PWD_H
620         /*  Set UID.  */
621         if (uid_name) {
622                 struct passwd *pw;
623
624                 pw = getpwnam(uid_name);
625                 if (pw == NULL) {
626                         fprintf(stderr, "%s: Cannot get passwd entry for user %s: %s\n",
627                                 progname, uid_name, fr_syserror(errno));
628                         return 0;
629                 }
630
631                 if (getuid() == pw->pw_uid) {
632                         uid_name = NULL;
633                 } else {
634
635                         server_uid = pw->pw_uid;
636 #ifdef HAVE_INITGROUPS
637                         if (initgroups(uid_name, server_gid) < 0) {
638                                 fprintf(stderr, "%s: Cannot initialize supplementary group list for user %s: %s\n",
639                                         progname, uid_name, fr_syserror(errno));
640                                 return 0;
641                         }
642 #endif
643                 }
644         } else {
645                 server_uid = getuid();
646         }
647 #endif
648
649         if (chroot_dir) {
650                 if (chroot(chroot_dir) < 0) {
651                         fprintf(stderr, "%s: Failed to perform chroot %s: %s",
652                                 progname, chroot_dir, fr_syserror(errno));
653                         return 0;
654                 }
655
656                 /*
657                  *      Note that we leave chdir alone.  It may be
658                  *      OUTSIDE of the root.  This allows us to read
659                  *      the configuration from "-d ./etc/raddb", with
660                  *      the chroot as "./chroot/" for example.  After
661                  *      the server has been loaded, it does a "cd
662                  *      ${logdir}" below, so that core files (if any)
663                  *      go to a logging directory.
664                  *
665                  *      This also allows the configuration of the
666                  *      server to be outside of the chroot.  If the
667                  *      server is statically linked, then the only
668                  *      things needed inside of the chroot are the
669                  *      logging directories.
670                  */
671         }
672
673 #ifdef HAVE_GRP_H
674         /*  Set GID.  */
675         if (gid_name && (setgid(server_gid) < 0)) {
676                 fprintf(stderr, "%s: Failed setting group to %s: %s",
677                         progname, gid_name, fr_syserror(errno));
678                 return 0;
679         }
680 #endif
681
682 #ifdef HAVE_SETUID
683         /*
684          *      Just before losing root permissions, ensure that the
685          *      log files have the correct owner && group.
686          *
687          *      We have to do this because the log file MAY have been
688          *      specified on the command-line.
689          */
690         if (uid_name || gid_name) {
691                 if ((default_log.dest == L_DST_FILES) &&
692                     (default_log.fd < 0)) {
693                         default_log.fd = open(mainconfig.log_file,
694                                               O_WRONLY | O_APPEND | O_CREAT, 0640);
695                         if (default_log.fd < 0) {
696                                 fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", mainconfig.log_file, fr_syserror(errno));
697                                 return 0;
698                         }
699
700                         if (chown(mainconfig.log_file, server_uid, server_gid) < 0) {
701                                 fprintf(stderr, "%s: Cannot change ownership of log file %s: %s\n",
702                                         progname, mainconfig.log_file, fr_syserror(errno));
703                                 return 0;
704                         }
705                 }
706         }
707
708         if (uid_name) {
709                 doing_setuid = true;
710
711                 fr_suid_down();
712         }
713 #endif
714
715         /*
716          *      This also clears the dumpable flag if core dumps
717          *      aren't allowed.
718          */
719         fr_set_dumpable();
720
721         if (allow_core_dumps) {
722                 INFO("Core dumps are enabled.");
723         }
724
725         return 1;
726 }
727 #endif  /* HAVE_SETUID */
728
729
730 /*
731  *      Read config files.
732  *
733  *      This function can ONLY be called from the main server process.
734  */
735 int read_mainconfig(int reload)
736 {
737         char const *p = NULL;
738         CONF_SECTION *cs;
739         struct stat statbuf;
740         cached_config_t *cc;
741         char buffer[1024];
742
743         if (reload != 0) {
744                 ERROR("Reload is not implemented");
745                 return -1;
746         }
747
748         if (stat(radius_dir, &statbuf) < 0) {
749                 ERROR("Errors reading %s: %s",
750                        radius_dir, fr_syserror(errno));
751                 return -1;
752         }
753
754 #ifdef S_IWOTH
755         if ((statbuf.st_mode & S_IWOTH) != 0) {
756                 ERROR("Configuration directory %s is globally writable.  Refusing to start due to insecure configuration.",
757                        radius_dir);
758           return -1;
759         }
760 #endif
761
762 #ifdef S_IROTH
763         if (0 && (statbuf.st_mode & S_IROTH) != 0) {
764                 ERROR("Configuration directory %s is globally readable.  Refusing to start due to insecure configuration.",
765                        radius_dir);
766                 return -1;
767         }
768 #endif
769         INFO("Starting - reading configuration files ...");
770
771         /*
772          *      Load the dictionaries.  If it's unset, set it to the
773          *      raddb directory, as per older versions of the server.
774          *      This should probably never happen, but we should
775          *      ideally be backwards compatible.
776          */
777         if (!mainconfig.dictionary_dir || !*mainconfig.dictionary_dir) {
778                 mainconfig.dictionary_dir = talloc_strdup(NULL, radius_dir);
779
780                 DEBUG2("including dictionary file %s/%s", mainconfig.dictionary_dir, RADIUS_DICTIONARY);
781                 if (dict_init(mainconfig.dictionary_dir, RADIUS_DICTIONARY) != 0) {
782                         ERROR("Errors reading dictionary: %s",
783                               fr_strerror());
784                         return -1;
785                 }
786         } else {
787                 int rcode;
788
789                 /*
790                  *      Read the distribution dictionaries first, then
791                  *      the ones in raddb.
792                  */
793                 DEBUG2("including dictionary file %s/%s", mainconfig.dictionary_dir, RADIUS_DICTIONARY);
794                 if (dict_init(mainconfig.dictionary_dir, RADIUS_DICTIONARY) != 0) {
795                         ERROR("Errors reading dictionary: %s",
796                               fr_strerror());
797                         return -1;
798                 }
799
800                 /*
801                  *      It's OK if this one doesn't exist.
802                  */
803                 rcode = dict_read(radius_dir, RADIUS_DICTIONARY);
804                 if (rcode == -1) {
805                         ERROR("Errors reading %s/%s: %s", radius_dir, RADIUS_DICTIONARY,
806                               fr_strerror());
807                         return -1;
808                 }
809
810                 /*
811                  *      We print this after reading it.  That way if
812                  *      it doesn't exist, it's OK, and we don't print
813                  *      anything.
814                  */
815                 if (rcode == 0) {
816                         DEBUG2("including dictionary file %s/%s", radius_dir, RADIUS_DICTIONARY);
817                 }
818         }
819
820         /* Read the configuration file */
821         snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf",
822                  radius_dir, mainconfig.name);
823         if ((cs = cf_file_read(buffer)) == NULL) {
824                 ERROR("Errors reading or parsing %s", buffer);
825                 return -1;
826         }
827
828         /*
829          *      If there was no log destination set on the command line,
830          *      set it now.
831          */
832         if (default_log.dest == L_DST_NULL) {
833                 if (cf_section_parse(cs, NULL, serverdest_config) < 0) {
834                         fprintf(stderr, "radiusd: Error: Failed to parse log{} section.\n");
835                         cf_file_free(cs);
836                         return -1;
837                 }
838
839                 if (!radlog_dest) {
840                         fprintf(stderr, "radiusd: Error: No log destination specified.\n");
841                         cf_file_free(cs);
842                         return -1;
843                 }
844
845                 default_log.dest = fr_str2int(log_str2dst, radlog_dest,
846                                               L_DST_NUM_DEST);
847                 if (default_log.dest == L_DST_NUM_DEST) {
848                         fprintf(stderr, "radiusd: Error: Unknown log_destination %s\n",
849                                 radlog_dest);
850                         cf_file_free(cs);
851                         return -1;
852                 }
853
854                 if (default_log.dest == L_DST_SYSLOG) {
855                         /*
856                          *      Make sure syslog_facility isn't NULL
857                          *      before using it
858                          */
859                         if (!syslog_facility) {
860                                 fprintf(stderr, "radiusd: Error: Syslog chosen but no facility was specified\n");
861                                 cf_file_free(cs);
862                                 return -1;
863                         }
864                         mainconfig.syslog_facility = fr_str2int(syslog_str2fac, syslog_facility, -1);
865                         if (mainconfig.syslog_facility < 0) {
866                                 fprintf(stderr, "radiusd: Error: Unknown syslog_facility %s\n",
867                                         syslog_facility);
868                                 cf_file_free(cs);
869                                 return -1;
870                         }
871
872 #ifdef HAVE_SYSLOG_H
873                         /*
874                          *      Call openlog only once, when the
875                          *      program starts.
876                          */
877                         openlog(progname, LOG_PID, mainconfig.syslog_facility);
878 #endif
879
880                 } else if (default_log.dest == L_DST_FILES) {
881                         if (!mainconfig.log_file) {
882                                 fprintf(stderr, "radiusd: Error: Specified \"files\" as a log destination, but no log filename was given!\n");
883                                 cf_file_free(cs);
884                                 return -1;
885                         }
886                 }
887         }
888
889 #ifdef HAVE_SETUID
890         /*
891          *      Switch users as early as possible.
892          */
893         if (!switch_users(cs)) fr_exit(1);
894 #endif
895
896         /*
897          *      Open the log file AFTER switching uid / gid.  If we
898          *      did switch uid/gid, then the code in switch_users()
899          *      took care of setting the file permissions correctly.
900          */
901         if ((default_log.dest == L_DST_FILES) &&
902             (default_log.fd < 0)) {
903                 default_log.fd = open(mainconfig.log_file,
904                                             O_WRONLY | O_APPEND | O_CREAT, 0640);
905                 if (default_log.fd < 0) {
906                         fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", mainconfig.log_file, fr_syserror(errno));
907                         cf_file_free(cs);
908                         return -1;
909                 }
910         }
911
912         /*
913          *      This allows us to figure out where, relative to
914          *      radiusd.conf, the other configuration files exist.
915          */
916         if (cf_section_parse(cs, NULL, server_config) < 0) {
917                 return -1;
918         }
919
920         /*
921          *      We ignore colourization of output until after the
922          *      configuration files have been parsed.
923          */
924         p = getenv("TERM");
925         if (do_colourise && p && isatty(default_log.fd) && strstr(p, "xterm")) {
926                 default_log.colourise = true;
927         } else {
928                 default_log.colourise = false;
929         }
930
931         if (mainconfig.max_request_time == 0) mainconfig.max_request_time = 100;
932         if (mainconfig.reject_delay > 5) mainconfig.reject_delay = 5;
933         if (mainconfig.cleanup_delay > 5) mainconfig.cleanup_delay =5;
934
935         /*
936          *      Free the old configuration items, and replace them
937          *      with the new ones.
938          *
939          *      Note that where possible, we do atomic switch-overs,
940          *      to ensure that the pointers are always valid.
941          */
942         rad_assert(mainconfig.config == NULL);
943         root_config = mainconfig.config = cs;
944
945         DEBUG2("%s: #### Loading Realms and Home Servers ####", mainconfig.name);
946         if (!realms_init(cs)) {
947                 return -1;
948         }
949
950         DEBUG2("%s: #### Loading Clients ####", mainconfig.name);
951         if (!clients_parse_section(cs, false)) {
952                 return -1;
953         }
954
955         /*
956          *  Register the %{config:section.subsection} xlat function.
957          */
958         xlat_register("config", xlat_config, NULL, NULL);
959         xlat_register("client", xlat_client, NULL, NULL);
960         xlat_register("getclient", xlat_getclient, NULL, NULL);
961
962         /*
963          *      Starting the server, WITHOUT "-x" on the
964          *      command-line: use whatever is in the config
965          *      file.
966          */
967         if (debug_flag == 0) {
968                 debug_flag = mainconfig.debug_level;
969         }
970         fr_debug_flag = debug_flag;
971
972         /*
973          *  Go update our behaviour, based on the configuration
974          *  changes.
975          */
976
977         /*
978          *      Sanity check the configuration for internal
979          *      consistency.
980          */
981         if (mainconfig.reject_delay > mainconfig.cleanup_delay) {
982                 mainconfig.reject_delay = mainconfig.cleanup_delay;
983         }
984         if (mainconfig.reject_delay < 0) mainconfig.reject_delay = 0;
985
986         /*  Reload the modules.  */
987         if (setup_modules(reload, mainconfig.config) < 0) {
988                 return -1;
989         }
990
991         if (chroot_dir) {
992                 if (chdir(radlog_dir) < 0) {
993                         ERROR("Failed to 'chdir %s' after chroot: %s",
994                                radlog_dir, fr_syserror(errno));
995                         return -1;
996                 }
997         }
998
999         cc = rad_malloc(sizeof(*cc));
1000         memset(cc, 0, sizeof(*cc));
1001
1002         cc->cs = cs;
1003         rad_assert(cs_cache == NULL);
1004         cs_cache = cc;
1005
1006         /* Clear any unprocessed configuration errors */
1007         (void) fr_strerror();
1008
1009         return 0;
1010 }
1011
1012 /*
1013  *      Free the configuration.  Called only when the server is exiting.
1014  */
1015 int free_mainconfig(void)
1016 {
1017         cached_config_t *cc, *next;
1018
1019         virtual_servers_free(0);
1020
1021         /*
1022          *      Clean up the configuration data
1023          *      structures.
1024          */
1025         clients_free(NULL);
1026         realms_free();
1027         listen_free(&mainconfig.listen);
1028
1029         /*
1030          *      Free all of the cached configurations.
1031          */
1032         for (cc = cs_cache; cc != NULL; cc = next) {
1033                 next = cc->next;
1034                 cf_file_free(cc->cs);
1035                 free(cc);
1036         }
1037
1038         dict_free();
1039
1040         return 0;
1041 }
1042
1043 void hup_logfile(void)
1044 {
1045                 int fd, old_fd;
1046
1047                 if (default_log.dest != L_DST_FILES) return;
1048
1049                 fd = open(mainconfig.log_file,
1050                           O_WRONLY | O_APPEND | O_CREAT, 0640);
1051                 if (fd >= 0) {
1052                         /*
1053                          *      Atomic swap. We'd like to keep the old
1054                          *      FD around so that callers don't
1055                          *      suddenly find the FD closed, and the
1056                          *      writes go nowhere.  But that's hard to
1057                          *      do.  So... we have the case where a
1058                          *      log message *might* be lost on HUP.
1059                          */
1060                         old_fd = default_log.fd;
1061                         default_log.fd = fd;
1062                         close(old_fd);
1063                 }
1064 }
1065
1066 void hup_mainconfig(void)
1067 {
1068         cached_config_t *cc;
1069         CONF_SECTION *cs;
1070         char buffer[1024];
1071
1072         INFO("HUP - Re-reading configuration files");
1073
1074         /* Read the configuration file */
1075         snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf",
1076                  radius_dir, mainconfig.name);
1077         if ((cs = cf_file_read(buffer)) == NULL) {
1078                 ERROR("Failed to re-read or parse %s", buffer);
1079                 return;
1080         }
1081
1082         cc = rad_malloc(sizeof(*cc));
1083         memset(cc, 0, sizeof(*cc));
1084
1085         /*
1086          *      Save the current configuration.  Note that we do NOT
1087          *      free older ones.  We should probably do so at some
1088          *      point.  Doing so will require us to mark which modules
1089          *      are still in use, and which aren't.  Modules that
1090          *      can't be HUPed always use the original configuration.
1091          *      Modules that can be HUPed use one of the newer
1092          *      configurations.
1093          */
1094         cc->created = time(NULL);
1095         cc->cs = cs;
1096         cc->next = cs_cache;
1097         cs_cache = cc;
1098
1099         /*
1100          *      Re-open the log file.  If we can't, then keep logging
1101          *      to the old log file.
1102          *
1103          *      The "open log file" code is here rather than in log.c,
1104          *      because it makes that function MUCH simpler.
1105          */
1106         hup_logfile();
1107
1108         INFO("HUP - loading modules");
1109
1110         /*
1111          *      Prefer the new module configuration.
1112          */
1113         module_hup(cf_section_sub_find(cs, "modules"));
1114
1115         /*
1116          *      Load new servers BEFORE freeing old ones.
1117          */
1118         virtual_servers_load(cs);
1119
1120         virtual_servers_free(cc->created - mainconfig.max_request_time * 4);
1121 }