631babb1561944a0e6cb99fbaae782dc102292a4
[freeradius.git] / src / main / proxy.c
1 /*
2  * proxy.c      Proxy stuff.
3  *
4  * Version:     @(#)proxy.c  1.52  22-Jul-1999  miquels@cistron.nl
5  */
6
7 char proxy_sccsid[] =
8 "@(#)proxy.c    1.52 Copyright 1999 Cistron Internet Services B.V.";
9
10 #include        "autoconf.h"
11
12 #include        <sys/types.h>
13 #include        <sys/socket.h>
14 #include        <sys/time.h>
15 #include        <netinet/in.h>
16
17 #include        <stdio.h>
18 #include        <stdlib.h>
19 #include        <unistd.h>
20 #include        <ctype.h>
21 #include        <string.h>
22 #include        <time.h>
23
24 #if HAVE_MALLOC_H
25 #  include      <malloc.h>
26 #endif
27
28 #include        "radiusd.h"
29
30
31 static int      proxy_id = 1;
32 static REQUEST  *proxy_requests;
33
34 static int allowed [] = {
35         PW_SERVICE_TYPE,
36         PW_FRAMED_PROTOCOL,
37         PW_FILTER_ID,
38         PW_FRAMED_MTU,
39         PW_FRAMED_COMPRESSION,
40         PW_LOGIN_SERVICE,
41         PW_REPLY_MESSAGE,
42         PW_SESSION_TIMEOUT,
43         PW_IDLE_TIMEOUT,
44         PW_PORT_LIMIT,
45         0,
46 };
47
48
49 /*
50  *      Cleanup old outstanding requests.
51  */
52 static void proxy_cleanup(void)
53 {
54         REQUEST                 *a, *last, *next;
55         time_t                  now;
56
57         last = NULL;
58         now  = time(NULL);
59
60         for (a = proxy_requests; a; a = next) {
61                 next = a->next;
62                 if (a->timestamp + MAX_REQUEST_TIME < now) {
63                         if (last)
64                                 last->next = a->next;
65                         else
66                                 proxy_requests = a->next;
67                         request_free(a);
68                         continue;
69                 }
70                 last = a;
71         }
72 }
73
74 /*
75  *      Add a proxy-pair to the end of the request.
76  */
77 static void proxy_addinfo(RADIUS_PACKET *rp)
78 {
79         VALUE_PAIR              *proxy_pair, *vp;
80
81         if  (!(proxy_pair = paircreate(PW_PROXY_STATE, PW_TYPE_STRING))) {
82                 log(L_ERR|L_CONS, "no memory");
83                 exit(1);
84         }
85         sprintf(proxy_pair->strvalue, "%04x", rp->id);
86         proxy_pair->length = 4;
87
88         for (vp = rp->vps; vp && vp->next; vp = vp->next)
89                 ;
90         vp->next = proxy_pair;
91 }
92
93 /*
94  *      Add the request to the list.
95  */
96 int proxy_addrequest(REQUEST *request, int *proxy_id)
97 {
98         REQUEST         *a, *last = NULL;
99         int             id = -1;
100
101         /*
102          *      See if we already have a similar outstanding request.
103          */
104         for (a = proxy_requests; a; a = a->next) {
105                 if (a->packet->src_ipaddr == request->packet->src_ipaddr &&
106                     a->packet->id == request->packet->id &&
107                     !memcmp(a->packet->vector, request->packet->vector, 16))
108                         break;
109                 last = a;
110         }
111         if (a) {
112                 /*
113                  *      Yes, this is a retransmit so delete the
114                  *      old request.
115                  */
116                 id = a->proxy->id;
117                 if (last)
118                         last->next = a->next;
119                 else
120                         proxy_requests = a->next;
121                 request_free(a);
122                 free(a);
123         }
124         if (id < 0) {
125                 id = (*proxy_id)++;
126                 *proxy_id &= 0xFFFF;
127         }
128
129         request->next = NULL;
130         request->child_pid = -1;
131         request->timestamp = time(NULL);
132
133         request->next = proxy_requests;
134         proxy_requests = request;
135
136         return id;
137 }
138
139
140 /*
141  *      Relay the request to a remote server.
142  *      Returns:  1 success (we reply, caller returns without replying)
143  *                0 fail (caller falls through to normal processing)
144  *               -1 fail (we don't reply, caller returns without replying)
145  */
146 int proxy_send(REQUEST *request, int activefd)
147 {
148         VALUE_PAIR              *namepair;
149         VALUE_PAIR              *passpair;
150         VALUE_PAIR              *vp, *vps;
151         CLIENT                  *client;
152         REALM                   *realm;
153         char                    *realmname;
154
155         /*
156          *      First copy the request, then look up
157          *      name and (encrypted) password in the copy.
158          */
159         vps = paircopy(request->packet->vps);
160         namepair = pairfind(vps, PW_USER_NAME);
161         if (namepair == NULL) {
162                 pairfree(vps);
163                 return 0;
164         }
165         passpair = pairfind(vps, PW_PASSWORD);
166
167         /*
168          *      Use the original username if available. The one
169          *      in the A/V pairs might have been stripped already.
170          */
171         if (request->username[0]) {
172                 strncpy(namepair->strvalue, request->username,
173                         sizeof(namepair->strvalue));
174                 namepair->strvalue[sizeof(namepair->strvalue) - 1] = 0;
175         }
176
177         /*
178          *      Now check if we know this realm!
179          *      A NULL realm is OK.
180          *      If not found, we treat it as usual.
181          *      Find the realm from the _end_ so that we can
182          *      cascade realms: user@realm1@realm2.
183          */
184         if ((realmname = strrchr(namepair->strvalue, '@')) != NULL)
185                 realmname++;
186         if ((realm = realm_find(realmname ? realmname : "NULL")) == NULL) {
187                 pairfree(vps);
188                 return 0;
189         }
190         if (realmname != NULL && realm->striprealm)
191                         realmname[-1] = 0;
192         namepair->length = strlen(namepair->strvalue);
193
194         /*
195          *      Perhaps accounting proxying was turned off.
196          */
197         if (request->packet->code == PW_ACCOUNTING_REQUEST &&
198             realm-acct_port == 0) {
199                 pairfree(vps);
200                 return 0;
201         }
202
203         /*
204          *      The special server LOCAL ?
205          */
206         if (strcmp(realm->server, "LOCAL") == 0) {
207                 pairfree(vps);
208                 namepair = pairfind(request->packet->vps, PW_USER_NAME);
209                 if (realm->striprealm &&
210                     ((realmname = strrchr(namepair->strvalue, '@')) != NULL)) {
211                         *realmname = 0;
212                         namepair->length = strlen(namepair->strvalue);
213                 }
214                 return 0;
215         }
216
217         /*
218          *      Find the remote server in the "client" list-
219          *      we need the secret.
220          */
221         if ((client = client_find(realm->ipaddr)) == NULL) {
222                 log(L_PROXY, "cannot find secret for server %s in clients file",
223                         realm->server);
224                 pairfree(vps);
225                 return 0;
226         }
227
228         /*
229          *      Now build a new RADIUS_PACKET and send it.
230          *
231          *      FIXME: it could be that the id wraps around too fast if
232          *      we have a lot of requests, it might be better to keep
233          *      a seperate ID value per remote server.
234          *
235          *      OTOH the remote radius server should be smart enough to
236          *      compare _both_ ID and vector. Right ?
237          */
238         if ((request->proxy = rad_alloc(0)) == NULL) {
239                 log(L_ERR|L_CONS, "no memory");
240                 exit(1);
241         }
242
243         request->proxy->code = request->packet->code;
244         request->proxy->dst_ipaddr = realm->ipaddr;
245         if (request->packet->code == PW_AUTHENTICATION_REQUEST)
246                 request->proxy->dst_port = realm->auth_port;
247         else
248                 request->proxy->dst_port = realm->acct_port;
249         request->proxy->vps = vps;
250
251         printf("Destination port: %d, server %s (%d/%d)\n",
252                 request->proxy->dst_port, realm->server,
253                 realm->auth_port, realm->acct_port);
254
255         /*
256          *      XXX: we re-use the vector from the original request
257          *      here, since that's easy for retransmits ...
258          */
259         memcpy(request->proxy->vector, request->packet->vector,
260                 AUTH_VECTOR_LEN);
261
262         /*
263          *      Add the request to the list of outstanding requests.
264          *      Note that request->proxy->id is a 16 bits value,
265          *      while rad_send sends only the 8 least significant
266          *      bits of that same value.
267          */
268         request->proxy->id = proxy_addrequest(request, &proxy_id);
269
270         /*
271          *      Add PROXY_STATE attribute.
272          */
273         proxy_addinfo(request->proxy);
274
275         /*
276          *      We need to re-encode the password with
277          *      the new secret.
278          */
279         if (passpair) {
280                 rad_pwdecode(passpair->strvalue, passpair->length,
281                         request->secret, request->packet->vector);
282                 rad_pwencode(passpair->strvalue, &(passpair->length),
283                         client->secret, request->packet->vector);
284         }
285
286         /*
287          *      If there is no PW_CHAP_CHALLENGE attribute but there
288          *      is a PW_CHAP_PASSWORD we need to add it since we can't
289          *      use the request authenticator anymore - we changed it.
290          */
291         if (pairfind(vps, PW_CHAP_PASSWORD) &&
292             pairfind(vps, PW_CHAP_CHALLENGE) == NULL) {
293                 if (!(vp = paircreate(PW_CHAP_CHALLENGE, PW_TYPE_STRING))) {
294                         log(L_ERR|L_CONS, "no memory");
295                         exit(1);
296                 }
297                 vp->length = AUTH_VECTOR_LEN;
298                 memcpy(vp->strvalue, request->packet->vector, AUTH_VECTOR_LEN);
299                 pairadd(&vps, vp);
300         }
301
302         /*
303          *      Send the request.
304          */
305         rad_send(request->proxy, activefd, client->secret);
306
307         /*
308          *      We can free proxy->vps now, not needed anymore.
309          */
310         pairfree(request->proxy->vps);
311         request->proxy->vps = NULL;
312
313         return 1;
314 }
315
316
317 /*
318  *      We received a response from a remote radius server.
319  *      Find the original request, then return.
320  *      Returns:   0 proxy found
321  *                -1 error don't reply
322  */
323 int proxy_receive(REQUEST *request, int activefd)
324 {
325         VALUE_PAIR      *vp, *last, *prev, *x;
326         VALUE_PAIR      *allowed_pairs;
327         REQUEST *oldreq, *lastreq;
328         char            *s;
329         int             pp = -1;
330         int             i;
331
332         /*
333          *      First cleanup old outstanding requests.
334          */
335         proxy_cleanup();
336
337         /*
338          *      FIXME: calculate md5 checksum!
339          */
340
341         /*
342          *      Find the last PROXY_STATE attribute.
343          */
344         oldreq  = NULL;
345         lastreq = NULL;
346         last    = NULL;
347         x       = NULL;
348         prev    = NULL;
349
350         for (vp = request->packet->vps; vp; vp = vp->next) {
351                 if (vp->attribute == PW_PROXY_STATE) {
352                         prev = x;
353                         last = vp;
354                 }
355                 x = vp;
356         }
357         if (last && last->strvalue) {
358                 /*
359                  *      Merit really rapes the Proxy-State attribute.
360                  *      See if it still is a valid 4-digit hex number.
361                  */
362                 s = last->strvalue;
363                 if (strlen(s) == 4 && isxdigit(s[0]) && isxdigit(s[1]) &&
364                     isxdigit(s[2]) && isxdigit(s[3])) {
365                         pp = strtol(last->strvalue, NULL, 16);
366                 } else {
367                         log(L_PROXY, "server %s mangled Proxy-State attribute",
368                         client_name(request->packet->src_ipaddr));
369                 }
370         }
371
372         /*
373          *      Now find it in the list of outstanding requests.
374          */
375
376         for (oldreq = proxy_requests; oldreq; oldreq = oldreq->next) {
377                 /*
378                  *      Some servers drop the proxy pair. So
379                  *      compare in another way if needed.
380                  */
381                 if (pp >= 0 && pp == oldreq->proxy->id)
382                         break;
383                 if (pp < 0 &&
384                     request->packet->src_ipaddr == oldreq->proxy->dst_ipaddr &&
385                     request->packet->id     == (oldreq->proxy->id & 0xFF))
386                         break;
387                 lastreq = oldreq;
388         }
389
390         if (oldreq == NULL) {
391                 log(L_PROXY, "Unreckognized proxy reply from server %s - ID %d",
392                         client_name(request->packet->src_ipaddr),
393                         request->packet->id);
394                 return -1;
395         }
396
397         /*
398          *      Remove oldreq from list.
399          */
400         if (lastreq)
401                 lastreq->next = oldreq->next;
402         else
403                 proxy_requests = oldreq->next;
404
405         /*
406          *      Remove proxy pair from list.
407          */
408         if (last) {
409                 if (prev)
410                         prev->next = last->next;
411                 else
412                         request->packet->vps = last->next;
413         }
414
415         /*
416          *      Only allow some attributes to be propagated from
417          *      the remote server back to the NAS, for security.
418          */
419         allowed_pairs = NULL;
420         for(i = 0; allowed[i]; i++)
421                 pairmove2(&allowed_pairs, &(request->packet->vps), allowed[i]);
422
423         /*
424          *      Now rebuild the AUTHREQ struct, so that the
425          *      normal functions can process it.
426          */
427         request->proxy = oldreq->proxy;
428         oldreq->proxy = NULL;
429         request->proxy->vps  = allowed_pairs;
430         request->proxy->code = request->packet->code;
431
432         pairfree(request->packet->vps);
433         free(request->packet);
434         request->packet = oldreq->packet;
435         oldreq->packet = NULL;
436
437         request->timestamp = oldreq->timestamp;
438
439         request_free(oldreq);
440
441         return 0;
442 }
443