#include "autoconf.h"
#include "libradius.h"
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#include "radiusd.h"
#include "rad_assert.h"
#include "conffile.h"
#include "token.h"
#include <sys/resource.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/stat.h>
#include <grp.h>
#include <pwd.h>
#ifdef HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
#endif
#ifdef WITH_UDPFROMTO
#include "udpfromto.h"
#endif
#include "request_list.h"
#include <dlfcn.h>
struct main_config_t mainconfig;
static uid_t server_uid;
static gid_t server_gid;
static const char *localstatedir = NULL;
static const char *prefix = NULL;
static int auth_port = 0;
static CONF_PARSER proxy_config[] = {
{ "retry_delay", PW_TYPE_INTEGER, 0, &mainconfig.proxy_retry_delay, Stringify(RETRY_DELAY) },
{ "retry_count", PW_TYPE_INTEGER, 0, &mainconfig.proxy_retry_count, Stringify(RETRY_COUNT) },
{ "synchronous", PW_TYPE_BOOLEAN, 0, &mainconfig.proxy_synchronous, "no" },
{ "default_fallback", PW_TYPE_BOOLEAN, 0, &mainconfig.proxy_fallback, "no" },
{ "dead_time", PW_TYPE_INTEGER, 0, &mainconfig.proxy_dead_time, Stringify(DEAD_TIME) },
{ "post_proxy_authorize", PW_TYPE_BOOLEAN, 0, &mainconfig.post_proxy_authorize, "yes" },
{ "wake_all_if_all_dead", PW_TYPE_BOOLEAN, 0, &mainconfig.wake_all_if_all_dead, "no" },
{ NULL, -1, 0, NULL, NULL }
};
static CONF_PARSER security_config[] = {
{ "max_attributes", PW_TYPE_INTEGER, 0, &librad_max_attributes, Stringify(0) },
{ "reject_delay", PW_TYPE_INTEGER, 0, &mainconfig.reject_delay, Stringify(0) },
{ "status_server", PW_TYPE_BOOLEAN, 0, &mainconfig.status_server, "no"},
{ NULL, -1, 0, NULL, NULL }
};
static CONF_PARSER server_config[] = {
{ "prefix", PW_TYPE_STRING_PTR, 0, &prefix, "/usr/local"},
{ "localstatedir", PW_TYPE_STRING_PTR, 0, &localstatedir, "${prefix}/var"},
{ "logdir", PW_TYPE_STRING_PTR, 0, &radlog_dir, "${localstatedir}/log"},
{ "libdir", PW_TYPE_STRING_PTR, 0, &radlib_dir, "${prefix}/lib"},
{ "radacctdir", PW_TYPE_STRING_PTR, 0, &radacct_dir, "${logdir}/radacct" },
{ "hostname_lookups", PW_TYPE_BOOLEAN, 0, &librad_dodns, "no" },
#ifdef WITH_SNMP
{ "snmp", PW_TYPE_BOOLEAN, 0, &mainconfig.do_snmp, "no" },
#endif
{ "max_request_time", PW_TYPE_INTEGER, 0, &mainconfig.max_request_time, Stringify(MAX_REQUEST_TIME) },
{ "cleanup_delay", PW_TYPE_INTEGER, 0, &mainconfig.cleanup_delay, Stringify(CLEANUP_DELAY) },
{ "max_requests", PW_TYPE_INTEGER, 0, &mainconfig.max_requests, Stringify(MAX_REQUESTS) },
{ "delete_blocked_requests", PW_TYPE_INTEGER, 0, &mainconfig.kill_unresponsive_children, Stringify(FALSE) },
{ "port", PW_TYPE_INTEGER, 0, &auth_port, Stringify(PW_AUTH_UDP_PORT) },
{ "allow_core_dumps", PW_TYPE_BOOLEAN, 0, &mainconfig.allow_core_dumps, "no" },
{ "log_stripped_names", PW_TYPE_BOOLEAN, 0, &log_stripped_names,"no" },
{ "log_file", PW_TYPE_STRING_PTR, -1, &mainconfig.log_file, "${logdir}/radius.log" },
{ "log_auth", PW_TYPE_BOOLEAN, -1, &mainconfig.log_auth, "no" },
{ "log_auth_badpass", PW_TYPE_BOOLEAN, 0, &mainconfig.log_auth_badpass, "no" },
{ "log_auth_goodpass", PW_TYPE_BOOLEAN, 0, &mainconfig.log_auth_goodpass, "no" },
{ "pidfile", PW_TYPE_STRING_PTR, 0, &mainconfig.pid_file, "${run_dir}/radiusd.pid"},
{ "bind_address", PW_TYPE_IPADDR, 0, &mainconfig.myip, "*" },
{ "user", PW_TYPE_STRING_PTR, 0, &mainconfig.uid_name, NULL},
{ "group", PW_TYPE_STRING_PTR, 0, &mainconfig.gid_name, NULL},
{ "usercollide", PW_TYPE_BOOLEAN, 0, &mainconfig.do_usercollide, "no" },
{ "lower_user", PW_TYPE_STRING_PTR, 0, &mainconfig.do_lower_user, "no" },
{ "lower_pass", PW_TYPE_STRING_PTR, 0, &mainconfig.do_lower_pass, "no" },
{ "nospace_user", PW_TYPE_STRING_PTR, 0, &mainconfig.do_nospace_user, "no" },
{ "nospace_pass", PW_TYPE_STRING_PTR, 0, &mainconfig.do_nospace_pass, "no" },
{ "checkrad", PW_TYPE_STRING_PTR, 0, &mainconfig.checkrad, "${sbindir}/checkrad" },
{ "proxy_requests", PW_TYPE_BOOLEAN, 0, &mainconfig.proxy_requests, "yes" },
{ "proxy", PW_TYPE_SUBSECTION, 0, proxy_config, NULL },
{ "security", PW_TYPE_SUBSECTION, 0, security_config, NULL },
{ "debug_level", PW_TYPE_INTEGER, 0, &mainconfig.debug_level, "0"},
{ NULL, -1, 0, NULL, NULL }
};
void paircompare_builtin_free(void);
void xlat_free(void);
static int xlat_config(void *instance, REQUEST *request,
char *fmt, char *out,
size_t outlen,
RADIUS_ESCAPE_STRING func)
{
CONF_SECTION *cs;
CONF_PAIR *cp;
char buffer[1024];
char *p, *value;
const char *start = fmt;
request = request;
instance = instance;
cp = NULL;
cs = NULL;
while (cp == NULL) {
for (p = buffer; (*fmt != 0) && (*fmt != '.'); p++, fmt++) {
*p = *fmt;
}
*p = '\0';
if (*fmt == '.') {
CONF_SECTION *next;
fmt++;
if (cs == NULL) {
next = cf_section_find(buffer);
} else {
next = cf_subsection_find_next(cs, NULL, buffer);
}
if (next == NULL) {
radlog(L_ERR, "config: No such section %s in format string %s", buffer, start);
return 0;
}
cs = next;
} else {
cp = cf_pair_find(cs, buffer);
if (cp == NULL) {
radlog(L_ERR, "config: No such section %s in format string %s", buffer, start);
return 0;
}
}
}
value = cf_pair_value(cp);
if (value) {
if (outlen > strlen(value)) {
outlen = strlen(value) + 1;
}
}
return func(out, outlen, value);
}
static int r_mkdir(const char *part)
{
char *ptr, parentdir[500];
struct stat st;
if (stat(part, &st) == 0)
return(0);
ptr = strrchr(part, '/');
if (ptr == part)
return(0);
snprintf(parentdir, (ptr - part)+1, "%s", part);
if (r_mkdir(parentdir) != 0)
return(1);
if (mkdir(part, 0770) != 0) {
fprintf(stderr, "mkdir(%s) error: %s\n", part, strerror(errno));
return(1);
}
return(0);
}
static int radlogdir_iswritable(const char *effectiveuser)
{
struct passwd *pwent;
if (radlog_dir[0] != '/')
return(0);
if (r_mkdir(radlog_dir) != 0)
return(1);
#if __APPLE__
if (chown(radlog_dir, 0, 80) != 0)
return(1);
#endif
if (strstr(radlog_dir, "radius") == NULL)
return(0);
if (!effectiveuser) {
return 1;
}
pwent = getpwnam(effectiveuser);
if (pwent == NULL)
return(1);
if (chown(radlog_dir, pwent->pw_uid, -1) != 0)
return(1);
return(0);
}
static int switch_users(void)
{
if (mainconfig.gid_name != NULL) {
struct group *gr;
gr = getgrnam(mainconfig.gid_name);
if (gr == NULL) {
if (errno == ENOMEM) {
radlog(L_ERR|L_CONS, "Cannot switch to Group %s: out of memory", mainconfig.gid_name);
} else {
radlog(L_ERR|L_CONS, "Cannot switch group; %s doesn't exist", mainconfig.gid_name);
}
exit(1);
}
server_gid = gr->gr_gid;
if (setgid(server_gid) < 0) {
radlog(L_ERR|L_CONS, "Failed setting Group to %s: %s",
mainconfig.gid_name, strerror(errno));
exit(1);
}
} else {
server_gid = getgid();
}
if (mainconfig.uid_name != NULL) {
struct passwd *pw;
pw = getpwnam(mainconfig.uid_name);
if (pw == NULL) {
if (errno == ENOMEM) {
radlog(L_ERR|L_CONS, "Cannot switch to User %s: out of memory", mainconfig.uid_name);
} else {
radlog(L_ERR|L_CONS, "Cannot switch user; %s doesn't exist", mainconfig.uid_name);
}
exit(1);
}
server_uid = pw->pw_uid;
#ifdef HAVE_INITGROUPS
if (initgroups(mainconfig.uid_name, server_gid) < 0) {
if (errno != EPERM) {
radlog(L_ERR|L_CONS, "Failed setting supplementary groups for User %s: %s", mainconfig.uid_name, strerror(errno));
exit(1);
}
}
#endif
if (setuid(server_uid) < 0) {
radlog(L_ERR|L_CONS, "Failed setting User to %s: %s", mainconfig.uid_name, strerror(errno));
exit(1);
}
}
return(0);
}
static int generate_realms(const char *filename)
{
CONF_SECTION *cs;
REALM *my_realms = NULL;
REALM *c, **tail;
char *s, *t, *authhost, *accthost;
char *name2;
tail = &my_realms;
for (cs = cf_subsection_find_next(mainconfig.config, NULL, "realm");
cs != NULL;
cs = cf_subsection_find_next(mainconfig.config, cs, "realm")) {
name2 = cf_section_name2(cs);
if (!name2) {
radlog(L_CONS|L_ERR, "%s[%d]: Missing realm name",
filename, cf_section_lineno(cs));
return -1;
}
c = rad_malloc(sizeof(REALM));
memset(c, 0, sizeof(REALM));
c->secret[0] = '\0';
if ((authhost = cf_section_value_find(cs, "authhost")) == NULL) {
c->ipaddr = htonl(INADDR_NONE);
c->auth_port = 0;
} else {
if ((s = strchr(authhost, ':')) != NULL) {
*s++ = 0;
c->auth_port = atoi(s);
} else {
c->auth_port = PW_AUTH_UDP_PORT;
}
if (strcmp(authhost, "LOCAL") == 0) {
c->ipaddr = htonl(INADDR_NONE);
c->auth_port = 0;
} else {
c->ipaddr = ip_getaddr(authhost);
if (c->ipaddr == htonl(INADDR_NONE)) {
free(c);
radlog(L_ERR, "%s[%d]: Host %s not found",
filename, cf_section_lineno(cs),
authhost);
return -1;
}
}
if (strlen(authhost) >= sizeof(c->server)) {
free(c);
radlog(L_ERR, "%s[%d]: Server name of length %d is greater than allowed: %d",
filename, cf_section_lineno(cs),
(int) strlen(authhost),
(int) sizeof(c->server) - 1);
return -1;
}
}
if ((accthost = cf_section_value_find(cs, "accthost")) == NULL) {
c->acct_ipaddr = htonl(INADDR_NONE);
c->acct_port = 0;
} else {
if ((s = strchr(accthost, ':')) != NULL) {
*s++ = 0;
c->acct_port = atoi(s);
} else {
c->acct_port = PW_ACCT_UDP_PORT;
}
if (strcmp(accthost, "LOCAL") == 0) {
c->acct_ipaddr = htonl(INADDR_NONE);
c->acct_port = 0;
} else {
c->acct_ipaddr = ip_getaddr(accthost);
if (c->acct_ipaddr == htonl(INADDR_NONE)) {
free(c);
radlog(L_ERR, "%s[%d]: Host %s not found",
filename, cf_section_lineno(cs),
accthost);
return -1;
}
}
if (strlen(accthost) >= sizeof(c->acct_server)) {
free(c);
radlog(L_ERR, "%s[%d]: Server name of length %d is greater than allowed: %d",
filename, cf_section_lineno(cs),
(int) strlen(accthost),
(int) sizeof(c->acct_server) - 1);
return -1;
}
}
if (strlen(name2) >= sizeof(c->realm)) {
free(c);
radlog(L_ERR, "%s[%d]: Realm name of length %d is greater than allowed %d",
filename, cf_section_lineno(cs),
(int) strlen(name2),
(int) sizeof(c->server) - 1);
return -1;
}
strcpy(c->realm, name2);
if (authhost) strcpy(c->server, authhost);
if (accthost) strcpy(c->acct_server, accthost);
if ((c->ipaddr != htonl(INADDR_NONE)) ||
(c->acct_ipaddr != htonl(INADDR_NONE))) {
if ((s = cf_section_value_find(cs, "secret")) == NULL ) {
free(c);
radlog(L_ERR, "%s[%d]: No shared secret supplied for realm: %s",
filename, cf_section_lineno(cs), name2);
return -1;
}
if (strlen(s) >= sizeof(c->secret)) {
free(c);
radlog(L_ERR, "%s[%d]: Secret of length %u is greater than the allowed maximum of %u.",
filename, cf_section_lineno(cs),
strlen(s), sizeof(c->secret) - 1);
return -1;
}
strNcpy((char *)c->secret, s, sizeof(c->secret));
}
c->striprealm = 1;
if ((cf_section_value_find(cs, "nostrip")) != NULL)
c->striprealm = 0;
if ((cf_section_value_find(cs, "noacct")) != NULL)
c->acct_port = 0;
if ((cf_section_value_find(cs, "trusted")) != NULL)
c->trusted = 1;
if ((cf_section_value_find(cs, "notrealm")) != NULL)
c->notrealm = 1;
if ((cf_section_value_find(cs, "notsuffix")) != NULL)
c->notrealm = 1;
if ((t = cf_section_value_find(cs,"ldflag")) != NULL) {
static const LRAD_NAME_NUMBER ldflags[] = {
{ "fail_over", 0 },
{ "round_robin", 1 },
{ NULL, 0 }
};
c->ldflag = lrad_str2int(ldflags, t, -1);
if (c->ldflag == -1) {
free(c);
radlog(L_ERR, "%s[%d]: Unknown value \"%s\" for ldflag",
filename, cf_section_lineno(cs),
t);
return -1;
}
} else {
c->ldflag = 0;
}
c->active = TRUE;
c->acct_active = TRUE;
c->next = NULL;
*tail = c;
tail = &c->next;
}
*tail = mainconfig.realms;
mainconfig.realms = my_realms;
for(c = mainconfig.realms; c != NULL; c = c->next) {
REALM *this;
if ((c->ldflag == 1) &&
((c->ipaddr == htonl(INADDR_NONE)) ||
(c->acct_ipaddr == htonl(INADDR_NONE)))) {
radlog(L_ERR | L_CONS, "ERROR: Realm %s cannot be load balanced to LOCAL",
c->realm);
exit(1);
}
for (this = c->next; this != NULL; this = this->next) {
if (strcasecmp(c->realm, this->realm) != 0) {
continue;
}
if (c->ldflag != this->ldflag) {
radlog(L_ERR | L_CONS, "ERROR: Inconsistent value in realm %s for load balancing 'ldflag' attribute",
c->realm);
exit(1);
}
}
}
return 0;
}
static RADCLIENT *generate_clients(const char *filename, CONF_SECTION *section)
{
CONF_SECTION *cs;
RADCLIENT *list, *c;
char *hostnm, *secret, *shortnm, *netmask;
char *nastype, *login, *password;
char *name2;
list = NULL;
for (cs = cf_subsection_find_next(section, NULL, "client");
cs != NULL;
cs = cf_subsection_find_next(section, cs, "client")) {
name2 = cf_section_name2(cs);
if (!name2) {
radlog(L_CONS|L_ERR, "%s[%d]: Missing client name",
filename, cf_section_lineno(cs));
clients_free(list);
return NULL;
}
hostnm = name2;
if((secret = cf_section_value_find(cs, "secret")) == NULL) {
radlog(L_ERR, "%s[%d]: Missing secret for client: %s",
filename, cf_section_lineno(cs), name2);
clients_free(list);
return NULL;
}
if((shortnm = cf_section_value_find(cs, "shortname")) == NULL) {
radlog(L_ERR, "%s[%d]: Missing shortname for client: %s",
filename, cf_section_lineno(cs), name2);
clients_free(list);
return NULL;
}
netmask = strchr(hostnm, '/');
if (strlen(secret) >= sizeof(c->secret)) {
radlog(L_ERR, "%s[%d]: Secret of length %ld is greater than the allowed maximum of %ld.",
filename, cf_section_lineno(cs),
strlen(secret), sizeof(c->secret) - 1);
clients_free(list);
return NULL;
}
if (strlen(shortnm) > sizeof(c->shortname)) {
radlog(L_ERR, "%s[%d]: Client short name of length %ld is greater than the allowed maximum of %ld.",
filename, cf_section_lineno(cs),
strlen(shortnm), sizeof(c->shortname) - 1);
clients_free(list);
return NULL;
}
if((nastype = cf_section_value_find(cs, "nastype")) != NULL) {
if(strlen(nastype) >= sizeof(c->nastype)) {
radlog(L_ERR, "%s[%d]: nastype of length %ld longer than the allowed maximum of %ld",
filename, cf_section_lineno(cs),
strlen(nastype), sizeof(c->nastype) - 1);
clients_free(list);
return NULL;
}
}
if((login = cf_section_value_find(cs, "login")) != NULL) {
if(strlen(login) >= sizeof(c->login)) {
radlog(L_ERR, "%s[%d]: login of length %ld longer than the allowed maximum of %ld",
filename, cf_section_lineno(cs),
strlen(login), sizeof(c->login) - 1);
clients_free(list);
return NULL;
}
}
if((password = cf_section_value_find(cs, "password")) != NULL) {
if(strlen(password) >= sizeof(c->password)) {
radlog(L_ERR, "%s[%d]: password of length %ld longer than the allowed maximum of %ld",
filename, cf_section_lineno(cs),
strlen(password), sizeof(c->password) - 1);
clients_free(list);
return NULL;
}
}
c = rad_malloc(sizeof(RADCLIENT));
memset(c, 0, sizeof(RADCLIENT));
c->netmask = ~0;
if (netmask) {
int mask_length;
mask_length = atoi(netmask + 1);
if ((mask_length < 0) || (mask_length > 32)) {
radlog(L_ERR, "%s[%d]: Invalid value '%s' for IP network mask.",
filename, cf_section_lineno(cs), netmask + 1);
clients_free(list);
free(c);
return NULL;
}
if (mask_length == 0) {
c->netmask = 0;
} else {
c->netmask = ~0 << (32 - mask_length);
}
*netmask = '\0';
c->netmask = htonl(c->netmask);
}
c->ipaddr = ip_getaddr(hostnm);
if (c->ipaddr == INADDR_NONE) {
radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s",
filename, cf_section_lineno(cs), hostnm);
clients_free(list);
free(c);
return NULL;
}
if (netmask) {
*netmask = '/';
c->ipaddr &= c->netmask;
strcpy(c->longname, hostnm);
} else {
ip_hostname(c->longname, sizeof(c->longname),
c->ipaddr);
}
strcpy((char *)c->secret, secret);
strcpy(c->shortname, shortnm);
if(nastype != NULL)
strcpy(c->nastype, nastype);
if(login != NULL)
strcpy(c->login, login);
if(password != NULL)
strcpy(c->password, password);
c->next = list;
list = c;
}
return list;
}
static rad_listen_t listen_inst;
static const char *listen_type = NULL;
static const CONF_PARSER listen_config[] = {
{ "ipaddr", PW_TYPE_IPADDR,
offsetof(rad_listen_t,ipaddr), NULL, "0.0.0.0" },
{ "port", PW_TYPE_INTEGER,
offsetof(rad_listen_t,port), NULL, "0" },
{ "type", PW_TYPE_STRING_PTR,
0, &listen_type, "" },
{ NULL, -1, 0, NULL, NULL }
};
static const LRAD_NAME_NUMBER listen_compare[] = {
{ "auth", RAD_LISTEN_AUTH },
{ "acct", RAD_LISTEN_ACCT },
{ NULL, 0 },
};
static void listen_free(rad_listen_t *list)
{
while (list) {
rad_listen_t *next = list->next;
if (list->fd >= 0) close(list->fd);
free(list);
list = next;
}
}
static int listen_bind(rad_listen_t *this)
{
struct sockaddr salocal;
struct sockaddr_in *sa;
rad_listen_t **last;
if (this->port == 0) {
struct servent *svp;
switch (this->type) {
case RAD_LISTEN_AUTH:
svp = getservbyname ("radius", "udp");
if (svp != NULL) {
this->port = ntohs(svp->s_port);
} else {
this->port = PW_AUTH_UDP_PORT;
}
break;
case RAD_LISTEN_ACCT:
svp = getservbyname ("radacct", "udp");
if (svp != NULL) {
this->port = ntohs(svp->s_port);
} else {
this->port = PW_ACCT_UDP_PORT;
}
break;
default:
radlog(L_ERR|L_CONS, "ERROR: Non-fatal internal sanity check failed in bind.");
return -1;
}
}
for (last = &mainconfig.listen;
*last != NULL;
last = &((*last)->next)) {
if ((this->ipaddr == (*last)->ipaddr) &&
(this->type == (*last)->type) &&
(this->port == (*last)->port)) {
this->fd = (*last)->fd;
(*last)->fd = -1;
return 0;
}
}
this->fd = socket(AF_INET, SOCK_DGRAM, 0);
if (this->fd < 0) {
return -1;
}
#ifdef WITH_UDPFROMTO
if (udpfromto_init(this->fd) != 0) {
radlog(L_ERR|L_CONS, "ERROR: udpfromto init failed.");
}
#endif
sa = (struct sockaddr_in *) &salocal;
memset ((char *) sa, '\0', sizeof(salocal));
sa->sin_family = AF_INET;
sa->sin_addr.s_addr = this->ipaddr;
sa->sin_port = htons(this->port);
if (bind(this->fd, &salocal, sizeof(*sa)) < 0) {
close(this->fd);
this->fd = -1;
return -1;
}
return 0;
}
static int last_proxy_port = 0;
int proxy_new_listener(void)
{
int port;
rad_listen_t *this;
this = rad_malloc(sizeof(*this));
memset(this, 0, sizeof(*this));
this->ipaddr = mainconfig.myip;
this->type = RAD_LISTEN_PROXY;
if (last_proxy_port == 0) {
free(this);
return -1;
}
for (port = last_proxy_port + 1; port < 64000; port++) {
this->port = port;
if (listen_bind(this) == 0) {
rad_listen_t **last;
last_proxy_port = port;
for (last = &mainconfig.listen;
*last != NULL;
last = &((*last)->next)) {
}
*last = this;
return this->fd;
}
}
free(this);
return -1;
}
static int listen_init(const char *filename, rad_listen_t **head)
{
CONF_SECTION *cs;
rad_listen_t **last;
char buffer[32];
rad_listen_t *this;
for (last = head; *last != NULL; last = &((*last)->next)) {
}
for (cs = cf_subsection_find_next(mainconfig.config,
NULL, "listen");
cs != NULL;
cs = cf_subsection_find_next(mainconfig.config,
cs, "listen")) {
memset(&listen_inst, 0, sizeof(listen_inst));
if (cf_section_parse(cs, &listen_inst, listen_config) < 0) {
radlog(L_CONS|L_ERR, "%s[%d]: Error parsing listen section.",
filename, cf_section_lineno(cs));
return -1;
}
if (listen_type) {
listen_inst.type = lrad_str2int(listen_compare,
listen_type, 0);
}
if (listen_inst.type == RAD_LISTEN_NONE) {
radlog(L_CONS|L_ERR, "%s[%d]: Invalid type in listen section.",
filename, cf_section_lineno(cs));
return -1;
}
this = rad_malloc(sizeof(*this));
memcpy(this, &listen_inst, sizeof(*this));
if (listen_bind(this) < 0) {
radlog(L_CONS|L_ERR, "%s[%d]: Error binding to port for %s:%d",
filename, cf_section_lineno(cs),
ip_ntoa(buffer, this->ipaddr), this->port);
free(this);
return -1;
}
*last = this;
last = &(this->next);
}
if (mainconfig.proxy_requests == TRUE) {
int port = -1;
rad_listen_t *auth;
int num_realms = 0;
int localhost = 0;
int otherhost = 0;
REALM *realm;
uint32_t proxy_ip;
uint32_t ipaddr;
for (realm = mainconfig.realms;
realm != NULL;
realm = realm->next) {
if ((realm->ipaddr == htonl(INADDR_NONE)) &&
(realm->acct_ipaddr == htonl(INADDR_NONE))) {
continue;
}
num_realms++;
if (realm->ipaddr == htonl(INADDR_LOOPBACK)) {
localhost = 1;
} else {
otherhost = 1;
}
if (realm->acct_ipaddr == htonl(INADDR_LOOPBACK)) {
localhost = 1;
} else {
otherhost = 1;
}
}
if (num_realms == 0) {
return 0;
}
if (localhost && !otherhost) {
proxy_ip = htonl(INADDR_LOOPBACK);
} else {
proxy_ip = htonl(INADDR_NONE);
}
ipaddr = htonl(INADDR_NONE);
for (auth = *head; auth != NULL; auth = auth->next) {
if (ipaddr != htonl(INADDR_ANY)) {
if ((ipaddr == htonl(INADDR_NONE)) ||
(auth->ipaddr == htonl(INADDR_ANY))) {
ipaddr = auth->ipaddr;
} else if (ipaddr != auth->ipaddr) {
ipaddr = htonl(INADDR_ANY);
}
}
if (auth->type == RAD_LISTEN_AUTH) {
port = auth->port + 2;
break;
}
}
if (port < 0) for (auth = *head; auth != NULL; auth = auth->next) {
if (auth->type == RAD_LISTEN_ACCT) {
port = auth->port + 1;
break;
}
}
if (port < 0) {
return 0;
}
this = rad_malloc(sizeof(*this));
memset(this, 0, sizeof(*this));
if (proxy_ip == htonl(INADDR_NONE)) {
proxy_ip = ipaddr;
}
this->ipaddr = proxy_ip;
this->type = RAD_LISTEN_PROXY;
for (this->port = port;
this->port < 64000;
this->port++) {
if (listen_bind(this) == 0) {
last_proxy_port = this->port;
*last = this;
return 0;
}
}
radlog(L_ERR|L_CONS, "Failed to open socket for proxying");
free(this);
return -1;
}
return 0;
}
static int old_listen_init(rad_listen_t **head)
{
CONF_PAIR *cp;
rad_listen_t *this, **last;
cp = cf_pair_find(mainconfig.config, "bind_address");
if (!cp) return 0;
last = head;
this = rad_malloc(sizeof(*this));
memset(this, 0, sizeof(*this));
this->ipaddr = mainconfig.myip;
this->type = RAD_LISTEN_AUTH;
this->port = auth_port;
if (listen_bind(this) < 0) {
radlog(L_CONS|L_ERR, "There appears to be another RADIUS server running on the authentication port %d", this->port);
free(this);
return -1;
}
auth_port = this->port;
*last = this;
last = &(this->next);
this = rad_malloc(sizeof(*this));
memset(this, 0, sizeof(*this));
this->ipaddr = mainconfig.myip;
this->type = RAD_LISTEN_ACCT;
this->port = auth_port + 1;
if (listen_bind(this) < 0) {
radlog(L_CONS|L_ERR, "There appears to be another RADIUS server running on the accounting port %d", this->port);
free(this);
return -1;
}
*last = this;
return 0;
}
#ifndef RADIUS_CONFIG
#define RADIUS_CONFIG "radiusd.conf"
#endif
CONF_SECTION *read_radius_conf_file(void)
{
char buffer[256];
CONF_SECTION *cs;
struct stat statbuf;
if (stat(radius_dir, &statbuf) < 0) {
radlog(L_ERR|L_CONS, "Errors reading %s: %s",
radius_dir, strerror(errno));
return NULL;
}
if ((statbuf.st_mode & S_IWOTH) != 0) {
radlog(L_ERR|L_CONS, "Configuration directory %s is globally writable. Refusing to start due to insecure configuration.",
radius_dir);
return NULL;
}
if (0 && (statbuf.st_mode & S_IROTH) != 0) {
radlog(L_ERR|L_CONS, "Configuration directory %s is globally readable. Refusing to start due to insecure configuration.",
radius_dir);
return NULL;
}
snprintf(buffer, sizeof(buffer), "%.200s/%.50s", radius_dir, RADIUS_CONFIG);
if ((cs = conf_read(NULL, 0, buffer, NULL)) == NULL) {
return NULL;
}
cf_section_parse(cs, NULL, server_config);
DEBUG2("read_config_files: reading dictionary");
if (dict_init(radius_dir, RADIUS_DICTIONARY) != 0) {
radlog(L_ERR|L_CONS, "Errors reading dictionary: %s",
librad_errstr);
cf_section_free(&cs);
return NULL;
}
return cs;
}
int read_mainconfig(int reload)
{
struct rlimit core_limits;
static int old_debug_level = -1;
char buffer[1024];
CONF_SECTION *cs, *oldcs;
rad_listen_t *listener;
RADCLIENT *c, *tail;
if (!reload) {
radlog(L_INFO, "Starting - reading configuration files ...");
} else {
radlog(L_INFO, "Reloading configuration files.");
}
DEBUG2("reread_config: reading radiusd.conf");
if ((cs = read_radius_conf_file()) == NULL) {
if (debug_flag ||
(radlog_dir == NULL)) {
radlog(L_ERR|L_CONS, "Errors reading radiusd.conf");
} else {
radlog(L_ERR|L_CONS, "Errors reading %s/radiusd.conf: For more information, please read the tail end of %s", radius_dir, mainconfig.log_file);
}
return -1;
}
oldcs = mainconfig.config;
mainconfig.config = cs;
cf_section_free(&oldcs);
snprintf(buffer, sizeof(buffer), "%.200s/%.50s", radius_dir, RADIUS_NASLIST);
DEBUG2("read_config_files: reading naslist");
if (read_naslist_file(buffer) < 0) {
radlog(L_ERR|L_CONS, "Errors reading naslist");
return -1;
}
snprintf(buffer, sizeof(buffer), "%.200s/%.50s", radius_dir, RADIUS_CLIENTS);
DEBUG2("read_config_files: reading clients");
if (read_clients_file(buffer) < 0) {
radlog(L_ERR|L_CONS, "Errors reading clients");
return -1;
}
snprintf(buffer, sizeof(buffer), "%.200s/%.50s", radius_dir, RADIUS_CONFIG);
c = generate_clients(buffer, mainconfig.config);
if (!c) {
return -1;
}
for (tail = c; tail->next != NULL; tail = tail->next) {
}
tail->next = mainconfig.clients;
mainconfig.clients = c;
snprintf(buffer, sizeof(buffer), "%.200s/%.50s", radius_dir, RADIUS_REALMS);
DEBUG2("read_config_files: reading realms");
if (read_realms_file(buffer) < 0) {
radlog(L_ERR|L_CONS, "Errors reading realms");
return -1;
}
snprintf(buffer, sizeof(buffer), "%.200s/%.50s", radius_dir, RADIUS_CONFIG);
if (generate_realms(buffer) < 0) {
return -1;
}
xlat_register("config", xlat_config, NULL);
if ((mainconfig.debug_level > debug_flag) ||
(mainconfig.debug_level <= old_debug_level)) {
debug_flag = mainconfig.debug_level;
}
librad_debug = debug_flag;
old_debug_level = mainconfig.debug_level;
if (getrlimit(RLIMIT_CORE, &core_limits) < 0) {
radlog(L_ERR|L_CONS, "Failed to get current core limit: %s", strerror(errno));
exit(1);
}
if (mainconfig.allow_core_dumps) {
if (setrlimit(RLIMIT_CORE, &core_limits) < 0) {
radlog(L_ERR|L_CONS, "Cannot update core dump limit: %s",
strerror(errno));
exit(1);
} else if ((core_limits.rlim_cur != 0) && !debug_flag)
radlog(L_INFO|L_CONS, "Core dumps are enabled.");
} else if (!debug_flag) {
struct rlimit limits;
limits.rlim_cur = 0;
limits.rlim_max = core_limits.rlim_max;
if (setrlimit(RLIMIT_CORE, &limits) < 0) {
radlog(L_ERR|L_CONS, "Cannot disable core dumps: %s",
strerror(errno));
exit(1);
}
}
if (!reload) {
radlogdir_iswritable(mainconfig.uid_name);
}
switch_users();
#ifdef HAVE_SYS_PRCTL_H
#ifdef HAVE_PR_SET_DUMPABLE
if (mainconfig.allow_core_dumps) {
if (prctl(PR_SET_DUMPABLE, 1) < 0) {
radlog(L_ERR|L_CONS,"Cannot enable core dumps: prctl(PR_SET_DUMPABLE) failed: '%s'",
strerror(errno));
}
}
#endif
#endif
if (mainconfig.reject_delay > mainconfig.cleanup_delay) {
mainconfig.reject_delay = mainconfig.cleanup_delay;
}
listener = NULL;
if (old_listen_init(&listener) < 0) {
exit(1);
}
snprintf(buffer, sizeof(buffer), "%.200s/radiusd.conf", radius_dir);
if (listen_init(buffer, &listener) < 0) {
exit(1);
}
if (!listener) {
radlog(L_ERR|L_CONS, "Server is not configured to listen on any ports. Exiting.");
exit(1);
}
listen_free(mainconfig.listen);
mainconfig.listen = listener;
return 0;
}
int free_mainconfig(void)
{
cf_section_free(&mainconfig.config);
realm_free(mainconfig.realms);
clients_free(mainconfig.clients);
read_naslist_file(NULL);
rl_free();
listen_free(mainconfig.listen);
paircompare_builtin_free();
xlat_free();
dict_free();
return 0;
}