-/*\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
+/*
+ * proxy.c Proxy stuff.
+ *
+ * Version: $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Copyright 2000 The FreeRADIUS server project
+ * Copyright 2000 Miquel van Smoorenburg <miquels@cistron.nl>
+ * Copyright 2000 Chris Parker <cparker@starnetusa.com>
+ */
+
+static const char rcsid[] = "$Id$";
+
+#include "autoconf.h"
+#include "libradius.h"
+
+#include <sys/socket.h>
+
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "radiusd.h"
+#include "rad_assert.h"
+#include "modules.h"
+
+
+/*
+ * Reprocess the request in possibly a child thread, only through
+ * a subsection of the post-proxy section of radiusd.conf.
+ */
+static int process_post_proxy_fail(REQUEST *request)
+{
+ VALUE_PAIR *vps;
+
+ /*
+ *
+ */
+
+
+ /*
+ * Hmm... this code is copied from below, which isn't good,
+ * and is similar to the code in rad_respond.
+ */
+ 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;
+ }
+ }
+
+ return 0; /* ignored for now */
+}
+
+
+/*
+ * If we're told to proxess the request through the post-proxy "fail"
+ * subsection, do so.
+ *
+ * Called only from request_reject in util.c
+ */
+static void proxy_failed_home_server(REQUEST *request, request_fail_t reason)
+{
+ DICT_VALUE *val;
+
+ val = dict_valbyname(PW_POST_PROXY_TYPE, mainconfig.proxy_fail_type);
+ if (!val) {
+ DEBUG("ERROR: No such post-proxy type of \"%s\", cancelling post-proxy-failure call.", mainconfig.proxy_fail_type);
+ return;
+ }
+
+ request->options |= RAD_REQUEST_OPTION_REPROCESS;
+
+ thread_pool_addrequest(request, process_post_proxy_fail);
+}
+
+/*
+ * 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"));
+
+ /*
+ * 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;
+
+ /*
+ * If the home server goes down for some reason,
+ * we want to be able to know when. We do this
+ * by calling a sub-section of the post_proxy section,
+ * and processing any modules we find there.
+ *
+ * Note that this subsection CAN edit the response
+ * to the NAS.
+ */
+ case REQUEST_FAIL_HOME_SERVER: /* Hmm... we may want only one */
+ case REQUEST_FAIL_HOME_SERVER2:
+ case REQUEST_FAIL_HOME_SERVER3:
+ if (!mainconfig.proxy_fail_type) {
+ request->finished = TRUE;
+ break;
+ }
+
+ /*
+ * This function takes care of doing everything
+ * below...
+ */
+ proxy_failed_home_server(request, reason);
+ return;
+ 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;
+ }
+
+ 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;
+ }
+ }
+}
+
+
+/*
+ * 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.
+ */
+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;
+ }
+
+
+ /*
+ * Re-process the request.
+ */
+ if ((request->options & RAD_REQUEST_OPTION_REPROCESS) != 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;
+}