Added WirelessManager, a port of wpa_supplicant.
[AROS.git] / workbench / network / WirelessManager / src / radius / radius.c
blob70754ef5dd7254fb4902f4a6f93a5dae8a0da3b2
1 /*
2 * RADIUS message processing
3 * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
12 * See README and COPYING for more details.
15 #include "utils/includes.h"
17 #include "utils/common.h"
18 #include "utils/wpabuf.h"
19 #include "crypto/md5.h"
20 #include "crypto/crypto.h"
21 #include "radius.h"
24 /**
25 * struct radius_msg - RADIUS message structure for new and parsed messages
27 struct radius_msg {
28 /**
29 * buf - Allocated buffer for RADIUS message
31 struct wpabuf *buf;
33 /**
34 * hdr - Pointer to the RADIUS header in buf
36 struct radius_hdr *hdr;
38 /**
39 * attr_pos - Array of indexes to attributes
41 * The values are number of bytes from buf to the beginning of
42 * struct radius_attr_hdr.
44 size_t *attr_pos;
46 /**
47 * attr_size - Total size of the attribute pointer array
49 size_t attr_size;
51 /**
52 * attr_used - Total number of attributes in the array
54 size_t attr_used;
58 struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg)
60 return msg->hdr;
64 struct wpabuf * radius_msg_get_buf(struct radius_msg *msg)
66 return msg->buf;
70 static struct radius_attr_hdr *
71 radius_get_attr_hdr(struct radius_msg *msg, int idx)
73 return (struct radius_attr_hdr *)
74 (wpabuf_mhead_u8(msg->buf) + msg->attr_pos[idx]);
78 static void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier)
80 msg->hdr->code = code;
81 msg->hdr->identifier = identifier;
85 static int radius_msg_initialize(struct radius_msg *msg)
87 msg->attr_pos =
88 os_zalloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attr_pos));
89 if (msg->attr_pos == NULL)
90 return -1;
92 msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT;
93 msg->attr_used = 0;
95 return 0;
99 /**
100 * radius_msg_new - Create a new RADIUS message
101 * @code: Code for RADIUS header
102 * @identifier: Identifier for RADIUS header
103 * Returns: Context for RADIUS message or %NULL on failure
105 * The caller is responsible for freeing the returned data with
106 * radius_msg_free().
108 struct radius_msg * radius_msg_new(u8 code, u8 identifier)
110 struct radius_msg *msg;
112 msg = os_zalloc(sizeof(*msg));
113 if (msg == NULL)
114 return NULL;
116 msg->buf = wpabuf_alloc(RADIUS_DEFAULT_MSG_SIZE);
117 if (msg->buf == NULL || radius_msg_initialize(msg)) {
118 radius_msg_free(msg);
119 return NULL;
121 msg->hdr = wpabuf_put(msg->buf, sizeof(struct radius_hdr));
123 radius_msg_set_hdr(msg, code, identifier);
125 return msg;
130 * radius_msg_free - Free a RADIUS message
131 * @msg: RADIUS message from radius_msg_new() or radius_msg_parse()
133 void radius_msg_free(struct radius_msg *msg)
135 if (msg == NULL)
136 return;
138 wpabuf_free(msg->buf);
139 os_free(msg->attr_pos);
140 os_free(msg);
144 static const char *radius_code_string(u8 code)
146 switch (code) {
147 case RADIUS_CODE_ACCESS_REQUEST: return "Access-Request";
148 case RADIUS_CODE_ACCESS_ACCEPT: return "Access-Accept";
149 case RADIUS_CODE_ACCESS_REJECT: return "Access-Reject";
150 case RADIUS_CODE_ACCOUNTING_REQUEST: return "Accounting-Request";
151 case RADIUS_CODE_ACCOUNTING_RESPONSE: return "Accounting-Response";
152 case RADIUS_CODE_ACCESS_CHALLENGE: return "Access-Challenge";
153 case RADIUS_CODE_STATUS_SERVER: return "Status-Server";
154 case RADIUS_CODE_STATUS_CLIENT: return "Status-Client";
155 case RADIUS_CODE_RESERVED: return "Reserved";
156 default: return "?Unknown?";
161 struct radius_attr_type {
162 u8 type;
163 char *name;
164 enum {
165 RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP,
166 RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6
167 } data_type;
170 static struct radius_attr_type radius_attrs[] =
172 { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT },
173 { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST },
174 { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP },
175 { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 },
176 { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 },
177 { RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT },
178 { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST },
179 { RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST },
180 { RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST },
181 { RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 },
182 { RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 },
183 { RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action",
184 RADIUS_ATTR_INT32 },
185 { RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id",
186 RADIUS_ATTR_TEXT },
187 { RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id",
188 RADIUS_ATTR_TEXT },
189 { RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT },
190 { RADIUS_ATTR_PROXY_STATE, "Proxy-State", RADIUS_ATTR_UNDIST },
191 { RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type",
192 RADIUS_ATTR_INT32 },
193 { RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 },
194 { RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets",
195 RADIUS_ATTR_INT32 },
196 { RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets",
197 RADIUS_ATTR_INT32 },
198 { RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT },
199 { RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 },
200 { RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time",
201 RADIUS_ATTR_INT32 },
202 { RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets",
203 RADIUS_ATTR_INT32 },
204 { RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets",
205 RADIUS_ATTR_INT32 },
206 { RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause",
207 RADIUS_ATTR_INT32 },
208 { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id",
209 RADIUS_ATTR_TEXT },
210 { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 },
211 { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords",
212 RADIUS_ATTR_INT32 },
213 { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords",
214 RADIUS_ATTR_INT32 },
215 { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp",
216 RADIUS_ATTR_INT32 },
217 { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 },
218 { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP },
219 { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type",
220 RADIUS_ATTR_HEXDUMP },
221 { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT },
222 { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST },
223 { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator",
224 RADIUS_ATTR_UNDIST },
225 { RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, "Tunnel-Private-Group-Id",
226 RADIUS_ATTR_HEXDUMP },
227 { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval",
228 RADIUS_ATTR_INT32 },
229 { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargable-User-Identity",
230 RADIUS_ATTR_TEXT },
231 { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 },
233 #define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0]))
236 static struct radius_attr_type *radius_get_attr_type(u8 type)
238 size_t i;
240 for (i = 0; i < RADIUS_ATTRS; i++) {
241 if (type == radius_attrs[i].type)
242 return &radius_attrs[i];
245 return NULL;
249 static void print_char(char c)
251 if (c >= 32 && c < 127)
252 printf("%c", c);
253 else
254 printf("<%02x>", c);
258 static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
260 struct radius_attr_type *attr;
261 int i, len;
262 unsigned char *pos;
264 attr = radius_get_attr_type(hdr->type);
266 printf(" Attribute %d (%s) length=%d\n",
267 hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
269 if (attr == NULL)
270 return;
272 len = hdr->length - sizeof(struct radius_attr_hdr);
273 pos = (unsigned char *) (hdr + 1);
275 switch (attr->data_type) {
276 case RADIUS_ATTR_TEXT:
277 printf(" Value: '");
278 for (i = 0; i < len; i++)
279 print_char(pos[i]);
280 printf("'\n");
281 break;
283 case RADIUS_ATTR_IP:
284 if (len == 4) {
285 struct in_addr addr;
286 os_memcpy(&addr, pos, 4);
287 printf(" Value: %s\n", inet_ntoa(addr));
288 } else
289 printf(" Invalid IP address length %d\n", len);
290 break;
292 #ifdef CONFIG_IPV6
293 case RADIUS_ATTR_IPV6:
294 if (len == 16) {
295 char buf[128];
296 const char *atxt;
297 struct in6_addr *addr = (struct in6_addr *) pos;
298 atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf));
299 printf(" Value: %s\n", atxt ? atxt : "?");
300 } else
301 printf(" Invalid IPv6 address length %d\n", len);
302 break;
303 #endif /* CONFIG_IPV6 */
305 case RADIUS_ATTR_HEXDUMP:
306 case RADIUS_ATTR_UNDIST:
307 printf(" Value:");
308 for (i = 0; i < len; i++)
309 printf(" %02x", pos[i]);
310 printf("\n");
311 break;
313 case RADIUS_ATTR_INT32:
314 if (len == 4)
315 printf(" Value: %u\n", WPA_GET_BE32(pos));
316 else
317 printf(" Invalid INT32 length %d\n", len);
318 break;
320 default:
321 break;
326 void radius_msg_dump(struct radius_msg *msg)
328 size_t i;
330 printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n",
331 msg->hdr->code, radius_code_string(msg->hdr->code),
332 msg->hdr->identifier, ntohs(msg->hdr->length));
334 for (i = 0; i < msg->attr_used; i++) {
335 struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
336 radius_msg_dump_attr(attr);
341 int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
342 size_t secret_len)
344 if (secret) {
345 u8 auth[MD5_MAC_LEN];
346 struct radius_attr_hdr *attr;
348 os_memset(auth, 0, MD5_MAC_LEN);
349 attr = radius_msg_add_attr(msg,
350 RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
351 auth, MD5_MAC_LEN);
352 if (attr == NULL) {
353 wpa_printf(MSG_WARNING, "RADIUS: Could not add "
354 "Message-Authenticator");
355 return -1;
357 msg->hdr->length = htons(wpabuf_len(msg->buf));
358 hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
359 wpabuf_len(msg->buf), (u8 *) (attr + 1));
360 } else
361 msg->hdr->length = htons(wpabuf_len(msg->buf));
363 if (wpabuf_len(msg->buf) > 0xffff) {
364 wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
365 (unsigned long) wpabuf_len(msg->buf));
366 return -1;
368 return 0;
372 int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
373 size_t secret_len, const u8 *req_authenticator)
375 u8 auth[MD5_MAC_LEN];
376 struct radius_attr_hdr *attr;
377 const u8 *addr[4];
378 size_t len[4];
380 os_memset(auth, 0, MD5_MAC_LEN);
381 attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
382 auth, MD5_MAC_LEN);
383 if (attr == NULL) {
384 printf("WARNING: Could not add Message-Authenticator\n");
385 return -1;
387 msg->hdr->length = htons(wpabuf_len(msg->buf));
388 os_memcpy(msg->hdr->authenticator, req_authenticator,
389 sizeof(msg->hdr->authenticator));
390 hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
391 wpabuf_len(msg->buf), (u8 *) (attr + 1));
393 /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
394 addr[0] = (u8 *) msg->hdr;
395 len[0] = 1 + 1 + 2;
396 addr[1] = req_authenticator;
397 len[1] = MD5_MAC_LEN;
398 addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr);
399 len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
400 addr[3] = secret;
401 len[3] = secret_len;
402 md5_vector(4, addr, len, msg->hdr->authenticator);
404 if (wpabuf_len(msg->buf) > 0xffff) {
405 wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
406 (unsigned long) wpabuf_len(msg->buf));
407 return -1;
409 return 0;
413 void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
414 size_t secret_len)
416 const u8 *addr[2];
417 size_t len[2];
419 msg->hdr->length = htons(wpabuf_len(msg->buf));
420 os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN);
421 addr[0] = wpabuf_head(msg->buf);
422 len[0] = wpabuf_len(msg->buf);
423 addr[1] = secret;
424 len[1] = secret_len;
425 md5_vector(2, addr, len, msg->hdr->authenticator);
427 if (wpabuf_len(msg->buf) > 0xffff) {
428 wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)",
429 (unsigned long) wpabuf_len(msg->buf));
434 static int radius_msg_add_attr_to_array(struct radius_msg *msg,
435 struct radius_attr_hdr *attr)
437 if (msg->attr_used >= msg->attr_size) {
438 size_t *nattr_pos;
439 int nlen = msg->attr_size * 2;
441 nattr_pos = os_realloc(msg->attr_pos,
442 nlen * sizeof(*msg->attr_pos));
443 if (nattr_pos == NULL)
444 return -1;
446 msg->attr_pos = nattr_pos;
447 msg->attr_size = nlen;
450 msg->attr_pos[msg->attr_used++] =
451 (unsigned char *) attr - wpabuf_head_u8(msg->buf);
453 return 0;
457 struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
458 const u8 *data, size_t data_len)
460 size_t buf_needed;
461 struct radius_attr_hdr *attr;
463 if (data_len > RADIUS_MAX_ATTR_LEN) {
464 printf("radius_msg_add_attr: too long attribute (%lu bytes)\n",
465 (unsigned long) data_len);
466 return NULL;
469 buf_needed = sizeof(*attr) + data_len;
471 if (wpabuf_tailroom(msg->buf) < buf_needed) {
472 /* allocate more space for message buffer */
473 if (wpabuf_resize(&msg->buf, buf_needed) < 0)
474 return NULL;
475 msg->hdr = wpabuf_mhead(msg->buf);
478 attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr));
479 attr->type = type;
480 attr->length = sizeof(*attr) + data_len;
481 wpabuf_put_data(msg->buf, data, data_len);
483 if (radius_msg_add_attr_to_array(msg, attr))
484 return NULL;
486 return attr;
491 * radius_msg_parse - Parse a RADIUS message
492 * @data: RADIUS message to be parsed
493 * @len: Length of data buffer in octets
494 * Returns: Parsed RADIUS message or %NULL on failure
496 * This parses a RADIUS message and makes a copy of its data. The caller is
497 * responsible for freeing the returned data with radius_msg_free().
499 struct radius_msg * radius_msg_parse(const u8 *data, size_t len)
501 struct radius_msg *msg;
502 struct radius_hdr *hdr;
503 struct radius_attr_hdr *attr;
504 size_t msg_len;
505 unsigned char *pos, *end;
507 if (data == NULL || len < sizeof(*hdr))
508 return NULL;
510 hdr = (struct radius_hdr *) data;
512 msg_len = ntohs(hdr->length);
513 if (msg_len < sizeof(*hdr) || msg_len > len) {
514 wpa_printf(MSG_INFO, "RADIUS: Invalid message length");
515 return NULL;
518 if (msg_len < len) {
519 wpa_printf(MSG_DEBUG, "RADIUS: Ignored %lu extra bytes after "
520 "RADIUS message", (unsigned long) len - msg_len);
523 msg = os_zalloc(sizeof(*msg));
524 if (msg == NULL)
525 return NULL;
527 msg->buf = wpabuf_alloc_copy(data, msg_len);
528 if (msg->buf == NULL || radius_msg_initialize(msg)) {
529 radius_msg_free(msg);
530 return NULL;
532 msg->hdr = wpabuf_mhead(msg->buf);
534 /* parse attributes */
535 pos = wpabuf_mhead_u8(msg->buf) + sizeof(struct radius_hdr);
536 end = wpabuf_mhead_u8(msg->buf) + wpabuf_len(msg->buf);
537 while (pos < end) {
538 if ((size_t) (end - pos) < sizeof(*attr))
539 goto fail;
541 attr = (struct radius_attr_hdr *) pos;
543 if (pos + attr->length > end || attr->length < sizeof(*attr))
544 goto fail;
546 /* TODO: check that attr->length is suitable for attr->type */
548 if (radius_msg_add_attr_to_array(msg, attr))
549 goto fail;
551 pos += attr->length;
554 return msg;
556 fail:
557 radius_msg_free(msg);
558 return NULL;
562 int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len)
564 const u8 *pos = data;
565 size_t left = data_len;
567 while (left > 0) {
568 int len;
569 if (left > RADIUS_MAX_ATTR_LEN)
570 len = RADIUS_MAX_ATTR_LEN;
571 else
572 len = left;
574 if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE,
575 pos, len))
576 return 0;
578 pos += len;
579 left -= len;
582 return 1;
586 u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len)
588 u8 *eap, *pos;
589 size_t len, i;
590 struct radius_attr_hdr *attr;
592 if (msg == NULL)
593 return NULL;
595 len = 0;
596 for (i = 0; i < msg->attr_used; i++) {
597 attr = radius_get_attr_hdr(msg, i);
598 if (attr->type == RADIUS_ATTR_EAP_MESSAGE)
599 len += attr->length - sizeof(struct radius_attr_hdr);
602 if (len == 0)
603 return NULL;
605 eap = os_malloc(len);
606 if (eap == NULL)
607 return NULL;
609 pos = eap;
610 for (i = 0; i < msg->attr_used; i++) {
611 attr = radius_get_attr_hdr(msg, i);
612 if (attr->type == RADIUS_ATTR_EAP_MESSAGE) {
613 int flen = attr->length - sizeof(*attr);
614 os_memcpy(pos, attr + 1, flen);
615 pos += flen;
619 if (eap_len)
620 *eap_len = len;
622 return eap;
626 int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
627 size_t secret_len, const u8 *req_auth)
629 u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN];
630 u8 orig_authenticator[16];
631 struct radius_attr_hdr *attr = NULL, *tmp;
632 size_t i;
634 for (i = 0; i < msg->attr_used; i++) {
635 tmp = radius_get_attr_hdr(msg, i);
636 if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
637 if (attr != NULL) {
638 printf("Multiple Message-Authenticator "
639 "attributes in RADIUS message\n");
640 return 1;
642 attr = tmp;
646 if (attr == NULL) {
647 printf("No Message-Authenticator attribute found\n");
648 return 1;
651 os_memcpy(orig, attr + 1, MD5_MAC_LEN);
652 os_memset(attr + 1, 0, MD5_MAC_LEN);
653 if (req_auth) {
654 os_memcpy(orig_authenticator, msg->hdr->authenticator,
655 sizeof(orig_authenticator));
656 os_memcpy(msg->hdr->authenticator, req_auth,
657 sizeof(msg->hdr->authenticator));
659 hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
660 wpabuf_len(msg->buf), auth);
661 os_memcpy(attr + 1, orig, MD5_MAC_LEN);
662 if (req_auth) {
663 os_memcpy(msg->hdr->authenticator, orig_authenticator,
664 sizeof(orig_authenticator));
667 if (os_memcmp(orig, auth, MD5_MAC_LEN) != 0) {
668 printf("Invalid Message-Authenticator!\n");
669 return 1;
672 return 0;
676 int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
677 size_t secret_len, struct radius_msg *sent_msg, int auth)
679 const u8 *addr[4];
680 size_t len[4];
681 u8 hash[MD5_MAC_LEN];
683 if (sent_msg == NULL) {
684 printf("No matching Access-Request message found\n");
685 return 1;
688 if (auth &&
689 radius_msg_verify_msg_auth(msg, secret, secret_len,
690 sent_msg->hdr->authenticator)) {
691 return 1;
694 /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
695 addr[0] = (u8 *) msg->hdr;
696 len[0] = 1 + 1 + 2;
697 addr[1] = sent_msg->hdr->authenticator;
698 len[1] = MD5_MAC_LEN;
699 addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr);
700 len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
701 addr[3] = secret;
702 len[3] = secret_len;
703 md5_vector(4, addr, len, hash);
704 if (os_memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
705 printf("Response Authenticator invalid!\n");
706 return 1;
709 return 0;
713 int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
714 u8 type)
716 struct radius_attr_hdr *attr;
717 size_t i;
718 int count = 0;
720 for (i = 0; i < src->attr_used; i++) {
721 attr = radius_get_attr_hdr(src, i);
722 if (attr->type == type) {
723 if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1),
724 attr->length - sizeof(*attr)))
725 return -1;
726 count++;
730 return count;
734 /* Create Request Authenticator. The value should be unique over the lifetime
735 * of the shared secret between authenticator and authentication server.
736 * Use one-way MD5 hash calculated from current timestamp and some data given
737 * by the caller. */
738 void radius_msg_make_authenticator(struct radius_msg *msg,
739 const u8 *data, size_t len)
741 struct os_time tv;
742 long int l;
743 const u8 *addr[3];
744 size_t elen[3];
746 os_get_time(&tv);
747 l = os_random();
748 addr[0] = (u8 *) &tv;
749 elen[0] = sizeof(tv);
750 addr[1] = data;
751 elen[1] = len;
752 addr[2] = (u8 *) &l;
753 elen[2] = sizeof(l);
754 md5_vector(3, addr, elen, msg->hdr->authenticator);
758 /* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message.
759 * Returns the Attribute payload and sets alen to indicate the length of the
760 * payload if a vendor attribute with subtype is found, otherwise returns NULL.
761 * The returned payload is allocated with os_malloc() and caller must free it
762 * by calling os_free().
764 static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor,
765 u8 subtype, size_t *alen)
767 u8 *data, *pos;
768 size_t i, len;
770 if (msg == NULL)
771 return NULL;
773 for (i = 0; i < msg->attr_used; i++) {
774 struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
775 size_t left;
776 u32 vendor_id;
777 struct radius_attr_vendor *vhdr;
779 if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC)
780 continue;
782 left = attr->length - sizeof(*attr);
783 if (left < 4)
784 continue;
786 pos = (u8 *) (attr + 1);
788 os_memcpy(&vendor_id, pos, 4);
789 pos += 4;
790 left -= 4;
792 if (ntohl(vendor_id) != vendor)
793 continue;
795 while (left >= sizeof(*vhdr)) {
796 vhdr = (struct radius_attr_vendor *) pos;
797 if (vhdr->vendor_length > left ||
798 vhdr->vendor_length < sizeof(*vhdr)) {
799 left = 0;
800 break;
802 if (vhdr->vendor_type != subtype) {
803 pos += vhdr->vendor_length;
804 left -= vhdr->vendor_length;
805 continue;
808 len = vhdr->vendor_length - sizeof(*vhdr);
809 data = os_malloc(len);
810 if (data == NULL)
811 return NULL;
812 os_memcpy(data, pos + sizeof(*vhdr), len);
813 if (alen)
814 *alen = len;
815 return data;
819 return NULL;
823 static u8 * decrypt_ms_key(const u8 *key, size_t len,
824 const u8 *req_authenticator,
825 const u8 *secret, size_t secret_len, size_t *reslen)
827 u8 *plain, *ppos, *res;
828 const u8 *pos;
829 size_t left, plen;
830 u8 hash[MD5_MAC_LEN];
831 int i, first = 1;
832 const u8 *addr[3];
833 size_t elen[3];
835 /* key: 16-bit salt followed by encrypted key info */
837 if (len < 2 + 16)
838 return NULL;
840 pos = key + 2;
841 left = len - 2;
842 if (left % 16) {
843 printf("Invalid ms key len %lu\n", (unsigned long) left);
844 return NULL;
847 plen = left;
848 ppos = plain = os_malloc(plen);
849 if (plain == NULL)
850 return NULL;
851 plain[0] = 0;
853 while (left > 0) {
854 /* b(1) = MD5(Secret + Request-Authenticator + Salt)
855 * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
857 addr[0] = secret;
858 elen[0] = secret_len;
859 if (first) {
860 addr[1] = req_authenticator;
861 elen[1] = MD5_MAC_LEN;
862 addr[2] = key;
863 elen[2] = 2; /* Salt */
864 } else {
865 addr[1] = pos - MD5_MAC_LEN;
866 elen[1] = MD5_MAC_LEN;
868 md5_vector(first ? 3 : 2, addr, elen, hash);
869 first = 0;
871 for (i = 0; i < MD5_MAC_LEN; i++)
872 *ppos++ = *pos++ ^ hash[i];
873 left -= MD5_MAC_LEN;
876 if (plain[0] == 0 || plain[0] > plen - 1) {
877 printf("Failed to decrypt MPPE key\n");
878 os_free(plain);
879 return NULL;
882 res = os_malloc(plain[0]);
883 if (res == NULL) {
884 os_free(plain);
885 return NULL;
887 os_memcpy(res, plain + 1, plain[0]);
888 if (reslen)
889 *reslen = plain[0];
890 os_free(plain);
891 return res;
895 static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt,
896 const u8 *req_authenticator,
897 const u8 *secret, size_t secret_len,
898 u8 *ebuf, size_t *elen)
900 int i, len, first = 1;
901 u8 hash[MD5_MAC_LEN], saltbuf[2], *pos;
902 const u8 *addr[3];
903 size_t _len[3];
905 WPA_PUT_BE16(saltbuf, salt);
907 len = 1 + key_len;
908 if (len & 0x0f) {
909 len = (len & 0xf0) + 16;
911 os_memset(ebuf, 0, len);
912 ebuf[0] = key_len;
913 os_memcpy(ebuf + 1, key, key_len);
915 *elen = len;
917 pos = ebuf;
918 while (len > 0) {
919 /* b(1) = MD5(Secret + Request-Authenticator + Salt)
920 * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
921 addr[0] = secret;
922 _len[0] = secret_len;
923 if (first) {
924 addr[1] = req_authenticator;
925 _len[1] = MD5_MAC_LEN;
926 addr[2] = saltbuf;
927 _len[2] = sizeof(saltbuf);
928 } else {
929 addr[1] = pos - MD5_MAC_LEN;
930 _len[1] = MD5_MAC_LEN;
932 md5_vector(first ? 3 : 2, addr, _len, hash);
933 first = 0;
935 for (i = 0; i < MD5_MAC_LEN; i++)
936 *pos++ ^= hash[i];
938 len -= MD5_MAC_LEN;
943 struct radius_ms_mppe_keys *
944 radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
945 const u8 *secret, size_t secret_len)
947 u8 *key;
948 size_t keylen;
949 struct radius_ms_mppe_keys *keys;
951 if (msg == NULL || sent_msg == NULL)
952 return NULL;
954 keys = os_zalloc(sizeof(*keys));
955 if (keys == NULL)
956 return NULL;
958 key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
959 RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY,
960 &keylen);
961 if (key) {
962 keys->send = decrypt_ms_key(key, keylen,
963 sent_msg->hdr->authenticator,
964 secret, secret_len,
965 &keys->send_len);
966 os_free(key);
969 key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
970 RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY,
971 &keylen);
972 if (key) {
973 keys->recv = decrypt_ms_key(key, keylen,
974 sent_msg->hdr->authenticator,
975 secret, secret_len,
976 &keys->recv_len);
977 os_free(key);
980 return keys;
984 struct radius_ms_mppe_keys *
985 radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
986 const u8 *secret, size_t secret_len)
988 u8 *key;
989 size_t keylen;
990 struct radius_ms_mppe_keys *keys;
992 if (msg == NULL || sent_msg == NULL)
993 return NULL;
995 keys = os_zalloc(sizeof(*keys));
996 if (keys == NULL)
997 return NULL;
999 key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO,
1000 RADIUS_CISCO_AV_PAIR, &keylen);
1001 if (key && keylen == 51 &&
1002 os_memcmp(key, "leap:session-key=", 17) == 0) {
1003 keys->recv = decrypt_ms_key(key + 17, keylen - 17,
1004 sent_msg->hdr->authenticator,
1005 secret, secret_len,
1006 &keys->recv_len);
1008 os_free(key);
1010 return keys;
1014 int radius_msg_add_mppe_keys(struct radius_msg *msg,
1015 const u8 *req_authenticator,
1016 const u8 *secret, size_t secret_len,
1017 const u8 *send_key, size_t send_key_len,
1018 const u8 *recv_key, size_t recv_key_len)
1020 struct radius_attr_hdr *attr;
1021 u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT);
1022 u8 *buf;
1023 struct radius_attr_vendor *vhdr;
1024 u8 *pos;
1025 size_t elen;
1026 int hlen;
1027 u16 salt;
1029 hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2;
1031 /* MS-MPPE-Send-Key */
1032 buf = os_malloc(hlen + send_key_len + 16);
1033 if (buf == NULL) {
1034 return 0;
1036 pos = buf;
1037 os_memcpy(pos, &vendor_id, sizeof(vendor_id));
1038 pos += sizeof(vendor_id);
1039 vhdr = (struct radius_attr_vendor *) pos;
1040 vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY;
1041 pos = (u8 *) (vhdr + 1);
1042 salt = os_random() | 0x8000;
1043 WPA_PUT_BE16(pos, salt);
1044 pos += 2;
1045 encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret,
1046 secret_len, pos, &elen);
1047 vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
1049 attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
1050 buf, hlen + elen);
1051 os_free(buf);
1052 if (attr == NULL) {
1053 return 0;
1056 /* MS-MPPE-Recv-Key */
1057 buf = os_malloc(hlen + send_key_len + 16);
1058 if (buf == NULL) {
1059 return 0;
1061 pos = buf;
1062 os_memcpy(pos, &vendor_id, sizeof(vendor_id));
1063 pos += sizeof(vendor_id);
1064 vhdr = (struct radius_attr_vendor *) pos;
1065 vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY;
1066 pos = (u8 *) (vhdr + 1);
1067 salt ^= 1;
1068 WPA_PUT_BE16(pos, salt);
1069 pos += 2;
1070 encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret,
1071 secret_len, pos, &elen);
1072 vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
1074 attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
1075 buf, hlen + elen);
1076 os_free(buf);
1077 if (attr == NULL) {
1078 return 0;
1081 return 1;
1085 /* Add User-Password attribute to a RADIUS message and encrypt it as specified
1086 * in RFC 2865, Chap. 5.2 */
1087 struct radius_attr_hdr *
1088 radius_msg_add_attr_user_password(struct radius_msg *msg,
1089 const u8 *data, size_t data_len,
1090 const u8 *secret, size_t secret_len)
1092 u8 buf[128];
1093 int padlen, i;
1094 size_t buf_len, pos;
1095 const u8 *addr[2];
1096 size_t len[2];
1097 u8 hash[16];
1099 if (data_len > 128)
1100 return NULL;
1102 os_memcpy(buf, data, data_len);
1103 buf_len = data_len;
1105 padlen = data_len % 16;
1106 if (padlen) {
1107 padlen = 16 - padlen;
1108 os_memset(buf + data_len, 0, padlen);
1109 buf_len += padlen;
1112 addr[0] = secret;
1113 len[0] = secret_len;
1114 addr[1] = msg->hdr->authenticator;
1115 len[1] = 16;
1116 md5_vector(2, addr, len, hash);
1118 for (i = 0; i < 16; i++)
1119 buf[i] ^= hash[i];
1120 pos = 16;
1122 while (pos < buf_len) {
1123 addr[0] = secret;
1124 len[0] = secret_len;
1125 addr[1] = &buf[pos - 16];
1126 len[1] = 16;
1127 md5_vector(2, addr, len, hash);
1129 for (i = 0; i < 16; i++)
1130 buf[pos + i] ^= hash[i];
1132 pos += 16;
1135 return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD,
1136 buf, buf_len);
1140 int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len)
1142 struct radius_attr_hdr *attr = NULL, *tmp;
1143 size_t i, dlen;
1145 for (i = 0; i < msg->attr_used; i++) {
1146 tmp = radius_get_attr_hdr(msg, i);
1147 if (tmp->type == type) {
1148 attr = tmp;
1149 break;
1153 if (!attr)
1154 return -1;
1156 dlen = attr->length - sizeof(*attr);
1157 if (buf)
1158 os_memcpy(buf, (attr + 1), dlen > len ? len : dlen);
1159 return dlen;
1163 int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
1164 size_t *len, const u8 *start)
1166 size_t i;
1167 struct radius_attr_hdr *attr = NULL, *tmp;
1169 for (i = 0; i < msg->attr_used; i++) {
1170 tmp = radius_get_attr_hdr(msg, i);
1171 if (tmp->type == type &&
1172 (start == NULL || (u8 *) tmp > start)) {
1173 attr = tmp;
1174 break;
1178 if (!attr)
1179 return -1;
1181 *buf = (u8 *) (attr + 1);
1182 *len = attr->length - sizeof(*attr);
1183 return 0;
1187 int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len)
1189 size_t i;
1190 int count;
1192 for (count = 0, i = 0; i < msg->attr_used; i++) {
1193 struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
1194 if (attr->type == type &&
1195 attr->length >= sizeof(struct radius_attr_hdr) + min_len)
1196 count++;
1199 return count;
1203 struct radius_tunnel_attrs {
1204 int tag_used;
1205 int type; /* Tunnel-Type */
1206 int medium_type; /* Tunnel-Medium-Type */
1207 int vlanid;
1212 * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information
1213 * @msg: RADIUS message
1214 * Returns: VLAN ID for the first tunnel configuration of -1 if none is found
1216 int radius_msg_get_vlanid(struct radius_msg *msg)
1218 struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun;
1219 size_t i;
1220 struct radius_attr_hdr *attr = NULL;
1221 const u8 *data;
1222 char buf[10];
1223 size_t dlen;
1225 os_memset(&tunnel, 0, sizeof(tunnel));
1227 for (i = 0; i < msg->attr_used; i++) {
1228 attr = radius_get_attr_hdr(msg, i);
1229 data = (const u8 *) (attr + 1);
1230 dlen = attr->length - sizeof(*attr);
1231 if (attr->length < 3)
1232 continue;
1233 if (data[0] >= RADIUS_TUNNEL_TAGS)
1234 tun = &tunnel[0];
1235 else
1236 tun = &tunnel[data[0]];
1238 switch (attr->type) {
1239 case RADIUS_ATTR_TUNNEL_TYPE:
1240 if (attr->length != 6)
1241 break;
1242 tun->tag_used++;
1243 tun->type = WPA_GET_BE24(data + 1);
1244 break;
1245 case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE:
1246 if (attr->length != 6)
1247 break;
1248 tun->tag_used++;
1249 tun->medium_type = WPA_GET_BE24(data + 1);
1250 break;
1251 case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID:
1252 if (data[0] < RADIUS_TUNNEL_TAGS) {
1253 data++;
1254 dlen--;
1256 if (dlen >= sizeof(buf))
1257 break;
1258 os_memcpy(buf, data, dlen);
1259 buf[dlen] = '\0';
1260 tun->tag_used++;
1261 tun->vlanid = atoi(buf);
1262 break;
1266 for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) {
1267 tun = &tunnel[i];
1268 if (tun->tag_used &&
1269 tun->type == RADIUS_TUNNEL_TYPE_VLAN &&
1270 tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 &&
1271 tun->vlanid > 0)
1272 return tun->vlanid;
1275 return -1;
1279 void radius_free_class(struct radius_class_data *c)
1281 size_t i;
1282 if (c == NULL)
1283 return;
1284 for (i = 0; i < c->count; i++)
1285 os_free(c->attr[i].data);
1286 os_free(c->attr);
1287 c->attr = NULL;
1288 c->count = 0;
1292 int radius_copy_class(struct radius_class_data *dst,
1293 const struct radius_class_data *src)
1295 size_t i;
1297 if (src->attr == NULL)
1298 return 0;
1300 dst->attr = os_zalloc(src->count * sizeof(struct radius_attr_data));
1301 if (dst->attr == NULL)
1302 return -1;
1304 dst->count = 0;
1306 for (i = 0; i < src->count; i++) {
1307 dst->attr[i].data = os_malloc(src->attr[i].len);
1308 if (dst->attr[i].data == NULL)
1309 break;
1310 dst->count++;
1311 os_memcpy(dst->attr[i].data, src->attr[i].data,
1312 src->attr[i].len);
1313 dst->attr[i].len = src->attr[i].len;
1316 return 0;