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
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"
25 * struct radius_msg - RADIUS message structure for new and parsed messages
29 * buf - Allocated buffer for RADIUS message
34 * hdr - Pointer to the RADIUS header in buf
36 struct radius_hdr *hdr;
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.
47 * attr_size - Total size of the attribute pointer array
52 * attr_used - Total number of attributes in the array
58 struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg)
64 struct wpabuf * radius_msg_get_buf(struct radius_msg *msg)
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)
88 os_zalloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attr_pos));
89 if (msg->attr_pos == NULL)
92 msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT;
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
108 struct radius_msg * radius_msg_new(u8 code, u8 identifier)
110 struct radius_msg *msg;
112 msg = os_zalloc(sizeof(*msg));
116 msg->buf = wpabuf_alloc(RADIUS_DEFAULT_MSG_SIZE);
117 if (msg->buf == NULL || radius_msg_initialize(msg)) {
118 radius_msg_free(msg);
121 msg->hdr = wpabuf_put(msg->buf, sizeof(struct radius_hdr));
123 radius_msg_set_hdr(msg, code, identifier);
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)
138 wpabuf_free(msg->buf);
139 os_free(msg->attr_pos);
144 static const char *radius_code_string(u8 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 {
165 RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP,
166 RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6
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",
185 { RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id",
187 { RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id",
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",
193 { RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 },
194 { RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets",
196 { RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets",
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",
202 { RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets",
204 { RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets",
206 { RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause",
208 { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id",
210 { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 },
211 { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords",
213 { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords",
215 { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp",
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",
229 { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargable-User-Identity",
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)
240 for (i = 0; i < RADIUS_ATTRS; i++) {
241 if (type == radius_attrs[i].type)
242 return &radius_attrs[i];
249 static void print_char(char c)
251 if (c >= 32 && c < 127)
258 static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
260 struct radius_attr_type *attr;
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);
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:
278 for (i = 0; i < len; i++)
286 os_memcpy(&addr, pos, 4);
287 printf(" Value: %s\n", inet_ntoa(addr));
289 printf(" Invalid IP address length %d\n", len);
293 case RADIUS_ATTR_IPV6:
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 : "?");
301 printf(" Invalid IPv6 address length %d\n", len);
303 #endif /* CONFIG_IPV6 */
305 case RADIUS_ATTR_HEXDUMP:
306 case RADIUS_ATTR_UNDIST:
308 for (i = 0; i < len; i++)
309 printf(" %02x", pos[i]);
313 case RADIUS_ATTR_INT32:
315 printf(" Value: %u\n", WPA_GET_BE32(pos));
317 printf(" Invalid INT32 length %d\n", len);
326 void radius_msg_dump(struct radius_msg *msg)
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,
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,
353 wpa_printf(MSG_WARNING, "RADIUS: Could not add "
354 "Message-Authenticator");
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));
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));
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;
380 os_memset(auth, 0, MD5_MAC_LEN);
381 attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
384 printf("WARNING: Could not add Message-Authenticator\n");
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;
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);
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));
413 void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
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);
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) {
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)
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);
457 struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
458 const u8 *data, size_t data_len)
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);
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)
475 msg->hdr = wpabuf_mhead(msg->buf);
478 attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr));
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))
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;
505 unsigned char *pos, *end;
507 if (data == NULL || len < sizeof(*hdr))
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");
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));
527 msg->buf = wpabuf_alloc_copy(data, msg_len);
528 if (msg->buf == NULL || radius_msg_initialize(msg)) {
529 radius_msg_free(msg);
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);
538 if ((size_t) (end - pos) < sizeof(*attr))
541 attr = (struct radius_attr_hdr *) pos;
543 if (pos + attr->length > end || attr->length < sizeof(*attr))
546 /* TODO: check that attr->length is suitable for attr->type */
548 if (radius_msg_add_attr_to_array(msg, attr))
557 radius_msg_free(msg);
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;
569 if (left > RADIUS_MAX_ATTR_LEN)
570 len = RADIUS_MAX_ATTR_LEN;
574 if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE,
586 u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len)
590 struct radius_attr_hdr *attr;
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);
605 eap = os_malloc(len);
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);
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;
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) {
638 printf("Multiple Message-Authenticator "
639 "attributes in RADIUS message\n");
647 printf("No Message-Authenticator attribute found\n");
651 os_memcpy(orig, attr + 1, MD5_MAC_LEN);
652 os_memset(attr + 1, 0, MD5_MAC_LEN);
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);
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");
676 int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
677 size_t secret_len, struct radius_msg *sent_msg, int auth)
681 u8 hash[MD5_MAC_LEN];
683 if (sent_msg == NULL) {
684 printf("No matching Access-Request message found\n");
689 radius_msg_verify_msg_auth(msg, secret, secret_len,
690 sent_msg->hdr->authenticator)) {
694 /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
695 addr[0] = (u8 *) msg->hdr;
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);
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");
713 int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
716 struct radius_attr_hdr *attr;
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)))
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
738 void radius_msg_make_authenticator(struct radius_msg *msg,
739 const u8 *data, size_t len)
748 addr[0] = (u8 *) &tv;
749 elen[0] = sizeof(tv);
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)
773 for (i = 0; i < msg->attr_used; i++) {
774 struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
777 struct radius_attr_vendor *vhdr;
779 if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC)
782 left = attr->length - sizeof(*attr);
786 pos = (u8 *) (attr + 1);
788 os_memcpy(&vendor_id, pos, 4);
792 if (ntohl(vendor_id) != vendor)
795 while (left >= sizeof(*vhdr)) {
796 vhdr = (struct radius_attr_vendor *) pos;
797 if (vhdr->vendor_length > left ||
798 vhdr->vendor_length < sizeof(*vhdr)) {
802 if (vhdr->vendor_type != subtype) {
803 pos += vhdr->vendor_length;
804 left -= vhdr->vendor_length;
808 len = vhdr->vendor_length - sizeof(*vhdr);
809 data = os_malloc(len);
812 os_memcpy(data, pos + sizeof(*vhdr), len);
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;
830 u8 hash[MD5_MAC_LEN];
835 /* key: 16-bit salt followed by encrypted key info */
843 printf("Invalid ms key len %lu\n", (unsigned long) left);
848 ppos = plain = os_malloc(plen);
854 /* b(1) = MD5(Secret + Request-Authenticator + Salt)
855 * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
858 elen[0] = secret_len;
860 addr[1] = req_authenticator;
861 elen[1] = MD5_MAC_LEN;
863 elen[2] = 2; /* Salt */
865 addr[1] = pos - MD5_MAC_LEN;
866 elen[1] = MD5_MAC_LEN;
868 md5_vector(first ? 3 : 2, addr, elen, hash);
871 for (i = 0; i < MD5_MAC_LEN; i++)
872 *ppos++ = *pos++ ^ hash[i];
876 if (plain[0] == 0 || plain[0] > plen - 1) {
877 printf("Failed to decrypt MPPE key\n");
882 res = os_malloc(plain[0]);
887 os_memcpy(res, plain + 1, plain[0]);
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;
905 WPA_PUT_BE16(saltbuf, salt);
909 len = (len & 0xf0) + 16;
911 os_memset(ebuf, 0, len);
913 os_memcpy(ebuf + 1, key, key_len);
919 /* b(1) = MD5(Secret + Request-Authenticator + Salt)
920 * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
922 _len[0] = secret_len;
924 addr[1] = req_authenticator;
925 _len[1] = MD5_MAC_LEN;
927 _len[2] = sizeof(saltbuf);
929 addr[1] = pos - MD5_MAC_LEN;
930 _len[1] = MD5_MAC_LEN;
932 md5_vector(first ? 3 : 2, addr, _len, hash);
935 for (i = 0; i < MD5_MAC_LEN; i++)
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)
949 struct radius_ms_mppe_keys *keys;
951 if (msg == NULL || sent_msg == NULL)
954 keys = os_zalloc(sizeof(*keys));
958 key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
959 RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY,
962 keys->send = decrypt_ms_key(key, keylen,
963 sent_msg->hdr->authenticator,
969 key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
970 RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY,
973 keys->recv = decrypt_ms_key(key, keylen,
974 sent_msg->hdr->authenticator,
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)
990 struct radius_ms_mppe_keys *keys;
992 if (msg == NULL || sent_msg == NULL)
995 keys = os_zalloc(sizeof(*keys));
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,
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);
1023 struct radius_attr_vendor *vhdr;
1029 hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2;
1031 /* MS-MPPE-Send-Key */
1032 buf = os_malloc(hlen + send_key_len + 16);
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);
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,
1056 /* MS-MPPE-Recv-Key */
1057 buf = os_malloc(hlen + send_key_len + 16);
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);
1068 WPA_PUT_BE16(pos, salt);
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,
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)
1094 size_t buf_len, pos;
1102 os_memcpy(buf, data, data_len);
1105 padlen = data_len % 16;
1107 padlen = 16 - padlen;
1108 os_memset(buf + data_len, 0, padlen);
1113 len[0] = secret_len;
1114 addr[1] = msg->hdr->authenticator;
1116 md5_vector(2, addr, len, hash);
1118 for (i = 0; i < 16; i++)
1122 while (pos < buf_len) {
1124 len[0] = secret_len;
1125 addr[1] = &buf[pos - 16];
1127 md5_vector(2, addr, len, hash);
1129 for (i = 0; i < 16; i++)
1130 buf[pos + i] ^= hash[i];
1135 return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD,
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;
1145 for (i = 0; i < msg->attr_used; i++) {
1146 tmp = radius_get_attr_hdr(msg, i);
1147 if (tmp->type == type) {
1156 dlen = attr->length - sizeof(*attr);
1158 os_memcpy(buf, (attr + 1), dlen > len ? len : dlen);
1163 int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
1164 size_t *len, const u8 *start)
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)) {
1181 *buf = (u8 *) (attr + 1);
1182 *len = attr->length - sizeof(*attr);
1187 int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len)
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)
1203 struct radius_tunnel_attrs {
1205 int type; /* Tunnel-Type */
1206 int medium_type; /* Tunnel-Medium-Type */
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;
1220 struct radius_attr_hdr *attr = NULL;
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)
1233 if (data[0] >= RADIUS_TUNNEL_TAGS)
1236 tun = &tunnel[data[0]];
1238 switch (attr->type) {
1239 case RADIUS_ATTR_TUNNEL_TYPE:
1240 if (attr->length != 6)
1243 tun->type = WPA_GET_BE24(data + 1);
1245 case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE:
1246 if (attr->length != 6)
1249 tun->medium_type = WPA_GET_BE24(data + 1);
1251 case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID:
1252 if (data[0] < RADIUS_TUNNEL_TAGS) {
1256 if (dlen >= sizeof(buf))
1258 os_memcpy(buf, data, dlen);
1261 tun->vlanid = atoi(buf);
1266 for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) {
1268 if (tun->tag_used &&
1269 tun->type == RADIUS_TUNNEL_TYPE_VLAN &&
1270 tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 &&
1279 void radius_free_class(struct radius_class_data *c)
1284 for (i = 0; i < c->count; i++)
1285 os_free(c->attr[i].data);
1292 int radius_copy_class(struct radius_class_data *dst,
1293 const struct radius_class_data *src)
1297 if (src->attr == NULL)
1300 dst->attr = os_zalloc(src->count * sizeof(struct radius_attr_data));
1301 if (dst->attr == NULL)
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)
1311 os_memcpy(dst->attr[i].data, src->attr[i].data,
1313 dst->attr[i].len = src->attr[i].len;