$search
00001 /* 00002 * RADIUS message processing 00003 * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> 00004 * 00005 * This program is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU General Public License version 2 as 00007 * published by the Free Software Foundation. 00008 * 00009 * Alternatively, this software may be distributed under the terms of BSD 00010 * license. 00011 * 00012 * See README and COPYING for more details. 00013 */ 00014 00015 #include "utils/includes.h" 00016 00017 #include "utils/common.h" 00018 #include "utils/wpabuf.h" 00019 #include "crypto/md5.h" 00020 #include "crypto/crypto.h" 00021 #include "radius.h" 00022 00023 00027 struct radius_msg { 00031 struct wpabuf *buf; 00032 00036 struct radius_hdr *hdr; 00037 00044 size_t *attr_pos; 00045 00049 size_t attr_size; 00050 00054 size_t attr_used; 00055 }; 00056 00057 00058 struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg) 00059 { 00060 return msg->hdr; 00061 } 00062 00063 00064 struct wpabuf * radius_msg_get_buf(struct radius_msg *msg) 00065 { 00066 return msg->buf; 00067 } 00068 00069 00070 static struct radius_attr_hdr * 00071 radius_get_attr_hdr(struct radius_msg *msg, int idx) 00072 { 00073 return (struct radius_attr_hdr *) 00074 (wpabuf_mhead_u8(msg->buf) + msg->attr_pos[idx]); 00075 } 00076 00077 00078 static void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier) 00079 { 00080 msg->hdr->code = code; 00081 msg->hdr->identifier = identifier; 00082 } 00083 00084 00085 static int radius_msg_initialize(struct radius_msg *msg) 00086 { 00087 msg->attr_pos = 00088 os_zalloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attr_pos)); 00089 if (msg->attr_pos == NULL) 00090 return -1; 00091 00092 msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT; 00093 msg->attr_used = 0; 00094 00095 return 0; 00096 } 00097 00098 00108 struct radius_msg * radius_msg_new(u8 code, u8 identifier) 00109 { 00110 struct radius_msg *msg; 00111 00112 msg = os_zalloc(sizeof(*msg)); 00113 if (msg == NULL) 00114 return NULL; 00115 00116 msg->buf = wpabuf_alloc(RADIUS_DEFAULT_MSG_SIZE); 00117 if (msg->buf == NULL || radius_msg_initialize(msg)) { 00118 radius_msg_free(msg); 00119 return NULL; 00120 } 00121 msg->hdr = wpabuf_put(msg->buf, sizeof(struct radius_hdr)); 00122 00123 radius_msg_set_hdr(msg, code, identifier); 00124 00125 return msg; 00126 } 00127 00128 00133 void radius_msg_free(struct radius_msg *msg) 00134 { 00135 if (msg == NULL) 00136 return; 00137 00138 wpabuf_free(msg->buf); 00139 os_free(msg->attr_pos); 00140 os_free(msg); 00141 } 00142 00143 00144 static const char *radius_code_string(u8 code) 00145 { 00146 switch (code) { 00147 case RADIUS_CODE_ACCESS_REQUEST: return "Access-Request"; 00148 case RADIUS_CODE_ACCESS_ACCEPT: return "Access-Accept"; 00149 case RADIUS_CODE_ACCESS_REJECT: return "Access-Reject"; 00150 case RADIUS_CODE_ACCOUNTING_REQUEST: return "Accounting-Request"; 00151 case RADIUS_CODE_ACCOUNTING_RESPONSE: return "Accounting-Response"; 00152 case RADIUS_CODE_ACCESS_CHALLENGE: return "Access-Challenge"; 00153 case RADIUS_CODE_STATUS_SERVER: return "Status-Server"; 00154 case RADIUS_CODE_STATUS_CLIENT: return "Status-Client"; 00155 case RADIUS_CODE_RESERVED: return "Reserved"; 00156 default: return "?Unknown?"; 00157 } 00158 } 00159 00160 00161 struct radius_attr_type { 00162 u8 type; 00163 char *name; 00164 enum { 00165 RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP, 00166 RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6 00167 } data_type; 00168 }; 00169 00170 static struct radius_attr_type radius_attrs[] = 00171 { 00172 { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT }, 00173 { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST }, 00174 { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP }, 00175 { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 }, 00176 { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 }, 00177 { RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT }, 00178 { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST }, 00179 { RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST }, 00180 { RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST }, 00181 { RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 }, 00182 { RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 }, 00183 { RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action", 00184 RADIUS_ATTR_INT32 }, 00185 { RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id", 00186 RADIUS_ATTR_TEXT }, 00187 { RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id", 00188 RADIUS_ATTR_TEXT }, 00189 { RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT }, 00190 { RADIUS_ATTR_PROXY_STATE, "Proxy-State", RADIUS_ATTR_UNDIST }, 00191 { RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type", 00192 RADIUS_ATTR_INT32 }, 00193 { RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 }, 00194 { RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets", 00195 RADIUS_ATTR_INT32 }, 00196 { RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets", 00197 RADIUS_ATTR_INT32 }, 00198 { RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT }, 00199 { RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 }, 00200 { RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time", 00201 RADIUS_ATTR_INT32 }, 00202 { RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets", 00203 RADIUS_ATTR_INT32 }, 00204 { RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets", 00205 RADIUS_ATTR_INT32 }, 00206 { RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause", 00207 RADIUS_ATTR_INT32 }, 00208 { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id", 00209 RADIUS_ATTR_TEXT }, 00210 { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 }, 00211 { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords", 00212 RADIUS_ATTR_INT32 }, 00213 { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords", 00214 RADIUS_ATTR_INT32 }, 00215 { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp", 00216 RADIUS_ATTR_INT32 }, 00217 { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 }, 00218 { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP }, 00219 { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type", 00220 RADIUS_ATTR_HEXDUMP }, 00221 { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT }, 00222 { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST }, 00223 { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator", 00224 RADIUS_ATTR_UNDIST }, 00225 { RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, "Tunnel-Private-Group-Id", 00226 RADIUS_ATTR_HEXDUMP }, 00227 { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval", 00228 RADIUS_ATTR_INT32 }, 00229 { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargable-User-Identity", 00230 RADIUS_ATTR_TEXT }, 00231 { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 }, 00232 }; 00233 #define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0])) 00234 00235 00236 static struct radius_attr_type *radius_get_attr_type(u8 type) 00237 { 00238 size_t i; 00239 00240 for (i = 0; i < RADIUS_ATTRS; i++) { 00241 if (type == radius_attrs[i].type) 00242 return &radius_attrs[i]; 00243 } 00244 00245 return NULL; 00246 } 00247 00248 00249 static void print_char(char c) 00250 { 00251 if (c >= 32 && c < 127) 00252 printf("%c", c); 00253 else 00254 printf("<%02x>", c); 00255 } 00256 00257 00258 static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) 00259 { 00260 struct radius_attr_type *attr; 00261 int i, len; 00262 unsigned char *pos; 00263 00264 attr = radius_get_attr_type(hdr->type); 00265 00266 printf(" Attribute %d (%s) length=%d\n", 00267 hdr->type, attr ? attr->name : "?Unknown?", hdr->length); 00268 00269 if (attr == NULL) 00270 return; 00271 00272 len = hdr->length - sizeof(struct radius_attr_hdr); 00273 pos = (unsigned char *) (hdr + 1); 00274 00275 switch (attr->data_type) { 00276 case RADIUS_ATTR_TEXT: 00277 printf(" Value: '"); 00278 for (i = 0; i < len; i++) 00279 print_char(pos[i]); 00280 printf("'\n"); 00281 break; 00282 00283 case RADIUS_ATTR_IP: 00284 if (len == 4) { 00285 struct in_addr addr; 00286 os_memcpy(&addr, pos, 4); 00287 printf(" Value: %s\n", inet_ntoa(addr)); 00288 } else 00289 printf(" Invalid IP address length %d\n", len); 00290 break; 00291 00292 #ifdef CONFIG_IPV6 00293 case RADIUS_ATTR_IPV6: 00294 if (len == 16) { 00295 char buf[128]; 00296 const char *atxt; 00297 struct in6_addr *addr = (struct in6_addr *) pos; 00298 atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf)); 00299 printf(" Value: %s\n", atxt ? atxt : "?"); 00300 } else 00301 printf(" Invalid IPv6 address length %d\n", len); 00302 break; 00303 #endif /* CONFIG_IPV6 */ 00304 00305 case RADIUS_ATTR_HEXDUMP: 00306 case RADIUS_ATTR_UNDIST: 00307 printf(" Value:"); 00308 for (i = 0; i < len; i++) 00309 printf(" %02x", pos[i]); 00310 printf("\n"); 00311 break; 00312 00313 case RADIUS_ATTR_INT32: 00314 if (len == 4) 00315 printf(" Value: %u\n", WPA_GET_BE32(pos)); 00316 else 00317 printf(" Invalid INT32 length %d\n", len); 00318 break; 00319 00320 default: 00321 break; 00322 } 00323 } 00324 00325 00326 void radius_msg_dump(struct radius_msg *msg) 00327 { 00328 size_t i; 00329 00330 printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n", 00331 msg->hdr->code, radius_code_string(msg->hdr->code), 00332 msg->hdr->identifier, ntohs(msg->hdr->length)); 00333 00334 for (i = 0; i < msg->attr_used; i++) { 00335 struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); 00336 radius_msg_dump_attr(attr); 00337 } 00338 } 00339 00340 00341 int radius_msg_finish(struct radius_msg *msg, const u8 *secret, 00342 size_t secret_len) 00343 { 00344 if (secret) { 00345 u8 auth[MD5_MAC_LEN]; 00346 struct radius_attr_hdr *attr; 00347 00348 os_memset(auth, 0, MD5_MAC_LEN); 00349 attr = radius_msg_add_attr(msg, 00350 RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 00351 auth, MD5_MAC_LEN); 00352 if (attr == NULL) { 00353 wpa_printf(MSG_WARNING, "RADIUS: Could not add " 00354 "Message-Authenticator"); 00355 return -1; 00356 } 00357 msg->hdr->length = htons(wpabuf_len(msg->buf)); 00358 hmac_md5(secret, secret_len, wpabuf_head(msg->buf), 00359 wpabuf_len(msg->buf), (u8 *) (attr + 1)); 00360 } else 00361 msg->hdr->length = htons(wpabuf_len(msg->buf)); 00362 00363 if (wpabuf_len(msg->buf) > 0xffff) { 00364 wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", 00365 (unsigned long) wpabuf_len(msg->buf)); 00366 return -1; 00367 } 00368 return 0; 00369 } 00370 00371 00372 int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, 00373 size_t secret_len, const u8 *req_authenticator) 00374 { 00375 u8 auth[MD5_MAC_LEN]; 00376 struct radius_attr_hdr *attr; 00377 const u8 *addr[4]; 00378 size_t len[4]; 00379 00380 os_memset(auth, 0, MD5_MAC_LEN); 00381 attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 00382 auth, MD5_MAC_LEN); 00383 if (attr == NULL) { 00384 printf("WARNING: Could not add Message-Authenticator\n"); 00385 return -1; 00386 } 00387 msg->hdr->length = htons(wpabuf_len(msg->buf)); 00388 os_memcpy(msg->hdr->authenticator, req_authenticator, 00389 sizeof(msg->hdr->authenticator)); 00390 hmac_md5(secret, secret_len, wpabuf_head(msg->buf), 00391 wpabuf_len(msg->buf), (u8 *) (attr + 1)); 00392 00393 /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ 00394 addr[0] = (u8 *) msg->hdr; 00395 len[0] = 1 + 1 + 2; 00396 addr[1] = req_authenticator; 00397 len[1] = MD5_MAC_LEN; 00398 addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr); 00399 len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); 00400 addr[3] = secret; 00401 len[3] = secret_len; 00402 md5_vector(4, addr, len, msg->hdr->authenticator); 00403 00404 if (wpabuf_len(msg->buf) > 0xffff) { 00405 wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", 00406 (unsigned long) wpabuf_len(msg->buf)); 00407 return -1; 00408 } 00409 return 0; 00410 } 00411 00412 00413 void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, 00414 size_t secret_len) 00415 { 00416 const u8 *addr[2]; 00417 size_t len[2]; 00418 00419 msg->hdr->length = htons(wpabuf_len(msg->buf)); 00420 os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN); 00421 addr[0] = wpabuf_head(msg->buf); 00422 len[0] = wpabuf_len(msg->buf); 00423 addr[1] = secret; 00424 len[1] = secret_len; 00425 md5_vector(2, addr, len, msg->hdr->authenticator); 00426 00427 if (wpabuf_len(msg->buf) > 0xffff) { 00428 wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)", 00429 (unsigned long) wpabuf_len(msg->buf)); 00430 } 00431 } 00432 00433 00434 static int radius_msg_add_attr_to_array(struct radius_msg *msg, 00435 struct radius_attr_hdr *attr) 00436 { 00437 if (msg->attr_used >= msg->attr_size) { 00438 size_t *nattr_pos; 00439 int nlen = msg->attr_size * 2; 00440 00441 nattr_pos = os_realloc(msg->attr_pos, 00442 nlen * sizeof(*msg->attr_pos)); 00443 if (nattr_pos == NULL) 00444 return -1; 00445 00446 msg->attr_pos = nattr_pos; 00447 msg->attr_size = nlen; 00448 } 00449 00450 msg->attr_pos[msg->attr_used++] = 00451 (unsigned char *) attr - wpabuf_head_u8(msg->buf); 00452 00453 return 0; 00454 } 00455 00456 00457 struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, 00458 const u8 *data, size_t data_len) 00459 { 00460 size_t buf_needed; 00461 struct radius_attr_hdr *attr; 00462 00463 if (data_len > RADIUS_MAX_ATTR_LEN) { 00464 printf("radius_msg_add_attr: too long attribute (%lu bytes)\n", 00465 (unsigned long) data_len); 00466 return NULL; 00467 } 00468 00469 buf_needed = sizeof(*attr) + data_len; 00470 00471 if (wpabuf_tailroom(msg->buf) < buf_needed) { 00472 /* allocate more space for message buffer */ 00473 if (wpabuf_resize(&msg->buf, buf_needed) < 0) 00474 return NULL; 00475 msg->hdr = wpabuf_mhead(msg->buf); 00476 } 00477 00478 attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr)); 00479 attr->type = type; 00480 attr->length = sizeof(*attr) + data_len; 00481 wpabuf_put_data(msg->buf, data, data_len); 00482 00483 if (radius_msg_add_attr_to_array(msg, attr)) 00484 return NULL; 00485 00486 return attr; 00487 } 00488 00489 00499 struct radius_msg * radius_msg_parse(const u8 *data, size_t len) 00500 { 00501 struct radius_msg *msg; 00502 struct radius_hdr *hdr; 00503 struct radius_attr_hdr *attr; 00504 size_t msg_len; 00505 unsigned char *pos, *end; 00506 00507 if (data == NULL || len < sizeof(*hdr)) 00508 return NULL; 00509 00510 hdr = (struct radius_hdr *) data; 00511 00512 msg_len = ntohs(hdr->length); 00513 if (msg_len < sizeof(*hdr) || msg_len > len) { 00514 wpa_printf(MSG_INFO, "RADIUS: Invalid message length"); 00515 return NULL; 00516 } 00517 00518 if (msg_len < len) { 00519 wpa_printf(MSG_DEBUG, "RADIUS: Ignored %lu extra bytes after " 00520 "RADIUS message", (unsigned long) len - msg_len); 00521 } 00522 00523 msg = os_zalloc(sizeof(*msg)); 00524 if (msg == NULL) 00525 return NULL; 00526 00527 msg->buf = wpabuf_alloc_copy(data, msg_len); 00528 if (msg->buf == NULL || radius_msg_initialize(msg)) { 00529 radius_msg_free(msg); 00530 return NULL; 00531 } 00532 msg->hdr = wpabuf_mhead(msg->buf); 00533 00534 /* parse attributes */ 00535 pos = wpabuf_mhead_u8(msg->buf) + sizeof(struct radius_hdr); 00536 end = wpabuf_mhead_u8(msg->buf) + wpabuf_len(msg->buf); 00537 while (pos < end) { 00538 if ((size_t) (end - pos) < sizeof(*attr)) 00539 goto fail; 00540 00541 attr = (struct radius_attr_hdr *) pos; 00542 00543 if (pos + attr->length > end || attr->length < sizeof(*attr)) 00544 goto fail; 00545 00546 /* TODO: check that attr->length is suitable for attr->type */ 00547 00548 if (radius_msg_add_attr_to_array(msg, attr)) 00549 goto fail; 00550 00551 pos += attr->length; 00552 } 00553 00554 return msg; 00555 00556 fail: 00557 radius_msg_free(msg); 00558 return NULL; 00559 } 00560 00561 00562 int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len) 00563 { 00564 const u8 *pos = data; 00565 size_t left = data_len; 00566 00567 while (left > 0) { 00568 int len; 00569 if (left > RADIUS_MAX_ATTR_LEN) 00570 len = RADIUS_MAX_ATTR_LEN; 00571 else 00572 len = left; 00573 00574 if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE, 00575 pos, len)) 00576 return 0; 00577 00578 pos += len; 00579 left -= len; 00580 } 00581 00582 return 1; 00583 } 00584 00585 00586 u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len) 00587 { 00588 u8 *eap, *pos; 00589 size_t len, i; 00590 struct radius_attr_hdr *attr; 00591 00592 if (msg == NULL) 00593 return NULL; 00594 00595 len = 0; 00596 for (i = 0; i < msg->attr_used; i++) { 00597 attr = radius_get_attr_hdr(msg, i); 00598 if (attr->type == RADIUS_ATTR_EAP_MESSAGE) 00599 len += attr->length - sizeof(struct radius_attr_hdr); 00600 } 00601 00602 if (len == 0) 00603 return NULL; 00604 00605 eap = os_malloc(len); 00606 if (eap == NULL) 00607 return NULL; 00608 00609 pos = eap; 00610 for (i = 0; i < msg->attr_used; i++) { 00611 attr = radius_get_attr_hdr(msg, i); 00612 if (attr->type == RADIUS_ATTR_EAP_MESSAGE) { 00613 int flen = attr->length - sizeof(*attr); 00614 os_memcpy(pos, attr + 1, flen); 00615 pos += flen; 00616 } 00617 } 00618 00619 if (eap_len) 00620 *eap_len = len; 00621 00622 return eap; 00623 } 00624 00625 00626 int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, 00627 size_t secret_len, const u8 *req_auth) 00628 { 00629 u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN]; 00630 u8 orig_authenticator[16]; 00631 struct radius_attr_hdr *attr = NULL, *tmp; 00632 size_t i; 00633 00634 for (i = 0; i < msg->attr_used; i++) { 00635 tmp = radius_get_attr_hdr(msg, i); 00636 if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { 00637 if (attr != NULL) { 00638 printf("Multiple Message-Authenticator " 00639 "attributes in RADIUS message\n"); 00640 return 1; 00641 } 00642 attr = tmp; 00643 } 00644 } 00645 00646 if (attr == NULL) { 00647 printf("No Message-Authenticator attribute found\n"); 00648 return 1; 00649 } 00650 00651 os_memcpy(orig, attr + 1, MD5_MAC_LEN); 00652 os_memset(attr + 1, 0, MD5_MAC_LEN); 00653 if (req_auth) { 00654 os_memcpy(orig_authenticator, msg->hdr->authenticator, 00655 sizeof(orig_authenticator)); 00656 os_memcpy(msg->hdr->authenticator, req_auth, 00657 sizeof(msg->hdr->authenticator)); 00658 } 00659 hmac_md5(secret, secret_len, wpabuf_head(msg->buf), 00660 wpabuf_len(msg->buf), auth); 00661 os_memcpy(attr + 1, orig, MD5_MAC_LEN); 00662 if (req_auth) { 00663 os_memcpy(msg->hdr->authenticator, orig_authenticator, 00664 sizeof(orig_authenticator)); 00665 } 00666 00667 if (os_memcmp(orig, auth, MD5_MAC_LEN) != 0) { 00668 printf("Invalid Message-Authenticator!\n"); 00669 return 1; 00670 } 00671 00672 return 0; 00673 } 00674 00675 00676 int radius_msg_verify(struct radius_msg *msg, const u8 *secret, 00677 size_t secret_len, struct radius_msg *sent_msg, int auth) 00678 { 00679 const u8 *addr[4]; 00680 size_t len[4]; 00681 u8 hash[MD5_MAC_LEN]; 00682 00683 if (sent_msg == NULL) { 00684 printf("No matching Access-Request message found\n"); 00685 return 1; 00686 } 00687 00688 if (auth && 00689 radius_msg_verify_msg_auth(msg, secret, secret_len, 00690 sent_msg->hdr->authenticator)) { 00691 return 1; 00692 } 00693 00694 /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ 00695 addr[0] = (u8 *) msg->hdr; 00696 len[0] = 1 + 1 + 2; 00697 addr[1] = sent_msg->hdr->authenticator; 00698 len[1] = MD5_MAC_LEN; 00699 addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr); 00700 len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); 00701 addr[3] = secret; 00702 len[3] = secret_len; 00703 md5_vector(4, addr, len, hash); 00704 if (os_memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) { 00705 printf("Response Authenticator invalid!\n"); 00706 return 1; 00707 } 00708 00709 return 0; 00710 } 00711 00712 00713 int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, 00714 u8 type) 00715 { 00716 struct radius_attr_hdr *attr; 00717 size_t i; 00718 int count = 0; 00719 00720 for (i = 0; i < src->attr_used; i++) { 00721 attr = radius_get_attr_hdr(src, i); 00722 if (attr->type == type) { 00723 if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1), 00724 attr->length - sizeof(*attr))) 00725 return -1; 00726 count++; 00727 } 00728 } 00729 00730 return count; 00731 } 00732 00733 00734 /* Create Request Authenticator. The value should be unique over the lifetime 00735 * of the shared secret between authenticator and authentication server. 00736 * Use one-way MD5 hash calculated from current timestamp and some data given 00737 * by the caller. */ 00738 void radius_msg_make_authenticator(struct radius_msg *msg, 00739 const u8 *data, size_t len) 00740 { 00741 struct os_time tv; 00742 long int l; 00743 const u8 *addr[3]; 00744 size_t elen[3]; 00745 00746 os_get_time(&tv); 00747 l = os_random(); 00748 addr[0] = (u8 *) &tv; 00749 elen[0] = sizeof(tv); 00750 addr[1] = data; 00751 elen[1] = len; 00752 addr[2] = (u8 *) &l; 00753 elen[2] = sizeof(l); 00754 md5_vector(3, addr, elen, msg->hdr->authenticator); 00755 } 00756 00757 00758 /* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message. 00759 * Returns the Attribute payload and sets alen to indicate the length of the 00760 * payload if a vendor attribute with subtype is found, otherwise returns NULL. 00761 * The returned payload is allocated with os_malloc() and caller must free it 00762 * by calling os_free(). 00763 */ 00764 static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor, 00765 u8 subtype, size_t *alen) 00766 { 00767 u8 *data, *pos; 00768 size_t i, len; 00769 00770 if (msg == NULL) 00771 return NULL; 00772 00773 for (i = 0; i < msg->attr_used; i++) { 00774 struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); 00775 size_t left; 00776 u32 vendor_id; 00777 struct radius_attr_vendor *vhdr; 00778 00779 if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC) 00780 continue; 00781 00782 left = attr->length - sizeof(*attr); 00783 if (left < 4) 00784 continue; 00785 00786 pos = (u8 *) (attr + 1); 00787 00788 os_memcpy(&vendor_id, pos, 4); 00789 pos += 4; 00790 left -= 4; 00791 00792 if (ntohl(vendor_id) != vendor) 00793 continue; 00794 00795 while (left >= sizeof(*vhdr)) { 00796 vhdr = (struct radius_attr_vendor *) pos; 00797 if (vhdr->vendor_length > left || 00798 vhdr->vendor_length < sizeof(*vhdr)) { 00799 left = 0; 00800 break; 00801 } 00802 if (vhdr->vendor_type != subtype) { 00803 pos += vhdr->vendor_length; 00804 left -= vhdr->vendor_length; 00805 continue; 00806 } 00807 00808 len = vhdr->vendor_length - sizeof(*vhdr); 00809 data = os_malloc(len); 00810 if (data == NULL) 00811 return NULL; 00812 os_memcpy(data, pos + sizeof(*vhdr), len); 00813 if (alen) 00814 *alen = len; 00815 return data; 00816 } 00817 } 00818 00819 return NULL; 00820 } 00821 00822 00823 static u8 * decrypt_ms_key(const u8 *key, size_t len, 00824 const u8 *req_authenticator, 00825 const u8 *secret, size_t secret_len, size_t *reslen) 00826 { 00827 u8 *plain, *ppos, *res; 00828 const u8 *pos; 00829 size_t left, plen; 00830 u8 hash[MD5_MAC_LEN]; 00831 int i, first = 1; 00832 const u8 *addr[3]; 00833 size_t elen[3]; 00834 00835 /* key: 16-bit salt followed by encrypted key info */ 00836 00837 if (len < 2 + 16) 00838 return NULL; 00839 00840 pos = key + 2; 00841 left = len - 2; 00842 if (left % 16) { 00843 printf("Invalid ms key len %lu\n", (unsigned long) left); 00844 return NULL; 00845 } 00846 00847 plen = left; 00848 ppos = plain = os_malloc(plen); 00849 if (plain == NULL) 00850 return NULL; 00851 plain[0] = 0; 00852 00853 while (left > 0) { 00854 /* b(1) = MD5(Secret + Request-Authenticator + Salt) 00855 * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ 00856 00857 addr[0] = secret; 00858 elen[0] = secret_len; 00859 if (first) { 00860 addr[1] = req_authenticator; 00861 elen[1] = MD5_MAC_LEN; 00862 addr[2] = key; 00863 elen[2] = 2; /* Salt */ 00864 } else { 00865 addr[1] = pos - MD5_MAC_LEN; 00866 elen[1] = MD5_MAC_LEN; 00867 } 00868 md5_vector(first ? 3 : 2, addr, elen, hash); 00869 first = 0; 00870 00871 for (i = 0; i < MD5_MAC_LEN; i++) 00872 *ppos++ = *pos++ ^ hash[i]; 00873 left -= MD5_MAC_LEN; 00874 } 00875 00876 if (plain[0] == 0 || plain[0] > plen - 1) { 00877 printf("Failed to decrypt MPPE key\n"); 00878 os_free(plain); 00879 return NULL; 00880 } 00881 00882 res = os_malloc(plain[0]); 00883 if (res == NULL) { 00884 os_free(plain); 00885 return NULL; 00886 } 00887 os_memcpy(res, plain + 1, plain[0]); 00888 if (reslen) 00889 *reslen = plain[0]; 00890 os_free(plain); 00891 return res; 00892 } 00893 00894 00895 static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt, 00896 const u8 *req_authenticator, 00897 const u8 *secret, size_t secret_len, 00898 u8 *ebuf, size_t *elen) 00899 { 00900 int i, len, first = 1; 00901 u8 hash[MD5_MAC_LEN], saltbuf[2], *pos; 00902 const u8 *addr[3]; 00903 size_t _len[3]; 00904 00905 WPA_PUT_BE16(saltbuf, salt); 00906 00907 len = 1 + key_len; 00908 if (len & 0x0f) { 00909 len = (len & 0xf0) + 16; 00910 } 00911 os_memset(ebuf, 0, len); 00912 ebuf[0] = key_len; 00913 os_memcpy(ebuf + 1, key, key_len); 00914 00915 *elen = len; 00916 00917 pos = ebuf; 00918 while (len > 0) { 00919 /* b(1) = MD5(Secret + Request-Authenticator + Salt) 00920 * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ 00921 addr[0] = secret; 00922 _len[0] = secret_len; 00923 if (first) { 00924 addr[1] = req_authenticator; 00925 _len[1] = MD5_MAC_LEN; 00926 addr[2] = saltbuf; 00927 _len[2] = sizeof(saltbuf); 00928 } else { 00929 addr[1] = pos - MD5_MAC_LEN; 00930 _len[1] = MD5_MAC_LEN; 00931 } 00932 md5_vector(first ? 3 : 2, addr, _len, hash); 00933 first = 0; 00934 00935 for (i = 0; i < MD5_MAC_LEN; i++) 00936 *pos++ ^= hash[i]; 00937 00938 len -= MD5_MAC_LEN; 00939 } 00940 } 00941 00942 00943 struct radius_ms_mppe_keys * 00944 radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, 00945 const u8 *secret, size_t secret_len) 00946 { 00947 u8 *key; 00948 size_t keylen; 00949 struct radius_ms_mppe_keys *keys; 00950 00951 if (msg == NULL || sent_msg == NULL) 00952 return NULL; 00953 00954 keys = os_zalloc(sizeof(*keys)); 00955 if (keys == NULL) 00956 return NULL; 00957 00958 key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT, 00959 RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY, 00960 &keylen); 00961 if (key) { 00962 keys->send = decrypt_ms_key(key, keylen, 00963 sent_msg->hdr->authenticator, 00964 secret, secret_len, 00965 &keys->send_len); 00966 os_free(key); 00967 } 00968 00969 key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT, 00970 RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY, 00971 &keylen); 00972 if (key) { 00973 keys->recv = decrypt_ms_key(key, keylen, 00974 sent_msg->hdr->authenticator, 00975 secret, secret_len, 00976 &keys->recv_len); 00977 os_free(key); 00978 } 00979 00980 return keys; 00981 } 00982 00983 00984 struct radius_ms_mppe_keys * 00985 radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg, 00986 const u8 *secret, size_t secret_len) 00987 { 00988 u8 *key; 00989 size_t keylen; 00990 struct radius_ms_mppe_keys *keys; 00991 00992 if (msg == NULL || sent_msg == NULL) 00993 return NULL; 00994 00995 keys = os_zalloc(sizeof(*keys)); 00996 if (keys == NULL) 00997 return NULL; 00998 00999 key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO, 01000 RADIUS_CISCO_AV_PAIR, &keylen); 01001 if (key && keylen == 51 && 01002 os_memcmp(key, "leap:session-key=", 17) == 0) { 01003 keys->recv = decrypt_ms_key(key + 17, keylen - 17, 01004 sent_msg->hdr->authenticator, 01005 secret, secret_len, 01006 &keys->recv_len); 01007 } 01008 os_free(key); 01009 01010 return keys; 01011 } 01012 01013 01014 int radius_msg_add_mppe_keys(struct radius_msg *msg, 01015 const u8 *req_authenticator, 01016 const u8 *secret, size_t secret_len, 01017 const u8 *send_key, size_t send_key_len, 01018 const u8 *recv_key, size_t recv_key_len) 01019 { 01020 struct radius_attr_hdr *attr; 01021 u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT); 01022 u8 *buf; 01023 struct radius_attr_vendor *vhdr; 01024 u8 *pos; 01025 size_t elen; 01026 int hlen; 01027 u16 salt; 01028 01029 hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2; 01030 01031 /* MS-MPPE-Send-Key */ 01032 buf = os_malloc(hlen + send_key_len + 16); 01033 if (buf == NULL) { 01034 return 0; 01035 } 01036 pos = buf; 01037 os_memcpy(pos, &vendor_id, sizeof(vendor_id)); 01038 pos += sizeof(vendor_id); 01039 vhdr = (struct radius_attr_vendor *) pos; 01040 vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY; 01041 pos = (u8 *) (vhdr + 1); 01042 salt = os_random() | 0x8000; 01043 WPA_PUT_BE16(pos, salt); 01044 pos += 2; 01045 encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret, 01046 secret_len, pos, &elen); 01047 vhdr->vendor_length = hlen + elen - sizeof(vendor_id); 01048 01049 attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, 01050 buf, hlen + elen); 01051 os_free(buf); 01052 if (attr == NULL) { 01053 return 0; 01054 } 01055 01056 /* MS-MPPE-Recv-Key */ 01057 buf = os_malloc(hlen + send_key_len + 16); 01058 if (buf == NULL) { 01059 return 0; 01060 } 01061 pos = buf; 01062 os_memcpy(pos, &vendor_id, sizeof(vendor_id)); 01063 pos += sizeof(vendor_id); 01064 vhdr = (struct radius_attr_vendor *) pos; 01065 vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY; 01066 pos = (u8 *) (vhdr + 1); 01067 salt ^= 1; 01068 WPA_PUT_BE16(pos, salt); 01069 pos += 2; 01070 encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret, 01071 secret_len, pos, &elen); 01072 vhdr->vendor_length = hlen + elen - sizeof(vendor_id); 01073 01074 attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, 01075 buf, hlen + elen); 01076 os_free(buf); 01077 if (attr == NULL) { 01078 return 0; 01079 } 01080 01081 return 1; 01082 } 01083 01084 01085 /* Add User-Password attribute to a RADIUS message and encrypt it as specified 01086 * in RFC 2865, Chap. 5.2 */ 01087 struct radius_attr_hdr * 01088 radius_msg_add_attr_user_password(struct radius_msg *msg, 01089 const u8 *data, size_t data_len, 01090 const u8 *secret, size_t secret_len) 01091 { 01092 u8 buf[128]; 01093 int padlen, i; 01094 size_t buf_len, pos; 01095 const u8 *addr[2]; 01096 size_t len[2]; 01097 u8 hash[16]; 01098 01099 if (data_len > 128) 01100 return NULL; 01101 01102 os_memcpy(buf, data, data_len); 01103 buf_len = data_len; 01104 01105 padlen = data_len % 16; 01106 if (padlen) { 01107 padlen = 16 - padlen; 01108 os_memset(buf + data_len, 0, padlen); 01109 buf_len += padlen; 01110 } 01111 01112 addr[0] = secret; 01113 len[0] = secret_len; 01114 addr[1] = msg->hdr->authenticator; 01115 len[1] = 16; 01116 md5_vector(2, addr, len, hash); 01117 01118 for (i = 0; i < 16; i++) 01119 buf[i] ^= hash[i]; 01120 pos = 16; 01121 01122 while (pos < buf_len) { 01123 addr[0] = secret; 01124 len[0] = secret_len; 01125 addr[1] = &buf[pos - 16]; 01126 len[1] = 16; 01127 md5_vector(2, addr, len, hash); 01128 01129 for (i = 0; i < 16; i++) 01130 buf[pos + i] ^= hash[i]; 01131 01132 pos += 16; 01133 } 01134 01135 return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD, 01136 buf, buf_len); 01137 } 01138 01139 01140 int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len) 01141 { 01142 struct radius_attr_hdr *attr = NULL, *tmp; 01143 size_t i, dlen; 01144 01145 for (i = 0; i < msg->attr_used; i++) { 01146 tmp = radius_get_attr_hdr(msg, i); 01147 if (tmp->type == type) { 01148 attr = tmp; 01149 break; 01150 } 01151 } 01152 01153 if (!attr) 01154 return -1; 01155 01156 dlen = attr->length - sizeof(*attr); 01157 if (buf) 01158 os_memcpy(buf, (attr + 1), dlen > len ? len : dlen); 01159 return dlen; 01160 } 01161 01162 01163 int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, 01164 size_t *len, const u8 *start) 01165 { 01166 size_t i; 01167 struct radius_attr_hdr *attr = NULL, *tmp; 01168 01169 for (i = 0; i < msg->attr_used; i++) { 01170 tmp = radius_get_attr_hdr(msg, i); 01171 if (tmp->type == type && 01172 (start == NULL || (u8 *) tmp > start)) { 01173 attr = tmp; 01174 break; 01175 } 01176 } 01177 01178 if (!attr) 01179 return -1; 01180 01181 *buf = (u8 *) (attr + 1); 01182 *len = attr->length - sizeof(*attr); 01183 return 0; 01184 } 01185 01186 01187 int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len) 01188 { 01189 size_t i; 01190 int count; 01191 01192 for (count = 0, i = 0; i < msg->attr_used; i++) { 01193 struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); 01194 if (attr->type == type && 01195 attr->length >= sizeof(struct radius_attr_hdr) + min_len) 01196 count++; 01197 } 01198 01199 return count; 01200 } 01201 01202 01203 struct radius_tunnel_attrs { 01204 int tag_used; 01205 int type; /* Tunnel-Type */ 01206 int medium_type; /* Tunnel-Medium-Type */ 01207 int vlanid; 01208 }; 01209 01210 01216 int radius_msg_get_vlanid(struct radius_msg *msg) 01217 { 01218 struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun; 01219 size_t i; 01220 struct radius_attr_hdr *attr = NULL; 01221 const u8 *data; 01222 char buf[10]; 01223 size_t dlen; 01224 01225 os_memset(&tunnel, 0, sizeof(tunnel)); 01226 01227 for (i = 0; i < msg->attr_used; i++) { 01228 attr = radius_get_attr_hdr(msg, i); 01229 data = (const u8 *) (attr + 1); 01230 dlen = attr->length - sizeof(*attr); 01231 if (attr->length < 3) 01232 continue; 01233 if (data[0] >= RADIUS_TUNNEL_TAGS) 01234 tun = &tunnel[0]; 01235 else 01236 tun = &tunnel[data[0]]; 01237 01238 switch (attr->type) { 01239 case RADIUS_ATTR_TUNNEL_TYPE: 01240 if (attr->length != 6) 01241 break; 01242 tun->tag_used++; 01243 tun->type = WPA_GET_BE24(data + 1); 01244 break; 01245 case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE: 01246 if (attr->length != 6) 01247 break; 01248 tun->tag_used++; 01249 tun->medium_type = WPA_GET_BE24(data + 1); 01250 break; 01251 case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID: 01252 if (data[0] < RADIUS_TUNNEL_TAGS) { 01253 data++; 01254 dlen--; 01255 } 01256 if (dlen >= sizeof(buf)) 01257 break; 01258 os_memcpy(buf, data, dlen); 01259 buf[dlen] = '\0'; 01260 tun->tag_used++; 01261 tun->vlanid = atoi(buf); 01262 break; 01263 } 01264 } 01265 01266 for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) { 01267 tun = &tunnel[i]; 01268 if (tun->tag_used && 01269 tun->type == RADIUS_TUNNEL_TYPE_VLAN && 01270 tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 && 01271 tun->vlanid > 0) 01272 return tun->vlanid; 01273 } 01274 01275 return -1; 01276 } 01277 01278 01279 void radius_free_class(struct radius_class_data *c) 01280 { 01281 size_t i; 01282 if (c == NULL) 01283 return; 01284 for (i = 0; i < c->count; i++) 01285 os_free(c->attr[i].data); 01286 os_free(c->attr); 01287 c->attr = NULL; 01288 c->count = 0; 01289 } 01290 01291 01292 int radius_copy_class(struct radius_class_data *dst, 01293 const struct radius_class_data *src) 01294 { 01295 size_t i; 01296 01297 if (src->attr == NULL) 01298 return 0; 01299 01300 dst->attr = os_zalloc(src->count * sizeof(struct radius_attr_data)); 01301 if (dst->attr == NULL) 01302 return -1; 01303 01304 dst->count = 0; 01305 01306 for (i = 0; i < src->count; i++) { 01307 dst->attr[i].data = os_malloc(src->attr[i].len); 01308 if (dst->attr[i].data == NULL) 01309 break; 01310 dst->count++; 01311 os_memcpy(dst->attr[i].data, src->attr[i].data, 01312 src->attr[i].len); 01313 dst->attr[i].len = src->attr[i].len; 01314 } 01315 01316 return 0; 01317 }