1189251Ssam/* 2214734Srpaulo * RADIUS message processing 3252726Srpaulo * Copyright (c) 2002-2009, 2011-2012, 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 170189251Ssamstatic 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 }, 234252726Srpaulo { RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 } 235189251Ssam}; 236189251Ssam#define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0])) 237189251Ssam 238189251Ssam 239189251Ssamstatic struct radius_attr_type *radius_get_attr_type(u8 type) 240189251Ssam{ 241189251Ssam size_t i; 242189251Ssam 243189251Ssam for (i = 0; i < RADIUS_ATTRS; i++) { 244189251Ssam if (type == radius_attrs[i].type) 245189251Ssam return &radius_attrs[i]; 246189251Ssam } 247189251Ssam 248189251Ssam return NULL; 249189251Ssam} 250189251Ssam 251189251Ssam 252189251Ssamstatic void print_char(char c) 253189251Ssam{ 254189251Ssam if (c >= 32 && c < 127) 255189251Ssam printf("%c", c); 256189251Ssam else 257189251Ssam printf("<%02x>", c); 258189251Ssam} 259189251Ssam 260189251Ssam 261189251Ssamstatic void radius_msg_dump_attr(struct radius_attr_hdr *hdr) 262189251Ssam{ 263189251Ssam struct radius_attr_type *attr; 264189251Ssam int i, len; 265189251Ssam unsigned char *pos; 266189251Ssam 267189251Ssam attr = radius_get_attr_type(hdr->type); 268189251Ssam 269189251Ssam printf(" Attribute %d (%s) length=%d\n", 270189251Ssam hdr->type, attr ? attr->name : "?Unknown?", hdr->length); 271189251Ssam 272252726Srpaulo if (attr == NULL || hdr->length < sizeof(struct radius_attr_hdr)) 273189251Ssam return; 274189251Ssam 275189251Ssam len = hdr->length - sizeof(struct radius_attr_hdr); 276189251Ssam pos = (unsigned char *) (hdr + 1); 277189251Ssam 278189251Ssam switch (attr->data_type) { 279189251Ssam case RADIUS_ATTR_TEXT: 280189251Ssam printf(" Value: '"); 281189251Ssam for (i = 0; i < len; i++) 282189251Ssam print_char(pos[i]); 283189251Ssam printf("'\n"); 284189251Ssam break; 285189251Ssam 286189251Ssam case RADIUS_ATTR_IP: 287189251Ssam if (len == 4) { 288189251Ssam struct in_addr addr; 289189251Ssam os_memcpy(&addr, pos, 4); 290189251Ssam printf(" Value: %s\n", inet_ntoa(addr)); 291189251Ssam } else 292189251Ssam printf(" Invalid IP address length %d\n", len); 293189251Ssam break; 294189251Ssam 295189251Ssam#ifdef CONFIG_IPV6 296189251Ssam case RADIUS_ATTR_IPV6: 297189251Ssam if (len == 16) { 298189251Ssam char buf[128]; 299189251Ssam const char *atxt; 300189251Ssam struct in6_addr *addr = (struct in6_addr *) pos; 301189251Ssam atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf)); 302189251Ssam printf(" Value: %s\n", atxt ? atxt : "?"); 303189251Ssam } else 304189251Ssam printf(" Invalid IPv6 address length %d\n", len); 305189251Ssam break; 306189251Ssam#endif /* CONFIG_IPV6 */ 307189251Ssam 308189251Ssam case RADIUS_ATTR_HEXDUMP: 309189251Ssam case RADIUS_ATTR_UNDIST: 310189251Ssam printf(" Value:"); 311189251Ssam for (i = 0; i < len; i++) 312189251Ssam printf(" %02x", pos[i]); 313189251Ssam printf("\n"); 314189251Ssam break; 315189251Ssam 316189251Ssam case RADIUS_ATTR_INT32: 317189251Ssam if (len == 4) 318189251Ssam printf(" Value: %u\n", WPA_GET_BE32(pos)); 319189251Ssam else 320189251Ssam printf(" Invalid INT32 length %d\n", len); 321189251Ssam break; 322189251Ssam 323189251Ssam default: 324189251Ssam break; 325189251Ssam } 326189251Ssam} 327189251Ssam 328189251Ssam 329189251Ssamvoid radius_msg_dump(struct radius_msg *msg) 330189251Ssam{ 331189251Ssam size_t i; 332189251Ssam 333189251Ssam printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n", 334189251Ssam msg->hdr->code, radius_code_string(msg->hdr->code), 335252726Srpaulo msg->hdr->identifier, be_to_host16(msg->hdr->length)); 336189251Ssam 337189251Ssam for (i = 0; i < msg->attr_used; i++) { 338189251Ssam struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); 339189251Ssam radius_msg_dump_attr(attr); 340189251Ssam } 341189251Ssam} 342189251Ssam 343189251Ssam 344189251Ssamint radius_msg_finish(struct radius_msg *msg, const u8 *secret, 345189251Ssam size_t secret_len) 346189251Ssam{ 347189251Ssam if (secret) { 348189251Ssam u8 auth[MD5_MAC_LEN]; 349189251Ssam struct radius_attr_hdr *attr; 350189251Ssam 351189251Ssam os_memset(auth, 0, MD5_MAC_LEN); 352189251Ssam attr = radius_msg_add_attr(msg, 353189251Ssam RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 354189251Ssam auth, MD5_MAC_LEN); 355189251Ssam if (attr == NULL) { 356214734Srpaulo wpa_printf(MSG_WARNING, "RADIUS: Could not add " 357214734Srpaulo "Message-Authenticator"); 358189251Ssam return -1; 359189251Ssam } 360252726Srpaulo msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); 361214734Srpaulo hmac_md5(secret, secret_len, wpabuf_head(msg->buf), 362214734Srpaulo wpabuf_len(msg->buf), (u8 *) (attr + 1)); 363189251Ssam } else 364252726Srpaulo msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); 365189251Ssam 366214734Srpaulo if (wpabuf_len(msg->buf) > 0xffff) { 367214734Srpaulo wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", 368214734Srpaulo (unsigned long) wpabuf_len(msg->buf)); 369189251Ssam return -1; 370189251Ssam } 371189251Ssam return 0; 372189251Ssam} 373189251Ssam 374189251Ssam 375189251Ssamint radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, 376189251Ssam size_t secret_len, const u8 *req_authenticator) 377189251Ssam{ 378189251Ssam u8 auth[MD5_MAC_LEN]; 379189251Ssam struct radius_attr_hdr *attr; 380189251Ssam const u8 *addr[4]; 381189251Ssam size_t len[4]; 382189251Ssam 383189251Ssam os_memset(auth, 0, MD5_MAC_LEN); 384189251Ssam attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 385189251Ssam auth, MD5_MAC_LEN); 386189251Ssam if (attr == NULL) { 387189251Ssam printf("WARNING: Could not add Message-Authenticator\n"); 388189251Ssam return -1; 389189251Ssam } 390252726Srpaulo msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); 391189251Ssam os_memcpy(msg->hdr->authenticator, req_authenticator, 392189251Ssam sizeof(msg->hdr->authenticator)); 393214734Srpaulo hmac_md5(secret, secret_len, wpabuf_head(msg->buf), 394214734Srpaulo wpabuf_len(msg->buf), (u8 *) (attr + 1)); 395189251Ssam 396189251Ssam /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ 397189251Ssam addr[0] = (u8 *) msg->hdr; 398189251Ssam len[0] = 1 + 1 + 2; 399189251Ssam addr[1] = req_authenticator; 400189251Ssam len[1] = MD5_MAC_LEN; 401214734Srpaulo addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr); 402214734Srpaulo len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); 403189251Ssam addr[3] = secret; 404189251Ssam len[3] = secret_len; 405189251Ssam md5_vector(4, addr, len, msg->hdr->authenticator); 406189251Ssam 407214734Srpaulo if (wpabuf_len(msg->buf) > 0xffff) { 408214734Srpaulo wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", 409214734Srpaulo (unsigned long) wpabuf_len(msg->buf)); 410189251Ssam return -1; 411189251Ssam } 412189251Ssam return 0; 413189251Ssam} 414189251Ssam 415189251Ssam 416252726Srpauloint radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret, 417252726Srpaulo size_t secret_len, 418252726Srpaulo const struct radius_hdr *req_hdr) 419252726Srpaulo{ 420252726Srpaulo const u8 *addr[2]; 421252726Srpaulo size_t len[2]; 422252726Srpaulo u8 auth[MD5_MAC_LEN]; 423252726Srpaulo struct radius_attr_hdr *attr; 424252726Srpaulo 425252726Srpaulo os_memset(auth, 0, MD5_MAC_LEN); 426252726Srpaulo attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 427252726Srpaulo auth, MD5_MAC_LEN); 428252726Srpaulo if (attr == NULL) { 429252726Srpaulo wpa_printf(MSG_WARNING, "Could not add Message-Authenticator"); 430252726Srpaulo return -1; 431252726Srpaulo } 432252726Srpaulo 433252726Srpaulo msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); 434252726Srpaulo os_memcpy(msg->hdr->authenticator, req_hdr->authenticator, 16); 435252726Srpaulo hmac_md5(secret, secret_len, wpabuf_head(msg->buf), 436252726Srpaulo wpabuf_len(msg->buf), (u8 *) (attr + 1)); 437252726Srpaulo 438252726Srpaulo /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ 439252726Srpaulo addr[0] = wpabuf_head_u8(msg->buf); 440252726Srpaulo len[0] = wpabuf_len(msg->buf); 441252726Srpaulo addr[1] = secret; 442252726Srpaulo len[1] = secret_len; 443252726Srpaulo if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0) 444252726Srpaulo return -1; 445252726Srpaulo 446252726Srpaulo if (wpabuf_len(msg->buf) > 0xffff) { 447252726Srpaulo wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", 448252726Srpaulo (unsigned long) wpabuf_len(msg->buf)); 449252726Srpaulo return -1; 450252726Srpaulo } 451252726Srpaulo return 0; 452252726Srpaulo} 453252726Srpaulo 454252726Srpaulo 455189251Ssamvoid radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, 456189251Ssam size_t secret_len) 457189251Ssam{ 458189251Ssam const u8 *addr[2]; 459189251Ssam size_t len[2]; 460189251Ssam 461252726Srpaulo msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); 462189251Ssam os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN); 463214734Srpaulo addr[0] = wpabuf_head(msg->buf); 464214734Srpaulo len[0] = wpabuf_len(msg->buf); 465189251Ssam addr[1] = secret; 466189251Ssam len[1] = secret_len; 467189251Ssam md5_vector(2, addr, len, msg->hdr->authenticator); 468189251Ssam 469214734Srpaulo if (wpabuf_len(msg->buf) > 0xffff) { 470214734Srpaulo wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)", 471214734Srpaulo (unsigned long) wpabuf_len(msg->buf)); 472189251Ssam } 473189251Ssam} 474189251Ssam 475189251Ssam 476252726Srpauloint radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret, 477252726Srpaulo size_t secret_len) 478252726Srpaulo{ 479252726Srpaulo const u8 *addr[4]; 480252726Srpaulo size_t len[4]; 481252726Srpaulo u8 zero[MD5_MAC_LEN]; 482252726Srpaulo u8 hash[MD5_MAC_LEN]; 483252726Srpaulo 484252726Srpaulo os_memset(zero, 0, sizeof(zero)); 485252726Srpaulo addr[0] = (u8 *) msg->hdr; 486252726Srpaulo len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN; 487252726Srpaulo addr[1] = zero; 488252726Srpaulo len[1] = MD5_MAC_LEN; 489252726Srpaulo addr[2] = (u8 *) (msg->hdr + 1); 490252726Srpaulo len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); 491252726Srpaulo addr[3] = secret; 492252726Srpaulo len[3] = secret_len; 493252726Srpaulo md5_vector(4, addr, len, hash); 494252726Srpaulo return os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0; 495252726Srpaulo} 496252726Srpaulo 497252726Srpaulo 498252726Srpauloint radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, 499252726Srpaulo size_t secret_len) 500252726Srpaulo{ 501252726Srpaulo const u8 *addr[4]; 502252726Srpaulo size_t len[4]; 503252726Srpaulo u8 zero[MD5_MAC_LEN]; 504252726Srpaulo u8 hash[MD5_MAC_LEN]; 505252726Srpaulo u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN]; 506252726Srpaulo u8 orig_authenticator[16]; 507252726Srpaulo 508252726Srpaulo struct radius_attr_hdr *attr = NULL, *tmp; 509252726Srpaulo size_t i; 510252726Srpaulo 511252726Srpaulo os_memset(zero, 0, sizeof(zero)); 512252726Srpaulo addr[0] = (u8 *) msg->hdr; 513252726Srpaulo len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN; 514252726Srpaulo addr[1] = zero; 515252726Srpaulo len[1] = MD5_MAC_LEN; 516252726Srpaulo addr[2] = (u8 *) (msg->hdr + 1); 517252726Srpaulo len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); 518252726Srpaulo addr[3] = secret; 519252726Srpaulo len[3] = secret_len; 520252726Srpaulo md5_vector(4, addr, len, hash); 521252726Srpaulo if (os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0) 522252726Srpaulo return 1; 523252726Srpaulo 524252726Srpaulo for (i = 0; i < msg->attr_used; i++) { 525252726Srpaulo tmp = radius_get_attr_hdr(msg, i); 526252726Srpaulo if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { 527252726Srpaulo if (attr != NULL) { 528252726Srpaulo wpa_printf(MSG_WARNING, "Multiple " 529252726Srpaulo "Message-Authenticator attributes " 530252726Srpaulo "in RADIUS message"); 531252726Srpaulo return 1; 532252726Srpaulo } 533252726Srpaulo attr = tmp; 534252726Srpaulo } 535252726Srpaulo } 536252726Srpaulo 537252726Srpaulo if (attr == NULL) { 538252726Srpaulo /* Message-Authenticator is MAY; not required */ 539252726Srpaulo return 0; 540252726Srpaulo } 541252726Srpaulo 542252726Srpaulo os_memcpy(orig, attr + 1, MD5_MAC_LEN); 543252726Srpaulo os_memset(attr + 1, 0, MD5_MAC_LEN); 544252726Srpaulo os_memcpy(orig_authenticator, msg->hdr->authenticator, 545252726Srpaulo sizeof(orig_authenticator)); 546252726Srpaulo os_memset(msg->hdr->authenticator, 0, 547252726Srpaulo sizeof(msg->hdr->authenticator)); 548252726Srpaulo hmac_md5(secret, secret_len, wpabuf_head(msg->buf), 549252726Srpaulo wpabuf_len(msg->buf), auth); 550252726Srpaulo os_memcpy(attr + 1, orig, MD5_MAC_LEN); 551252726Srpaulo os_memcpy(msg->hdr->authenticator, orig_authenticator, 552252726Srpaulo sizeof(orig_authenticator)); 553252726Srpaulo 554252726Srpaulo return os_memcmp(orig, auth, MD5_MAC_LEN) != 0; 555252726Srpaulo} 556252726Srpaulo 557252726Srpaulo 558189251Ssamstatic int radius_msg_add_attr_to_array(struct radius_msg *msg, 559189251Ssam struct radius_attr_hdr *attr) 560189251Ssam{ 561189251Ssam if (msg->attr_used >= msg->attr_size) { 562189251Ssam size_t *nattr_pos; 563189251Ssam int nlen = msg->attr_size * 2; 564189251Ssam 565252726Srpaulo nattr_pos = os_realloc_array(msg->attr_pos, nlen, 566252726Srpaulo sizeof(*msg->attr_pos)); 567189251Ssam if (nattr_pos == NULL) 568189251Ssam return -1; 569189251Ssam 570189251Ssam msg->attr_pos = nattr_pos; 571189251Ssam msg->attr_size = nlen; 572189251Ssam } 573189251Ssam 574214734Srpaulo msg->attr_pos[msg->attr_used++] = 575214734Srpaulo (unsigned char *) attr - wpabuf_head_u8(msg->buf); 576189251Ssam 577189251Ssam return 0; 578189251Ssam} 579189251Ssam 580189251Ssam 581189251Ssamstruct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, 582189251Ssam const u8 *data, size_t data_len) 583189251Ssam{ 584189251Ssam size_t buf_needed; 585189251Ssam struct radius_attr_hdr *attr; 586189251Ssam 587189251Ssam if (data_len > RADIUS_MAX_ATTR_LEN) { 588189251Ssam printf("radius_msg_add_attr: too long attribute (%lu bytes)\n", 589189251Ssam (unsigned long) data_len); 590189251Ssam return NULL; 591189251Ssam } 592189251Ssam 593214734Srpaulo buf_needed = sizeof(*attr) + data_len; 594189251Ssam 595214734Srpaulo if (wpabuf_tailroom(msg->buf) < buf_needed) { 596189251Ssam /* allocate more space for message buffer */ 597214734Srpaulo if (wpabuf_resize(&msg->buf, buf_needed) < 0) 598189251Ssam return NULL; 599214734Srpaulo msg->hdr = wpabuf_mhead(msg->buf); 600189251Ssam } 601189251Ssam 602214734Srpaulo attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr)); 603189251Ssam attr->type = type; 604189251Ssam attr->length = sizeof(*attr) + data_len; 605214734Srpaulo wpabuf_put_data(msg->buf, data, data_len); 606189251Ssam 607189251Ssam if (radius_msg_add_attr_to_array(msg, attr)) 608189251Ssam return NULL; 609189251Ssam 610189251Ssam return attr; 611189251Ssam} 612189251Ssam 613189251Ssam 614214734Srpaulo/** 615214734Srpaulo * radius_msg_parse - Parse a RADIUS message 616214734Srpaulo * @data: RADIUS message to be parsed 617214734Srpaulo * @len: Length of data buffer in octets 618214734Srpaulo * Returns: Parsed RADIUS message or %NULL on failure 619214734Srpaulo * 620214734Srpaulo * This parses a RADIUS message and makes a copy of its data. The caller is 621214734Srpaulo * responsible for freeing the returned data with radius_msg_free(). 622214734Srpaulo */ 623214734Srpaulostruct radius_msg * radius_msg_parse(const u8 *data, size_t len) 624189251Ssam{ 625189251Ssam struct radius_msg *msg; 626189251Ssam struct radius_hdr *hdr; 627189251Ssam struct radius_attr_hdr *attr; 628189251Ssam size_t msg_len; 629189251Ssam unsigned char *pos, *end; 630189251Ssam 631189251Ssam if (data == NULL || len < sizeof(*hdr)) 632189251Ssam return NULL; 633189251Ssam 634189251Ssam hdr = (struct radius_hdr *) data; 635189251Ssam 636252726Srpaulo msg_len = be_to_host16(hdr->length); 637189251Ssam if (msg_len < sizeof(*hdr) || msg_len > len) { 638214734Srpaulo wpa_printf(MSG_INFO, "RADIUS: Invalid message length"); 639189251Ssam return NULL; 640189251Ssam } 641189251Ssam 642189251Ssam if (msg_len < len) { 643214734Srpaulo wpa_printf(MSG_DEBUG, "RADIUS: Ignored %lu extra bytes after " 644214734Srpaulo "RADIUS message", (unsigned long) len - msg_len); 645189251Ssam } 646189251Ssam 647214734Srpaulo msg = os_zalloc(sizeof(*msg)); 648189251Ssam if (msg == NULL) 649189251Ssam return NULL; 650189251Ssam 651214734Srpaulo msg->buf = wpabuf_alloc_copy(data, msg_len); 652214734Srpaulo if (msg->buf == NULL || radius_msg_initialize(msg)) { 653214734Srpaulo radius_msg_free(msg); 654189251Ssam return NULL; 655189251Ssam } 656214734Srpaulo msg->hdr = wpabuf_mhead(msg->buf); 657189251Ssam 658189251Ssam /* parse attributes */ 659214734Srpaulo pos = wpabuf_mhead_u8(msg->buf) + sizeof(struct radius_hdr); 660214734Srpaulo end = wpabuf_mhead_u8(msg->buf) + wpabuf_len(msg->buf); 661189251Ssam while (pos < end) { 662189251Ssam if ((size_t) (end - pos) < sizeof(*attr)) 663189251Ssam goto fail; 664189251Ssam 665189251Ssam attr = (struct radius_attr_hdr *) pos; 666189251Ssam 667189251Ssam if (pos + attr->length > end || attr->length < sizeof(*attr)) 668189251Ssam goto fail; 669189251Ssam 670189251Ssam /* TODO: check that attr->length is suitable for attr->type */ 671189251Ssam 672189251Ssam if (radius_msg_add_attr_to_array(msg, attr)) 673189251Ssam goto fail; 674189251Ssam 675189251Ssam pos += attr->length; 676189251Ssam } 677189251Ssam 678189251Ssam return msg; 679189251Ssam 680189251Ssam fail: 681189251Ssam radius_msg_free(msg); 682189251Ssam return NULL; 683189251Ssam} 684189251Ssam 685189251Ssam 686189251Ssamint radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len) 687189251Ssam{ 688189251Ssam const u8 *pos = data; 689189251Ssam size_t left = data_len; 690189251Ssam 691189251Ssam while (left > 0) { 692189251Ssam int len; 693189251Ssam if (left > RADIUS_MAX_ATTR_LEN) 694189251Ssam len = RADIUS_MAX_ATTR_LEN; 695189251Ssam else 696189251Ssam len = left; 697189251Ssam 698189251Ssam if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE, 699189251Ssam pos, len)) 700189251Ssam return 0; 701189251Ssam 702189251Ssam pos += len; 703189251Ssam left -= len; 704189251Ssam } 705189251Ssam 706189251Ssam return 1; 707189251Ssam} 708189251Ssam 709189251Ssam 710252726Srpaulostruct wpabuf * radius_msg_get_eap(struct radius_msg *msg) 711189251Ssam{ 712252726Srpaulo struct wpabuf *eap; 713189251Ssam size_t len, i; 714189251Ssam struct radius_attr_hdr *attr; 715189251Ssam 716189251Ssam if (msg == NULL) 717189251Ssam return NULL; 718189251Ssam 719189251Ssam len = 0; 720189251Ssam for (i = 0; i < msg->attr_used; i++) { 721189251Ssam attr = radius_get_attr_hdr(msg, i); 722252726Srpaulo if (attr->type == RADIUS_ATTR_EAP_MESSAGE && 723252726Srpaulo attr->length > sizeof(struct radius_attr_hdr)) 724189251Ssam len += attr->length - sizeof(struct radius_attr_hdr); 725189251Ssam } 726189251Ssam 727189251Ssam if (len == 0) 728189251Ssam return NULL; 729189251Ssam 730252726Srpaulo eap = wpabuf_alloc(len); 731189251Ssam if (eap == NULL) 732189251Ssam return NULL; 733189251Ssam 734189251Ssam for (i = 0; i < msg->attr_used; i++) { 735189251Ssam attr = radius_get_attr_hdr(msg, i); 736252726Srpaulo if (attr->type == RADIUS_ATTR_EAP_MESSAGE && 737252726Srpaulo attr->length > sizeof(struct radius_attr_hdr)) { 738189251Ssam int flen = attr->length - sizeof(*attr); 739252726Srpaulo wpabuf_put_data(eap, attr + 1, flen); 740189251Ssam } 741189251Ssam } 742189251Ssam 743189251Ssam return eap; 744189251Ssam} 745189251Ssam 746189251Ssam 747189251Ssamint radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, 748189251Ssam size_t secret_len, const u8 *req_auth) 749189251Ssam{ 750189251Ssam u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN]; 751189251Ssam u8 orig_authenticator[16]; 752189251Ssam struct radius_attr_hdr *attr = NULL, *tmp; 753189251Ssam size_t i; 754189251Ssam 755189251Ssam for (i = 0; i < msg->attr_used; i++) { 756189251Ssam tmp = radius_get_attr_hdr(msg, i); 757189251Ssam if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { 758189251Ssam if (attr != NULL) { 759189251Ssam printf("Multiple Message-Authenticator " 760189251Ssam "attributes in RADIUS message\n"); 761189251Ssam return 1; 762189251Ssam } 763189251Ssam attr = tmp; 764189251Ssam } 765189251Ssam } 766189251Ssam 767189251Ssam if (attr == NULL) { 768189251Ssam printf("No Message-Authenticator attribute found\n"); 769189251Ssam return 1; 770189251Ssam } 771189251Ssam 772189251Ssam os_memcpy(orig, attr + 1, MD5_MAC_LEN); 773189251Ssam os_memset(attr + 1, 0, MD5_MAC_LEN); 774189251Ssam if (req_auth) { 775189251Ssam os_memcpy(orig_authenticator, msg->hdr->authenticator, 776189251Ssam sizeof(orig_authenticator)); 777189251Ssam os_memcpy(msg->hdr->authenticator, req_auth, 778189251Ssam sizeof(msg->hdr->authenticator)); 779189251Ssam } 780214734Srpaulo hmac_md5(secret, secret_len, wpabuf_head(msg->buf), 781214734Srpaulo wpabuf_len(msg->buf), auth); 782189251Ssam os_memcpy(attr + 1, orig, MD5_MAC_LEN); 783189251Ssam if (req_auth) { 784189251Ssam os_memcpy(msg->hdr->authenticator, orig_authenticator, 785189251Ssam sizeof(orig_authenticator)); 786189251Ssam } 787189251Ssam 788189251Ssam if (os_memcmp(orig, auth, MD5_MAC_LEN) != 0) { 789189251Ssam printf("Invalid Message-Authenticator!\n"); 790189251Ssam return 1; 791189251Ssam } 792189251Ssam 793189251Ssam return 0; 794189251Ssam} 795189251Ssam 796189251Ssam 797189251Ssamint radius_msg_verify(struct radius_msg *msg, const u8 *secret, 798189251Ssam size_t secret_len, struct radius_msg *sent_msg, int auth) 799189251Ssam{ 800189251Ssam const u8 *addr[4]; 801189251Ssam size_t len[4]; 802189251Ssam u8 hash[MD5_MAC_LEN]; 803189251Ssam 804189251Ssam if (sent_msg == NULL) { 805189251Ssam printf("No matching Access-Request message found\n"); 806189251Ssam return 1; 807189251Ssam } 808189251Ssam 809189251Ssam if (auth && 810189251Ssam radius_msg_verify_msg_auth(msg, secret, secret_len, 811189251Ssam sent_msg->hdr->authenticator)) { 812189251Ssam return 1; 813189251Ssam } 814189251Ssam 815189251Ssam /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ 816189251Ssam addr[0] = (u8 *) msg->hdr; 817189251Ssam len[0] = 1 + 1 + 2; 818189251Ssam addr[1] = sent_msg->hdr->authenticator; 819189251Ssam len[1] = MD5_MAC_LEN; 820214734Srpaulo addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr); 821214734Srpaulo len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); 822189251Ssam addr[3] = secret; 823189251Ssam len[3] = secret_len; 824189251Ssam md5_vector(4, addr, len, hash); 825189251Ssam if (os_memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) { 826189251Ssam printf("Response Authenticator invalid!\n"); 827189251Ssam return 1; 828189251Ssam } 829189251Ssam 830189251Ssam return 0; 831189251Ssam} 832189251Ssam 833189251Ssam 834189251Ssamint radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, 835189251Ssam u8 type) 836189251Ssam{ 837189251Ssam struct radius_attr_hdr *attr; 838189251Ssam size_t i; 839189251Ssam int count = 0; 840189251Ssam 841189251Ssam for (i = 0; i < src->attr_used; i++) { 842189251Ssam attr = radius_get_attr_hdr(src, i); 843252726Srpaulo if (attr->type == type && attr->length >= sizeof(*attr)) { 844189251Ssam if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1), 845189251Ssam attr->length - sizeof(*attr))) 846189251Ssam return -1; 847189251Ssam count++; 848189251Ssam } 849189251Ssam } 850189251Ssam 851189251Ssam return count; 852189251Ssam} 853189251Ssam 854189251Ssam 855189251Ssam/* Create Request Authenticator. The value should be unique over the lifetime 856189251Ssam * of the shared secret between authenticator and authentication server. 857189251Ssam * Use one-way MD5 hash calculated from current timestamp and some data given 858189251Ssam * by the caller. */ 859189251Ssamvoid radius_msg_make_authenticator(struct radius_msg *msg, 860189251Ssam const u8 *data, size_t len) 861189251Ssam{ 862189251Ssam struct os_time tv; 863189251Ssam long int l; 864189251Ssam const u8 *addr[3]; 865189251Ssam size_t elen[3]; 866189251Ssam 867189251Ssam os_get_time(&tv); 868189251Ssam l = os_random(); 869189251Ssam addr[0] = (u8 *) &tv; 870189251Ssam elen[0] = sizeof(tv); 871189251Ssam addr[1] = data; 872189251Ssam elen[1] = len; 873189251Ssam addr[2] = (u8 *) &l; 874189251Ssam elen[2] = sizeof(l); 875189251Ssam md5_vector(3, addr, elen, msg->hdr->authenticator); 876189251Ssam} 877189251Ssam 878189251Ssam 879189251Ssam/* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message. 880189251Ssam * Returns the Attribute payload and sets alen to indicate the length of the 881189251Ssam * payload if a vendor attribute with subtype is found, otherwise returns NULL. 882189251Ssam * The returned payload is allocated with os_malloc() and caller must free it 883189251Ssam * by calling os_free(). 884189251Ssam */ 885189251Ssamstatic u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor, 886189251Ssam u8 subtype, size_t *alen) 887189251Ssam{ 888189251Ssam u8 *data, *pos; 889189251Ssam size_t i, len; 890189251Ssam 891189251Ssam if (msg == NULL) 892189251Ssam return NULL; 893189251Ssam 894189251Ssam for (i = 0; i < msg->attr_used; i++) { 895189251Ssam struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); 896189251Ssam size_t left; 897189251Ssam u32 vendor_id; 898189251Ssam struct radius_attr_vendor *vhdr; 899189251Ssam 900252726Srpaulo if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC || 901252726Srpaulo attr->length < sizeof(*attr)) 902189251Ssam continue; 903189251Ssam 904189251Ssam left = attr->length - sizeof(*attr); 905189251Ssam if (left < 4) 906189251Ssam continue; 907189251Ssam 908189251Ssam pos = (u8 *) (attr + 1); 909189251Ssam 910189251Ssam os_memcpy(&vendor_id, pos, 4); 911189251Ssam pos += 4; 912189251Ssam left -= 4; 913189251Ssam 914189251Ssam if (ntohl(vendor_id) != vendor) 915189251Ssam continue; 916189251Ssam 917189251Ssam while (left >= sizeof(*vhdr)) { 918189251Ssam vhdr = (struct radius_attr_vendor *) pos; 919189251Ssam if (vhdr->vendor_length > left || 920189251Ssam vhdr->vendor_length < sizeof(*vhdr)) { 921189251Ssam left = 0; 922189251Ssam break; 923189251Ssam } 924189251Ssam if (vhdr->vendor_type != subtype) { 925189251Ssam pos += vhdr->vendor_length; 926189251Ssam left -= vhdr->vendor_length; 927189251Ssam continue; 928189251Ssam } 929189251Ssam 930189251Ssam len = vhdr->vendor_length - sizeof(*vhdr); 931189251Ssam data = os_malloc(len); 932189251Ssam if (data == NULL) 933189251Ssam return NULL; 934189251Ssam os_memcpy(data, pos + sizeof(*vhdr), len); 935189251Ssam if (alen) 936189251Ssam *alen = len; 937189251Ssam return data; 938189251Ssam } 939189251Ssam } 940189251Ssam 941189251Ssam return NULL; 942189251Ssam} 943189251Ssam 944189251Ssam 945189251Ssamstatic u8 * decrypt_ms_key(const u8 *key, size_t len, 946189251Ssam const u8 *req_authenticator, 947189251Ssam const u8 *secret, size_t secret_len, size_t *reslen) 948189251Ssam{ 949189251Ssam u8 *plain, *ppos, *res; 950189251Ssam const u8 *pos; 951189251Ssam size_t left, plen; 952189251Ssam u8 hash[MD5_MAC_LEN]; 953189251Ssam int i, first = 1; 954189251Ssam const u8 *addr[3]; 955189251Ssam size_t elen[3]; 956189251Ssam 957189251Ssam /* key: 16-bit salt followed by encrypted key info */ 958189251Ssam 959189251Ssam if (len < 2 + 16) 960189251Ssam return NULL; 961189251Ssam 962189251Ssam pos = key + 2; 963189251Ssam left = len - 2; 964189251Ssam if (left % 16) { 965189251Ssam printf("Invalid ms key len %lu\n", (unsigned long) left); 966189251Ssam return NULL; 967189251Ssam } 968189251Ssam 969189251Ssam plen = left; 970189251Ssam ppos = plain = os_malloc(plen); 971189251Ssam if (plain == NULL) 972189251Ssam return NULL; 973189251Ssam plain[0] = 0; 974189251Ssam 975189251Ssam while (left > 0) { 976189251Ssam /* b(1) = MD5(Secret + Request-Authenticator + Salt) 977189251Ssam * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ 978189251Ssam 979189251Ssam addr[0] = secret; 980189251Ssam elen[0] = secret_len; 981189251Ssam if (first) { 982189251Ssam addr[1] = req_authenticator; 983189251Ssam elen[1] = MD5_MAC_LEN; 984189251Ssam addr[2] = key; 985189251Ssam elen[2] = 2; /* Salt */ 986189251Ssam } else { 987189251Ssam addr[1] = pos - MD5_MAC_LEN; 988189251Ssam elen[1] = MD5_MAC_LEN; 989189251Ssam } 990189251Ssam md5_vector(first ? 3 : 2, addr, elen, hash); 991189251Ssam first = 0; 992189251Ssam 993189251Ssam for (i = 0; i < MD5_MAC_LEN; i++) 994189251Ssam *ppos++ = *pos++ ^ hash[i]; 995189251Ssam left -= MD5_MAC_LEN; 996189251Ssam } 997189251Ssam 998189251Ssam if (plain[0] == 0 || plain[0] > plen - 1) { 999189251Ssam printf("Failed to decrypt MPPE key\n"); 1000189251Ssam os_free(plain); 1001189251Ssam return NULL; 1002189251Ssam } 1003189251Ssam 1004189251Ssam res = os_malloc(plain[0]); 1005189251Ssam if (res == NULL) { 1006189251Ssam os_free(plain); 1007189251Ssam return NULL; 1008189251Ssam } 1009189251Ssam os_memcpy(res, plain + 1, plain[0]); 1010189251Ssam if (reslen) 1011189251Ssam *reslen = plain[0]; 1012189251Ssam os_free(plain); 1013189251Ssam return res; 1014189251Ssam} 1015189251Ssam 1016189251Ssam 1017189251Ssamstatic void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt, 1018189251Ssam const u8 *req_authenticator, 1019189251Ssam const u8 *secret, size_t secret_len, 1020189251Ssam u8 *ebuf, size_t *elen) 1021189251Ssam{ 1022189251Ssam int i, len, first = 1; 1023189251Ssam u8 hash[MD5_MAC_LEN], saltbuf[2], *pos; 1024189251Ssam const u8 *addr[3]; 1025189251Ssam size_t _len[3]; 1026189251Ssam 1027189251Ssam WPA_PUT_BE16(saltbuf, salt); 1028189251Ssam 1029189251Ssam len = 1 + key_len; 1030189251Ssam if (len & 0x0f) { 1031189251Ssam len = (len & 0xf0) + 16; 1032189251Ssam } 1033189251Ssam os_memset(ebuf, 0, len); 1034189251Ssam ebuf[0] = key_len; 1035189251Ssam os_memcpy(ebuf + 1, key, key_len); 1036189251Ssam 1037189251Ssam *elen = len; 1038189251Ssam 1039189251Ssam pos = ebuf; 1040189251Ssam while (len > 0) { 1041189251Ssam /* b(1) = MD5(Secret + Request-Authenticator + Salt) 1042189251Ssam * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ 1043189251Ssam addr[0] = secret; 1044189251Ssam _len[0] = secret_len; 1045189251Ssam if (first) { 1046189251Ssam addr[1] = req_authenticator; 1047189251Ssam _len[1] = MD5_MAC_LEN; 1048189251Ssam addr[2] = saltbuf; 1049189251Ssam _len[2] = sizeof(saltbuf); 1050189251Ssam } else { 1051189251Ssam addr[1] = pos - MD5_MAC_LEN; 1052189251Ssam _len[1] = MD5_MAC_LEN; 1053189251Ssam } 1054189251Ssam md5_vector(first ? 3 : 2, addr, _len, hash); 1055189251Ssam first = 0; 1056189251Ssam 1057189251Ssam for (i = 0; i < MD5_MAC_LEN; i++) 1058189251Ssam *pos++ ^= hash[i]; 1059189251Ssam 1060189251Ssam len -= MD5_MAC_LEN; 1061189251Ssam } 1062189251Ssam} 1063189251Ssam 1064189251Ssam 1065189251Ssamstruct radius_ms_mppe_keys * 1066189251Ssamradius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, 1067189251Ssam const u8 *secret, size_t secret_len) 1068189251Ssam{ 1069189251Ssam u8 *key; 1070189251Ssam size_t keylen; 1071189251Ssam struct radius_ms_mppe_keys *keys; 1072189251Ssam 1073189251Ssam if (msg == NULL || sent_msg == NULL) 1074189251Ssam return NULL; 1075189251Ssam 1076189251Ssam keys = os_zalloc(sizeof(*keys)); 1077189251Ssam if (keys == NULL) 1078189251Ssam return NULL; 1079189251Ssam 1080189251Ssam key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT, 1081189251Ssam RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY, 1082189251Ssam &keylen); 1083189251Ssam if (key) { 1084189251Ssam keys->send = decrypt_ms_key(key, keylen, 1085189251Ssam sent_msg->hdr->authenticator, 1086189251Ssam secret, secret_len, 1087189251Ssam &keys->send_len); 1088189251Ssam os_free(key); 1089189251Ssam } 1090189251Ssam 1091189251Ssam key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT, 1092189251Ssam RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY, 1093189251Ssam &keylen); 1094189251Ssam if (key) { 1095189251Ssam keys->recv = decrypt_ms_key(key, keylen, 1096189251Ssam sent_msg->hdr->authenticator, 1097189251Ssam secret, secret_len, 1098189251Ssam &keys->recv_len); 1099189251Ssam os_free(key); 1100189251Ssam } 1101189251Ssam 1102189251Ssam return keys; 1103189251Ssam} 1104189251Ssam 1105189251Ssam 1106189251Ssamstruct radius_ms_mppe_keys * 1107189251Ssamradius_msg_get_cisco_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_CISCO, 1122189251Ssam RADIUS_CISCO_AV_PAIR, &keylen); 1123189251Ssam if (key && keylen == 51 && 1124189251Ssam os_memcmp(key, "leap:session-key=", 17) == 0) { 1125189251Ssam keys->recv = decrypt_ms_key(key + 17, keylen - 17, 1126189251Ssam sent_msg->hdr->authenticator, 1127189251Ssam secret, secret_len, 1128189251Ssam &keys->recv_len); 1129189251Ssam } 1130189251Ssam os_free(key); 1131189251Ssam 1132189251Ssam return keys; 1133189251Ssam} 1134189251Ssam 1135189251Ssam 1136189251Ssamint radius_msg_add_mppe_keys(struct radius_msg *msg, 1137189251Ssam const u8 *req_authenticator, 1138189251Ssam const u8 *secret, size_t secret_len, 1139189251Ssam const u8 *send_key, size_t send_key_len, 1140189251Ssam const u8 *recv_key, size_t recv_key_len) 1141189251Ssam{ 1142189251Ssam struct radius_attr_hdr *attr; 1143189251Ssam u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT); 1144189251Ssam u8 *buf; 1145189251Ssam struct radius_attr_vendor *vhdr; 1146189251Ssam u8 *pos; 1147189251Ssam size_t elen; 1148189251Ssam int hlen; 1149189251Ssam u16 salt; 1150189251Ssam 1151189251Ssam hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2; 1152189251Ssam 1153189251Ssam /* MS-MPPE-Send-Key */ 1154189251Ssam buf = os_malloc(hlen + send_key_len + 16); 1155189251Ssam if (buf == NULL) { 1156189251Ssam return 0; 1157189251Ssam } 1158189251Ssam pos = buf; 1159189251Ssam os_memcpy(pos, &vendor_id, sizeof(vendor_id)); 1160189251Ssam pos += sizeof(vendor_id); 1161189251Ssam vhdr = (struct radius_attr_vendor *) pos; 1162189251Ssam vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY; 1163189251Ssam pos = (u8 *) (vhdr + 1); 1164189251Ssam salt = os_random() | 0x8000; 1165189251Ssam WPA_PUT_BE16(pos, salt); 1166189251Ssam pos += 2; 1167189251Ssam encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret, 1168189251Ssam secret_len, pos, &elen); 1169189251Ssam vhdr->vendor_length = hlen + elen - sizeof(vendor_id); 1170189251Ssam 1171189251Ssam attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, 1172189251Ssam buf, hlen + elen); 1173189251Ssam os_free(buf); 1174189251Ssam if (attr == NULL) { 1175189251Ssam return 0; 1176189251Ssam } 1177189251Ssam 1178189251Ssam /* MS-MPPE-Recv-Key */ 1179189251Ssam buf = os_malloc(hlen + send_key_len + 16); 1180189251Ssam if (buf == NULL) { 1181189251Ssam return 0; 1182189251Ssam } 1183189251Ssam pos = buf; 1184189251Ssam os_memcpy(pos, &vendor_id, sizeof(vendor_id)); 1185189251Ssam pos += sizeof(vendor_id); 1186189251Ssam vhdr = (struct radius_attr_vendor *) pos; 1187189251Ssam vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY; 1188189251Ssam pos = (u8 *) (vhdr + 1); 1189189251Ssam salt ^= 1; 1190189251Ssam WPA_PUT_BE16(pos, salt); 1191189251Ssam pos += 2; 1192189251Ssam encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret, 1193189251Ssam secret_len, pos, &elen); 1194189251Ssam vhdr->vendor_length = hlen + elen - sizeof(vendor_id); 1195189251Ssam 1196189251Ssam attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, 1197189251Ssam buf, hlen + elen); 1198189251Ssam os_free(buf); 1199189251Ssam if (attr == NULL) { 1200189251Ssam return 0; 1201189251Ssam } 1202189251Ssam 1203189251Ssam return 1; 1204189251Ssam} 1205189251Ssam 1206189251Ssam 1207189251Ssam/* Add User-Password attribute to a RADIUS message and encrypt it as specified 1208189251Ssam * in RFC 2865, Chap. 5.2 */ 1209189251Ssamstruct radius_attr_hdr * 1210189251Ssamradius_msg_add_attr_user_password(struct radius_msg *msg, 1211189251Ssam const u8 *data, size_t data_len, 1212189251Ssam const u8 *secret, size_t secret_len) 1213189251Ssam{ 1214189251Ssam u8 buf[128]; 1215252726Srpaulo size_t padlen, i, buf_len, pos; 1216189251Ssam const u8 *addr[2]; 1217189251Ssam size_t len[2]; 1218189251Ssam u8 hash[16]; 1219189251Ssam 1220189251Ssam if (data_len > 128) 1221189251Ssam return NULL; 1222189251Ssam 1223189251Ssam os_memcpy(buf, data, data_len); 1224189251Ssam buf_len = data_len; 1225189251Ssam 1226189251Ssam padlen = data_len % 16; 1227252726Srpaulo if (padlen && data_len < sizeof(buf)) { 1228189251Ssam padlen = 16 - padlen; 1229189251Ssam os_memset(buf + data_len, 0, padlen); 1230189251Ssam buf_len += padlen; 1231189251Ssam } 1232189251Ssam 1233189251Ssam addr[0] = secret; 1234189251Ssam len[0] = secret_len; 1235189251Ssam addr[1] = msg->hdr->authenticator; 1236189251Ssam len[1] = 16; 1237189251Ssam md5_vector(2, addr, len, hash); 1238189251Ssam 1239189251Ssam for (i = 0; i < 16; i++) 1240189251Ssam buf[i] ^= hash[i]; 1241189251Ssam pos = 16; 1242189251Ssam 1243189251Ssam while (pos < buf_len) { 1244189251Ssam addr[0] = secret; 1245189251Ssam len[0] = secret_len; 1246189251Ssam addr[1] = &buf[pos - 16]; 1247189251Ssam len[1] = 16; 1248189251Ssam md5_vector(2, addr, len, hash); 1249189251Ssam 1250189251Ssam for (i = 0; i < 16; i++) 1251189251Ssam buf[pos + i] ^= hash[i]; 1252189251Ssam 1253189251Ssam pos += 16; 1254189251Ssam } 1255189251Ssam 1256189251Ssam return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD, 1257189251Ssam buf, buf_len); 1258189251Ssam} 1259189251Ssam 1260189251Ssam 1261189251Ssamint radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len) 1262189251Ssam{ 1263189251Ssam struct radius_attr_hdr *attr = NULL, *tmp; 1264189251Ssam size_t i, dlen; 1265189251Ssam 1266189251Ssam for (i = 0; i < msg->attr_used; i++) { 1267189251Ssam tmp = radius_get_attr_hdr(msg, i); 1268189251Ssam if (tmp->type == type) { 1269189251Ssam attr = tmp; 1270189251Ssam break; 1271189251Ssam } 1272189251Ssam } 1273189251Ssam 1274252726Srpaulo if (!attr || attr->length < sizeof(*attr)) 1275189251Ssam return -1; 1276189251Ssam 1277189251Ssam dlen = attr->length - sizeof(*attr); 1278189251Ssam if (buf) 1279189251Ssam os_memcpy(buf, (attr + 1), dlen > len ? len : dlen); 1280189251Ssam return dlen; 1281189251Ssam} 1282189251Ssam 1283189251Ssam 1284189251Ssamint radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, 1285189251Ssam size_t *len, const u8 *start) 1286189251Ssam{ 1287189251Ssam size_t i; 1288189251Ssam struct radius_attr_hdr *attr = NULL, *tmp; 1289189251Ssam 1290189251Ssam for (i = 0; i < msg->attr_used; i++) { 1291189251Ssam tmp = radius_get_attr_hdr(msg, i); 1292189251Ssam if (tmp->type == type && 1293189251Ssam (start == NULL || (u8 *) tmp > start)) { 1294189251Ssam attr = tmp; 1295189251Ssam break; 1296189251Ssam } 1297189251Ssam } 1298189251Ssam 1299252726Srpaulo if (!attr || attr->length < sizeof(*attr)) 1300189251Ssam return -1; 1301189251Ssam 1302189251Ssam *buf = (u8 *) (attr + 1); 1303189251Ssam *len = attr->length - sizeof(*attr); 1304189251Ssam return 0; 1305189251Ssam} 1306189251Ssam 1307189251Ssam 1308189251Ssamint radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len) 1309189251Ssam{ 1310189251Ssam size_t i; 1311189251Ssam int count; 1312189251Ssam 1313189251Ssam for (count = 0, i = 0; i < msg->attr_used; i++) { 1314189251Ssam struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); 1315189251Ssam if (attr->type == type && 1316189251Ssam attr->length >= sizeof(struct radius_attr_hdr) + min_len) 1317189251Ssam count++; 1318189251Ssam } 1319189251Ssam 1320189251Ssam return count; 1321189251Ssam} 1322189251Ssam 1323189251Ssam 1324189251Ssamstruct radius_tunnel_attrs { 1325189251Ssam int tag_used; 1326189251Ssam int type; /* Tunnel-Type */ 1327189251Ssam int medium_type; /* Tunnel-Medium-Type */ 1328189251Ssam int vlanid; 1329189251Ssam}; 1330189251Ssam 1331189251Ssam 1332189251Ssam/** 1333189251Ssam * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information 1334189251Ssam * @msg: RADIUS message 1335189251Ssam * Returns: VLAN ID for the first tunnel configuration of -1 if none is found 1336189251Ssam */ 1337189251Ssamint radius_msg_get_vlanid(struct radius_msg *msg) 1338189251Ssam{ 1339189251Ssam struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun; 1340189251Ssam size_t i; 1341189251Ssam struct radius_attr_hdr *attr = NULL; 1342189251Ssam const u8 *data; 1343189251Ssam char buf[10]; 1344189251Ssam size_t dlen; 1345189251Ssam 1346189251Ssam os_memset(&tunnel, 0, sizeof(tunnel)); 1347189251Ssam 1348189251Ssam for (i = 0; i < msg->attr_used; i++) { 1349189251Ssam attr = radius_get_attr_hdr(msg, i); 1350252726Srpaulo if (attr->length < sizeof(*attr)) 1351252726Srpaulo return -1; 1352189251Ssam data = (const u8 *) (attr + 1); 1353189251Ssam dlen = attr->length - sizeof(*attr); 1354189251Ssam if (attr->length < 3) 1355189251Ssam continue; 1356189251Ssam if (data[0] >= RADIUS_TUNNEL_TAGS) 1357189251Ssam tun = &tunnel[0]; 1358189251Ssam else 1359189251Ssam tun = &tunnel[data[0]]; 1360189251Ssam 1361189251Ssam switch (attr->type) { 1362189251Ssam case RADIUS_ATTR_TUNNEL_TYPE: 1363189251Ssam if (attr->length != 6) 1364189251Ssam break; 1365189251Ssam tun->tag_used++; 1366189251Ssam tun->type = WPA_GET_BE24(data + 1); 1367189251Ssam break; 1368189251Ssam case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE: 1369189251Ssam if (attr->length != 6) 1370189251Ssam break; 1371189251Ssam tun->tag_used++; 1372189251Ssam tun->medium_type = WPA_GET_BE24(data + 1); 1373189251Ssam break; 1374189251Ssam case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID: 1375189251Ssam if (data[0] < RADIUS_TUNNEL_TAGS) { 1376189251Ssam data++; 1377189251Ssam dlen--; 1378189251Ssam } 1379189251Ssam if (dlen >= sizeof(buf)) 1380189251Ssam break; 1381189251Ssam os_memcpy(buf, data, dlen); 1382189251Ssam buf[dlen] = '\0'; 1383189251Ssam tun->tag_used++; 1384189251Ssam tun->vlanid = atoi(buf); 1385189251Ssam break; 1386189251Ssam } 1387189251Ssam } 1388189251Ssam 1389189251Ssam for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) { 1390189251Ssam tun = &tunnel[i]; 1391189251Ssam if (tun->tag_used && 1392189251Ssam tun->type == RADIUS_TUNNEL_TYPE_VLAN && 1393189251Ssam tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 && 1394189251Ssam tun->vlanid > 0) 1395189251Ssam return tun->vlanid; 1396189251Ssam } 1397189251Ssam 1398189251Ssam return -1; 1399189251Ssam} 1400214734Srpaulo 1401214734Srpaulo 1402252726Srpaulo/** 1403252726Srpaulo * radius_msg_get_tunnel_password - Parse RADIUS attribute Tunnel-Password 1404252726Srpaulo * @msg: Received RADIUS message 1405252726Srpaulo * @keylen: Length of returned password 1406252726Srpaulo * @secret: RADIUS shared secret 1407252726Srpaulo * @secret_len: Length of secret 1408252726Srpaulo * @sent_msg: Sent RADIUS message 1409252726Srpaulo * @n: Number of password attribute to return (starting with 0) 1410252726Srpaulo * Returns: Pointer to n-th password (free with os_free) or %NULL 1411252726Srpaulo */ 1412252726Srpaulochar * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen, 1413252726Srpaulo const u8 *secret, size_t secret_len, 1414252726Srpaulo struct radius_msg *sent_msg, size_t n) 1415252726Srpaulo{ 1416252726Srpaulo u8 *buf = NULL; 1417252726Srpaulo size_t buflen; 1418252726Srpaulo const u8 *salt; 1419252726Srpaulo u8 *str; 1420252726Srpaulo const u8 *addr[3]; 1421252726Srpaulo size_t len[3]; 1422252726Srpaulo u8 hash[16]; 1423252726Srpaulo u8 *pos; 1424252726Srpaulo size_t i, j = 0; 1425252726Srpaulo struct radius_attr_hdr *attr; 1426252726Srpaulo const u8 *data; 1427252726Srpaulo size_t dlen; 1428252726Srpaulo const u8 *fdata = NULL; /* points to found item */ 1429252726Srpaulo size_t fdlen = -1; 1430252726Srpaulo char *ret = NULL; 1431252726Srpaulo 1432252726Srpaulo /* find n-th valid Tunnel-Password attribute */ 1433252726Srpaulo for (i = 0; i < msg->attr_used; i++) { 1434252726Srpaulo attr = radius_get_attr_hdr(msg, i); 1435252726Srpaulo if (attr == NULL || 1436252726Srpaulo attr->type != RADIUS_ATTR_TUNNEL_PASSWORD) { 1437252726Srpaulo continue; 1438252726Srpaulo } 1439252726Srpaulo if (attr->length <= 5) 1440252726Srpaulo continue; 1441252726Srpaulo data = (const u8 *) (attr + 1); 1442252726Srpaulo dlen = attr->length - sizeof(*attr); 1443252726Srpaulo if (dlen <= 3 || dlen % 16 != 3) 1444252726Srpaulo continue; 1445252726Srpaulo j++; 1446252726Srpaulo if (j <= n) 1447252726Srpaulo continue; 1448252726Srpaulo 1449252726Srpaulo fdata = data; 1450252726Srpaulo fdlen = dlen; 1451252726Srpaulo break; 1452252726Srpaulo } 1453252726Srpaulo if (fdata == NULL) 1454252726Srpaulo goto out; 1455252726Srpaulo 1456252726Srpaulo /* alloc writable memory for decryption */ 1457252726Srpaulo buf = os_malloc(fdlen); 1458252726Srpaulo if (buf == NULL) 1459252726Srpaulo goto out; 1460252726Srpaulo os_memcpy(buf, fdata, fdlen); 1461252726Srpaulo buflen = fdlen; 1462252726Srpaulo 1463252726Srpaulo /* init pointers */ 1464252726Srpaulo salt = buf + 1; 1465252726Srpaulo str = buf + 3; 1466252726Srpaulo 1467252726Srpaulo /* decrypt blocks */ 1468252726Srpaulo pos = buf + buflen - 16; /* last block */ 1469252726Srpaulo while (pos >= str + 16) { /* all but the first block */ 1470252726Srpaulo addr[0] = secret; 1471252726Srpaulo len[0] = secret_len; 1472252726Srpaulo addr[1] = pos - 16; 1473252726Srpaulo len[1] = 16; 1474252726Srpaulo md5_vector(2, addr, len, hash); 1475252726Srpaulo 1476252726Srpaulo for (i = 0; i < 16; i++) 1477252726Srpaulo pos[i] ^= hash[i]; 1478252726Srpaulo 1479252726Srpaulo pos -= 16; 1480252726Srpaulo } 1481252726Srpaulo 1482252726Srpaulo /* decrypt first block */ 1483252726Srpaulo if (str != pos) 1484252726Srpaulo goto out; 1485252726Srpaulo addr[0] = secret; 1486252726Srpaulo len[0] = secret_len; 1487252726Srpaulo addr[1] = sent_msg->hdr->authenticator; 1488252726Srpaulo len[1] = 16; 1489252726Srpaulo addr[2] = salt; 1490252726Srpaulo len[2] = 2; 1491252726Srpaulo md5_vector(3, addr, len, hash); 1492252726Srpaulo 1493252726Srpaulo for (i = 0; i < 16; i++) 1494252726Srpaulo pos[i] ^= hash[i]; 1495252726Srpaulo 1496252726Srpaulo /* derive plaintext length from first subfield */ 1497252726Srpaulo *keylen = (unsigned char) str[0]; 1498252726Srpaulo if ((u8 *) (str + *keylen) >= (u8 *) (buf + buflen)) { 1499252726Srpaulo /* decryption error - invalid key length */ 1500252726Srpaulo goto out; 1501252726Srpaulo } 1502252726Srpaulo if (*keylen == 0) { 1503252726Srpaulo /* empty password */ 1504252726Srpaulo goto out; 1505252726Srpaulo } 1506252726Srpaulo 1507252726Srpaulo /* copy passphrase into new buffer */ 1508252726Srpaulo ret = os_malloc(*keylen); 1509252726Srpaulo if (ret) 1510252726Srpaulo os_memcpy(ret, str + 1, *keylen); 1511252726Srpaulo 1512252726Srpauloout: 1513252726Srpaulo /* return new buffer */ 1514252726Srpaulo os_free(buf); 1515252726Srpaulo return ret; 1516252726Srpaulo} 1517252726Srpaulo 1518252726Srpaulo 1519214734Srpaulovoid radius_free_class(struct radius_class_data *c) 1520214734Srpaulo{ 1521214734Srpaulo size_t i; 1522214734Srpaulo if (c == NULL) 1523214734Srpaulo return; 1524214734Srpaulo for (i = 0; i < c->count; i++) 1525214734Srpaulo os_free(c->attr[i].data); 1526214734Srpaulo os_free(c->attr); 1527214734Srpaulo c->attr = NULL; 1528214734Srpaulo c->count = 0; 1529214734Srpaulo} 1530214734Srpaulo 1531214734Srpaulo 1532214734Srpauloint radius_copy_class(struct radius_class_data *dst, 1533214734Srpaulo const struct radius_class_data *src) 1534214734Srpaulo{ 1535214734Srpaulo size_t i; 1536214734Srpaulo 1537214734Srpaulo if (src->attr == NULL) 1538214734Srpaulo return 0; 1539214734Srpaulo 1540252726Srpaulo dst->attr = os_calloc(src->count, sizeof(struct radius_attr_data)); 1541214734Srpaulo if (dst->attr == NULL) 1542214734Srpaulo return -1; 1543214734Srpaulo 1544214734Srpaulo dst->count = 0; 1545214734Srpaulo 1546214734Srpaulo for (i = 0; i < src->count; i++) { 1547214734Srpaulo dst->attr[i].data = os_malloc(src->attr[i].len); 1548214734Srpaulo if (dst->attr[i].data == NULL) 1549214734Srpaulo break; 1550214734Srpaulo dst->count++; 1551214734Srpaulo os_memcpy(dst->attr[i].data, src->attr[i].data, 1552214734Srpaulo src->attr[i].len); 1553214734Srpaulo dst->attr[i].len = src->attr[i].len; 1554214734Srpaulo } 1555214734Srpaulo 1556214734Srpaulo return 0; 1557214734Srpaulo} 1558252726Srpaulo 1559252726Srpaulo 1560252726Srpaulou8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs) 1561252726Srpaulo{ 1562252726Srpaulo size_t i, j; 1563252726Srpaulo struct radius_attr_hdr *attr; 1564252726Srpaulo 1565252726Srpaulo for (i = 0; i < msg->attr_used; i++) { 1566252726Srpaulo attr = radius_get_attr_hdr(msg, i); 1567252726Srpaulo 1568252726Srpaulo for (j = 0; attrs[j]; j++) { 1569252726Srpaulo if (attr->type == attrs[j]) 1570252726Srpaulo break; 1571252726Srpaulo } 1572252726Srpaulo 1573252726Srpaulo if (attrs[j] == 0) 1574252726Srpaulo return attr->type; /* unlisted attr */ 1575252726Srpaulo } 1576252726Srpaulo 1577252726Srpaulo return 0; 1578252726Srpaulo} 1579