--- /dev/null
+/*\r
+ * proxy.c Proxy stuff.\r
+ *\r
+ * Version: $Id$\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ *\r
+ * Copyright 2000 The FreeRADIUS server project\r
+ * Copyright 2000 Miquel van Smoorenburg <miquels@cistron.nl>\r
+ * Copyright 2000 Chris Parker <cparker@starnetusa.com>\r
+ */\r
+\r
+static const char rcsid[] = "$Id$";\r
+\r
+#include "autoconf.h"\r
+#include "libradius.h"\r
+\r
+#include <sys/socket.h>\r
+\r
+#ifdef HAVE_NETINET_IN_H\r
+# include <netinet/in.h>\r
+#endif\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <ctype.h>\r
+#include <string.h>\r
+\r
+#include "radiusd.h"\r
+#include "rad_assert.h"\r
+#include "modules.h"\r
+\r
+\r
+/*\r
+ * Reprocess the request in possibly a child thread, only through\r
+ * a subsection of the post-proxy section of radiusd.conf.\r
+ */\r
+static int process_post_proxy_fail(REQUEST *request)\r
+{\r
+ VALUE_PAIR *vps;\r
+\r
+ /*\r
+ *\r
+ */\r
+\r
+\r
+ /*\r
+ * Hmm... this code is copied from below, which isn't good,\r
+ * and is similar to the code in rad_respond.\r
+ */\r
+ switch (request->packet->code) {\r
+ /*\r
+ * Accounting requests, etc. get dropped on the floor.\r
+ */\r
+ default:\r
+ case PW_ACCOUNTING_REQUEST:\r
+ case PW_STATUS_SERVER:\r
+ break;\r
+ \r
+ /*\r
+ * Authentication requests get their Proxy-State\r
+ * attributes copied over, and an otherwise blank\r
+ * reject message sent.\r
+ */\r
+ case PW_AUTHENTICATION_REQUEST:\r
+ request->reply->code = PW_AUTHENTICATION_REJECT;\r
+ \r
+ /*\r
+ * Perform RFC limitations on outgoing replies.\r
+ */\r
+ rfc_clean(request->reply);\r
+ \r
+ /*\r
+ * Need to copy Proxy-State from request->packet->vps\r
+ */\r
+ vps = paircopy2(request->packet->vps, PW_PROXY_STATE);\r
+ if (vps != NULL)\r
+ pairadd(&(request->reply->vps), vps);\r
+ break;\r
+ }\r
+ \r
+ /*\r
+ * If a reply exists, send it.\r
+ *\r
+ * But DON'T send a RADIUS packet for a fake request.\r
+ */\r
+ if ((request->reply->code != 0) &&\r
+ ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0)) {\r
+ /*\r
+ * If we're not delaying authentication rejects,\r
+ * then send the response immediately. Otherwise,\r
+ * mark the request as delayed, and do NOT send a\r
+ * response.\r
+ */\r
+ if (mainconfig.reject_delay == 0) {\r
+ rad_send(request->reply, request->packet,\r
+ request->secret);\r
+ } else {\r
+ request->options |= RAD_REQUEST_OPTION_DELAYED_REJECT;\r
+ }\r
+ }\r
+\r
+ return 0; /* ignored for now */\r
+}\r
+\r
+\r
+/*\r
+ * If we're told to proxess the request through the post-proxy "fail"\r
+ * subsection, do so.\r
+ *\r
+ * Called only from request_reject in util.c\r
+ */\r
+static void proxy_failed_home_server(REQUEST *request, request_fail_t reason)\r
+{\r
+ DICT_VALUE *val;\r
+\r
+ val = dict_valbyname(PW_POST_PROXY_TYPE, mainconfig.proxy_fail_type);\r
+ if (!val) {\r
+ DEBUG("ERROR: No such post-proxy type of \"%s\", cancelling post-proxy-failure call.", mainconfig.proxy_fail_type);\r
+ return;\r
+ }\r
+\r
+ request->options |= RAD_REQUEST_OPTION_REPROCESS;\r
+\r
+ thread_pool_addrequest(request, process_post_proxy_fail);\r
+}\r
+\r
+/*\r
+ * Perform any RFC specified cleaning of outgoing replies\r
+ */\r
+void rfc_clean(RADIUS_PACKET *packet)\r
+{\r
+ VALUE_PAIR *vps = NULL;\r
+\r
+ switch (packet->code) {\r
+ /*\r
+ * In the default case, we just move all of the\r
+ * attributes over.\r
+ */\r
+ default:\r
+ vps = packet->vps;\r
+ packet->vps = NULL;\r
+ break;\r
+\r
+ /*\r
+ * Accounting responses can only contain\r
+ * Proxy-State and VSA's. Note that we do NOT\r
+ * move the Proxy-State attributes over, as the\r
+ * Proxy-State attributes in this packet are NOT\r
+ * the right ones to use. The reply function\r
+ * takes care of copying those attributes from\r
+ * the original request, which ARE the right ones\r
+ * to use.\r
+ */\r
+ case PW_ACCOUNTING_RESPONSE:\r
+ pairmove2(&vps, &(packet->vps), PW_VENDOR_SPECIFIC);\r
+ break;\r
+\r
+ /*\r
+ * Authentication REJECT's can have only\r
+ * EAP-Message, Message-Authenticator\r
+ * Reply-Message and Proxy-State.\r
+ *\r
+ * We delete everything other than these.\r
+ * Proxy-State is added below, just before the\r
+ * reply is sent.\r
+ */\r
+ case PW_AUTHENTICATION_REJECT:\r
+ pairmove2(&vps, &(packet->vps), PW_EAP_MESSAGE);\r
+ pairmove2(&vps, &(packet->vps), PW_MESSAGE_AUTHENTICATOR);\r
+ pairmove2(&vps, &(packet->vps), PW_REPLY_MESSAGE);\r
+ pairmove2(&vps, &(packet->vps), PW_VENDOR_SPECIFIC);\r
+ break;\r
+ }\r
+\r
+ /*\r
+ * Move the newly cleaned attributes over.\r
+ */\r
+ pairfree(&packet->vps);\r
+ packet->vps = vps;\r
+\r
+ /*\r
+ * FIXME: Perform other, more generic sanity checks.\r
+ */\r
+}\r
+\r
+\r
+/*\r
+ * For debugging\r
+ */\r
+static LRAD_NAME_NUMBER request_fail_reason[] = {\r
+ { "no threads available to handle the request",\r
+ REQUEST_FAIL_NO_THREADS },\r
+\r
+ { "malformed RADIUS packet",\r
+ REQUEST_FAIL_DECODE},\r
+\r
+ { "pre-proxying failed",\r
+ REQUEST_FAIL_PROXY},\r
+\r
+ { "sending of the proxy packet failed",\r
+ REQUEST_FAIL_PROXY_SEND},\r
+\r
+ { "failure to be told how to respond",\r
+ REQUEST_FAIL_NO_RESPONSE},\r
+\r
+ { "no response from the home server",\r
+ REQUEST_FAIL_HOME_SERVER},\r
+ \r
+ { "no response from the home server after multiple tries",\r
+ REQUEST_FAIL_HOME_SERVER2},\r
+ \r
+ { "no response from the home server for a long period of time",\r
+ REQUEST_FAIL_HOME_SERVER3},\r
+\r
+ { "we were told to reject the request",\r
+ REQUEST_FAIL_NORMAL_REJECT},\r
+\r
+ { NULL, REQUEST_FAIL_UNKNOWN }\r
+};\r
+\r
+\r
+/*\r
+ * Reject a request, by sending a trivial reply packet.\r
+ */\r
+ void request_reject(REQUEST *request, request_fail_t reason)\r
+{\r
+ VALUE_PAIR *vps;\r
+\r
+ /*\r
+ * Already rejected. Don't do anything.\r
+ */\r
+ if (request->options & RAD_REQUEST_OPTION_REJECTED) {\r
+ return;\r
+ }\r
+\r
+ DEBUG2("Server rejecting request %d due to %s.",\r
+ request->number, lrad_int2str(request_fail_reason,\r
+ reason, "unknown"));\r
+\r
+ /*\r
+ * Remember that it was rejected.\r
+ */\r
+ request->options |= RAD_REQUEST_OPTION_REJECTED;\r
+\r
+ switch (reason) {\r
+ case REQUEST_FAIL_NO_THREADS:\r
+ DEBUG("WARNING: We recommend that you fix any TIMEOUT errors, or increase the value for \"max_servers\".");\r
+ break;\r
+\r
+ case REQUEST_FAIL_DECODE:\r
+ DEBUG("WARNING: Someone may be attacking your RADIUS server.");\r
+ break;\r
+\r
+ case REQUEST_FAIL_NO_RESPONSE:\r
+ DEBUG("WARNING: You did not configure the server to accept, or reject the user. Double-check Auth-Type.");\r
+ break;\r
+\r
+ /*\r
+ * If the home server goes down for some reason,\r
+ * we want to be able to know when. We do this\r
+ * by calling a sub-section of the post_proxy section,\r
+ * and processing any modules we find there.\r
+ *\r
+ * Note that this subsection CAN edit the response\r
+ * to the NAS.\r
+ */\r
+ case REQUEST_FAIL_HOME_SERVER: /* Hmm... we may want only one */\r
+ case REQUEST_FAIL_HOME_SERVER2:\r
+ case REQUEST_FAIL_HOME_SERVER3:\r
+ if (!mainconfig.proxy_fail_type) {\r
+ request->finished = TRUE;\r
+ break;\r
+ }\r
+\r
+ /*\r
+ * This function takes care of doing everything\r
+ * below...\r
+ */\r
+ proxy_failed_home_server(request, reason);\r
+ return; \r
+ break;\r
+\r
+ case REQUEST_FAIL_SERVER_TIMEOUT:\r
+ radlog(L_ERR, "TIMEOUT for request %d in module %s, component %s",\r
+ request->number,\r
+ request->module ? request->module : "<server core>",\r
+ request->component ? request->component : "<server core>");\r
+\r
+ /*\r
+ * Tell the server to stop processing the request\r
+ */\r
+ request->options |= RAD_REQUEST_OPTION_STOP_NOW;\r
+ break;\r
+\r
+ default: /* no additional messages, or things to do */\r
+ break;\r
+ }\r
+\r
+ switch (request->packet->code) {\r
+ /*\r
+ * Accounting requests, etc. get dropped on the floor.\r
+ */\r
+ default:\r
+ case PW_ACCOUNTING_REQUEST:\r
+ case PW_STATUS_SERVER:\r
+ break;\r
+\r
+ /*\r
+ * Authentication requests get their Proxy-State\r
+ * attributes copied over, and an otherwise blank\r
+ * reject message sent.\r
+ */\r
+ case PW_AUTHENTICATION_REQUEST:\r
+ request->reply->code = PW_AUTHENTICATION_REJECT;\r
+\r
+ /*\r
+ * Perform RFC limitations on outgoing replies.\r
+ */\r
+ rfc_clean(request->reply);\r
+\r
+ /*\r
+ * Need to copy Proxy-State from request->packet->vps\r
+ */\r
+ vps = paircopy2(request->packet->vps, PW_PROXY_STATE);\r
+ if (vps != NULL)\r
+ pairadd(&(request->reply->vps), vps);\r
+ break;\r
+ }\r
+\r
+ /*\r
+ * If a reply exists, send it.\r
+ *\r
+ * But DON'T send a RADIUS packet for a fake request.\r
+ */\r
+ if ((request->reply->code != 0) &&\r
+ ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0)) {\r
+ /*\r
+ * If we're not delaying authentication rejects,\r
+ * then send the response immediately. Otherwise,\r
+ * mark the request as delayed, and do NOT send a\r
+ * response.\r
+ */\r
+ if (mainconfig.reject_delay == 0) {\r
+ rad_send(request->reply, request->packet,\r
+ request->secret);\r
+ } else {\r
+ request->options |= RAD_REQUEST_OPTION_DELAYED_REJECT;\r
+ }\r
+ }\r
+}\r
+\r
+\r
+/*\r
+ * FIXME: The next two functions should all\r
+ * be in a module. But not until we have\r
+ * more control over module execution.\r
+ * -jcarneal\r
+ */\r
+\r
+/*\r
+ * Lowercase the string value of a pair.\r
+ */\r
+static int rad_lowerpair(REQUEST *request UNUSED, VALUE_PAIR *vp) {\r
+ if (vp == NULL) {\r
+ return -1;\r
+ }\r
+\r
+ rad_lowercase((char *)vp->strvalue);\r
+ DEBUG2("rad_lowerpair: %s now '%s'", vp->name, vp->strvalue);\r
+ return 0;\r
+}\r
+\r
+/*\r
+ * Remove spaces in a pair.\r
+ */\r
+static int rad_rmspace_pair(REQUEST *request UNUSED, VALUE_PAIR *vp) {\r
+ if (vp == NULL) {\r
+ return -1;\r
+ }\r
+\r
+ rad_rmspace((char *)vp->strvalue);\r
+ vp->length = strlen((char *)vp->strvalue);\r
+ DEBUG2("rad_rmspace_pair: %s now '%s'", vp->name, vp->strvalue);\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+/*\r
+ * Respond to a request packet.\r
+ *\r
+ * Maybe we reply, maybe we don't.\r
+ * Maybe we proxy the request to another server, or else maybe\r
+ * we replicate it to another server.\r
+ */\r
+int rad_respond(REQUEST *request, RAD_REQUEST_FUNP fun)\r
+{\r
+ RADIUS_PACKET *packet, *original;\r
+ const char *secret;\r
+ int finished = FALSE;\r
+ int reprocess = 0;\r
+ int decoderesult = 0;\r
+\r
+ rad_assert(request->magic == REQUEST_MAGIC);\r
+\r
+ /*\r
+ * Don't decode the packet if it's an internal "fake"\r
+ * request. Instead, just skip ahead to processing it.\r
+ */\r
+ if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) != 0) {\r
+ goto skip_decode;\r
+ }\r
+\r
+\r
+ /*\r
+ * Re-process the request.\r
+ */\r
+ if ((request->options & RAD_REQUEST_OPTION_REPROCESS) != 0) {\r
+ goto skip_decode;\r
+ }\r
+\r
+ /*\r
+ * Put the decoded packet into it's proper place.\r
+ */\r
+ if (request->proxy_reply != NULL) {\r
+ packet = request->proxy_reply;\r
+ secret = request->proxysecret;\r
+ original = request->proxy;\r
+ } else {\r
+ packet = request->packet;\r
+ secret = request->secret;\r
+ original = NULL;\r
+ }\r
+\r
+ /*\r
+ * Decode the packet, verifying it's signature,\r
+ * and parsing the attributes into structures.\r
+ *\r
+ * Note that we do this CPU-intensive work in\r
+ * a child thread, not the master. This helps to\r
+ * spread the load a little bit.\r
+ *\r
+ * Internal requests (ones that never go on the\r
+ * wire) have ->data==NULL (data is the wire\r
+ * format) and don't need to be "decoded"\r
+ */\r
+ if (packet->data) {\r
+ decoderesult = rad_decode(packet, original, secret);\r
+ switch (decoderesult) {\r
+ case -1:\r
+ radlog(L_ERR, "%s", librad_errstr);\r
+ request_reject(request, REQUEST_FAIL_DECODE);\r
+ goto finished_request;\r
+ break;\r
+ case -2:\r
+ radlog(L_ERR, "%s Dropping packet without response.", librad_errstr);\r
+ /* Since accounting packets get this set in\r
+ * request_reject but no response is sent...\r
+ */\r
+ request->options |= RAD_REQUEST_OPTION_REJECTED;\r
+ goto finished_request;\r
+ }\r
+ }\r
+\r
+ /*\r
+ * For proxy replies, remove non-allowed\r
+ * attributes from the list of VP's.\r
+ */\r
+ if (request->proxy) {\r
+ int rcode;\r
+ rcode = proxy_receive(request);\r
+ switch (rcode) {\r
+ default: /* Don't Do Anything */\r
+ break;\r
+ case RLM_MODULE_FAIL:\r
+ /* on error just continue with next request */\r
+ goto next_request;\r
+ case RLM_MODULE_HANDLED:\r
+ /* if this was a replicated request, mark it as\r
+ * finished first, because it was postponed\r
+ */\r
+ goto finished_request;\r
+ }\r
+\r
+ } else {\r
+ /*\r
+ * This is the initial incoming request which\r
+ * we're processing.\r
+ *\r
+ * Some requests do NOT get cached, as they\r
+ * CANNOT possibly have duplicates. Set the\r
+ * magic option here.\r
+ *\r
+ * Status-Server messages are easy to generate,\r
+ * so we toss them as soon as we see a reply.\r
+ *\r
+ * Accounting-Request packets WITHOUT an\r
+ * Acct-Delay-Time attribute are NEVER\r
+ * duplicated, as RFC 2866 Section 4.1 says that\r
+ * the Acct-Delay-Time MUST be updated when the\r
+ * packet is re-sent, which means the packet\r
+ * changes, so it MUST have a new identifier and\r
+ * Request Authenticator. */\r
+ if ((request->packet->code == PW_STATUS_SERVER) ||\r
+ ((request->packet->code == PW_ACCOUNTING_REQUEST) &&\r
+ (pairfind(request->packet->vps, PW_ACCT_DELAY_TIME) == NULL))) {\r
+ request->options |= RAD_REQUEST_OPTION_DONT_CACHE;\r
+ }\r
+ }\r
+\r
+ skip_decode:\r
+ /*\r
+ * We should have a User-Name attribute now.\r
+ */\r
+ if (request->username == NULL) {\r
+ request->username = pairfind(request->packet->vps,\r
+ PW_USER_NAME);\r
+ }\r
+\r
+ /*\r
+ * FIXME: All this lowercase/nospace junk will be moved\r
+ * into a module after module failover is fully in place\r
+ *\r
+ * See if we have to lower user/pass before processing\r
+ */\r
+ if(strcmp(mainconfig.do_lower_user, "before") == 0)\r
+ rad_lowerpair(request, request->username);\r
+ if(strcmp(mainconfig.do_lower_pass, "before") == 0)\r
+ rad_lowerpair(request,\r
+ pairfind(request->packet->vps, PW_PASSWORD));\r
+\r
+ if(strcmp(mainconfig.do_nospace_user, "before") == 0)\r
+ rad_rmspace_pair(request, request->username);\r
+ if(strcmp(mainconfig.do_nospace_pass, "before") == 0)\r
+ rad_rmspace_pair(request,\r
+ pairfind(request->packet->vps, PW_PASSWORD));\r
+\r
+ (*fun)(request);\r
+\r
+ /*\r
+ * If the request took too long to process, don't do\r
+ * anything else.\r
+ */\r
+ if (request->options & RAD_REQUEST_OPTION_REJECTED) {\r
+ finished = TRUE;\r
+ goto postpone_request;\r
+ }\r
+\r
+ /*\r
+ * Reprocess if we rejected last time\r
+ */\r
+ if ((fun == rad_authenticate) &&\r
+ (request->reply->code == PW_AUTHENTICATION_REJECT)) {\r
+ /* See if we have to lower user/pass after processing */\r
+ if (strcmp(mainconfig.do_lower_user, "after") == 0) {\r
+ rad_lowerpair(request, request->username);\r
+ reprocess = 1;\r
+ }\r
+ if (strcmp(mainconfig.do_lower_pass, "after") == 0) {\r
+ rad_lowerpair(request,\r
+ pairfind(request->packet->vps, PW_PASSWORD));\r
+ reprocess = 1;\r
+ }\r
+ if (strcmp(mainconfig.do_nospace_user, "after") == 0) {\r
+ rad_rmspace_pair(request, request->username);\r
+ reprocess = 1;\r
+ }\r
+ if (strcmp(mainconfig.do_nospace_pass, "after") == 0) {\r
+ rad_rmspace_pair(request,\r
+ pairfind(request->packet->vps, PW_PASSWORD));\r
+ reprocess = 1;\r
+ }\r
+\r
+ /*\r
+ * If we're re-processing the request, re-set it.\r
+ */\r
+ if (reprocess) {\r
+ pairfree(&request->config_items);\r
+ pairfree(&request->reply->vps);\r
+ request->reply->code = 0;\r
+ (*fun)(request);\r
+ \r
+ /*\r
+ * If the request took too long to process, don't do\r
+ * anything else.\r
+ */\r
+ if (request->options & RAD_REQUEST_OPTION_REJECTED) {\r
+ finished = TRUE;\r
+ goto postpone_request;\r
+ }\r
+ }\r
+ }\r
+\r
+ /*\r
+ * Status-Server requests NEVER get proxied.\r
+ */\r
+ if (mainconfig.proxy_requests) {\r
+ if ((request->packet->code != PW_STATUS_SERVER) &&\r
+ ((request->options & RAD_REQUEST_OPTION_PROXIED) == 0)) {\r
+ int rcode;\r
+\r
+ /*\r
+ * Try to proxy this request.\r
+ */\r
+ rcode = proxy_send(request);\r
+\r
+ switch (rcode) {\r
+ default:\r
+ break;\r
+\r
+ /*\r
+ * There was an error trying to proxy the request.\r
+ * Drop it on the floor.\r
+ */\r
+ case RLM_MODULE_FAIL:\r
+ DEBUG2("Error trying to proxy request %d: Rejecting it", request->number);\r
+ request_reject(request, REQUEST_FAIL_PROXY);\r
+ goto finished_request;\r
+ break;\r
+\r
+ /*\r
+ * The pre-proxy module has decided to reject\r
+ * the request. Do so.\r
+ */\r
+ case RLM_MODULE_REJECT:\r
+ DEBUG2("Request %d rejected in proxy_send.", request->number);\r
+ request_reject(request, REQUEST_FAIL_PROXY_SEND);\r
+ goto finished_request;\r
+ break;\r
+\r
+ /*\r
+ * If the proxy code has handled the request,\r
+ * then postpone more processing, until we get\r
+ * the reply packet from the home server.\r
+ */\r
+ case RLM_MODULE_HANDLED:\r
+ goto postpone_request;\r
+ break;\r
+ }\r
+\r
+ /*\r
+ * Else rcode==RLM_MODULE_NOOP\r
+ * and the proxy code didn't do anything, so\r
+ * we continue handling the request here.\r
+ */\r
+ }\r
+ } else if ((request->packet->code == PW_AUTHENTICATION_REQUEST) &&\r
+ (request->reply->code == 0)) {\r
+ /*\r
+ * We're not configured to reply to the packet,\r
+ * and we're not proxying, so the DEFAULT behaviour\r
+ * is to REJECT the user.\r
+ */\r
+ DEBUG2("There was no response configured: rejecting request %d", request->number);\r
+ request_reject(request, REQUEST_FAIL_NO_RESPONSE);\r
+ goto finished_request;\r
+ }\r
+\r
+ /*\r
+ * If we have a reply to send, copy the Proxy-State\r
+ * attributes from the request to the tail of the reply,\r
+ * and send the packet.\r
+ */\r
+ rad_assert(request->magic == REQUEST_MAGIC);\r
+ if (request->reply->code != 0) {\r
+ VALUE_PAIR *vp = NULL;\r
+\r
+ /*\r
+ * Perform RFC limitations on outgoing replies.\r
+ */\r
+ rfc_clean(request->reply);\r
+\r
+ /*\r
+ * Need to copy Proxy-State from request->packet->vps\r
+ */\r
+ vp = paircopy2(request->packet->vps, PW_PROXY_STATE);\r
+ if (vp) pairadd(&(request->reply->vps), vp);\r
+\r
+ /*\r
+ * If the request isn't an authentication reject, OR\r
+ * it's a reject, but the reject_delay is zero, then\r
+ * send it immediately.\r
+ *\r
+ * Otherwise, delay the authentication reject to shut\r
+ * up DoS attacks.\r
+ */\r
+ if ((request->reply->code != PW_AUTHENTICATION_REJECT) ||\r
+ (mainconfig.reject_delay == 0)) {\r
+ /*\r
+ * Send the response. IF it's a real request.\r
+ */\r
+ if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0) {\r
+ rad_send(request->reply, request->packet,\r
+ request->secret);\r
+ }\r
+ /*\r
+ * Otherwise, it's a tunneled request.\r
+ * Don't do anything.\r
+ */\r
+ } else {\r
+ DEBUG2("Delaying request %d for %d seconds",\r
+ request->number, mainconfig.reject_delay);\r
+ request->options |= RAD_REQUEST_OPTION_DELAYED_REJECT;\r
+ }\r
+ }\r
+\r
+ /*\r
+ * We're done processing the request, set the\r
+ * request to be finished, clean up as necessary,\r
+ * and forget about the request.\r
+ */\r
+\r
+finished_request:\r
+\r
+ /*\r
+ * Don't decode the packet if it's an internal "fake"\r
+ * request. Instead, just skip ahead to processing it.\r
+ */\r
+ if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) != 0) {\r
+ goto skip_free;\r
+ }\r
+\r
+ /*\r
+ * We're done handling the request. Free up the linked\r
+ * lists of value pairs. This might take a long time,\r
+ * so it's more efficient to do it in a child thread,\r
+ * instead of in the main handler when it eventually\r
+ * gets around to deleting the request.\r
+ *\r
+ * Also, no one should be using these items after the\r
+ * request is finished, and the reply is sent. Cleaning\r
+ * them up here ensures that they're not being used again.\r
+ *\r
+ * Hmm... cleaning them up in the child thread also seems\r
+ * to make the server run more efficiently!\r
+ *\r
+ * If we've delayed the REJECT, then do NOT clean up the request,\r
+ * as we haven't created the REJECT message yet.\r
+ */\r
+ if ((request->options & RAD_REQUEST_OPTION_DELAYED_REJECT) == 0) {\r
+ if (request->packet) {\r
+ pairfree(&request->packet->vps);\r
+ request->username = NULL;\r
+ request->password = NULL;\r
+ }\r
+\r
+ /*\r
+ * If we've sent a reply to the NAS, then this request is\r
+ * pretty much finished, and we have no more need for any\r
+ * of the value-pair's in it, including the proxy stuff.\r
+ */\r
+ if (request->reply->code != 0) {\r
+ pairfree(&request->reply->vps);\r
+ }\r
+ }\r
+\r
+ pairfree(&request->config_items);\r
+ if (request->proxy) {\r
+ pairfree(&request->proxy->vps);\r
+ }\r
+ if (request->proxy_reply) {\r
+ pairfree(&request->proxy_reply->vps);\r
+ }\r
+\r
+ skip_free:\r
+ DEBUG2("Finished request %d", request->number);\r
+ finished = TRUE;\r
+\r
+ /*\r
+ * Go to the next request, without marking\r
+ * the current one as finished.\r
+ *\r
+ * Hmm... this may not be the brightest thing to do.\r
+ */\r
+next_request:\r
+ DEBUG2("Going to the next request");\r
+\r
+postpone_request:\r
+#ifdef HAVE_PTHREAD_H\r
+ /*\r
+ * We are finished with the child thread. The thread is detached,\r
+ * so that when it exits, there's nothing more for the server\r
+ * to do.\r
+ *\r
+ * If we're running with thread pools, then this frees up the\r
+ * thread in the pool for another request.\r
+ */\r
+ request->child_pid = NO_SUCH_CHILD_PID;\r
+#endif\r
+ request->finished = finished; /* do as the LAST thing before exiting */\r
+ return 0;\r
+}\r
static const char rcsid[] =
"$Id$";
-static int rad_respond(REQUEST *request, RAD_REQUEST_FUNP fun);
-
#ifdef HAVE_PTHREAD_H
#define SEMAPHORE_LOCKED (0)
int thread_pool_addrequest(REQUEST *request, RAD_REQUEST_FUNP fun)
{
rad_respond(request, fun);
-
return 1;
}
#endif /* HAVE_PTHREAD_H */
-
-/***********************************************************************
- *
- * And now we have the code to respond to requests, finally
- * moved out of "radiusd.c".
- *
- ***********************************************************************/
-/*
- * FIXME: The next two functions should all
- * be in a module. But not until we have
- * more control over module execution.
- * -jcarneal
- */
-
-/*
- * Lowercase the string value of a pair.
- */
-static int rad_lowerpair(REQUEST *request UNUSED, VALUE_PAIR *vp) {
- if (vp == NULL) {
- return -1;
- }
-
- rad_lowercase((char *)vp->strvalue);
- DEBUG2("rad_lowerpair: %s now '%s'", vp->name, vp->strvalue);
- return 0;
-}
-
-/*
- * Remove spaces in a pair.
- */
-static int rad_rmspace_pair(REQUEST *request UNUSED, VALUE_PAIR *vp) {
- if (vp == NULL) {
- return -1;
- }
-
- rad_rmspace((char *)vp->strvalue);
- vp->length = strlen((char *)vp->strvalue);
- DEBUG2("rad_rmspace_pair: %s now '%s'", vp->name, vp->strvalue);
-
- return 0;
-}
-
-
-/*
- * 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.
- */
-static int rad_respond(REQUEST *request, RAD_REQUEST_FUNP fun)
-{
- RADIUS_PACKET *packet, *original;
- const char *secret;
- int finished = FALSE;
- int reprocess = 0;
- int decoderesult = 0;
-
- rad_assert(request->magic == REQUEST_MAGIC);
-
- /*
- * 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;
- }
-
- /*
- * 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;
- }
-
- /*
- * 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) {
- decoderesult = rad_decode(packet, original, secret);
- switch (decoderesult) {
- case -1:
- radlog(L_ERR, "%s", librad_errstr);
- request_reject(request, REQUEST_FAIL_DECODE);
- goto finished_request;
- break;
- case -2:
- radlog(L_ERR, "%s Dropping packet without response.", librad_errstr);
- /* Since accounting packets get this set in
- * request_reject but no response is sent...
- */
- request->options |= RAD_REQUEST_OPTION_REJECTED;
- goto finished_request;
- }
- }
-
- /*
- * For proxy replies, remove non-allowed
- * attributes from the list of VP's.
- */
- 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;
- }
-
- } 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;
- }
- }
-
- skip_decode:
- /*
- * We should have a User-Name attribute now.
- */
- if (request->username == NULL) {
- request->username = pairfind(request->packet->vps,
- PW_USER_NAME);
- }
-
- /*
- * 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(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);
-
- /*
- * 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;
- }
- }
- }
-
- /*
- * Status-Server requests NEVER get proxied.
- */
- 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, REQUEST_FAIL_PROXY);
- 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, REQUEST_FAIL_PROXY_SEND);
- 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, REQUEST_FAIL_NO_RESPONSE);
- 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.
- */
- 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 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;
- }
- }
-
- /*
- * We're done processing the request, set the
- * request to be finished, clean up as necessary,
- * and forget about the request.
- */
-
-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;
- }
-
- /*
- * 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.
- */
- 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;
-
- /*
- * Go to the next request, without marking
- * the current one as finished.
- *
- * Hmm... this may not be the brightest thing to do.
- */
-next_request:
- DEBUG2("Going to the next request");
-
-postpone_request:
-#ifdef HAVE_PTHREAD_H
- /*
- * 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.
- */
- request->child_pid = NO_SUCH_CHILD_PID;
-#endif
- request->finished = finished; /* do as the LAST thing before exiting */
- return 0;
-}
}
-/*
- * Perform any RFC specified cleaning of outgoing replies
- */
-void rfc_clean(RADIUS_PACKET *packet)
-{
- VALUE_PAIR *vps = NULL;
-
- switch (packet->code) {
- /*
- * In the default case, we just move all of the
- * attributes over.
- */
- default:
- vps = packet->vps;
- packet->vps = NULL;
- break;
-
- /*
- * Accounting responses can only contain
- * Proxy-State and VSA's. Note that we do NOT
- * move the Proxy-State attributes over, as the
- * Proxy-State attributes in this packet are NOT
- * the right ones to use. The reply function
- * takes care of copying those attributes from
- * the original request, which ARE the right ones
- * to use.
- */
- case PW_ACCOUNTING_RESPONSE:
- pairmove2(&vps, &(packet->vps), PW_VENDOR_SPECIFIC);
- break;
-
- /*
- * Authentication REJECT's can have only
- * EAP-Message, Message-Authenticator
- * Reply-Message and Proxy-State.
- *
- * We delete everything other than these.
- * Proxy-State is added below, just before the
- * reply is sent.
- */
- case PW_AUTHENTICATION_REJECT:
- pairmove2(&vps, &(packet->vps), PW_EAP_MESSAGE);
- pairmove2(&vps, &(packet->vps), PW_MESSAGE_AUTHENTICATOR);
- pairmove2(&vps, &(packet->vps), PW_REPLY_MESSAGE);
- pairmove2(&vps, &(packet->vps), PW_VENDOR_SPECIFIC);
- break;
- }
-
- /*
- * Move the newly cleaned attributes over.
- */
- pairfree(&packet->vps);
- packet->vps = vps;
-
- /*
- * FIXME: Perform other, more generic sanity checks.
- */
-}
-
-
-/*
- * For debugging
- */
-static LRAD_NAME_NUMBER request_fail_reason[] = {
- { "no threads available to handle the request",
- REQUEST_FAIL_NO_THREADS },
-
- { "malformed RADIUS packet",
- REQUEST_FAIL_DECODE},
-
- { "pre-proxying failed",
- REQUEST_FAIL_PROXY},
-
- { "sending of the proxy packet failed",
- REQUEST_FAIL_PROXY_SEND},
-
- { "failure to be told how to respond",
- REQUEST_FAIL_NO_RESPONSE},
-
- { "no response from the home server",
- REQUEST_FAIL_HOME_SERVER},
-
- { "no response from the home server after multiple tries",
- REQUEST_FAIL_HOME_SERVER2},
-
- { "no response from the home server for a long period of time",
- REQUEST_FAIL_HOME_SERVER3},
-
- { "we were told to reject the request",
- REQUEST_FAIL_NORMAL_REJECT},
-
- { NULL, REQUEST_FAIL_UNKNOWN }
-};
-
-
-/*
- * Reject a request, by sending a trivial reply packet.
- */
- void request_reject(REQUEST *request, request_fail_t reason)
-{
- VALUE_PAIR *vps;
-
- /*
- * Already rejected. Don't do anything.
- */
- if (request->options & RAD_REQUEST_OPTION_REJECTED) {
- return;
- }
-
- DEBUG2("Server rejecting request %d due to %s.",
- request->number, lrad_int2str(request_fail_reason,
- reason, "unknown"));
- switch (request->packet->code) {
- /*
- * Accounting requests, etc. get dropped on the floor.
- */
- default:
- case PW_ACCOUNTING_REQUEST:
- case PW_STATUS_SERVER:
- break;
-
- /*
- * Authentication requests get their Proxy-State
- * attributes copied over, and an otherwise blank
- * reject message sent.
- */
- case PW_AUTHENTICATION_REQUEST:
- request->reply->code = PW_AUTHENTICATION_REJECT;
-
- /*
- * Perform RFC limitations on outgoing replies.
- */
- rfc_clean(request->reply);
-
- /*
- * Need to copy Proxy-State from request->packet->vps
- */
- vps = paircopy2(request->packet->vps, PW_PROXY_STATE);
- if (vps != NULL)
- pairadd(&(request->reply->vps), vps);
- break;
- }
-
- /*
- * If a reply exists, send it.
- *
- * But DON'T send a RADIUS packet for a fake request.
- */
- if ((request->reply->code != 0) &&
- ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0)) {
- /*
- * If we're not delaying authentication rejects,
- * then send the response immediately. Otherwise,
- * mark the request as delayed, and do NOT send a
- * response.
- */
- if (mainconfig.reject_delay == 0) {
- rad_send(request->reply, request->packet,
- request->secret);
- } else {
- request->options |= RAD_REQUEST_OPTION_DELAYED_REJECT;
- }
- }
-
- /*
- * Remember that it was rejected.
- */
- request->options |= RAD_REQUEST_OPTION_REJECTED;
-
- switch (reason) {
- case REQUEST_FAIL_NO_THREADS:
- DEBUG("WARNING: We recommend that you fix any TIMEOUT errors, or increase the value for \"max_servers\".");
- break;
-
- case REQUEST_FAIL_DECODE:
- DEBUG("WARNING: Someone may be attacking your RADIUS server.");
- break;
-
- case REQUEST_FAIL_NO_RESPONSE:
- DEBUG("WARNING: You did not configure the server to accept, or reject the user. Double-check Auth-Type.");
- break;
-
- case REQUEST_FAIL_HOME_SERVER: /* Hmm... we may want only one */
- case REQUEST_FAIL_HOME_SERVER2:
- case REQUEST_FAIL_HOME_SERVER3:
- break;
-
- case REQUEST_FAIL_SERVER_TIMEOUT:
- radlog(L_ERR, "TIMEOUT for request %d in module %s, component %s",
- request->number,
- request->module ? request->module : "<server core>",
- request->component ? request->component : "<server core>");
-
- /*
- * Tell the server to stop processing the request
- */
- request->options |= RAD_REQUEST_OPTION_STOP_NOW;
- break;
-
- default: /* no additional messages, or things to do */
- break;
- }
-}