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
;