1 |
/* $MidnightBSD$ */ |
2 |
/* |
3 |
* Copyright 1999 Internet Business Solutions Ltd., Switzerland |
4 |
* All rights reserved. |
5 |
* |
6 |
* Redistribution and use in source and binary forms, with or without |
7 |
* modification, are permitted provided that the following conditions |
8 |
* are met: |
9 |
* 1. Redistributions of source code must retain the above copyright |
10 |
* notice, this list of conditions and the following disclaimer. |
11 |
* 2. Redistributions in binary form must reproduce the above copyright |
12 |
* notice, this list of conditions and the following disclaimer in the |
13 |
* documentation and/or other materials provided with the distribution. |
14 |
* |
15 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
16 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
19 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
21 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 |
* SUCH DAMAGE. |
26 |
* |
27 |
* $FreeBSD: stable/10/usr.sbin/ppp/radius.c 241844 2012-10-22 03:00:37Z eadler $ |
28 |
* |
29 |
*/ |
30 |
|
31 |
#include <stdint.h> |
32 |
#include <sys/param.h> |
33 |
|
34 |
#include <sys/select.h> |
35 |
#include <sys/socket.h> |
36 |
#include <netinet/in_systm.h> |
37 |
#include <netinet/in.h> |
38 |
#include <netinet/ip.h> |
39 |
#include <arpa/inet.h> |
40 |
#include <sys/un.h> |
41 |
#include <net/route.h> |
42 |
|
43 |
#ifdef LOCALRAD |
44 |
#include "radlib.h" |
45 |
#include "radlib_vs.h" |
46 |
#else |
47 |
#include <radlib.h> |
48 |
#include <radlib_vs.h> |
49 |
#endif |
50 |
|
51 |
#include <errno.h> |
52 |
#ifndef NODES |
53 |
#include <md5.h> |
54 |
#endif |
55 |
#include <stdarg.h> |
56 |
#include <stdio.h> |
57 |
#include <stdlib.h> |
58 |
#include <string.h> |
59 |
#include <sys/time.h> |
60 |
#include <termios.h> |
61 |
#include <unistd.h> |
62 |
#include <netdb.h> |
63 |
|
64 |
#include "layer.h" |
65 |
#include "defs.h" |
66 |
#include "log.h" |
67 |
#include "descriptor.h" |
68 |
#include "prompt.h" |
69 |
#include "timer.h" |
70 |
#include "fsm.h" |
71 |
#include "iplist.h" |
72 |
#include "slcompress.h" |
73 |
#include "throughput.h" |
74 |
#include "lqr.h" |
75 |
#include "hdlc.h" |
76 |
#include "mbuf.h" |
77 |
#include "ncpaddr.h" |
78 |
#include "ip.h" |
79 |
#include "ipcp.h" |
80 |
#include "ipv6cp.h" |
81 |
#include "route.h" |
82 |
#include "command.h" |
83 |
#include "filter.h" |
84 |
#include "lcp.h" |
85 |
#include "ccp.h" |
86 |
#include "link.h" |
87 |
#include "mp.h" |
88 |
#include "radius.h" |
89 |
#include "auth.h" |
90 |
#include "async.h" |
91 |
#include "physical.h" |
92 |
#include "chat.h" |
93 |
#include "cbcp.h" |
94 |
#include "chap.h" |
95 |
#include "datalink.h" |
96 |
#include "ncp.h" |
97 |
#include "bundle.h" |
98 |
#include "proto.h" |
99 |
#include "iface.h" |
100 |
|
101 |
#ifndef NODES |
102 |
struct mschap_response { |
103 |
u_char ident; |
104 |
u_char flags; |
105 |
u_char lm_response[24]; |
106 |
u_char nt_response[24]; |
107 |
}; |
108 |
|
109 |
struct mschap2_response { |
110 |
u_char ident; |
111 |
u_char flags; |
112 |
u_char pchallenge[16]; |
113 |
u_char reserved[8]; |
114 |
u_char response[24]; |
115 |
}; |
116 |
|
117 |
#define AUTH_LEN 16 |
118 |
#define SALT_LEN 2 |
119 |
#endif |
120 |
|
121 |
static const char * |
122 |
radius_policyname(int policy) |
123 |
{ |
124 |
switch(policy) { |
125 |
case MPPE_POLICY_ALLOWED: |
126 |
return "Allowed"; |
127 |
case MPPE_POLICY_REQUIRED: |
128 |
return "Required"; |
129 |
} |
130 |
return NumStr(policy, NULL, 0); |
131 |
} |
132 |
|
133 |
static const char * |
134 |
radius_typesname(int types) |
135 |
{ |
136 |
switch(types) { |
137 |
case MPPE_TYPE_40BIT: |
138 |
return "40 bit"; |
139 |
case MPPE_TYPE_128BIT: |
140 |
return "128 bit"; |
141 |
case MPPE_TYPE_40BIT|MPPE_TYPE_128BIT: |
142 |
return "40 or 128 bit"; |
143 |
} |
144 |
return NumStr(types, NULL, 0); |
145 |
} |
146 |
|
147 |
#ifndef NODES |
148 |
static void |
149 |
demangle(struct radius *r, const void *mangled, size_t mlen, |
150 |
char **buf, size_t *len) |
151 |
{ |
152 |
char R[AUTH_LEN]; /* variable names as per rfc2548 */ |
153 |
const char *S; |
154 |
u_char b[16]; |
155 |
const u_char *A, *C; |
156 |
MD5_CTX Context; |
157 |
int Slen, i, Clen, Ppos; |
158 |
u_char *P; |
159 |
|
160 |
if (mlen % 16 != SALT_LEN) { |
161 |
log_Printf(LogWARN, "Cannot interpret mangled data of length %ld\n", |
162 |
(u_long)mlen); |
163 |
*buf = NULL; |
164 |
*len = 0; |
165 |
return; |
166 |
} |
167 |
|
168 |
/* We need the RADIUS Request-Authenticator */ |
169 |
if (rad_request_authenticator(r->cx.rad, R, sizeof R) != AUTH_LEN) { |
170 |
log_Printf(LogWARN, "Cannot obtain the RADIUS request authenticator\n"); |
171 |
*buf = NULL; |
172 |
*len = 0; |
173 |
return; |
174 |
} |
175 |
|
176 |
A = (const u_char *)mangled; /* Salt comes first */ |
177 |
C = (const u_char *)mangled + SALT_LEN; /* Then the ciphertext */ |
178 |
Clen = mlen - SALT_LEN; |
179 |
S = rad_server_secret(r->cx.rad); /* We need the RADIUS secret */ |
180 |
Slen = strlen(S); |
181 |
P = alloca(Clen); /* We derive our plaintext */ |
182 |
|
183 |
MD5Init(&Context); |
184 |
MD5Update(&Context, S, Slen); |
185 |
MD5Update(&Context, R, AUTH_LEN); |
186 |
MD5Update(&Context, A, SALT_LEN); |
187 |
MD5Final(b, &Context); |
188 |
Ppos = 0; |
189 |
|
190 |
while (Clen) { |
191 |
Clen -= 16; |
192 |
|
193 |
for (i = 0; i < 16; i++) |
194 |
P[Ppos++] = C[i] ^ b[i]; |
195 |
|
196 |
if (Clen) { |
197 |
MD5Init(&Context); |
198 |
MD5Update(&Context, S, Slen); |
199 |
MD5Update(&Context, C, 16); |
200 |
MD5Final(b, &Context); |
201 |
} |
202 |
|
203 |
C += 16; |
204 |
} |
205 |
|
206 |
/* |
207 |
* The resulting plain text consists of a one-byte length, the text and |
208 |
* maybe some padding. |
209 |
*/ |
210 |
*len = *P; |
211 |
if (*len > mlen - 1) { |
212 |
log_Printf(LogWARN, "Mangled data seems to be garbage\n"); |
213 |
*buf = NULL; |
214 |
*len = 0; |
215 |
return; |
216 |
} |
217 |
|
218 |
if ((*buf = malloc(*len)) == NULL) { |
219 |
log_Printf(LogWARN, "demangle: Out of memory (%lu bytes)\n", (u_long)*len); |
220 |
*len = 0; |
221 |
} else |
222 |
memcpy(*buf, P + 1, *len); |
223 |
} |
224 |
#endif |
225 |
|
226 |
/* XXX: This should go into librarius. */ |
227 |
#ifndef NOINET6 |
228 |
static uint8_t * |
229 |
rad_cvt_ipv6prefix(const void *data, size_t len) |
230 |
{ |
231 |
const size_t ipv6len = sizeof(struct in6_addr) + 2; |
232 |
uint8_t *s; |
233 |
|
234 |
if (len > ipv6len) |
235 |
return NULL; |
236 |
s = malloc(ipv6len); |
237 |
if (s != NULL) { |
238 |
memset(s, 0, ipv6len); |
239 |
memcpy(s, data, len); |
240 |
} |
241 |
return s; |
242 |
} |
243 |
#endif |
244 |
|
245 |
/* |
246 |
* rad_continue_send_request() has given us `got' (non-zero). Deal with it. |
247 |
*/ |
248 |
static void |
249 |
radius_Process(struct radius *r, int got) |
250 |
{ |
251 |
char *argv[MAXARGS], *nuke; |
252 |
struct bundle *bundle; |
253 |
int argc, addrs, res, width; |
254 |
size_t len; |
255 |
struct ncprange dest; |
256 |
struct ncpaddr gw; |
257 |
const void *data; |
258 |
const char *stype; |
259 |
u_int32_t ipaddr, vendor; |
260 |
struct in_addr ip; |
261 |
#ifndef NOINET6 |
262 |
uint8_t ipv6addr[INET6_ADDRSTRLEN]; |
263 |
struct in6_addr ip6; |
264 |
#endif |
265 |
|
266 |
r->cx.fd = -1; /* Stop select()ing */ |
267 |
stype = r->cx.auth ? "auth" : "acct"; |
268 |
|
269 |
switch (got) { |
270 |
case RAD_ACCESS_ACCEPT: |
271 |
log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE, |
272 |
"Radius(%s): ACCEPT received\n", stype); |
273 |
if (!r->cx.auth) { |
274 |
rad_close(r->cx.rad); |
275 |
return; |
276 |
} |
277 |
break; |
278 |
|
279 |
case RAD_ACCESS_REJECT: |
280 |
log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE, |
281 |
"Radius(%s): REJECT received\n", stype); |
282 |
if (!r->cx.auth) { |
283 |
rad_close(r->cx.rad); |
284 |
return; |
285 |
} |
286 |
break; |
287 |
|
288 |
case RAD_ACCESS_CHALLENGE: |
289 |
/* we can't deal with this (for now) ! */ |
290 |
log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE, |
291 |
"Radius: CHALLENGE received (can't handle yet)\n"); |
292 |
if (r->cx.auth) |
293 |
auth_Failure(r->cx.auth); |
294 |
rad_close(r->cx.rad); |
295 |
return; |
296 |
|
297 |
case RAD_ACCOUNTING_RESPONSE: |
298 |
/* |
299 |
* It's probably not ideal to log this at PHASE level as we'll see |
300 |
* too much stuff going to the log when ``set rad_alive'' is used. |
301 |
* So we differ from older behaviour (ppp version 3.1 and before) |
302 |
* and just log accounting responses to LogRADIUS. |
303 |
*/ |
304 |
log_Printf(LogRADIUS, "Radius(%s): Accounting response received\n", |
305 |
stype); |
306 |
if (r->cx.auth) |
307 |
auth_Failure(r->cx.auth); /* unexpected !!! */ |
308 |
|
309 |
/* No further processing for accounting requests, please */ |
310 |
rad_close(r->cx.rad); |
311 |
return; |
312 |
|
313 |
case -1: |
314 |
log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE, |
315 |
"radius(%s): %s\n", stype, rad_strerror(r->cx.rad)); |
316 |
if (r->cx.auth) |
317 |
auth_Failure(r->cx.auth); |
318 |
rad_close(r->cx.rad); |
319 |
return; |
320 |
|
321 |
default: |
322 |
log_Printf(LogERROR, "rad_send_request(%s): Failed %d: %s\n", stype, |
323 |
got, rad_strerror(r->cx.rad)); |
324 |
if (r->cx.auth) |
325 |
auth_Failure(r->cx.auth); |
326 |
rad_close(r->cx.rad); |
327 |
return; |
328 |
} |
329 |
|
330 |
/* Let's see what we've got in our reply */ |
331 |
r->ip.s_addr = r->mask.s_addr = INADDR_NONE; |
332 |
r->mtu = 0; |
333 |
r->vj = 0; |
334 |
while ((res = rad_get_attr(r->cx.rad, &data, &len)) > 0) { |
335 |
switch (res) { |
336 |
case RAD_FRAMED_IP_ADDRESS: |
337 |
r->ip = rad_cvt_addr(data); |
338 |
log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE, |
339 |
" IP %s\n", inet_ntoa(r->ip)); |
340 |
break; |
341 |
|
342 |
case RAD_FILTER_ID: |
343 |
free(r->filterid); |
344 |
if ((r->filterid = rad_cvt_string(data, len)) == NULL) { |
345 |
log_Printf(LogERROR, "rad_cvt_string: %s\n", rad_strerror(r->cx.rad)); |
346 |
auth_Failure(r->cx.auth); |
347 |
rad_close(r->cx.rad); |
348 |
return; |
349 |
} |
350 |
log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE, |
351 |
" Filter \"%s\"\n", r->filterid); |
352 |
break; |
353 |
|
354 |
case RAD_SESSION_TIMEOUT: |
355 |
r->sessiontime = rad_cvt_int(data); |
356 |
log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE, |
357 |
" Session-Timeout %lu\n", r->sessiontime); |
358 |
break; |
359 |
|
360 |
case RAD_FRAMED_IP_NETMASK: |
361 |
r->mask = rad_cvt_addr(data); |
362 |
log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE, |
363 |
" Netmask %s\n", inet_ntoa(r->mask)); |
364 |
break; |
365 |
|
366 |
case RAD_FRAMED_MTU: |
367 |
r->mtu = rad_cvt_int(data); |
368 |
log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE, |
369 |
" MTU %lu\n", r->mtu); |
370 |
break; |
371 |
|
372 |
case RAD_FRAMED_ROUTING: |
373 |
/* Disabled for now - should we automatically set up some filters ? */ |
374 |
/* rad_cvt_int(data); */ |
375 |
/* bit 1 = Send routing packets */ |
376 |
/* bit 2 = Receive routing packets */ |
377 |
break; |
378 |
|
379 |
case RAD_FRAMED_COMPRESSION: |
380 |
r->vj = rad_cvt_int(data) == 1 ? 1 : 0; |
381 |
log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE, |
382 |
" VJ %sabled\n", r->vj ? "en" : "dis"); |
383 |
break; |
384 |
|
385 |
case RAD_FRAMED_ROUTE: |
386 |
/* |
387 |
* We expect a string of the format ``dest[/bits] gw [metrics]'' |
388 |
* Any specified metrics are ignored. MYADDR and HISADDR are |
389 |
* understood for ``dest'' and ``gw'' and ``0.0.0.0'' is the same |
390 |
* as ``HISADDR''. |
391 |
*/ |
392 |
|
393 |
if ((nuke = rad_cvt_string(data, len)) == NULL) { |
394 |
log_Printf(LogERROR, "rad_cvt_string: %s\n", rad_strerror(r->cx.rad)); |
395 |
auth_Failure(r->cx.auth); |
396 |
rad_close(r->cx.rad); |
397 |
return; |
398 |
} |
399 |
|
400 |
log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE, |
401 |
" Route: %s\n", nuke); |
402 |
bundle = r->cx.auth->physical->dl->bundle; |
403 |
ip.s_addr = INADDR_ANY; |
404 |
ncpaddr_setip4(&gw, ip); |
405 |
ncprange_setip4host(&dest, ip); |
406 |
argc = command_Interpret(nuke, strlen(nuke), argv); |
407 |
if (argc < 0) |
408 |
log_Printf(LogWARN, "radius: %s: Syntax error\n", |
409 |
argc == 1 ? argv[0] : "\"\""); |
410 |
else if (argc < 2) |
411 |
log_Printf(LogWARN, "radius: %s: Invalid route\n", |
412 |
argc == 1 ? argv[0] : "\"\""); |
413 |
else if ((strcasecmp(argv[0], "default") != 0 && |
414 |
!ncprange_aton(&dest, &bundle->ncp, argv[0])) || |
415 |
!ncpaddr_aton(&gw, &bundle->ncp, argv[1])) |
416 |
log_Printf(LogWARN, "radius: %s %s: Invalid route\n", |
417 |
argv[0], argv[1]); |
418 |
else { |
419 |
ncprange_getwidth(&dest, &width); |
420 |
if (width == 32 && strchr(argv[0], '/') == NULL) { |
421 |
/* No mask specified - use the natural mask */ |
422 |
ncprange_getip4addr(&dest, &ip); |
423 |
ncprange_setip4mask(&dest, addr2mask(ip)); |
424 |
} |
425 |
addrs = 0; |
426 |
|
427 |
if (!strncasecmp(argv[0], "HISADDR", 7)) |
428 |
addrs = ROUTE_DSTHISADDR; |
429 |
else if (!strncasecmp(argv[0], "MYADDR", 6)) |
430 |
addrs = ROUTE_DSTMYADDR; |
431 |
|
432 |
if (ncpaddr_getip4addr(&gw, &ipaddr) && ipaddr == INADDR_ANY) { |
433 |
addrs |= ROUTE_GWHISADDR; |
434 |
ncpaddr_setip4(&gw, bundle->ncp.ipcp.peer_ip); |
435 |
} else if (strcasecmp(argv[1], "HISADDR") == 0) |
436 |
addrs |= ROUTE_GWHISADDR; |
437 |
|
438 |
route_Add(&r->routes, addrs, &dest, &gw); |
439 |
} |
440 |
free(nuke); |
441 |
break; |
442 |
|
443 |
case RAD_REPLY_MESSAGE: |
444 |
free(r->repstr); |
445 |
if ((r->repstr = rad_cvt_string(data, len)) == NULL) { |
446 |
log_Printf(LogERROR, "rad_cvt_string: %s\n", rad_strerror(r->cx.rad)); |
447 |
auth_Failure(r->cx.auth); |
448 |
rad_close(r->cx.rad); |
449 |
return; |
450 |
} |
451 |
log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE, |
452 |
" Reply-Message \"%s\"\n", r->repstr); |
453 |
break; |
454 |
|
455 |
#ifndef NOINET6 |
456 |
case RAD_FRAMED_IPV6_PREFIX: |
457 |
free(r->ipv6prefix); |
458 |
if ((r->ipv6prefix = rad_cvt_ipv6prefix(data, len)) == NULL) { |
459 |
log_Printf(LogERROR, "rad_cvt_ipv6prefix: %s\n", |
460 |
"Malformed attribute in response"); |
461 |
auth_Failure(r->cx.auth); |
462 |
rad_close(r->cx.rad); |
463 |
return; |
464 |
} |
465 |
inet_ntop(AF_INET6, &r->ipv6prefix[2], ipv6addr, sizeof(ipv6addr)); |
466 |
log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE, |
467 |
" IPv6 %s/%d\n", ipv6addr, r->ipv6prefix[1]); |
468 |
break; |
469 |
|
470 |
case RAD_FRAMED_IPV6_ROUTE: |
471 |
/* |
472 |
* We expect a string of the format ``dest[/bits] gw [metrics]'' |
473 |
* Any specified metrics are ignored. MYADDR6 and HISADDR6 are |
474 |
* understood for ``dest'' and ``gw'' and ``::'' is the same |
475 |
* as ``HISADDR6''. |
476 |
*/ |
477 |
|
478 |
if ((nuke = rad_cvt_string(data, len)) == NULL) { |
479 |
log_Printf(LogERROR, "rad_cvt_string: %s\n", rad_strerror(r->cx.rad)); |
480 |
auth_Failure(r->cx.auth); |
481 |
rad_close(r->cx.rad); |
482 |
return; |
483 |
} |
484 |
|
485 |
log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE, |
486 |
" IPv6 Route: %s\n", nuke); |
487 |
bundle = r->cx.auth->physical->dl->bundle; |
488 |
ncpaddr_setip6(&gw, &in6addr_any); |
489 |
ncprange_set(&dest, &gw, 0); |
490 |
argc = command_Interpret(nuke, strlen(nuke), argv); |
491 |
if (argc < 0) |
492 |
log_Printf(LogWARN, "radius: %s: Syntax error\n", |
493 |
argc == 1 ? argv[0] : "\"\""); |
494 |
else if (argc < 2) |
495 |
log_Printf(LogWARN, "radius: %s: Invalid route\n", |
496 |
argc == 1 ? argv[0] : "\"\""); |
497 |
else if ((strcasecmp(argv[0], "default") != 0 && |
498 |
!ncprange_aton(&dest, &bundle->ncp, argv[0])) || |
499 |
!ncpaddr_aton(&gw, &bundle->ncp, argv[1])) |
500 |
log_Printf(LogWARN, "radius: %s %s: Invalid route\n", |
501 |
argv[0], argv[1]); |
502 |
else { |
503 |
addrs = 0; |
504 |
|
505 |
if (!strncasecmp(argv[0], "HISADDR6", 8)) |
506 |
addrs = ROUTE_DSTHISADDR6; |
507 |
else if (!strncasecmp(argv[0], "MYADDR6", 7)) |
508 |
addrs = ROUTE_DSTMYADDR6; |
509 |
|
510 |
if (ncpaddr_getip6(&gw, &ip6) && IN6_IS_ADDR_UNSPECIFIED(&ip6)) { |
511 |
addrs |= ROUTE_GWHISADDR6; |
512 |
ncpaddr_copy(&gw, &bundle->ncp.ipv6cp.hisaddr); |
513 |
} else if (strcasecmp(argv[1], "HISADDR6") == 0) |
514 |
addrs |= ROUTE_GWHISADDR6; |
515 |
|
516 |
route_Add(&r->ipv6routes, addrs, &dest, &gw); |
517 |
} |
518 |
free(nuke); |
519 |
break; |
520 |
#endif |
521 |
|
522 |
case RAD_VENDOR_SPECIFIC: |
523 |
if ((res = rad_get_vendor_attr(&vendor, &data, &len)) <= 0) { |
524 |
log_Printf(LogERROR, "rad_get_vendor_attr: %s (failing!)\n", |
525 |
rad_strerror(r->cx.rad)); |
526 |
auth_Failure(r->cx.auth); |
527 |
rad_close(r->cx.rad); |
528 |
return; |
529 |
} |
530 |
|
531 |
switch (vendor) { |
532 |
case RAD_VENDOR_MICROSOFT: |
533 |
switch (res) { |
534 |
#ifndef NODES |
535 |
case RAD_MICROSOFT_MS_CHAP_ERROR: |
536 |
free(r->errstr); |
537 |
if (len == 0) |
538 |
r->errstr = NULL; |
539 |
else { |
540 |
if (len < 3 || ((const char *)data)[1] != '=') { |
541 |
/* |
542 |
* Only point at the String field if we don't think the |
543 |
* peer has misformatted the response. |
544 |
*/ |
545 |
data = (const char *)data + 1; |
546 |
len--; |
547 |
} else |
548 |
log_Printf(LogWARN, "Warning: The MS-CHAP-Error " |
549 |
"attribute is mis-formatted. Compensating\n"); |
550 |
if ((r->errstr = rad_cvt_string((const char *)data, |
551 |
len)) == NULL) { |
552 |
log_Printf(LogERROR, "rad_cvt_string: %s\n", |
553 |
rad_strerror(r->cx.rad)); |
554 |
auth_Failure(r->cx.auth); |
555 |
rad_close(r->cx.rad); |
556 |
return; |
557 |
} |
558 |
log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE, |
559 |
" MS-CHAP-Error \"%s\"\n", r->errstr); |
560 |
} |
561 |
break; |
562 |
|
563 |
case RAD_MICROSOFT_MS_CHAP2_SUCCESS: |
564 |
free(r->msrepstr); |
565 |
if (len == 0) |
566 |
r->msrepstr = NULL; |
567 |
else { |
568 |
if (len < 3 || ((const char *)data)[1] != '=') { |
569 |
/* |
570 |
* Only point at the String field if we don't think the |
571 |
* peer has misformatted the response. |
572 |
*/ |
573 |
data = (const char *)data + 1; |
574 |
len--; |
575 |
} else |
576 |
log_Printf(LogWARN, "Warning: The MS-CHAP2-Success " |
577 |
"attribute is mis-formatted. Compensating\n"); |
578 |
if ((r->msrepstr = rad_cvt_string((const char *)data, |
579 |
len)) == NULL) { |
580 |
log_Printf(LogERROR, "rad_cvt_string: %s\n", |
581 |
rad_strerror(r->cx.rad)); |
582 |
auth_Failure(r->cx.auth); |
583 |
rad_close(r->cx.rad); |
584 |
return; |
585 |
} |
586 |
log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE, |
587 |
" MS-CHAP2-Success \"%s\"\n", r->msrepstr); |
588 |
} |
589 |
break; |
590 |
|
591 |
case RAD_MICROSOFT_MS_MPPE_ENCRYPTION_POLICY: |
592 |
r->mppe.policy = rad_cvt_int(data); |
593 |
log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE, |
594 |
" MS-MPPE-Encryption-Policy %s\n", |
595 |
radius_policyname(r->mppe.policy)); |
596 |
break; |
597 |
|
598 |
case RAD_MICROSOFT_MS_MPPE_ENCRYPTION_TYPES: |
599 |
r->mppe.types = rad_cvt_int(data); |
600 |
log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE, |
601 |
" MS-MPPE-Encryption-Types %s\n", |
602 |
radius_typesname(r->mppe.types)); |
603 |
break; |
604 |
|
605 |
case RAD_MICROSOFT_MS_MPPE_RECV_KEY: |
606 |
free(r->mppe.recvkey); |
607 |
demangle(r, data, len, &r->mppe.recvkey, &r->mppe.recvkeylen); |
608 |
log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE, |
609 |
" MS-MPPE-Recv-Key ********\n"); |
610 |
break; |
611 |
|
612 |
case RAD_MICROSOFT_MS_MPPE_SEND_KEY: |
613 |
demangle(r, data, len, &r->mppe.sendkey, &r->mppe.sendkeylen); |
614 |
log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE, |
615 |
" MS-MPPE-Send-Key ********\n"); |
616 |
break; |
617 |
#endif |
618 |
|
619 |
default: |
620 |
log_Printf(LogDEBUG, "Dropping MICROSOFT vendor specific " |
621 |
"RADIUS attribute %d\n", res); |
622 |
break; |
623 |
} |
624 |
break; |
625 |
|
626 |
default: |
627 |
log_Printf(LogDEBUG, "Dropping vendor %lu RADIUS attribute %d\n", |
628 |
(unsigned long)vendor, res); |
629 |
break; |
630 |
} |
631 |
break; |
632 |
|
633 |
default: |
634 |
log_Printf(LogDEBUG, "Dropping RADIUS attribute %d\n", res); |
635 |
break; |
636 |
} |
637 |
} |
638 |
|
639 |
if (res == -1) { |
640 |
log_Printf(LogERROR, "rad_get_attr: %s (failing!)\n", |
641 |
rad_strerror(r->cx.rad)); |
642 |
auth_Failure(r->cx.auth); |
643 |
} else if (got == RAD_ACCESS_REJECT) |
644 |
auth_Failure(r->cx.auth); |
645 |
else { |
646 |
r->valid = 1; |
647 |
auth_Success(r->cx.auth); |
648 |
} |
649 |
rad_close(r->cx.rad); |
650 |
} |
651 |
|
652 |
/* |
653 |
* We've either timed out or select()ed on the read descriptor |
654 |
*/ |
655 |
static void |
656 |
radius_Continue(struct radius *r, int sel) |
657 |
{ |
658 |
struct timeval tv; |
659 |
int got; |
660 |
|
661 |
timer_Stop(&r->cx.timer); |
662 |
if ((got = rad_continue_send_request(r->cx.rad, sel, &r->cx.fd, &tv)) == 0) { |
663 |
log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE, |
664 |
"Radius: Request re-sent\n"); |
665 |
r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS; |
666 |
timer_Start(&r->cx.timer); |
667 |
return; |
668 |
} |
669 |
|
670 |
radius_Process(r, got); |
671 |
} |
672 |
|
673 |
/* |
674 |
* Time to call rad_continue_send_request() - timed out. |
675 |
*/ |
676 |
static void |
677 |
radius_Timeout(void *v) |
678 |
{ |
679 |
radius_Continue((struct radius *)v, 0); |
680 |
} |
681 |
|
682 |
/* |
683 |
* Time to call rad_continue_send_request() - something to read. |
684 |
*/ |
685 |
static void |
686 |
radius_Read(struct fdescriptor *d, struct bundle *bundle __unused, |
687 |
const fd_set *fdset __unused) |
688 |
{ |
689 |
radius_Continue(descriptor2radius(d), 1); |
690 |
} |
691 |
|
692 |
/* |
693 |
* Flush any pending transactions |
694 |
*/ |
695 |
void |
696 |
radius_Flush(struct radius *r) |
697 |
{ |
698 |
struct timeval tv; |
699 |
fd_set s; |
700 |
|
701 |
while (r->cx.fd != -1) { |
702 |
FD_ZERO(&s); |
703 |
FD_SET(r->cx.fd, &s); |
704 |
tv.tv_sec = 0; |
705 |
tv.tv_usec = TICKUNIT; |
706 |
select(r->cx.fd + 1, &s, NULL, NULL, &tv); |
707 |
radius_Continue(r, 1); |
708 |
} |
709 |
} |
710 |
|
711 |
/* |
712 |
* Behave as a struct fdescriptor (descriptor.h) |
713 |
*/ |
714 |
static int |
715 |
radius_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w __unused, |
716 |
fd_set *e __unused, int *n) |
717 |
{ |
718 |
struct radius *rad = descriptor2radius(d); |
719 |
|
720 |
if (r && rad->cx.fd != -1) { |
721 |
FD_SET(rad->cx.fd, r); |
722 |
if (*n < rad->cx.fd + 1) |
723 |
*n = rad->cx.fd + 1; |
724 |
log_Printf(LogTIMER, "Radius: fdset(r) %d\n", rad->cx.fd); |
725 |
return 1; |
726 |
} |
727 |
|
728 |
return 0; |
729 |
} |
730 |
|
731 |
/* |
732 |
* Behave as a struct fdescriptor (descriptor.h) |
733 |
*/ |
734 |
static int |
735 |
radius_IsSet(struct fdescriptor *d, const fd_set *fdset) |
736 |
{ |
737 |
struct radius *r = descriptor2radius(d); |
738 |
|
739 |
return r && r->cx.fd != -1 && FD_ISSET(r->cx.fd, fdset); |
740 |
} |
741 |
|
742 |
/* |
743 |
* Behave as a struct fdescriptor (descriptor.h) |
744 |
*/ |
745 |
static int |
746 |
radius_Write(struct fdescriptor *d __unused, struct bundle *bundle __unused, |
747 |
const fd_set *fdset __unused) |
748 |
{ |
749 |
/* We never want to write here ! */ |
750 |
log_Printf(LogALERT, "radius_Write: Internal error: Bad call !\n"); |
751 |
return 0; |
752 |
} |
753 |
|
754 |
/* |
755 |
* Initialise ourselves |
756 |
*/ |
757 |
void |
758 |
radius_Init(struct radius *r) |
759 |
{ |
760 |
r->desc.type = RADIUS_DESCRIPTOR; |
761 |
r->desc.UpdateSet = radius_UpdateSet; |
762 |
r->desc.IsSet = radius_IsSet; |
763 |
r->desc.Read = radius_Read; |
764 |
r->desc.Write = radius_Write; |
765 |
r->cx.fd = -1; |
766 |
r->cx.rad = NULL; |
767 |
memset(&r->cx.timer, '\0', sizeof r->cx.timer); |
768 |
r->cx.auth = NULL; |
769 |
r->valid = 0; |
770 |
r->vj = 0; |
771 |
r->ip.s_addr = INADDR_ANY; |
772 |
r->mask.s_addr = INADDR_NONE; |
773 |
r->routes = NULL; |
774 |
r->mtu = DEF_MTU; |
775 |
r->msrepstr = NULL; |
776 |
r->repstr = NULL; |
777 |
#ifndef NOINET6 |
778 |
r->ipv6prefix = NULL; |
779 |
r->ipv6routes = NULL; |
780 |
#endif |
781 |
r->errstr = NULL; |
782 |
r->mppe.policy = 0; |
783 |
r->mppe.types = 0; |
784 |
r->mppe.recvkey = NULL; |
785 |
r->mppe.recvkeylen = 0; |
786 |
r->mppe.sendkey = NULL; |
787 |
r->mppe.sendkeylen = 0; |
788 |
*r->cfg.file = '\0'; |
789 |
log_Printf(LogDEBUG, "Radius: radius_Init\n"); |
790 |
} |
791 |
|
792 |
/* |
793 |
* Forget everything and go back to initialised state. |
794 |
*/ |
795 |
void |
796 |
radius_Destroy(struct radius *r) |
797 |
{ |
798 |
r->valid = 0; |
799 |
log_Printf(LogDEBUG, "Radius: radius_Destroy\n"); |
800 |
timer_Stop(&r->cx.timer); |
801 |
route_DeleteAll(&r->routes); |
802 |
#ifndef NOINET6 |
803 |
route_DeleteAll(&r->ipv6routes); |
804 |
#endif |
805 |
free(r->filterid); |
806 |
r->filterid = NULL; |
807 |
free(r->msrepstr); |
808 |
r->msrepstr = NULL; |
809 |
free(r->repstr); |
810 |
r->repstr = NULL; |
811 |
#ifndef NOINET6 |
812 |
free(r->ipv6prefix); |
813 |
r->ipv6prefix = NULL; |
814 |
#endif |
815 |
free(r->errstr); |
816 |
r->errstr = NULL; |
817 |
free(r->mppe.recvkey); |
818 |
r->mppe.recvkey = NULL; |
819 |
r->mppe.recvkeylen = 0; |
820 |
free(r->mppe.sendkey); |
821 |
r->mppe.sendkey = NULL; |
822 |
r->mppe.sendkeylen = 0; |
823 |
if (r->cx.fd != -1) { |
824 |
r->cx.fd = -1; |
825 |
rad_close(r->cx.rad); |
826 |
} |
827 |
} |
828 |
|
829 |
static int |
830 |
radius_put_physical_details(struct radius *rad, struct physical *p) |
831 |
{ |
832 |
int slot, type; |
833 |
|
834 |
type = RAD_VIRTUAL; |
835 |
if (p->handler) |
836 |
switch (p->handler->type) { |
837 |
case I4B_DEVICE: |
838 |
type = RAD_ISDN_SYNC; |
839 |
break; |
840 |
|
841 |
case TTY_DEVICE: |
842 |
type = RAD_ASYNC; |
843 |
break; |
844 |
|
845 |
case ETHER_DEVICE: |
846 |
type = RAD_ETHERNET; |
847 |
break; |
848 |
|
849 |
case TCP_DEVICE: |
850 |
case UDP_DEVICE: |
851 |
case EXEC_DEVICE: |
852 |
case ATM_DEVICE: |
853 |
case NG_DEVICE: |
854 |
type = RAD_VIRTUAL; |
855 |
break; |
856 |
} |
857 |
|
858 |
if (rad_put_int(rad->cx.rad, RAD_NAS_PORT_TYPE, type) != 0) { |
859 |
log_Printf(LogERROR, "rad_put: rad_put_int: %s\n", rad_strerror(rad->cx.rad)); |
860 |
rad_close(rad->cx.rad); |
861 |
return 0; |
862 |
} |
863 |
|
864 |
switch (rad->port_id_type) { |
865 |
case RPI_PID: |
866 |
slot = (int)getpid(); |
867 |
break; |
868 |
case RPI_IFNUM: |
869 |
slot = p->dl->bundle->iface->index; |
870 |
break; |
871 |
case RPI_TUNNUM: |
872 |
slot = p->dl->bundle->unit; |
873 |
break; |
874 |
case RPI_DEFAULT: |
875 |
default: |
876 |
slot = physical_Slot(p); |
877 |
break; |
878 |
} |
879 |
|
880 |
if (slot >= 0) |
881 |
if (rad_put_int(rad->cx.rad, RAD_NAS_PORT, slot) != 0) { |
882 |
log_Printf(LogERROR, "rad_put: rad_put_int: %s\n", rad_strerror(rad->cx.rad)); |
883 |
rad_close(rad->cx.rad); |
884 |
return 0; |
885 |
} |
886 |
|
887 |
return 1; |
888 |
} |
889 |
|
890 |
/* |
891 |
* Start an authentication request to the RADIUS server. |
892 |
*/ |
893 |
int |
894 |
radius_Authenticate(struct radius *r, struct authinfo *authp, const char *name, |
895 |
const char *key, int klen, const char *nchallenge, |
896 |
int nclen) |
897 |
{ |
898 |
char hostname[MAXHOSTNAMELEN]; |
899 |
struct timeval tv; |
900 |
const char *what = "questionable"; /* silence warnings! */ |
901 |
char *mac_addr; |
902 |
int got; |
903 |
struct hostent *hp; |
904 |
struct in_addr hostaddr; |
905 |
#ifndef NODES |
906 |
struct mschap_response msresp; |
907 |
struct mschap2_response msresp2; |
908 |
const struct MSCHAPv2_resp *keyv2; |
909 |
#endif |
910 |
|
911 |
if (!*r->cfg.file) |
912 |
return 0; |
913 |
|
914 |
if (r->cx.fd != -1) |
915 |
/* |
916 |
* We assume that our name/key/challenge is the same as last time, |
917 |
* and just continue to wait for the RADIUS server(s). |
918 |
*/ |
919 |
return 1; |
920 |
|
921 |
radius_Destroy(r); |
922 |
|
923 |
if ((r->cx.rad = rad_auth_open()) == NULL) { |
924 |
log_Printf(LogERROR, "rad_auth_open: %s\n", strerror(errno)); |
925 |
return 0; |
926 |
} |
927 |
|
928 |
if (rad_config(r->cx.rad, r->cfg.file) != 0) { |
929 |
log_Printf(LogERROR, "rad_config: %s\n", rad_strerror(r->cx.rad)); |
930 |
rad_close(r->cx.rad); |
931 |
return 0; |
932 |
} |
933 |
|
934 |
if (rad_create_request(r->cx.rad, RAD_ACCESS_REQUEST) != 0) { |
935 |
log_Printf(LogERROR, "rad_create_request: %s\n", rad_strerror(r->cx.rad)); |
936 |
rad_close(r->cx.rad); |
937 |
return 0; |
938 |
} |
939 |
|
940 |
if (rad_put_string(r->cx.rad, RAD_USER_NAME, name) != 0 || |
941 |
rad_put_int(r->cx.rad, RAD_SERVICE_TYPE, RAD_FRAMED) != 0 || |
942 |
rad_put_int(r->cx.rad, RAD_FRAMED_PROTOCOL, RAD_PPP) != 0) { |
943 |
log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad)); |
944 |
rad_close(r->cx.rad); |
945 |
return 0; |
946 |
} |
947 |
|
948 |
switch (authp->physical->link.lcp.want_auth) { |
949 |
case PROTO_PAP: |
950 |
/* We're talking PAP */ |
951 |
if (rad_put_attr(r->cx.rad, RAD_USER_PASSWORD, key, klen) != 0) { |
952 |
log_Printf(LogERROR, "PAP: rad_put_string: %s\n", |
953 |
rad_strerror(r->cx.rad)); |
954 |
rad_close(r->cx.rad); |
955 |
return 0; |
956 |
} |
957 |
what = "PAP"; |
958 |
break; |
959 |
|
960 |
case PROTO_CHAP: |
961 |
switch (authp->physical->link.lcp.want_authtype) { |
962 |
case 0x5: |
963 |
if (rad_put_attr(r->cx.rad, RAD_CHAP_PASSWORD, key, klen) != 0 || |
964 |
rad_put_attr(r->cx.rad, RAD_CHAP_CHALLENGE, nchallenge, nclen) != 0) { |
965 |
log_Printf(LogERROR, "CHAP: rad_put_string: %s\n", |
966 |
rad_strerror(r->cx.rad)); |
967 |
rad_close(r->cx.rad); |
968 |
return 0; |
969 |
} |
970 |
what = "CHAP"; |
971 |
break; |
972 |
|
973 |
#ifndef NODES |
974 |
case 0x80: |
975 |
if (klen != 50) { |
976 |
log_Printf(LogERROR, "CHAP80: Unrecognised key length %d\n", klen); |
977 |
rad_close(r->cx.rad); |
978 |
return 0; |
979 |
} |
980 |
|
981 |
rad_put_vendor_attr(r->cx.rad, RAD_VENDOR_MICROSOFT, |
982 |
RAD_MICROSOFT_MS_CHAP_CHALLENGE, nchallenge, nclen); |
983 |
msresp.ident = *key; |
984 |
msresp.flags = 0x01; |
985 |
memcpy(msresp.lm_response, key + 1, 24); |
986 |
memcpy(msresp.nt_response, key + 25, 24); |
987 |
rad_put_vendor_attr(r->cx.rad, RAD_VENDOR_MICROSOFT, |
988 |
RAD_MICROSOFT_MS_CHAP_RESPONSE, &msresp, |
989 |
sizeof msresp); |
990 |
what = "MSCHAP"; |
991 |
break; |
992 |
|
993 |
case 0x81: |
994 |
if (klen != sizeof(*keyv2) + 1) { |
995 |
log_Printf(LogERROR, "CHAP81: Unrecognised key length %d\n", klen); |
996 |
rad_close(r->cx.rad); |
997 |
return 0; |
998 |
} |
999 |
|
1000 |
keyv2 = (const struct MSCHAPv2_resp *)(key + 1); |
1001 |
rad_put_vendor_attr(r->cx.rad, RAD_VENDOR_MICROSOFT, |
1002 |
RAD_MICROSOFT_MS_CHAP_CHALLENGE, nchallenge, nclen); |
1003 |
msresp2.ident = *key; |
1004 |
msresp2.flags = keyv2->Flags; |
1005 |
memcpy(msresp2.response, keyv2->NTResponse, sizeof msresp2.response); |
1006 |
memset(msresp2.reserved, '\0', sizeof msresp2.reserved); |
1007 |
memcpy(msresp2.pchallenge, keyv2->PeerChallenge, |
1008 |
sizeof msresp2.pchallenge); |
1009 |
rad_put_vendor_attr(r->cx.rad, RAD_VENDOR_MICROSOFT, |
1010 |
RAD_MICROSOFT_MS_CHAP2_RESPONSE, &msresp2, |
1011 |
sizeof msresp2); |
1012 |
what = "MSCHAPv2"; |
1013 |
break; |
1014 |
#endif |
1015 |
default: |
1016 |
log_Printf(LogERROR, "CHAP: Unrecognised type 0x%02x\n", |
1017 |
authp->physical->link.lcp.want_authtype); |
1018 |
rad_close(r->cx.rad); |
1019 |
return 0; |
1020 |
} |
1021 |
} |
1022 |
|
1023 |
if (gethostname(hostname, sizeof hostname) != 0) |
1024 |
log_Printf(LogERROR, "rad_put: gethostname(): %s\n", strerror(errno)); |
1025 |
else { |
1026 |
if (Enabled(authp->physical->dl->bundle, OPT_NAS_IP_ADDRESS) && |
1027 |
(hp = gethostbyname(hostname)) != NULL) { |
1028 |
hostaddr.s_addr = *(u_long *)hp->h_addr; |
1029 |
if (rad_put_addr(r->cx.rad, RAD_NAS_IP_ADDRESS, hostaddr) != 0) { |
1030 |
log_Printf(LogERROR, "rad_put: rad_put_string: %s\n", |
1031 |
rad_strerror(r->cx.rad)); |
1032 |
rad_close(r->cx.rad); |
1033 |
return 0; |
1034 |
} |
1035 |
} |
1036 |
if (Enabled(authp->physical->dl->bundle, OPT_NAS_IDENTIFIER) && |
1037 |
rad_put_string(r->cx.rad, RAD_NAS_IDENTIFIER, hostname) != 0) { |
1038 |
log_Printf(LogERROR, "rad_put: rad_put_string: %s\n", |
1039 |
rad_strerror(r->cx.rad)); |
1040 |
rad_close(r->cx.rad); |
1041 |
return 0; |
1042 |
} |
1043 |
} |
1044 |
|
1045 |
if ((mac_addr = getenv("HISMACADDR")) != NULL && |
1046 |
rad_put_string(r->cx.rad, RAD_CALLING_STATION_ID, mac_addr) != 0) { |
1047 |
log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad)); |
1048 |
rad_close(r->cx.rad); |
1049 |
return 0; |
1050 |
} |
1051 |
|
1052 |
radius_put_physical_details(r, authp->physical); |
1053 |
|
1054 |
log_Printf(LogRADIUS, "Radius(auth): %s data sent for %s\n", what, name); |
1055 |
|
1056 |
r->cx.auth = authp; |
1057 |
if ((got = rad_init_send_request(r->cx.rad, &r->cx.fd, &tv))) |
1058 |
radius_Process(r, got); |
1059 |
else { |
1060 |
log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE, |
1061 |
"Radius: Request sent\n"); |
1062 |
log_Printf(LogDEBUG, "Using radius_Timeout [%p]\n", radius_Timeout); |
1063 |
r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS; |
1064 |
r->cx.timer.func = radius_Timeout; |
1065 |
r->cx.timer.name = "radius auth"; |
1066 |
r->cx.timer.arg = r; |
1067 |
timer_Start(&r->cx.timer); |
1068 |
} |
1069 |
|
1070 |
return 1; |
1071 |
} |
1072 |
|
1073 |
/* Fetch IP, netmask from IPCP */ |
1074 |
void |
1075 |
radius_Account_Set_Ip(struct radacct *ac, struct in_addr *peer_ip, |
1076 |
struct in_addr *netmask) |
1077 |
{ |
1078 |
ac->proto = PROTO_IPCP; |
1079 |
memcpy(&ac->peer.ip.addr, peer_ip, sizeof(ac->peer.ip.addr)); |
1080 |
memcpy(&ac->peer.ip.mask, netmask, sizeof(ac->peer.ip.mask)); |
1081 |
} |
1082 |
|
1083 |
#ifndef NOINET6 |
1084 |
/* Fetch interface-id from IPV6CP */ |
1085 |
void |
1086 |
radius_Account_Set_Ipv6(struct radacct *ac, u_char *ifid) |
1087 |
{ |
1088 |
ac->proto = PROTO_IPV6CP; |
1089 |
memcpy(&ac->peer.ipv6.ifid, ifid, sizeof(ac->peer.ipv6.ifid)); |
1090 |
} |
1091 |
#endif |
1092 |
|
1093 |
/* |
1094 |
* Send an accounting request to the RADIUS server |
1095 |
*/ |
1096 |
void |
1097 |
radius_Account(struct radius *r, struct radacct *ac, struct datalink *dl, |
1098 |
int acct_type, struct pppThroughput *stats) |
1099 |
{ |
1100 |
struct timeval tv; |
1101 |
int got; |
1102 |
char hostname[MAXHOSTNAMELEN]; |
1103 |
char *mac_addr; |
1104 |
struct hostent *hp; |
1105 |
struct in_addr hostaddr; |
1106 |
|
1107 |
if (!*r->cfg.file) |
1108 |
return; |
1109 |
|
1110 |
if (r->cx.fd != -1) |
1111 |
/* |
1112 |
* We assume that our name/key/challenge is the same as last time, |
1113 |
* and just continue to wait for the RADIUS server(s). |
1114 |
*/ |
1115 |
return; |
1116 |
|
1117 |
timer_Stop(&r->cx.timer); |
1118 |
|
1119 |
if ((r->cx.rad = rad_acct_open()) == NULL) { |
1120 |
log_Printf(LogERROR, "rad_auth_open: %s\n", strerror(errno)); |
1121 |
return; |
1122 |
} |
1123 |
|
1124 |
if (rad_config(r->cx.rad, r->cfg.file) != 0) { |
1125 |
log_Printf(LogERROR, "rad_config: %s\n", rad_strerror(r->cx.rad)); |
1126 |
rad_close(r->cx.rad); |
1127 |
return; |
1128 |
} |
1129 |
|
1130 |
if (rad_create_request(r->cx.rad, RAD_ACCOUNTING_REQUEST) != 0) { |
1131 |
log_Printf(LogERROR, "rad_create_request: %s\n", rad_strerror(r->cx.rad)); |
1132 |
rad_close(r->cx.rad); |
1133 |
return; |
1134 |
} |
1135 |
|
1136 |
/* Grab some accounting data and initialize structure */ |
1137 |
if (acct_type == RAD_START) { |
1138 |
ac->rad_parent = r; |
1139 |
/* Fetch username from datalink */ |
1140 |
strncpy(ac->user_name, dl->peer.authname, sizeof ac->user_name); |
1141 |
ac->user_name[AUTHLEN-1] = '\0'; |
1142 |
|
1143 |
ac->authentic = 2; /* Assume RADIUS verified auth data */ |
1144 |
|
1145 |
/* Generate a session ID */ |
1146 |
snprintf(ac->session_id, sizeof ac->session_id, "%s%ld-%s%lu", |
1147 |
dl->bundle->cfg.auth.name, (long)getpid(), |
1148 |
dl->peer.authname, (unsigned long)stats->uptime); |
1149 |
|
1150 |
/* And grab our MP socket name */ |
1151 |
snprintf(ac->multi_session_id, sizeof ac->multi_session_id, "%s", |
1152 |
dl->bundle->ncp.mp.active ? |
1153 |
dl->bundle->ncp.mp.server.socket.sun_path : ""); |
1154 |
}; |
1155 |
|
1156 |
if (rad_put_string(r->cx.rad, RAD_USER_NAME, ac->user_name) != 0 || |
1157 |
rad_put_int(r->cx.rad, RAD_SERVICE_TYPE, RAD_FRAMED) != 0 || |
1158 |
rad_put_int(r->cx.rad, RAD_FRAMED_PROTOCOL, RAD_PPP) != 0) { |
1159 |
log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad)); |
1160 |
rad_close(r->cx.rad); |
1161 |
return; |
1162 |
} |
1163 |
switch (ac->proto) { |
1164 |
case PROTO_IPCP: |
1165 |
if (rad_put_addr(r->cx.rad, RAD_FRAMED_IP_ADDRESS, |
1166 |
ac->peer.ip.addr) != 0 || |
1167 |
rad_put_addr(r->cx.rad, RAD_FRAMED_IP_NETMASK, |
1168 |
ac->peer.ip.mask) != 0) { |
1169 |
log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad)); |
1170 |
rad_close(r->cx.rad); |
1171 |
return; |
1172 |
} |
1173 |
break; |
1174 |
#ifndef NOINET6 |
1175 |
case PROTO_IPV6CP: |
1176 |
if (rad_put_attr(r->cx.rad, RAD_FRAMED_INTERFACE_ID, ac->peer.ipv6.ifid, |
1177 |
sizeof(ac->peer.ipv6.ifid)) != 0) { |
1178 |
log_Printf(LogERROR, "rad_put_attr: %s\n", rad_strerror(r->cx.rad)); |
1179 |
rad_close(r->cx.rad); |
1180 |
return; |
1181 |
} |
1182 |
if (r->ipv6prefix) { |
1183 |
/* |
1184 |
* Since PPP doesn't delegate an IPv6 prefix to a peer, |
1185 |
* Framed-IPv6-Prefix may be not used, actually. |
1186 |
*/ |
1187 |
if (rad_put_attr(r->cx.rad, RAD_FRAMED_IPV6_PREFIX, r->ipv6prefix, |
1188 |
sizeof(struct in6_addr) + 2) != 0) { |
1189 |
log_Printf(LogERROR, "rad_put_attr: %s\n", rad_strerror(r->cx.rad)); |
1190 |
rad_close(r->cx.rad); |
1191 |
return; |
1192 |
} |
1193 |
} |
1194 |
break; |
1195 |
#endif |
1196 |
default: |
1197 |
/* We don't log any protocol specific information */ |
1198 |
break; |
1199 |
} |
1200 |
|
1201 |
if ((mac_addr = getenv("HISMACADDR")) != NULL && |
1202 |
rad_put_string(r->cx.rad, RAD_CALLING_STATION_ID, mac_addr) != 0) { |
1203 |
log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad)); |
1204 |
rad_close(r->cx.rad); |
1205 |
return; |
1206 |
} |
1207 |
|
1208 |
if (gethostname(hostname, sizeof hostname) != 0) |
1209 |
log_Printf(LogERROR, "rad_put: gethostname(): %s\n", strerror(errno)); |
1210 |
else { |
1211 |
if (Enabled(dl->bundle, OPT_NAS_IP_ADDRESS) && |
1212 |
(hp = gethostbyname(hostname)) != NULL) { |
1213 |
hostaddr.s_addr = *(u_long *)hp->h_addr; |
1214 |
if (rad_put_addr(r->cx.rad, RAD_NAS_IP_ADDRESS, hostaddr) != 0) { |
1215 |
log_Printf(LogERROR, "rad_put: rad_put_string: %s\n", |
1216 |
rad_strerror(r->cx.rad)); |
1217 |
rad_close(r->cx.rad); |
1218 |
return; |
1219 |
} |
1220 |
} |
1221 |
if (Enabled(dl->bundle, OPT_NAS_IDENTIFIER) && |
1222 |
rad_put_string(r->cx.rad, RAD_NAS_IDENTIFIER, hostname) != 0) { |
1223 |
log_Printf(LogERROR, "rad_put: rad_put_string: %s\n", |
1224 |
rad_strerror(r->cx.rad)); |
1225 |
rad_close(r->cx.rad); |
1226 |
return; |
1227 |
} |
1228 |
} |
1229 |
|
1230 |
radius_put_physical_details(r, dl->physical); |
1231 |
|
1232 |
if (rad_put_int(r->cx.rad, RAD_ACCT_STATUS_TYPE, acct_type) != 0 || |
1233 |
rad_put_string(r->cx.rad, RAD_ACCT_SESSION_ID, ac->session_id) != 0 || |
1234 |
rad_put_string(r->cx.rad, RAD_ACCT_MULTI_SESSION_ID, |
1235 |
ac->multi_session_id) != 0 || |
1236 |
rad_put_int(r->cx.rad, RAD_ACCT_DELAY_TIME, 0) != 0) { |
1237 |
/* XXX ACCT_DELAY_TIME should be increased each time a packet is waiting */ |
1238 |
log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad)); |
1239 |
rad_close(r->cx.rad); |
1240 |
return; |
1241 |
} |
1242 |
|
1243 |
if (acct_type == RAD_STOP || acct_type == RAD_ALIVE) |
1244 |
/* Show some statistics */ |
1245 |
if (rad_put_int(r->cx.rad, RAD_ACCT_INPUT_OCTETS, stats->OctetsIn % UINT32_MAX) != 0 || |
1246 |
rad_put_int(r->cx.rad, RAD_ACCT_INPUT_GIGAWORDS, stats->OctetsIn / UINT32_MAX) != 0 || |
1247 |
rad_put_int(r->cx.rad, RAD_ACCT_INPUT_PACKETS, stats->PacketsIn) != 0 || |
1248 |
rad_put_int(r->cx.rad, RAD_ACCT_OUTPUT_OCTETS, stats->OctetsOut % UINT32_MAX) != 0 || |
1249 |
rad_put_int(r->cx.rad, RAD_ACCT_OUTPUT_GIGAWORDS, stats->OctetsOut / UINT32_MAX) != 0 || |
1250 |
rad_put_int(r->cx.rad, RAD_ACCT_OUTPUT_PACKETS, stats->PacketsOut) |
1251 |
!= 0 || |
1252 |
rad_put_int(r->cx.rad, RAD_ACCT_SESSION_TIME, throughput_uptime(stats)) |
1253 |
!= 0) { |
1254 |
log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad)); |
1255 |
rad_close(r->cx.rad); |
1256 |
return; |
1257 |
} |
1258 |
|
1259 |
if (log_IsKept(LogPHASE) || log_IsKept(LogRADIUS)) { |
1260 |
const char *what; |
1261 |
int level; |
1262 |
|
1263 |
switch (acct_type) { |
1264 |
case RAD_START: |
1265 |
what = "START"; |
1266 |
level = log_IsKept(LogPHASE) ? LogPHASE : LogRADIUS; |
1267 |
break; |
1268 |
case RAD_STOP: |
1269 |
what = "STOP"; |
1270 |
level = log_IsKept(LogPHASE) ? LogPHASE : LogRADIUS; |
1271 |
break; |
1272 |
case RAD_ALIVE: |
1273 |
what = "ALIVE"; |
1274 |
level = LogRADIUS; |
1275 |
break; |
1276 |
default: |
1277 |
what = "<unknown>"; |
1278 |
level = log_IsKept(LogPHASE) ? LogPHASE : LogRADIUS; |
1279 |
break; |
1280 |
} |
1281 |
log_Printf(level, "Radius(acct): %s data sent\n", what); |
1282 |
} |
1283 |
|
1284 |
r->cx.auth = NULL; /* Not valid for accounting requests */ |
1285 |
if ((got = rad_init_send_request(r->cx.rad, &r->cx.fd, &tv))) |
1286 |
radius_Process(r, got); |
1287 |
else { |
1288 |
log_Printf(LogDEBUG, "Using radius_Timeout [%p]\n", radius_Timeout); |
1289 |
r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS; |
1290 |
r->cx.timer.func = radius_Timeout; |
1291 |
r->cx.timer.name = "radius acct"; |
1292 |
r->cx.timer.arg = r; |
1293 |
timer_Start(&r->cx.timer); |
1294 |
} |
1295 |
} |
1296 |
|
1297 |
/* |
1298 |
* How do things look at the moment ? |
1299 |
*/ |
1300 |
void |
1301 |
radius_Show(struct radius *r, struct prompt *p) |
1302 |
{ |
1303 |
prompt_Printf(p, " Radius config: %s", |
1304 |
*r->cfg.file ? r->cfg.file : "none"); |
1305 |
if (r->valid) { |
1306 |
prompt_Printf(p, "\n IP: %s\n", inet_ntoa(r->ip)); |
1307 |
prompt_Printf(p, " Netmask: %s\n", inet_ntoa(r->mask)); |
1308 |
prompt_Printf(p, " MTU: %lu\n", r->mtu); |
1309 |
prompt_Printf(p, " VJ: %sabled\n", r->vj ? "en" : "dis"); |
1310 |
prompt_Printf(p, " Message: %s\n", r->repstr ? r->repstr : ""); |
1311 |
prompt_Printf(p, " MPPE Enc Policy: %s\n", |
1312 |
radius_policyname(r->mppe.policy)); |
1313 |
prompt_Printf(p, " MPPE Enc Types: %s\n", |
1314 |
radius_typesname(r->mppe.types)); |
1315 |
prompt_Printf(p, " MPPE Recv Key: %seceived\n", |
1316 |
r->mppe.recvkey ? "R" : "Not r"); |
1317 |
prompt_Printf(p, " MPPE Send Key: %seceived\n", |
1318 |
r->mppe.sendkey ? "R" : "Not r"); |
1319 |
prompt_Printf(p, " MS-CHAP2-Response: %s\n", |
1320 |
r->msrepstr ? r->msrepstr : ""); |
1321 |
prompt_Printf(p, " Error Message: %s\n", r->errstr ? r->errstr : ""); |
1322 |
if (r->routes) |
1323 |
route_ShowSticky(p, r->routes, " Routes", 16); |
1324 |
#ifndef NOINET6 |
1325 |
if (r->ipv6routes) |
1326 |
route_ShowSticky(p, r->ipv6routes, " IPv6 Routes", 16); |
1327 |
#endif |
1328 |
} else |
1329 |
prompt_Printf(p, " (not authenticated)\n"); |
1330 |
} |
1331 |
|
1332 |
static void |
1333 |
radius_alive(void *v) |
1334 |
{ |
1335 |
struct bundle *bundle = (struct bundle *)v; |
1336 |
|
1337 |
timer_Stop(&bundle->radius.alive.timer); |
1338 |
bundle->radius.alive.timer.load = bundle->radius.alive.interval * SECTICKS; |
1339 |
if (bundle->radius.alive.timer.load) { |
1340 |
radius_Account(&bundle->radius, &bundle->radacct, |
1341 |
bundle->links, RAD_ALIVE, &bundle->ncp.ipcp.throughput); |
1342 |
timer_Start(&bundle->radius.alive.timer); |
1343 |
} |
1344 |
} |
1345 |
|
1346 |
void |
1347 |
radius_StartTimer(struct bundle *bundle) |
1348 |
{ |
1349 |
if (bundle->radius.cfg.file && bundle->radius.alive.interval) { |
1350 |
bundle->radius.alive.timer.func = radius_alive; |
1351 |
bundle->radius.alive.timer.name = "radius alive"; |
1352 |
bundle->radius.alive.timer.load = bundle->radius.alive.interval * SECTICKS; |
1353 |
bundle->radius.alive.timer.arg = bundle; |
1354 |
radius_alive(bundle); |
1355 |
} |
1356 |
} |
1357 |
|
1358 |
void |
1359 |
radius_StopTimer(struct radius *r) |
1360 |
{ |
1361 |
timer_Stop(&r->alive.timer); |
1362 |
} |