6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Copyright 2000 The FreeRADIUS server project
21 * Copyright 2000 Miquel van Smoorenburg <miquels@cistron.nl>
22 * Copyright 2000 Chris Parker <cparker@starnetusa.com>
25 static const char rcsid[] = "$Id$";
28 #include "libradius.h"
30 #include <sys/socket.h>
32 #ifdef HAVE_NETINET_IN_H
33 # include <netinet/in.h>
42 #include "rad_assert.h"
44 #include "request_list.h"
47 * We received a response from a remote radius server.
48 * Find the original request, then return.
49 * Returns: 1 replication don't reply
51 * -1 error don't reply
53 int proxy_receive(REQUEST *request)
56 int post_proxy_type = 0;
60 * Delete any reply we had accumulated until now.
62 pairfree(&request->reply->vps);
65 * Run the packet through the post-proxy stage,
66 * BEFORE playing games with the attributes.
68 vp = pairfind(request->config_items, PW_POST_PROXY_TYPE);
70 DEBUG2(" Found Post-Proxy-Type %s", vp->strvalue);
71 post_proxy_type = vp->lvalue;
73 rcode = module_post_proxy(post_proxy_type, request);
76 * Delete the Proxy-State Attributes from the reply.
77 * These include Proxy-State attributes from us and
80 pairdelete(&request->proxy_reply->vps, PW_PROXY_STATE);
83 * Add the attributes left in the proxy reply to
86 pairadd(&request->reply->vps, request->proxy_reply->vps);
87 request->proxy_reply->vps = NULL;
90 * Free proxy request pairs.
92 pairfree(&request->proxy->vps);
98 * Add a proxy-pair to the end of the request.
100 static void proxy_addinfo(REQUEST *request)
102 VALUE_PAIR *proxy_pair;
104 proxy_pair = paircreate(PW_PROXY_STATE, PW_TYPE_STRING);
105 if (proxy_pair == NULL) {
106 radlog(L_ERR|L_CONS, "no memory");
109 sprintf((char *)proxy_pair->strvalue, "%d", request->packet->id);
110 proxy_pair->length = strlen((char *)proxy_pair->strvalue);
112 pairadd(&request->proxy->vps, proxy_pair);
117 * Like realm find, but does load balancing, and we don't
118 * wake up any sleeping realms. Someone should already have
121 * It also does NOT do fail-over to default if the realms are dead,
122 * as that decision has already been made.
124 static REALM *proxy_realm_ldb(REQUEST *request, const char *realm_name,
134 for (cl = mainconfig.realms; cl; cl = cl->next) {
136 * Wake up any sleeping realm.
138 * Note that the 'realm find' function will only
139 * wake up the FIRST realm which matches. We've
140 * got to wake up ALL of the matching realms.
142 if (cl->wakeup <= request->timestamp) {
145 if (cl->acct_wakeup <= request->timestamp) {
146 cl->acct_active = TRUE;
150 * Asked for auth/acct, and the auth/acct server
151 * is not active. Skip it.
153 if ((!accounting && !cl->active) ||
154 (accounting && !cl->acct_active)) {
159 * The realm name doesn't match, skip it.
161 if (strcasecmp(cl->realm, realm_name) != 0) {
166 * Fail-over, pick the first one that matches.
168 if ((count == 0) && /* if size > 0, we have round-robin */
174 * We're doing load-balancing. Pick a random
175 * number, which will be used to determine which
176 * home server is chosen.
185 * Keep track of how many load balancing servers
186 * we've gone through.
191 * See the "camel book" for why this works.
193 * If (rand(0..n) < 1), pick the current realm.
194 * We add a scale factor of 65536, to avoid
197 if ((count * (lrad_rand() & 0xffff)) < (uint32_t) 0x10000) {
200 } /* loop over the realms */
203 * All are dead, see if we have to wake
205 if (!redone && !lb && mainconfig.wake_all_if_all_dead) {
206 for (cl = mainconfig.realms; cl; cl = cl->next) {
207 if(strcasecmp(cl->realm,realm_name) == 0) {
208 if (!accounting && !cl->active) {
211 else if (accounting &&
213 cl->acct_active = TRUE;
222 * Return the load-balanced realm.
228 * Relay the request to a remote server.
231 * RLM_MODULE_FAIL: we don't reply, caller returns without replying
232 * RLM_MODULE_NOOP: caller falls through to normal processing
233 * RLM_MODULE_HANDLED : we reply, caller returns without replying
235 int proxy_send(REQUEST *request)
238 int pre_proxy_type = 0;
239 VALUE_PAIR *realmpair;
240 VALUE_PAIR *strippedname;
241 VALUE_PAIR *delaypair;
247 * Not authentication or accounting. Stop it.
249 if ((request->packet->code != PW_AUTHENTICATION_REQUEST) &&
250 (request->packet->code != PW_ACCOUNTING_REQUEST)) {
251 DEBUG2(" ERROR: Cannot proxy packets of type %d",
252 request->packet->code);
253 return RLM_MODULE_FAIL;
257 * The timestamp is used below to figure the
258 * next_try. The request needs to "hang around" until
259 * either the other server sends a reply or the retry
260 * count has been exceeded. Until then, it should not
261 * be eligible for the time-based cleanup. --Pac. */
263 realmpair = pairfind(request->config_items, PW_PROXY_TO_REALM);
266 * Not proxying, so we can exit from the proxy
269 return RLM_MODULE_NOOP;
273 * If the server has already decided to reject the request,
274 * then don't try to proxy it.
276 if (request->reply->code == PW_AUTHENTICATION_REJECT) {
277 DEBUG2("Cancelling proxy as request was already rejected");
278 return RLM_MODULE_REJECT;
280 if (((vp = pairfind(request->config_items, PW_AUTH_TYPE)) != NULL) &&
281 (vp->lvalue == PW_AUTHTYPE_REJECT)) {
282 DEBUG2("Cancelling proxy as request was already rejected");
283 return RLM_MODULE_REJECT;
286 * Length == 0 means it exists, but there's no realm.
289 if (realmpair->length == 0) {
290 return RLM_MODULE_NOOP;
293 realmname = (char *)realmpair->strvalue;
296 * Look for the realm, using the load balancing
297 * version of realm find.
299 realm = proxy_realm_ldb(request, realmname,
300 (request->packet->code == PW_ACCOUNTING_REQUEST));
302 DEBUG2(" ERROR: Failed to find live home server for realm %s",
304 return RLM_MODULE_FAIL;
308 * Remember that we sent the request to a Realm.
310 pairadd(&request->packet->vps,
311 pairmake("Realm", realm->realm, T_OP_EQ));
314 * Access-Request: look for LOCAL realm.
315 * Accounting-Request: look for LOCAL realm.
317 if (((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
318 (realm->ipaddr == htonl(INADDR_NONE))) ||
319 ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
320 (realm->acct_ipaddr == htonl(INADDR_NONE)))) {
321 DEBUG2(" WARNING: Cancelling proxy to Realm %s, as the realm is local.",
323 return RLM_MODULE_NOOP;
327 * Allocate the proxy packet, only if it wasn't already
328 * allocated by a module. This check is mainly to support
329 * the proxying of EAP-TTLS and EAP-PEAP tunneled requests.
331 * In those cases, the EAP module creates a "fake"
332 * request, and recursively passes it through the
333 * authentication stage of the server. The module then
334 * checks if the request was supposed to be proxied, and
335 * if so, creates a proxy packet from the TUNNELED request,
336 * and not from the EAP request outside of the tunnel.
338 * The proxy then works like normal, except that the response
339 * packet is "eaten" by the EAP module, and encapsulated into
342 if (!request->proxy) {
344 * Now build a new RADIUS_PACKET.
346 * FIXME: it could be that the id wraps around
347 * too fast if we have a lot of requests, it
348 * might be better to keep a seperate ID value
351 * OTOH the remote radius server should be smart
352 * enough to compare _both_ ID and vector.
355 if ((request->proxy = rad_alloc(TRUE)) == NULL) {
356 radlog(L_ERR|L_CONS, "no memory");
361 * We now massage the attributes to be proxied...
365 * Copy the request, then look up name and
366 * plain-text password in the copy.
368 * Note that the User-Name attribute is the
369 * *original* as sent over by the client. The
370 * Stripped-User-Name attribute is the one hacked
371 * through the 'hints' file.
373 request->proxy->vps = paircopy(request->packet->vps);
377 * Strip the name, if told to.
379 * Doing it here catches the case of proxied tunneled
382 if (realm->striprealm == TRUE &&
383 (strippedname = pairfind(request->proxy->vps, PW_STRIPPED_USER_NAME)) != NULL) {
385 * If there's a Stripped-User-Name attribute in
386 * the request, then use THAT as the User-Name
387 * for the proxied request, instead of the
390 * This is done by making a copy of the
391 * Stripped-User-Name attribute, turning it into
392 * a User-Name attribute, deleting the
393 * Stripped-User-Name and User-Name attributes
394 * from the vps list, and making the new
395 * User-Name the head of the vps list.
397 vp = pairfind(request->proxy->vps, PW_USER_NAME);
399 vp = paircreate(PW_USER_NAME, PW_TYPE_STRING);
401 radlog(L_ERR|L_CONS, "no memory");
404 vp->next = request->proxy->vps;
405 request->proxy->vps = vp;
407 memcpy(vp->strvalue, strippedname->strvalue,
408 sizeof(vp->strvalue));
409 vp->length = strippedname->length;
412 * Do NOT delete Stripped-User-Name.
417 * If there is no PW_CHAP_CHALLENGE attribute but
418 * there is a PW_CHAP_PASSWORD we need to add it
419 * since we can't use the request authenticator
420 * anymore - we changed it.
422 if (pairfind(request->proxy->vps, PW_CHAP_PASSWORD) &&
423 pairfind(request->proxy->vps, PW_CHAP_CHALLENGE) == NULL) {
424 vp = paircreate(PW_CHAP_CHALLENGE, PW_TYPE_STRING);
426 radlog(L_ERR|L_CONS, "no memory");
429 vp->length = AUTH_VECTOR_LEN;
430 memcpy(vp->strvalue, request->packet->vector, AUTH_VECTOR_LEN);
431 pairadd(&(request->proxy->vps), vp);
434 request->proxy->code = request->packet->code;
435 if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
436 request->proxy->dst_port = realm->auth_port;
437 request->proxy->dst_ipaddr = realm->ipaddr;
438 } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
439 request->proxy->dst_port = realm->acct_port;
440 request->proxy->dst_ipaddr = realm->acct_ipaddr;
444 * Add PROXY_STATE attribute, before pre-proxy stage,
445 * so the pre-proxy modules have access to it.
447 * Note that, at this point, the proxied request HAS NOT
448 * been assigned a RADIUS Id.
450 proxy_addinfo(request);
453 * Set up for sending the request.
455 memcpy(request->proxysecret, realm->secret, sizeof(request->proxysecret));
456 request->proxy_try_count = mainconfig.proxy_retry_count - 1;
457 request->proxy_next_try = request->timestamp + mainconfig.proxy_retry_delay;
458 delaypair = pairfind(request->proxy->vps, PW_ACCT_DELAY_TIME);
459 request->proxy->timestamp = request->timestamp - (delaypair ? delaypair->lvalue : 0);
464 vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE);
466 DEBUG2(" Found Pre-Proxy-Type %s", vp->strvalue);
467 pre_proxy_type = vp->lvalue;
469 rcode = module_pre_proxy(pre_proxy_type, request);
472 * Do NOT free request->proxy->vps, the pairs are needed
473 * for the retries! --Pac.
477 * Delay sending the proxy packet until after we've
478 * done the work above, playing with the request.
480 * After this point, it becomes dangerous to play
481 * with the request data structure, as the reply MAY
482 * come in and get processed before we're done with it here.
484 * Only proxy the packet if the pre-proxy code succeeded.
486 if ((rcode == RLM_MODULE_OK) ||
487 (rcode == RLM_MODULE_NOOP) ||
488 (rcode == RLM_MODULE_UPDATED)) {
489 request->options |= RAD_REQUEST_OPTION_PROXIED;
492 * IF it's a fake request, don't send the proxy
493 * packet. The outer tunnel session will take
494 * care of doing that.
496 if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0) {
498 * Add the proxied request to the
499 * list of outstanding proxied
500 * requests, BEFORE we send it, so
501 * we have fewer problems with race
502 * conditions when the responses come
505 if (!rl_add_proxy(request)) {
506 DEBUG("ERROR: Failed to proxy request %d",
508 return RLM_MODULE_FAIL; /* caller doesn't reply */
511 rad_send(request->proxy, NULL,
512 (char *)request->proxysecret);
514 rcode = RLM_MODULE_HANDLED; /* caller doesn't reply */
516 rcode = RLM_MODULE_FAIL; /* caller doesn't reply */