Annotation of embedaddon/strongswan/src/libradius/radius_message.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (C) 2009 Martin Willi
3: * HSR Hochschule fuer Technik Rapperswil
4: *
5: * This program is free software; you can redistribute it and/or modify it
6: * under the terms of the GNU General Public License as published by the
7: * Free Software Foundation; either version 2 of the License, or (at your
8: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9: *
10: * This program is distributed in the hope that it will be useful, but
11: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13: * for more details.
14: */
15:
16: #include "radius_message.h"
17:
18: #include <utils/debug.h>
19: #include <bio/bio_reader.h>
20: #include <crypto/hashers/hasher.h>
21:
22: typedef struct private_radius_message_t private_radius_message_t;
23: typedef struct rmsg_t rmsg_t;
24: typedef struct rattr_t rattr_t;
25:
26: /**
27: * RADIUS message header
28: */
29: struct rmsg_t {
30: /** message code, radius_message_code_t */
31: uint8_t code;
32: /** message identifier */
33: uint8_t identifier;
34: /** length of Code, Identifier, Length, Authenticator and Attributes */
35: uint16_t length;
36: /** message authenticator, MD5 hash */
37: uint8_t authenticator[HASH_SIZE_MD5];
38: /** variable list of packed attributes */
39: uint8_t attributes[];
40: } __attribute__((packed));
41:
42: /**
43: * RADIUS message attribute.
44: */
45: struct rattr_t {
46: /** attribute type, radius_attribute_type_t */
47: uint8_t type;
48: /** length of the attribute, including the Type, Length and Value fields */
49: uint8_t length;
50: /** variable length attribute value */
51: uint8_t value[];
52: } __attribute__((packed));
53:
54: /**
55: * Private data of an radius_message_t object.
56: */
57: struct private_radius_message_t {
58:
59: /**
60: * Public radius_message_t interface.
61: */
62: radius_message_t public;
63:
64: /**
65: * message data, allocated
66: */
67: rmsg_t *msg;
68:
69: /**
70: * User-Password to encrypt and encode, if any
71: */
72: chunk_t password;
73: };
74:
75: /**
76: * Described in header.
77: */
78: void libradius_init(void)
79: {
80: /* empty */
81: }
82:
83: ENUM_BEGIN(radius_message_code_names, RMC_ACCESS_REQUEST, RMC_ACCOUNTING_RESPONSE,
84: "Access-Request",
85: "Access-Accept",
86: "Access-Reject",
87: "Accounting-Request",
88: "Accounting-Response");
89: ENUM_NEXT(radius_message_code_names, RMC_ACCESS_CHALLENGE, RMC_ACCESS_CHALLENGE, RMC_ACCOUNTING_RESPONSE,
90: "Access-Challenge");
91: ENUM_NEXT(radius_message_code_names, RMC_DISCONNECT_REQUEST, RMC_COA_NAK, RMC_ACCESS_CHALLENGE,
92: "Disconnect-Request",
93: "Disconnect-ACK",
94: "Disconnect-NAK",
95: "CoA-Request",
96: "CoA-ACK",
97: "CoA-NAK");
98: ENUM_END(radius_message_code_names, RMC_COA_NAK);
99:
100: ENUM_BEGIN(radius_attribute_type_names, RAT_USER_NAME, RAT_MIP6_HOME_LINK_PREFIX,
101: "User-Name",
102: "User-Password",
103: "CHAP-Password",
104: "NAS-IP-Address",
105: "NAS-Port",
106: "Service-Type",
107: "Framed-Protocol",
108: "Framed-IP-Address",
109: "Framed-IP-Netmask",
110: "Framed-Routing",
111: "Filter-Id",
112: "Framed-MTU",
113: "Framed-Compression",
114: "Login-IP-Host",
115: "Login-Service",
116: "Login-TCP-Port",
117: "Unassigned",
118: "Reply-Message",
119: "Callback-Number",
120: "Callback-Id",
121: "Unassigned",
122: "Framed-Route",
123: "Framed-IPX-Network",
124: "State",
125: "Class",
126: "Vendor-Specific",
127: "Session-Timeout",
128: "Idle-Timeout",
129: "Termination-Action",
130: "Called-Station-Id",
131: "Calling-Station-Id",
132: "NAS-Identifier",
133: "Proxy-State",
134: "Login-LAT-Service",
135: "Login-LAT-Node",
136: "Login-LAT-Group",
137: "Framed-AppleTalk-Link",
138: "Framed-AppleTalk-Network",
139: "Framed-AppleTalk-Zone",
140: "Acct-Status-Type",
141: "Acct-Delay-Time",
142: "Acct-Input-Octets",
143: "Acct-Output-Octets",
144: "Acct-Session-Id",
145: "Acct-Authentic",
146: "Acct-Session-Time",
147: "Acct-Input-Packets",
148: "Acct-Output-Packets",
149: "Acct-Terminate-Cause",
150: "Acct-Multi-Session-Id",
151: "Acct-Link-Count",
152: "Acct-Input-Gigawords",
153: "Acct-Output-Gigawords",
154: "Unassigned",
155: "Event-Timestamp",
156: "Egress-VLANID",
157: "Ingress-Filters",
158: "Egress-VLAN-Name",
159: "User-Priority-Table",
160: "CHAP-Challenge",
161: "NAS-Port-Type",
162: "Port-Limit",
163: "Login-LAT-Port",
164: "Tunnel-Type",
165: "Tunnel-Medium-Type",
166: "Tunnel-Client-Endpoint",
167: "Tunnel-Server-Endpoint",
168: "Acct-Tunnel-Connection",
169: "Tunnel-Password",
170: "ARAP-Password",
171: "ARAP-Features",
172: "ARAP-Zone-Access",
173: "ARAP-Security",
174: "ARAP-Security-Data",
175: "Password-Retry",
176: "Prompt",
177: "Connect-Info",
178: "Configuration-Token",
179: "EAP-Message",
180: "Message-Authenticator",
181: "Tunnel-Private-Group-ID",
182: "Tunnel-Assignment-ID",
183: "Tunnel-Preference",
184: "ARAP-Challenge-Response",
185: "Acct-Interim-Interval",
186: "Acct-Tunnel-Packets-Lost",
187: "NAS-Port-Id",
188: "Framed-Pool",
189: "CUI",
190: "Tunnel-Client-Auth-ID",
191: "Tunnel-Server-Auth-ID",
192: "NAS-Filter-Rule",
193: "Unassigned",
194: "Originating-Line-Info",
195: "NAS-IPv6-Address",
196: "Framed-Interface-Id",
197: "Framed-IPv6-Prefix",
198: "Login-IPv6-Host",
199: "Framed-IPv6-Route",
200: "Framed-IPv6-Pool",
201: "Error-Cause",
202: "EAP-Key-Name",
203: "Digest-Response",
204: "Digest-Realm",
205: "Digest-Nonce",
206: "Digest-Response-Auth",
207: "Digest-Nextnonce",
208: "Digest-Method",
209: "Digest-URI",
210: "Digest-Qop",
211: "Digest-Algorithm",
212: "Digest-Entity-Body-Hash",
213: "Digest-CNonce",
214: "Digest-Nonce-Count",
215: "Digest-Username",
216: "Digest-Opaque",
217: "Digest-Auth-Param",
218: "Digest-AKA-Auts",
219: "Digest-Domain",
220: "Digest-Stale",
221: "Digest-HA1",
222: "SIP-AOR",
223: "Delegated-IPv6-Prefix",
224: "MIP6-Feature-Vector",
225: "MIP6-Home-Link-Prefix");
226: ENUM_NEXT(radius_attribute_type_names, RAT_FRAMED_IPV6_ADDRESS, RAT_STATEFUL_IPV6_ADDRESS_POOL, RAT_MIP6_HOME_LINK_PREFIX,
227: "Framed-IPv6-Address",
228: "DNS-Server-IPv6-Address",
229: "Route-IPv6-Information",
230: "Delegated-IPv6-Prefix-Pool",
231: "Stateful-IPv6-Address-Pool");
232: ENUM_END(radius_attribute_type_names, RAT_STATEFUL_IPV6_ADDRESS_POOL);
233:
234: /**
235: * Attribute enumerator implementation
236: */
237: typedef struct {
238: /** implements enumerator interface */
239: enumerator_t public;
240: /** currently pointing attribute */
241: rattr_t *next;
242: /** bytes left */
243: int left;
244: } attribute_enumerator_t;
245:
246: METHOD(enumerator_t, attribute_enumerate, bool,
247: attribute_enumerator_t *this, va_list args)
248: {
249: chunk_t *data;
250: int *type;
251:
252: VA_ARGS_VGET(args, type, data);
253: if (this->left == 0)
254: {
255: return FALSE;
256: }
257: if (this->left < sizeof(rattr_t) ||
258: this->left < this->next->length)
259: {
260: DBG1(DBG_IKE, "RADIUS message truncated");
261: return FALSE;
262: }
263: *type = this->next->type;
264: data->ptr = this->next->value;
265: data->len = this->next->length - sizeof(rattr_t);
266: this->left -= this->next->length;
267: this->next = ((void*)this->next) + this->next->length;
268: return TRUE;
269: }
270:
271: METHOD(radius_message_t, create_enumerator, enumerator_t*,
272: private_radius_message_t *this)
273: {
274: attribute_enumerator_t *e;
275:
276: if (ntohs(this->msg->length) < sizeof(rmsg_t) + sizeof(rattr_t))
277: {
278: return enumerator_create_empty();
279: }
280: INIT(e,
281: .public = {
282: .enumerate = enumerator_enumerate_default,
283: .venumerate = _attribute_enumerate,
284: .destroy = (void*)free,
285: },
286: .next = (rattr_t*)this->msg->attributes,
287: .left = ntohs(this->msg->length) - sizeof(rmsg_t),
288: );
289: return &e->public;
290: }
291:
292: /**
293: * Vendor attribute enumerator implementation
294: */
295: typedef struct {
296: /** implements enumerator interface */
297: enumerator_t public;
298: /** inner attribute enumerator */
299: enumerator_t *inner;
300: /** current vendor ID */
301: uint32_t vendor;
302: /** reader for current vendor ID */
303: bio_reader_t *reader;
304: } vendor_enumerator_t;
305:
306: METHOD(enumerator_t, vendor_enumerate, bool,
307: vendor_enumerator_t *this, va_list args)
308: {
309: chunk_t inner_data, *data;
310: int inner_type, *vendor, *type;
311: uint8_t type8, len;
312:
313: VA_ARGS_VGET(args, vendor, type, data);
314:
315: while (TRUE)
316: {
317: if (this->reader)
318: {
319: if (this->reader->remaining(this->reader) >= 2 &&
320: this->reader->read_uint8(this->reader, &type8) &&
321: this->reader->read_uint8(this->reader, &len) && len >= 2 &&
322: this->reader->read_data(this->reader, len - 2, data))
323: {
324: *vendor = this->vendor;
325: *type = type8;
326: return TRUE;
327: }
328: this->reader->destroy(this->reader);
329: this->reader = NULL;
330: }
331: if (this->inner->enumerate(this->inner, &inner_type, &inner_data))
332: {
333: if (inner_type == RAT_VENDOR_SPECIFIC)
334: {
335: this->reader = bio_reader_create(inner_data);
336: if (!this->reader->read_uint32(this->reader, &this->vendor))
337: {
338: this->reader->destroy(this->reader);
339: this->reader = NULL;
340: }
341: }
342: }
343: else
344: {
345: return FALSE;
346: }
347: }
348: }
349: METHOD(enumerator_t, vendor_destroy, void,
350: vendor_enumerator_t *this)
351: {
352: DESTROY_IF(this->reader);
353: this->inner->destroy(this->inner);
354: free(this);
355: }
356:
357: METHOD(radius_message_t, create_vendor_enumerator, enumerator_t*,
358: private_radius_message_t *this)
359: {
360: vendor_enumerator_t *e;
361:
362: INIT(e,
363: .public = {
364: .enumerate = enumerator_enumerate_default,
365: .venumerate = _vendor_enumerate,
366: .destroy = _vendor_destroy,
367: },
368: .inner = create_enumerator(this),
369: );
370:
371: return &e->public;
372: }
373:
374: METHOD(radius_message_t, add, void,
375: private_radius_message_t *this, radius_attribute_type_t type, chunk_t data)
376: {
377: rattr_t *attribute;
378:
379: if (type == RAT_USER_PASSWORD && !this->password.len)
380: {
381: /* store a null-padded password */
382: this->password = chunk_alloc(round_up(data.len, HASH_SIZE_MD5));
383: memset(this->password.ptr + data.len, 0, this->password.len - data.len);
384: memcpy(this->password.ptr, data.ptr, data.len);
385: return;
386: }
387:
388: data.len = min(data.len, MAX_RADIUS_ATTRIBUTE_SIZE);
389: this->msg = realloc(this->msg,
390: ntohs(this->msg->length) + sizeof(rattr_t) + data.len);
391: attribute = ((void*)this->msg) + ntohs(this->msg->length);
392: attribute->type = type;
393: attribute->length = data.len + sizeof(rattr_t);
394: memcpy(attribute->value, data.ptr, data.len);
395: this->msg->length = htons(ntohs(this->msg->length) + attribute->length);
396: }
397:
398: METHOD(radius_message_t, crypt, bool,
399: private_radius_message_t *this, chunk_t salt, chunk_t in, chunk_t out,
400: chunk_t secret, hasher_t *hasher)
401: {
402: char b[HASH_SIZE_MD5];
403:
404: /**
405: * From RFC2548 (encryption):
406: * b(1) = MD5(S + R + A) c(1) = p(1) xor b(1) C = c(1)
407: * b(2) = MD5(S + c(1)) c(2) = p(2) xor b(2) C = C + c(2)
408: * . . .
409: * b(i) = MD5(S + c(i-1)) c(i) = p(i) xor b(i) C = C + c(i)
410: *
411: * P/C = Plain/Crypted => in/out
412: * S = secret
413: * R = authenticator
414: * A = salt
415: */
416: if (in.len != out.len)
417: {
418: return FALSE;
419: }
420: if (in.len % HASH_SIZE_MD5 || in.len < HASH_SIZE_MD5)
421: {
422: return FALSE;
423: }
424: if (out.ptr != in.ptr)
425: {
426: memcpy(out.ptr, in.ptr, in.len);
427: }
428: /* Preparse seed for first round:
429: * b(1) = MD5(S + R + A) */
430: if (!hasher->get_hash(hasher, secret, NULL) ||
431: !hasher->get_hash(hasher,
432: chunk_from_thing(this->msg->authenticator), NULL) ||
433: !hasher->get_hash(hasher, salt, b))
434: {
435: return FALSE;
436: }
437: while (in.len)
438: {
439: /* p(i) = b(i) xor c(1) */
440: memxor(out.ptr, b, HASH_SIZE_MD5);
441:
442: out = chunk_skip(out, HASH_SIZE_MD5);
443: if (out.len)
444: {
445: /* Prepare seed for next round::
446: * b(i) = MD5(S + c(i-1)) */
447: if (!hasher->get_hash(hasher, secret, NULL) ||
448: !hasher->get_hash(hasher,
449: chunk_create(in.ptr, HASH_SIZE_MD5), b))
450: {
451: return FALSE;
452: }
453: }
454: in = chunk_skip(in, HASH_SIZE_MD5);
455: }
456: return TRUE;
457: }
458:
459: METHOD(radius_message_t, sign, bool,
460: private_radius_message_t *this, uint8_t *req_auth, chunk_t secret,
461: hasher_t *hasher, signer_t *signer, rng_t *rng, bool msg_auth)
462: {
463: if (rng)
464: {
465: /* build Request-Authenticator */
466: if (!rng->get_bytes(rng, HASH_SIZE_MD5, this->msg->authenticator))
467: {
468: return FALSE;
469: }
470: }
471: else
472: {
473: /* prepare build of Response-Authenticator */
474: if (req_auth)
475: {
476: memcpy(this->msg->authenticator, req_auth, HASH_SIZE_MD5);
477: }
478: else
479: {
480: memset(this->msg->authenticator, 0, sizeof(this->msg->authenticator));
481: }
482: }
483:
484: if (this->password.len)
485: {
486: /* encrypt password inline */
487: if (!crypt(this, chunk_empty, this->password, this->password,
488: secret, hasher))
489: {
490: return FALSE;
491: }
492: add(this, RAT_USER_PASSWORD, this->password);
493: chunk_clear(&this->password);
494: }
495:
496: if (msg_auth)
497: {
498: char buf[HASH_SIZE_MD5];
499:
500: /* build Message-Authenticator attribute, using 16 null bytes */
501: memset(buf, 0, sizeof(buf));
502: add(this, RAT_MESSAGE_AUTHENTICATOR, chunk_create(buf, sizeof(buf)));
503: if (!signer->get_signature(signer,
504: chunk_create((u_char*)this->msg, ntohs(this->msg->length)),
505: ((u_char*)this->msg) + ntohs(this->msg->length) - HASH_SIZE_MD5))
506: {
507: return FALSE;
508: }
509: }
510:
511: if (!rng)
512: {
513: chunk_t msg;
514:
515: /* build Response-Authenticator */
516: msg = chunk_create((u_char*)this->msg, ntohs(this->msg->length));
517: if (!hasher->get_hash(hasher, msg, NULL) ||
518: !hasher->get_hash(hasher, secret, this->msg->authenticator))
519: {
520: return FALSE;
521: }
522: }
523: return TRUE;
524: }
525:
526: METHOD(radius_message_t, verify, bool,
527: private_radius_message_t *this, uint8_t *req_auth, chunk_t secret,
528: hasher_t *hasher, signer_t *signer)
529: {
530: char buf[HASH_SIZE_MD5], res_auth[HASH_SIZE_MD5];
531: enumerator_t *enumerator;
532: int type;
533: chunk_t data, msg;
534: bool has_eap = FALSE, has_auth = FALSE;
535:
536: msg = chunk_create((u_char*)this->msg, ntohs(this->msg->length));
537:
538: if (this->msg->code != RMC_ACCESS_REQUEST)
539: {
540: /* replace Response by Request Authenticator for verification */
541: memcpy(res_auth, this->msg->authenticator, HASH_SIZE_MD5);
542: if (req_auth)
543: {
544: memcpy(this->msg->authenticator, req_auth, HASH_SIZE_MD5);
545: }
546: else
547: {
548: memset(this->msg->authenticator, 0, HASH_SIZE_MD5);
549: }
550:
551: /* verify Response-Authenticator */
552: if (!hasher->get_hash(hasher, msg, NULL) ||
553: !hasher->get_hash(hasher, secret, buf) ||
554: !memeq_const(buf, res_auth, HASH_SIZE_MD5))
555: {
556: DBG1(DBG_CFG, "RADIUS Response-Authenticator verification failed");
557: return FALSE;
558: }
559: }
560:
561: /* verify Message-Authenticator attribute */
562: enumerator = create_enumerator(this);
563: while (enumerator->enumerate(enumerator, &type, &data))
564: {
565: if (type == RAT_MESSAGE_AUTHENTICATOR)
566: {
567: if (data.len != HASH_SIZE_MD5)
568: {
569: DBG1(DBG_CFG, "RADIUS Message-Authenticator invalid length");
570: enumerator->destroy(enumerator);
571: return FALSE;
572: }
573: memcpy(buf, data.ptr, data.len);
574: memset(data.ptr, 0, data.len);
575: if (signer->verify_signature(signer, msg,
576: chunk_create(buf, sizeof(buf))))
577: {
578: /* restore Message-Authenticator */
579: memcpy(data.ptr, buf, data.len);
580: has_auth = TRUE;
581: break;
582: }
583: else
584: {
585: DBG1(DBG_CFG, "RADIUS Message-Authenticator verification failed");
586: enumerator->destroy(enumerator);
587: return FALSE;
588: }
589: }
590: else if (type == RAT_EAP_MESSAGE)
591: {
592: has_eap = TRUE;
593: }
594: }
595: enumerator->destroy(enumerator);
596:
597: if (this->msg->code != RMC_ACCESS_REQUEST)
598: {
599: /* restore Response-Authenticator */
600: memcpy(this->msg->authenticator, res_auth, HASH_SIZE_MD5);
601: }
602:
603: if (has_eap && !has_auth)
604: { /* Message-Authenticator is required if we have an EAP-Message */
605: DBG1(DBG_CFG, "RADIUS Message-Authenticator attribute missing");
606: return FALSE;
607: }
608: return TRUE;
609: }
610:
611: METHOD(radius_message_t, get_code, radius_message_code_t,
612: private_radius_message_t *this)
613: {
614: return this->msg->code;
615: }
616:
617: METHOD(radius_message_t, get_identifier, uint8_t,
618: private_radius_message_t *this)
619: {
620: return this->msg->identifier;
621: }
622:
623: METHOD(radius_message_t, set_identifier, void,
624: private_radius_message_t *this, uint8_t identifier)
625: {
626: this->msg->identifier = identifier;
627: }
628:
629: METHOD(radius_message_t, get_authenticator, uint8_t*,
630: private_radius_message_t *this)
631: {
632: return this->msg->authenticator;
633: }
634:
635:
636: METHOD(radius_message_t, get_encoding, chunk_t,
637: private_radius_message_t *this)
638: {
639: return chunk_create((u_char*)this->msg, ntohs(this->msg->length));
640: }
641:
642: METHOD(radius_message_t, destroy, void,
643: private_radius_message_t *this)
644: {
645: chunk_clear(&this->password);
646: free(this->msg);
647: free(this);
648: }
649:
650: /**
651: * Generic constructor
652: */
653: static private_radius_message_t *radius_message_create_empty()
654: {
655: private_radius_message_t *this;
656:
657: INIT(this,
658: .public = {
659: .create_enumerator = _create_enumerator,
660: .create_vendor_enumerator = _create_vendor_enumerator,
661: .add = _add,
662: .get_code = _get_code,
663: .get_identifier = _get_identifier,
664: .set_identifier = _set_identifier,
665: .get_authenticator = _get_authenticator,
666: .get_encoding = _get_encoding,
667: .sign = _sign,
668: .verify = _verify,
669: .crypt = _crypt,
670: .destroy = _destroy,
671: },
672: );
673:
674: return this;
675: }
676:
677: /**
678: * See header
679: */
680: radius_message_t *radius_message_create(radius_message_code_t code)
681: {
682: private_radius_message_t *this = radius_message_create_empty();
683:
684: INIT(this->msg,
685: .code = code,
686: .identifier = 0,
687: .length = htons(sizeof(rmsg_t)),
688: );
689:
690: return &this->public;
691: }
692:
693: /**
694: * See header
695: */
696: radius_message_t *radius_message_parse(chunk_t data)
697: {
698: private_radius_message_t *this = radius_message_create_empty();
699:
700: this->msg = malloc(data.len);
701: memcpy(this->msg, data.ptr, data.len);
702: if (data.len < sizeof(rmsg_t) ||
703: ntohs(this->msg->length) != data.len)
704: {
705: DBG1(DBG_IKE, "RADIUS message has invalid length");
706: destroy(this);
707: return NULL;
708: }
709: return &this->public;
710: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>