2 * Host AP (software wireless LAN access point) user space daemon for
3 * Host AP kernel driver / RADIUS client
4 * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * Alternatively, this software may be distributed under the terms of BSD
13 * See README and COPYING for more details.
22 #ifndef CONFIG_NATIVE_WINDOWS
23 #include <netinet/in.h>
24 #include <sys/ioctl.h>
25 #include <sys/socket.h>
26 #include <arpa/inet.h>
27 #endif /* CONFIG_NATIVE_WINDOWS */
35 struct radius_msg
*radius_msg_new(u8 code
, u8 identifier
)
37 struct radius_msg
*msg
;
39 msg
= (struct radius_msg
*) malloc(sizeof(*msg
));
43 if (radius_msg_initialize(msg
, RADIUS_DEFAULT_MSG_SIZE
)) {
48 radius_msg_set_hdr(msg
, code
, identifier
);
54 int radius_msg_initialize(struct radius_msg
*msg
, size_t init_len
)
56 if (msg
== NULL
|| init_len
< sizeof(struct radius_hdr
))
59 memset(msg
, 0, sizeof(*msg
));
60 msg
->buf
= (unsigned char *) malloc(init_len
);
63 memset(msg
->buf
, 0, init_len
);
65 msg
->buf_size
= init_len
;
66 msg
->hdr
= (struct radius_hdr
*) msg
->buf
;
67 msg
->buf_used
= sizeof(*msg
->hdr
);
69 msg
->attrs
= (struct radius_attr_hdr
**)
70 malloc(RADIUS_DEFAULT_ATTR_COUNT
* sizeof(*msg
->attrs
));
71 if (msg
->attrs
== NULL
) {
78 msg
->attr_size
= RADIUS_DEFAULT_ATTR_COUNT
;
85 void radius_msg_set_hdr(struct radius_msg
*msg
, u8 code
, u8 identifier
)
87 msg
->hdr
->code
= code
;
88 msg
->hdr
->identifier
= identifier
;
92 void radius_msg_free(struct radius_msg
*msg
)
94 if (msg
->buf
!= NULL
) {
99 msg
->buf_size
= msg
->buf_used
= 0;
101 if (msg
->attrs
!= NULL
) {
105 msg
->attr_size
= msg
->attr_used
= 0;
109 static const char *radius_code_string(u8 code
)
112 case RADIUS_CODE_ACCESS_REQUEST
: return "Access-Request";
113 case RADIUS_CODE_ACCESS_ACCEPT
: return "Access-Accept";
114 case RADIUS_CODE_ACCESS_REJECT
: return "Access-Reject";
115 case RADIUS_CODE_ACCOUNTING_REQUEST
: return "Accounting-Request";
116 case RADIUS_CODE_ACCOUNTING_RESPONSE
: return "Accounting-Response";
117 case RADIUS_CODE_ACCESS_CHALLENGE
: return "Access-Challenge";
118 case RADIUS_CODE_STATUS_SERVER
: return "Status-Server";
119 case RADIUS_CODE_STATUS_CLIENT
: return "Status-Client";
120 case RADIUS_CODE_RESERVED
: return "Reserved";
121 default: return "?Unknown?";
126 struct radius_attr_type
{
130 RADIUS_ATTR_UNDIST
, RADIUS_ATTR_TEXT
, RADIUS_ATTR_IP
,
131 RADIUS_ATTR_HEXDUMP
, RADIUS_ATTR_INT32
, RADIUS_ATTR_IPV6
135 static struct radius_attr_type radius_attrs
[] =
137 { RADIUS_ATTR_USER_NAME
, "User-Name", RADIUS_ATTR_TEXT
},
138 { RADIUS_ATTR_USER_PASSWORD
, "User-Password", RADIUS_ATTR_UNDIST
},
139 { RADIUS_ATTR_NAS_IP_ADDRESS
, "NAS-IP-Address", RADIUS_ATTR_IP
},
140 { RADIUS_ATTR_NAS_PORT
, "NAS-Port", RADIUS_ATTR_INT32
},
141 { RADIUS_ATTR_FRAMED_MTU
, "Framed-MTU", RADIUS_ATTR_INT32
},
142 { RADIUS_ATTR_STATE
, "State", RADIUS_ATTR_UNDIST
},
143 { RADIUS_ATTR_CLASS
, "Class", RADIUS_ATTR_UNDIST
},
144 { RADIUS_ATTR_VENDOR_SPECIFIC
, "Vendor-Specific", RADIUS_ATTR_UNDIST
},
145 { RADIUS_ATTR_SESSION_TIMEOUT
, "Session-Timeout", RADIUS_ATTR_INT32
},
146 { RADIUS_ATTR_IDLE_TIMEOUT
, "Idle-Timeout", RADIUS_ATTR_INT32
},
147 { RADIUS_ATTR_TERMINATION_ACTION
, "Termination-Action",
149 { RADIUS_ATTR_CALLED_STATION_ID
, "Called-Station-Id",
151 { RADIUS_ATTR_CALLING_STATION_ID
, "Calling-Station-Id",
153 { RADIUS_ATTR_NAS_IDENTIFIER
, "NAS-Identifier", RADIUS_ATTR_TEXT
},
154 { RADIUS_ATTR_ACCT_STATUS_TYPE
, "Acct-Status-Type",
156 { RADIUS_ATTR_ACCT_DELAY_TIME
, "Acct-Delay-Time", RADIUS_ATTR_INT32
},
157 { RADIUS_ATTR_ACCT_INPUT_OCTETS
, "Acct-Input-Octets",
159 { RADIUS_ATTR_ACCT_OUTPUT_OCTETS
, "Acct-Output-Octets",
161 { RADIUS_ATTR_ACCT_SESSION_ID
, "Acct-Session-Id", RADIUS_ATTR_TEXT
},
162 { RADIUS_ATTR_ACCT_AUTHENTIC
, "Acct-Authentic", RADIUS_ATTR_INT32
},
163 { RADIUS_ATTR_ACCT_SESSION_TIME
, "Acct-Session-Time",
165 { RADIUS_ATTR_ACCT_INPUT_PACKETS
, "Acct-Input-Packets",
167 { RADIUS_ATTR_ACCT_OUTPUT_PACKETS
, "Acct-Output-Packets",
169 { RADIUS_ATTR_ACCT_TERMINATE_CAUSE
, "Acct-Terminate-Cause",
171 { RADIUS_ATTR_ACCT_MULTI_SESSION_ID
, "Acct-Multi-Session-Id",
173 { RADIUS_ATTR_ACCT_LINK_COUNT
, "Acct-Link-Count", RADIUS_ATTR_INT32
},
174 { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS
, "Acct-Input-Gigawords",
176 { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS
, "Acct-Output-Gigawords",
178 { RADIUS_ATTR_EVENT_TIMESTAMP
, "Event-Timestamp",
180 { RADIUS_ATTR_NAS_PORT_TYPE
, "NAS-Port-Type", RADIUS_ATTR_INT32
},
181 { RADIUS_ATTR_CONNECT_INFO
, "Connect-Info", RADIUS_ATTR_TEXT
},
182 { RADIUS_ATTR_EAP_MESSAGE
, "EAP-Message", RADIUS_ATTR_UNDIST
},
183 { RADIUS_ATTR_MESSAGE_AUTHENTICATOR
, "Message-Authenticator",
184 RADIUS_ATTR_UNDIST
},
185 { RADIUS_ATTR_ACCT_INTERIM_INTERVAL
, "Acct-Interim-Interval",
187 { RADIUS_ATTR_NAS_IPV6_ADDRESS
, "NAS-IPv6-Address", RADIUS_ATTR_IPV6
},
189 #define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0]))
192 static struct radius_attr_type
*radius_get_attr_type(u8 type
)
196 for (i
= 0; i
< RADIUS_ATTRS
; i
++) {
197 if (type
== radius_attrs
[i
].type
)
198 return &radius_attrs
[i
];
205 static void radius_msg_dump_attr(struct radius_attr_hdr
*hdr
)
207 struct radius_attr_type
*attr
;
211 attr
= radius_get_attr_type(hdr
->type
);
213 printf(" Attribute %d (%s) length=%d\n",
214 hdr
->type
, attr
? attr
->name
: "?Unknown?", hdr
->length
);
219 len
= hdr
->length
- sizeof(struct radius_attr_hdr
);
220 pos
= (unsigned char *) (hdr
+ 1);
222 switch (attr
->data_type
) {
223 case RADIUS_ATTR_TEXT
:
225 for (i
= 0; i
< len
; i
++)
232 struct in_addr
*addr
= (struct in_addr
*) pos
;
233 printf(" Value: %s\n", inet_ntoa(*addr
));
235 printf(" Invalid IP address length %d\n", len
);
239 case RADIUS_ATTR_IPV6
:
243 struct in6_addr
*addr
= (struct in6_addr
*) pos
;
244 atxt
= inet_ntop(AF_INET6
, addr
, buf
, sizeof(buf
));
245 printf(" Value: %s\n", atxt
? atxt
: "?");
247 printf(" Invalid IPv6 address length %d\n", len
);
249 #endif /* CONFIG_IPV6 */
251 case RADIUS_ATTR_HEXDUMP
:
252 case RADIUS_ATTR_UNDIST
:
254 for (i
= 0; i
< len
; i
++)
255 printf(" %02x", pos
[i
]);
259 case RADIUS_ATTR_INT32
:
261 printf(" Value: %u\n", WPA_GET_BE32(pos
));
263 printf(" Invalid INT32 length %d\n", len
);
272 void radius_msg_dump(struct radius_msg
*msg
)
276 printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n",
277 msg
->hdr
->code
, radius_code_string(msg
->hdr
->code
),
278 msg
->hdr
->identifier
, ntohs(msg
->hdr
->length
));
280 for (i
= 0; i
< msg
->attr_used
; i
++) {
281 radius_msg_dump_attr(msg
->attrs
[i
]);
286 int radius_msg_finish(struct radius_msg
*msg
, u8
*secret
, size_t secret_len
)
289 u8 auth
[MD5_MAC_LEN
];
290 struct radius_attr_hdr
*attr
;
292 memset(auth
, 0, MD5_MAC_LEN
);
293 attr
= radius_msg_add_attr(msg
,
294 RADIUS_ATTR_MESSAGE_AUTHENTICATOR
,
297 printf("WARNING: Could not add "
298 "Message-Authenticator\n");
301 msg
->hdr
->length
= htons(msg
->buf_used
);
302 hmac_md5(secret
, secret_len
, msg
->buf
, msg
->buf_used
,
305 msg
->hdr
->length
= htons(msg
->buf_used
);
307 if (msg
->buf_used
> 0xffff) {
308 printf("WARNING: too long RADIUS message (%lu)\n",
309 (unsigned long) msg
->buf_used
);
316 int radius_msg_finish_srv(struct radius_msg
*msg
, const u8
*secret
,
317 size_t secret_len
, const u8
*req_authenticator
)
319 u8 auth
[MD5_MAC_LEN
];
320 struct radius_attr_hdr
*attr
;
324 memset(auth
, 0, MD5_MAC_LEN
);
325 attr
= radius_msg_add_attr(msg
, RADIUS_ATTR_MESSAGE_AUTHENTICATOR
,
328 printf("WARNING: Could not add Message-Authenticator\n");
331 msg
->hdr
->length
= htons(msg
->buf_used
);
332 memcpy(msg
->hdr
->authenticator
, req_authenticator
,
333 sizeof(msg
->hdr
->authenticator
));
334 hmac_md5(secret
, secret_len
, msg
->buf
, msg
->buf_used
,
337 /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
338 addr
[0] = (u8
*) msg
->hdr
;
340 addr
[1] = req_authenticator
;
341 len
[1] = MD5_MAC_LEN
;
342 addr
[2] = (u8
*) (msg
->hdr
+ 1);
343 len
[2] = msg
->buf_used
- sizeof(*msg
->hdr
);
346 md5_vector(4, addr
, len
, msg
->hdr
->authenticator
);
348 if (msg
->buf_used
> 0xffff) {
349 printf("WARNING: too long RADIUS message (%lu)\n",
350 (unsigned long) msg
->buf_used
);
357 void radius_msg_finish_acct(struct radius_msg
*msg
, u8
*secret
,
363 msg
->hdr
->length
= htons(msg
->buf_used
);
364 memset(msg
->hdr
->authenticator
, 0, MD5_MAC_LEN
);
366 len
[0] = msg
->buf_used
;
369 md5_vector(2, addr
, len
, msg
->hdr
->authenticator
);
371 if (msg
->buf_used
> 0xffff) {
372 printf("WARNING: too long RADIUS messages (%lu)\n",
373 (unsigned long) msg
->buf_used
);
378 static int radius_msg_add_attr_to_array(struct radius_msg
*msg
,
379 struct radius_attr_hdr
*attr
)
381 if (msg
->attr_used
>= msg
->attr_size
) {
382 struct radius_attr_hdr
**nattrs
;
383 int nlen
= msg
->attr_size
* 2;
385 nattrs
= (struct radius_attr_hdr
**)
386 realloc(msg
->attrs
, nlen
* sizeof(*msg
->attrs
));
392 msg
->attr_size
= nlen
;
395 msg
->attrs
[msg
->attr_used
++] = attr
;
401 struct radius_attr_hdr
*radius_msg_add_attr(struct radius_msg
*msg
, u8 type
,
402 const u8
*data
, size_t data_len
)
405 struct radius_attr_hdr
*attr
;
407 if (data_len
> RADIUS_MAX_ATTR_LEN
) {
408 printf("radius_msg_add_attr: too long attribute (%lu bytes)\n",
409 (unsigned long) data_len
);
413 buf_needed
= msg
->buf_used
+ sizeof(*attr
) + data_len
;
415 if (msg
->buf_size
< buf_needed
) {
416 /* allocate more space for message buffer */
418 int nlen
= msg
->buf_size
;
421 while (nlen
< buf_needed
)
423 nbuf
= (unsigned char *) realloc(msg
->buf
, nlen
);
426 diff
= nbuf
- msg
->buf
;
428 msg
->hdr
= (struct radius_hdr
*) msg
->buf
;
429 /* adjust attr pointers to match with the new buffer */
430 for (i
= 0; i
< msg
->attr_used
; i
++)
431 msg
->attrs
[i
] = (struct radius_attr_hdr
*)
432 (((u8
*) msg
->attrs
[i
]) + diff
);
433 memset(msg
->buf
+ msg
->buf_size
, 0, nlen
- msg
->buf_size
);
434 msg
->buf_size
= nlen
;
437 attr
= (struct radius_attr_hdr
*) (msg
->buf
+ msg
->buf_used
);
439 attr
->length
= sizeof(*attr
) + data_len
;
441 memcpy(attr
+ 1, data
, data_len
);
443 msg
->buf_used
+= sizeof(*attr
) + data_len
;
445 if (radius_msg_add_attr_to_array(msg
, attr
))
452 struct radius_msg
*radius_msg_parse(const u8
*data
, size_t len
)
454 struct radius_msg
*msg
;
455 struct radius_hdr
*hdr
;
456 struct radius_attr_hdr
*attr
;
458 unsigned char *pos
, *end
;
460 if (data
== NULL
|| len
< sizeof(*hdr
))
463 hdr
= (struct radius_hdr
*) data
;
465 msg_len
= ntohs(hdr
->length
);
466 if (msg_len
< sizeof(*hdr
) || msg_len
> len
) {
467 printf("Invalid RADIUS message length\n");
472 printf("Ignored %lu extra bytes after RADIUS message\n",
473 (unsigned long) len
- msg_len
);
476 msg
= (struct radius_msg
*) malloc(sizeof(*msg
));
480 if (radius_msg_initialize(msg
, msg_len
)) {
485 memcpy(msg
->buf
, data
, msg_len
);
486 msg
->buf_size
= msg
->buf_used
= msg_len
;
488 /* parse attributes */
489 pos
= (unsigned char *) (msg
->hdr
+ 1);
490 end
= msg
->buf
+ msg
->buf_used
;
492 if (end
- pos
< sizeof(*attr
))
495 attr
= (struct radius_attr_hdr
*) pos
;
497 if (pos
+ attr
->length
> end
|| attr
->length
< sizeof(*attr
))
500 /* TODO: check that attr->length is suitable for attr->type */
502 if (radius_msg_add_attr_to_array(msg
, attr
))
511 radius_msg_free(msg
);
517 int radius_msg_add_eap(struct radius_msg
*msg
, const u8
*data
, size_t data_len
)
519 const u8
*pos
= data
;
520 size_t left
= data_len
;
524 if (left
> RADIUS_MAX_ATTR_LEN
)
525 len
= RADIUS_MAX_ATTR_LEN
;
529 if (!radius_msg_add_attr(msg
, RADIUS_ATTR_EAP_MESSAGE
,
541 u8
*radius_msg_get_eap(struct radius_msg
*msg
, size_t *eap_len
)
551 for (i
= 0; i
< msg
->attr_used
; i
++) {
552 if (msg
->attrs
[i
]->type
== RADIUS_ATTR_EAP_MESSAGE
)
553 len
+= msg
->attrs
[i
]->length
-
554 sizeof(struct radius_attr_hdr
);
565 for (i
= 0; i
< msg
->attr_used
; i
++) {
566 if (msg
->attrs
[i
]->type
== RADIUS_ATTR_EAP_MESSAGE
) {
567 struct radius_attr_hdr
*attr
= msg
->attrs
[i
];
568 int flen
= attr
->length
- sizeof(*attr
);
569 memcpy(pos
, attr
+ 1, flen
);
581 int radius_msg_verify_msg_auth(struct radius_msg
*msg
, const u8
*secret
,
582 size_t secret_len
, const u8
*req_auth
)
584 u8 auth
[MD5_MAC_LEN
], orig
[MD5_MAC_LEN
];
585 u8 orig_authenticator
[16];
586 struct radius_attr_hdr
*attr
= NULL
;
589 for (i
= 0; i
< msg
->attr_used
; i
++) {
590 if (msg
->attrs
[i
]->type
== RADIUS_ATTR_MESSAGE_AUTHENTICATOR
) {
592 printf("Multiple Message-Authenticator "
593 "attributes in RADIUS message\n");
596 attr
= msg
->attrs
[i
];
601 printf("No Message-Authenticator attribute found\n");
605 memcpy(orig
, attr
+ 1, MD5_MAC_LEN
);
606 memset(attr
+ 1, 0, MD5_MAC_LEN
);
608 memcpy(orig_authenticator
, msg
->hdr
->authenticator
,
609 sizeof(orig_authenticator
));
610 memcpy(msg
->hdr
->authenticator
, req_auth
,
611 sizeof(msg
->hdr
->authenticator
));
613 hmac_md5(secret
, secret_len
, msg
->buf
, msg
->buf_used
, auth
);
614 memcpy(attr
+ 1, orig
, MD5_MAC_LEN
);
616 memcpy(msg
->hdr
->authenticator
, orig_authenticator
,
617 sizeof(orig_authenticator
));
620 if (memcmp(orig
, auth
, MD5_MAC_LEN
) != 0) {
621 printf("Invalid Message-Authenticator!\n");
629 int radius_msg_verify(struct radius_msg
*msg
, const u8
*secret
,
630 size_t secret_len
, struct radius_msg
*sent_msg
, int auth
)
634 u8 hash
[MD5_MAC_LEN
];
636 if (sent_msg
== NULL
) {
637 printf("No matching Access-Request message found\n");
642 radius_msg_verify_msg_auth(msg
, secret
, secret_len
,
643 sent_msg
->hdr
->authenticator
)) {
647 /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
648 addr
[0] = (u8
*) msg
->hdr
;
650 addr
[1] = sent_msg
->hdr
->authenticator
;
651 len
[1] = MD5_MAC_LEN
;
652 addr
[2] = (u8
*) (msg
->hdr
+ 1);
653 len
[2] = msg
->buf_used
- sizeof(*msg
->hdr
);
656 md5_vector(4, addr
, len
, hash
);
657 if (memcmp(hash
, msg
->hdr
->authenticator
, MD5_MAC_LEN
) != 0) {
658 printf("Response Authenticator invalid!\n");
667 int radius_msg_copy_attr(struct radius_msg
*dst
, struct radius_msg
*src
,
670 struct radius_attr_hdr
*attr
= NULL
;
673 for (i
= 0; i
< src
->attr_used
; i
++) {
674 if (src
->attrs
[i
]->type
== type
) {
675 attr
= src
->attrs
[i
];
683 if (!radius_msg_add_attr(dst
, type
, (u8
*) (attr
+ 1),
684 attr
->length
- sizeof(*attr
)))
691 /* Create Request Authenticator. The value should be unique over the lifetime
692 * of the shared secret between authenticator and authentication server.
693 * Use one-way MD5 hash calculated from current timestamp and some data given
695 void radius_msg_make_authenticator(struct radius_msg
*msg
,
696 u8
*data
, size_t len
)
703 gettimeofday(&tv
, NULL
);
705 addr
[0] = (u8
*) &tv
;
706 elen
[0] = sizeof(tv
);
711 md5_vector(3, addr
, elen
, msg
->hdr
->authenticator
);
715 /* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message.
716 * Returns the Attribute payload and sets alen to indicate the length of the
717 * payload if a vendor attribute with subtype is found, otherwise returns NULL.
718 * The returned payload is allocated with malloc() and caller must free it.
720 static u8
*radius_msg_get_vendor_attr(struct radius_msg
*msg
, u32 vendor
,
721 u8 subtype
, size_t *alen
)
730 for (i
= 0; i
< msg
->attr_used
; i
++) {
731 struct radius_attr_hdr
*attr
= msg
->attrs
[i
];
734 struct radius_attr_vendor
*vhdr
;
736 if (attr
->type
!= RADIUS_ATTR_VENDOR_SPECIFIC
)
739 left
= attr
->length
- sizeof(*attr
);
743 pos
= (u8
*) (attr
+ 1);
745 memcpy(&vendor_id
, pos
, 4);
749 if (ntohl(vendor_id
) != vendor
)
752 while (left
>= sizeof(*vhdr
)) {
753 vhdr
= (struct radius_attr_vendor
*) pos
;
754 if (vhdr
->vendor_length
> left
||
755 vhdr
->vendor_length
< sizeof(*vhdr
)) {
759 if (vhdr
->vendor_type
!= subtype
) {
760 pos
+= vhdr
->vendor_length
;
761 left
-= vhdr
->vendor_length
;
765 len
= vhdr
->vendor_length
- sizeof(*vhdr
);
769 memcpy(data
, pos
+ sizeof(*vhdr
), len
);
780 static u8
* decrypt_ms_key(const u8
*key
, size_t len
,
781 const u8
*req_authenticator
,
782 const u8
*secret
, size_t secret_len
, size_t *reslen
)
784 u8
*plain
, *ppos
, *res
;
787 u8 hash
[MD5_MAC_LEN
];
792 /* key: 16-bit salt followed by encrypted key info */
800 printf("Invalid ms key len %lu\n", (unsigned long) left
);
805 ppos
= plain
= malloc(plen
);
810 /* b(1) = MD5(Secret + Request-Authenticator + Salt)
811 * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
814 elen
[0] = secret_len
;
816 addr
[1] = req_authenticator
;
817 elen
[1] = MD5_MAC_LEN
;
819 elen
[2] = 2; /* Salt */
821 addr
[1] = pos
- MD5_MAC_LEN
;
822 elen
[1] = MD5_MAC_LEN
;
824 md5_vector(first
? 3 : 2, addr
, elen
, hash
);
827 for (i
= 0; i
< MD5_MAC_LEN
; i
++)
828 *ppos
++ = *pos
++ ^ hash
[i
];
832 if (plain
[0] > plen
- 1) {
833 printf("Failed to decrypt MPPE key\n");
838 res
= malloc(plain
[0]);
843 memcpy(res
, plain
+ 1, plain
[0]);
851 static void encrypt_ms_key(const u8
*key
, size_t key_len
, u16 salt
,
852 const u8
*req_authenticator
,
853 const u8
*secret
, size_t secret_len
,
854 u8
*ebuf
, size_t *elen
)
856 int i
, len
, first
= 1;
857 u8 hash
[MD5_MAC_LEN
], saltbuf
[2], *pos
;
861 saltbuf
[0] = salt
>> 8;
866 len
= (len
& 0xf0) + 16;
868 memset(ebuf
, 0, len
);
870 memcpy(ebuf
+ 1, key
, key_len
);
876 /* b(1) = MD5(Secret + Request-Authenticator + Salt)
877 * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
879 _len
[0] = secret_len
;
881 addr
[1] = req_authenticator
;
882 _len
[1] = MD5_MAC_LEN
;
884 _len
[2] = sizeof(saltbuf
);
886 addr
[1] = pos
- MD5_MAC_LEN
;
887 _len
[1] = MD5_MAC_LEN
;
889 md5_vector(first
? 3 : 2, addr
, _len
, hash
);
892 for (i
= 0; i
< MD5_MAC_LEN
; i
++)
900 struct radius_ms_mppe_keys
*
901 radius_msg_get_ms_keys(struct radius_msg
*msg
, struct radius_msg
*sent_msg
,
902 u8
*secret
, size_t secret_len
)
906 struct radius_ms_mppe_keys
*keys
;
908 if (msg
== NULL
|| sent_msg
== NULL
)
911 keys
= (struct radius_ms_mppe_keys
*) malloc(sizeof(*keys
));
915 memset(keys
, 0, sizeof(*keys
));
917 key
= radius_msg_get_vendor_attr(msg
, RADIUS_VENDOR_ID_MICROSOFT
,
918 RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY
,
921 keys
->send
= decrypt_ms_key(key
, keylen
,
922 sent_msg
->hdr
->authenticator
,
928 key
= radius_msg_get_vendor_attr(msg
, RADIUS_VENDOR_ID_MICROSOFT
,
929 RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY
,
932 keys
->recv
= decrypt_ms_key(key
, keylen
,
933 sent_msg
->hdr
->authenticator
,
943 struct radius_ms_mppe_keys
*
944 radius_msg_get_cisco_keys(struct radius_msg
*msg
, struct radius_msg
*sent_msg
,
945 u8
*secret
, size_t secret_len
)
949 struct radius_ms_mppe_keys
*keys
;
951 if (msg
== NULL
|| sent_msg
== NULL
)
954 keys
= (struct radius_ms_mppe_keys
*) malloc(sizeof(*keys
));
958 memset(keys
, 0, sizeof(*keys
));
960 key
= radius_msg_get_vendor_attr(msg
, RADIUS_VENDOR_ID_CISCO
,
961 RADIUS_CISCO_AV_PAIR
, &keylen
);
962 if (key
&& keylen
== 51 && memcmp(key
, "leap:session-key=", 17) == 0) {
963 keys
->recv
= decrypt_ms_key(key
+ 17, keylen
- 17,
964 sent_msg
->hdr
->authenticator
,
974 int radius_msg_add_mppe_keys(struct radius_msg
*msg
,
975 const u8
*req_authenticator
,
976 const u8
*secret
, size_t secret_len
,
977 const u8
*send_key
, size_t send_key_len
,
978 const u8
*recv_key
, size_t recv_key_len
)
980 struct radius_attr_hdr
*attr
;
981 u32 vendor_id
= htonl(RADIUS_VENDOR_ID_MICROSOFT
);
983 struct radius_attr_vendor
*vhdr
;
989 hlen
= sizeof(vendor_id
) + sizeof(*vhdr
) + 2;
991 /* MS-MPPE-Send-Key */
992 buf
= malloc(hlen
+ send_key_len
+ 16);
997 memcpy(pos
, &vendor_id
, sizeof(vendor_id
));
998 pos
+= sizeof(vendor_id
);
999 vhdr
= (struct radius_attr_vendor
*) pos
;
1000 vhdr
->vendor_type
= RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY
;
1001 pos
= (u8
*) (vhdr
+ 1);
1002 salt
= random() | 0x8000;
1005 encrypt_ms_key(send_key
, send_key_len
, salt
, req_authenticator
, secret
,
1006 secret_len
, pos
, &elen
);
1007 vhdr
->vendor_length
= hlen
+ elen
- sizeof(vendor_id
);
1009 attr
= radius_msg_add_attr(msg
, RADIUS_ATTR_VENDOR_SPECIFIC
,
1016 /* MS-MPPE-Recv-Key */
1017 buf
= malloc(hlen
+ send_key_len
+ 16);
1022 memcpy(pos
, &vendor_id
, sizeof(vendor_id
));
1023 pos
+= sizeof(vendor_id
);
1024 vhdr
= (struct radius_attr_vendor
*) pos
;
1025 vhdr
->vendor_type
= RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY
;
1026 pos
= (u8
*) (vhdr
+ 1);
1030 encrypt_ms_key(recv_key
, recv_key_len
, salt
, req_authenticator
, secret
,
1031 secret_len
, pos
, &elen
);
1032 vhdr
->vendor_length
= hlen
+ elen
- sizeof(vendor_id
);
1034 attr
= radius_msg_add_attr(msg
, RADIUS_ATTR_VENDOR_SPECIFIC
,
1045 /* Add User-Password attribute to a RADIUS message and encrypt it as specified
1046 * in RFC 2865, Chap. 5.2 */
1047 struct radius_attr_hdr
*
1048 radius_msg_add_attr_user_password(struct radius_msg
*msg
,
1049 u8
*data
, size_t data_len
,
1050 u8
*secret
, size_t secret_len
)
1062 memcpy(buf
, data
, data_len
);
1065 padlen
= data_len
% 16;
1067 padlen
= 16 - padlen
;
1068 memset(buf
+ data_len
, 0, padlen
);
1073 len
[0] = secret_len
;
1074 addr
[1] = msg
->hdr
->authenticator
;
1076 md5_vector(2, addr
, len
, hash
);
1078 for (i
= 0; i
< 16; i
++)
1082 while (pos
< buf_len
) {
1084 len
[0] = secret_len
;
1085 addr
[1] = &buf
[pos
- 16];
1087 md5_vector(2, addr
, len
, hash
);
1089 for (i
= 0; i
< 16; i
++)
1090 buf
[pos
+ i
] ^= hash
[i
];
1095 return radius_msg_add_attr(msg
, RADIUS_ATTR_USER_PASSWORD
,
1100 int radius_msg_get_attr(struct radius_msg
*msg
, u8 type
, u8
*buf
, size_t len
)
1103 struct radius_attr_hdr
*attr
= NULL
;
1106 for (i
= 0; i
< msg
->attr_used
; i
++) {
1107 if (msg
->attrs
[i
]->type
== type
) {
1108 attr
= msg
->attrs
[i
];
1116 dlen
= attr
->length
- sizeof(*attr
);
1118 memcpy(buf
, (attr
+ 1), dlen
> len
? len
: dlen
);
1123 int radius_msg_get_attr_ptr(struct radius_msg
*msg
, u8 type
, u8
**buf
,
1124 size_t *len
, const u8
*start
)
1127 struct radius_attr_hdr
*attr
= NULL
;
1129 for (i
= 0; i
< msg
->attr_used
; i
++) {
1130 if (msg
->attrs
[i
]->type
== type
&&
1131 (start
== NULL
|| (u8
*) msg
->attrs
[i
] > start
)) {
1132 attr
= msg
->attrs
[i
];
1140 *buf
= (u8
*) (attr
+ 1);
1141 *len
= attr
->length
- sizeof(*attr
);
1146 int radius_msg_count_attr(struct radius_msg
*msg
, u8 type
, int min_len
)
1150 for (count
= 0, i
= 0; i
< msg
->attr_used
; i
++) {
1151 if (msg
->attrs
[i
]->type
== type
&&
1152 msg
->attrs
[i
]->length
>=
1153 sizeof(struct radius_attr_hdr
) + min_len
)