change minimum pool size to be 2K
[freeradius.git] / src / main / mainconfig.c
index a876a8b..3046906 100644 (file)
@@ -25,33 +25,25 @@ RCSID("$Id$")
 
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/modules.h>
+#include <freeradius-devel/modpriv.h>
 #include <freeradius-devel/rad_assert.h>
 
 #include <sys/stat.h>
-
-#ifdef HAVE_PWD_H
 #include <pwd.h>
-#endif
-
-#ifdef HAVE_GRP_H
 #include <grp.h>
-#endif
 
 #ifdef HAVE_SYSLOG_H
-#      include <syslog.h>
-#endif
-
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
+#  include <syslog.h>
 #endif
 
 #ifdef HAVE_FCNTL_H
-#include <fcntl.h>
+#  include <fcntl.h>
 #endif
 
-struct main_config_t main_config;
-char *debug_condition = NULL;
-extern bool log_dates_utc;
+main_config_t          main_config;                            //!< Main server configuration.
+extern fr_cond_t       *debug_condition;
+fr_cond_t              *debug_condition = NULL;                        //!< Condition used to mark packets up for checking.
+bool                   event_loop_started = false;             //!< Whether the main event loop has been started yet.
 
 typedef struct cached_config_t {
        struct cached_config_t *next;
@@ -91,42 +83,56 @@ static bool         do_colourise = false;
 
 static char const      *radius_dir = NULL;     //!< Path to raddb directory
 
+/**********************************************************************
+ *
+ *     We need to figure out where the logs go, before doing anything
+ *     else.  This is so that the log messages go to the correct
+ *     place.
+ *
+ *     BUT, we want the settings from the command line to over-ride
+ *     the ones in the configuration file.  So, these items are
+ *     parsed ONLY if there is no "-l foo" on the command line.
+ *
+ **********************************************************************/
 
 /*
- *  Security configuration for the server.
- */
-static const CONF_PARSER security_config[] = {
-       { "max_attributes",  FR_CONF_POINTER(PW_TYPE_INTEGER, &fr_max_attributes), STRINGIFY(0) },
-       { "reject_delay",  FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.reject_delay), STRINGIFY(0) },
-       { "status_server", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.status_server), "no"},
-       { "allow_vulnerable_openssl", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.allow_vulnerable_openssl), "no"},
-       { NULL, -1, 0, NULL, NULL }
-};
-
-
-/*
- *     Logging configuration for the server.
+ *     Log destinations
  */
-static const CONF_PARSER logdest_config[] = {
+static const CONF_PARSER startup_log_config[] = {
        { "destination",  FR_CONF_POINTER(PW_TYPE_STRING, &radlog_dest), "files" },
        { "syslog_facility",  FR_CONF_POINTER(PW_TYPE_STRING, &syslog_facility), STRINGIFY(0) },
 
+       { "localstatedir", FR_CONF_POINTER(PW_TYPE_STRING, &localstatedir), "${prefix}/var"},
+       { "logdir", FR_CONF_POINTER(PW_TYPE_STRING, &radlog_dir), "${localstatedir}/log"},
        { "file",  FR_CONF_POINTER(PW_TYPE_STRING, &main_config.log_file), "${logdir}/radius.log" },
-       { "requests",  FR_CONF_POINTER(PW_TYPE_STRING, &default_log.file), NULL },
-       { NULL, -1, 0, NULL, NULL }
+       { "requests",  FR_CONF_POINTER(PW_TYPE_STRING | PW_TYPE_DEPRECATED, &default_log.file), NULL },
+       CONF_PARSER_TERMINATOR
 };
 
 
-static const CONF_PARSER serverdest_config[] = {
-       { "log",  FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) logdest_config },
+/*
+ *     Basic configuration for the server.
+ */
+static const CONF_PARSER startup_server_config[] = {
+       { "log",  FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) startup_log_config },
+
+       { "name", FR_CONF_POINTER(PW_TYPE_STRING, &my_name), "radiusd"},
+       { "prefix", FR_CONF_POINTER(PW_TYPE_STRING, &prefix), "/usr/local"},
+
        { "log_file",  FR_CONF_POINTER(PW_TYPE_STRING, &main_config.log_file), NULL },
        { "log_destination", FR_CONF_POINTER(PW_TYPE_STRING, &radlog_dest), NULL },
        { "use_utc", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &log_dates_utc), NULL },
-       { NULL, -1, 0, NULL, NULL }
+       CONF_PARSER_TERMINATOR
 };
 
 
-static const CONF_PARSER log_config_nodest[] = {
+/**********************************************************************
+ *
+ *     Now that we've parsed the log destination, AND the security
+ *     items, we can parse the rest of the configuration items.
+ *
+ **********************************************************************/
+static const CONF_PARSER log_config[] = {
        { "stripped_names", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &log_stripped_names),"no" },
        { "auth", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.log_auth), "no" },
        { "auth_badpass", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.log_auth_badpass), "no" },
@@ -135,16 +141,34 @@ static const CONF_PARSER log_config_nodest[] = {
        { "msg_goodpass", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.auth_goodpass_msg), NULL},
        { "colourise",FR_CONF_POINTER(PW_TYPE_BOOLEAN, &do_colourise), NULL },
        { "use_utc", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &log_dates_utc), NULL },
-       { "msg_denied", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.denied_msg),
-         "You are already logged in - access denied" },
-
-       { NULL, -1, 0, NULL, NULL }
+       { "msg_denied", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.denied_msg), "You are already logged in - access denied" },
+       CONF_PARSER_TERMINATOR
 };
 
 
 /*
- *  A mapping of configuration file names to internal variables
+ *  Security configuration for the server.
  */
+static const CONF_PARSER security_config[] = {
+       { "max_attributes",  FR_CONF_POINTER(PW_TYPE_INTEGER, &fr_max_attributes), STRINGIFY(0) },
+       { "reject_delay",  FR_CONF_POINTER(PW_TYPE_TIMEVAL, &main_config.reject_delay), STRINGIFY(0) },
+       { "status_server", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.status_server), "no"},
+#ifdef ENABLE_OPENSSL_VERSION_CHECK
+       { "allow_vulnerable_openssl", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.allow_vulnerable_openssl), "no"},
+#endif
+       CONF_PARSER_TERMINATOR
+};
+
+static const CONF_PARSER resources[] = {
+       /*
+        *      Don't set a default here.  It's set in the code, below.  This means that
+        *      the config item will *not* get printed out in debug mode, so that no one knows
+        *      it exists.
+        */
+       { "talloc_pool_size", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.talloc_pool_size), NULL },
+       CONF_PARSER_TERMINATOR
+};
+
 static const CONF_PARSER server_config[] = {
        /*
         *      FIXME: 'prefix' is the ONLY one which should be
@@ -174,7 +198,9 @@ static const CONF_PARSER server_config[] = {
 #ifdef WITH_PROXY
        { "proxy_requests", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.proxy_requests), "yes" },
 #endif
-       { "log", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) log_config_nodest },
+       { "log", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) log_config },
+
+       { "resources", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) resources },
 
        /*
         *      People with old configs will have these.  They are listed
@@ -190,10 +216,23 @@ static const CONF_PARSER server_config[] = {
        { "log_stripped_names", FR_CONF_POINTER(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, &log_stripped_names), NULL },
 
        {  "security", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) security_config },
-
-       { NULL, -1, 0, NULL, NULL }
+       CONF_PARSER_TERMINATOR
 };
 
+
+/**********************************************************************
+ *
+ *     The next few items are here as a "bootstrap" for security.
+ *     They allow the server to switch users, chroot, while still
+ *     opening the various output files with the correct permission.
+ *
+ *     It's rare (or impossible) to have parse errors here, so we
+ *     don't worry too much about that.  In contrast, when we parse
+ *     the rest of the configuration, we CAN get parse errors.  We
+ *     want THOSE parse errors to go to the log file, and we want the
+ *     log file to have the correct permissions.
+ *
+ **********************************************************************/
 static const CONF_PARSER bootstrap_security_config[] = {
 #ifdef HAVE_SETUID
        { "user",  FR_CONF_POINTER(PW_TYPE_STRING, &uid_name), NULL },
@@ -201,13 +240,19 @@ static const CONF_PARSER bootstrap_security_config[] = {
 #endif
        { "chroot",  FR_CONF_POINTER(PW_TYPE_STRING, &chroot_dir), NULL },
        { "allow_core_dumps", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &allow_core_dumps), "no" },
-
-       { NULL, -1, 0, NULL, NULL }
+       CONF_PARSER_TERMINATOR
 };
 
 static const CONF_PARSER bootstrap_config[] = {
        {  "security", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) bootstrap_security_config },
 
+       { "name", FR_CONF_POINTER(PW_TYPE_STRING, &my_name), "radiusd"},
+       { "prefix", FR_CONF_POINTER(PW_TYPE_STRING, &prefix), "/usr/local"},
+       { "localstatedir", FR_CONF_POINTER(PW_TYPE_STRING, &localstatedir), "${prefix}/var"},
+
+       { "logdir", FR_CONF_POINTER(PW_TYPE_STRING, &radlog_dir), "${localstatedir}/log"},
+       { "run_dir", FR_CONF_POINTER(PW_TYPE_STRING, &run_dir), "${localstatedir}/run/${name}"},
+
        /*
         *      For backwards compatibility.
         */
@@ -217,19 +262,14 @@ static const CONF_PARSER bootstrap_config[] = {
 #endif
        { "chroot",  FR_CONF_POINTER(PW_TYPE_STRING | PW_TYPE_DEPRECATED, &chroot_dir), NULL },
        { "allow_core_dumps", FR_CONF_POINTER(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, &allow_core_dumps), NULL },
-
-       { NULL, -1, 0, NULL, NULL }
+       CONF_PARSER_TERMINATOR
 };
 
 
-
-#define MAX_ARGV (256)
-
-
 static size_t config_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
 {
        size_t len = 0;
-       static char const *disallowed = "%{}\\'\"`";
+       static char const disallowed[] = "%{}\\'\"`";
 
        while (in[0]) {
                /*
@@ -303,7 +343,7 @@ static ssize_t xlat_config(UNUSED void *instance, REQUEST *request, char const *
                return -1;
        }
 
-       cp = cf_itemtopair(ci);
+       cp = cf_item_to_pair(ci);
 
        /*
         *  Ensure that we only copy what's necessary.
@@ -348,7 +388,6 @@ static ssize_t xlat_client(UNUSED void *instance, REQUEST *request, char const *
                        strlcpy(out, request->client->shortname, outlen);
                        return strlen(out);
                }
-               RDEBUG("Client does not contain config item \"%s\"", fmt);
                *out = '\0';
                return 0;
        }
@@ -379,7 +418,7 @@ static ssize_t xlat_getclient(UNUSED void *instance, REQUEST *request, char cons
        }
 
        strlcpy(buffer, p, (q + 1) - p);
-       if (fr_pton(&ip, buffer, 0, false) <= 0) {
+       if (fr_pton(&ip, buffer, -1, AF_UNSPEC, false) < 0) {
                REDEBUG("\"%s\" is not a valid IPv4 or IPv6 address", buffer);
                goto error;
        }
@@ -399,7 +438,6 @@ static ssize_t xlat_getclient(UNUSED void *instance, REQUEST *request, char cons
                        strlcpy(out, request->client->shortname, outlen);
                        return strlen(out);
                }
-               RDEBUG("Client does not contain config item \"%s\"", fmt);
                *out = '\0';
                return 0;
        }
@@ -412,108 +450,36 @@ static ssize_t xlat_getclient(UNUSED void *instance, REQUEST *request, char cons
        return -1;
 }
 
-#ifdef HAVE_SETUID
-static bool doing_setuid = false;
-
-#  if defined(HAVE_SETRESUID) && defined (HAVE_GETRESUID)
-void fr_suid_up(void)
-{
-       uid_t ruid, euid, suid;
-
-       if (getresuid(&ruid, &euid, &suid) < 0) {
-               ERROR("Failed getting saved UID's");
-               fr_exit_now(1);
-       }
-
-       if (setresuid(-1, suid, -1) < 0) {
-               ERROR("Failed switching to privileged user");
-               fr_exit_now(1);
-       }
-
-       if (geteuid() != suid) {
-               ERROR("Switched to unknown UID");
-               fr_exit_now(1);
-       }
-}
-
-void fr_suid_down(void)
-{
-       if (!doing_setuid) return;
-
-       if (setresuid(-1, server_uid, geteuid()) < 0) {
-               fprintf(stderr, "%s: Failed switching to uid %s: %s\n",
-                       progname, uid_name, fr_syserror(errno));
-               fr_exit_now(1);
-       }
-
-       if (geteuid() != server_uid) {
-               fprintf(stderr, "%s: Failed switching uid: UID is incorrect\n",
-                       progname);
-               fr_exit_now(1);
-       }
-
-       fr_set_dumpable(allow_core_dumps);
-}
-
-void fr_suid_down_permanent(void)
-{
-       if (!doing_setuid) return;
-
-       if (setresuid(server_uid, server_uid, server_uid) < 0) {
-               ERROR("Failed in permanent switch to uid %s: %s",
-                      uid_name, fr_syserror(errno));
-               fr_exit_now(1);
-       }
-
-       if (geteuid() != server_uid) {
-               ERROR("Switched to unknown uid");
-               fr_exit_now(1);
-       }
-
-       fr_set_dumpable(allow_core_dumps);
-}
-#  else
 /*
- *     Much less secure...
+ *     Xlat for %{listen:foo}
  */
-void fr_suid_up(void)
+static ssize_t xlat_listen(UNUSED void *instance, REQUEST *request,
+                          char const *fmt, char *out, size_t outlen)
 {
-}
+       char const *value = NULL;
+       CONF_PAIR *cp;
 
-void fr_suid_down(void)
-{
-       if (!uid_name) return;
+       if (!fmt || !out || (outlen < 1)) return 0;
 
-       if (setuid(server_uid) < 0) {
-               fprintf(stderr, "%s: Failed switching to uid %s: %s\n",
-                       progname, uid_name, fr_syserror(errno));
-               fr_exit(1);
+       if (!request->listener) {
+               RWDEBUG("No listener associated with this request");
+               *out = '\0';
+               return 0;
        }
 
-       fr_set_dumpable(allow_core_dumps);
-}
+       cp = cf_pair_find(request->listener->cs, fmt);
+       if (!cp || !(value = cf_pair_value(cp))) {
+               RDEBUG("Listener does not contain config item \"%s\"", fmt);
+               *out = '\0';
+               return 0;
+       }
 
-void fr_suid_down_permanent(void)
-{
-       fr_set_dumpable(allow_core_dumps);
-}
-#  endif /* HAVE_SETRESUID && HAVE_GETRESUID */
-#else  /* HAVE_SETUID */
-void fr_suid_up(void)
-{
-}
-void fr_suid_down(void)
-{
-       fr_set_dumpable(allow_core_dumps);
-}
-void fr_suid_down_permanent(void)
-{
-       fr_set_dumpable(allow_core_dumps);
+       strlcpy(out, value, outlen);
+
+       return strlen(out);
 }
-#endif /* HAVE_SETUID */
 
 #ifdef HAVE_SETUID
-
 /*
  *  Do chroot, if requested.
  *
@@ -521,6 +487,9 @@ void fr_suid_down_permanent(void)
  */
 static int switch_users(CONF_SECTION *cs)
 {
+       bool do_suid = false;
+       bool do_sgid = false;
+
        /*
         *      Get the current maximum for core files.  Do this
         *      before anything else so as to ensure it's properly
@@ -535,61 +504,72 @@ static int switch_users(CONF_SECTION *cs)
         *      Don't do chroot/setuid/setgid if we're in debugging
         *      as non-root.
         */
-       if (debug_flag && (getuid() != 0)) return 1;
+       if (rad_debug_lvl && (getuid() != 0)) return 1;
 
        if (cf_section_parse(cs, NULL, bootstrap_config) < 0) {
                fprintf(stderr, "radiusd: Error: Failed to parse user/group information.\n");
                return 0;
        }
 
-
 #ifdef HAVE_GRP_H
-       /*  Set GID.  */
+       /*
+        *      Get the correct GID for the server.
+        */
+       server_gid = getgid();
+
        if (gid_name) {
                struct group *gr;
 
                gr = getgrnam(gid_name);
-               if (gr == NULL) {
+               if (!gr) {
                        fprintf(stderr, "%s: Cannot get ID for group %s: %s\n",
                                progname, gid_name, fr_syserror(errno));
                        return 0;
                }
-               server_gid = gr->gr_gid;
-       } else {
-               server_gid = getgid();
+
+               if (server_gid != gr->gr_gid) {
+                       server_gid = gr->gr_gid;
+                       do_sgid = true;
+               }
        }
 #endif
 
-#ifdef HAVE_PWD_H
-       /*  Set UID.  */
+       /*
+        *      Get the correct UID for the server.
+        */
+       server_uid = getuid();
+
        if (uid_name) {
-               struct passwd *pw;
+               struct passwd *user;
 
-               pw = getpwnam(uid_name);
-               if (pw == NULL) {
+               if (rad_getpwnam(cs, &user, uid_name) < 0) {
                        fprintf(stderr, "%s: Cannot get passwd entry for user %s: %s\n",
-                               progname, uid_name, fr_syserror(errno));
+                               progname, uid_name, fr_strerror());
                        return 0;
                }
 
-               if (getuid() == pw->pw_uid) {
-                       uid_name = NULL;
-               } else {
-
-                       server_uid = pw->pw_uid;
+               /*
+                *      We're not the correct user.  Go set that.
+                */
+               if (server_uid != user->pw_uid) {
+                       server_uid = user->pw_uid;
+                       do_suid = true;
 #ifdef HAVE_INITGROUPS
                        if (initgroups(uid_name, server_gid) < 0) {
                                fprintf(stderr, "%s: Cannot initialize supplementary group list for user %s: %s\n",
                                        progname, uid_name, fr_syserror(errno));
+                               talloc_free(user);
                                return 0;
                        }
 #endif
                }
-       } else {
-               server_uid = getuid();
+
+               talloc_free(user);
        }
-#endif
 
+       /*
+        *      Do chroot BEFORE changing UIDs.
+        */
        if (chroot_dir) {
                if (chroot(chroot_dir) < 0) {
                        fprintf(stderr, "%s: Failed to perform chroot %s: %s",
@@ -615,46 +595,89 @@ static int switch_users(CONF_SECTION *cs)
        }
 
 #ifdef HAVE_GRP_H
-       /*  Set GID.  */
-       if (gid_name && (setgid(server_gid) < 0)) {
-               fprintf(stderr, "%s: Failed setting group to %s: %s",
-                       progname, gid_name, fr_syserror(errno));
-               return 0;
+       /*
+        *      Set the GID.  Don't bother checking it.
+        */
+       if (do_sgid) {
+               if (setgid(server_gid) < 0){
+                       fprintf(stderr, "%s: Failed setting group to %s: %s",
+                               progname, gid_name, fr_syserror(errno));
+                       return 0;
+               }
        }
 #endif
 
-#ifdef HAVE_SETUID
        /*
-        *      Just before losing root permissions, ensure that the
-        *      log files have the correct owner && group.
+        *      The directories for PID files and logs must exist.  We
+        *      need to create them if we're told to write files to
+        *      those directories.
+        *
+        *      Because this creation is new in 3.0.9, it's a soft
+        *      fail.
         *
-        *      We have to do this because the log file MAY have been
-        *      specified on the command-line.
         */
-       if (uid_name || gid_name) {
-               if ((default_log.dst == L_DST_FILES) &&
-                   (default_log.fd < 0)) {
-                       default_log.fd = open(main_config.log_file,
-                                             O_WRONLY | O_APPEND | O_CREAT, 0640);
-                       if (default_log.fd < 0) {
-                               fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", main_config.log_file, fr_syserror(errno));
-                               return 0;
-                       }
+       if (main_config.write_pid) {
+               char *my_dir;
 
-                       if (chown(main_config.log_file, server_uid, server_gid) < 0) {
-                               fprintf(stderr, "%s: Cannot change ownership of log file %s: %s\n",
-                                       progname, main_config.log_file, fr_syserror(errno));
-                               return 0;
-                       }
+               my_dir = talloc_strdup(NULL, run_dir);
+               if (rad_mkdir(my_dir, 0750, server_uid, server_gid) < 0) {
+                       DEBUG("Failed to create run_dir %s: %s",
+                             my_dir, strerror(errno));
                }
+               talloc_free(my_dir);
        }
 
-       if (uid_name) {
-               doing_setuid = true;
+       if (default_log.dst == L_DST_FILES) {
+               char *my_dir;
 
-               fr_suid_down();
+               my_dir = talloc_strdup(NULL, radlog_dir);
+               if (rad_mkdir(my_dir, 0750, server_uid, server_gid) < 0) {
+                       DEBUG("Failed to create logdir %s: %s",
+                             my_dir, strerror(errno));
+               }
+               talloc_free(my_dir);
+       }
+
+       /*
+        *      Once we're done with all of the privileged work,
+        *      permanently change the UID.
+        */
+       if (do_suid) {
+               rad_suid_set_down_uid(server_uid);
+               rad_suid_down();
+       }
+
+       /*
+        *      If we don't already have a log file open, open one
+        *      now.  We may not have been logging anything yet.  The
+        *      server normally starts up fairly quietly.
+        */
+       if ((default_log.dst == L_DST_FILES) &&
+           (default_log.fd < 0)) {
+               default_log.fd = open(main_config.log_file,
+                                     O_WRONLY | O_APPEND | O_CREAT, 0640);
+               if (default_log.fd < 0) {
+                       fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", main_config.log_file, fr_syserror(errno));
+                       return 0;
+               }
+       }
+
+       /*
+        *      If we need to change UID, ensure that the log files
+        *      have the correct owner && group.
+        *
+        *      We have to do this because some log files MAY already
+        *      have been written as root.  We need to change them to
+        *      have the correct ownership before proceeding.
+        */
+       if ((do_suid || do_sgid) &&
+           (default_log.dst == L_DST_FILES)) {
+               if (fchown(default_log.fd, server_uid, server_gid) < 0) {
+                       fprintf(stderr, "%s: Cannot change ownership of log file %s: %s\n",
+                               progname, main_config.log_file, fr_syserror(errno));
+                       return 0;
+               }
        }
-#endif
 
        /*
         *      This also clears the dumpable flag if core dumps
@@ -706,7 +729,7 @@ char const *get_radius_dir(void)
 int main_config_init(void)
 {
        char const *p = NULL;
-       CONF_SECTION *cs;
+       CONF_SECTION *cs, *subcs;
        struct stat statbuf;
        cached_config_t *cc;
        char buffer[1024];
@@ -725,8 +748,8 @@ int main_config_init(void)
        }
 #endif
 
-#ifdef S_IROTH
-       if (0 && (statbuf.st_mode & S_IROTH) != 0) {
+#if 0 && defined(S_IROTH)
+       if (statbuf.st_mode & S_IROTH != 0) {
                ERROR("Configuration directory %s is globally readable.  Refusing to start due to insecure configuration.",
                       radius_dir);
                return -1;
@@ -745,6 +768,13 @@ int main_config_init(void)
        }
 
        /*
+        *      About sizeof(REQUEST) + sizeof(RADIUS_PACKET) * 2 + sizeof(VALUE_PAIR) * 400
+        *
+        *      Which should be enough for many configurations.
+        */
+       main_config.talloc_pool_size = 8 * 1024; /* default */
+
+       /*
         *      Read the distribution dictionaries first, then
         *      the ones in raddb.
         */
@@ -786,11 +816,43 @@ do {\
         */
        DICT_READ_OPTIONAL(radius_dir, RADIUS_DICTIONARY);
 
+       cs = cf_section_alloc(NULL, "main", NULL);
+       if (!cs) return -1;
+
+       /*
+        *      Add a 'feature' subsection off the main config
+        *      We check if it's defined first, as the user may
+        *      have defined their own feature flags, or want
+        *      to manually override the ones set by modules
+        *      or the server.
+        */
+       subcs = cf_section_sub_find(cs, "feature");
+       if (!subcs) {
+               subcs = cf_section_alloc(cs, "feature", NULL);
+               if (!subcs) return -1;
+
+               cf_section_add(cs, subcs);
+       }
+       version_init_features(subcs);
+
+       /*
+        *      Add a 'version' subsection off the main config
+        *      We check if it's defined first, this is for
+        *      backwards compatibility.
+        */
+       subcs = cf_section_sub_find(cs, "version");
+       if (!subcs) {
+               subcs = cf_section_alloc(cs, "version", NULL);
+               if (!subcs) return -1;
+               cf_section_add(cs, subcs);
+       }
+       version_init_numbers(subcs);
+
        /* Read the configuration file */
-       snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf",
-                radius_dir, main_config.name);
-       if ((cs = cf_file_read(buffer)) == NULL) {
+       snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf", radius_dir, main_config.name);
+       if (cf_file_read(cs, buffer) < 0) {
                ERROR("Errors reading or parsing %s", buffer);
+               talloc_free(cs);
                return -1;
        }
 
@@ -799,7 +861,7 @@ do {\
         *      set it now.
         */
        if (default_log.dst == L_DST_NULL) {
-               if (cf_section_parse(cs, NULL, serverdest_config) < 0) {
+               if (cf_section_parse(cs, NULL, startup_server_config) < 0) {
                        fprintf(stderr, "radiusd: Error: Failed to parse log{} section.\n");
                        cf_file_free(cs);
                        return -1;
@@ -830,7 +892,7 @@ do {\
                                cf_file_free(cs);
                                return -1;
                        }
-                       main_config.syslog_facility = fr_str2int(syslog_str2fac, syslog_facility, -1);
+                       main_config.syslog_facility = fr_str2int(syslog_facility_table, syslog_facility, -1);
                        if (main_config.syslog_facility < 0) {
                                fprintf(stderr, "radiusd: Error: Unknown syslog_facility %s\n",
                                        syslog_facility);
@@ -863,28 +925,10 @@ do {\
 #endif
 
        /*
-        *      Open the log file AFTER switching uid / gid.  If we
-        *      did switch uid/gid, then the code in switch_users()
-        *      took care of setting the file permissions correctly.
-        */
-       if ((default_log.dst == L_DST_FILES) &&
-           (default_log.fd < 0)) {
-               default_log.fd = open(main_config.log_file,
-                                           O_WRONLY | O_APPEND | O_CREAT, 0640);
-               if (default_log.fd < 0) {
-                       fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", main_config.log_file, fr_syserror(errno));
-                       cf_file_free(cs);
-                       return -1;
-               }
-       }
-
-       /*
         *      This allows us to figure out where, relative to
         *      radiusd.conf, the other configuration files exist.
         */
-       if (cf_section_parse(cs, NULL, server_config) < 0) {
-               return -1;
-       }
+       if (cf_section_parse(cs, NULL, server_config) < 0) return -1;
 
        /*
         *      We ignore colourization of output until after the
@@ -902,22 +946,34 @@ do {\
         *      command-line: use whatever is in the config
         *      file.
         */
-       if (debug_flag == 0) {
-               debug_flag = main_config.debug_level;
+       if (rad_debug_lvl == 0) {
+               rad_debug_lvl = main_config.debug_level;
+       }
+       fr_debug_lvl = rad_debug_lvl;
+
+       FR_INTEGER_COND_CHECK("max_request_time", main_config.max_request_time,
+                             (main_config.max_request_time != 0), 100);
+
+       /*
+        *      reject_delay can be zero.  OR 1 though 10.
+        */
+       if ((main_config.reject_delay.tv_sec != 0) || (main_config.reject_delay.tv_usec != 0)) {
+               FR_TIMEVAL_BOUND_CHECK("reject_delay", &main_config.reject_delay, >=, 1, 0);
        }
-       fr_debug_flag = debug_flag;
+       FR_TIMEVAL_BOUND_CHECK("reject_delay", &main_config.reject_delay, <=, 10, 0);
 
-       FR_INTEGER_COND_CHECK("max_request_time", main_config.max_request_time, (main_config.max_request_time != 0), 100);
-       FR_INTEGER_BOUND_CHECK("reject_delay", main_config.reject_delay, <=, 10);
        FR_INTEGER_BOUND_CHECK("cleanup_delay", main_config.cleanup_delay, <=, 10);
 
+       FR_INTEGER_BOUND_CHECK("resources.talloc_pool_size", main_config.talloc_pool_size, >=, 2 * 1024);
+       FR_INTEGER_BOUND_CHECK("resources.talloc_pool_size", main_config.talloc_pool_size, <=, 1024 * 1024);
+
        /*
         * Set default initial request processing delay to 1/3 of a second.
         * Will be updated by the lowest response window across all home servers,
         * if it is less than this.
         */
        main_config.init_delay.tv_sec = 0;
-       main_config.init_delay.tv_usec = 1000000 / 3;
+       main_config.init_delay.tv_usec = 2* (1000000 / 3);
 
        /*
         *      Free the old configuration items, and replace them
@@ -935,16 +991,17 @@ do {\
        }
 
        DEBUG2("%s: #### Loading Clients ####", main_config.name);
-       if (!clients_parse_section(cs, false)) {
+       if (!client_list_parse_section(cs, false)) {
                return -1;
        }
 
        /*
-        *  Register the %{config:section.subsection} xlat function.
+        *      Register the %{config:section.subsection} xlat function.
         */
        xlat_register("config", xlat_config, NULL, NULL);
        xlat_register("client", xlat_client, NULL, NULL);
        xlat_register("getclient", xlat_getclient, NULL, NULL);
+       xlat_register("listen", xlat_listen, NULL, NULL);
 
        /*
         *  Go update our behaviour, based on the configuration
@@ -955,7 +1012,7 @@ do {\
         *      Sanity check the configuration for internal
         *      consistency.
         */
-       FR_INTEGER_BOUND_CHECK("reject_delay", main_config.reject_delay, <=, main_config.cleanup_delay);
+       FR_TIMEVAL_BOUND_CHECK("reject_delay", &main_config.reject_delay, <=, main_config.cleanup_delay, 0);
 
        if (chroot_dir) {
                if (chdir(radlog_dir) < 0) {
@@ -989,7 +1046,7 @@ int main_config_free(void)
         *      Clean up the configuration data
         *      structures.
         */
-       clients_free(NULL);
+       client_list_free(NULL);
        realms_free();
        listen_free(&main_config.listen);
 
@@ -1004,40 +1061,129 @@ int main_config_free(void)
 
 void hup_logfile(void)
 {
-               int fd, old_fd;
+       int fd, old_fd;
 
-               if (default_log.dst != L_DST_FILES) return;
+       if (default_log.dst != L_DST_FILES) return;
 
-               fd = open(main_config.log_file,
-                         O_WRONLY | O_APPEND | O_CREAT, 0640);
-               if (fd >= 0) {
-                       /*
-                        *      Atomic swap. We'd like to keep the old
-                        *      FD around so that callers don't
-                        *      suddenly find the FD closed, and the
-                        *      writes go nowhere.  But that's hard to
-                        *      do.  So... we have the case where a
-                        *      log message *might* be lost on HUP.
-                        */
-                       old_fd = default_log.fd;
-                       default_log.fd = fd;
-                       close(old_fd);
-               }
+       fd = open(main_config.log_file,
+                 O_WRONLY | O_APPEND | O_CREAT, 0640);
+       if (fd >= 0) {
+               /*
+                *      Atomic swap. We'd like to keep the old
+                *      FD around so that callers don't
+                *      suddenly find the FD closed, and the
+                *      writes go nowhere.  But that's hard to
+                *      do.  So... we have the case where a
+                *      log message *might* be lost on HUP.
+                */
+               old_fd = default_log.fd;
+               default_log.fd = fd;
+               close(old_fd);
+       }
+}
+
+static int hup_callback(void *ctx, void *data)
+{
+       CONF_SECTION *modules = ctx;
+       CONF_SECTION *cs = data;
+       CONF_SECTION *parent;
+       char const *name;
+       module_instance_t *mi;
+
+       /*
+        *      Files may be defined in sub-sections of a module
+        *      config.  Walk up the tree until we find the module
+        *      definition.
+        */
+       parent = cf_item_parent(cf_section_to_item(cs));
+       while (parent != modules) {
+               cs = parent;
+               parent = cf_item_parent(cf_section_to_item(cs));
+
+               /*
+                *      Something went wrong.  Oh well...
+                */
+               if (!parent) return 0;
+       }
+
+       name = cf_section_name2(cs);
+       if (!name) name = cf_section_name1(cs);
+
+       mi = module_find(modules, name);
+       if (!mi) return 0;
+
+       if ((mi->entry->module->type & RLM_TYPE_HUP_SAFE) == 0) return 0;
+
+       if (!module_hup_module(mi->cs, mi, time(NULL))) return 0;
+
+       return 0;
 }
 
 void main_config_hup(void)
 {
+       int rcode;
        cached_config_t *cc;
        CONF_SECTION *cs;
+       time_t when;
        char buffer[1024];
 
-       INFO("HUP - Re-reading configuration files");
+       static time_t last_hup = 0;
+
+       /*
+        *      Re-open the log file.  If we can't, then keep logging
+        *      to the old log file.
+        *
+        *      The "open log file" code is here rather than in log.c,
+        *      because it makes that function MUCH simpler.
+        */
+       hup_logfile();
+
+       /*
+        *      Only check the config files every few seconds.
+        */
+       when = time(NULL);
+       if ((last_hup + 2) >= when) {
+               INFO("HUP - Last HUP was too recent.  Ignoring");
+               return;
+       }
+       last_hup = when;
+
+       rcode = cf_file_changed(cs_cache->cs, hup_callback);
+       if (rcode == CF_FILE_NONE) {
+               INFO("HUP - No files changed.  Ignoring");
+               return;
+       }
+
+       if (rcode == CF_FILE_ERROR) {
+               INFO("HUP - Cannot read configuration files.  Ignoring");
+               return;
+       }
+
+       /*
+        *      No config files have changed.
+        */
+       if ((rcode & CF_FILE_CONFIG) == 0) {
+               if ((rcode & CF_FILE_MODULE) != 0) {
+                       INFO("HUP - Files loaded by a module have changed.");
+
+                       /*
+                        *      FIXME: reload the module.
+                        */
+
+               }
+               return;
+       }
+
+       cs = cf_section_alloc(NULL, "main", NULL);
+       if (!cs) return;
 
        /* Read the configuration file */
-       snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf",
-                radius_dir, main_config.name);
-       if ((cs = cf_file_read(buffer)) == NULL) {
+       snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf", radius_dir, main_config.name);
+
+       INFO("HUP - Re-reading configuration files");
+       if (cf_file_read(cs, buffer) < 0) {
                ERROR("Failed to re-read or parse %s", buffer);
+               talloc_free(cs);
                return;
        }
 
@@ -1061,15 +1207,6 @@ void main_config_hup(void)
        cc->next = cs_cache;
        cs_cache = cc;
 
-       /*
-        *      Re-open the log file.  If we can't, then keep logging
-        *      to the old log file.
-        *
-        *      The "open log file" code is here rather than in log.c,
-        *      because it makes that function MUCH simpler.
-        */
-       hup_logfile();
-
        INFO("HUP - loading modules");
 
        /*
@@ -1082,5 +1219,5 @@ void main_config_hup(void)
         */
        virtual_servers_load(cs);
 
-       virtual_servers_free(cc->created - main_config.max_request_time * 4);
+       virtual_servers_free(cc->created - (main_config.max_request_time * 4));
 }