-I ..../libltdl
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000,2006  The FreeRADIUS server project
21  * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
22  * Copyright 2000  Chris Parker <cparker@starnetusa.com>
23  */
24
25 #include <freeradius-devel/ident.h>
26 RCSID("$Id$")
27
28 #include <freeradius-devel/autoconf.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 <freeradius-devel/radiusd.h>
42 #include <freeradius-devel/rad_assert.h>
43 #include <freeradius-devel/modules.h>
44 #include <freeradius-devel/request_list.h>
45
46 /*
47  *      We received a response from a remote radius server.
48  *      Call the post-proxy modules.
49  */
50 int proxy_receive(REQUEST *request)
51 {
52         int rcode;
53         int post_proxy_type = 0;
54         VALUE_PAIR *vp;
55
56         /*
57          *      Delete any reply we had accumulated until now.
58          */
59         pairfree(&request->reply->vps);
60
61         /*
62          *      Run the packet through the post-proxy stage,
63          *      BEFORE playing games with the attributes.
64          */
65         vp = pairfind(request->config_items, PW_POST_PROXY_TYPE);
66         if (vp) {
67                 DEBUG2("  Found Post-Proxy-Type %s", vp->vp_strvalue);
68                 post_proxy_type = vp->lvalue;
69         }
70         rcode = module_post_proxy(post_proxy_type, request);
71
72         /*
73          *      Delete the Proxy-State Attributes from the reply.
74          *      These include Proxy-State attributes from us and
75          *      remote server.
76          */
77         pairdelete(&request->proxy_reply->vps, PW_PROXY_STATE);
78
79         /*
80          *      Add the attributes left in the proxy reply to
81          *      the reply list.
82          */
83         pairadd(&request->reply->vps, request->proxy_reply->vps);
84         request->proxy_reply->vps = NULL;
85
86         /*
87          *      Free proxy request pairs.
88          */
89         pairfree(&request->proxy->vps);
90
91         /*
92          *      FIXME: If the packet is an Access-Challenge,
93          *      THEN add it to a cache, which does:
94          *
95          *      (src IP, State) -> (home server ip/port)
96          *
97          *      This allows the load-balancing code to
98          *      work for EAP...
99          *
100          *      Alternately, we can delete the State from the home
101          *      server, and use our own..  that might be better.
102          */
103
104         return rcode;
105 }
106
107 /*
108  *      Add a proxy-pair to the end of the request.
109  */
110 static void proxy_addinfo(REQUEST *request)
111 {
112         VALUE_PAIR *proxy_pair;
113
114         proxy_pair = paircreate(PW_PROXY_STATE, PW_TYPE_STRING);
115         if (proxy_pair == NULL) {
116                 radlog(L_ERR|L_CONS, "no memory");
117                 exit(1);
118         }
119         sprintf(proxy_pair->vp_strvalue, "%d", request->packet->id);
120         proxy_pair->length = strlen(proxy_pair->vp_strvalue);
121
122         pairadd(&request->proxy->vps, proxy_pair);
123 }
124
125
126 /*
127  *      Like realm find, but does load balancing, and we don't
128  *      wake up any sleeping realms.  Someone should already have
129  *      done that.
130  *
131  *      It also does NOT do fail-over to default if the realms are dead,
132  *      as that decision has already been made.
133  */
134 static REALM *proxy_realm_ldb(REQUEST *request, const char *realm_name,
135                               int accounting)
136 {
137         REALM           *cl, *lb;
138         uint32_t        count;
139
140         /*
141          *      FIXME: If the packet contains a State attribute,
142          *      AND the realm is load-balance,
143          *      AND there is a matching
144          *      State attribute in the cached entry, THEN proxy it to
145          *      that realm.
146          */
147
148         lb = NULL;
149         count = 0;
150         for (cl = mainconfig.realms; cl; cl = cl->next) {
151                 /*
152                  *      Wake up any sleeping realm.
153                  *
154                  *      Note that the 'realm find' function will only
155                  *      wake up the FIRST realm which matches.  We've
156                  *      got to wake up ALL of the matching realms.
157                  */
158                 if (cl->wakeup <= request->timestamp) {
159                         cl->active = TRUE;
160                 }
161                 if (cl->acct_wakeup <= request->timestamp) {
162                         cl->acct_active = TRUE;
163                 }
164
165                 /*
166                  *      Asked for auth/acct, and the auth/acct server
167                  *      is not active.  Skip it.
168                  */
169                 if ((!accounting && !cl->active) ||
170                     (accounting && !cl->acct_active)) {
171                         continue;
172                 }
173
174                 /*
175                  *      The realm name doesn't match, skip it.
176                  */
177                 if (strcasecmp(cl->realm, realm_name) != 0) {
178                         continue;
179                 }
180
181                 /*
182                  *      Fail-over, pick the first one that matches.
183                  */
184                 if ((count == 0) && /* if size > 0, we have round-robin */
185                     (cl->ldflag == 0)) {
186                         return cl;
187                 }
188
189                 /*
190                  *      We're doing load-balancing.  Pick a random
191                  *      number, which will be used to determine which
192                  *      home server is chosen.
193                  */
194                 if (!lb) {
195                         lb = cl;
196                         count = 1;
197                         continue;
198                 }
199
200                 /*
201                  *      Keep track of how many load balancing servers
202                  *      we've gone through.
203                  */
204                 count++;
205
206                 /*
207                  *      See the "camel book" for why this works.
208                  *
209                  *      If (rand(0..n) < 1), pick the current realm.
210                  *      We add a scale factor of 65536, to avoid
211                  *      floating point.
212                  */
213                 if ((count * (lrad_rand() & 0xffff)) < (uint32_t) 0x10000) {
214                         lb = cl;
215                 }
216         } /* loop over the realms */
217
218         /*
219          *      Return the load-balanced realm.
220          */
221         return lb;
222 }
223
224 /*
225  *      Relay the request to a remote server.
226  *      Returns:
227  *
228  *      RLM_MODULE_FAIL: we don't reply, caller returns without replying
229  *      RLM_MODULE_NOOP: caller falls through to normal processing
230  *      RLM_MODULE_HANDLED  : we reply, caller returns without replying
231  */
232 int proxy_send(REQUEST *request)
233 {
234         int rcode;
235         int pre_proxy_type = 0;
236         VALUE_PAIR *realmpair;
237         VALUE_PAIR *strippedname;
238         VALUE_PAIR *vp;
239         REALM *realm;
240         char *realmname;
241
242         /*
243          *      Not authentication or accounting.  Stop it.
244          */
245         if ((request->packet->code != PW_AUTHENTICATION_REQUEST) &&
246             (request->packet->code != PW_ACCOUNTING_REQUEST)) {
247                 DEBUG2("  ERROR: Cannot proxy packets of type %d",
248                        request->packet->code);
249                 return RLM_MODULE_FAIL;
250         }
251
252         /*
253          *      The timestamp is used below to figure the
254          *      next_try. The request needs to "hang around" until
255          *      either the other server sends a reply or the retry
256          *      count has been exceeded.  Until then, it should not
257          *      be eligible for the time-based cleanup.  --Pac. */
258
259         realmpair = pairfind(request->config_items, PW_PROXY_TO_REALM);
260         if (!realmpair) {
261                 /*
262                  *      Not proxying, so we can exit from the proxy
263                  *      code.
264                  */
265                 return RLM_MODULE_NOOP;
266         }
267
268         /*
269          *      If the server has already decided to reject the request,
270          *      then don't try to proxy it.
271          */
272         if (request->reply->code == PW_AUTHENTICATION_REJECT) {
273                 DEBUG2("Cancelling proxy as request was already rejected");
274                 return RLM_MODULE_REJECT;
275         }
276         if (((vp = pairfind(request->config_items, PW_AUTH_TYPE)) != NULL) &&
277             (vp->lvalue == PW_AUTHTYPE_REJECT)) {
278                 DEBUG2("Cancelling proxy as request was already rejected");
279                 return RLM_MODULE_REJECT;
280         }
281         /*
282          *      Length == 0 means it exists, but there's no realm.
283          *      Don't proxy it.
284          */
285         if (realmpair->length == 0) {
286                 return RLM_MODULE_NOOP;
287         }
288
289         realmname = (char *)realmpair->vp_strvalue;
290
291         /*
292          *      Look for the realm, using the load balancing
293          *      version of realm find.
294          */
295         realm = proxy_realm_ldb(request, realmname,
296                                 (request->packet->code == PW_ACCOUNTING_REQUEST));
297         if (realm == NULL) {
298                 DEBUG2("  ERROR: Failed to find live home server for realm %s",
299                        realmname);
300                 return RLM_MODULE_FAIL;
301         }
302
303         /*
304          *      Remember that we sent the request to a Realm.
305          */
306         pairadd(&request->packet->vps,
307                 pairmake("Realm", realm->realm, T_OP_EQ));
308
309         /*
310          *      Access-Request: look for LOCAL realm.
311          *      Accounting-Request: look for LOCAL realm.
312          */
313         if (((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
314              (realm->ipaddr.af == AF_INET) &&
315              (realm->ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_NONE))) ||
316             ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
317              (realm->acct_ipaddr.af == AF_INET) &&
318              (realm->acct_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_NONE)))) {
319                 DEBUG2(" WARNING: Cancelling proxy to Realm %s, as the realm is local.",
320                        realm->realm);
321                 return RLM_MODULE_NOOP;
322         }
323
324         /*
325          *      This is mainly for radrelay.  Don't proxy packets back
326          *      to servers which sent them to us.
327          */
328         if ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
329             (request->listener->type == RAD_LISTEN_DETAIL) &&
330             (realm->acct_ipaddr.af == AF_INET) &&
331             (request->packet->src_ipaddr.af == AF_INET) &&
332             (realm->acct_ipaddr.ipaddr.ip4addr.s_addr == request->packet->src_ipaddr.ipaddr.ip4addr.s_addr)) {
333                 DEBUG2("    rlm_realm: Packet came from realm %s, proxy cancelled", realm->realm);
334                 return RLM_MODULE_NOOP;
335         }
336
337         /*
338          *      Allocate the proxy packet, only if it wasn't already
339          *      allocated by a module.  This check is mainly to support
340          *      the proxying of EAP-TTLS and EAP-PEAP tunneled requests.
341          *
342          *      In those cases, the EAP module creates a "fake"
343          *      request, and recursively passes it through the
344          *      authentication stage of the server.  The module then
345          *      checks if the request was supposed to be proxied, and
346          *      if so, creates a proxy packet from the TUNNELED request,
347          *      and not from the EAP request outside of the tunnel.
348          *
349          *      The proxy then works like normal, except that the response
350          *      packet is "eaten" by the EAP module, and encapsulated into
351          *      an EAP packet.
352          */
353         if (!request->proxy) {
354                 /*
355                  *      Now build a new RADIUS_PACKET.
356                  *
357                  *      FIXME: it could be that the id wraps around
358                  *      too fast if we have a lot of requests, it
359                  *      might be better to keep a seperate ID value
360                  *      per remote server.
361                  *
362                  *      OTOH the remote radius server should be smart
363                  *      enough to compare _both_ ID and vector.
364                  *      Right?
365                  */
366                 if ((request->proxy = rad_alloc(TRUE)) == NULL) {
367                         radlog(L_ERR|L_CONS, "no memory");
368                         exit(1);
369                 }
370
371                 /*
372                  *      We now massage the attributes to be proxied...
373                  */
374
375                 /*
376                  *      Copy the request, then look up name and
377                  *      plain-text password in the copy.
378                  *
379                  *      Note that the User-Name attribute is the
380                  *      *original* as sent over by the client.  The
381                  *      Stripped-User-Name attribute is the one hacked
382                  *      through the 'hints' file.
383                  */
384                 request->proxy->vps =  paircopy(request->packet->vps);
385         }
386
387         /*
388          *      Strip the name, if told to.
389          *
390          *      Doing it here catches the case of proxied tunneled
391          *      requests.
392          */
393         if (realm->striprealm == TRUE &&
394            (strippedname = pairfind(request->proxy->vps, PW_STRIPPED_USER_NAME)) != NULL) {
395                 /*
396                  *      If there's a Stripped-User-Name attribute in
397                  *      the request, then use THAT as the User-Name
398                  *      for the proxied request, instead of the
399                  *      original name.
400                  *
401                  *      This is done by making a copy of the
402                  *      Stripped-User-Name attribute, turning it into
403                  *      a User-Name attribute, deleting the
404                  *      Stripped-User-Name and User-Name attributes
405                  *      from the vps list, and making the new
406                  *      User-Name the head of the vps list.
407                  */
408                 vp = pairfind(request->proxy->vps, PW_USER_NAME);
409                 if (!vp) {
410                         vp = paircreate(PW_USER_NAME, PW_TYPE_STRING);
411                         if (!vp) {
412                                 radlog(L_ERR|L_CONS, "no memory");
413                                 exit(1);
414                         }
415                         vp->next = request->proxy->vps;
416                         request->proxy->vps = vp;
417                 }
418                 memcpy(vp->vp_strvalue, strippedname->vp_strvalue,
419                        sizeof(vp->vp_strvalue));
420                 vp->length = strippedname->length;
421
422                 /*
423                  *      Do NOT delete Stripped-User-Name.
424                  */
425         }
426         
427         /*
428          *      If there is no PW_CHAP_CHALLENGE attribute but
429          *      there is a PW_CHAP_PASSWORD we need to add it
430          *      since we can't use the request authenticator
431          *      anymore - we changed it.
432          */
433         if (pairfind(request->proxy->vps, PW_CHAP_PASSWORD) &&
434             pairfind(request->proxy->vps, PW_CHAP_CHALLENGE) == NULL) {
435                 vp = paircreate(PW_CHAP_CHALLENGE, PW_TYPE_STRING);
436                 if (!vp) {
437                         radlog(L_ERR|L_CONS, "no memory");
438                         exit(1);
439                 }
440                 vp->length = AUTH_VECTOR_LEN;
441                 memcpy(vp->vp_strvalue, request->packet->vector, AUTH_VECTOR_LEN);
442                 pairadd(&(request->proxy->vps), vp);
443         }
444
445         request->proxy->code = request->packet->code;
446         if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
447                 request->proxy->dst_port = realm->auth_port;
448                 request->proxy->dst_ipaddr = realm->ipaddr;
449         } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
450                 request->proxy->dst_port = realm->acct_port;
451                 request->proxy->dst_ipaddr = realm->acct_ipaddr;
452         }
453
454         /*
455          *      Add PROXY_STATE attribute, before pre-proxy stage,
456          *      so the pre-proxy modules have access to it.
457          *
458          *      Note that, at this point, the proxied request HAS NOT
459          *      been assigned a RADIUS Id.
460          */
461         proxy_addinfo(request);
462
463         /*
464          *      Set up for sending the request.
465          */
466         memcpy(request->proxysecret, realm->secret, sizeof(request->proxysecret));
467         request->proxy_try_count = mainconfig.proxy_retry_count - 1;
468
469         vp = NULL;
470         if (request->packet->code == PW_ACCOUNTING_REQUEST) {
471                 vp = pairfind(request->proxy->vps, PW_ACCT_DELAY_TIME);
472         }
473         if (vp) {
474                 request->proxy->timestamp = request->timestamp - vp->lvalue;
475         } else {
476                 request->proxy->timestamp = request->timestamp;
477         }
478         request->proxy_start_time = request->timestamp;
479
480         /*
481          *      Do pre-proxying.
482          */
483         vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE);
484         if (vp) {
485                 DEBUG2("  Found Pre-Proxy-Type %s", vp->vp_strvalue);
486                 pre_proxy_type = vp->lvalue;
487         }
488         rcode = module_pre_proxy(pre_proxy_type, request);
489         switch (rcode) {
490         /*
491          *      Only proxy the packet if the pre-proxy code succeeded.
492          */
493         case RLM_MODULE_NOOP:
494         case RLM_MODULE_OK:
495         case RLM_MODULE_UPDATED:
496                 /*
497                  *      Delay sending the proxy packet until after we've
498                  *      done the work above, playing with the request.
499                  *
500                  *      After this point, it becomes dangerous to play with
501                  *      the request data structure, as the reply MAY come in
502                  *      and get processed before we're done with it here.
503                  */
504                 request->options |= RAD_REQUEST_OPTION_PROXIED;
505
506                 /*
507                  *      If it's a fake request, don't send the proxy
508                  *      packet.  The outer tunnel session will take
509                  *      care of doing that.
510                  */
511                 if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0) {
512                         /*
513                          *      Add the proxied request to the
514                          *      list of outstanding proxied
515                          *      requests, BEFORE we send it, so
516                          *      we have fewer problems with race
517                          *      conditions when the responses come
518                          *      back very quickly.
519                          */
520                         if (!rl_add_proxy(request)) {
521                                 DEBUG("ERROR: Failed to proxy request %d",
522                                       request->number);
523                                 return RLM_MODULE_FAIL; /* caller doesn't reply */
524                         }
525
526                         /*
527                          *      We're still running, encode & sign the
528                          *      packet outside of the critical section.
529                          */
530                         if (request->child_pid != NO_SUCH_CHILD_PID) {
531                                 rad_encode(request->proxy, NULL,
532                                            (char *)request->proxysecret);
533                                 rad_sign(request->proxy, NULL,
534                                          (char *)request->proxysecret);
535                         } else {
536                                 request->proxy_listener->send(request->proxy_listener,
537                                                               request);
538                         }
539                 }
540                 rcode = RLM_MODULE_HANDLED; /* caller doesn't reply */
541                 break;
542         /*
543          *      The module handled the request, don't reply.
544          */
545         case RLM_MODULE_HANDLED:
546                 break;
547         /*
548          *      Neither proxy, nor reply to invalid requests.
549          */
550         case RLM_MODULE_FAIL:
551         case RLM_MODULE_INVALID:
552         case RLM_MODULE_NOTFOUND:
553         case RLM_MODULE_REJECT:
554         case RLM_MODULE_USERLOCK:
555         default:
556                 rcode = RLM_MODULE_FAIL; /* caller doesn't reply */
557                 break;
558         }
559
560         /*
561          *      Do NOT free request->proxy->vps, the pairs are needed
562          *      for the retries!
563          */
564         return rcode;
565 }