*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
- * Copyright 2000,2001,2002,2003,2004 The FreeRADIUS server project
+ * Copyright 2000-2004,2006 The FreeRADIUS server project
* Copyright 1999,2000 Miquel van Smoorenburg <miquels@cistron.nl>
* Copyright 2000 Alan DeKok <aland@ox.org>
* Copyright 2000 Alan Curry <pacman-radius@cqc.com>
* Copyright 2000 Chad Miller <cmiller@surfsouth.com>
*/
-/* don't look here for the version, run radiusd -v or look in version.c */
-static const char rcsid[] =
-"$Id$";
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
-#include "autoconf.h"
-#include "libradius.h"
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/radius_snmp.h>
+#include <freeradius-devel/modules.h>
+#include <freeradius-devel/rad_assert.h>
#include <sys/file.h>
-#ifdef HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
#include <fcntl.h>
#include <ctype.h>
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-
#include <signal.h>
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#endif
-#ifdef HAVE_SYS_SELECT_H
-# include <sys/select.h>
-#endif
-
-#ifdef HAVE_SYSLOG_H
-# include <syslog.h>
-#endif
-
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
#endif
-#include "radiusd.h"
-#include "rad_assert.h"
-#include "conffile.h"
-#include "modules.h"
-#include "request_list.h"
-#include "radius_snmp.h"
+#ifndef HAVE_PTHREAD_H
+#define thread_pool_lock(_x)
+#define thread_pool_unlock(_x)
+#endif
/*
* Global variables.
const char *radius_dir = NULL;
const char *radacct_dir = NULL;
const char *radlog_dir = NULL;
-radlog_dest_t radlog_dest = RADLOG_FILES;
const char *radlib_dir = NULL;
-int syslog_facility;
int log_stripped_names;
int debug_flag = 0;
-int log_auth_detail = FALSE;
-int need_reload = FALSE;
-int sig_hup_block = FALSE;
+int check_config = FALSE;
+
const char *radiusd_version = "FreeRADIUS Version " RADIUSD_VERSION ", for host " HOSTINFO ", built on " __DATE__ " at " __TIME__;
-static time_t time_now;
-static pid_t radius_pid;
+time_t time_now;
+pid_t radius_pid;
+
+static int debug_memory = 0;
/*
* Configuration items.
*/
-static int dont_fork = FALSE;
-static time_t start_time = 0;
-static int spawn_flag = TRUE;
-static int do_exit = 0;
/*
* Static functions.
static void usage(int);
static void sig_fatal (int);
+#ifdef SIGHUP
static void sig_hup (int);
-
-static int rad_status_server(REQUEST *request);
-
-/*
- * Parse a string into a syslog facility level.
- */
-static int str2fac(const char *s)
-{
-#ifdef LOG_KERN
- if(!strcmp(s, "kern"))
- return LOG_KERN;
- else
-#endif
-#ifdef LOG_USER
- if(!strcmp(s, "user"))
- return LOG_USER;
- else
-#endif
-#ifdef LOG_MAIL
- if(!strcmp(s, "mail"))
- return LOG_MAIL;
- else
-#endif
-#ifdef LOG_DAEMON
- if(!strcmp(s, "daemon"))
- return LOG_DAEMON;
- else
-#endif
-#ifdef LOG_AUTH
- if(!strcmp(s, "auth"))
- return LOG_AUTH;
- else
-#endif
-#ifdef LOG_SYSLOG
- if(!strcmp(s, "auth"))
- return LOG_AUTH;
- else
-#endif
-#ifdef LOG_LPR
- if(!strcmp(s, "lpr"))
- return LOG_LPR;
- else
-#endif
-#ifdef LOG_NEWS
- if(!strcmp(s, "news"))
- return LOG_NEWS;
- else
#endif
-#ifdef LOG_UUCP
- if(!strcmp(s, "uucp"))
- return LOG_UUCP;
- else
-#endif
-#ifdef LOG_CRON
- if(!strcmp(s, "cron"))
- return LOG_CRON;
- else
-#endif
-#ifdef LOG_AUTHPRIV
- if(!strcmp(s, "authpriv"))
- return LOG_AUTHPRIV;
- else
-#endif
-#ifdef LOG_FTP
- if(!strcmp(s, "ftp"))
- return LOG_FTP;
- else
-#endif
-#ifdef LOG_LOCAL0
- if(!strcmp(s, "local0"))
- return LOG_LOCAL0;
- else
-#endif
-#ifdef LOG_LOCAL1
- if(!strcmp(s, "local1"))
- return LOG_LOCAL1;
- else
-#endif
-#ifdef LOG_LOCAL2
- if(!strcmp(s, "local2"))
- return LOG_LOCAL2;
- else
-#endif
-#ifdef LOG_LOCAL3
- if(!strcmp(s, "local3"))
- return LOG_LOCAL3;
- else
-#endif
-#ifdef LOG_LOCAL4
- if(!strcmp(s, "local4"))
- return LOG_LOCAL4;
- else
-#endif
-#ifdef LOG_LOCAL5
- if(!strcmp(s, "local5"))
- return LOG_LOCAL5;
- else
-#endif
-#ifdef LOG_LOCAL6
- if(!strcmp(s, "local6"))
- return LOG_LOCAL6;
- else
-#endif
-#ifdef LOG_LOCAL7
- if(!strcmp(s, "local7"))
- return LOG_LOCAL7;
- else
-#endif
- {
- fprintf(stderr, "%s: Error: Unknown syslog facility: %s\n",
- progname, s);
- exit(1);
- }
-
- /* this should never be reached */
- return LOG_DAEMON;
-}
-
-
-/*
- * Check if an incoming request is "ok"
- *
- * It takes packets, not requests. It sees if the packet looks
- * OK. If so, it does a number of sanity checks on it.
- */
-static RAD_REQUEST_FUNP packet_ok(RADIUS_PACKET *packet,
- rad_listen_t *listener)
-{
- REQUEST *curreq;
- RAD_REQUEST_FUNP fun = NULL;
-
- /*
- * Some sanity checks, based on the packet code.
- */
- switch(packet->code) {
- case PW_AUTHENTICATION_REQUEST:
- /*
- * Check for requests sent to the wrong
- * port, and ignore them, if so.
- */
- if (listener->type != RAD_LISTEN_AUTH) {
- RAD_SNMP_INC(rad_snmp.auth.total_packets_dropped);
- radlog(L_ERR, "Authentication-Request sent to a non-authentication port from "
- "client %s:%d - ID %d : IGNORED",
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id);
- return NULL;
- }
- fun = rad_authenticate;
- break;
-
- case PW_ACCOUNTING_REQUEST:
- /*
- * Check for requests sent to the wrong
- * port, and ignore them, if so.
- */
- if (listener->type != RAD_LISTEN_ACCT) {
- RAD_SNMP_INC(rad_snmp.acct.total_packets_dropped);
- radlog(L_ERR, "Accounting-Request packet sent to a non-accounting port from "
- "client %s:%d - ID %d : IGNORED",
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id);
- return NULL;
- }
- fun = rad_accounting;
- break;
-
- case PW_AUTHENTICATION_ACK:
- case PW_ACCESS_CHALLENGE:
- case PW_AUTHENTICATION_REJECT:
- /*
- * Replies NOT sent to the proxy port get
- * an error message logged, and the
- * packet is dropped.
- */
- if (listener->type != RAD_LISTEN_PROXY) {
- RAD_SNMP_INC(rad_snmp.auth.total_packets_dropped);
- radlog(L_ERR, "Authentication reply packet code %d sent to a non-proxy reply port from "
- "client %s:%d - ID %d : IGNORED",
- packet->code,
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id);
- return NULL;
- }
- fun = rad_authenticate;
- break;
-
- case PW_ACCOUNTING_RESPONSE:
- /*
- * Replies NOT sent to the proxy port get
- * an error message logged, and the
- * packet is dropped.
- */
- if (listener->type != RAD_LISTEN_PROXY) {
- RAD_SNMP_INC(rad_snmp.acct.total_packets_dropped);
- radlog(L_ERR, "Accounting reply packet code %d sent to a non-proxy reply port from "
- "client %s:%d - ID %d : IGNORED",
- packet->code,
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id);
- return 0;
- }
- fun = rad_accounting;
- break;
-
- case PW_STATUS_SERVER:
- if (!mainconfig.status_server) {
- DEBUG("WARNING: Ignoring Status-Server request due to security configuration");
- return NULL;
- }
- fun = rad_status_server;
- break;
-
- case PW_PASSWORD_REQUEST:
- RAD_SNMP_INC(rad_snmp.auth.total_unknown_types);
-
- /*
- * We don't support this anymore.
- */
- radlog(L_ERR, "Deprecated password change request from client %s:%d - ID %d : IGNORED",
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id);
- return NULL;
- break;
-
- default:
- RAD_SNMP_INC(rad_snmp.auth.total_unknown_types);
-
- radlog(L_ERR, "Unknown packet code %d from client %s:%d "
- "- ID %d : IGNORED", packet->code,
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id);
- return NULL;
- break;
-
- } /* switch over packet types */
-
- /*
- * Don't handle proxy replies here. They need to
- * return the *old* request, so we can re-process it.
- */
- if (listener->type == RAD_LISTEN_PROXY) {
- return fun;
- }
-
- /*
- * If there is no existing request of id, code, etc.,
- * then we can return, and let it be processed.
- */
- if ((curreq = rl_find(packet)) == NULL) {
- /*
- * Count the total number of requests, to see if
- * there are too many. If so, return with an
- * error.
- */
- if (mainconfig.max_requests) {
- int request_count = rl_num_requests();
-
- /*
- * This is a new request. Let's see if
- * it makes us go over our configured
- * bounds.
- */
- if (request_count > mainconfig.max_requests) {
- radlog(L_ERR, "Dropping request (%d is too many): "
- "from client %s:%d - ID: %d", request_count,
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id);
- radlog(L_INFO, "WARNING: Please check the radiusd.conf file.\n"
- "\tThe value for 'max_requests' is probably set too low.\n");
- return NULL;
- } /* else there were a small number of requests */
- } /* else there was no configured limit for requests */
-
- /*
- * FIXME: Add checks for system load. If the
- * system is busy, start dropping requests...
- *
- * We can probably keep some statistics
- * ourselves... if there are more requests
- * coming in than we can handle, start dropping
- * some.
- */
-
- return fun;
- }
-
- /*
- * "fake" requests MUST NEVER be in the request list.
- *
- * They're used internally in the server. Any reply
- * is a reply to the local server, and any proxied packet
- * gets sent outside of the tunnel.
- */
- rad_assert((curreq->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0);
-
- /*
- * The current request isn't finished, which
- * means that the NAS sent us a new packet, while
- * we are still processing the old request.
- */
- if (!curreq->finished) {
- /*
- * If the authentication vectors are identical,
- * then the NAS is re-transmitting it, trying to
- * kick us into responding to the request.
- */
- if (memcmp(curreq->packet->vector, packet->vector,
- sizeof(packet->vector)) == 0) {
- RAD_SNMP_INC(rad_snmp.auth.total_dup_requests);
-
- /*
- * It's not finished because the request
- * was proxied, but there was no reply
- * from the home server.
- */
- if (curreq->proxy && !curreq->proxy_reply) {
- /*
- * We're taking care of sending
- * duplicate proxied packets, so
- * we ignore any duplicate
- * requests from the NAS.
- *
- * FIXME: Make it ALWAYS synchronous!
- */
- if (!mainconfig.proxy_synchronous) {
- RAD_SNMP_TYPE_INC(listener, total_packets_dropped);
-
- DEBUG2("Ignoring duplicate packet from client "
- "%s:%d - ID: %d, due to outstanding proxied request %d.",
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id,
- curreq->number);
- return NULL;
-
- /*
- * We ARE proxying the request,
- * and we have NOT received a
- * proxy reply yet, and we ARE
- * doing synchronous proxying.
- *
- * In that case, go kick
- * the home RADIUS server
- * again.
- */
- } else {
- char buffer[64];
-
- DEBUG2("Sending duplicate proxied request to home server %s:%d - ID: %d",
- ip_ntoa(buffer, curreq->proxy->dst_ipaddr),
- curreq->proxy->dst_port,
-
- curreq->proxy->id);
- }
- curreq->proxy_next_try = time_now + mainconfig.proxy_retry_delay;
- rad_send(curreq->proxy, curreq->packet,
- curreq->proxysecret);
- return NULL;
- } /* else the packet was not proxied */
-
- /*
- * Someone's still working on it, so we
- * ignore the duplicate request.
- */
- radlog(L_ERR, "Discarding duplicate request from "
- "client %s:%d - ID: %d due to unfinished request %d",
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id,
- curreq->number);
- return NULL;
- } /* else the authentication vectors were different */
-
- /*
- * The authentication vectors are different, so
- * the NAS has given up on us, as we've taken too
- * long to process the request. This is a
- * SERIOUS problem!
- */
- RAD_SNMP_TYPE_INC(listener, total_packets_dropped);
-
- radlog(L_ERR, "Dropping conflicting packet from "
- "client %s:%d - ID: %d due to unfinished request %d",
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id,
- curreq->number);
- return NULL;
- }
-
- /*
- * The old request is finished. We now check the
- * authentication vectors. If the client has sent us a
- * request with identical code && ID, but different
- * vector, then they MUST have gotten our response, so we
- * can delete the original request, and process the new
- * one.
- *
- * If the vectors are the same, then it's a duplicate
- * request, and we can send a duplicate reply.
- */
- if (memcmp(curreq->packet->vector, packet->vector,
- sizeof(packet->vector)) == 0) {
- RAD_SNMP_INC(rad_snmp.auth.total_dup_requests);
-
- /*
- * If the packet has been delayed, then silently
- * send a response, and clear the delayed flag.
- *
- * Note that this means if the NAS kicks us while
- * we're delaying a reject, then the reject may
- * be sent sooner than otherwise.
- *
- * This COULD be construed as a bug. Maybe what
- * we want to do is to ignore the duplicate
- * packet, and send the reject later.
- */
- if (curreq->options & RAD_REQUEST_OPTION_DELAYED_REJECT) {
- curreq->options &= ~RAD_REQUEST_OPTION_DELAYED_REJECT;
- rad_send(curreq->reply, curreq->packet, curreq->secret);
- return NULL;
- }
-
- /*
- * Maybe we've saved a reply packet. If so,
- * re-send it. Otherwise, just complain.
- */
- if (curreq->reply->code != 0) {
- DEBUG2("Sending duplicate reply "
- "to client %s:%d - ID: %d",
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id);
- rad_send(curreq->reply, curreq->packet, curreq->secret);
- return NULL;
- }
-
- /*
- * Else we never sent a reply to the NAS,
- * as we decided somehow we didn't like the request.
- *
- * This shouldn't happen, in general...
- */
- DEBUG2("Discarding duplicate request from client %s:%d - ID: %d",
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id);
- return NULL;
- } /* else the vectors were different, so we discard the old request. */
-
- /*
- * 'packet' has the same source IP, source port, code,
- * and Id as 'curreq', but a different authentication
- * vector. We can therefore delete 'curreq', as we were
- * only keeping it around to send out duplicate replies,
- * if the first reply got lost in the network.
- */
- rl_delete(curreq);
-
- /*
- * The request is OK. We can process it...
- *
- * Don't bother checking the maximum nubmer of requests
- * here. we've just deleted one, so we KNOW we're under
- * the limit if we add one more.
- */
- return fun;
-}
-
-
-/*
- * Do a proxy check of the REQUEST list when using the new proxy code.
- */
-static REQUEST *proxy_ok(RADIUS_PACKET *packet)
-{
- REALM *cl;
- REQUEST *oldreq;
- char buffer[32];
-
- /*
- * Find the original request in the request list
- */
- oldreq = rl_find_proxy(packet);
-
- /*
- * If we haven't found the original request which was
- * sent, to get this reply. Complain, and discard this
- * request, as there's no way for us to send it to a NAS.
- */
- if (!oldreq) {
- radlog(L_PROXY, "No outstanding request was found for proxy reply from home server %s:%d - ID %d",
- ip_ntoa(buffer, packet->src_ipaddr),
- packet->src_port, packet->id);
- return NULL;
- }
-
- /*
- * The proxy reply has arrived too late, as the original
- * (old) request has timed out, been rejected, and marked
- * as finished. The client has already received a
- * response, so there is nothing that can be done. Delete
- * the tardy reply from the home server, and return NULL.
- */
- if ((oldreq->reply->code != 0) ||
- (oldreq->finished)) {
- radlog(L_ERR, "Reply from home server %s:%d - ID: %d arrived too late for request %d. Try increasing 'retry_delay' or 'max_request_time'",
- ip_ntoa(buffer, packet->src_ipaddr),
- packet->src_port, packet->id,
- oldreq->number);
- return NULL;
- }
-
- /*
- * If there is already a reply, maybe this one is a
- * duplicate?
- */
- if (oldreq->proxy_reply) {
- if (memcmp(oldreq->proxy_reply->vector,
- packet->vector,
- sizeof(oldreq->proxy_reply->vector)) == 0) {
- radlog(L_ERR, "Discarding duplicate reply from home server %s:%d - ID: %d for request %d",
- ip_ntoa(buffer, packet->src_ipaddr),
- packet->src_port, packet->id,
- oldreq->number);
- } else {
- /*
- * ? The home server gave us a new *
- * proxy reply, which doesn't match * the
- * old one. Delete it
- ! */
- DEBUG2("Ignoring conflicting proxy reply");
- }
-
- /*
- * We've already received a reply, so
- * we discard this one, as we don't want
- * to do duplicate work.
- */
- return NULL;
- } /* else there wasn't a proxy reply yet, so we can process it */
-
- /*
- * Refresh the old request, and update it with the proxy
- * reply.
- *
- * ? Can we delete the proxy request here? * Is there
- * any more need for it?
- *
- * FIXME: we probably shouldn't be updating the time
- * stamp here.
- */
- oldreq->timestamp = time_now;
- oldreq->proxy_reply = packet;
-
- /*
- * Now that we've verified the packet IS actually
- * from that realm, and not forged, we can go mark the
- * realms for this home server as active.
- *
- * If we had done this check in the 'find realm by IP address'
- * function, then an attacker could force us to use a home
- * server which was inactive, by forging reply packets
- * which didn't match any request. We would think that
- * the reply meant the home server was active, would
- * re-activate the realms, and THEN bounce the packet
- * as garbage.
- */
- for (cl = mainconfig.realms; cl != NULL; cl = cl->next) {
- if (oldreq->proxy_reply->src_ipaddr == cl->ipaddr) {
- if (oldreq->proxy_reply->src_port == cl->auth_port) {
- cl->active = TRUE;
- cl->last_reply = oldreq->timestamp;
- } else if (oldreq->proxy_reply->src_port == cl->acct_port) {
- cl->acct_active = TRUE;
- cl->last_reply = oldreq->timestamp;
- }
- }
- }
-
- return oldreq;
-}
-
-/*
- * Do more checks, this time on the REQUEST data structure.
- *
- * The main purpose of this code is to handle proxied requests.
- */
-static REQUEST *request_ok(RADIUS_PACKET *packet, uint8_t *secret,
- rad_listen_t *listener)
-{
- REQUEST *request = NULL;
-
- /*
- * If the request has come in on the proxy FD, then
- * it's a proxy reply, so pass it through the code which
- * tries to find the original request, which we should
- * process, rather than processing the reply as a "new"
- * request.
- */
- if (listener->type == RAD_LISTEN_PROXY) {
- /*
- * Find the old request, based on the current
- * packet.
- */
- request = proxy_ok(packet);
- if (!request) {
- return NULL;
- }
- rad_assert(request->magic == REQUEST_MAGIC);
-
- /*
- * We must have passed through the code below
- * for the original request, which adds the
- * reply packet to it.
- */
- rad_assert(request->reply != NULL);
-
- } else { /* remember the new request */
- /*
- * A unique per-request counter.
- */
- static int request_num_counter = 0;
-
- request = request_alloc(); /* never fails */
- request->packet = packet;
- request->number = request_num_counter++;
- strNcpy(request->secret, (char *)secret,
- sizeof(request->secret));
-
- /*
- * Remember the request.
- */
- rl_add(request);
-
- /*
- * ADD IN "server identifier" from "listen"
- * directive!
- */
-
- /*
- * The request passes many of our sanity checks.
- * From here on in, if anything goes wrong, we
- * send a reject message, instead of dropping the
- * packet.
- *
- * Build the reply template from the request
- * template.
- */
- rad_assert(request->reply == NULL);
- if ((request->reply = rad_alloc(0)) == NULL) {
- radlog(L_ERR, "No memory");
- exit(1);
- }
- request->reply->sockfd = request->packet->sockfd;
- request->reply->dst_ipaddr = request->packet->src_ipaddr;
- request->reply->src_ipaddr = request->packet->dst_ipaddr;
- request->reply->dst_port = request->packet->src_port;
- request->reply->src_port = request->packet->dst_port;
- request->reply->id = request->packet->id;
- request->reply->code = 0; /* UNKNOWN code */
- memcpy(request->reply->vector, request->packet->vector,
- sizeof(request->reply->vector));
- request->reply->vps = NULL;
- request->reply->data = NULL;
- request->reply->data_len = 0;
- }
-
- return request;
-}
-
/*
* The main guy.
*/
int main(int argc, char *argv[])
{
- REQUEST *request;
- RADIUS_PACKET *packet;
- u_char *secret;
- unsigned char buffer[4096];
- fd_set readfds;
+ int rcode;
int argval;
- int pid;
- int max_fd;
- int status;
- struct timeval *tv = NULL;
+ int spawn_flag = TRUE;
+ int dont_fork = FALSE;
+ int flag = 0;
+
#ifdef HAVE_SIGACTION
struct sigaction act;
#endif
- rad_listen_t *listener;
-
- syslog_facility = LOG_DAEMON;
#ifdef OSFC2
set_auth_parameters(argc,argv);
else
progname++;
+#ifdef WIN32
+ {
+ WSADATA wsaData;
+ if (WSAStartup(MAKEWORD(2, 0), &wsaData)) {
+ fprintf(stderr, "%s: Unable to initialize socket library.\n");
+ return 1;
+ }
+ }
+#endif
+
debug_flag = 0;
spawn_flag = TRUE;
radius_dir = strdup(RADIUS_DIR);
* Ensure that the configuration is initialized.
*/
memset(&mainconfig, 0, sizeof(mainconfig));
+ mainconfig.myip.af = AF_UNSPEC;
+ mainconfig.port = -1;
+ mainconfig.radiusd_conf = strdup("radiusd.conf");
+
#ifdef HAVE_SIGACTION
memset(&act, 0, sizeof(act));
act.sa_flags = 0 ;
sigemptyset( &act.sa_mask ) ;
#endif
- /* Process the options. */
- while ((argval = getopt(argc, argv, "Aa:bcd:fg:hi:l:p:sSvxXyz")) != EOF) {
-
- switch(argval) {
-
- case 'A':
- log_auth_detail = TRUE;
- break;
-
- case 'a':
- if (radacct_dir) xfree(radacct_dir);
- radacct_dir = strdup(optarg);
- break;
-
- case 'c':
- /* ignore for backwards compatibility with Cistron */
- break;
-
- case 'd':
- if (radius_dir) xfree(radius_dir);
- radius_dir = strdup(optarg);
- break;
+ /*
+ * Don't put output anywhere until we get told a little
+ * more.
+ */
+ mainconfig.radlog_fd = -1;
+ mainconfig.log_file = NULL;
- case 'f':
- dont_fork = TRUE;
- break;
-
- case 'h':
- usage(0);
- break;
-
- case 'i':
- if ((mainconfig.myip = ip_getaddr(optarg)) == INADDR_NONE) {
- fprintf(stderr, "radiusd: %s: host unknown\n",
- optarg);
- exit(1);
- }
- break;
-
- case 'l':
- radlog_dir = strdup(optarg);
- break;
-
- /*
- * We should also have this as a configuration
- * file directive.
- */
- case 'g':
- syslog_facility = str2fac(optarg);
- break;
-
- case 'S':
- log_stripped_names++;
- break;
-
- case 'p':
- fprintf(stderr, "Ignoring deprecated command-line option -p");
- break;
-
- case 's': /* Single process mode */
- spawn_flag = FALSE;
- dont_fork = TRUE;
- break;
-
- case 'v':
- version();
- break;
-
- /*
- * BIG debugging mode for users who are
- * TOO LAZY to type '-sfxxyz -l stdout' themselves.
- */
- case 'X':
- spawn_flag = FALSE;
- dont_fork = TRUE;
- debug_flag += 2;
- mainconfig.log_auth = TRUE;
- mainconfig.log_auth_badpass = TRUE;
- mainconfig.log_auth_goodpass = TRUE;
- radlog_dir = strdup("stdout");
- break;
-
- case 'x':
- debug_flag++;
- break;
-
- case 'y':
- mainconfig.log_auth = TRUE;
- mainconfig.log_auth_badpass = TRUE;
- break;
-
- case 'z':
- mainconfig.log_auth_badpass = TRUE;
- mainconfig.log_auth_goodpass = TRUE;
- break;
-
- default:
- usage(1);
- break;
- }
- }
-
- /*
- * Get our PID.
- */
- radius_pid = getpid();
-
- /* Read the configuration files, BEFORE doing anything else. */
- if (read_mainconfig(0) < 0) {
- exit(1);
- }
-
- /*
- * If we're NOT debugging, trap fatal signals, so we can
- * easily clean up after ourselves.
- *
- * If we ARE debugging, don't trap them, so we can
- * dump core.
- */
- if ((mainconfig.allow_core_dumps == FALSE) && (debug_flag == 0)) {
-#ifdef SIGSEGV
-#ifdef HAVE_SIGACTION
- act.sa_handler = sig_fatal;
- sigaction(SIGSEGV, &act, NULL);
-#else
- signal(SIGSEGV, sig_fatal);
-#endif
-#endif
- }
-
- /* Reload the modules. */
- DEBUG2("radiusd: entering modules setup");
- if (setup_modules() < 0) {
- radlog(L_ERR|L_CONS, "Errors setting up modules");
- exit(1);
- }
-
-#ifdef HAVE_SYSLOG_H
- /*
- * If they asked for syslog, then give it to them.
- * Also, initialize the logging facility with the
- * configuration that they asked for.
- */
- if (strcmp(radlog_dir, "syslog") == 0) {
- openlog(progname, LOG_PID, syslog_facility);
- radlog_dest = RADLOG_SYSLOG;
- }
- /* Do you want a warning if -g is used without a -l to activate it? */
-#endif
- if (strcmp(radlog_dir, "stdout") == 0) {
- radlog_dest = RADLOG_STDOUT;
- } else if (strcmp(radlog_dir, "stderr") == 0) {
- radlog_dest = RADLOG_STDERR;
- }
-
- /* Initialize the request list. */
- rl_init();
-
- /*
- * Register built-in compare functions.
- */
- pair_builtincompare_init();
-
-#ifdef WITH_SNMP
- if (mainconfig.do_snmp) radius_snmp_init();
-#endif
-
- /*
- * Disconnect from session
- */
- if (debug_flag == 0 && dont_fork == FALSE) {
- pid = fork();
- if(pid < 0) {
- radlog(L_ERR|L_CONS, "Couldn't fork");
- exit(1);
- }
-
- /*
- * The parent exits, so the child can run in the background.
- */
- if(pid > 0) {
- exit(0);
- }
-#ifdef HAVE_SETSID
- setsid();
-#endif
- }
-
- /*
- * Ensure that we're using the CORRECT pid after forking,
- * NOT the one we started with.
- */
- radius_pid = getpid();
-
-
- /*
- * Only write the PID file if we're running as a daemon.
- *
- * And write it AFTER we've forked, so that we write the
- * correct PID.
- */
- if (dont_fork == FALSE) {
- FILE *fp;
-
- fp = fopen(mainconfig.pid_file, "w");
- if (fp != NULL) {
- /*
- * FIXME: What about following symlinks,
- * and having it over-write a normal file?
- */
- fprintf(fp, "%d\n", (int) radius_pid);
- fclose(fp);
- } else {
- radlog(L_ERR|L_CONS, "Failed creating PID file %s: %s\n",
- mainconfig.pid_file, strerror(errno));
- exit(1);
- }
- }
-
- /*
- * If we're running as a daemon, close the default file
- * descriptors, AFTER forking.
- */
- if (debug_flag == FALSE) {
- int devnull;
-
- devnull = open("/dev/null", O_RDWR);
- if (devnull < 0) {
- radlog(L_ERR|L_CONS, "Failed opening /dev/null: %s\n",
- strerror(errno));
- exit(1);
- }
- dup2(devnull, STDIN_FILENO);
- dup2(devnull, STDOUT_FILENO);
- dup2(devnull, STDERR_FILENO);
- close(devnull);
- }
-
-#ifdef HAVE_PTHREAD_H
- /*
- * If we're spawning children, set up the thread pool.
- */
- if (spawn_flag == TRUE) {
- thread_pool_init();
- }
-#else
- /*
- * Without threads, we ALWAYS run in single-server mode.
- */
- spawn_flag = FALSE;
-#endif
-
- /*
- * Use linebuffered or unbuffered stdout if
- * the debug flag is on.
- */
- if (debug_flag == TRUE)
- setlinebuf(stdout);
-
- /*
- * Print out which ports we're listening on.
- */
- for (listener = mainconfig.listen;
- listener != NULL;
- listener = listener->next) {
- if (listener->ipaddr == INADDR_ANY) {
- strcpy((char *)buffer, "*");
- } else {
- ip_ntoa((char *)buffer, listener->ipaddr);
- }
-
- switch (listener->type) {
- case RAD_LISTEN_AUTH:
- DEBUG("Listening on authentication %s:%d",
- buffer, listener->port);
- break;
-
- case RAD_LISTEN_ACCT:
- DEBUG("Listening on accounting %s:%d",
- buffer, listener->port);
- break;
-
- case RAD_LISTEN_PROXY:
- DEBUG("Listening on proxy %s:%d",
- buffer, listener->port);
- break;
-
- default:
- break;
- }
- }
-
- /*
- * Now that we've set everything up, we can install the signal
- * handlers. Before this, if we get any signal, we don't know
- * what to do, so we might as well do the default, and die.
- */
- signal(SIGPIPE, SIG_IGN);
-#ifdef HAVE_SIGACTION
- act.sa_handler = sig_hup;
- sigaction(SIGHUP, &act, NULL);
- act.sa_handler = sig_fatal;
- sigaction(SIGTERM, &act, NULL);
-#else
- signal(SIGHUP, sig_hup);
- signal(SIGTERM, sig_fatal);
-#endif
- /*
- * If we're debugging, then a CTRL-C will cause the
- * server to die immediately. Use SIGTERM to shut down
- * the server cleanly in that case.
- */
- if (debug_flag == 0) {
-#ifdef HAVE_SIGACTION
- act.sa_handler = sig_fatal;
- sigaction(SIGINT, &act, NULL);
- sigaction(SIGQUIT, &act, NULL);
-#else
- signal(SIGINT, sig_fatal);
- signal(SIGQUIT, sig_fatal);
-#endif
- }
-
- radlog(L_INFO, "Ready to process requests.");
- start_time = time(NULL);
-
- /*
- * Receive user requests
- */
- for (;;) {
- /*
- * If we've been told to exit, then do so,
- * even if we have data waiting.
- */
- if (do_exit) {
- DEBUG("Exiting...");
-
- /*
- * Ignore the TERM signal: we're about
- * to die.
- */
- signal(SIGTERM, SIG_IGN);
-
- /*
- * Send a TERM signal to all associated
- * processes (including us, which gets
- * ignored.)
- */
- kill(-radius_pid, SIGTERM);
-
- /*
- * FIXME: Kill child threads, and
- * clean up?
- */
-
- /*
- * Detach any modules.
- */
- detach_modules();
-
- /*
- * FIXME: clean up any active REQUEST
- * handles.
- */
-
- /*
- * We're exiting, so we can delete the PID
- * file. (If it doesn't exist, we can ignore
- * the error returned by unlink)
- */
- if (dont_fork == FALSE) {
- unlink(mainconfig.pid_file);
- }
-
- /*
- * Free the configuration items.
- */
- free_mainconfig();
-
- /*
- * SIGTERM gets do_exit=0,
- * and we want to exit cleanly.
- *
- * Other signals make us exit
- * with an error status.
- */
- exit(do_exit - 1);
- }
-
- if (need_reload) {
-#ifdef HAVE_PTHREAD_H
- /*
- * Threads: wait for all threads to stop
- * processing before re-loading the
- * config, so we don't pull the rug out
- * from under them.
- */
- int max_wait = 0;
- if (spawn_flag) for(;;) {
- /*
- * Block until there are '0' threads
- * with a REQUEST handle.
- */
- sig_hup_block = TRUE;
- if( (total_active_threads() == 0) ||
- (max_wait >= 5) ) {
- sig_hup_block = FALSE;
- break;
- }
- sleep(1);
- max_wait++;
- }
-#endif
- if (read_mainconfig(TRUE) < 0) {
- exit(1);
- }
-
- /* Reload the modules. */
- DEBUG2("radiusd: entering modules setup");
- if (setup_modules() < 0) {
- radlog(L_ERR|L_CONS, "Errors setting up modules");
- exit(1);
- }
-
- need_reload = FALSE;
- radlog(L_INFO, "Ready to process requests.");
- }
-
- FD_ZERO(&readfds);
- max_fd = 0;
-
- /*
- * Loop over all the listening FD's.
- */
- for (listener = mainconfig.listen;
- listener != NULL;
- listener = listener->next) {
- FD_SET(listener->fd, &readfds);
- if (listener->fd > max_fd) max_fd = listener->fd;
- }
-
-#ifdef WITH_SNMP
- if (mainconfig.do_snmp &&
- (rad_snmp.smux_fd >= 0)) {
- FD_SET(rad_snmp.smux_fd, &readfds);
- if (rad_snmp.smux_fd > max_fd) max_fd = rad_snmp.smux_fd;
- }
-#endif
- status = select(max_fd + 1, &readfds, NULL, NULL, tv);
- if (status == -1) {
- /*
- * On interrupts, we clean up the request
- * list. We then continue with the loop,
- * so that if we're supposed to exit,
- * then the code at the start of the loop
- * catches that, and exits.
- */
- if (errno == EINTR) {
- tv = rl_clean_list(time(NULL));
- continue;
- }
- radlog(L_ERR, "Unexpected error in select(): %s",
- strerror(errno));
- exit(1);
- }
-
- time_now = time(NULL);
-#ifndef HAVE_PTHREAD_H
- /*
- * If there are no child threads, then there may
- * be child processes. In that case, wait for
- * their exit status, and throw that exit status
- * away. This helps get rid of zxombie children.
- */
- while (waitpid(-1, &argval, WNOHANG) > 0) {
- /* do nothing */
- }
-#endif
-
- /*
- * Loop over the open socket FD's, reading any data.
- */
- for (listener = mainconfig.listen;
- listener != NULL;
- listener = listener->next) {
- RAD_REQUEST_FUNP fun;
-
- if (!FD_ISSET(listener->fd, &readfds))
- continue;
- /*
- * Receive the packet.
- */
- if (sig_hup_block != FALSE) {
- continue;
- }
- packet = rad_recv(listener->fd);
- if (packet == NULL) {
- radlog(L_ERR, "%s", librad_errstr);
- continue;
- }
-
- /*
- * If the destination IP is unknown, check
- * if the listener has a known IP. If so,
- * use that.
- */
- if ((packet->dst_ipaddr == htonl(INADDR_ANY)) &&
- (packet->dst_ipaddr != listener->ipaddr)) {
- packet->dst_ipaddr = listener->ipaddr;
- }
-
- /*
- * Fill in the destination port.
- */
- packet->dst_port = listener->port;
-
- RAD_SNMP_TYPE_INC(listener, total_requests);
-
- /*
- * FIXME: Move this next check into
- * the packet_ok() function, and add
- * a 'secret' to the RAIDUS_PACKET
- * data structure. This involves changing
- * a bunch of code, but it's probably the
- * best thing to do.
- */
-
- /*
- * Check if we know this client for
- * authentication and accounting. Check if we know
- * this proxy for proxying.
- */
- if (listener->type != RAD_LISTEN_PROXY) {
- RADCLIENT *cl;
- if ((cl = client_find(packet->src_ipaddr)) == NULL) {
- RAD_SNMP_TYPE_INC(listener, total_invalid_requests);
-
- radlog(L_ERR, "Ignoring request from unknown client %s:%d",
- ip_ntoa((char *)buffer, packet->src_ipaddr),
- packet->src_port);
- rad_free(&packet);
- continue;
- }
- secret = cl->secret;
- } else { /* It came in on the proxy port */
- REALM *rl;
- if ((rl = realm_findbyaddr(packet->src_ipaddr,packet->src_port)) == NULL) {
- radlog(L_ERR, "Ignoring request from unknown home server %s:%d",
- ip_ntoa((char *)buffer, packet->src_ipaddr),
- packet->src_port);
- rad_free(&packet);
- continue;
- }
-
- /*
- * The secret isn't needed here,
- * as it's already in the old request
- */
- secret = NULL;
- }
-
- /*
- * Do some simple checks before we process
- * the request.
- */
- if ((fun = packet_ok(packet, listener)) == NULL) {
- rad_free(&packet);
- continue;
- }
-
- /*
- * Allocate a new request for packets from
- * our clients, OR find the old request,
- * for packets which are replies from a home
- * server.
- */
- request = request_ok(packet, secret, listener);
- if (!request) {
- rad_free(&packet);
- continue;
- }
-
- /*
- * Drop the request into the thread pool,
- * and let the thread pool take care of
- * doing something with it.
- */
-#ifdef HAVE_PTHREAD_H
- if (spawn_flag) {
- if (!thread_pool_addrequest(request, fun)) {
- /*
- * FIXME: Maybe just drop
- * the packet on the floor?
- */
- request_reject(request);
- request->finished = TRUE;
- }
- } else
-#endif
- rad_respond(request, fun);
- } /* loop over listening sockets*/
-
-#ifdef WITH_SNMP
- if (mainconfig.do_snmp) {
- /*
- * After handling all authentication/accounting
- * requests, THEN process any pending SMUX/SNMP
- * queries.
- *
- * Note that the handling is done in the main server,
- * which probably isn't a Good Thing. It really
- * should be wrapped, and handled in a thread pool.
- */
- if ((rad_snmp.smux_fd >= 0) &&
- FD_ISSET(rad_snmp.smux_fd, &readfds) &&
- (rad_snmp.smux_event == SMUX_READ)) {
- smux_read();
- }
-
- /*
- * If we've got to re-connect, then do so now,
- * before calling select again.
- */
- if (rad_snmp.smux_event == SMUX_CONNECT) {
- smux_connect();
- }
- }
-#endif
-
- /*
- * After processing all new requests,
- * check if we've got to delete old requests
- * from the request list.
- */
- tv = rl_clean_list(time_now);
-#ifdef HAVE_PTHREAD_H
-
- /*
- * Only clean the thread pool if we're spawning
- * child threads.
- */
- if (spawn_flag) {
- thread_pool_clean(time_now);
- }
-#endif
+ /* Process the options. */
+ while ((argval = getopt(argc, argv, "Cd:fhi:mn:p:svxX")) != EOF) {
+ switch(argval) {
+ case 'C':
+ check_config = TRUE;
+ spawn_flag = FALSE;
+ dont_fork = TRUE;
+ break;
- } /* loop forever */
-}
+ case 'd':
+ if (radius_dir) free(radius_dir);
+ radius_dir = strdup(optarg);
+ break;
+ case 'f':
+ dont_fork = TRUE;
+ break;
-/*
- * FIXME: The next two functions should all
- * be in a module. But not until we have
- * more control over module execution.
- * -jcarneal
- */
+ case 'h':
+ usage(0);
+ break;
-/*
- * Lowercase the string value of a pair.
- */
-static int rad_lowerpair(REQUEST *request UNUSED, VALUE_PAIR *vp) {
- if (vp == NULL) {
- return -1;
- }
+ case 'i':
+ if (ip_hton(optarg, AF_UNSPEC, &mainconfig.myip) < 0) {
+ fprintf(stderr, "radiusd: Invalid IP Address or hostname \"%s\"\n", optarg);
+ exit(1);
+ }
+ flag |= 1;
+ break;
- rad_lowercase((char *)vp->strvalue);
- DEBUG2("rad_lowerpair: %s now '%s'", vp->name, vp->strvalue);
- return 0;
-}
+ case 'm':
+ debug_memory = 1;
+ break;
-/*
- * Remove spaces in a pair.
- */
-static int rad_rmspace_pair(REQUEST *request UNUSED, VALUE_PAIR *vp) {
- if (vp == NULL) {
- return -1;
- }
+ case 'p':
+ mainconfig.port = atoi(optarg);
+ if ((mainconfig.port <= 0) ||
+ (mainconfig.port >= 65536)) {
+ fprintf(stderr, "radiusd: Invalid port number %s\n", optarg);
+ exit(1);
+ }
+ flag |= 2;
+ break;
- rad_rmspace((char *)vp->strvalue);
- vp->length = strlen((char *)vp->strvalue);
- DEBUG2("rad_rmspace_pair: %s now '%s'", vp->name, vp->strvalue);
+ case 's': /* Single process mode */
+ spawn_flag = FALSE;
+ dont_fork = TRUE;
+ break;
- return 0;
-}
+ case 'v':
+ version();
+ break;
-/*
- * Respond to a request packet.
- *
- * Maybe we reply, maybe we don't.
- * Maybe we proxy the request to another server, or else maybe
- * we replicate it to another server.
- */
-int rad_respond(REQUEST *request, RAD_REQUEST_FUNP fun)
-{
- RADIUS_PACKET *packet, *original;
- const char *secret;
- int finished = FALSE;
- int reprocess = 0;
+ case 'X':
+ spawn_flag = FALSE;
+ dont_fork = TRUE;
+ debug_flag += 2;
+ mainconfig.log_auth = TRUE;
+ mainconfig.log_auth_badpass = TRUE;
+ mainconfig.log_auth_goodpass = TRUE;
+ mainconfig.radlog_dest = RADLOG_STDOUT;
+ mainconfig.radlog_fd = STDOUT_FILENO;
+ break;
- rad_assert(request->magic == REQUEST_MAGIC);
+ case 'x':
+ debug_flag++;
+ break;
- /*
- * Don't decode the packet if it's an internal "fake"
- * request. Instead, just skip ahead to processing it.
- */
- if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) != 0) {
- goto skip_decode;
+ default:
+ usage(1);
+ break;
+ }
}
- /*
- * Put the decoded packet into it's proper place.
- */
- if (request->proxy_reply != NULL) {
- packet = request->proxy_reply;
- secret = request->proxysecret;
- original = request->proxy;
- } else {
- packet = request->packet;
- secret = request->secret;
- original = NULL;
+ if (flag && (flag != 0x03)) {
+ fprintf(stderr, "radiusd: The options -i and -p cannot be used individually.\n");
+ exit(1);
}
- /*
- * Decode the packet, verifying it's signature,
- * and parsing the attributes into structures.
- *
- * Note that we do this CPU-intensive work in
- * a child thread, not the master. This helps to
- * spread the load a little bit.
- *
- * Internal requests (ones that never go on the
- * wire) have ->data==NULL (data is the wire
- * format) and don't need to be "decoded"
- */
- if (packet->data && rad_decode(packet, original, secret) != 0) {
- radlog(L_ERR, "%s", librad_errstr);
- request_reject(request);
- goto finished_request;
+ if (debug_flag) {
+ radlog(L_INFO, "%s", radiusd_version);
+ radlog(L_INFO, "Copyright (C) 2000-2007 The FreeRADIUS server project.\n");
+ radlog(L_INFO, "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n");
+ radlog(L_INFO, "PARTICULAR PURPOSE.\n");
+ radlog(L_INFO, "You may redistribute copies of FreeRADIUS under the terms of the\n");
+ radlog(L_INFO, "GNU General Public License.\n");
+ fflush(NULL);
+ }
+
+ /* Read the configuration files, BEFORE doing anything else. */
+ if (read_mainconfig(0) < 0) {
+ exit(1);
}
+#ifndef __MINGW32__
/*
- * For proxy replies, remove non-allowed
- * attributes from the list of VP's.
+ * Disconnect from session
*/
- if (request->proxy) {
- int rcode;
- rcode = proxy_receive(request);
- switch (rcode) {
- default: /* Don't Do Anything */
- break;
- case RLM_MODULE_FAIL:
- /* on error just continue with next request */
- goto next_request;
- case RLM_MODULE_HANDLED:
- /* if this was a replicated request, mark it as
- * finished first, because it was postponed
- */
- goto finished_request;
+ if (debug_flag == 0 && dont_fork == FALSE) {
+ pid_t pid = fork();
+
+ if (pid < 0) {
+ radlog(L_ERR, "Couldn't fork: %s", strerror(errno));
+ exit(1);
}
- } else {
/*
- * This is the initial incoming request which
- * we're processing.
- *
- * Some requests do NOT get cached, as they
- * CANNOT possibly have duplicates. Set the
- * magic option here.
- *
- * Status-Server messages are easy to generate,
- * so we toss them as soon as we see a reply.
- *
- * Accounting-Request packets WITHOUT an
- * Acct-Delay-Time attribute are NEVER
- * duplicated, as RFC 2866 Section 4.1 says that
- * the Acct-Delay-Time MUST be updated when the
- * packet is re-sent, which means the packet
- * changes, so it MUST have a new identifier and
- * Request Authenticator. */
- if ((request->packet->code == PW_STATUS_SERVER) ||
- ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
- (pairfind(request->packet->vps, PW_ACCT_DELAY_TIME) == NULL))) {
- request->options |= RAD_REQUEST_OPTION_DONT_CACHE;
+ * The parent exits, so the child can run in the background.
+ */
+ if (pid > 0) {
+ exit(0);
}
+#ifdef HAVE_SETSID
+ setsid();
+#endif
}
+#endif
- skip_decode:
/*
- * We should have a User-Name attribute now.
+ * Ensure that we're using the CORRECT pid after forking,
+ * NOT the one we started with.
*/
- if (request->username == NULL) {
- request->username = pairfind(request->packet->vps,
- PW_USER_NAME);
- }
+ radius_pid = getpid();
/*
- * FIXME: All this lowercase/nospace junk will be moved
- * into a module after module failover is fully in place
+ * Only write the PID file if we're running as a daemon.
*
- * See if we have to lower user/pass before processing
- */
- if(strcmp(mainconfig.do_lower_user, "before") == 0)
- rad_lowerpair(request, request->username);
- if(strcmp(mainconfig.do_lower_pass, "before") == 0)
- rad_lowerpair(request,
- pairfind(request->packet->vps, PW_PASSWORD));
-
- if(strcmp(mainconfig.do_nospace_user, "before") == 0)
- rad_rmspace_pair(request, request->username);
- if(strcmp(mainconfig.do_nospace_pass, "before") == 0)
- rad_rmspace_pair(request,
- pairfind(request->packet->vps, PW_PASSWORD));
-
- (*fun)(request);
-
- /*
- * If the request took too long to process, don't do
- * anything else.
- */
- if (request->options & RAD_REQUEST_OPTION_REJECTED) {
- finished = TRUE;
- goto postpone_request;
- }
-
- /*
- * Reprocess if we rejected last time
- */
- if ((fun == rad_authenticate) &&
- (request->reply->code == PW_AUTHENTICATION_REJECT)) {
- /* See if we have to lower user/pass after processing */
- if (strcmp(mainconfig.do_lower_user, "after") == 0) {
- rad_lowerpair(request, request->username);
- reprocess = 1;
- }
- if (strcmp(mainconfig.do_lower_pass, "after") == 0) {
- rad_lowerpair(request,
- pairfind(request->packet->vps, PW_PASSWORD));
- reprocess = 1;
- }
- if (strcmp(mainconfig.do_nospace_user, "after") == 0) {
- rad_rmspace_pair(request, request->username);
- reprocess = 1;
- }
- if (strcmp(mainconfig.do_nospace_pass, "after") == 0) {
- rad_rmspace_pair(request,
- pairfind(request->packet->vps, PW_PASSWORD));
- reprocess = 1;
- }
-
- /*
- * If we're re-processing the request, re-set it.
- */
- if (reprocess) {
- pairfree(&request->config_items);
- pairfree(&request->reply->vps);
- request->reply->code = 0;
- (*fun)(request);
- }
- }
-
- /*
- * Status-Server requests NEVER get proxied.
+ * And write it AFTER we've forked, so that we write the
+ * correct PID.
*/
- if (mainconfig.proxy_requests) {
- if ((request->packet->code != PW_STATUS_SERVER) &&
- ((request->options & RAD_REQUEST_OPTION_PROXIED) == 0)) {
- int rcode;
-
- /*
- * Try to proxy this request.
- */
- rcode = proxy_send(request);
-
- switch (rcode) {
- default:
- break;
-
- /*
- * There was an error trying to proxy the request.
- * Drop it on the floor.
- */
- case RLM_MODULE_FAIL:
- DEBUG2("Error trying to proxy request %d: Rejecting it", request->number);
- request_reject(request);
- goto finished_request;
- break;
-
- /*
- * The pre-proxy module has decided to reject
- * the request. Do so.
- */
- case RLM_MODULE_REJECT:
- DEBUG2("Request %d rejected in proxy_send.", request->number);
- request_reject(request);
- goto finished_request;
- break;
-
- /*
- * If the proxy code has handled the request,
- * then postpone more processing, until we get
- * the reply packet from the home server.
- */
- case RLM_MODULE_HANDLED:
- goto postpone_request;
- break;
- }
+ if (dont_fork == FALSE) {
+ FILE *fp;
+ fp = fopen(mainconfig.pid_file, "w");
+ if (fp != NULL) {
/*
- * Else rcode==RLM_MODULE_NOOP
- * and the proxy code didn't do anything, so
- * we continue handling the request here.
+ * FIXME: What about following symlinks,
+ * and having it over-write a normal file?
*/
+ fprintf(fp, "%d\n", (int) radius_pid);
+ fclose(fp);
+ } else {
+ radlog(L_ERR|L_CONS, "Failed creating PID file %s: %s\n",
+ mainconfig.pid_file, strerror(errno));
+ exit(1);
}
- } else if ((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
- (request->reply->code == 0)) {
- /*
- * We're not configured to reply to the packet,
- * and we're not proxying, so the DEFAULT behaviour
- * is to REJECT the user.
- */
- DEBUG2("There was no response configured: rejecting request %d", request->number);
- request_reject(request);
- goto finished_request;
}
/*
- * If we have a reply to send, copy the Proxy-State
- * attributes from the request to the tail of the reply,
- * and send the packet.
+ * If we're running as a daemon, close the default file
+ * descriptors, AFTER forking.
*/
- rad_assert(request->magic == REQUEST_MAGIC);
- if (request->reply->code != 0) {
- VALUE_PAIR *vp = NULL;
-
- /*
- * Perform RFC limitations on outgoing replies.
- */
- rfc_clean(request->reply);
-
- /*
- * Need to copy Proxy-State from request->packet->vps
- */
- vp = paircopy2(request->packet->vps, PW_PROXY_STATE);
- if (vp) pairadd(&(request->reply->vps), vp);
+ if (!debug_flag) {
+ int devnull;
- /*
- * If the request isn't an authentication reject, OR
- * it's a reject, but the reject_delay is zero, then
- * send it immediately.
- *
- * Otherwise, delay the authentication reject to shut
- * up DoS attacks.
- */
- if ((request->reply->code != PW_AUTHENTICATION_REJECT) ||
- (mainconfig.reject_delay == 0)) {
- /*
- * Send the response. IF it's a real request.
- */
- if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0) {
- rad_send(request->reply, request->packet,
- request->secret);
- }
- /*
- * Otherwise, it's a tunneled request.
- * Don't do anything.
- */
- } else {
- DEBUG2("Delaying request %d for %d seconds",
- request->number, mainconfig.reject_delay);
- request->options |= RAD_REQUEST_OPTION_DELAYED_REJECT;
+ devnull = open("/dev/null", O_RDWR);
+ if (devnull < 0) {
+ radlog(L_ERR|L_CONS, "Failed opening /dev/null: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+ dup2(devnull, STDIN_FILENO);
+ if (mainconfig.radlog_dest == RADLOG_STDOUT) {
+ mainconfig.radlog_fd = dup(STDOUT_FILENO);
+ }
+ dup2(devnull, STDOUT_FILENO);
+ if (mainconfig.radlog_dest == RADLOG_STDERR) {
+ mainconfig.radlog_fd = dup(STDERR_FILENO);
}
+ dup2(devnull, STDERR_FILENO);
+ close(devnull);
+
+ } else {
+ setlinebuf(stdout); /* unbuffered output */
}
/*
- * We're done processing the request, set the
- * request to be finished, clean up as necessary,
- * and forget about the request.
+ * It's called the thread pool, but it does a little
+ * more than that.
*/
+ radius_event_init(mainconfig.config, spawn_flag);
-finished_request:
-
/*
- * Don't decode the packet if it's an internal "fake"
- * request. Instead, just skip ahead to processing it.
+ * Now that we've set everything up, we can install the signal
+ * handlers. Before this, if we get any signal, we don't know
+ * what to do, so we might as well do the default, and die.
+ */
+#ifdef SIGPIPE
+ signal(SIGPIPE, SIG_IGN);
+#endif
+#ifdef HAVE_SIGACTION
+ act.sa_handler = sig_hup;
+ sigaction(SIGHUP, &act, NULL);
+ act.sa_handler = sig_fatal;
+ sigaction(SIGTERM, &act, NULL);
+#else
+#ifdef SIGHUP
+ signal(SIGHUP, sig_hup);
+#endif
+ signal(SIGTERM, sig_fatal);
+#endif
+ /*
+ * If we're debugging, then a CTRL-C will cause the
+ * server to die immediately. Use SIGTERM to shut down
+ * the server cleanly in that case.
*/
- if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) != 0) {
- goto skip_free;
+ if ((debug_memory == 1) || (debug_flag == 0)) {
+#ifdef HAVE_SIGACTION
+ act.sa_handler = sig_fatal;
+ sigaction(SIGINT, &act, NULL);
+ sigaction(SIGQUIT, &act, NULL);
+#else
+ signal(SIGINT, sig_fatal);
+#ifdef SIGQUIT
+ signal(SIGQUIT, sig_fatal);
+#endif
+#endif
}
/*
- * We're done handling the request. Free up the linked
- * lists of value pairs. This might take a long time,
- * so it's more efficient to do it in a child thread,
- * instead of in the main handler when it eventually
- * gets around to deleting the request.
- *
- * Also, no one should be using these items after the
- * request is finished, and the reply is sent. Cleaning
- * them up here ensures that they're not being used again.
- *
- * Hmm... cleaning them up in the child thread also seems
- * to make the server run more efficiently!
- *
- * If we've delayed the REJECT, then do NOT clean up the request,
- * as we haven't created the REJECT message yet.
+ * Everything seems to have loaded OK, exit gracefully.
*/
- if ((request->options & RAD_REQUEST_OPTION_DELAYED_REJECT) == 0) {
- if (request->packet) {
- pairfree(&request->packet->vps);
- request->username = NULL;
- request->password = NULL;
- }
-
- /*
- * If we've sent a reply to the NAS, then this request is
- * pretty much finished, and we have no more need for any
- * of the value-pair's in it, including the proxy stuff.
- */
- if (request->reply->code != 0) {
- pairfree(&request->reply->vps);
- }
+ if (check_config) {
+ DEBUG("Configuration appears OK.");
+ exit(0);
}
- pairfree(&request->config_items);
- if (request->proxy) {
- pairfree(&request->proxy->vps);
+ /*
+ * Process requests until HUP or exit.
+ */
+ while ((rcode = radius_event_process()) == 0x80) {
+ module_hup(cf_section_sub_find(mainconfig.config, "modules"));
}
- if (request->proxy_reply) {
- pairfree(&request->proxy_reply->vps);
+
+ DEBUG("Exiting...");
+
+ /*
+ * Ignore the TERM signal: we're
+ * about to die.
+ */
+ signal(SIGTERM, SIG_IGN);
+
+ /*
+ * Send a TERM signal to all
+ * associated processes
+ * (including us, which gets
+ * ignored.)
+ */
+#ifndef __MINGW32__
+ kill(-radius_pid, SIGTERM);
+#endif
+
+ /*
+ * We're exiting, so we can delete the PID
+ * file. (If it doesn't exist, we can ignore
+ * the error returned by unlink)
+ */
+ if (dont_fork == FALSE) {
+ unlink(mainconfig.pid_file);
}
-
- skip_free:
- DEBUG2("Finished request %d", request->number);
- finished = TRUE;
-
+
+ radius_event_free();
+
/*
- * Go to the next request, without marking
- * the current one as finished.
- *
- * Hmm... this may not be the brightest thing to do.
+ * Free the configuration items.
*/
-next_request:
- DEBUG2("Going to the next request");
-
-postpone_request:
-#ifdef HAVE_PTHREAD_H
+ free_mainconfig();
+
/*
- * We are finished with the child thread. The thread is detached,
- * so that when it exits, there's nothing more for the server
- * to do.
- *
- * If we're running with thread pools, then this frees up the
- * thread in the pool for another request.
+ * Detach any modules.
*/
- request->child_pid = NO_SUCH_CHILD_PID;
+ detach_modules();
+
+ xlat_free(); /* modules may have xlat's */
+
+ free(radius_dir);
+
+#ifdef WIN32
+ WSACleanup();
#endif
- request->finished = finished; /* do as the LAST thing before exiting */
- return 0;
+
+ return (rcode - 1);
}
FILE *output = status?stderr:stdout;
fprintf(output,
- "Usage: %s [-a acct_dir] [-d db_dir] [-l log_dir] [-i address] [-p port] [-AcfnsSvXxyz]\n", progname);
+ "Usage: %s [-d db_dir] [-l log_dir] [-i address] [-fsvXx]\n", progname);
fprintf(output, "Options:\n\n");
- fprintf(output, " -a acct_dir use accounting directory 'acct_dir'.\n");
- fprintf(output, " -A Log auth detail.\n");
- fprintf(output, " -d db_dir Use database directory 'db_dir'.\n");
+ fprintf(output, " -C Check configuration and exit.\n");
+ fprintf(output, " -d raddb_dir Configuration files are in \"raddbdir/*\".\n");
fprintf(output, " -f Run as a foreground process, not a daemon.\n");
fprintf(output, " -h Print this help message.\n");
- fprintf(output, " -i address Listen only in the given IP address.\n");
- fprintf(output, " -l log_dir Log messages to 'log_dir'. Special values are:\n");
- fprintf(output, " stdout == log all messages to standard output.\n");
- fprintf(output, " syslog == log all messages to the system logger.\n");
- fprintf(output, " -p port Bind to 'port', and not to the radius/udp, or 1646/udp.\n");
+ fprintf(output, " -i ipaddr Listen on ipaddr ONLY\n");
+ fprintf(output, " -p port Listen on port ONLY\n");
fprintf(output, " -s Do not spawn child processes to handle requests.\n");
- fprintf(output, " -S Log stripped names.\n");
fprintf(output, " -v Print server version information.\n");
- fprintf(output, " -X Turn on full debugging. (Means: -sfxxyz -l stdout)\n");
- fprintf(output, " -x Turn on partial debugging. (-xx gives more debugging).\n");
- fprintf(output, " -y Log authentication failures, with password.\n");
- fprintf(output, " -z Log authentication successes, with password.\n");
+ fprintf(output, " -X Turn on full debugging.\n");
+ fprintf(output, " -x Turn on additional debugging. (-xx gives more debugging).\n");
exit(status);
}
static void sig_fatal(int sig)
{
switch(sig) {
+ case SIGSEGV:
+ /*
+ * We can't really do anything
+ * intelligent here so just die
+ */
+ _exit(1);
+
case SIGTERM:
- do_exit = 1;
+ radius_signal_self(RADIUS_SIGNAL_SELF_TERM);
break;
+
+ case SIGINT:
+#ifdef SIGQUIT
+ case SIGQUIT:
+#endif
+ if (debug_memory) {
+ radius_signal_self(RADIUS_SIGNAL_SELF_TERM);
+ break;
+ }
+ /* FALL-THROUGH */
+
default:
- do_exit = 2;
+ radius_signal_self(RADIUS_SIGNAL_SELF_EXIT);
break;
}
}
-
+#ifdef SIGHUP
/*
* We got the hangup signal.
* Re-read the configuration files.
*/
-/*ARGSUSED*/
static void sig_hup(int sig)
{
sig = sig; /* -Wunused */
- reset_signal(SIGHUP, sig_hup);
-
- /*
- * Only do the reload if we're the main server, both
- * for processes, and for threads.
- */
- if (getpid() == radius_pid) {
- need_reload = TRUE;
- }
-#ifdef WITH_SNMP
- if (mainconfig.do_snmp) {
- rad_snmp.smux_failures = 0;
- rad_snmp.smux_event = SMUX_CONNECT;
- }
-#endif
-}
-
-
-/*
- * Process and reply to a server-status request.
- * Like rad_authenticate and rad_accounting this should
- * live in it's own file but it's so small we don't bother.
- */
-static int rad_status_server(REQUEST *request)
-{
- char reply_msg[64];
- time_t t;
- VALUE_PAIR *vp;
-
- /*
- * Reply with an ACK. We might want to add some more
- * interesting reply attributes, such as server uptime.
- */
- t = request->timestamp - start_time;
- sprintf(reply_msg, "FreeRADIUS up %d day%s, %02d:%02d",
- (int)(t / 86400), (t / 86400) == 1 ? "" : "s",
- (int)((t / 3600) % 24), (int)(t / 60) % 60);
- request->reply->code = PW_AUTHENTICATION_ACK;
- vp = pairmake("Reply-Message", reply_msg, T_OP_SET);
- pairadd(&request->reply->vps, vp); /* don't need to check if !vp */
+ reset_signal(SIGHUP, sig_hup);
- return 0;
+ radius_signal_self(RADIUS_SIGNAL_SELF_HUP);
}
+#endif