travis: Enable caching for sonarcloud scan
[strongswan.git] / src / libradius / radius_message.c
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 attriubte, 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 }