X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fmain%2Fradiusd.c;h=20ad58e57c019968ae3e359115d97330c083d681;hb=1147ab4f2843e6deee5fd07f2b716de7cbb1675b;hp=7b6909fcf3baf0c4ec49f9f768caf14c7d0aaa12;hpb=93233da20a83ecb4e2815466ce3fa35a6dd2e71d;p=freeradius.git diff --git a/src/main/radiusd.c b/src/main/radiusd.c index 7b6909f..20ad58e 100644 --- a/src/main/radiusd.c +++ b/src/main/radiusd.c @@ -15,9 +15,9 @@ * * 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 * Copyright 2000 Alan DeKok * Copyright 2000 Alan Curry @@ -25,42 +25,24 @@ * Copyright 2000 Chad Miller */ -/* don't look here for the version, run radiusd -v or look in version.c */ -static const char rcsid[] = -"$Id$"; +#include +RCSID("$Id$") -#include "autoconf.h" -#include "libradius.h" +#include +#include +#include #include -#ifdef HAVE_NETINET_IN_H -# include -#endif - -#include -#include #include #include -#ifdef HAVE_UNISTD_H -# include -#endif - #include #ifdef HAVE_GETOPT_H # include #endif -#ifdef HAVE_SYS_SELECT_H -# include -#endif - -#ifdef HAVE_SYSLOG_H -# include -#endif - #ifdef HAVE_SYS_WAIT_H # include #endif @@ -71,13 +53,6 @@ static const char rcsid[] = # 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" - /* * Global variables. */ @@ -85,28 +60,20 @@ const char *progname = NULL; 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 int got_child = FALSE; -static time_t time_now; -static pid_t radius_pid; +pid_t radius_pid; + +static int debug_memory = 0; /* * Configuration items. */ -static int dont_fork = FALSE; -static int needs_child_cleanup = 0; -static time_t start_time = 0; -static int spawn_flag = TRUE; -static int do_exit = 0; /* * Static functions. @@ -114,708 +81,44 @@ static int do_exit = 0; static void usage(int); static void sig_fatal (int); +#ifdef SIGHUP static void sig_hup (int); -#ifdef HAVE_PTHREAD_H -static void sig_cleanup(int); -#endif - -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); #endif - if ((progname = strrchr(argv[0], '/')) == NULL) + if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL) progname = argv[0]; 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); @@ -824,32 +127,36 @@ int main(int argc, char *argv[]) * Ensure that the configuration is initialized. */ memset(&mainconfig, 0, sizeof(mainconfig)); + mainconfig.myip.af = AF_UNSPEC; + mainconfig.port = -1; + mainconfig.name = "radiusd"; + #ifdef HAVE_SIGACTION memset(&act, 0, sizeof(act)); act.sa_flags = 0 ; sigemptyset( &act.sa_mask ) ; #endif + /* + * Don't put output anywhere until we get told a little + * more. + */ + mainconfig.radlog_dest = RADLOG_NULL; + mainconfig.radlog_fd = -1; + mainconfig.log_file = NULL; + /* Process the options. */ - while ((argval = getopt(argc, argv, "Aa:bcd:fg:hi:l:p:sSvxXyz")) != EOF) { + while ((argval = getopt(argc, argv, "Cd:fhi:l:mn:p:stvxX")) != 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 */ + case 'C': + check_config = TRUE; + spawn_flag = FALSE; + dont_fork = TRUE; break; case 'd': - if (radius_dir) xfree(radius_dir); + if (radius_dir) free(radius_dir); radius_dir = strdup(optarg); break; @@ -861,1139 +168,320 @@ int main(int argc, char *argv[]) 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(); - } - - rad_exec_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 - } - -#ifdef HAVE_PTHREAD_H - /* - * If we have pthreads, then the child threads block - * SIGCHLD, and the main server thread catches it. - * - * That way, the SIGCHLD handler can grab the exit status, - * and save it for the child thread. - * - * If we don't have pthreads, then each child process - * will do a waitpid(), and we ignore SIGCHLD. - * - * Once we have multiple child processes to handle - * requests, and shared memory, then we've got to - * re-enable SIGCHLD catching. - */ -#ifdef HAVE_SIGACTION - act.sa_handler = sig_cleanup; - sigaction(SIGCHLD, &act, NULL); -#else - signal(SIGCHLD, sig_cleanup); -#endif -#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; + if (strcmp(optarg, "stdout") == 0) { + goto do_stdout; } - 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 - + mainconfig.log_file = strdup(optarg); + mainconfig.radlog_dest = RADLOG_FILES; + mainconfig.radlog_fd = open(mainconfig.log_file, + O_WRONLY | O_APPEND | O_CREAT, 0640); + if (mainconfig.radlog_fd < 0) { + fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", mainconfig.log_file, strerror(errno)); + exit(1); + } + break; - } /* loop forever */ -} + 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; + case 'n': + mainconfig.name = optarg; + break; -/* - * FIXME: The next two functions should all - * be in a module. But not until we have - * more control over module execution. - * -jcarneal - */ + case 'm': + debug_memory = 1; + break; -/* - * Lowercase the string value of a pair. - */ -static int rad_lowerpair(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_lowercase((char *)vp->strvalue); - DEBUG2("rad_lowerpair: %s now '%s'", vp->name, vp->strvalue); - return 0; -} + case 's': /* Single process mode */ + spawn_flag = FALSE; + dont_fork = TRUE; + break; -/* - * Remove spaces in a pair. - */ -static int rad_rmspace_pair(REQUEST *request UNUSED, VALUE_PAIR *vp) { - if (vp == NULL) { - return -1; - } + case 't': /* no child threads */ + spawn_flag = FALSE; + break; - rad_rmspace((char *)vp->strvalue); - vp->length = strlen((char *)vp->strvalue); - DEBUG2("rad_rmspace_pair: %s now '%s'", vp->name, vp->strvalue); + case 'v': + version(); + break; - return 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; + do_stdout: + mainconfig.radlog_dest = RADLOG_STDOUT; + mainconfig.radlog_fd = STDOUT_FILENO; + fr_log_fp = stdout; + 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': + debug_flag++; + break; - rad_assert(request->magic == REQUEST_MAGIC); + default: + usage(1); + 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; + if (flag && (flag != 0x03)) { + fprintf(stderr, "radiusd: The options -i and -p cannot be used individually.\n"); + exit(1); } - /* - * 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 (debug_flag) { + radlog(L_INFO, "%s", radiusd_version); + radlog(L_INFO, "Copyright (C) 1999-2009 The FreeRADIUS server project and contributors.\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 v2.\n"); + fflush(NULL); } - /* - * 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; + /* 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 (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 - * - * See if we have to lower user/pass before processing + * If we're running as a daemon, close the default file + * descriptors, AFTER forking. */ - 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 (!debug_flag) { + int devnull; - 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)); + 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); + setlinebuf(stdout); + } + dup2(devnull, STDOUT_FILENO); + if (mainconfig.radlog_dest == RADLOG_STDERR) { + mainconfig.radlog_fd = dup(STDERR_FILENO); + setlinebuf(stdout); + } + dup2(devnull, STDERR_FILENO); + close(devnull); - (*fun)(request); + } else { + setlinebuf(stdout); /* unbuffered output */ + } /* - * If the request took too long to process, don't do - * anything else. + * Initialize the event pool, including threads. */ - if (request->options & RAD_REQUEST_OPTION_REJECTED) { - finished = TRUE; - goto postpone_request; - } + radius_event_init(mainconfig.config, spawn_flag); /* - * Reprocess if we rejected last time + * 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. */ - 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); - } - } - +#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 /* - * Status-Server requests NEVER get proxied. + * 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 (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; - } - - /* - * Else rcode==RLM_MODULE_NOOP - * and the proxy code didn't do anything, so - * we continue handling the request here. - */ - } - } 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 ((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 } /* - * 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. + * Everything seems to have loaded OK, exit gracefully. */ - rad_assert(request->magic == REQUEST_MAGIC); - if (request->reply->code != 0) { - VALUE_PAIR *vp = NULL; + if (check_config) { + DEBUG("Configuration appears to be OK."); + exit(0); + } - /* - * Perform RFC limitations on outgoing replies. - */ - rfc_clean(request->reply); +#ifdef WITH_STATS + radius_stats_init(0); +#endif - /* - * Need to copy Proxy-State from request->packet->vps - */ - vp = paircopy2(request->packet->vps, PW_PROXY_STATE); - if (vp) pairadd(&(request->reply->vps), vp); + /* + * 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; - /* - * 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); - } + fp = fopen(mainconfig.pid_file, "w"); + if (fp != NULL) { /* - * Otherwise, it's a tunneled request. - * Don't do anything. + * FIXME: What about following symlinks, + * and having it over-write a normal file? */ + fprintf(fp, "%d\n", (int) radius_pid); + fclose(fp); } else { - DEBUG2("Delaying request %d for %d seconds", - request->number, mainconfig.reject_delay); - request->options |= RAD_REQUEST_OPTION_DELAYED_REJECT; + radlog(L_ERR|L_CONS, "Failed creating PID file %s: %s\n", + mainconfig.pid_file, strerror(errno)); + exit(1); } } /* - * We're done processing the request, set the - * request to be finished, clean up as necessary, - * and forget about the request. + * Process requests until HUP or exit. */ - -finished_request: - - /* - * 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_free; + while ((rcode = radius_event_process()) == 0x80) { + radius_stats_init(1); + hup_mainconfig(); } - + + radlog(L_INFO, "Exiting normally."); + /* - * 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. + * Ignore the TERM signal: we're + * about to die. */ - 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); - } - } - - pairfree(&request->config_items); - if (request->proxy) { - pairfree(&request->proxy->vps); - } - if (request->proxy_reply) { - pairfree(&request->proxy_reply->vps); - } - - skip_free: - DEBUG2("Finished request %d", request->number); - finished = TRUE; - + signal(SIGTERM, SIG_IGN); + /* - * Go to the next request, without marking - * the current one as finished. - * - * Hmm... this may not be the brightest thing to do. + * Send a TERM signal to all + * associated processes + * (including us, which gets + * ignored.) */ -next_request: - DEBUG2("Going to the next request"); - -postpone_request: -#ifdef HAVE_PTHREAD_H +#ifndef __MINGW32__ + if (spawn_flag) kill(-radius_pid, SIGTERM); +#endif + /* - * 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. + * We're exiting, so we can delete the PID + * file. (If it doesn't exist, we can ignore + * the error returned by unlink) */ - request->child_pid = NO_SUCH_CHILD_PID; -#endif - request->finished = finished; /* do as the LAST thing before exiting */ - return 0; -} - - -#ifdef HAVE_PTHREAD_H -static void sig_cleanup(int sig) -{ - int status; - pid_t pid; - - sig = sig; /* -Wunused */ - - got_child = FALSE; - - needs_child_cleanup = 0; /* reset the queued cleanup number */ - + if (dont_fork == FALSE) { + unlink(mainconfig.pid_file); + } + + radius_event_free(); + /* - * Reset the signal handler, if required. + * Free the configuration items. */ - reset_signal(SIGCHLD, sig_cleanup); - + free_mainconfig(); + /* - * Wait for the child, without hanging. + * Detach any modules. */ - for (;;) { - pid = waitpid((pid_t)-1, &status, WNOHANG); - if (pid <= 0) - return; + detach_modules(); + + xlat_free(); /* modules may have xlat's */ - /* - * Check to see if the child did a bad thing. - * If so, kill ALL processes in the current - * process group, to prevent further attacks. - */ - if (debug_flag && (WIFSIGNALED(status))) { - radlog(L_ERR|L_CONS, "MASTER: Child PID %d failed to catch " - "signal %d: killing all active servers.\n", - pid, WTERMSIG(status)); - kill(-radius_pid, SIGTERM); - exit(1); - } + free(radius_dir); + +#ifdef WIN32 + WSACleanup(); +#endif - /* - * If we have pthreads, then the only children - * are from Exec-Program. We don't care about them, - * so once we've grabbed their PID's, we're done. - */ -#ifdef HAVE_PTHREAD_H - rad_savepid(pid, status); -#endif /* !defined HAVE_PTHREAD_H */ - } + return (rcode - 1); } -#endif /* HAVE_PTHREAD_H */ + /* * Display the syntax for starting this program. */ -static void usage(int status) +static void NEVER_RETURNS usage(int status) { 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] [-n name] [-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, " -n name Read raddb/name.conf instead of raddb/radiusd.conf\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); } @@ -2005,64 +493,35 @@ static void sig_fatal(int sig) { switch(sig) { 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; - break; + _exit(sig); } } - +#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