1189251Ssam/* 2214734Srpaulo * RADIUS message processing 3281806Srpaulo * Copyright (c) 2002-2009, 2011-2014, Jouni Malinen <j@w1.fi> 4189251Ssam * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7189251Ssam */ 8189251Ssam 9214734Srpaulo#include "utils/includes.h" 10189251Ssam 11214734Srpaulo#include "utils/common.h" 12214734Srpaulo#include "utils/wpabuf.h" 13214734Srpaulo#include "crypto/md5.h" 14214734Srpaulo#include "crypto/crypto.h" 15189251Ssam#include "radius.h" 16189251Ssam 17189251Ssam 18214734Srpaulo/** 19214734Srpaulo * struct radius_msg - RADIUS message structure for new and parsed messages 20214734Srpaulo */ 21214734Srpaulostruct radius_msg { 22214734Srpaulo /** 23214734Srpaulo * buf - Allocated buffer for RADIUS message 24214734Srpaulo */ 25214734Srpaulo struct wpabuf *buf; 26214734Srpaulo 27214734Srpaulo /** 28214734Srpaulo * hdr - Pointer to the RADIUS header in buf 29214734Srpaulo */ 30214734Srpaulo struct radius_hdr *hdr; 31214734Srpaulo 32214734Srpaulo /** 33214734Srpaulo * attr_pos - Array of indexes to attributes 34214734Srpaulo * 35214734Srpaulo * The values are number of bytes from buf to the beginning of 36214734Srpaulo * struct radius_attr_hdr. 37214734Srpaulo */ 38214734Srpaulo size_t *attr_pos; 39214734Srpaulo 40214734Srpaulo /** 41214734Srpaulo * attr_size - Total size of the attribute pointer array 42214734Srpaulo */ 43214734Srpaulo size_t attr_size; 44214734Srpaulo 45214734Srpaulo /** 46214734Srpaulo * attr_used - Total number of attributes in the array 47214734Srpaulo */ 48214734Srpaulo size_t attr_used; 49214734Srpaulo}; 50214734Srpaulo 51214734Srpaulo 52214734Srpaulostruct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg) 53189251Ssam{ 54214734Srpaulo return msg->hdr; 55189251Ssam} 56189251Ssam 57189251Ssam 58214734Srpaulostruct wpabuf * radius_msg_get_buf(struct radius_msg *msg) 59189251Ssam{ 60214734Srpaulo return msg->buf; 61214734Srpaulo} 62189251Ssam 63189251Ssam 64214734Srpaulostatic struct radius_attr_hdr * 65214734Srpauloradius_get_attr_hdr(struct radius_msg *msg, int idx) 66214734Srpaulo{ 67214734Srpaulo return (struct radius_attr_hdr *) 68214734Srpaulo (wpabuf_mhead_u8(msg->buf) + msg->attr_pos[idx]); 69214734Srpaulo} 70189251Ssam 71189251Ssam 72214734Srpaulostatic void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier) 73214734Srpaulo{ 74214734Srpaulo msg->hdr->code = code; 75214734Srpaulo msg->hdr->identifier = identifier; 76189251Ssam} 77189251Ssam 78189251Ssam 79214734Srpaulostatic int radius_msg_initialize(struct radius_msg *msg) 80189251Ssam{ 81252726Srpaulo msg->attr_pos = os_calloc(RADIUS_DEFAULT_ATTR_COUNT, 82252726Srpaulo sizeof(*msg->attr_pos)); 83214734Srpaulo if (msg->attr_pos == NULL) 84189251Ssam return -1; 85189251Ssam 86189251Ssam msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT; 87189251Ssam msg->attr_used = 0; 88189251Ssam 89189251Ssam return 0; 90189251Ssam} 91189251Ssam 92189251Ssam 93214734Srpaulo/** 94214734Srpaulo * radius_msg_new - Create a new RADIUS message 95214734Srpaulo * @code: Code for RADIUS header 96214734Srpaulo * @identifier: Identifier for RADIUS header 97214734Srpaulo * Returns: Context for RADIUS message or %NULL on failure 98214734Srpaulo * 99214734Srpaulo * The caller is responsible for freeing the returned data with 100214734Srpaulo * radius_msg_free(). 101214734Srpaulo */ 102214734Srpaulostruct radius_msg * radius_msg_new(u8 code, u8 identifier) 103189251Ssam{ 104214734Srpaulo struct radius_msg *msg; 105214734Srpaulo 106214734Srpaulo msg = os_zalloc(sizeof(*msg)); 107214734Srpaulo if (msg == NULL) 108214734Srpaulo return NULL; 109214734Srpaulo 110214734Srpaulo msg->buf = wpabuf_alloc(RADIUS_DEFAULT_MSG_SIZE); 111214734Srpaulo if (msg->buf == NULL || radius_msg_initialize(msg)) { 112214734Srpaulo radius_msg_free(msg); 113214734Srpaulo return NULL; 114214734Srpaulo } 115214734Srpaulo msg->hdr = wpabuf_put(msg->buf, sizeof(struct radius_hdr)); 116214734Srpaulo 117214734Srpaulo radius_msg_set_hdr(msg, code, identifier); 118214734Srpaulo 119214734Srpaulo return msg; 120189251Ssam} 121189251Ssam 122189251Ssam 123214734Srpaulo/** 124214734Srpaulo * radius_msg_free - Free a RADIUS message 125214734Srpaulo * @msg: RADIUS message from radius_msg_new() or radius_msg_parse() 126214734Srpaulo */ 127189251Ssamvoid radius_msg_free(struct radius_msg *msg) 128189251Ssam{ 129214734Srpaulo if (msg == NULL) 130214734Srpaulo return; 131189251Ssam 132214734Srpaulo wpabuf_free(msg->buf); 133189251Ssam os_free(msg->attr_pos); 134214734Srpaulo os_free(msg); 135189251Ssam} 136189251Ssam 137189251Ssam 138189251Ssamstatic const char *radius_code_string(u8 code) 139189251Ssam{ 140189251Ssam switch (code) { 141189251Ssam case RADIUS_CODE_ACCESS_REQUEST: return "Access-Request"; 142189251Ssam case RADIUS_CODE_ACCESS_ACCEPT: return "Access-Accept"; 143189251Ssam case RADIUS_CODE_ACCESS_REJECT: return "Access-Reject"; 144189251Ssam case RADIUS_CODE_ACCOUNTING_REQUEST: return "Accounting-Request"; 145189251Ssam case RADIUS_CODE_ACCOUNTING_RESPONSE: return "Accounting-Response"; 146189251Ssam case RADIUS_CODE_ACCESS_CHALLENGE: return "Access-Challenge"; 147189251Ssam case RADIUS_CODE_STATUS_SERVER: return "Status-Server"; 148189251Ssam case RADIUS_CODE_STATUS_CLIENT: return "Status-Client"; 149189251Ssam case RADIUS_CODE_RESERVED: return "Reserved"; 150252726Srpaulo case RADIUS_CODE_DISCONNECT_REQUEST: return "Disconnect-Request"; 151252726Srpaulo case RADIUS_CODE_DISCONNECT_ACK: return "Disconnect-ACK"; 152252726Srpaulo case RADIUS_CODE_DISCONNECT_NAK: return "Disconnect-NAK"; 153252726Srpaulo case RADIUS_CODE_COA_REQUEST: return "CoA-Request"; 154252726Srpaulo case RADIUS_CODE_COA_ACK: return "CoA-ACK"; 155252726Srpaulo case RADIUS_CODE_COA_NAK: return "CoA-NAK"; 156189251Ssam default: return "?Unknown?"; 157189251Ssam } 158189251Ssam} 159189251Ssam 160189251Ssam 161189251Ssamstruct radius_attr_type { 162189251Ssam u8 type; 163189251Ssam char *name; 164189251Ssam enum { 165189251Ssam RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP, 166189251Ssam RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6 167189251Ssam } data_type; 168189251Ssam}; 169189251Ssam 170289549Srpaulostatic const struct radius_attr_type radius_attrs[] = 171189251Ssam{ 172189251Ssam { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT }, 173189251Ssam { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST }, 174189251Ssam { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP }, 175189251Ssam { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 }, 176189251Ssam { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 }, 177189251Ssam { RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT }, 178189251Ssam { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST }, 179189251Ssam { RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST }, 180189251Ssam { RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST }, 181189251Ssam { RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 }, 182189251Ssam { RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 }, 183189251Ssam { RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action", 184189251Ssam RADIUS_ATTR_INT32 }, 185189251Ssam { RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id", 186189251Ssam RADIUS_ATTR_TEXT }, 187189251Ssam { RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id", 188189251Ssam RADIUS_ATTR_TEXT }, 189189251Ssam { RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT }, 190189251Ssam { RADIUS_ATTR_PROXY_STATE, "Proxy-State", RADIUS_ATTR_UNDIST }, 191189251Ssam { RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type", 192189251Ssam RADIUS_ATTR_INT32 }, 193189251Ssam { RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 }, 194189251Ssam { RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets", 195189251Ssam RADIUS_ATTR_INT32 }, 196189251Ssam { RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets", 197189251Ssam RADIUS_ATTR_INT32 }, 198189251Ssam { RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT }, 199189251Ssam { RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 }, 200189251Ssam { RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time", 201189251Ssam RADIUS_ATTR_INT32 }, 202189251Ssam { RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets", 203189251Ssam RADIUS_ATTR_INT32 }, 204189251Ssam { RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets", 205189251Ssam RADIUS_ATTR_INT32 }, 206189251Ssam { RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause", 207189251Ssam RADIUS_ATTR_INT32 }, 208189251Ssam { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id", 209189251Ssam RADIUS_ATTR_TEXT }, 210189251Ssam { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 }, 211189251Ssam { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords", 212189251Ssam RADIUS_ATTR_INT32 }, 213189251Ssam { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords", 214189251Ssam RADIUS_ATTR_INT32 }, 215189251Ssam { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp", 216189251Ssam RADIUS_ATTR_INT32 }, 217189251Ssam { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 }, 218189251Ssam { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP }, 219189251Ssam { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type", 220189251Ssam RADIUS_ATTR_HEXDUMP }, 221252726Srpaulo { RADIUS_ATTR_TUNNEL_PASSWORD, "Tunnel-Password", 222252726Srpaulo RADIUS_ATTR_UNDIST }, 223189251Ssam { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT }, 224189251Ssam { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST }, 225189251Ssam { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator", 226189251Ssam RADIUS_ATTR_UNDIST }, 227189251Ssam { RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, "Tunnel-Private-Group-Id", 228189251Ssam RADIUS_ATTR_HEXDUMP }, 229189251Ssam { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval", 230189251Ssam RADIUS_ATTR_INT32 }, 231252726Srpaulo { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargeable-User-Identity", 232189251Ssam RADIUS_ATTR_TEXT }, 233189251Ssam { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 }, 234281806Srpaulo { RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 }, 235281806Srpaulo { RADIUS_ATTR_EAP_KEY_NAME, "EAP-Key-Name", RADIUS_ATTR_HEXDUMP }, 236281806Srpaulo { RADIUS_ATTR_OPERATOR_NAME, "Operator-Name", RADIUS_ATTR_TEXT }, 237281806Srpaulo { RADIUS_ATTR_LOCATION_INFO, "Location-Information", 238281806Srpaulo RADIUS_ATTR_HEXDUMP }, 239281806Srpaulo { RADIUS_ATTR_LOCATION_DATA, "Location-Data", RADIUS_ATTR_HEXDUMP }, 240281806Srpaulo { RADIUS_ATTR_BASIC_LOCATION_POLICY_RULES, 241281806Srpaulo "Basic-Location-Policy-Rules", RADIUS_ATTR_HEXDUMP }, 242281806Srpaulo { RADIUS_ATTR_EXTENDED_LOCATION_POLICY_RULES, 243281806Srpaulo "Extended-Location-Policy-Rules", RADIUS_ATTR_HEXDUMP }, 244281806Srpaulo { RADIUS_ATTR_LOCATION_CAPABLE, "Location-Capable", RADIUS_ATTR_INT32 }, 245281806Srpaulo { RADIUS_ATTR_REQUESTED_LOCATION_INFO, "Requested-Location-Info", 246281806Srpaulo RADIUS_ATTR_INT32 }, 247281806Srpaulo { RADIUS_ATTR_MOBILITY_DOMAIN_ID, "Mobility-Domain-Id", 248281806Srpaulo RADIUS_ATTR_INT32 }, 249281806Srpaulo { RADIUS_ATTR_WLAN_HESSID, "WLAN-HESSID", RADIUS_ATTR_TEXT }, 250281806Srpaulo { RADIUS_ATTR_WLAN_PAIRWISE_CIPHER, "WLAN-Pairwise-Cipher", 251281806Srpaulo RADIUS_ATTR_HEXDUMP }, 252281806Srpaulo { RADIUS_ATTR_WLAN_GROUP_CIPHER, "WLAN-Group-Cipher", 253281806Srpaulo RADIUS_ATTR_HEXDUMP }, 254281806Srpaulo { RADIUS_ATTR_WLAN_AKM_SUITE, "WLAN-AKM-Suite", 255281806Srpaulo RADIUS_ATTR_HEXDUMP }, 256281806Srpaulo { RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, "WLAN-Group-Mgmt-Pairwise-Cipher", 257281806Srpaulo RADIUS_ATTR_HEXDUMP }, 258189251Ssam}; 259281806Srpaulo#define RADIUS_ATTRS ARRAY_SIZE(radius_attrs) 260189251Ssam 261189251Ssam 262289549Srpaulostatic const struct radius_attr_type *radius_get_attr_type(u8 type) 263189251Ssam{ 264189251Ssam size_t i; 265189251Ssam 266189251Ssam for (i = 0; i < RADIUS_ATTRS; i++) { 267189251Ssam if (type == radius_attrs[i].type) 268189251Ssam return &radius_attrs[i]; 269189251Ssam } 270189251Ssam 271189251Ssam return NULL; 272189251Ssam} 273189251Ssam 274189251Ssam 275189251Ssamstatic void radius_msg_dump_attr(struct radius_attr_hdr *hdr) 276189251Ssam{ 277289549Srpaulo const struct radius_attr_type *attr; 278281806Srpaulo int len; 279189251Ssam unsigned char *pos; 280281806Srpaulo char buf[1000]; 281189251Ssam 282189251Ssam attr = radius_get_attr_type(hdr->type); 283189251Ssam 284281806Srpaulo wpa_printf(MSG_INFO, " Attribute %d (%s) length=%d", 285281806Srpaulo hdr->type, attr ? attr->name : "?Unknown?", hdr->length); 286189251Ssam 287252726Srpaulo if (attr == NULL || hdr->length < sizeof(struct radius_attr_hdr)) 288189251Ssam return; 289189251Ssam 290189251Ssam len = hdr->length - sizeof(struct radius_attr_hdr); 291189251Ssam pos = (unsigned char *) (hdr + 1); 292189251Ssam 293189251Ssam switch (attr->data_type) { 294189251Ssam case RADIUS_ATTR_TEXT: 295281806Srpaulo printf_encode(buf, sizeof(buf), pos, len); 296281806Srpaulo wpa_printf(MSG_INFO, " Value: '%s'", buf); 297189251Ssam break; 298189251Ssam 299189251Ssam case RADIUS_ATTR_IP: 300189251Ssam if (len == 4) { 301189251Ssam struct in_addr addr; 302189251Ssam os_memcpy(&addr, pos, 4); 303281806Srpaulo wpa_printf(MSG_INFO, " Value: %s", 304281806Srpaulo inet_ntoa(addr)); 305281806Srpaulo } else { 306281806Srpaulo wpa_printf(MSG_INFO, " Invalid IP address length %d", 307281806Srpaulo len); 308281806Srpaulo } 309189251Ssam break; 310189251Ssam 311189251Ssam#ifdef CONFIG_IPV6 312189251Ssam case RADIUS_ATTR_IPV6: 313189251Ssam if (len == 16) { 314189251Ssam const char *atxt; 315189251Ssam struct in6_addr *addr = (struct in6_addr *) pos; 316189251Ssam atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf)); 317281806Srpaulo wpa_printf(MSG_INFO, " Value: %s", 318281806Srpaulo atxt ? atxt : "?"); 319281806Srpaulo } else { 320281806Srpaulo wpa_printf(MSG_INFO, " Invalid IPv6 address length %d", 321281806Srpaulo len); 322281806Srpaulo } 323189251Ssam break; 324189251Ssam#endif /* CONFIG_IPV6 */ 325189251Ssam 326189251Ssam case RADIUS_ATTR_HEXDUMP: 327189251Ssam case RADIUS_ATTR_UNDIST: 328281806Srpaulo wpa_snprintf_hex(buf, sizeof(buf), pos, len); 329281806Srpaulo wpa_printf(MSG_INFO, " Value: %s", buf); 330189251Ssam break; 331189251Ssam 332189251Ssam case RADIUS_ATTR_INT32: 333189251Ssam if (len == 4) 334281806Srpaulo wpa_printf(MSG_INFO, " Value: %u", 335281806Srpaulo WPA_GET_BE32(pos)); 336189251Ssam else 337281806Srpaulo wpa_printf(MSG_INFO, " Invalid INT32 length %d", 338281806Srpaulo len); 339189251Ssam break; 340189251Ssam 341189251Ssam default: 342189251Ssam break; 343189251Ssam } 344189251Ssam} 345189251Ssam 346189251Ssam 347189251Ssamvoid radius_msg_dump(struct radius_msg *msg) 348189251Ssam{ 349189251Ssam size_t i; 350189251Ssam 351281806Srpaulo wpa_printf(MSG_INFO, "RADIUS message: code=%d (%s) identifier=%d length=%d", 352281806Srpaulo msg->hdr->code, radius_code_string(msg->hdr->code), 353281806Srpaulo msg->hdr->identifier, be_to_host16(msg->hdr->length)); 354189251Ssam 355189251Ssam for (i = 0; i < msg->attr_used; i++) { 356189251Ssam struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); 357189251Ssam radius_msg_dump_attr(attr); 358189251Ssam } 359189251Ssam} 360189251Ssam 361189251Ssam 362189251Ssamint radius_msg_finish(struct radius_msg *msg, const u8 *secret, 363189251Ssam size_t secret_len) 364189251Ssam{ 365189251Ssam if (secret) { 366189251Ssam u8 auth[MD5_MAC_LEN]; 367189251Ssam struct radius_attr_hdr *attr; 368189251Ssam 369189251Ssam os_memset(auth, 0, MD5_MAC_LEN); 370189251Ssam attr = radius_msg_add_attr(msg, 371189251Ssam RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 372189251Ssam auth, MD5_MAC_LEN); 373189251Ssam if (attr == NULL) { 374214734Srpaulo wpa_printf(MSG_WARNING, "RADIUS: Could not add " 375214734Srpaulo "Message-Authenticator"); 376189251Ssam return -1; 377189251Ssam } 378252726Srpaulo msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); 379214734Srpaulo hmac_md5(secret, secret_len, wpabuf_head(msg->buf), 380214734Srpaulo wpabuf_len(msg->buf), (u8 *) (attr + 1)); 381189251Ssam } else 382252726Srpaulo msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); 383189251Ssam 384214734Srpaulo if (wpabuf_len(msg->buf) > 0xffff) { 385214734Srpaulo wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", 386214734Srpaulo (unsigned long) wpabuf_len(msg->buf)); 387189251Ssam return -1; 388189251Ssam } 389189251Ssam return 0; 390189251Ssam} 391189251Ssam 392189251Ssam 393189251Ssamint radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, 394189251Ssam size_t secret_len, const u8 *req_authenticator) 395189251Ssam{ 396189251Ssam u8 auth[MD5_MAC_LEN]; 397189251Ssam struct radius_attr_hdr *attr; 398189251Ssam const u8 *addr[4]; 399189251Ssam size_t len[4]; 400189251Ssam 401189251Ssam os_memset(auth, 0, MD5_MAC_LEN); 402189251Ssam attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 403189251Ssam auth, MD5_MAC_LEN); 404189251Ssam if (attr == NULL) { 405281806Srpaulo wpa_printf(MSG_ERROR, "WARNING: Could not add Message-Authenticator"); 406189251Ssam return -1; 407189251Ssam } 408252726Srpaulo msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); 409189251Ssam os_memcpy(msg->hdr->authenticator, req_authenticator, 410189251Ssam sizeof(msg->hdr->authenticator)); 411214734Srpaulo hmac_md5(secret, secret_len, wpabuf_head(msg->buf), 412214734Srpaulo wpabuf_len(msg->buf), (u8 *) (attr + 1)); 413189251Ssam 414189251Ssam /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ 415189251Ssam addr[0] = (u8 *) msg->hdr; 416189251Ssam len[0] = 1 + 1 + 2; 417189251Ssam addr[1] = req_authenticator; 418189251Ssam len[1] = MD5_MAC_LEN; 419214734Srpaulo addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr); 420214734Srpaulo len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); 421189251Ssam addr[3] = secret; 422189251Ssam len[3] = secret_len; 423189251Ssam md5_vector(4, addr, len, msg->hdr->authenticator); 424189251Ssam 425214734Srpaulo if (wpabuf_len(msg->buf) > 0xffff) { 426214734Srpaulo wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", 427214734Srpaulo (unsigned long) wpabuf_len(msg->buf)); 428189251Ssam return -1; 429189251Ssam } 430189251Ssam return 0; 431189251Ssam} 432189251Ssam 433189251Ssam 434252726Srpauloint radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret, 435252726Srpaulo size_t secret_len, 436252726Srpaulo const struct radius_hdr *req_hdr) 437252726Srpaulo{ 438252726Srpaulo const u8 *addr[2]; 439252726Srpaulo size_t len[2]; 440252726Srpaulo u8 auth[MD5_MAC_LEN]; 441252726Srpaulo struct radius_attr_hdr *attr; 442252726Srpaulo 443252726Srpaulo os_memset(auth, 0, MD5_MAC_LEN); 444252726Srpaulo attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 445252726Srpaulo auth, MD5_MAC_LEN); 446252726Srpaulo if (attr == NULL) { 447252726Srpaulo wpa_printf(MSG_WARNING, "Could not add Message-Authenticator"); 448252726Srpaulo return -1; 449252726Srpaulo } 450252726Srpaulo 451252726Srpaulo msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); 452252726Srpaulo os_memcpy(msg->hdr->authenticator, req_hdr->authenticator, 16); 453252726Srpaulo hmac_md5(secret, secret_len, wpabuf_head(msg->buf), 454252726Srpaulo wpabuf_len(msg->buf), (u8 *) (attr + 1)); 455252726Srpaulo 456252726Srpaulo /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ 457252726Srpaulo addr[0] = wpabuf_head_u8(msg->buf); 458252726Srpaulo len[0] = wpabuf_len(msg->buf); 459252726Srpaulo addr[1] = secret; 460252726Srpaulo len[1] = secret_len; 461252726Srpaulo if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0) 462252726Srpaulo return -1; 463252726Srpaulo 464252726Srpaulo if (wpabuf_len(msg->buf) > 0xffff) { 465252726Srpaulo wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", 466252726Srpaulo (unsigned long) wpabuf_len(msg->buf)); 467252726Srpaulo return -1; 468252726Srpaulo } 469252726Srpaulo return 0; 470252726Srpaulo} 471252726Srpaulo 472252726Srpaulo 473189251Ssamvoid radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, 474189251Ssam size_t secret_len) 475189251Ssam{ 476189251Ssam const u8 *addr[2]; 477189251Ssam size_t len[2]; 478189251Ssam 479252726Srpaulo msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); 480189251Ssam os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN); 481214734Srpaulo addr[0] = wpabuf_head(msg->buf); 482214734Srpaulo len[0] = wpabuf_len(msg->buf); 483189251Ssam addr[1] = secret; 484189251Ssam len[1] = secret_len; 485189251Ssam md5_vector(2, addr, len, msg->hdr->authenticator); 486189251Ssam 487214734Srpaulo if (wpabuf_len(msg->buf) > 0xffff) { 488214734Srpaulo wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)", 489214734Srpaulo (unsigned long) wpabuf_len(msg->buf)); 490189251Ssam } 491189251Ssam} 492189251Ssam 493189251Ssam 494281806Srpaulovoid radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret, 495281806Srpaulo size_t secret_len, const u8 *req_authenticator) 496281806Srpaulo{ 497281806Srpaulo const u8 *addr[2]; 498281806Srpaulo size_t len[2]; 499281806Srpaulo 500281806Srpaulo msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); 501281806Srpaulo os_memcpy(msg->hdr->authenticator, req_authenticator, MD5_MAC_LEN); 502281806Srpaulo addr[0] = wpabuf_head(msg->buf); 503281806Srpaulo len[0] = wpabuf_len(msg->buf); 504281806Srpaulo addr[1] = secret; 505281806Srpaulo len[1] = secret_len; 506281806Srpaulo md5_vector(2, addr, len, msg->hdr->authenticator); 507281806Srpaulo 508281806Srpaulo if (wpabuf_len(msg->buf) > 0xffff) { 509281806Srpaulo wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)", 510281806Srpaulo (unsigned long) wpabuf_len(msg->buf)); 511281806Srpaulo } 512281806Srpaulo} 513281806Srpaulo 514281806Srpaulo 515252726Srpauloint radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret, 516252726Srpaulo size_t secret_len) 517252726Srpaulo{ 518252726Srpaulo const u8 *addr[4]; 519252726Srpaulo size_t len[4]; 520252726Srpaulo u8 zero[MD5_MAC_LEN]; 521252726Srpaulo u8 hash[MD5_MAC_LEN]; 522252726Srpaulo 523252726Srpaulo os_memset(zero, 0, sizeof(zero)); 524252726Srpaulo addr[0] = (u8 *) msg->hdr; 525252726Srpaulo len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN; 526252726Srpaulo addr[1] = zero; 527252726Srpaulo len[1] = MD5_MAC_LEN; 528252726Srpaulo addr[2] = (u8 *) (msg->hdr + 1); 529252726Srpaulo len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); 530252726Srpaulo addr[3] = secret; 531252726Srpaulo len[3] = secret_len; 532252726Srpaulo md5_vector(4, addr, len, hash); 533281806Srpaulo return os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0; 534252726Srpaulo} 535252726Srpaulo 536252726Srpaulo 537252726Srpauloint radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, 538252726Srpaulo size_t secret_len) 539252726Srpaulo{ 540252726Srpaulo const u8 *addr[4]; 541252726Srpaulo size_t len[4]; 542252726Srpaulo u8 zero[MD5_MAC_LEN]; 543252726Srpaulo u8 hash[MD5_MAC_LEN]; 544252726Srpaulo u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN]; 545252726Srpaulo u8 orig_authenticator[16]; 546252726Srpaulo 547252726Srpaulo struct radius_attr_hdr *attr = NULL, *tmp; 548252726Srpaulo size_t i; 549252726Srpaulo 550252726Srpaulo os_memset(zero, 0, sizeof(zero)); 551252726Srpaulo addr[0] = (u8 *) msg->hdr; 552252726Srpaulo len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN; 553252726Srpaulo addr[1] = zero; 554252726Srpaulo len[1] = MD5_MAC_LEN; 555252726Srpaulo addr[2] = (u8 *) (msg->hdr + 1); 556252726Srpaulo len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); 557252726Srpaulo addr[3] = secret; 558252726Srpaulo len[3] = secret_len; 559252726Srpaulo md5_vector(4, addr, len, hash); 560281806Srpaulo if (os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0) 561252726Srpaulo return 1; 562252726Srpaulo 563252726Srpaulo for (i = 0; i < msg->attr_used; i++) { 564252726Srpaulo tmp = radius_get_attr_hdr(msg, i); 565252726Srpaulo if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { 566252726Srpaulo if (attr != NULL) { 567252726Srpaulo wpa_printf(MSG_WARNING, "Multiple " 568252726Srpaulo "Message-Authenticator attributes " 569252726Srpaulo "in RADIUS message"); 570252726Srpaulo return 1; 571252726Srpaulo } 572252726Srpaulo attr = tmp; 573252726Srpaulo } 574252726Srpaulo } 575252726Srpaulo 576252726Srpaulo if (attr == NULL) { 577252726Srpaulo /* Message-Authenticator is MAY; not required */ 578252726Srpaulo return 0; 579252726Srpaulo } 580252726Srpaulo 581252726Srpaulo os_memcpy(orig, attr + 1, MD5_MAC_LEN); 582252726Srpaulo os_memset(attr + 1, 0, MD5_MAC_LEN); 583252726Srpaulo os_memcpy(orig_authenticator, msg->hdr->authenticator, 584252726Srpaulo sizeof(orig_authenticator)); 585252726Srpaulo os_memset(msg->hdr->authenticator, 0, 586252726Srpaulo sizeof(msg->hdr->authenticator)); 587252726Srpaulo hmac_md5(secret, secret_len, wpabuf_head(msg->buf), 588252726Srpaulo wpabuf_len(msg->buf), auth); 589252726Srpaulo os_memcpy(attr + 1, orig, MD5_MAC_LEN); 590252726Srpaulo os_memcpy(msg->hdr->authenticator, orig_authenticator, 591252726Srpaulo sizeof(orig_authenticator)); 592252726Srpaulo 593281806Srpaulo return os_memcmp_const(orig, auth, MD5_MAC_LEN) != 0; 594252726Srpaulo} 595252726Srpaulo 596252726Srpaulo 597189251Ssamstatic int radius_msg_add_attr_to_array(struct radius_msg *msg, 598189251Ssam struct radius_attr_hdr *attr) 599189251Ssam{ 600189251Ssam if (msg->attr_used >= msg->attr_size) { 601189251Ssam size_t *nattr_pos; 602189251Ssam int nlen = msg->attr_size * 2; 603189251Ssam 604252726Srpaulo nattr_pos = os_realloc_array(msg->attr_pos, nlen, 605252726Srpaulo sizeof(*msg->attr_pos)); 606189251Ssam if (nattr_pos == NULL) 607189251Ssam return -1; 608189251Ssam 609189251Ssam msg->attr_pos = nattr_pos; 610189251Ssam msg->attr_size = nlen; 611189251Ssam } 612189251Ssam 613214734Srpaulo msg->attr_pos[msg->attr_used++] = 614214734Srpaulo (unsigned char *) attr - wpabuf_head_u8(msg->buf); 615189251Ssam 616189251Ssam return 0; 617189251Ssam} 618189251Ssam 619189251Ssam 620189251Ssamstruct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, 621189251Ssam const u8 *data, size_t data_len) 622189251Ssam{ 623189251Ssam size_t buf_needed; 624189251Ssam struct radius_attr_hdr *attr; 625189251Ssam 626189251Ssam if (data_len > RADIUS_MAX_ATTR_LEN) { 627281806Srpaulo wpa_printf(MSG_ERROR, "radius_msg_add_attr: too long attribute (%lu bytes)", 628189251Ssam (unsigned long) data_len); 629189251Ssam return NULL; 630189251Ssam } 631189251Ssam 632214734Srpaulo buf_needed = sizeof(*attr) + data_len; 633189251Ssam 634214734Srpaulo if (wpabuf_tailroom(msg->buf) < buf_needed) { 635189251Ssam /* allocate more space for message buffer */ 636214734Srpaulo if (wpabuf_resize(&msg->buf, buf_needed) < 0) 637189251Ssam return NULL; 638214734Srpaulo msg->hdr = wpabuf_mhead(msg->buf); 639189251Ssam } 640189251Ssam 641214734Srpaulo attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr)); 642189251Ssam attr->type = type; 643189251Ssam attr->length = sizeof(*attr) + data_len; 644214734Srpaulo wpabuf_put_data(msg->buf, data, data_len); 645189251Ssam 646189251Ssam if (radius_msg_add_attr_to_array(msg, attr)) 647189251Ssam return NULL; 648189251Ssam 649189251Ssam return attr; 650189251Ssam} 651189251Ssam 652189251Ssam 653214734Srpaulo/** 654214734Srpaulo * radius_msg_parse - Parse a RADIUS message 655214734Srpaulo * @data: RADIUS message to be parsed 656214734Srpaulo * @len: Length of data buffer in octets 657214734Srpaulo * Returns: Parsed RADIUS message or %NULL on failure 658214734Srpaulo * 659214734Srpaulo * This parses a RADIUS message and makes a copy of its data. The caller is 660214734Srpaulo * responsible for freeing the returned data with radius_msg_free(). 661214734Srpaulo */ 662214734Srpaulostruct radius_msg * radius_msg_parse(const u8 *data, size_t len) 663189251Ssam{ 664189251Ssam struct radius_msg *msg; 665189251Ssam struct radius_hdr *hdr; 666189251Ssam struct radius_attr_hdr *attr; 667189251Ssam size_t msg_len; 668189251Ssam unsigned char *pos, *end; 669189251Ssam 670189251Ssam if (data == NULL || len < sizeof(*hdr)) 671189251Ssam return NULL; 672189251Ssam 673189251Ssam hdr = (struct radius_hdr *) data; 674189251Ssam 675252726Srpaulo msg_len = be_to_host16(hdr->length); 676189251Ssam if (msg_len < sizeof(*hdr) || msg_len > len) { 677214734Srpaulo wpa_printf(MSG_INFO, "RADIUS: Invalid message length"); 678189251Ssam return NULL; 679189251Ssam } 680189251Ssam 681189251Ssam if (msg_len < len) { 682214734Srpaulo wpa_printf(MSG_DEBUG, "RADIUS: Ignored %lu extra bytes after " 683214734Srpaulo "RADIUS message", (unsigned long) len - msg_len); 684189251Ssam } 685189251Ssam 686214734Srpaulo msg = os_zalloc(sizeof(*msg)); 687189251Ssam if (msg == NULL) 688189251Ssam return NULL; 689189251Ssam 690214734Srpaulo msg->buf = wpabuf_alloc_copy(data, msg_len); 691214734Srpaulo if (msg->buf == NULL || radius_msg_initialize(msg)) { 692214734Srpaulo radius_msg_free(msg); 693189251Ssam return NULL; 694189251Ssam } 695214734Srpaulo msg->hdr = wpabuf_mhead(msg->buf); 696189251Ssam 697189251Ssam /* parse attributes */ 698214734Srpaulo pos = wpabuf_mhead_u8(msg->buf) + sizeof(struct radius_hdr); 699214734Srpaulo end = wpabuf_mhead_u8(msg->buf) + wpabuf_len(msg->buf); 700189251Ssam while (pos < end) { 701189251Ssam if ((size_t) (end - pos) < sizeof(*attr)) 702189251Ssam goto fail; 703189251Ssam 704189251Ssam attr = (struct radius_attr_hdr *) pos; 705189251Ssam 706189251Ssam if (pos + attr->length > end || attr->length < sizeof(*attr)) 707189251Ssam goto fail; 708189251Ssam 709189251Ssam /* TODO: check that attr->length is suitable for attr->type */ 710189251Ssam 711189251Ssam if (radius_msg_add_attr_to_array(msg, attr)) 712189251Ssam goto fail; 713189251Ssam 714189251Ssam pos += attr->length; 715189251Ssam } 716189251Ssam 717189251Ssam return msg; 718189251Ssam 719189251Ssam fail: 720189251Ssam radius_msg_free(msg); 721189251Ssam return NULL; 722189251Ssam} 723189251Ssam 724189251Ssam 725189251Ssamint radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len) 726189251Ssam{ 727189251Ssam const u8 *pos = data; 728189251Ssam size_t left = data_len; 729189251Ssam 730189251Ssam while (left > 0) { 731189251Ssam int len; 732189251Ssam if (left > RADIUS_MAX_ATTR_LEN) 733189251Ssam len = RADIUS_MAX_ATTR_LEN; 734189251Ssam else 735189251Ssam len = left; 736189251Ssam 737189251Ssam if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE, 738189251Ssam pos, len)) 739189251Ssam return 0; 740189251Ssam 741189251Ssam pos += len; 742189251Ssam left -= len; 743189251Ssam } 744189251Ssam 745189251Ssam return 1; 746189251Ssam} 747189251Ssam 748189251Ssam 749252726Srpaulostruct wpabuf * radius_msg_get_eap(struct radius_msg *msg) 750189251Ssam{ 751252726Srpaulo struct wpabuf *eap; 752189251Ssam size_t len, i; 753189251Ssam struct radius_attr_hdr *attr; 754189251Ssam 755189251Ssam if (msg == NULL) 756189251Ssam return NULL; 757189251Ssam 758189251Ssam len = 0; 759189251Ssam for (i = 0; i < msg->attr_used; i++) { 760189251Ssam attr = radius_get_attr_hdr(msg, i); 761252726Srpaulo if (attr->type == RADIUS_ATTR_EAP_MESSAGE && 762252726Srpaulo attr->length > sizeof(struct radius_attr_hdr)) 763189251Ssam len += attr->length - sizeof(struct radius_attr_hdr); 764189251Ssam } 765189251Ssam 766189251Ssam if (len == 0) 767189251Ssam return NULL; 768189251Ssam 769252726Srpaulo eap = wpabuf_alloc(len); 770189251Ssam if (eap == NULL) 771189251Ssam return NULL; 772189251Ssam 773189251Ssam for (i = 0; i < msg->attr_used; i++) { 774189251Ssam attr = radius_get_attr_hdr(msg, i); 775252726Srpaulo if (attr->type == RADIUS_ATTR_EAP_MESSAGE && 776252726Srpaulo attr->length > sizeof(struct radius_attr_hdr)) { 777189251Ssam int flen = attr->length - sizeof(*attr); 778252726Srpaulo wpabuf_put_data(eap, attr + 1, flen); 779189251Ssam } 780189251Ssam } 781189251Ssam 782189251Ssam return eap; 783189251Ssam} 784189251Ssam 785189251Ssam 786189251Ssamint radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, 787189251Ssam size_t secret_len, const u8 *req_auth) 788189251Ssam{ 789189251Ssam u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN]; 790189251Ssam u8 orig_authenticator[16]; 791189251Ssam struct radius_attr_hdr *attr = NULL, *tmp; 792189251Ssam size_t i; 793189251Ssam 794189251Ssam for (i = 0; i < msg->attr_used; i++) { 795189251Ssam tmp = radius_get_attr_hdr(msg, i); 796189251Ssam if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { 797189251Ssam if (attr != NULL) { 798281806Srpaulo wpa_printf(MSG_INFO, "Multiple Message-Authenticator attributes in RADIUS message"); 799189251Ssam return 1; 800189251Ssam } 801189251Ssam attr = tmp; 802189251Ssam } 803189251Ssam } 804189251Ssam 805189251Ssam if (attr == NULL) { 806281806Srpaulo wpa_printf(MSG_INFO, "No Message-Authenticator attribute found"); 807189251Ssam return 1; 808189251Ssam } 809189251Ssam 810189251Ssam os_memcpy(orig, attr + 1, MD5_MAC_LEN); 811189251Ssam os_memset(attr + 1, 0, MD5_MAC_LEN); 812189251Ssam if (req_auth) { 813189251Ssam os_memcpy(orig_authenticator, msg->hdr->authenticator, 814189251Ssam sizeof(orig_authenticator)); 815189251Ssam os_memcpy(msg->hdr->authenticator, req_auth, 816189251Ssam sizeof(msg->hdr->authenticator)); 817189251Ssam } 818214734Srpaulo hmac_md5(secret, secret_len, wpabuf_head(msg->buf), 819214734Srpaulo wpabuf_len(msg->buf), auth); 820189251Ssam os_memcpy(attr + 1, orig, MD5_MAC_LEN); 821189251Ssam if (req_auth) { 822189251Ssam os_memcpy(msg->hdr->authenticator, orig_authenticator, 823189251Ssam sizeof(orig_authenticator)); 824189251Ssam } 825189251Ssam 826281806Srpaulo if (os_memcmp_const(orig, auth, MD5_MAC_LEN) != 0) { 827281806Srpaulo wpa_printf(MSG_INFO, "Invalid Message-Authenticator!"); 828189251Ssam return 1; 829189251Ssam } 830189251Ssam 831189251Ssam return 0; 832189251Ssam} 833189251Ssam 834189251Ssam 835189251Ssamint radius_msg_verify(struct radius_msg *msg, const u8 *secret, 836189251Ssam size_t secret_len, struct radius_msg *sent_msg, int auth) 837189251Ssam{ 838189251Ssam const u8 *addr[4]; 839189251Ssam size_t len[4]; 840189251Ssam u8 hash[MD5_MAC_LEN]; 841189251Ssam 842189251Ssam if (sent_msg == NULL) { 843281806Srpaulo wpa_printf(MSG_INFO, "No matching Access-Request message found"); 844189251Ssam return 1; 845189251Ssam } 846189251Ssam 847189251Ssam if (auth && 848189251Ssam radius_msg_verify_msg_auth(msg, secret, secret_len, 849189251Ssam sent_msg->hdr->authenticator)) { 850189251Ssam return 1; 851189251Ssam } 852189251Ssam 853189251Ssam /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ 854189251Ssam addr[0] = (u8 *) msg->hdr; 855189251Ssam len[0] = 1 + 1 + 2; 856189251Ssam addr[1] = sent_msg->hdr->authenticator; 857189251Ssam len[1] = MD5_MAC_LEN; 858214734Srpaulo addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr); 859214734Srpaulo len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); 860189251Ssam addr[3] = secret; 861189251Ssam len[3] = secret_len; 862189251Ssam md5_vector(4, addr, len, hash); 863281806Srpaulo if (os_memcmp_const(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) { 864281806Srpaulo wpa_printf(MSG_INFO, "Response Authenticator invalid!"); 865189251Ssam return 1; 866189251Ssam } 867189251Ssam 868189251Ssam return 0; 869189251Ssam} 870189251Ssam 871189251Ssam 872189251Ssamint radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, 873189251Ssam u8 type) 874189251Ssam{ 875189251Ssam struct radius_attr_hdr *attr; 876189251Ssam size_t i; 877189251Ssam int count = 0; 878189251Ssam 879189251Ssam for (i = 0; i < src->attr_used; i++) { 880189251Ssam attr = radius_get_attr_hdr(src, i); 881252726Srpaulo if (attr->type == type && attr->length >= sizeof(*attr)) { 882189251Ssam if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1), 883189251Ssam attr->length - sizeof(*attr))) 884189251Ssam return -1; 885189251Ssam count++; 886189251Ssam } 887189251Ssam } 888189251Ssam 889189251Ssam return count; 890189251Ssam} 891189251Ssam 892189251Ssam 893189251Ssam/* Create Request Authenticator. The value should be unique over the lifetime 894189251Ssam * of the shared secret between authenticator and authentication server. 895189251Ssam * Use one-way MD5 hash calculated from current timestamp and some data given 896189251Ssam * by the caller. */ 897189251Ssamvoid radius_msg_make_authenticator(struct radius_msg *msg, 898189251Ssam const u8 *data, size_t len) 899189251Ssam{ 900189251Ssam struct os_time tv; 901189251Ssam long int l; 902189251Ssam const u8 *addr[3]; 903189251Ssam size_t elen[3]; 904189251Ssam 905189251Ssam os_get_time(&tv); 906189251Ssam l = os_random(); 907189251Ssam addr[0] = (u8 *) &tv; 908189251Ssam elen[0] = sizeof(tv); 909189251Ssam addr[1] = data; 910189251Ssam elen[1] = len; 911189251Ssam addr[2] = (u8 *) &l; 912189251Ssam elen[2] = sizeof(l); 913189251Ssam md5_vector(3, addr, elen, msg->hdr->authenticator); 914189251Ssam} 915189251Ssam 916189251Ssam 917189251Ssam/* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message. 918189251Ssam * Returns the Attribute payload and sets alen to indicate the length of the 919189251Ssam * payload if a vendor attribute with subtype is found, otherwise returns NULL. 920189251Ssam * The returned payload is allocated with os_malloc() and caller must free it 921189251Ssam * by calling os_free(). 922189251Ssam */ 923189251Ssamstatic u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor, 924189251Ssam u8 subtype, size_t *alen) 925189251Ssam{ 926189251Ssam u8 *data, *pos; 927189251Ssam size_t i, len; 928189251Ssam 929189251Ssam if (msg == NULL) 930189251Ssam return NULL; 931189251Ssam 932189251Ssam for (i = 0; i < msg->attr_used; i++) { 933189251Ssam struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); 934189251Ssam size_t left; 935189251Ssam u32 vendor_id; 936189251Ssam struct radius_attr_vendor *vhdr; 937189251Ssam 938252726Srpaulo if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC || 939252726Srpaulo attr->length < sizeof(*attr)) 940189251Ssam continue; 941189251Ssam 942189251Ssam left = attr->length - sizeof(*attr); 943189251Ssam if (left < 4) 944189251Ssam continue; 945189251Ssam 946189251Ssam pos = (u8 *) (attr + 1); 947189251Ssam 948189251Ssam os_memcpy(&vendor_id, pos, 4); 949189251Ssam pos += 4; 950189251Ssam left -= 4; 951189251Ssam 952189251Ssam if (ntohl(vendor_id) != vendor) 953189251Ssam continue; 954189251Ssam 955189251Ssam while (left >= sizeof(*vhdr)) { 956189251Ssam vhdr = (struct radius_attr_vendor *) pos; 957189251Ssam if (vhdr->vendor_length > left || 958189251Ssam vhdr->vendor_length < sizeof(*vhdr)) { 959189251Ssam break; 960189251Ssam } 961189251Ssam if (vhdr->vendor_type != subtype) { 962189251Ssam pos += vhdr->vendor_length; 963189251Ssam left -= vhdr->vendor_length; 964189251Ssam continue; 965189251Ssam } 966189251Ssam 967189251Ssam len = vhdr->vendor_length - sizeof(*vhdr); 968189251Ssam data = os_malloc(len); 969189251Ssam if (data == NULL) 970189251Ssam return NULL; 971189251Ssam os_memcpy(data, pos + sizeof(*vhdr), len); 972189251Ssam if (alen) 973189251Ssam *alen = len; 974189251Ssam return data; 975189251Ssam } 976189251Ssam } 977189251Ssam 978189251Ssam return NULL; 979189251Ssam} 980189251Ssam 981189251Ssam 982189251Ssamstatic u8 * decrypt_ms_key(const u8 *key, size_t len, 983189251Ssam const u8 *req_authenticator, 984189251Ssam const u8 *secret, size_t secret_len, size_t *reslen) 985189251Ssam{ 986189251Ssam u8 *plain, *ppos, *res; 987189251Ssam const u8 *pos; 988189251Ssam size_t left, plen; 989189251Ssam u8 hash[MD5_MAC_LEN]; 990189251Ssam int i, first = 1; 991189251Ssam const u8 *addr[3]; 992189251Ssam size_t elen[3]; 993189251Ssam 994189251Ssam /* key: 16-bit salt followed by encrypted key info */ 995189251Ssam 996281806Srpaulo if (len < 2 + 16) { 997281806Srpaulo wpa_printf(MSG_DEBUG, "RADIUS: %s: Len is too small: %d", 998281806Srpaulo __func__, (int) len); 999189251Ssam return NULL; 1000281806Srpaulo } 1001189251Ssam 1002189251Ssam pos = key + 2; 1003189251Ssam left = len - 2; 1004189251Ssam if (left % 16) { 1005281806Srpaulo wpa_printf(MSG_INFO, "RADIUS: Invalid ms key len %lu", 1006281806Srpaulo (unsigned long) left); 1007189251Ssam return NULL; 1008189251Ssam } 1009189251Ssam 1010189251Ssam plen = left; 1011189251Ssam ppos = plain = os_malloc(plen); 1012189251Ssam if (plain == NULL) 1013189251Ssam return NULL; 1014189251Ssam plain[0] = 0; 1015189251Ssam 1016189251Ssam while (left > 0) { 1017189251Ssam /* b(1) = MD5(Secret + Request-Authenticator + Salt) 1018189251Ssam * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ 1019189251Ssam 1020189251Ssam addr[0] = secret; 1021189251Ssam elen[0] = secret_len; 1022189251Ssam if (first) { 1023189251Ssam addr[1] = req_authenticator; 1024189251Ssam elen[1] = MD5_MAC_LEN; 1025189251Ssam addr[2] = key; 1026189251Ssam elen[2] = 2; /* Salt */ 1027189251Ssam } else { 1028189251Ssam addr[1] = pos - MD5_MAC_LEN; 1029189251Ssam elen[1] = MD5_MAC_LEN; 1030189251Ssam } 1031189251Ssam md5_vector(first ? 3 : 2, addr, elen, hash); 1032189251Ssam first = 0; 1033189251Ssam 1034189251Ssam for (i = 0; i < MD5_MAC_LEN; i++) 1035189251Ssam *ppos++ = *pos++ ^ hash[i]; 1036189251Ssam left -= MD5_MAC_LEN; 1037189251Ssam } 1038189251Ssam 1039189251Ssam if (plain[0] == 0 || plain[0] > plen - 1) { 1040281806Srpaulo wpa_printf(MSG_INFO, "RADIUS: Failed to decrypt MPPE key"); 1041189251Ssam os_free(plain); 1042189251Ssam return NULL; 1043189251Ssam } 1044189251Ssam 1045189251Ssam res = os_malloc(plain[0]); 1046189251Ssam if (res == NULL) { 1047189251Ssam os_free(plain); 1048189251Ssam return NULL; 1049189251Ssam } 1050189251Ssam os_memcpy(res, plain + 1, plain[0]); 1051189251Ssam if (reslen) 1052189251Ssam *reslen = plain[0]; 1053189251Ssam os_free(plain); 1054189251Ssam return res; 1055189251Ssam} 1056189251Ssam 1057189251Ssam 1058189251Ssamstatic void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt, 1059189251Ssam const u8 *req_authenticator, 1060189251Ssam const u8 *secret, size_t secret_len, 1061189251Ssam u8 *ebuf, size_t *elen) 1062189251Ssam{ 1063189251Ssam int i, len, first = 1; 1064189251Ssam u8 hash[MD5_MAC_LEN], saltbuf[2], *pos; 1065189251Ssam const u8 *addr[3]; 1066189251Ssam size_t _len[3]; 1067189251Ssam 1068189251Ssam WPA_PUT_BE16(saltbuf, salt); 1069189251Ssam 1070189251Ssam len = 1 + key_len; 1071189251Ssam if (len & 0x0f) { 1072189251Ssam len = (len & 0xf0) + 16; 1073189251Ssam } 1074189251Ssam os_memset(ebuf, 0, len); 1075189251Ssam ebuf[0] = key_len; 1076189251Ssam os_memcpy(ebuf + 1, key, key_len); 1077189251Ssam 1078189251Ssam *elen = len; 1079189251Ssam 1080189251Ssam pos = ebuf; 1081189251Ssam while (len > 0) { 1082189251Ssam /* b(1) = MD5(Secret + Request-Authenticator + Salt) 1083189251Ssam * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ 1084189251Ssam addr[0] = secret; 1085189251Ssam _len[0] = secret_len; 1086189251Ssam if (first) { 1087189251Ssam addr[1] = req_authenticator; 1088189251Ssam _len[1] = MD5_MAC_LEN; 1089189251Ssam addr[2] = saltbuf; 1090189251Ssam _len[2] = sizeof(saltbuf); 1091189251Ssam } else { 1092189251Ssam addr[1] = pos - MD5_MAC_LEN; 1093189251Ssam _len[1] = MD5_MAC_LEN; 1094189251Ssam } 1095189251Ssam md5_vector(first ? 3 : 2, addr, _len, hash); 1096189251Ssam first = 0; 1097189251Ssam 1098189251Ssam for (i = 0; i < MD5_MAC_LEN; i++) 1099189251Ssam *pos++ ^= hash[i]; 1100189251Ssam 1101189251Ssam len -= MD5_MAC_LEN; 1102189251Ssam } 1103189251Ssam} 1104189251Ssam 1105189251Ssam 1106189251Ssamstruct radius_ms_mppe_keys * 1107189251Ssamradius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, 1108189251Ssam const u8 *secret, size_t secret_len) 1109189251Ssam{ 1110189251Ssam u8 *key; 1111189251Ssam size_t keylen; 1112189251Ssam struct radius_ms_mppe_keys *keys; 1113189251Ssam 1114189251Ssam if (msg == NULL || sent_msg == NULL) 1115189251Ssam return NULL; 1116189251Ssam 1117189251Ssam keys = os_zalloc(sizeof(*keys)); 1118189251Ssam if (keys == NULL) 1119189251Ssam return NULL; 1120189251Ssam 1121189251Ssam key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT, 1122189251Ssam RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY, 1123189251Ssam &keylen); 1124189251Ssam if (key) { 1125189251Ssam keys->send = decrypt_ms_key(key, keylen, 1126189251Ssam sent_msg->hdr->authenticator, 1127189251Ssam secret, secret_len, 1128189251Ssam &keys->send_len); 1129281806Srpaulo if (!keys->send) { 1130281806Srpaulo wpa_printf(MSG_DEBUG, 1131281806Srpaulo "RADIUS: Failed to decrypt send key"); 1132281806Srpaulo } 1133189251Ssam os_free(key); 1134189251Ssam } 1135189251Ssam 1136189251Ssam key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT, 1137189251Ssam RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY, 1138189251Ssam &keylen); 1139189251Ssam if (key) { 1140189251Ssam keys->recv = decrypt_ms_key(key, keylen, 1141189251Ssam sent_msg->hdr->authenticator, 1142189251Ssam secret, secret_len, 1143189251Ssam &keys->recv_len); 1144281806Srpaulo if (!keys->recv) { 1145281806Srpaulo wpa_printf(MSG_DEBUG, 1146281806Srpaulo "RADIUS: Failed to decrypt recv key"); 1147281806Srpaulo } 1148189251Ssam os_free(key); 1149189251Ssam } 1150189251Ssam 1151189251Ssam return keys; 1152189251Ssam} 1153189251Ssam 1154189251Ssam 1155189251Ssamstruct radius_ms_mppe_keys * 1156189251Ssamradius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg, 1157189251Ssam const u8 *secret, size_t secret_len) 1158189251Ssam{ 1159189251Ssam u8 *key; 1160189251Ssam size_t keylen; 1161189251Ssam struct radius_ms_mppe_keys *keys; 1162189251Ssam 1163189251Ssam if (msg == NULL || sent_msg == NULL) 1164189251Ssam return NULL; 1165189251Ssam 1166189251Ssam keys = os_zalloc(sizeof(*keys)); 1167189251Ssam if (keys == NULL) 1168189251Ssam return NULL; 1169189251Ssam 1170189251Ssam key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO, 1171189251Ssam RADIUS_CISCO_AV_PAIR, &keylen); 1172189251Ssam if (key && keylen == 51 && 1173189251Ssam os_memcmp(key, "leap:session-key=", 17) == 0) { 1174189251Ssam keys->recv = decrypt_ms_key(key + 17, keylen - 17, 1175189251Ssam sent_msg->hdr->authenticator, 1176189251Ssam secret, secret_len, 1177189251Ssam &keys->recv_len); 1178189251Ssam } 1179189251Ssam os_free(key); 1180189251Ssam 1181189251Ssam return keys; 1182189251Ssam} 1183189251Ssam 1184189251Ssam 1185189251Ssamint radius_msg_add_mppe_keys(struct radius_msg *msg, 1186189251Ssam const u8 *req_authenticator, 1187189251Ssam const u8 *secret, size_t secret_len, 1188189251Ssam const u8 *send_key, size_t send_key_len, 1189189251Ssam const u8 *recv_key, size_t recv_key_len) 1190189251Ssam{ 1191189251Ssam struct radius_attr_hdr *attr; 1192189251Ssam u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT); 1193189251Ssam u8 *buf; 1194189251Ssam struct radius_attr_vendor *vhdr; 1195189251Ssam u8 *pos; 1196189251Ssam size_t elen; 1197189251Ssam int hlen; 1198189251Ssam u16 salt; 1199189251Ssam 1200189251Ssam hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2; 1201189251Ssam 1202189251Ssam /* MS-MPPE-Send-Key */ 1203189251Ssam buf = os_malloc(hlen + send_key_len + 16); 1204189251Ssam if (buf == NULL) { 1205189251Ssam return 0; 1206189251Ssam } 1207189251Ssam pos = buf; 1208189251Ssam os_memcpy(pos, &vendor_id, sizeof(vendor_id)); 1209189251Ssam pos += sizeof(vendor_id); 1210189251Ssam vhdr = (struct radius_attr_vendor *) pos; 1211189251Ssam vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY; 1212189251Ssam pos = (u8 *) (vhdr + 1); 1213189251Ssam salt = os_random() | 0x8000; 1214189251Ssam WPA_PUT_BE16(pos, salt); 1215189251Ssam pos += 2; 1216189251Ssam encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret, 1217189251Ssam secret_len, pos, &elen); 1218189251Ssam vhdr->vendor_length = hlen + elen - sizeof(vendor_id); 1219189251Ssam 1220189251Ssam attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, 1221189251Ssam buf, hlen + elen); 1222189251Ssam os_free(buf); 1223189251Ssam if (attr == NULL) { 1224189251Ssam return 0; 1225189251Ssam } 1226189251Ssam 1227189251Ssam /* MS-MPPE-Recv-Key */ 1228289549Srpaulo buf = os_malloc(hlen + recv_key_len + 16); 1229189251Ssam if (buf == NULL) { 1230189251Ssam return 0; 1231189251Ssam } 1232189251Ssam pos = buf; 1233189251Ssam os_memcpy(pos, &vendor_id, sizeof(vendor_id)); 1234189251Ssam pos += sizeof(vendor_id); 1235189251Ssam vhdr = (struct radius_attr_vendor *) pos; 1236189251Ssam vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY; 1237189251Ssam pos = (u8 *) (vhdr + 1); 1238189251Ssam salt ^= 1; 1239189251Ssam WPA_PUT_BE16(pos, salt); 1240189251Ssam pos += 2; 1241189251Ssam encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret, 1242189251Ssam secret_len, pos, &elen); 1243189251Ssam vhdr->vendor_length = hlen + elen - sizeof(vendor_id); 1244189251Ssam 1245189251Ssam attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, 1246189251Ssam buf, hlen + elen); 1247189251Ssam os_free(buf); 1248189251Ssam if (attr == NULL) { 1249189251Ssam return 0; 1250189251Ssam } 1251189251Ssam 1252189251Ssam return 1; 1253189251Ssam} 1254189251Ssam 1255189251Ssam 1256281806Srpauloint radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data, 1257281806Srpaulo size_t len) 1258189251Ssam{ 1259281806Srpaulo struct radius_attr_hdr *attr; 1260281806Srpaulo u8 *buf, *pos; 1261281806Srpaulo size_t alen; 1262281806Srpaulo 1263281806Srpaulo alen = 4 + 2 + len; 1264281806Srpaulo buf = os_malloc(alen); 1265281806Srpaulo if (buf == NULL) 1266281806Srpaulo return 0; 1267281806Srpaulo pos = buf; 1268281806Srpaulo WPA_PUT_BE32(pos, RADIUS_VENDOR_ID_WFA); 1269281806Srpaulo pos += 4; 1270281806Srpaulo *pos++ = subtype; 1271281806Srpaulo *pos++ = 2 + len; 1272281806Srpaulo os_memcpy(pos, data, len); 1273281806Srpaulo attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, 1274281806Srpaulo buf, alen); 1275281806Srpaulo os_free(buf); 1276281806Srpaulo if (attr == NULL) 1277281806Srpaulo return 0; 1278281806Srpaulo 1279281806Srpaulo return 1; 1280281806Srpaulo} 1281281806Srpaulo 1282281806Srpaulo 1283281806Srpauloint radius_user_password_hide(struct radius_msg *msg, 1284281806Srpaulo const u8 *data, size_t data_len, 1285281806Srpaulo const u8 *secret, size_t secret_len, 1286281806Srpaulo u8 *buf, size_t buf_len) 1287281806Srpaulo{ 1288281806Srpaulo size_t padlen, i, pos; 1289189251Ssam const u8 *addr[2]; 1290189251Ssam size_t len[2]; 1291189251Ssam u8 hash[16]; 1292189251Ssam 1293281806Srpaulo if (data_len + 16 > buf_len) 1294281806Srpaulo return -1; 1295189251Ssam 1296189251Ssam os_memcpy(buf, data, data_len); 1297189251Ssam 1298189251Ssam padlen = data_len % 16; 1299281806Srpaulo if (padlen && data_len < buf_len) { 1300189251Ssam padlen = 16 - padlen; 1301189251Ssam os_memset(buf + data_len, 0, padlen); 1302281806Srpaulo buf_len = data_len + padlen; 1303281806Srpaulo } else { 1304281806Srpaulo buf_len = data_len; 1305189251Ssam } 1306189251Ssam 1307189251Ssam addr[0] = secret; 1308189251Ssam len[0] = secret_len; 1309189251Ssam addr[1] = msg->hdr->authenticator; 1310189251Ssam len[1] = 16; 1311189251Ssam md5_vector(2, addr, len, hash); 1312189251Ssam 1313189251Ssam for (i = 0; i < 16; i++) 1314189251Ssam buf[i] ^= hash[i]; 1315189251Ssam pos = 16; 1316189251Ssam 1317189251Ssam while (pos < buf_len) { 1318189251Ssam addr[0] = secret; 1319189251Ssam len[0] = secret_len; 1320189251Ssam addr[1] = &buf[pos - 16]; 1321189251Ssam len[1] = 16; 1322189251Ssam md5_vector(2, addr, len, hash); 1323189251Ssam 1324189251Ssam for (i = 0; i < 16; i++) 1325189251Ssam buf[pos + i] ^= hash[i]; 1326189251Ssam 1327189251Ssam pos += 16; 1328189251Ssam } 1329189251Ssam 1330281806Srpaulo return buf_len; 1331281806Srpaulo} 1332281806Srpaulo 1333281806Srpaulo 1334281806Srpaulo/* Add User-Password attribute to a RADIUS message and encrypt it as specified 1335281806Srpaulo * in RFC 2865, Chap. 5.2 */ 1336281806Srpaulostruct radius_attr_hdr * 1337281806Srpauloradius_msg_add_attr_user_password(struct radius_msg *msg, 1338281806Srpaulo const u8 *data, size_t data_len, 1339281806Srpaulo const u8 *secret, size_t secret_len) 1340281806Srpaulo{ 1341281806Srpaulo u8 buf[128]; 1342281806Srpaulo int res; 1343281806Srpaulo 1344281806Srpaulo res = radius_user_password_hide(msg, data, data_len, 1345281806Srpaulo secret, secret_len, buf, sizeof(buf)); 1346281806Srpaulo if (res < 0) 1347281806Srpaulo return NULL; 1348281806Srpaulo 1349189251Ssam return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD, 1350281806Srpaulo buf, res); 1351189251Ssam} 1352189251Ssam 1353189251Ssam 1354189251Ssamint radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len) 1355189251Ssam{ 1356189251Ssam struct radius_attr_hdr *attr = NULL, *tmp; 1357189251Ssam size_t i, dlen; 1358189251Ssam 1359189251Ssam for (i = 0; i < msg->attr_used; i++) { 1360189251Ssam tmp = radius_get_attr_hdr(msg, i); 1361189251Ssam if (tmp->type == type) { 1362189251Ssam attr = tmp; 1363189251Ssam break; 1364189251Ssam } 1365189251Ssam } 1366189251Ssam 1367252726Srpaulo if (!attr || attr->length < sizeof(*attr)) 1368189251Ssam return -1; 1369189251Ssam 1370189251Ssam dlen = attr->length - sizeof(*attr); 1371189251Ssam if (buf) 1372189251Ssam os_memcpy(buf, (attr + 1), dlen > len ? len : dlen); 1373189251Ssam return dlen; 1374189251Ssam} 1375189251Ssam 1376189251Ssam 1377189251Ssamint radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, 1378189251Ssam size_t *len, const u8 *start) 1379189251Ssam{ 1380189251Ssam size_t i; 1381189251Ssam struct radius_attr_hdr *attr = NULL, *tmp; 1382189251Ssam 1383189251Ssam for (i = 0; i < msg->attr_used; i++) { 1384189251Ssam tmp = radius_get_attr_hdr(msg, i); 1385189251Ssam if (tmp->type == type && 1386189251Ssam (start == NULL || (u8 *) tmp > start)) { 1387189251Ssam attr = tmp; 1388189251Ssam break; 1389189251Ssam } 1390189251Ssam } 1391189251Ssam 1392252726Srpaulo if (!attr || attr->length < sizeof(*attr)) 1393189251Ssam return -1; 1394189251Ssam 1395189251Ssam *buf = (u8 *) (attr + 1); 1396189251Ssam *len = attr->length - sizeof(*attr); 1397189251Ssam return 0; 1398189251Ssam} 1399189251Ssam 1400189251Ssam 1401189251Ssamint radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len) 1402189251Ssam{ 1403189251Ssam size_t i; 1404189251Ssam int count; 1405189251Ssam 1406189251Ssam for (count = 0, i = 0; i < msg->attr_used; i++) { 1407189251Ssam struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); 1408189251Ssam if (attr->type == type && 1409189251Ssam attr->length >= sizeof(struct radius_attr_hdr) + min_len) 1410189251Ssam count++; 1411189251Ssam } 1412189251Ssam 1413189251Ssam return count; 1414189251Ssam} 1415189251Ssam 1416189251Ssam 1417189251Ssamstruct radius_tunnel_attrs { 1418189251Ssam int tag_used; 1419189251Ssam int type; /* Tunnel-Type */ 1420189251Ssam int medium_type; /* Tunnel-Medium-Type */ 1421189251Ssam int vlanid; 1422189251Ssam}; 1423189251Ssam 1424189251Ssam 1425189251Ssam/** 1426189251Ssam * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information 1427189251Ssam * @msg: RADIUS message 1428289549Srpaulo * Returns: VLAN ID for the first tunnel configuration or 0 if none is found 1429189251Ssam */ 1430189251Ssamint radius_msg_get_vlanid(struct radius_msg *msg) 1431189251Ssam{ 1432189251Ssam struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun; 1433189251Ssam size_t i; 1434189251Ssam struct radius_attr_hdr *attr = NULL; 1435189251Ssam const u8 *data; 1436189251Ssam char buf[10]; 1437189251Ssam size_t dlen; 1438189251Ssam 1439189251Ssam os_memset(&tunnel, 0, sizeof(tunnel)); 1440189251Ssam 1441189251Ssam for (i = 0; i < msg->attr_used; i++) { 1442189251Ssam attr = radius_get_attr_hdr(msg, i); 1443252726Srpaulo if (attr->length < sizeof(*attr)) 1444252726Srpaulo return -1; 1445189251Ssam data = (const u8 *) (attr + 1); 1446189251Ssam dlen = attr->length - sizeof(*attr); 1447189251Ssam if (attr->length < 3) 1448189251Ssam continue; 1449189251Ssam if (data[0] >= RADIUS_TUNNEL_TAGS) 1450189251Ssam tun = &tunnel[0]; 1451189251Ssam else 1452189251Ssam tun = &tunnel[data[0]]; 1453189251Ssam 1454189251Ssam switch (attr->type) { 1455189251Ssam case RADIUS_ATTR_TUNNEL_TYPE: 1456189251Ssam if (attr->length != 6) 1457189251Ssam break; 1458189251Ssam tun->tag_used++; 1459189251Ssam tun->type = WPA_GET_BE24(data + 1); 1460189251Ssam break; 1461189251Ssam case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE: 1462189251Ssam if (attr->length != 6) 1463189251Ssam break; 1464189251Ssam tun->tag_used++; 1465189251Ssam tun->medium_type = WPA_GET_BE24(data + 1); 1466189251Ssam break; 1467189251Ssam case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID: 1468189251Ssam if (data[0] < RADIUS_TUNNEL_TAGS) { 1469189251Ssam data++; 1470189251Ssam dlen--; 1471189251Ssam } 1472189251Ssam if (dlen >= sizeof(buf)) 1473189251Ssam break; 1474189251Ssam os_memcpy(buf, data, dlen); 1475189251Ssam buf[dlen] = '\0'; 1476189251Ssam tun->tag_used++; 1477189251Ssam tun->vlanid = atoi(buf); 1478189251Ssam break; 1479189251Ssam } 1480189251Ssam } 1481189251Ssam 1482189251Ssam for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) { 1483189251Ssam tun = &tunnel[i]; 1484189251Ssam if (tun->tag_used && 1485189251Ssam tun->type == RADIUS_TUNNEL_TYPE_VLAN && 1486189251Ssam tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 && 1487189251Ssam tun->vlanid > 0) 1488189251Ssam return tun->vlanid; 1489189251Ssam } 1490189251Ssam 1491289549Srpaulo return 0; 1492189251Ssam} 1493214734Srpaulo 1494214734Srpaulo 1495252726Srpaulo/** 1496252726Srpaulo * radius_msg_get_tunnel_password - Parse RADIUS attribute Tunnel-Password 1497252726Srpaulo * @msg: Received RADIUS message 1498252726Srpaulo * @keylen: Length of returned password 1499252726Srpaulo * @secret: RADIUS shared secret 1500252726Srpaulo * @secret_len: Length of secret 1501252726Srpaulo * @sent_msg: Sent RADIUS message 1502252726Srpaulo * @n: Number of password attribute to return (starting with 0) 1503252726Srpaulo * Returns: Pointer to n-th password (free with os_free) or %NULL 1504252726Srpaulo */ 1505252726Srpaulochar * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen, 1506252726Srpaulo const u8 *secret, size_t secret_len, 1507252726Srpaulo struct radius_msg *sent_msg, size_t n) 1508252726Srpaulo{ 1509252726Srpaulo u8 *buf = NULL; 1510252726Srpaulo size_t buflen; 1511252726Srpaulo const u8 *salt; 1512252726Srpaulo u8 *str; 1513252726Srpaulo const u8 *addr[3]; 1514252726Srpaulo size_t len[3]; 1515252726Srpaulo u8 hash[16]; 1516252726Srpaulo u8 *pos; 1517252726Srpaulo size_t i, j = 0; 1518252726Srpaulo struct radius_attr_hdr *attr; 1519252726Srpaulo const u8 *data; 1520252726Srpaulo size_t dlen; 1521252726Srpaulo const u8 *fdata = NULL; /* points to found item */ 1522252726Srpaulo size_t fdlen = -1; 1523252726Srpaulo char *ret = NULL; 1524252726Srpaulo 1525252726Srpaulo /* find n-th valid Tunnel-Password attribute */ 1526252726Srpaulo for (i = 0; i < msg->attr_used; i++) { 1527252726Srpaulo attr = radius_get_attr_hdr(msg, i); 1528252726Srpaulo if (attr == NULL || 1529252726Srpaulo attr->type != RADIUS_ATTR_TUNNEL_PASSWORD) { 1530252726Srpaulo continue; 1531252726Srpaulo } 1532252726Srpaulo if (attr->length <= 5) 1533252726Srpaulo continue; 1534252726Srpaulo data = (const u8 *) (attr + 1); 1535252726Srpaulo dlen = attr->length - sizeof(*attr); 1536252726Srpaulo if (dlen <= 3 || dlen % 16 != 3) 1537252726Srpaulo continue; 1538252726Srpaulo j++; 1539252726Srpaulo if (j <= n) 1540252726Srpaulo continue; 1541252726Srpaulo 1542252726Srpaulo fdata = data; 1543252726Srpaulo fdlen = dlen; 1544252726Srpaulo break; 1545252726Srpaulo } 1546252726Srpaulo if (fdata == NULL) 1547252726Srpaulo goto out; 1548252726Srpaulo 1549252726Srpaulo /* alloc writable memory for decryption */ 1550252726Srpaulo buf = os_malloc(fdlen); 1551252726Srpaulo if (buf == NULL) 1552252726Srpaulo goto out; 1553252726Srpaulo os_memcpy(buf, fdata, fdlen); 1554252726Srpaulo buflen = fdlen; 1555252726Srpaulo 1556252726Srpaulo /* init pointers */ 1557252726Srpaulo salt = buf + 1; 1558252726Srpaulo str = buf + 3; 1559252726Srpaulo 1560252726Srpaulo /* decrypt blocks */ 1561252726Srpaulo pos = buf + buflen - 16; /* last block */ 1562252726Srpaulo while (pos >= str + 16) { /* all but the first block */ 1563252726Srpaulo addr[0] = secret; 1564252726Srpaulo len[0] = secret_len; 1565252726Srpaulo addr[1] = pos - 16; 1566252726Srpaulo len[1] = 16; 1567252726Srpaulo md5_vector(2, addr, len, hash); 1568252726Srpaulo 1569252726Srpaulo for (i = 0; i < 16; i++) 1570252726Srpaulo pos[i] ^= hash[i]; 1571252726Srpaulo 1572252726Srpaulo pos -= 16; 1573252726Srpaulo } 1574252726Srpaulo 1575252726Srpaulo /* decrypt first block */ 1576252726Srpaulo if (str != pos) 1577252726Srpaulo goto out; 1578252726Srpaulo addr[0] = secret; 1579252726Srpaulo len[0] = secret_len; 1580252726Srpaulo addr[1] = sent_msg->hdr->authenticator; 1581252726Srpaulo len[1] = 16; 1582252726Srpaulo addr[2] = salt; 1583252726Srpaulo len[2] = 2; 1584252726Srpaulo md5_vector(3, addr, len, hash); 1585252726Srpaulo 1586252726Srpaulo for (i = 0; i < 16; i++) 1587252726Srpaulo pos[i] ^= hash[i]; 1588252726Srpaulo 1589252726Srpaulo /* derive plaintext length from first subfield */ 1590252726Srpaulo *keylen = (unsigned char) str[0]; 1591252726Srpaulo if ((u8 *) (str + *keylen) >= (u8 *) (buf + buflen)) { 1592252726Srpaulo /* decryption error - invalid key length */ 1593252726Srpaulo goto out; 1594252726Srpaulo } 1595252726Srpaulo if (*keylen == 0) { 1596252726Srpaulo /* empty password */ 1597252726Srpaulo goto out; 1598252726Srpaulo } 1599252726Srpaulo 1600252726Srpaulo /* copy passphrase into new buffer */ 1601252726Srpaulo ret = os_malloc(*keylen); 1602252726Srpaulo if (ret) 1603252726Srpaulo os_memcpy(ret, str + 1, *keylen); 1604252726Srpaulo 1605252726Srpauloout: 1606252726Srpaulo /* return new buffer */ 1607252726Srpaulo os_free(buf); 1608252726Srpaulo return ret; 1609252726Srpaulo} 1610252726Srpaulo 1611252726Srpaulo 1612214734Srpaulovoid radius_free_class(struct radius_class_data *c) 1613214734Srpaulo{ 1614214734Srpaulo size_t i; 1615214734Srpaulo if (c == NULL) 1616214734Srpaulo return; 1617214734Srpaulo for (i = 0; i < c->count; i++) 1618214734Srpaulo os_free(c->attr[i].data); 1619214734Srpaulo os_free(c->attr); 1620214734Srpaulo c->attr = NULL; 1621214734Srpaulo c->count = 0; 1622214734Srpaulo} 1623214734Srpaulo 1624214734Srpaulo 1625214734Srpauloint radius_copy_class(struct radius_class_data *dst, 1626214734Srpaulo const struct radius_class_data *src) 1627214734Srpaulo{ 1628214734Srpaulo size_t i; 1629214734Srpaulo 1630214734Srpaulo if (src->attr == NULL) 1631214734Srpaulo return 0; 1632214734Srpaulo 1633252726Srpaulo dst->attr = os_calloc(src->count, sizeof(struct radius_attr_data)); 1634214734Srpaulo if (dst->attr == NULL) 1635214734Srpaulo return -1; 1636214734Srpaulo 1637214734Srpaulo dst->count = 0; 1638214734Srpaulo 1639214734Srpaulo for (i = 0; i < src->count; i++) { 1640214734Srpaulo dst->attr[i].data = os_malloc(src->attr[i].len); 1641214734Srpaulo if (dst->attr[i].data == NULL) 1642214734Srpaulo break; 1643214734Srpaulo dst->count++; 1644214734Srpaulo os_memcpy(dst->attr[i].data, src->attr[i].data, 1645214734Srpaulo src->attr[i].len); 1646214734Srpaulo dst->attr[i].len = src->attr[i].len; 1647214734Srpaulo } 1648214734Srpaulo 1649214734Srpaulo return 0; 1650214734Srpaulo} 1651252726Srpaulo 1652252726Srpaulo 1653252726Srpaulou8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs) 1654252726Srpaulo{ 1655252726Srpaulo size_t i, j; 1656252726Srpaulo struct radius_attr_hdr *attr; 1657252726Srpaulo 1658252726Srpaulo for (i = 0; i < msg->attr_used; i++) { 1659252726Srpaulo attr = radius_get_attr_hdr(msg, i); 1660252726Srpaulo 1661252726Srpaulo for (j = 0; attrs[j]; j++) { 1662252726Srpaulo if (attr->type == attrs[j]) 1663252726Srpaulo break; 1664252726Srpaulo } 1665252726Srpaulo 1666252726Srpaulo if (attrs[j] == 0) 1667252726Srpaulo return attr->type; /* unlisted attr */ 1668252726Srpaulo } 1669252726Srpaulo 1670252726Srpaulo return 0; 1671252726Srpaulo} 1672