type.data is malloc'd by everyone BUT radeapclient, which
[freeradius.git] / src / main / proxy.c
1 /*
2  * proxy.c      Proxy stuff.
3  *
4  * Version:     $Id$
5  *
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.
10  *
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.
15  *
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
19  *
20  * Copyright 2000  The FreeRADIUS server project
21  * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
22  * Copyright 2000  Chris Parker <cparker@starnetusa.com>
23  */
24
25 static const char rcsid[] = "$Id$";
26
27 #include "autoconf.h"
28 #include "libradius.h"
29
30 #include <sys/socket.h>
31
32 #ifdef HAVE_NETINET_IN_H
33 #       include <netinet/in.h>
34 #endif
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <ctype.h>
39 #include <string.h>
40
41 #include "radiusd.h"
42 #include "rad_assert.h"
43 #include "modules.h"
44 #include "request_list.h"
45
46 /*
47  *      We received a response from a remote radius server.
48  *      Find the original request, then return.
49  *      Returns:   1 replication don't reply
50  *                 0 proxy found
51  *                -1 error don't reply
52  */
53 int proxy_receive(REQUEST *request)
54 {
55         int rcode;
56         int post_proxy_type = 0;
57         VALUE_PAIR *vp;
58
59         /*
60          *      Delete any reply we had accumulated until now.
61          */
62         pairfree(&request->reply->vps);
63
64         /*
65          *      Run the packet through the post-proxy stage,
66          *      BEFORE playing games with the attributes.
67          */
68         vp = pairfind(request->config_items, PW_POST_PROXY_TYPE);
69         if (vp) {
70                 DEBUG2("  Found Post-Proxy-Type %s", vp->strvalue);
71                 post_proxy_type = vp->lvalue;
72         }
73         rcode = module_post_proxy(post_proxy_type, request);
74
75         /*
76          *      Delete the Proxy-State Attributes from the reply.
77          *      These include Proxy-State attributes from us and
78          *      remote server.
79          */
80         pairdelete(&request->proxy_reply->vps, PW_PROXY_STATE);
81
82         /*
83          *      Add the attributes left in the proxy reply to
84          *      the reply list.
85          */
86         pairadd(&request->reply->vps, request->proxy_reply->vps);
87         request->proxy_reply->vps = NULL;
88
89         /*
90          *      Free proxy request pairs.
91          */
92         pairfree(&request->proxy->vps);
93
94         return rcode;
95 }
96
97 /*
98  *      Add a proxy-pair to the end of the request.
99  */
100 static void proxy_addinfo(REQUEST *request)
101 {
102         VALUE_PAIR *proxy_pair;
103
104         proxy_pair = paircreate(PW_PROXY_STATE, PW_TYPE_STRING);
105         if (proxy_pair == NULL) {
106                 radlog(L_ERR|L_CONS, "no memory");
107                 exit(1);
108         }
109         sprintf((char *)proxy_pair->strvalue, "%d", request->packet->id);
110         proxy_pair->length = strlen((char *)proxy_pair->strvalue);
111
112         pairadd(&request->proxy->vps, proxy_pair);
113 }
114
115
116 /*
117  *      Like realm find, but does load balancing, and we don't
118  *      wake up any sleeping realms.  Someone should already have
119  *      done that.
120  *
121  *      It also does NOT do fail-over to default if the realms are dead,
122  *      as that decision has already been made.
123  */
124 static REALM *proxy_realm_ldb(REQUEST *request, const char *realm_name,
125                               int accounting)
126 {
127         int             redone = 0;
128         REALM           *cl, *lb;
129         uint32_t        count;
130
131  redo:
132         lb = NULL;
133         count = 0;
134         for (cl = mainconfig.realms; cl; cl = cl->next) {
135                 /*
136                  *      Wake up any sleeping realm.
137                  *
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.
141                  */
142                 if (cl->wakeup <= request->timestamp) {
143                         cl->active = TRUE;
144                 }
145                 if (cl->acct_wakeup <= request->timestamp) {
146                         cl->acct_active = TRUE;
147                 }
148
149                 /*
150                  *      Asked for auth/acct, and the auth/acct server
151                  *      is not active.  Skip it.
152                  */
153                 if ((!accounting && !cl->active) ||
154                     (accounting && !cl->acct_active)) {
155                         continue;
156                 }
157
158                 /*
159                  *      The realm name doesn't match, skip it.
160                  */
161                 if (strcasecmp(cl->realm, realm_name) != 0) {
162                         continue;
163                 }
164
165                 /*
166                  *      Fail-over, pick the first one that matches.
167                  */
168                 if ((count == 0) && /* if size > 0, we have round-robin */
169                     (cl->ldflag == 0)) {
170                         return cl;
171                 }
172
173                 /*
174                  *      We're doing load-balancing.  Pick a random
175                  *      number, which will be used to determine which
176                  *      home server is chosen.
177                  */
178                 if (!lb) {
179                         lb = cl;
180                         count = 1;
181                         continue;
182                 }
183
184                 /*
185                  *      Keep track of how many load balancing servers
186                  *      we've gone through.
187                  */
188                 count++;
189
190                 /*
191                  *      See the "camel book" for why this works.
192                  *
193                  *      If (rand(0..n) < 1), pick the current realm.
194                  *      We add a scale factor of 65536, to avoid
195                  *      floating point.
196                  */
197                 if ((count * (lrad_rand() & 0xffff)) < (uint32_t) 0x10000) {
198                         lb = cl;
199                 }
200         } /* loop over the realms */
201
202         /*
203          *      All are dead, see if we have to wake
204          */
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) {
209                                         cl->active = TRUE;
210                                 }
211                                 else if (accounting &&
212                                          !cl->acct_active) {
213                                         cl->acct_active = TRUE;
214                                 }
215                         }
216                 }
217                 redone = 1;
218                 goto redo;
219         }
220
221         /*
222          *      Return the load-balanced realm.
223          */
224         return lb;
225 }
226
227 /*
228  *      Relay the request to a remote server.
229  *      Returns:
230  *
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
234  */
235 int proxy_send(REQUEST *request)
236 {
237         int rcode;
238         int pre_proxy_type = 0;
239         VALUE_PAIR *realmpair;
240         VALUE_PAIR *strippedname;
241         VALUE_PAIR *delaypair;
242         VALUE_PAIR *vp;
243         REALM *realm;
244         char *realmname;
245
246         /*
247          *      Not authentication or accounting.  Stop it.
248          */
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;
254         }
255
256         /*
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. */
262
263         realmpair = pairfind(request->config_items, PW_PROXY_TO_REALM);
264         if (!realmpair) {
265                 /*
266                  *      Not proxying, so we can exit from the proxy
267                  *      code.
268                  */
269                 return RLM_MODULE_NOOP;
270         }
271
272         /*
273          *      If the server has already decided to reject the request,
274          *      then don't try to proxy it.
275          */
276         if (request->reply->code == PW_AUTHENTICATION_REJECT) {
277                 DEBUG2("Cancelling proxy as request was already rejected");
278                 return RLM_MODULE_REJECT;
279         }
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;
284         }
285         /*
286          *      Length == 0 means it exists, but there's no realm.
287          *      Don't proxy it.
288          */
289         if (realmpair->length == 0) {
290                 return RLM_MODULE_NOOP;
291         }
292
293         realmname = (char *)realmpair->strvalue;
294
295         /*
296          *      Look for the realm, using the load balancing
297          *      version of realm find.
298          */
299         realm = proxy_realm_ldb(request, realmname,
300                                 (request->packet->code == PW_ACCOUNTING_REQUEST));
301         if (realm == NULL) {
302                 DEBUG2("  ERROR: Failed to find live home server for realm %s",
303                        realmname);
304                 return RLM_MODULE_FAIL;
305         }
306
307         /*
308          *      Remember that we sent the request to a Realm.
309          */
310         pairadd(&request->packet->vps,
311                 pairmake("Realm", realm->realm, T_OP_EQ));
312
313         /*
314          *      Access-Request: look for LOCAL realm.
315          *      Accounting-Request: look for LOCAL realm.
316          */
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.",
322                        realm->realm);
323                 return RLM_MODULE_NOOP;
324         }
325
326         /*
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.
330          *
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.
337          *
338          *      The proxy then works like normal, except that the response
339          *      packet is "eaten" by the EAP module, and encapsulated into
340          *      an EAP packet.
341          */
342         if (!request->proxy) {
343                 /*
344                  *      Now build a new RADIUS_PACKET.
345                  *
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
349                  *      per remote server.
350                  *
351                  *      OTOH the remote radius server should be smart
352                  *      enough to compare _both_ ID and vector.
353                  *      Right?
354                  */
355                 if ((request->proxy = rad_alloc(TRUE)) == NULL) {
356                         radlog(L_ERR|L_CONS, "no memory");
357                         exit(1);
358                 }
359
360                 /*
361                  *      We now massage the attributes to be proxied...
362                  */
363
364                 /*
365                  *      Copy the request, then look up name and
366                  *      plain-text password in the copy.
367                  *
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.
372                  */
373                 request->proxy->vps =  paircopy(request->packet->vps);
374         }
375
376         /*
377          *      Strip the name, if told to.
378          *
379          *      Doing it here catches the case of proxied tunneled
380          *      requests.
381          */
382         if (realm->striprealm == TRUE &&
383            (strippedname = pairfind(request->proxy->vps, PW_STRIPPED_USER_NAME)) != NULL) {
384                 /*
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
388                  *      original name.
389                  *
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.
396                  */
397                 vp = pairfind(request->proxy->vps, PW_USER_NAME);
398                 if (!vp) {
399                         vp = paircreate(PW_USER_NAME, PW_TYPE_STRING);
400                         if (!vp) {
401                                 radlog(L_ERR|L_CONS, "no memory");
402                                 exit(1);
403                         }
404                         vp->next = request->proxy->vps;
405                         request->proxy->vps = vp;
406                 }
407                 memcpy(vp->strvalue, strippedname->strvalue,
408                        sizeof(vp->strvalue));
409                 vp->length = strippedname->length;
410
411                 /*
412                  *      Do NOT delete Stripped-User-Name.
413                  */
414         }
415         
416         /*
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.
421          */
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);
425                 if (!vp) {
426                         radlog(L_ERR|L_CONS, "no memory");
427                         exit(1);
428                 }
429                 vp->length = AUTH_VECTOR_LEN;
430                 memcpy(vp->strvalue, request->packet->vector, AUTH_VECTOR_LEN);
431                 pairadd(&(request->proxy->vps), vp);
432         }
433
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;
441         }
442
443         /*
444          *      Add PROXY_STATE attribute, before pre-proxy stage,
445          *      so the pre-proxy modules have access to it.
446          *
447          *      Note that, at this point, the proxied request HAS NOT
448          *      been assigned a RADIUS Id.
449          */
450         proxy_addinfo(request);
451
452         /*
453          *      Set up for sending the request.
454          */
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);
460
461         /*
462          *  Do pre-proxying
463          */
464         vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE);
465         if (vp) {
466                 DEBUG2("  Found Pre-Proxy-Type %s", vp->strvalue);
467                 pre_proxy_type = vp->lvalue;
468         }
469         rcode = module_pre_proxy(pre_proxy_type, request);
470
471         /*
472          *      Do NOT free request->proxy->vps, the pairs are needed
473          *      for the retries! --Pac.
474          */
475
476         /*
477          *      Delay sending the proxy packet until after we've
478          *      done the work above, playing with the request.
479          *
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.
483          *
484          *      Only proxy the packet if the pre-proxy code succeeded.
485          */
486         if ((rcode == RLM_MODULE_OK) ||
487             (rcode == RLM_MODULE_NOOP) ||
488             (rcode == RLM_MODULE_UPDATED)) {
489                 request->options |= RAD_REQUEST_OPTION_PROXIED;
490
491                 /*
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.
495                  */
496                 if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0) {
497                         /*
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
503                          *      back very quickly.
504                          */
505                         if (!rl_add_proxy(request)) {
506                                 DEBUG("ERROR: Failed to proxy request %d",
507                                       request->number);
508                                 return RLM_MODULE_FAIL; /* caller doesn't reply */
509                         }
510
511                         rad_send(request->proxy, NULL,
512                                  (char *)request->proxysecret);
513                 }
514                 rcode = RLM_MODULE_HANDLED; /* caller doesn't reply */
515         } else {
516                 rcode = RLM_MODULE_FAIL; /* caller doesn't reply */
517         }
518
519         return rcode;
520 }