Use multiple radius sockets to allow more concurrent authentication requests
[l2tpns.git] / radius.c
1 // L2TPNS Radius Stuff
2 // $Id: radius.c,v 1.3 2004/05/24 04:27:11 fred_nerk Exp $
3
4 #include <time.h>
5 #include <stdio.h>
6 #include <sys/types.h>
7 #include <sys/socket.h>
8 #include <malloc.h>
9 #include <string.h>
10 #include <fcntl.h>
11 #include <arpa/inet.h>
12 #include <ctype.h>
13 #include <netinet/in.h>
14 #include "md5.h"
15 #include "constants.h"
16 #include "l2tpns.h"
17 #include "plugin.h"
18 #include "util.h"
19
20 extern radiust *radius;
21 extern sessiont *session;
22 extern tunnelt *tunnel;
23 extern u32 sessionid;
24 extern struct Tstats *_statistics;
25 extern struct configt *config;
26 extern int *radfds;
27
28 const char *radius_state(int state)
29 {
30 static char *tmp = NULL;
31 int i;
32 for (i = 0; radius_states[i]; i++)
33 if (i == state) return radius_states[i];
34
35 if (tmp == NULL) tmp = (char *)calloc(64, 1);
36 sprintf(tmp, "%d", state);
37 return tmp;
38 }
39
40 // Set up socket for radius requests
41 void initrad(void)
42 {
43 int i;
44 log(3, 0, 0, 0, "Creating %d sockets for RADIUS queries\n", config->num_radfds);
45 radfds = calloc(sizeof(int), config->num_radfds);
46 for (i = 0; i < config->num_radfds; i++)
47 {
48 int flags;
49 if (!radfds[i]) radfds[i] = socket(AF_INET, SOCK_DGRAM, UDP);
50 flags = fcntl(radfds[i], F_GETFL, 0);
51 fcntl(radfds[i], F_SETFL, flags | O_NONBLOCK);
52 }
53 }
54
55 void radiusclear(u16 r, sessionidt s)
56 {
57 if (s) session[s].radius = 0;
58 memset(&radius[r], 0, sizeof(radius[r])); // radius[r].state = RADIUSNULL;
59 }
60
61 int next_radius_id = 1;
62
63 static u16 new_radius()
64 {
65 u16 i;
66 int loops = 0;
67 for (i = next_radius_id; ; i = (i + 1) % MAXRADIUS)
68 {
69 if (radius[i].state == RADIUSNULL)
70 {
71 next_radius_id = (next_radius_id + 1) % MAXRADIUS;
72 return i;
73 }
74 if (next_radius_id == i)
75 {
76 if (++loops == 2)
77 {
78 log(0, 0, 0, 0, "Can't find a free radius session! This is very bad!\n");
79 return 0;
80 }
81 }
82 }
83 }
84
85 u16 radiusnew(sessionidt s)
86 {
87 u16 r;
88 if (!(r = new_radius()))
89 {
90 log(1, 0, s, session[s].tunnel, "No free RADIUS sessions\n");
91 STAT(radius_overflow);
92 return 0;
93 };
94 memset(&radius[r], 0, sizeof(radius[r]));
95 session[s].radius = r;
96 radius[r].session = s;
97 radius[r].state = RADIUSWAIT;
98 return r;
99 }
100
101 // Send a RADIUS request
102 void radiussend(u16 r, u8 state)
103 {
104 struct sockaddr_in addr;
105 u8 b[4096]; // RADIUS packet
106 char pass[129];
107 int pl;
108 u8 *p;
109 sessionidt s;
110 #ifdef STAT_CALLS
111 STAT(call_radiussend);
112 #endif
113 s = radius[r].session;
114 if (!config->numradiusservers)
115 {
116 log(0, 0, s, session[s].tunnel, "No RADIUS servers\n");
117 return;
118 }
119 if (!*config->radiussecret)
120 {
121 log(0, 0, s, session[s].tunnel, "No RADIUS secret\n");
122 return;
123 }
124
125 if (state != RADIUSAUTH && !config->radius_accounting)
126 {
127 // Radius accounting is turned off
128 radiusclear(r, s);
129 return;
130 }
131
132 if (radius[r].state != state)
133 radius[r].try = 0;
134 radius[r].state = state;
135 radius[r].retry = backoff(radius[r].try++);
136 log(4, 0, s, session[s].tunnel, "Send RADIUS id %d sock %d state %s try %d\n",
137 r >> RADIUS_SHIFT, r & RADIUS_MASK,
138 radius_state(radius[r].state), radius[r].try);
139 if (radius[r].try > config->numradiusservers * 2)
140 {
141 if (s)
142 {
143 if (state == RADIUSAUTH)
144 sessionshutdown(s, "RADIUS timeout");
145 else
146 {
147 log(1, 0, s, session[s].tunnel, "RADIUS timeout, but in state %s so don't timeout session\n",
148 radius_states[state]);
149 radiusclear(r, s);
150 }
151 STAT(radius_timeout);
152 }
153 else
154 {
155 STAT(radius_retries);
156 radius[r].state = RADIUSWAIT;
157 radius[r].retry = 100;
158 }
159 return ;
160 }
161 // contruct RADIUS access request
162 switch (state)
163 {
164 case RADIUSAUTH:
165 b[0] = 1; // access request
166 break;
167 case RADIUSSTART:
168 case RADIUSSTOP:
169 b[0] = 4; // accounting request
170 break;
171 default:
172 log(0, 0, 0, 0, "Unknown radius state %d\n", state);
173 }
174 b[1] = r >> RADIUS_SHIFT; // identifier
175 memcpy(b + 4, radius[r].auth, 16);
176 p = b + 20;
177 if (s)
178 {
179 *p = 1; // user name
180 p[1] = strlen(session[s].user) + 2;
181 strcpy(p + 2, session[s].user);
182 p += p[1];
183 }
184 if (state == RADIUSAUTH)
185 {
186 if (radius[r].chap)
187 {
188 *p = 3; // CHAP password
189 p[1] = 19; // length
190 p[2] = radius[r].id; // ID
191 memcpy(p + 3, radius[r].pass, 16); // response from CHAP request
192 p += p[1];
193 *p = 60; // CHAP Challenge
194 p[1] = 18; // length
195 memcpy(p + 2, radius[r].auth, 16);
196 p += p[1];
197 }
198 else
199 {
200 strcpy(pass, radius[r].pass);
201 pl = strlen(pass);
202 while (pl & 15)
203 pass[pl++] = 0; // pad
204 if (pl)
205 { // encrypt
206 hasht hash;
207 int p = 0;
208 while (p < pl)
209 {
210 MD5_CTX ctx;
211 MD5Init(&ctx);
212 MD5Update(&ctx, config->radiussecret, strlen(config->radiussecret));
213 if (p)
214 MD5Update(&ctx, pass + p - 16, 16);
215 else
216 MD5Update(&ctx, radius[r].auth, 16);
217 MD5Final(hash, &ctx);
218 do
219 {
220 pass[p] ^= hash[p & 15];
221 p++;
222 }
223 while (p & 15);
224 }
225 }
226 *p = 2; // password
227 p[1] = pl + 2;
228 if (pl)
229 memcpy(p + 2, pass, pl);
230 p += p[1];
231 }
232 }
233 else if (state == RADIUSSTART || state == RADIUSSTOP)
234 { // accounting
235 *p = 40; // accounting type
236 p[1] = 6;
237 *(u32 *) (p + 2) = htonl((state == RADIUSSTART) ? 1 : 2);
238 p += p[1];
239 if (s)
240 {
241 *p = 44; // session ID
242 p[1] = 18;
243 sprintf(p + 2, "%08X%08X", session[s].id, session[s].opened);
244 p += p[1];
245 if (state == RADIUSSTOP)
246 { // stop
247 *p = 42; // input octets
248 p[1] = 6;
249 *(u32 *) (p + 2) = htonl(session[s].cin);
250 p += p[1];
251 *p = 43; // output octets
252 p[1] = 6;
253 *(u32 *) (p + 2) = htonl(session[s].cout);
254 p += p[1];
255 *p = 46; // session time
256 p[1] = 6;
257 *(u32 *) (p + 2) = htonl(time(NULL) - session[s].opened);
258 p += p[1];
259 *p = 47; // input packets
260 p[1] = 6;
261 *(u32 *) (p + 2) = htonl(session[s].pin);
262 p += p[1];
263 *p = 48; // output spackets
264 p[1] = 6;
265 *(u32 *) (p + 2) = htonl(session[s].pout);
266 p += p[1];
267 }
268 else
269 { // start
270 *p = 41; // delay
271 p[1] = 6;
272 *(u32 *) (p + 2) = htonl(time(NULL) - session[s].opened);
273 p += p[1];
274 }
275 }
276 }
277 if (s)
278 {
279 *p = 5; // NAS-Port
280 p[1] = 6;
281 *(u32 *) (p + 2) = htonl(s);
282 p += p[1];
283 }
284 if (s && session[s].ip)
285 {
286 *p = 8; // Framed-IP-Address
287 p[1] = 6;
288 *(u32 *) (p + 2) = htonl(session[s].ip);
289 p += p[1];
290 }
291 if (*session[s].called)
292 {
293 *p = 30; // called
294 p[1] = strlen(session[s].called) + 2;
295 strcpy(p + 2, session[s].called);
296 p += p[1];
297 }
298 if (*radius[r].calling)
299 {
300 *p = 31; // calling
301 p[1] = strlen(radius[r].calling) + 2;
302 strcpy(p + 2, radius[r].calling);
303 p += p[1];
304 }
305 else if (*session[s].calling)
306 {
307 *p = 31; // calling
308 p[1] = strlen(session[s].calling) + 2;
309 strcpy(p + 2, session[s].calling);
310 p += p[1];
311 }
312 // NAS-IP-Address
313 *p = 4;
314 p[1] = 6;
315 *(u32 *)(p + 2) = config->bind_address;
316 p += p[1];
317
318 // All AVpairs added
319 *(u16 *) (b + 2) = htons(p - b);
320 if (state != RADIUSAUTH)
321 {
322 // Build auth for accounting packet
323 char z[16] = {0};
324 char hash[16] = {0};
325 MD5_CTX ctx;
326 MD5Init(&ctx);
327 MD5Update(&ctx, b, 4);
328 MD5Update(&ctx, z, 16);
329 MD5Update(&ctx, b + 20, (p - b) - 20);
330 MD5Update(&ctx, config->radiussecret, strlen(config->radiussecret));
331 MD5Final(hash, &ctx);
332 memcpy(b + 4, hash, 16);
333 memcpy(radius[r].auth, hash, 16);
334 }
335 memset(&addr, 0, sizeof(addr));
336 addr.sin_family = AF_INET;
337 *(u32 *) & addr.sin_addr = config->radiusserver[(radius[r].try - 1) % config->numradiusservers];
338 addr.sin_port = htons((state == RADIUSAUTH) ? RADPORT : RADAPORT);
339
340 log_hex(5, "RADIUS Send", b, (p - b));
341 sendto(radfds[r & RADIUS_MASK], b, p - b, 0, (void *) &addr, sizeof(addr));
342 }
343
344 // process RADIUS response
345 void processrad(u8 *buf, int len, char socket_index)
346 {
347 u8 b[MAXCONTROL];
348 MD5_CTX ctx;
349 u16 r;
350 sessionidt s;
351 tunnelidt t = 0;
352 hasht hash;
353 u8 routes = 0;
354
355 #ifdef STAT_CALLS
356 STAT(call_processrad);
357 #endif
358 log_hex(5, "RADIUS Response", buf, len);
359 if (len < 20 || len < ntohs(*(u16 *) (buf + 2)))
360 {
361 log(1, 0, 0, 0, "Duff RADIUS response length %d\n", len);
362 return ;
363 }
364 len = ntohs(*(u16 *) (buf + 2));
365 r = socket_index | (buf[1] << RADIUS_SHIFT);
366 s = radius[r].session;
367 log(3, 0, s, session[s].tunnel, "Received %s, radius %d response for session %u\n",
368 radius_states[radius[r].state], r, s);
369 if (!s && radius[r].state != RADIUSSTOP)
370 {
371 log(1, 0, s, session[s].tunnel, " Unexpected RADIUS response\n");
372 return;
373 }
374 if (radius[r].state != RADIUSAUTH && radius[r].state != RADIUSSTART && radius[r].state != RADIUSSTOP)
375 {
376 log(1, 0, s, session[s].tunnel, " Unexpected RADIUS response\n");
377 return;
378 }
379 t = session[s].tunnel;
380 MD5Init(&ctx);
381 MD5Update(&ctx, buf, 4);
382 MD5Update(&ctx, radius[r].auth, 16);
383 MD5Update(&ctx, buf + 20, len - 20);
384 MD5Update(&ctx, config->radiussecret, strlen(config->radiussecret));
385 MD5Final(hash, &ctx);
386 do {
387 if (memcmp(hash, buf + 4, 16))
388 {
389 log(0, 0, s, session[s].tunnel, " Incorrect auth on RADIUS response\n");
390 radius[r].state = RADIUSWAIT;
391 break;
392 }
393 if ((radius[r].state == RADIUSAUTH && *buf != 2 && *buf != 3) ||
394 ((radius[r].state == RADIUSSTART || radius[r].state == RADIUSSTOP) && *buf != 5))
395 {
396 log(1, 0, s, session[s].tunnel, " Unexpected RADIUS response %d\n", *buf);
397 radius[r].state = RADIUSWAIT;
398 break;
399 }
400 if (radius[r].state == RADIUSAUTH)
401 {
402 log(4, 0, s, session[s].tunnel, " Original response is \"%s\"\n", (*buf == 2) ? "accept" : "reject");
403 // process auth response
404 if (radius[r].chap)
405 {
406 // CHAP
407 u8 *p = makeppp(b, 0, 0, t, s, PPPCHAP);
408
409 {
410 struct param_post_auth packet = { &tunnel[t], &session[s], session[s].user, (*buf == 2), PPPCHAP };
411 run_plugins(PLUGIN_POST_AUTH, &packet);
412 *buf = packet.auth_allowed ? 2 : 3;
413 }
414
415 log(3, 0, s, session[s].tunnel, " CHAP User %s authentication %s.\n", session[s].user,
416 (*buf == 2) ? "allowed" : "denied");
417 *p = (*buf == 2) ? 3 : 4; // ack/nak
418 p[1] = radius[r].id;
419 *(u16 *) (p + 2) = ntohs(4); // no message
420 tunnelsend(b, (p - b) + 4, t); // send it
421 }
422 else
423 {
424 // PAP
425 u8 *p = makeppp(b, 0, 0, t, s, PPPPAP);
426
427 {
428 struct param_post_auth packet = { &tunnel[t], &session[s], session[s].user, (*buf == 2), PPPPAP };
429 run_plugins(PLUGIN_POST_AUTH, &packet);
430 *buf = packet.auth_allowed ? 2 : 3;
431 }
432
433 log(3, 0, s, session[s].tunnel, " PAP User %s authentication %s.\n", session[s].user,
434 (*buf == 2) ? "allowed" : "denied");
435 // ack/nak
436 *p = *buf;
437 p[1] = radius[r].id;
438 *(u16 *) (p + 2) = ntohs(5);
439 p[4] = 0; // no message
440 tunnelsend(b, (p - b) + 5, t); // send it
441 }
442
443 if (*buf == 2)
444 {
445 // Login successful
446 // Extract IP, routes, etc
447 u8 *p = buf + 20;
448 u8 *e = buf + len;
449 for (p = buf + 20; p < e && p[1]; p += p[1])
450 {
451 if (*p == 8)
452 {
453 // Statically assigned address
454 log(3, 0, s, session[s].tunnel, " Radius reply contains IP address %s\n", inet_toa(*(u32 *) (p + 2)));
455 session[s].ip = ntohl(*(u32 *) (p + 2));
456 }
457 else if (*p == 135)
458 {
459 // DNS address
460 log(3, 0, s, session[s].tunnel, " Radius reply contains primary DNS address %s\n", inet_toa(ntohl(*(u32 *) (p + 2))));
461 session[s].dns1 = ntohl(*(u32 *) (p + 2));
462 }
463 else if (*p == 136)
464 {
465 // DNS address
466 log(3, 0, s, session[s].tunnel, " Radius reply contains secondary DNS address %s\n", inet_toa(ntohl(*(u32 *) (p + 2))));
467 session[s].dns2 = ntohl(*(u32 *) (p + 2));
468 }
469 else if (*p == 22)
470 {
471 // framed-route
472 ipt ip = 0, mask = 0;
473 u8 u = 0;
474 u8 bits = 0;
475 u8 *n = p + 2;
476 u8 *e = p + p[1];
477 while (n < e && (isdigit(*n) || *n == '.'))
478 {
479 if (*n == '.')
480 {
481 ip = (ip << 8) + u;
482 u = 0;
483 }
484 else
485 u = u * 10 + *n - '0';
486 n++;
487 }
488 ip = (ip << 8) + u;
489 if (*n == '/')
490 {
491 n++;
492 while (n < e && isdigit(*n))
493 bits = bits * 10 + *n++ - '0';
494 mask = (( -1) << (32 - bits));
495 }
496 else if ((ip >> 24) < 128)
497 mask = 0xFF0000;
498 else if ((ip >> 24) < 192)
499 mask = 0xFFFF0000;
500 else
501 mask = 0xFFFFFF00;
502 if (routes == MAXROUTE)
503 {
504 log(1, 0, s, session[s].tunnel, " Too many routes\n");
505 }
506 else
507 {
508 char *ips, *masks;
509 ips = strdup(inet_toa(ip));
510 masks = strdup(inet_toa(mask));
511 log(3, 0, s, session[s].tunnel, " Radius reply contains route for %s/%s\n", ips, masks);
512 free(ips);
513 free(masks);
514 session[s].route[routes].ip = ip;
515 session[s].route[routes].mask = mask;
516 routes++;
517 }
518 }
519 else if (*p == 26)
520 {
521 // Vendor-Specific Attribute
522 int vendor = ntohl(*(int *)(p + 2));
523 char attrib = *(p + 6);
524 char attrib_length = *(p + 7) - 2;
525 log(3, 0, s, session[s].tunnel, " Radius reply contains Vendor-Specific. Vendor=%d Attrib=%d Length=%d\n", vendor, attrib, attrib_length);
526 if (attrib_length == 0) continue;
527 if (attrib != 1)
528 log(3, 0, s, session[s].tunnel, " Unknown vendor-specific\n");
529 else
530 {
531 char *avpair, *value, *key, *newp;
532 avpair = key = calloc(attrib_length + 1, 1);
533 memcpy(avpair, p + 8, attrib_length);
534 log(3, 0, s, session[s].tunnel, " Cisco-Avpair value: %s\n", avpair);
535 do {
536 value = strchr(key, '=');
537 if (!value) break;
538 *value++ = 0;
539
540 // Trim quotes off reply string
541 if (*value == '\'' || *value == '\"')
542 {
543 char *x;
544 value++;
545 x = value + strlen(value) - 1;
546 if (*x == '\'' || *x == '\"')
547 *x = 0;
548 }
549
550 // Run hooks
551 newp = strchr(value, ',');
552 if (newp) *newp++ = 0;
553 {
554 struct param_radius_response p = { &tunnel[session[s].tunnel], &session[s], key, value };
555 run_plugins(PLUGIN_RADIUS_RESPONSE, &p);
556 }
557 key = newp;
558 } while (newp);
559 free(avpair);
560 }
561 }
562 }
563 }
564 else if (*buf == 3)
565 {
566 log(2, 0, s, session[s].tunnel, " Authentication denied for %s\n", session[s].user);
567 break;
568 }
569
570 // Check for Assign-IP-Address
571 if (!session[s].ip || session[s].ip == 0xFFFFFFFE)
572 {
573 assign_ip_address(s);
574 if (session[s].ip)
575 log(3, 0, s, t, " No IP allocated by radius. Assigned %s from pool\n",
576 inet_toa(htonl(session[s].ip)));
577 else
578 log(0, 0, s, t, " No IP allocated by radius. The IP address pool is FULL!\n");
579 }
580 if (!session[s].dns1 && config->default_dns1)
581 {
582 session[s].dns1 = htonl(config->default_dns1);
583 log(3, 0, s, t, " Sending dns1 = %s\n", inet_toa(config->default_dns1));
584 }
585 if (!session[s].dns2 && config->default_dns2)
586 {
587 session[s].dns2 = htonl(config->default_dns2);
588 log(3, 0, s, t, " Sending dns2 = %s\n", inet_toa(config->default_dns2));
589 }
590
591 if (session[s].ip)
592 {
593 // Valid Session, set it up
594 session[s].sid = 0;
595 sessionsetup(t, s, routes);
596 }
597 else
598 {
599 log(0, 0, s, t, " End of processrad(), but no valid session exists.\n");
600 sessionkill(s, "Can't create valid session");
601 }
602 }
603 else
604 {
605 log(3, 0, s, t, " RADIUS response in state %s\n", radius_states[radius[r].state]);
606 }
607 } while (0);
608
609 // finished with RADIUS
610 radiusclear(r, s);
611 }
612
613 // Send a retry for RADIUS/CHAP message
614 void radiusretry(u16 r)
615 {
616 sessionidt s = radius[r].session;
617 tunnelidt t = 0;
618 #ifdef STAT_CALLS
619 STAT(call_radiusretry);
620 #endif
621 if (s)
622 t = session[s].tunnel;
623 radius[r].retry = backoff(radius[r].try + 1);
624 switch (radius[r].state)
625 {
626 case RADIUSCHAP: // sending CHAP down PPP
627 sendchap(t, s);
628 break;
629 case RADIUSIPCP:
630 sendipcp(t, s); // send IPCP
631 break;
632 case RADIUSAUTH: // sending auth to RADIUS server
633 radiussend(r, RADIUSAUTH);
634 break;
635 case RADIUSSTART: // sending start accounting to RADIUS server
636 radiussend(r, RADIUSSTART);
637 break;
638 case RADIUSSTOP: // sending stop accounting to RADIUS server
639 radiussend(r, RADIUSSTOP);
640 break;
641 default:
642 case RADIUSNULL: // Not in use
643 case RADIUSWAIT: // waiting timeout before available, in case delayed reply from RADIUS server
644 // free up RADIUS task
645 radiusclear(r, s);
646 log(3, 0, s, session[s].tunnel, "Freeing up radius session %d\n", r);
647 break;
648 }
649 }
650
651 void radius_clean()
652 {
653 int i;
654
655 log(1, 0, 0, 0, "Cleaning radius session array\n");
656
657 for (i = 1; i < MAXRADIUS; i++)
658 {
659 if (radius[i].retry == 0
660 || !session[radius[i].session].opened
661 || session[radius[i].session].die
662 || session[radius[i].session].tunnel == 0)
663 radiusclear(i, 0);
664 }
665 }