Re: Are there good reasons for running radiusd as user 'root'?
Maurice Makaay <[email protected]> Wed, 12 Nov 2003 12:00:39 +0100
| Newsgroups | gmane.comp.gnu.radius.bugs |
|---|---|
| Organization | InterNLnet BV |
| Message-ID | <[email protected]> |
Hi,
> You only have to run it as root if you plan to use Auth-Type = System
> and/or Auth-Type = PAM. Otherwise it is OK to run radiusd with usual user
> privileges, provided that the logging directory is writable for that
> user or group (the default /var/log obviously isn't).
...
> That is reasonable. I'll surely add the feature.
I created a patch which adds a new keyword "exec-user" to the configuration
file's vocabulary. By setting the exec-user, one can determine under which
uid the server should be run. Things that are in the patch:
* Cleanup of some old references to exec-program-group, which were still
in the texinfo files.
* A new type of configuration CFG_USER in config.y. This type handles
users that are specified using either uids or usernames. This will
fill a new type of struct "user_info" with some basic user information
(the username, the uid and the gid of a user; a complete passwd struct
would contain way more info than needed).
* A new droppriv.c file, which handles privilege dropping. The current
implementation for privilege dropping (for running external programs)
is only changing the euid. I think this is bad policy because this
way the external program can simply change its uid back to the root uid.
Setting the exec-program-user probably has a safety-reason, so let's
be as safe as we can. If a user really wants to keep the ruid "root",
he can simply set run-user and exec-program-user to "root" in the
radius server's config and set the euid in the external program himself.
The drop_privileges() function takes a pointer to a "user_info" struct
as its argument.
* The new way of setting the run-user/exec-program-user has has fixed
one of the FIXME's in the source of radiusd/exec.c:
Check user/group
FIXME: This should be checked *once* after re-reading the
configuration
* config.syntax contains information on run-user and some related
remarks were added to exec-program-user.
* Several checks are added to warn the user for:
- the use of non-existant uids/usernames (this will cause the server
to exit).
- mixing two differing non-root users for run-user and exec-program-user,
in which case the exec-program-user will not work (because only root
can switch uid).
- using Auth-Type System and/or PAM, for which root-access is needed.
- not being able to find the default user 'daemon' on the system, in
which case the program will revert to the user 'root' for
exec-program-user.
There are two files attached: a diff for changes that are made to the
already existing files in CVS and a copy of the new file droppriv.c.
There's a FIXME of my own in radiusd.c:
/* Now, try to drop our privileges... FIXME: exit on failure?*/
drop_privileges(&run_user);
I think it would be best to have the server aborting in case the privilege
dropping fails, but I don't know what your opinion on this is..
Regards,
-- Maurice Makaay
_______________________________________________
Bug-gnu-radius mailing list
[email protected]
http://mail.gnu.org/mailman/listinfo/bug-gnu-radius
CVS-droppriv.c
(text/plain, 2.4 KB)
/* This file is part of GNU Radius.
Copyright (C) 2000,2001,2002,2003 Free Software Foundation, Inc.
Written by Sergey Poznyakoff
GNU Radius is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GNU Radius is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Radius; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <sys/types.h>
#include <unistd.h>
#include <radiusd.h>
int
drop_privileges(struct user_info *user)
{
if (user->uid == 0 && user->gid == 0) return 0;
if (user->gid != 0 && setgid(user->gid)) {
radlog(L_ERR|L_PERROR,
_("setgid(%d) failed"), user->gid);
return -1;
}
if (user->uid != 0) {
#if defined(HAVE_SETUID)
if (setuid(user->uid)) {
radlog(L_ERR|L_PERROR,
_("setuid(%d) failed (ruid=%d, euid=%d)"),
user->uid, getuid(), geteuid());
return -1;
}
#elif defined(HAVE_SETREUID)
if (setreuid(user->uid, user->uid)) {
radlog(L_ERR|L_PERROR,
_("setreuid(%d,%d) failed (ruid=%d, euid=%d)"),
user->uid, user->uid, getuid(), geteuid());
return -1;
}
#elif defined(HAVE_SETRUID) && defined(HAVE_SETEUID)
if (seteuid(user->uid) || setruid(user->uid)) {
radlog(L_ERR|L_PERROR,
_("seteuid/setruid(%d) failed (ruid=%d, euid=%d)"),
user->uid, getuid(), geteuid());
return -1;
}
#elif defined(HAVE_SETEUID)
if (seteuid(user->uid)) {
radlog(L_ERR|L_PERROR,
_("seteuid(%d) failed (ruid=%d, euid=%d)"),
user->uid, getuid(), geteuid());
return -1;
}
radlog(L_WARN,
_("Incomplete drop of privileges to user %s (uid %d): "
"only the effective uid is set"),
user->username, user->uid);
#else
# error "*** NO WAY TO SET REAL AND EFFECTIVE UID IN drop_privileges() ***"
#endif
}
radlog(L_INFO,
_("Dropped privileges: user=%s gid=%d ruid=%d euid=%d"),
user->username, getgid(), getuid(), geteuid());
return 0;
}
CVS-patch-dropprivileges
(text/plain, 17.3 KB)
Index: configure.ac
===================================================================
RCS file: /cvsroot/radius/radius/configure.ac,v
retrieving revision 1.56
diff -u -r1.56 configure.ac
--- configure.ac 8 Nov 2003 15:47:33 -0000 1.56
+++ configure.ac 12 Nov 2003 10:01:03 -0000
@@ -235,7 +235,8 @@
AC_CHECK_FUNCS(setsid gethostname gettimeofday \
mkdir mktime select socket strdup strtol lockf \
setlocale bzero \
- inet_ntoa inet_aton setvbuf seteuid setreuid getdtablesize sigaction)
+ inet_ntoa inet_aton setvbuf setuid setruid seteuid setreuid \
+ getdtablesize sigaction)
AC_FUNC_OBSTACK
AC_CHECK_DECLS([strncasecmp, strtok_r, localtime_r, asprintf, vasprintf,
Index: doc/texinfo/attributes.texi
===================================================================
RCS file: /cvsroot/radius/radius/doc/texinfo/attributes.texi,v
retrieving revision 1.26
diff -u -r1.26 attributes.texi
--- doc/texinfo/attributes.texi 8 Nov 2003 09:48:08 -0000 1.26
+++ doc/texinfo/attributes.texi 12 Nov 2003 10:01:03 -0000
@@ -827,8 +827,8 @@
Before the execution of the program, @command{radiusd} switches to the
uid and gid of the user @code{daemon} and the group @code{daemon}. You can
-override these defaults by setting variables @code{exec-program-user}
-and @code{exec-program-group} in configuration file to proper values
+override these defaults by setting the variable @code{exec-program-user}
+in the configuration file to a proper value.
(@pxref{option,, The option statement}).
The accounting program must exit with status 0 to indicate a successful
@@ -1105,8 +1105,8 @@
Before the execution of the program, @command{radiusd} switches to the
uid and gid of the user @code{daemon} and the group @code{daemon}. You can
-override these defaults by setting variables @code{exec-program-user}
-and @code{exec-program-group} in configuration file to proper values
+override these defaults by setting the variable @code{exec-program-user}
+in the configuration file to a proper value.
@ref{option,, The option statement}.
The daemon does not wait for the process to terminate.
Index: elisp/radconf-mode.el
===================================================================
RCS file: /cvsroot/radius/radius/elisp/radconf-mode.el,v
retrieving revision 1.14
diff -u -r1.14 radconf-mode.el
--- elisp/radconf-mode.el 8 Nov 2003 09:48:08 -0000 1.14
+++ elisp/radconf-mode.el 12 Nov 2003 10:01:03 -0000
@@ -171,6 +171,7 @@
process-idle-timeout
master-read-timeout
master-write-timeout
+ run-user
exec-program-user
log-dir
acct-dir
Index: examples/config.syntax
===================================================================
RCS file: /cvsroot/radius/radius/examples/config.syntax,v
retrieving revision 1.19
diff -u -r1.19 config.syntax
--- examples/config.syntax 7 Nov 2003 14:47:00 -0000 1.19
+++ examples/config.syntax 12 Nov 2003 10:01:03 -0000
@@ -25,10 +25,29 @@
master-read-timeout 0;
master-write-timeout 0;
+ # The following specifies the uid under which the radius server
+ # should be run. The corresponding gid is determined from the
+ # user's passwd entry. The default is root (but we advice you
+ # to use another use whenever possible). Both the username and
+ # the numerical uid of a user may be specified.
+ # Remarks:
+ # If Auth-Type System or PAM is used, you will have to run your
+ # radius server as root, because these types need root access.
+ # If you you run your server as a non-root user, make sure the
+ # logging and accounting directories are writable for that user
+ # and the raddb files are readable.
+ run-user "root";
+
# The following specifies the uid under which external processes
# (those triggered by Exec-Program and Exec-Program-Wait
# attributes) should be run. The corresponding gid is determined
- # from the user's passwd entry. Default is daemon.
+ # from the user's passwd entry. Default is daemon. Both the username
+ # and the numerical uid of a user may be specified.
+ # Remarks:
+ # This feature will not work if the option run-user is set to a
+ # non-root user and the exec-program-user differs from the run-user.
+ # If the run-user is set and the exec-program-user is not, then the
+ # exec-program-user will automatically be the run-user.
exec-program-user "daemon";
# Specify the logging directory (-l command line option)
Index: include/cfg.h
===================================================================
RCS file: /cvsroot/radius/radius/include/cfg.h,v
retrieving revision 1.5
diff -u -r1.5 cfg.h
--- include/cfg.h 8 Nov 2003 09:48:09 -0000 1.5
+++ include/cfg.h 12 Nov 2003 10:01:03 -0000
@@ -41,6 +41,7 @@
#define CFG_PORT 5
#define CFG_CHAR 6
#define CFG_HOST 7
+#define CFG_USER 8
typedef struct {
int type;
@@ -94,6 +95,8 @@
int cfg_get_network(int argc, cfg_value_t *argv,
void *block_data, void *handler_data);
int cfg_get_port(int argc, cfg_value_t *argv,
+ void *block_data, void *handler_data);
+int cfg_get_user(int argc, cfg_value_t *argv,
void *block_data, void *handler_data);
int cfg_read(char *fname, struct cfg_stmt *syntax, void *data);
Index: include/radius.h
===================================================================
RCS file: /cvsroot/radius/radius/include/radius.h,v
retrieving revision 1.67
diff -u -r1.67 radius.h
--- include/radius.h 8 Nov 2003 09:48:09 -0000 1.67
+++ include/radius.h 12 Nov 2003 10:01:03 -0000
@@ -411,6 +411,9 @@
/* radpaths.c */
void radpath_init();
+/* radck.c */
+int check_root_authtypes();
+
/* users.y */
typedef int (*register_rule_fp) (void *, LOCUS *, char *,
VALUE_PAIR *, VALUE_PAIR *);
Index: include/radiusd.h
===================================================================
RCS file: /cvsroot/radius/radius/include/radiusd.h,v
retrieving revision 1.117
diff -u -r1.117 radiusd.h
--- include/radiusd.h 8 Nov 2003 09:48:09 -0000 1.117
+++ include/radiusd.h 12 Nov 2003 10:01:03 -0000
@@ -150,6 +150,16 @@
checks */
} User_symbol;
+/*
+ * Representation of a system user, to drop privileges to.
+ */
+struct user_info {
+ char *username;
+ int uid;
+ int gid;
+ int is_default;
+};
+
#define SNMP_RO 1
#define SNMP_RW 2
@@ -284,7 +294,8 @@
extern unsigned process_timeout;
extern unsigned radiusd_write_timeout;
extern unsigned radiusd_read_timeout;
-extern char *exec_user;
+extern struct user_info run_user;
+extern struct user_info exec_user;
extern UINT4 expiration_seconds;
extern UINT4 warning_seconds;
extern int use_dbm;
@@ -384,6 +395,8 @@
void radiusd_register_input_fd(char *name, int fd, void *data);
void radiusd_close_channel(int fd);
+#define DEFAULT_EXEC_USER "daemon"
+
/* exec.c */
int radius_exec_program(char *, RADIUS_REQ *, VALUE_PAIR **, int);
void filter_cleanup(pid_t pid, int status);
@@ -391,6 +404,9 @@
int filter_acct(char *name, RADIUS_REQ *req);
int filters_stmt_term(int finish, void *block_data, void *handler_data);
extern struct cfg_stmt filters_stmt[];
+
+/* droppriv.c */
+int drop_privileges(struct user_info *);
/* scheme.c */
void scheme_main();
Index: raddb/config
===================================================================
RCS file: /cvsroot/radius/radius/raddb/config,v
retrieving revision 1.11
diff -u -r1.11 config
--- raddb/config 19 Jun 2003 12:39:42 -0000 1.11
+++ raddb/config 12 Nov 2003 10:01:04 -0000
@@ -6,6 +6,7 @@
# Uncomment and edit these if you need to:
# log-dir "/var/log";
# acct-dir "/var/acct";
+# run-user "root";
resolve no;
};
Index: radiusd/Makefile.am
===================================================================
RCS file: /cvsroot/radius/radius/radiusd/Makefile.am,v
retrieving revision 1.43
diff -u -r1.43 Makefile.am
--- radiusd/Makefile.am 8 Nov 2003 09:48:10 -0000 1.43
+++ radiusd/Makefile.am 12 Nov 2003 10:01:04 -0000
@@ -21,6 +21,7 @@
checkrad.c\
config.c\
debugmod.c\
+ droppriv.c\
exec.c\
files.c\
forward.c\
Index: radiusd/config.y
===================================================================
RCS file: /cvsroot/radius/radius/radiusd/config.y,v
retrieving revision 1.65
diff -u -r1.65 config.y
--- radiusd/config.y 8 Nov 2003 09:48:10 -0000 1.65
+++ radiusd/config.y 12 Nov 2003 10:01:04 -0000
@@ -618,6 +618,7 @@
struct servent *s;
UINT4 ipaddr;
cfg_value_t value;
+ struct passwd *pwd;
value = *arg;
switch (type) {
@@ -679,6 +680,44 @@
}
break;
+ case CFG_USER:
+ pwd = NULL;
+ switch (value.type) {
+ case CFG_INTEGER:
+ pwd = getpwuid(value.v.number);
+ if (pwd == NULL)
+ radlog(L_ERR,
+ _("%s:%d: no such uid: %d - EXITING"),
+ cfg_filename, cfg_line_num,
+ value.v.number);
+ break;
+ case CFG_STRING:
+ pwd = getpwnam(value.v.string);
+ if (pwd == NULL)
+ radlog(L_ERR,
+ _("%s:%d: no such user: %s - EXITING"),
+ cfg_filename, cfg_line_num,
+ value.v.string);
+ break;
+ default:
+ radlog(L_CRIT,
+ _("%s:%d: illegal user specification - EXITING"),
+ cfg_filename, cfg_line_num);
+ break;
+ }
+
+ if (pwd != NULL) {
+ struct user_info *user = base;
+ if (user->username != NULL) efree(user->username);
+ user->username = estrdup(pwd->pw_name);
+ user->uid = pwd->pw_uid;
+ user->gid = pwd->pw_gid;
+ user->is_default = 0;
+ }
+ else
+ exit(1);
+ return 0;
+ break;
}
if (type != value.type) {
@@ -706,7 +745,7 @@
case CFG_NETWORK:
*(cfg_network_t *) base = value.v.network;
break;
-
+
default:
radlog(L_CRIT,
_("INTERNAL ERROR at %s:%d: unknown datatype %d"),
@@ -829,6 +868,14 @@
{
_check_argc(argc, 1);
return _get_value(&argv[1], CFG_PORT, handler_data);
+}
+
+int
+cfg_get_user(int argc, cfg_value_t *argv, void *block_data,
+ void *handler_data)
+{
+ _check_argc(argc, 1);
+ return _get_value(&argv[1], CFG_USER, handler_data);
}
int
Index: radiusd/exec.c
===================================================================
RCS file: /cvsroot/radius/radius/radiusd/exec.c,v
retrieving revision 1.59
diff -u -r1.59 exec.c
--- radiusd/exec.c 8 Nov 2003 09:48:10 -0000 1.59
+++ radiusd/exec.c 12 Nov 2003 10:01:04 -0000
@@ -36,7 +36,6 @@
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>
-#include <pwd.h>
#include <grp.h>
#include <string.h>
#include <syslog.h>
@@ -46,35 +45,6 @@
#include <symtab.h>
#include <rewrite.h>
-int
-radius_change_uid(struct passwd *pwd)
-{
- if (pwd->pw_gid != 0 && setgid(pwd->pw_gid)) {
- radlog(L_ERR|L_PERROR,
- _("setgid(%d) failed"), pwd->pw_gid);
- return -1;
- }
- if (pwd->pw_uid != 0) {
-#if defined(HAVE_SETEUID)
- if (seteuid(pwd->pw_uid)) {
- radlog(L_ERR|L_PERROR,
- _("seteuid(%d) failed (ruid=%d, euid=%d)"),
- pwd->pw_uid, getuid(), geteuid());
- return -1;
- }
-#elif defined(HAVE_SETREUID)
- if (setreuid(0, pwd->pw_uid)) {
- radlog(L_ERR|L_PERROR,
- _("setreuid(0,%d) failed (ruid=%d, euid=%d)"),
- pwd->pw_uid, getuid(), geteuid());
- return -1;
- }
-#else
-# error "*** NO WAY TO SET EFFECTIVE UID IN radius_change_uid() ***"
-#endif
- }
- return 0;
-}
/* Execute a program on successful authentication.
Return 0 if exec_wait == 0.
@@ -90,7 +60,6 @@
FILE *fp;
int line_num;
char buffer[RAD_BUFFER_SIZE];
- struct passwd *pwd;
pid_t pid;
int status;
RETSIGTYPE (*oldsig)();
@@ -102,17 +71,6 @@
return -1;
}
- /* Check user/group
- FIXME: This should be checked *once* after re-reading the
- configuration */
- pwd = getpwnam(exec_user);
- if (!pwd) {
- radlog(L_ERR,
- _("radius_exec_program(): won't execute, no such user: %s"),
- exec_user);
- return -1;
- }
-
if (exec_wait) {
if (pipe(p) != 0) {
radlog(L_ERR|L_PERROR, _("couldn't open pipe"));
@@ -151,7 +109,7 @@
chdir("/tmp");
- if (radius_change_uid(pwd))
+ if (drop_privileges(&exec_user))
exit(2);
execvp(argv[0], argv);
@@ -227,17 +185,8 @@
{
pid_t pid;
int rightp[2], leftp[2];
- struct passwd *pwd;
int i;
- pwd = getpwnam(exec_user);
- if (!pwd) {
- radlog(L_ERR,
- _("won't execute, no such user: %s"),
- exec_user);
- return -1;
- }
-
pipe(leftp);
pipe(rightp);
@@ -268,7 +217,7 @@
for (i = getmaxfd(); i > 2; i--)
close(i);
- if (radius_change_uid(pwd))
+ if (drop_privileges(&exec_user))
exit(2);
execvp(argv[0], argv);
Index: radiusd/radck.c
===================================================================
RCS file: /cvsroot/radius/radius/radiusd/radck.c,v
retrieving revision 1.22
diff -u -r1.22 radck.c
--- radiusd/radck.c 8 Nov 2003 09:48:10 -0000 1.22
+++ radiusd/radck.c 12 Nov 2003 10:01:04 -0000
@@ -462,3 +462,38 @@
rowi = (unsigned *) ((char *) rowi + rowsize);
}
}
+
+/* Check if there are Auth-Types used which need root access. */
+int
+root_authtype_checker(void *closure, User_symbol *sym)
+{
+ VALUE_PAIR *vp;
+
+ for (vp = sym->check; vp; vp = vp->next)
+ {
+ if (vp->attribute == DA_AUTH_TYPE &&
+ (vp->avp_lvalue == DV_AUTH_TYPE_SYSTEM ||
+ vp->avp_lvalue == DV_AUTH_TYPE_PAM)) {
+ (*(int *)closure)++;
+ }
+ }
+
+ return 0;
+}
+
+int
+check_root_authtypes()
+{
+ int fail_count = 0;
+
+ if (run_user.uid == 0) return 0;
+
+ symtab_iterate(user_tab, root_authtype_checker, &fail_count);
+ if (fail_count)
+ radlog(L_WARN,
+ _("Root-only Auth-Type (System or PAM) used with "
+ "non-root run-user %s: authentication might fail"),
+ run_user.username);
+ return fail_count;
+}
+
Index: radiusd/radiusd.c
===================================================================
RCS file: /cvsroot/radius/radius/radiusd/radiusd.c,v
retrieving revision 1.148
diff -u -r1.148 radiusd.c
--- radiusd/radiusd.c 6 Nov 2003 14:33:23 -0000 1.148
+++ radiusd/radiusd.c 12 Nov 2003 10:01:04 -0000
@@ -30,6 +30,7 @@
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>
+#include <pwd.h>
#include <radiusd.h>
#include <radargp.h>
@@ -132,7 +133,8 @@
unsigned process_timeout = PROCESS_TIMEOUT;
unsigned radiusd_write_timeout = RADIUSD_WRITE_TIMEOUT;
unsigned radiusd_read_timeout = RADIUSD_READ_TIMEOUT;
-char *exec_user = NULL;
+struct user_info run_user;
+struct user_info exec_user;
UINT4 warning_seconds;
int use_guile;
@@ -273,8 +275,27 @@
void
set_config_defaults()
{
- exec_user = estrdup("daemon");
+ struct passwd *pwd = getpwnam("daemon");
+ if (pwd != NULL) {
+ exec_user.username = estrdup(DEFAULT_EXEC_USER);
+ exec_user.uid = pwd->pw_uid;
+ exec_user.gid = pwd->pw_gid;
+ } else {
+ radlog(L_WARN,
+ _("User '%s' not found: reverting to "
+ "'root' as the default exec-program-user!"),
+ DEFAULT_EXEC_USER);
+ exec_user.username = estrdup("root");
+ exec_user.uid = exec_user.gid = 0;
+ }
+ exec_user.is_default = 1;
+
+ run_user.username = estrdup("root");
+ run_user.uid = run_user.gid = 0;
+ run_user.is_default = 1;
+
username_valid_chars = estrdup(".-_!@#$%^&\\/");
+
message_text[MSG_ACCOUNT_CLOSED] =
estrdup(_("Sorry, your account is currently closed\n"));
message_text[MSG_PASSWORD_EXPIRED] =
@@ -372,6 +393,35 @@
radlog(L_ALERT,
_("Radiusd is not listening on any port. Trying to continue anyway..."));
}
+
+ /* Is the program not running as root? */
+ if (run_user.uid) {
+ /* Check if the exec_user is the default exec_user. If yes,
+ then copy the run_user to the exec_user. If not, then
+ check if the exec_user differs from the run_user. In
+ that case things won't work correctly (after startup
+ we'll be non-root, so we won't be able to switch uid/gid
+ anymore). */
+ if (exec_user.is_default) {
+ exec_user.username = estrdup(run_user.username);
+ exec_user.uid = run_user.uid;
+ exec_user.gid = exec_user.gid;
+ }
+ else if (run_user.uid != exec_user.uid) {
+ radlog(L_ALERT,
+ _("Both the run-user and exec-program-user "
+ "are set, but they differ. This will not "
+ "work correctly in case external programs "
+ "are used!"));
+ }
+
+ /* Check if Auth-Type System or PAM is ever used. For this
+ Auth-Type, the program should run as root to function. */
+ check_root_authtypes();
+ }
+
+ /* Now, try to drop our privileges... FIXME: exit on failure?*/
+ drop_privileges(&run_user);
}
void
@@ -490,7 +540,7 @@
{
return radiusd_pid == getpid();
}
-
+
/* ****************************** Main function **************************** */
@@ -1221,7 +1271,9 @@
cfg_get_integer, &radiusd_read_timeout, NULL, NULL },
{ "master-write-timeout", CS_STMT, NULL,
cfg_get_integer, &radiusd_write_timeout, NULL, NULL },
- { "exec-program-user", CS_STMT, NULL, cfg_get_string, &exec_user,
+ { "run-user", CS_STMT, NULL, cfg_get_user, &run_user,
+ NULL, NULL },
+ { "exec-program-user", CS_STMT, NULL, cfg_get_user, &exec_user,
NULL, NULL },
{ "log-dir", CS_STMT, NULL, cfg_get_string, &radlog_dir,
NULL, NULL },