139beb93cSSam Leffler /* 2e28a4053SRui Paulo * RADIUS message processing 3*a90b9d01SCy Schubert * Copyright (c) 2002-2009, 2011-2022, Jouni Malinen <j@w1.fi> 439beb93cSSam Leffler * 5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license. 6f05cddf9SRui Paulo * See README for more details. 739beb93cSSam Leffler */ 839beb93cSSam Leffler 9e28a4053SRui Paulo #include "utils/includes.h" 1039beb93cSSam Leffler 11e28a4053SRui Paulo #include "utils/common.h" 12e28a4053SRui Paulo #include "utils/wpabuf.h" 13e28a4053SRui Paulo #include "crypto/md5.h" 14e28a4053SRui Paulo #include "crypto/crypto.h" 1539beb93cSSam Leffler #include "radius.h" 16e28a4053SRui Paulo 17e28a4053SRui Paulo 18e28a4053SRui Paulo /** 19e28a4053SRui Paulo * struct radius_msg - RADIUS message structure for new and parsed messages 20e28a4053SRui Paulo */ 21e28a4053SRui Paulo struct radius_msg { 22e28a4053SRui Paulo /** 23e28a4053SRui Paulo * buf - Allocated buffer for RADIUS message 24e28a4053SRui Paulo */ 25e28a4053SRui Paulo struct wpabuf *buf; 26e28a4053SRui Paulo 27e28a4053SRui Paulo /** 28e28a4053SRui Paulo * hdr - Pointer to the RADIUS header in buf 29e28a4053SRui Paulo */ 30e28a4053SRui Paulo struct radius_hdr *hdr; 31e28a4053SRui Paulo 32e28a4053SRui Paulo /** 33e28a4053SRui Paulo * attr_pos - Array of indexes to attributes 34e28a4053SRui Paulo * 35e28a4053SRui Paulo * The values are number of bytes from buf to the beginning of 36e28a4053SRui Paulo * struct radius_attr_hdr. 37e28a4053SRui Paulo */ 38e28a4053SRui Paulo size_t *attr_pos; 39e28a4053SRui Paulo 40e28a4053SRui Paulo /** 41e28a4053SRui Paulo * attr_size - Total size of the attribute pointer array 42e28a4053SRui Paulo */ 43e28a4053SRui Paulo size_t attr_size; 44e28a4053SRui Paulo 45e28a4053SRui Paulo /** 46e28a4053SRui Paulo * attr_used - Total number of attributes in the array 47e28a4053SRui Paulo */ 48e28a4053SRui Paulo size_t attr_used; 49e28a4053SRui Paulo }; 50e28a4053SRui Paulo 51e28a4053SRui Paulo 52e28a4053SRui Paulo struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg) 53e28a4053SRui Paulo { 54e28a4053SRui Paulo return msg->hdr; 55e28a4053SRui Paulo } 56e28a4053SRui Paulo 57e28a4053SRui Paulo 58e28a4053SRui Paulo struct wpabuf * radius_msg_get_buf(struct radius_msg *msg) 59e28a4053SRui Paulo { 60e28a4053SRui Paulo return msg->buf; 61e28a4053SRui Paulo } 6239beb93cSSam Leffler 6339beb93cSSam Leffler 6439beb93cSSam Leffler static struct radius_attr_hdr * 6539beb93cSSam Leffler radius_get_attr_hdr(struct radius_msg *msg, int idx) 6639beb93cSSam Leffler { 67e28a4053SRui Paulo return (struct radius_attr_hdr *) 68e28a4053SRui Paulo (wpabuf_mhead_u8(msg->buf) + msg->attr_pos[idx]); 6939beb93cSSam Leffler } 7039beb93cSSam Leffler 7139beb93cSSam Leffler 72e28a4053SRui Paulo static void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier) 7339beb93cSSam Leffler { 74e28a4053SRui Paulo msg->hdr->code = code; 75e28a4053SRui Paulo msg->hdr->identifier = identifier; 7639beb93cSSam Leffler } 7739beb93cSSam Leffler 7839beb93cSSam Leffler 79e28a4053SRui Paulo static int radius_msg_initialize(struct radius_msg *msg) 8039beb93cSSam Leffler { 81f05cddf9SRui Paulo msg->attr_pos = os_calloc(RADIUS_DEFAULT_ATTR_COUNT, 82f05cddf9SRui Paulo sizeof(*msg->attr_pos)); 83e28a4053SRui Paulo if (msg->attr_pos == NULL) 8439beb93cSSam Leffler return -1; 8539beb93cSSam Leffler 8639beb93cSSam Leffler msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT; 8739beb93cSSam Leffler msg->attr_used = 0; 8839beb93cSSam Leffler 8939beb93cSSam Leffler return 0; 9039beb93cSSam Leffler } 9139beb93cSSam Leffler 9239beb93cSSam Leffler 93e28a4053SRui Paulo /** 94e28a4053SRui Paulo * radius_msg_new - Create a new RADIUS message 95e28a4053SRui Paulo * @code: Code for RADIUS header 96e28a4053SRui Paulo * @identifier: Identifier for RADIUS header 97e28a4053SRui Paulo * Returns: Context for RADIUS message or %NULL on failure 98e28a4053SRui Paulo * 99e28a4053SRui Paulo * The caller is responsible for freeing the returned data with 100e28a4053SRui Paulo * radius_msg_free(). 101e28a4053SRui Paulo */ 102e28a4053SRui Paulo struct radius_msg * radius_msg_new(u8 code, u8 identifier) 10339beb93cSSam Leffler { 104e28a4053SRui Paulo struct radius_msg *msg; 105e28a4053SRui Paulo 106e28a4053SRui Paulo msg = os_zalloc(sizeof(*msg)); 107e28a4053SRui Paulo if (msg == NULL) 108e28a4053SRui Paulo return NULL; 109e28a4053SRui Paulo 110e28a4053SRui Paulo msg->buf = wpabuf_alloc(RADIUS_DEFAULT_MSG_SIZE); 111e28a4053SRui Paulo if (msg->buf == NULL || radius_msg_initialize(msg)) { 112e28a4053SRui Paulo radius_msg_free(msg); 113e28a4053SRui Paulo return NULL; 114e28a4053SRui Paulo } 115e28a4053SRui Paulo msg->hdr = wpabuf_put(msg->buf, sizeof(struct radius_hdr)); 116e28a4053SRui Paulo 117e28a4053SRui Paulo radius_msg_set_hdr(msg, code, identifier); 118e28a4053SRui Paulo 119e28a4053SRui Paulo return msg; 12039beb93cSSam Leffler } 12139beb93cSSam Leffler 12239beb93cSSam Leffler 123e28a4053SRui Paulo /** 124e28a4053SRui Paulo * radius_msg_free - Free a RADIUS message 125e28a4053SRui Paulo * @msg: RADIUS message from radius_msg_new() or radius_msg_parse() 126e28a4053SRui Paulo */ 12739beb93cSSam Leffler void radius_msg_free(struct radius_msg *msg) 12839beb93cSSam Leffler { 129e28a4053SRui Paulo if (msg == NULL) 130e28a4053SRui Paulo return; 13139beb93cSSam Leffler 132e28a4053SRui Paulo wpabuf_free(msg->buf); 13339beb93cSSam Leffler os_free(msg->attr_pos); 134e28a4053SRui Paulo os_free(msg); 13539beb93cSSam Leffler } 13639beb93cSSam Leffler 13739beb93cSSam Leffler 13839beb93cSSam Leffler static const char *radius_code_string(u8 code) 13939beb93cSSam Leffler { 14039beb93cSSam Leffler switch (code) { 14139beb93cSSam Leffler case RADIUS_CODE_ACCESS_REQUEST: return "Access-Request"; 14239beb93cSSam Leffler case RADIUS_CODE_ACCESS_ACCEPT: return "Access-Accept"; 14339beb93cSSam Leffler case RADIUS_CODE_ACCESS_REJECT: return "Access-Reject"; 14439beb93cSSam Leffler case RADIUS_CODE_ACCOUNTING_REQUEST: return "Accounting-Request"; 14539beb93cSSam Leffler case RADIUS_CODE_ACCOUNTING_RESPONSE: return "Accounting-Response"; 14639beb93cSSam Leffler case RADIUS_CODE_ACCESS_CHALLENGE: return "Access-Challenge"; 14739beb93cSSam Leffler case RADIUS_CODE_STATUS_SERVER: return "Status-Server"; 14839beb93cSSam Leffler case RADIUS_CODE_STATUS_CLIENT: return "Status-Client"; 14939beb93cSSam Leffler case RADIUS_CODE_RESERVED: return "Reserved"; 150f05cddf9SRui Paulo case RADIUS_CODE_DISCONNECT_REQUEST: return "Disconnect-Request"; 151f05cddf9SRui Paulo case RADIUS_CODE_DISCONNECT_ACK: return "Disconnect-ACK"; 152f05cddf9SRui Paulo case RADIUS_CODE_DISCONNECT_NAK: return "Disconnect-NAK"; 153f05cddf9SRui Paulo case RADIUS_CODE_COA_REQUEST: return "CoA-Request"; 154f05cddf9SRui Paulo case RADIUS_CODE_COA_ACK: return "CoA-ACK"; 155f05cddf9SRui Paulo case RADIUS_CODE_COA_NAK: return "CoA-NAK"; 15639beb93cSSam Leffler default: return "?Unknown?"; 15739beb93cSSam Leffler } 15839beb93cSSam Leffler } 15939beb93cSSam Leffler 16039beb93cSSam Leffler 16139beb93cSSam Leffler struct radius_attr_type { 162*a90b9d01SCy Schubert u16 type; /* 0..255 for basic types; 163*a90b9d01SCy Schubert * (241 << 8) | <ext-type> for extended types */ 16439beb93cSSam Leffler char *name; 16539beb93cSSam Leffler enum { 16639beb93cSSam Leffler RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP, 16739beb93cSSam Leffler RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6 16839beb93cSSam Leffler } data_type; 16939beb93cSSam Leffler }; 17039beb93cSSam Leffler 171325151a3SRui Paulo static const struct radius_attr_type radius_attrs[] = 17239beb93cSSam Leffler { 17339beb93cSSam Leffler { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT }, 17439beb93cSSam Leffler { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST }, 17539beb93cSSam Leffler { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP }, 17639beb93cSSam Leffler { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 }, 177780fb4a2SCy Schubert { RADIUS_ATTR_SERVICE_TYPE, "Service-Type", RADIUS_ATTR_INT32 }, 178780fb4a2SCy Schubert { RADIUS_ATTR_FRAMED_IP_ADDRESS, "Framed-IP-Address", RADIUS_ATTR_IP }, 179*a90b9d01SCy Schubert { RADIUS_ATTR_FILTER_ID, "Filter-Id", RADIUS_ATTR_TEXT }, 18039beb93cSSam Leffler { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 }, 18139beb93cSSam Leffler { RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT }, 18239beb93cSSam Leffler { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST }, 18339beb93cSSam Leffler { RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST }, 18439beb93cSSam Leffler { RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST }, 18539beb93cSSam Leffler { RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 }, 18639beb93cSSam Leffler { RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 }, 18739beb93cSSam Leffler { RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action", 18839beb93cSSam Leffler RADIUS_ATTR_INT32 }, 18939beb93cSSam Leffler { RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id", 19039beb93cSSam Leffler RADIUS_ATTR_TEXT }, 19139beb93cSSam Leffler { RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id", 19239beb93cSSam Leffler RADIUS_ATTR_TEXT }, 19339beb93cSSam Leffler { RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT }, 19439beb93cSSam Leffler { RADIUS_ATTR_PROXY_STATE, "Proxy-State", RADIUS_ATTR_UNDIST }, 19539beb93cSSam Leffler { RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type", 19639beb93cSSam Leffler RADIUS_ATTR_INT32 }, 19739beb93cSSam Leffler { RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 }, 19839beb93cSSam Leffler { RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets", 19939beb93cSSam Leffler RADIUS_ATTR_INT32 }, 20039beb93cSSam Leffler { RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets", 20139beb93cSSam Leffler RADIUS_ATTR_INT32 }, 20239beb93cSSam Leffler { RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT }, 20339beb93cSSam Leffler { RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 }, 20439beb93cSSam Leffler { RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time", 20539beb93cSSam Leffler RADIUS_ATTR_INT32 }, 20639beb93cSSam Leffler { RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets", 20739beb93cSSam Leffler RADIUS_ATTR_INT32 }, 20839beb93cSSam Leffler { RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets", 20939beb93cSSam Leffler RADIUS_ATTR_INT32 }, 21039beb93cSSam Leffler { RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause", 21139beb93cSSam Leffler RADIUS_ATTR_INT32 }, 21239beb93cSSam Leffler { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id", 21339beb93cSSam Leffler RADIUS_ATTR_TEXT }, 21439beb93cSSam Leffler { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 }, 21539beb93cSSam Leffler { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords", 21639beb93cSSam Leffler RADIUS_ATTR_INT32 }, 21739beb93cSSam Leffler { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords", 21839beb93cSSam Leffler RADIUS_ATTR_INT32 }, 21939beb93cSSam Leffler { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp", 22039beb93cSSam Leffler RADIUS_ATTR_INT32 }, 221780fb4a2SCy Schubert { RADIUS_ATTR_EGRESS_VLANID, "EGRESS-VLANID", RADIUS_ATTR_HEXDUMP }, 22239beb93cSSam Leffler { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 }, 22339beb93cSSam Leffler { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP }, 22439beb93cSSam Leffler { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type", 22539beb93cSSam Leffler RADIUS_ATTR_HEXDUMP }, 226f05cddf9SRui Paulo { RADIUS_ATTR_TUNNEL_PASSWORD, "Tunnel-Password", 227f05cddf9SRui Paulo RADIUS_ATTR_UNDIST }, 22839beb93cSSam Leffler { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT }, 22939beb93cSSam Leffler { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST }, 23039beb93cSSam Leffler { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator", 23139beb93cSSam Leffler RADIUS_ATTR_UNDIST }, 23239beb93cSSam Leffler { RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, "Tunnel-Private-Group-Id", 23339beb93cSSam Leffler RADIUS_ATTR_HEXDUMP }, 23439beb93cSSam Leffler { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval", 23539beb93cSSam Leffler RADIUS_ATTR_INT32 }, 236f05cddf9SRui Paulo { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargeable-User-Identity", 23739beb93cSSam Leffler RADIUS_ATTR_TEXT }, 23839beb93cSSam Leffler { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 }, 2395b9c547cSRui Paulo { RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 }, 2405b9c547cSRui Paulo { RADIUS_ATTR_EAP_KEY_NAME, "EAP-Key-Name", RADIUS_ATTR_HEXDUMP }, 2415b9c547cSRui Paulo { RADIUS_ATTR_OPERATOR_NAME, "Operator-Name", RADIUS_ATTR_TEXT }, 2425b9c547cSRui Paulo { RADIUS_ATTR_LOCATION_INFO, "Location-Information", 2435b9c547cSRui Paulo RADIUS_ATTR_HEXDUMP }, 2445b9c547cSRui Paulo { RADIUS_ATTR_LOCATION_DATA, "Location-Data", RADIUS_ATTR_HEXDUMP }, 2455b9c547cSRui Paulo { RADIUS_ATTR_BASIC_LOCATION_POLICY_RULES, 2465b9c547cSRui Paulo "Basic-Location-Policy-Rules", RADIUS_ATTR_HEXDUMP }, 2475b9c547cSRui Paulo { RADIUS_ATTR_EXTENDED_LOCATION_POLICY_RULES, 2485b9c547cSRui Paulo "Extended-Location-Policy-Rules", RADIUS_ATTR_HEXDUMP }, 2495b9c547cSRui Paulo { RADIUS_ATTR_LOCATION_CAPABLE, "Location-Capable", RADIUS_ATTR_INT32 }, 2505b9c547cSRui Paulo { RADIUS_ATTR_REQUESTED_LOCATION_INFO, "Requested-Location-Info", 2515b9c547cSRui Paulo RADIUS_ATTR_INT32 }, 2525b9c547cSRui Paulo { RADIUS_ATTR_MOBILITY_DOMAIN_ID, "Mobility-Domain-Id", 2535b9c547cSRui Paulo RADIUS_ATTR_INT32 }, 2545b9c547cSRui Paulo { RADIUS_ATTR_WLAN_HESSID, "WLAN-HESSID", RADIUS_ATTR_TEXT }, 25585732ac8SCy Schubert { RADIUS_ATTR_WLAN_REASON_CODE, "WLAN-Reason-Code", 25685732ac8SCy Schubert RADIUS_ATTR_INT32 }, 2575b9c547cSRui Paulo { RADIUS_ATTR_WLAN_PAIRWISE_CIPHER, "WLAN-Pairwise-Cipher", 2585b9c547cSRui Paulo RADIUS_ATTR_HEXDUMP }, 2595b9c547cSRui Paulo { RADIUS_ATTR_WLAN_GROUP_CIPHER, "WLAN-Group-Cipher", 2605b9c547cSRui Paulo RADIUS_ATTR_HEXDUMP }, 2615b9c547cSRui Paulo { RADIUS_ATTR_WLAN_AKM_SUITE, "WLAN-AKM-Suite", 2625b9c547cSRui Paulo RADIUS_ATTR_HEXDUMP }, 2635b9c547cSRui Paulo { RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, "WLAN-Group-Mgmt-Pairwise-Cipher", 2645b9c547cSRui Paulo RADIUS_ATTR_HEXDUMP }, 265*a90b9d01SCy Schubert { RADIUS_ATTR_EXT_TYPE_1, "Extended-Type-1", RADIUS_ATTR_UNDIST }, 266*a90b9d01SCy Schubert { RADIUS_ATTR_EXT_TYPE_2, "Extended-Type-2", RADIUS_ATTR_UNDIST }, 267*a90b9d01SCy Schubert { RADIUS_ATTR_EXT_TYPE_3, "Extended-Type-3", RADIUS_ATTR_UNDIST }, 268*a90b9d01SCy Schubert { RADIUS_ATTR_EXT_TYPE_4, "Extended-Type-4", RADIUS_ATTR_UNDIST }, 269*a90b9d01SCy Schubert { RADIUS_ATTR_LONG_EXT_TYPE_1, "Long-Extended-Type-1", 270*a90b9d01SCy Schubert RADIUS_ATTR_UNDIST }, 271*a90b9d01SCy Schubert { RADIUS_ATTR_LONG_EXT_TYPE_2, "Long-Extended-Type-2", 272*a90b9d01SCy Schubert RADIUS_ATTR_UNDIST }, 273*a90b9d01SCy Schubert { RADIUS_ATTR_EXT_VENDOR_SPECIFIC_1, "Extended-Vendor-Specific-1", 274*a90b9d01SCy Schubert RADIUS_ATTR_UNDIST }, 275*a90b9d01SCy Schubert { RADIUS_ATTR_EXT_VENDOR_SPECIFIC_2, "Extended-Vendor-Specific-2", 276*a90b9d01SCy Schubert RADIUS_ATTR_UNDIST }, 277*a90b9d01SCy Schubert { RADIUS_ATTR_EXT_VENDOR_SPECIFIC_3, "Extended-Vendor-Specific-3", 278*a90b9d01SCy Schubert RADIUS_ATTR_UNDIST }, 279*a90b9d01SCy Schubert { RADIUS_ATTR_EXT_VENDOR_SPECIFIC_4, "Extended-Vendor-Specific-4", 280*a90b9d01SCy Schubert RADIUS_ATTR_UNDIST }, 281*a90b9d01SCy Schubert { RADIUS_ATTR_EXT_VENDOR_SPECIFIC_5, "Extended-Vendor-Specific-5", 282*a90b9d01SCy Schubert RADIUS_ATTR_UNDIST }, 283*a90b9d01SCy Schubert { RADIUS_ATTR_EXT_VENDOR_SPECIFIC_6, "Extended-Vendor-Specific-6", 284*a90b9d01SCy Schubert RADIUS_ATTR_UNDIST }, 28539beb93cSSam Leffler }; 2865b9c547cSRui Paulo #define RADIUS_ATTRS ARRAY_SIZE(radius_attrs) 28739beb93cSSam Leffler 28839beb93cSSam Leffler 289*a90b9d01SCy Schubert static const struct radius_attr_type * radius_get_attr_type(u16 type) 29039beb93cSSam Leffler { 29139beb93cSSam Leffler size_t i; 29239beb93cSSam Leffler 29339beb93cSSam Leffler for (i = 0; i < RADIUS_ATTRS; i++) { 29439beb93cSSam Leffler if (type == radius_attrs[i].type) 29539beb93cSSam Leffler return &radius_attrs[i]; 29639beb93cSSam Leffler } 29739beb93cSSam Leffler 29839beb93cSSam Leffler return NULL; 29939beb93cSSam Leffler } 30039beb93cSSam Leffler 30139beb93cSSam Leffler 302*a90b9d01SCy Schubert static bool radius_is_long_ext_type(u8 type) 303*a90b9d01SCy Schubert { 304*a90b9d01SCy Schubert return type == RADIUS_ATTR_LONG_EXT_TYPE_1 || 305*a90b9d01SCy Schubert type == RADIUS_ATTR_LONG_EXT_TYPE_2; 306*a90b9d01SCy Schubert } 307*a90b9d01SCy Schubert 308*a90b9d01SCy Schubert 309*a90b9d01SCy Schubert static bool radius_is_ext_type(u8 type) 310*a90b9d01SCy Schubert { 311*a90b9d01SCy Schubert return type >= RADIUS_ATTR_EXT_TYPE_1 && 312*a90b9d01SCy Schubert type <= RADIUS_ATTR_LONG_EXT_TYPE_2; 313*a90b9d01SCy Schubert } 314*a90b9d01SCy Schubert 315*a90b9d01SCy Schubert 31639beb93cSSam Leffler static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) 31739beb93cSSam Leffler { 318*a90b9d01SCy Schubert struct radius_attr_hdr_ext *ext = NULL; 319325151a3SRui Paulo const struct radius_attr_type *attr; 3205b9c547cSRui Paulo int len; 32139beb93cSSam Leffler unsigned char *pos; 3225b9c547cSRui Paulo char buf[1000]; 32339beb93cSSam Leffler 324*a90b9d01SCy Schubert if (hdr->length < sizeof(struct radius_attr_hdr)) 32539beb93cSSam Leffler return; 32639beb93cSSam Leffler 327*a90b9d01SCy Schubert if (radius_is_ext_type(hdr->type)) { 328*a90b9d01SCy Schubert if (hdr->length < 4) { 329*a90b9d01SCy Schubert wpa_printf(MSG_INFO, 330*a90b9d01SCy Schubert " Invalid attribute %d (too short for extended type)", 331*a90b9d01SCy Schubert hdr->type); 332*a90b9d01SCy Schubert return; 333*a90b9d01SCy Schubert } 334*a90b9d01SCy Schubert 335*a90b9d01SCy Schubert ext = (struct radius_attr_hdr_ext *) hdr; 336*a90b9d01SCy Schubert } 337*a90b9d01SCy Schubert 338*a90b9d01SCy Schubert if (ext) { 339*a90b9d01SCy Schubert attr = radius_get_attr_type((ext->type << 8) | ext->ext_type); 340*a90b9d01SCy Schubert wpa_printf(MSG_INFO, " Attribute %d.%d (%s) length=%d", 341*a90b9d01SCy Schubert ext->type, ext->ext_type, 342*a90b9d01SCy Schubert attr ? attr->name : "?Unknown?", ext->length); 343*a90b9d01SCy Schubert pos = (unsigned char *) (ext + 1); 344*a90b9d01SCy Schubert len = ext->length - sizeof(struct radius_attr_hdr_ext); 345*a90b9d01SCy Schubert } else { 346*a90b9d01SCy Schubert attr = radius_get_attr_type(hdr->type); 347*a90b9d01SCy Schubert wpa_printf(MSG_INFO, " Attribute %d (%s) length=%d", 348*a90b9d01SCy Schubert hdr->type, attr ? attr->name : "?Unknown?", 349*a90b9d01SCy Schubert hdr->length); 35039beb93cSSam Leffler pos = (unsigned char *) (hdr + 1); 351*a90b9d01SCy Schubert len = hdr->length - sizeof(struct radius_attr_hdr); 352*a90b9d01SCy Schubert } 353*a90b9d01SCy Schubert 354*a90b9d01SCy Schubert if (!attr) 355*a90b9d01SCy Schubert return; 35639beb93cSSam Leffler 35739beb93cSSam Leffler switch (attr->data_type) { 35839beb93cSSam Leffler case RADIUS_ATTR_TEXT: 3595b9c547cSRui Paulo printf_encode(buf, sizeof(buf), pos, len); 3605b9c547cSRui Paulo wpa_printf(MSG_INFO, " Value: '%s'", buf); 36139beb93cSSam Leffler break; 36239beb93cSSam Leffler 36339beb93cSSam Leffler case RADIUS_ATTR_IP: 36439beb93cSSam Leffler if (len == 4) { 36539beb93cSSam Leffler struct in_addr addr; 36639beb93cSSam Leffler os_memcpy(&addr, pos, 4); 3675b9c547cSRui Paulo wpa_printf(MSG_INFO, " Value: %s", 3685b9c547cSRui Paulo inet_ntoa(addr)); 3695b9c547cSRui Paulo } else { 3705b9c547cSRui Paulo wpa_printf(MSG_INFO, " Invalid IP address length %d", 3715b9c547cSRui Paulo len); 3725b9c547cSRui Paulo } 37339beb93cSSam Leffler break; 37439beb93cSSam Leffler 37539beb93cSSam Leffler #ifdef CONFIG_IPV6 37639beb93cSSam Leffler case RADIUS_ATTR_IPV6: 37739beb93cSSam Leffler if (len == 16) { 37839beb93cSSam Leffler const char *atxt; 37939beb93cSSam Leffler struct in6_addr *addr = (struct in6_addr *) pos; 38039beb93cSSam Leffler atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf)); 3815b9c547cSRui Paulo wpa_printf(MSG_INFO, " Value: %s", 3825b9c547cSRui Paulo atxt ? atxt : "?"); 3835b9c547cSRui Paulo } else { 3845b9c547cSRui Paulo wpa_printf(MSG_INFO, " Invalid IPv6 address length %d", 3855b9c547cSRui Paulo len); 3865b9c547cSRui Paulo } 38739beb93cSSam Leffler break; 38839beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 38939beb93cSSam Leffler 39039beb93cSSam Leffler case RADIUS_ATTR_HEXDUMP: 39139beb93cSSam Leffler case RADIUS_ATTR_UNDIST: 3925b9c547cSRui Paulo wpa_snprintf_hex(buf, sizeof(buf), pos, len); 3935b9c547cSRui Paulo wpa_printf(MSG_INFO, " Value: %s", buf); 39439beb93cSSam Leffler break; 39539beb93cSSam Leffler 39639beb93cSSam Leffler case RADIUS_ATTR_INT32: 39739beb93cSSam Leffler if (len == 4) 3985b9c547cSRui Paulo wpa_printf(MSG_INFO, " Value: %u", 3995b9c547cSRui Paulo WPA_GET_BE32(pos)); 40039beb93cSSam Leffler else 4015b9c547cSRui Paulo wpa_printf(MSG_INFO, " Invalid INT32 length %d", 4025b9c547cSRui Paulo len); 40339beb93cSSam Leffler break; 40439beb93cSSam Leffler 40539beb93cSSam Leffler default: 40639beb93cSSam Leffler break; 40739beb93cSSam Leffler } 40839beb93cSSam Leffler } 40939beb93cSSam Leffler 41039beb93cSSam Leffler 41139beb93cSSam Leffler void radius_msg_dump(struct radius_msg *msg) 41239beb93cSSam Leffler { 41339beb93cSSam Leffler size_t i; 41439beb93cSSam Leffler 4155b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS message: code=%d (%s) identifier=%d length=%d", 41639beb93cSSam Leffler msg->hdr->code, radius_code_string(msg->hdr->code), 417f05cddf9SRui Paulo msg->hdr->identifier, be_to_host16(msg->hdr->length)); 41839beb93cSSam Leffler 41939beb93cSSam Leffler for (i = 0; i < msg->attr_used; i++) { 42039beb93cSSam Leffler struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); 42139beb93cSSam Leffler radius_msg_dump_attr(attr); 42239beb93cSSam Leffler } 42339beb93cSSam Leffler } 42439beb93cSSam Leffler 42539beb93cSSam Leffler 426*a90b9d01SCy Schubert u8 * radius_msg_add_msg_auth(struct radius_msg *msg) 42739beb93cSSam Leffler { 42839beb93cSSam Leffler u8 auth[MD5_MAC_LEN]; 42939beb93cSSam Leffler struct radius_attr_hdr *attr; 43039beb93cSSam Leffler 43139beb93cSSam Leffler os_memset(auth, 0, MD5_MAC_LEN); 432*a90b9d01SCy Schubert attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 43339beb93cSSam Leffler auth, MD5_MAC_LEN); 434*a90b9d01SCy Schubert if (!attr) { 435*a90b9d01SCy Schubert wpa_printf(MSG_ERROR, 436*a90b9d01SCy Schubert "WARNING: Could not add Message-Authenticator"); 437*a90b9d01SCy Schubert return NULL; 43839beb93cSSam Leffler } 439*a90b9d01SCy Schubert 440*a90b9d01SCy Schubert return (u8 *) (attr + 1); 441*a90b9d01SCy Schubert } 442*a90b9d01SCy Schubert 443*a90b9d01SCy Schubert 444*a90b9d01SCy Schubert static u8 * radius_msg_auth_pos(struct radius_msg *msg) 445*a90b9d01SCy Schubert { 446*a90b9d01SCy Schubert u8 *pos; 447*a90b9d01SCy Schubert size_t alen; 448*a90b9d01SCy Schubert 449*a90b9d01SCy Schubert if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 450*a90b9d01SCy Schubert &pos, &alen, NULL) == 0 && 451*a90b9d01SCy Schubert alen == MD5_MAC_LEN) { 452*a90b9d01SCy Schubert /* Use already added Message-Authenticator attribute */ 453*a90b9d01SCy Schubert return pos; 454*a90b9d01SCy Schubert } 455*a90b9d01SCy Schubert 456*a90b9d01SCy Schubert /* Add a Message-Authenticator attribute */ 457*a90b9d01SCy Schubert return radius_msg_add_msg_auth(msg); 458*a90b9d01SCy Schubert } 459*a90b9d01SCy Schubert 460*a90b9d01SCy Schubert 461*a90b9d01SCy Schubert int radius_msg_finish(struct radius_msg *msg, const u8 *secret, 462*a90b9d01SCy Schubert size_t secret_len) 463*a90b9d01SCy Schubert { 464*a90b9d01SCy Schubert if (secret) { 465*a90b9d01SCy Schubert u8 *pos; 466*a90b9d01SCy Schubert 467*a90b9d01SCy Schubert pos = radius_msg_auth_pos(msg); 468*a90b9d01SCy Schubert if (!pos) 469*a90b9d01SCy Schubert return -1; 470f05cddf9SRui Paulo msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); 471*a90b9d01SCy Schubert if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf), 472*a90b9d01SCy Schubert wpabuf_len(msg->buf), pos) < 0) 473*a90b9d01SCy Schubert return -1; 47439beb93cSSam Leffler } else 475f05cddf9SRui Paulo msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); 47639beb93cSSam Leffler 477e28a4053SRui Paulo if (wpabuf_len(msg->buf) > 0xffff) { 478e28a4053SRui Paulo wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", 479e28a4053SRui Paulo (unsigned long) wpabuf_len(msg->buf)); 48039beb93cSSam Leffler return -1; 48139beb93cSSam Leffler } 48239beb93cSSam Leffler return 0; 48339beb93cSSam Leffler } 48439beb93cSSam Leffler 48539beb93cSSam Leffler 48639beb93cSSam Leffler int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, 48739beb93cSSam Leffler size_t secret_len, const u8 *req_authenticator) 48839beb93cSSam Leffler { 48939beb93cSSam Leffler const u8 *addr[4]; 49039beb93cSSam Leffler size_t len[4]; 491*a90b9d01SCy Schubert u8 *pos; 49239beb93cSSam Leffler 493*a90b9d01SCy Schubert pos = radius_msg_auth_pos(msg); 494*a90b9d01SCy Schubert if (!pos) 49539beb93cSSam Leffler return -1; 496f05cddf9SRui Paulo msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); 49739beb93cSSam Leffler os_memcpy(msg->hdr->authenticator, req_authenticator, 49839beb93cSSam Leffler sizeof(msg->hdr->authenticator)); 499*a90b9d01SCy Schubert if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf), 500*a90b9d01SCy Schubert wpabuf_len(msg->buf), pos) < 0) 501*a90b9d01SCy Schubert return -1; 50239beb93cSSam Leffler 50339beb93cSSam Leffler /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ 50439beb93cSSam Leffler addr[0] = (u8 *) msg->hdr; 50539beb93cSSam Leffler len[0] = 1 + 1 + 2; 50639beb93cSSam Leffler addr[1] = req_authenticator; 50739beb93cSSam Leffler len[1] = MD5_MAC_LEN; 508e28a4053SRui Paulo addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr); 509e28a4053SRui Paulo len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); 51039beb93cSSam Leffler addr[3] = secret; 51139beb93cSSam Leffler len[3] = secret_len; 51239beb93cSSam Leffler md5_vector(4, addr, len, msg->hdr->authenticator); 51339beb93cSSam Leffler 514e28a4053SRui Paulo if (wpabuf_len(msg->buf) > 0xffff) { 515e28a4053SRui Paulo wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", 516e28a4053SRui Paulo (unsigned long) wpabuf_len(msg->buf)); 51739beb93cSSam Leffler return -1; 51839beb93cSSam Leffler } 51939beb93cSSam Leffler return 0; 52039beb93cSSam Leffler } 52139beb93cSSam Leffler 52239beb93cSSam Leffler 523f05cddf9SRui Paulo int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret, 524f05cddf9SRui Paulo size_t secret_len, 525f05cddf9SRui Paulo const struct radius_hdr *req_hdr) 526f05cddf9SRui Paulo { 527f05cddf9SRui Paulo const u8 *addr[2]; 528f05cddf9SRui Paulo size_t len[2]; 529*a90b9d01SCy Schubert u8 *pos; 530f05cddf9SRui Paulo 531*a90b9d01SCy Schubert pos = radius_msg_auth_pos(msg); 532*a90b9d01SCy Schubert if (!pos) 533f05cddf9SRui Paulo return -1; 534f05cddf9SRui Paulo 535f05cddf9SRui Paulo msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); 536f05cddf9SRui Paulo os_memcpy(msg->hdr->authenticator, req_hdr->authenticator, 16); 537*a90b9d01SCy Schubert if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf), 538*a90b9d01SCy Schubert wpabuf_len(msg->buf), pos) < 0) 539*a90b9d01SCy Schubert return -1; 540f05cddf9SRui Paulo 541f05cddf9SRui Paulo /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ 542f05cddf9SRui Paulo addr[0] = wpabuf_head_u8(msg->buf); 543f05cddf9SRui Paulo len[0] = wpabuf_len(msg->buf); 544f05cddf9SRui Paulo addr[1] = secret; 545f05cddf9SRui Paulo len[1] = secret_len; 546f05cddf9SRui Paulo if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0) 547f05cddf9SRui Paulo return -1; 548f05cddf9SRui Paulo 549f05cddf9SRui Paulo if (wpabuf_len(msg->buf) > 0xffff) { 550f05cddf9SRui Paulo wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", 551f05cddf9SRui Paulo (unsigned long) wpabuf_len(msg->buf)); 552f05cddf9SRui Paulo return -1; 553f05cddf9SRui Paulo } 554f05cddf9SRui Paulo return 0; 555f05cddf9SRui Paulo } 556f05cddf9SRui Paulo 557f05cddf9SRui Paulo 55839beb93cSSam Leffler void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, 55939beb93cSSam Leffler size_t secret_len) 56039beb93cSSam Leffler { 56139beb93cSSam Leffler const u8 *addr[2]; 56239beb93cSSam Leffler size_t len[2]; 56339beb93cSSam Leffler 564f05cddf9SRui Paulo msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); 56539beb93cSSam Leffler os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN); 566e28a4053SRui Paulo addr[0] = wpabuf_head(msg->buf); 567e28a4053SRui Paulo len[0] = wpabuf_len(msg->buf); 56839beb93cSSam Leffler addr[1] = secret; 56939beb93cSSam Leffler len[1] = secret_len; 57039beb93cSSam Leffler md5_vector(2, addr, len, msg->hdr->authenticator); 57139beb93cSSam Leffler 572e28a4053SRui Paulo if (wpabuf_len(msg->buf) > 0xffff) { 573e28a4053SRui Paulo wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)", 574e28a4053SRui Paulo (unsigned long) wpabuf_len(msg->buf)); 57539beb93cSSam Leffler } 57639beb93cSSam Leffler } 57739beb93cSSam Leffler 57839beb93cSSam Leffler 5795b9c547cSRui Paulo void radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret, 5805b9c547cSRui Paulo size_t secret_len, const u8 *req_authenticator) 5815b9c547cSRui Paulo { 5825b9c547cSRui Paulo const u8 *addr[2]; 5835b9c547cSRui Paulo size_t len[2]; 5845b9c547cSRui Paulo 5855b9c547cSRui Paulo msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); 5865b9c547cSRui Paulo os_memcpy(msg->hdr->authenticator, req_authenticator, MD5_MAC_LEN); 5875b9c547cSRui Paulo addr[0] = wpabuf_head(msg->buf); 5885b9c547cSRui Paulo len[0] = wpabuf_len(msg->buf); 5895b9c547cSRui Paulo addr[1] = secret; 5905b9c547cSRui Paulo len[1] = secret_len; 5915b9c547cSRui Paulo md5_vector(2, addr, len, msg->hdr->authenticator); 5925b9c547cSRui Paulo 5935b9c547cSRui Paulo if (wpabuf_len(msg->buf) > 0xffff) { 5945b9c547cSRui Paulo wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)", 5955b9c547cSRui Paulo (unsigned long) wpabuf_len(msg->buf)); 5965b9c547cSRui Paulo } 5975b9c547cSRui Paulo } 5985b9c547cSRui Paulo 5995b9c547cSRui Paulo 600f05cddf9SRui Paulo int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret, 601f05cddf9SRui Paulo size_t secret_len) 602f05cddf9SRui Paulo { 603f05cddf9SRui Paulo const u8 *addr[4]; 604f05cddf9SRui Paulo size_t len[4]; 605f05cddf9SRui Paulo u8 zero[MD5_MAC_LEN]; 606f05cddf9SRui Paulo u8 hash[MD5_MAC_LEN]; 607f05cddf9SRui Paulo 608f05cddf9SRui Paulo os_memset(zero, 0, sizeof(zero)); 609f05cddf9SRui Paulo addr[0] = (u8 *) msg->hdr; 610f05cddf9SRui Paulo len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN; 611f05cddf9SRui Paulo addr[1] = zero; 612f05cddf9SRui Paulo len[1] = MD5_MAC_LEN; 613f05cddf9SRui Paulo addr[2] = (u8 *) (msg->hdr + 1); 614f05cddf9SRui Paulo len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); 615f05cddf9SRui Paulo addr[3] = secret; 616f05cddf9SRui Paulo len[3] = secret_len; 617f05cddf9SRui Paulo md5_vector(4, addr, len, hash); 6185b9c547cSRui Paulo return os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0; 619f05cddf9SRui Paulo } 620f05cddf9SRui Paulo 621f05cddf9SRui Paulo 622f05cddf9SRui Paulo int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, 623780fb4a2SCy Schubert size_t secret_len, 624780fb4a2SCy Schubert int require_message_authenticator) 625f05cddf9SRui Paulo { 626f05cddf9SRui Paulo const u8 *addr[4]; 627f05cddf9SRui Paulo size_t len[4]; 628f05cddf9SRui Paulo u8 zero[MD5_MAC_LEN]; 629f05cddf9SRui Paulo u8 hash[MD5_MAC_LEN]; 630f05cddf9SRui Paulo u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN]; 631f05cddf9SRui Paulo u8 orig_authenticator[16]; 632f05cddf9SRui Paulo 633f05cddf9SRui Paulo struct radius_attr_hdr *attr = NULL, *tmp; 634f05cddf9SRui Paulo size_t i; 635f05cddf9SRui Paulo 636f05cddf9SRui Paulo os_memset(zero, 0, sizeof(zero)); 637f05cddf9SRui Paulo addr[0] = (u8 *) msg->hdr; 638f05cddf9SRui Paulo len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN; 639f05cddf9SRui Paulo addr[1] = zero; 640f05cddf9SRui Paulo len[1] = MD5_MAC_LEN; 641f05cddf9SRui Paulo addr[2] = (u8 *) (msg->hdr + 1); 642f05cddf9SRui Paulo len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); 643f05cddf9SRui Paulo addr[3] = secret; 644f05cddf9SRui Paulo len[3] = secret_len; 645f05cddf9SRui Paulo md5_vector(4, addr, len, hash); 6465b9c547cSRui Paulo if (os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0) 647f05cddf9SRui Paulo return 1; 648f05cddf9SRui Paulo 649f05cddf9SRui Paulo for (i = 0; i < msg->attr_used; i++) { 650f05cddf9SRui Paulo tmp = radius_get_attr_hdr(msg, i); 651f05cddf9SRui Paulo if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { 652f05cddf9SRui Paulo if (attr != NULL) { 653f05cddf9SRui Paulo wpa_printf(MSG_WARNING, "Multiple " 654f05cddf9SRui Paulo "Message-Authenticator attributes " 655f05cddf9SRui Paulo "in RADIUS message"); 656f05cddf9SRui Paulo return 1; 657f05cddf9SRui Paulo } 658f05cddf9SRui Paulo attr = tmp; 659f05cddf9SRui Paulo } 660f05cddf9SRui Paulo } 661f05cddf9SRui Paulo 662f05cddf9SRui Paulo if (attr == NULL) { 663780fb4a2SCy Schubert if (require_message_authenticator) { 664780fb4a2SCy Schubert wpa_printf(MSG_WARNING, 665780fb4a2SCy Schubert "Missing Message-Authenticator attribute in RADIUS message"); 666780fb4a2SCy Schubert return 1; 667780fb4a2SCy Schubert } 668f05cddf9SRui Paulo return 0; 669f05cddf9SRui Paulo } 670f05cddf9SRui Paulo 671f05cddf9SRui Paulo os_memcpy(orig, attr + 1, MD5_MAC_LEN); 672f05cddf9SRui Paulo os_memset(attr + 1, 0, MD5_MAC_LEN); 673f05cddf9SRui Paulo os_memcpy(orig_authenticator, msg->hdr->authenticator, 674f05cddf9SRui Paulo sizeof(orig_authenticator)); 675f05cddf9SRui Paulo os_memset(msg->hdr->authenticator, 0, 676f05cddf9SRui Paulo sizeof(msg->hdr->authenticator)); 677f05cddf9SRui Paulo hmac_md5(secret, secret_len, wpabuf_head(msg->buf), 678f05cddf9SRui Paulo wpabuf_len(msg->buf), auth); 679f05cddf9SRui Paulo os_memcpy(attr + 1, orig, MD5_MAC_LEN); 680f05cddf9SRui Paulo os_memcpy(msg->hdr->authenticator, orig_authenticator, 681f05cddf9SRui Paulo sizeof(orig_authenticator)); 682f05cddf9SRui Paulo 6835b9c547cSRui Paulo return os_memcmp_const(orig, auth, MD5_MAC_LEN) != 0; 684f05cddf9SRui Paulo } 685f05cddf9SRui Paulo 686f05cddf9SRui Paulo 68739beb93cSSam Leffler static int radius_msg_add_attr_to_array(struct radius_msg *msg, 68839beb93cSSam Leffler struct radius_attr_hdr *attr) 68939beb93cSSam Leffler { 69039beb93cSSam Leffler if (msg->attr_used >= msg->attr_size) { 69139beb93cSSam Leffler size_t *nattr_pos; 692c1d255d3SCy Schubert size_t nlen = msg->attr_size * 2; 69339beb93cSSam Leffler 694f05cddf9SRui Paulo nattr_pos = os_realloc_array(msg->attr_pos, nlen, 695f05cddf9SRui Paulo sizeof(*msg->attr_pos)); 69639beb93cSSam Leffler if (nattr_pos == NULL) 69739beb93cSSam Leffler return -1; 69839beb93cSSam Leffler 69939beb93cSSam Leffler msg->attr_pos = nattr_pos; 70039beb93cSSam Leffler msg->attr_size = nlen; 70139beb93cSSam Leffler } 70239beb93cSSam Leffler 703e28a4053SRui Paulo msg->attr_pos[msg->attr_used++] = 704e28a4053SRui Paulo (unsigned char *) attr - wpabuf_head_u8(msg->buf); 70539beb93cSSam Leffler 70639beb93cSSam Leffler return 0; 70739beb93cSSam Leffler } 70839beb93cSSam Leffler 70939beb93cSSam Leffler 710*a90b9d01SCy Schubert struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u16 type, 71139beb93cSSam Leffler const u8 *data, size_t data_len) 71239beb93cSSam Leffler { 713*a90b9d01SCy Schubert size_t buf_needed, max_len; 714*a90b9d01SCy Schubert struct radius_attr_hdr *attr = NULL; 715*a90b9d01SCy Schubert struct radius_attr_hdr_ext *ext; 716*a90b9d01SCy Schubert u8 ext_type = 0; 71739beb93cSSam Leffler 71885732ac8SCy Schubert if (TEST_FAIL()) 71985732ac8SCy Schubert return NULL; 72085732ac8SCy Schubert 721*a90b9d01SCy Schubert if (type > 255) { 722*a90b9d01SCy Schubert if (!radius_is_ext_type(type >> 8)) { 723*a90b9d01SCy Schubert wpa_printf(MSG_ERROR, 724*a90b9d01SCy Schubert "%s: Undefined extended type %d.%d", 725*a90b9d01SCy Schubert __func__, type >> 8, type & 0xff); 72639beb93cSSam Leffler return NULL; 72739beb93cSSam Leffler } 728*a90b9d01SCy Schubert ext_type = type & 0xff; 729*a90b9d01SCy Schubert type >>= 8; 730*a90b9d01SCy Schubert } else if (radius_is_ext_type(type)) { 731*a90b9d01SCy Schubert wpa_printf(MSG_ERROR, "%s: Unexpected extended type use for %d", 732*a90b9d01SCy Schubert __func__, type); 733*a90b9d01SCy Schubert } 73439beb93cSSam Leffler 735*a90b9d01SCy Schubert if (radius_is_long_ext_type(type)) { 736*a90b9d01SCy Schubert size_t hdr_len = sizeof(struct radius_attr_hdr_ext) + 1; 737*a90b9d01SCy Schubert size_t plen = 255 - hdr_len; 738*a90b9d01SCy Schubert size_t num; 739*a90b9d01SCy Schubert 740*a90b9d01SCy Schubert max_len = 4096; 741*a90b9d01SCy Schubert num = (data_len + plen - 1) / plen; 742*a90b9d01SCy Schubert if (num == 0) 743*a90b9d01SCy Schubert num = 1; 744*a90b9d01SCy Schubert buf_needed = num * hdr_len + data_len; 745*a90b9d01SCy Schubert } else if (radius_is_ext_type(type)) { 746*a90b9d01SCy Schubert max_len = RADIUS_MAX_EXT_ATTR_LEN; 747*a90b9d01SCy Schubert buf_needed = sizeof(struct radius_attr_hdr_ext) + data_len; 748*a90b9d01SCy Schubert } else { 749*a90b9d01SCy Schubert max_len = RADIUS_MAX_ATTR_LEN; 750e28a4053SRui Paulo buf_needed = sizeof(*attr) + data_len; 751*a90b9d01SCy Schubert } 752*a90b9d01SCy Schubert if (data_len > max_len) { 753*a90b9d01SCy Schubert wpa_printf(MSG_ERROR, 754*a90b9d01SCy Schubert "%s: too long attribute (%zu > %zu bytes)", 755*a90b9d01SCy Schubert __func__, data_len, max_len); 756*a90b9d01SCy Schubert return NULL; 757*a90b9d01SCy Schubert } 75839beb93cSSam Leffler 759e28a4053SRui Paulo if (wpabuf_tailroom(msg->buf) < buf_needed) { 76039beb93cSSam Leffler /* allocate more space for message buffer */ 761e28a4053SRui Paulo if (wpabuf_resize(&msg->buf, buf_needed) < 0) 76239beb93cSSam Leffler return NULL; 763e28a4053SRui Paulo msg->hdr = wpabuf_mhead(msg->buf); 76439beb93cSSam Leffler } 76539beb93cSSam Leffler 766*a90b9d01SCy Schubert if (radius_is_long_ext_type(type)) { 767*a90b9d01SCy Schubert size_t plen = 255 - sizeof(struct radius_attr_hdr_ext) - 1; 768*a90b9d01SCy Schubert size_t alen; 769*a90b9d01SCy Schubert 770*a90b9d01SCy Schubert do { 771*a90b9d01SCy Schubert alen = data_len > plen ? plen : data_len; 772*a90b9d01SCy Schubert ext = wpabuf_put(msg->buf, 773*a90b9d01SCy Schubert sizeof(struct radius_attr_hdr_ext)); 774*a90b9d01SCy Schubert if (!attr) 775*a90b9d01SCy Schubert attr = (struct radius_attr_hdr *) ext; 776*a90b9d01SCy Schubert ext->type = type; 777*a90b9d01SCy Schubert ext->length = sizeof(*ext) + 1 + alen; 778*a90b9d01SCy Schubert ext->ext_type = ext_type; 779*a90b9d01SCy Schubert wpabuf_put_u8(msg->buf, data_len > alen ? 0x80 : 0); 780*a90b9d01SCy Schubert wpabuf_put_data(msg->buf, data, data_len); 781*a90b9d01SCy Schubert data += alen; 782*a90b9d01SCy Schubert data_len -= alen; 783*a90b9d01SCy Schubert if (radius_msg_add_attr_to_array( 784*a90b9d01SCy Schubert msg, (struct radius_attr_hdr *) ext)) 785*a90b9d01SCy Schubert return NULL; 786*a90b9d01SCy Schubert } while (data_len > 0); 787*a90b9d01SCy Schubert } else if (radius_is_ext_type(type)) { 788*a90b9d01SCy Schubert ext = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr_ext)); 789*a90b9d01SCy Schubert attr = (struct radius_attr_hdr *) ext; 790*a90b9d01SCy Schubert ext->type = type; 791*a90b9d01SCy Schubert ext->length = sizeof(*ext) + data_len; 792*a90b9d01SCy Schubert ext->ext_type = ext_type; 793*a90b9d01SCy Schubert wpabuf_put_data(msg->buf, data, data_len); 794*a90b9d01SCy Schubert if (radius_msg_add_attr_to_array(msg, attr)) 795*a90b9d01SCy Schubert return NULL; 796*a90b9d01SCy Schubert } else { 797e28a4053SRui Paulo attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr)); 79839beb93cSSam Leffler attr->type = type; 79939beb93cSSam Leffler attr->length = sizeof(*attr) + data_len; 800e28a4053SRui Paulo wpabuf_put_data(msg->buf, data, data_len); 80139beb93cSSam Leffler if (radius_msg_add_attr_to_array(msg, attr)) 80239beb93cSSam Leffler return NULL; 803*a90b9d01SCy Schubert } 80439beb93cSSam Leffler 80539beb93cSSam Leffler return attr; 80639beb93cSSam Leffler } 80739beb93cSSam Leffler 80839beb93cSSam Leffler 809e28a4053SRui Paulo /** 810e28a4053SRui Paulo * radius_msg_parse - Parse a RADIUS message 811e28a4053SRui Paulo * @data: RADIUS message to be parsed 812e28a4053SRui Paulo * @len: Length of data buffer in octets 813e28a4053SRui Paulo * Returns: Parsed RADIUS message or %NULL on failure 814e28a4053SRui Paulo * 815e28a4053SRui Paulo * This parses a RADIUS message and makes a copy of its data. The caller is 816e28a4053SRui Paulo * responsible for freeing the returned data with radius_msg_free(). 817e28a4053SRui Paulo */ 81839beb93cSSam Leffler struct radius_msg * radius_msg_parse(const u8 *data, size_t len) 81939beb93cSSam Leffler { 82039beb93cSSam Leffler struct radius_msg *msg; 82139beb93cSSam Leffler struct radius_hdr *hdr; 82239beb93cSSam Leffler struct radius_attr_hdr *attr; 82339beb93cSSam Leffler size_t msg_len; 82439beb93cSSam Leffler unsigned char *pos, *end; 82539beb93cSSam Leffler 82639beb93cSSam Leffler if (data == NULL || len < sizeof(*hdr)) 82739beb93cSSam Leffler return NULL; 82839beb93cSSam Leffler 82939beb93cSSam Leffler hdr = (struct radius_hdr *) data; 83039beb93cSSam Leffler 831f05cddf9SRui Paulo msg_len = be_to_host16(hdr->length); 83239beb93cSSam Leffler if (msg_len < sizeof(*hdr) || msg_len > len) { 833e28a4053SRui Paulo wpa_printf(MSG_INFO, "RADIUS: Invalid message length"); 83439beb93cSSam Leffler return NULL; 83539beb93cSSam Leffler } 83639beb93cSSam Leffler 83739beb93cSSam Leffler if (msg_len < len) { 838e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "RADIUS: Ignored %lu extra bytes after " 839e28a4053SRui Paulo "RADIUS message", (unsigned long) len - msg_len); 84039beb93cSSam Leffler } 84139beb93cSSam Leffler 842e28a4053SRui Paulo msg = os_zalloc(sizeof(*msg)); 84339beb93cSSam Leffler if (msg == NULL) 84439beb93cSSam Leffler return NULL; 84539beb93cSSam Leffler 846e28a4053SRui Paulo msg->buf = wpabuf_alloc_copy(data, msg_len); 847e28a4053SRui Paulo if (msg->buf == NULL || radius_msg_initialize(msg)) { 848e28a4053SRui Paulo radius_msg_free(msg); 84939beb93cSSam Leffler return NULL; 85039beb93cSSam Leffler } 851e28a4053SRui Paulo msg->hdr = wpabuf_mhead(msg->buf); 85239beb93cSSam Leffler 85339beb93cSSam Leffler /* parse attributes */ 854e28a4053SRui Paulo pos = wpabuf_mhead_u8(msg->buf) + sizeof(struct radius_hdr); 855e28a4053SRui Paulo end = wpabuf_mhead_u8(msg->buf) + wpabuf_len(msg->buf); 85639beb93cSSam Leffler while (pos < end) { 85739beb93cSSam Leffler if ((size_t) (end - pos) < sizeof(*attr)) 85839beb93cSSam Leffler goto fail; 85939beb93cSSam Leffler 86039beb93cSSam Leffler attr = (struct radius_attr_hdr *) pos; 86139beb93cSSam Leffler 862780fb4a2SCy Schubert if (attr->length > end - pos || attr->length < sizeof(*attr)) 86339beb93cSSam Leffler goto fail; 86439beb93cSSam Leffler 86539beb93cSSam Leffler /* TODO: check that attr->length is suitable for attr->type */ 86639beb93cSSam Leffler 86739beb93cSSam Leffler if (radius_msg_add_attr_to_array(msg, attr)) 86839beb93cSSam Leffler goto fail; 86939beb93cSSam Leffler 87039beb93cSSam Leffler pos += attr->length; 87139beb93cSSam Leffler } 87239beb93cSSam Leffler 87339beb93cSSam Leffler return msg; 87439beb93cSSam Leffler 87539beb93cSSam Leffler fail: 87639beb93cSSam Leffler radius_msg_free(msg); 87739beb93cSSam Leffler return NULL; 87839beb93cSSam Leffler } 87939beb93cSSam Leffler 88039beb93cSSam Leffler 88139beb93cSSam Leffler int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len) 88239beb93cSSam Leffler { 88339beb93cSSam Leffler const u8 *pos = data; 88439beb93cSSam Leffler size_t left = data_len; 88539beb93cSSam Leffler 88639beb93cSSam Leffler while (left > 0) { 88739beb93cSSam Leffler int len; 88839beb93cSSam Leffler if (left > RADIUS_MAX_ATTR_LEN) 88939beb93cSSam Leffler len = RADIUS_MAX_ATTR_LEN; 89039beb93cSSam Leffler else 89139beb93cSSam Leffler len = left; 89239beb93cSSam Leffler 89339beb93cSSam Leffler if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE, 89439beb93cSSam Leffler pos, len)) 89539beb93cSSam Leffler return 0; 89639beb93cSSam Leffler 89739beb93cSSam Leffler pos += len; 89839beb93cSSam Leffler left -= len; 89939beb93cSSam Leffler } 90039beb93cSSam Leffler 90139beb93cSSam Leffler return 1; 90239beb93cSSam Leffler } 90339beb93cSSam Leffler 90439beb93cSSam Leffler 905f05cddf9SRui Paulo struct wpabuf * radius_msg_get_eap(struct radius_msg *msg) 90639beb93cSSam Leffler { 907f05cddf9SRui Paulo struct wpabuf *eap; 90839beb93cSSam Leffler size_t len, i; 90939beb93cSSam Leffler struct radius_attr_hdr *attr; 91039beb93cSSam Leffler 91139beb93cSSam Leffler if (msg == NULL) 91239beb93cSSam Leffler return NULL; 91339beb93cSSam Leffler 91439beb93cSSam Leffler len = 0; 91539beb93cSSam Leffler for (i = 0; i < msg->attr_used; i++) { 91639beb93cSSam Leffler attr = radius_get_attr_hdr(msg, i); 917f05cddf9SRui Paulo if (attr->type == RADIUS_ATTR_EAP_MESSAGE && 918f05cddf9SRui Paulo attr->length > sizeof(struct radius_attr_hdr)) 91939beb93cSSam Leffler len += attr->length - sizeof(struct radius_attr_hdr); 92039beb93cSSam Leffler } 92139beb93cSSam Leffler 92239beb93cSSam Leffler if (len == 0) 92339beb93cSSam Leffler return NULL; 92439beb93cSSam Leffler 925f05cddf9SRui Paulo eap = wpabuf_alloc(len); 92639beb93cSSam Leffler if (eap == NULL) 92739beb93cSSam Leffler return NULL; 92839beb93cSSam Leffler 92939beb93cSSam Leffler for (i = 0; i < msg->attr_used; i++) { 93039beb93cSSam Leffler attr = radius_get_attr_hdr(msg, i); 931f05cddf9SRui Paulo if (attr->type == RADIUS_ATTR_EAP_MESSAGE && 932f05cddf9SRui Paulo attr->length > sizeof(struct radius_attr_hdr)) { 93339beb93cSSam Leffler int flen = attr->length - sizeof(*attr); 934f05cddf9SRui Paulo wpabuf_put_data(eap, attr + 1, flen); 93539beb93cSSam Leffler } 93639beb93cSSam Leffler } 93739beb93cSSam Leffler 93839beb93cSSam Leffler return eap; 93939beb93cSSam Leffler } 94039beb93cSSam Leffler 94139beb93cSSam Leffler 94239beb93cSSam Leffler int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, 94339beb93cSSam Leffler size_t secret_len, const u8 *req_auth) 94439beb93cSSam Leffler { 94539beb93cSSam Leffler u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN]; 94639beb93cSSam Leffler u8 orig_authenticator[16]; 94739beb93cSSam Leffler struct radius_attr_hdr *attr = NULL, *tmp; 94839beb93cSSam Leffler size_t i; 94939beb93cSSam Leffler 95039beb93cSSam Leffler for (i = 0; i < msg->attr_used; i++) { 95139beb93cSSam Leffler tmp = radius_get_attr_hdr(msg, i); 95239beb93cSSam Leffler if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { 95339beb93cSSam Leffler if (attr != NULL) { 9545b9c547cSRui Paulo wpa_printf(MSG_INFO, "Multiple Message-Authenticator attributes in RADIUS message"); 95539beb93cSSam Leffler return 1; 95639beb93cSSam Leffler } 95739beb93cSSam Leffler attr = tmp; 95839beb93cSSam Leffler } 95939beb93cSSam Leffler } 96039beb93cSSam Leffler 96139beb93cSSam Leffler if (attr == NULL) { 9625b9c547cSRui Paulo wpa_printf(MSG_INFO, "No Message-Authenticator attribute found"); 96339beb93cSSam Leffler return 1; 96439beb93cSSam Leffler } 96539beb93cSSam Leffler 96639beb93cSSam Leffler os_memcpy(orig, attr + 1, MD5_MAC_LEN); 96739beb93cSSam Leffler os_memset(attr + 1, 0, MD5_MAC_LEN); 96839beb93cSSam Leffler if (req_auth) { 96939beb93cSSam Leffler os_memcpy(orig_authenticator, msg->hdr->authenticator, 97039beb93cSSam Leffler sizeof(orig_authenticator)); 97139beb93cSSam Leffler os_memcpy(msg->hdr->authenticator, req_auth, 97239beb93cSSam Leffler sizeof(msg->hdr->authenticator)); 97339beb93cSSam Leffler } 974780fb4a2SCy Schubert if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf), 975780fb4a2SCy Schubert wpabuf_len(msg->buf), auth) < 0) 976780fb4a2SCy Schubert return 1; 97739beb93cSSam Leffler os_memcpy(attr + 1, orig, MD5_MAC_LEN); 97839beb93cSSam Leffler if (req_auth) { 97939beb93cSSam Leffler os_memcpy(msg->hdr->authenticator, orig_authenticator, 98039beb93cSSam Leffler sizeof(orig_authenticator)); 98139beb93cSSam Leffler } 98239beb93cSSam Leffler 9835b9c547cSRui Paulo if (os_memcmp_const(orig, auth, MD5_MAC_LEN) != 0) { 9845b9c547cSRui Paulo wpa_printf(MSG_INFO, "Invalid Message-Authenticator!"); 98539beb93cSSam Leffler return 1; 98639beb93cSSam Leffler } 98739beb93cSSam Leffler 98839beb93cSSam Leffler return 0; 98939beb93cSSam Leffler } 99039beb93cSSam Leffler 99139beb93cSSam Leffler 99239beb93cSSam Leffler int radius_msg_verify(struct radius_msg *msg, const u8 *secret, 99339beb93cSSam Leffler size_t secret_len, struct radius_msg *sent_msg, int auth) 99439beb93cSSam Leffler { 99539beb93cSSam Leffler const u8 *addr[4]; 99639beb93cSSam Leffler size_t len[4]; 99739beb93cSSam Leffler u8 hash[MD5_MAC_LEN]; 99839beb93cSSam Leffler 99939beb93cSSam Leffler if (sent_msg == NULL) { 10005b9c547cSRui Paulo wpa_printf(MSG_INFO, "No matching Access-Request message found"); 100139beb93cSSam Leffler return 1; 100239beb93cSSam Leffler } 100339beb93cSSam Leffler 1004*a90b9d01SCy Schubert if (!auth) { 1005*a90b9d01SCy Schubert u8 *pos; 1006*a90b9d01SCy Schubert size_t alen; 1007*a90b9d01SCy Schubert 1008*a90b9d01SCy Schubert if (radius_msg_get_attr_ptr(msg, 1009*a90b9d01SCy Schubert RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 1010*a90b9d01SCy Schubert &pos, &alen, NULL) == 0) { 1011*a90b9d01SCy Schubert /* Check the Message-Authenticator attribute since it 1012*a90b9d01SCy Schubert * was included even if we are configured to not 1013*a90b9d01SCy Schubert * require it. */ 1014*a90b9d01SCy Schubert auth = 1; 1015*a90b9d01SCy Schubert } 1016*a90b9d01SCy Schubert } 1017*a90b9d01SCy Schubert 101839beb93cSSam Leffler if (auth && 101939beb93cSSam Leffler radius_msg_verify_msg_auth(msg, secret, secret_len, 102039beb93cSSam Leffler sent_msg->hdr->authenticator)) { 102139beb93cSSam Leffler return 1; 102239beb93cSSam Leffler } 102339beb93cSSam Leffler 102439beb93cSSam Leffler /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ 102539beb93cSSam Leffler addr[0] = (u8 *) msg->hdr; 102639beb93cSSam Leffler len[0] = 1 + 1 + 2; 102739beb93cSSam Leffler addr[1] = sent_msg->hdr->authenticator; 102839beb93cSSam Leffler len[1] = MD5_MAC_LEN; 1029e28a4053SRui Paulo addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr); 1030e28a4053SRui Paulo len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); 103139beb93cSSam Leffler addr[3] = secret; 103239beb93cSSam Leffler len[3] = secret_len; 1033780fb4a2SCy Schubert if (md5_vector(4, addr, len, hash) < 0 || 1034780fb4a2SCy Schubert os_memcmp_const(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) { 10355b9c547cSRui Paulo wpa_printf(MSG_INFO, "Response Authenticator invalid!"); 103639beb93cSSam Leffler return 1; 103739beb93cSSam Leffler } 103839beb93cSSam Leffler 103939beb93cSSam Leffler return 0; 104039beb93cSSam Leffler } 104139beb93cSSam Leffler 104239beb93cSSam Leffler 104339beb93cSSam Leffler int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, 104439beb93cSSam Leffler u8 type) 104539beb93cSSam Leffler { 104639beb93cSSam Leffler struct radius_attr_hdr *attr; 104739beb93cSSam Leffler size_t i; 104839beb93cSSam Leffler int count = 0; 104939beb93cSSam Leffler 105039beb93cSSam Leffler for (i = 0; i < src->attr_used; i++) { 105139beb93cSSam Leffler attr = radius_get_attr_hdr(src, i); 1052f05cddf9SRui Paulo if (attr->type == type && attr->length >= sizeof(*attr)) { 105339beb93cSSam Leffler if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1), 105439beb93cSSam Leffler attr->length - sizeof(*attr))) 105539beb93cSSam Leffler return -1; 105639beb93cSSam Leffler count++; 105739beb93cSSam Leffler } 105839beb93cSSam Leffler } 105939beb93cSSam Leffler 106039beb93cSSam Leffler return count; 106139beb93cSSam Leffler } 106239beb93cSSam Leffler 106339beb93cSSam Leffler 106439beb93cSSam Leffler /* Create Request Authenticator. The value should be unique over the lifetime 106539beb93cSSam Leffler * of the shared secret between authenticator and authentication server. 1066780fb4a2SCy Schubert */ 1067780fb4a2SCy Schubert int radius_msg_make_authenticator(struct radius_msg *msg) 106839beb93cSSam Leffler { 1069780fb4a2SCy Schubert return os_get_random((u8 *) &msg->hdr->authenticator, 1070780fb4a2SCy Schubert sizeof(msg->hdr->authenticator)); 107139beb93cSSam Leffler } 107239beb93cSSam Leffler 107339beb93cSSam Leffler 107439beb93cSSam Leffler /* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message. 107539beb93cSSam Leffler * Returns the Attribute payload and sets alen to indicate the length of the 107639beb93cSSam Leffler * payload if a vendor attribute with subtype is found, otherwise returns NULL. 107739beb93cSSam Leffler * The returned payload is allocated with os_malloc() and caller must free it 107839beb93cSSam Leffler * by calling os_free(). 107939beb93cSSam Leffler */ 108039beb93cSSam Leffler static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor, 108139beb93cSSam Leffler u8 subtype, size_t *alen) 108239beb93cSSam Leffler { 108339beb93cSSam Leffler u8 *data, *pos; 108439beb93cSSam Leffler size_t i, len; 108539beb93cSSam Leffler 108639beb93cSSam Leffler if (msg == NULL) 108739beb93cSSam Leffler return NULL; 108839beb93cSSam Leffler 108939beb93cSSam Leffler for (i = 0; i < msg->attr_used; i++) { 109039beb93cSSam Leffler struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); 109139beb93cSSam Leffler size_t left; 109239beb93cSSam Leffler u32 vendor_id; 109339beb93cSSam Leffler struct radius_attr_vendor *vhdr; 109439beb93cSSam Leffler 1095f05cddf9SRui Paulo if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC || 1096f05cddf9SRui Paulo attr->length < sizeof(*attr)) 109739beb93cSSam Leffler continue; 109839beb93cSSam Leffler 109939beb93cSSam Leffler left = attr->length - sizeof(*attr); 110039beb93cSSam Leffler if (left < 4) 110139beb93cSSam Leffler continue; 110239beb93cSSam Leffler 110339beb93cSSam Leffler pos = (u8 *) (attr + 1); 110439beb93cSSam Leffler 110539beb93cSSam Leffler os_memcpy(&vendor_id, pos, 4); 110639beb93cSSam Leffler pos += 4; 110739beb93cSSam Leffler left -= 4; 110839beb93cSSam Leffler 110939beb93cSSam Leffler if (ntohl(vendor_id) != vendor) 111039beb93cSSam Leffler continue; 111139beb93cSSam Leffler 111239beb93cSSam Leffler while (left >= sizeof(*vhdr)) { 111339beb93cSSam Leffler vhdr = (struct radius_attr_vendor *) pos; 111439beb93cSSam Leffler if (vhdr->vendor_length > left || 111539beb93cSSam Leffler vhdr->vendor_length < sizeof(*vhdr)) { 111639beb93cSSam Leffler break; 111739beb93cSSam Leffler } 111839beb93cSSam Leffler if (vhdr->vendor_type != subtype) { 111939beb93cSSam Leffler pos += vhdr->vendor_length; 112039beb93cSSam Leffler left -= vhdr->vendor_length; 112139beb93cSSam Leffler continue; 112239beb93cSSam Leffler } 112339beb93cSSam Leffler 112439beb93cSSam Leffler len = vhdr->vendor_length - sizeof(*vhdr); 112585732ac8SCy Schubert data = os_memdup(pos + sizeof(*vhdr), len); 112639beb93cSSam Leffler if (data == NULL) 112739beb93cSSam Leffler return NULL; 112839beb93cSSam Leffler if (alen) 112939beb93cSSam Leffler *alen = len; 113039beb93cSSam Leffler return data; 113139beb93cSSam Leffler } 113239beb93cSSam Leffler } 113339beb93cSSam Leffler 113439beb93cSSam Leffler return NULL; 113539beb93cSSam Leffler } 113639beb93cSSam Leffler 113739beb93cSSam Leffler 113839beb93cSSam Leffler static u8 * decrypt_ms_key(const u8 *key, size_t len, 113939beb93cSSam Leffler const u8 *req_authenticator, 114039beb93cSSam Leffler const u8 *secret, size_t secret_len, size_t *reslen) 114139beb93cSSam Leffler { 114239beb93cSSam Leffler u8 *plain, *ppos, *res; 114339beb93cSSam Leffler const u8 *pos; 114439beb93cSSam Leffler size_t left, plen; 114539beb93cSSam Leffler u8 hash[MD5_MAC_LEN]; 114639beb93cSSam Leffler int i, first = 1; 114739beb93cSSam Leffler const u8 *addr[3]; 114839beb93cSSam Leffler size_t elen[3]; 114939beb93cSSam Leffler 115039beb93cSSam Leffler /* key: 16-bit salt followed by encrypted key info */ 115139beb93cSSam Leffler 11525b9c547cSRui Paulo if (len < 2 + 16) { 11535b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "RADIUS: %s: Len is too small: %d", 11545b9c547cSRui Paulo __func__, (int) len); 115539beb93cSSam Leffler return NULL; 11565b9c547cSRui Paulo } 115739beb93cSSam Leffler 115839beb93cSSam Leffler pos = key + 2; 115939beb93cSSam Leffler left = len - 2; 116039beb93cSSam Leffler if (left % 16) { 11615b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: Invalid ms key len %lu", 11625b9c547cSRui Paulo (unsigned long) left); 116339beb93cSSam Leffler return NULL; 116439beb93cSSam Leffler } 116539beb93cSSam Leffler 116639beb93cSSam Leffler plen = left; 116739beb93cSSam Leffler ppos = plain = os_malloc(plen); 116839beb93cSSam Leffler if (plain == NULL) 116939beb93cSSam Leffler return NULL; 117039beb93cSSam Leffler plain[0] = 0; 117139beb93cSSam Leffler 117239beb93cSSam Leffler while (left > 0) { 117339beb93cSSam Leffler /* b(1) = MD5(Secret + Request-Authenticator + Salt) 117439beb93cSSam Leffler * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ 117539beb93cSSam Leffler 117639beb93cSSam Leffler addr[0] = secret; 117739beb93cSSam Leffler elen[0] = secret_len; 117839beb93cSSam Leffler if (first) { 117939beb93cSSam Leffler addr[1] = req_authenticator; 118039beb93cSSam Leffler elen[1] = MD5_MAC_LEN; 118139beb93cSSam Leffler addr[2] = key; 118239beb93cSSam Leffler elen[2] = 2; /* Salt */ 118339beb93cSSam Leffler } else { 118439beb93cSSam Leffler addr[1] = pos - MD5_MAC_LEN; 118539beb93cSSam Leffler elen[1] = MD5_MAC_LEN; 118639beb93cSSam Leffler } 1187780fb4a2SCy Schubert if (md5_vector(first ? 3 : 2, addr, elen, hash) < 0) { 1188780fb4a2SCy Schubert os_free(plain); 1189780fb4a2SCy Schubert return NULL; 1190780fb4a2SCy Schubert } 119139beb93cSSam Leffler first = 0; 119239beb93cSSam Leffler 119339beb93cSSam Leffler for (i = 0; i < MD5_MAC_LEN; i++) 119439beb93cSSam Leffler *ppos++ = *pos++ ^ hash[i]; 119539beb93cSSam Leffler left -= MD5_MAC_LEN; 119639beb93cSSam Leffler } 119739beb93cSSam Leffler 119839beb93cSSam Leffler if (plain[0] == 0 || plain[0] > plen - 1) { 11995b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: Failed to decrypt MPPE key"); 120039beb93cSSam Leffler os_free(plain); 120139beb93cSSam Leffler return NULL; 120239beb93cSSam Leffler } 120339beb93cSSam Leffler 120485732ac8SCy Schubert res = os_memdup(plain + 1, plain[0]); 120539beb93cSSam Leffler if (res == NULL) { 120639beb93cSSam Leffler os_free(plain); 120739beb93cSSam Leffler return NULL; 120839beb93cSSam Leffler } 120939beb93cSSam Leffler if (reslen) 121039beb93cSSam Leffler *reslen = plain[0]; 121139beb93cSSam Leffler os_free(plain); 121239beb93cSSam Leffler return res; 121339beb93cSSam Leffler } 121439beb93cSSam Leffler 121539beb93cSSam Leffler 121639beb93cSSam Leffler static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt, 121739beb93cSSam Leffler const u8 *req_authenticator, 121839beb93cSSam Leffler const u8 *secret, size_t secret_len, 121939beb93cSSam Leffler u8 *ebuf, size_t *elen) 122039beb93cSSam Leffler { 122139beb93cSSam Leffler int i, len, first = 1; 122239beb93cSSam Leffler u8 hash[MD5_MAC_LEN], saltbuf[2], *pos; 122339beb93cSSam Leffler const u8 *addr[3]; 122439beb93cSSam Leffler size_t _len[3]; 122539beb93cSSam Leffler 122639beb93cSSam Leffler WPA_PUT_BE16(saltbuf, salt); 122739beb93cSSam Leffler 122839beb93cSSam Leffler len = 1 + key_len; 122939beb93cSSam Leffler if (len & 0x0f) { 123039beb93cSSam Leffler len = (len & 0xf0) + 16; 123139beb93cSSam Leffler } 123239beb93cSSam Leffler os_memset(ebuf, 0, len); 123339beb93cSSam Leffler ebuf[0] = key_len; 123439beb93cSSam Leffler os_memcpy(ebuf + 1, key, key_len); 123539beb93cSSam Leffler 123639beb93cSSam Leffler *elen = len; 123739beb93cSSam Leffler 123839beb93cSSam Leffler pos = ebuf; 123939beb93cSSam Leffler while (len > 0) { 124039beb93cSSam Leffler /* b(1) = MD5(Secret + Request-Authenticator + Salt) 124139beb93cSSam Leffler * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ 124239beb93cSSam Leffler addr[0] = secret; 124339beb93cSSam Leffler _len[0] = secret_len; 124439beb93cSSam Leffler if (first) { 124539beb93cSSam Leffler addr[1] = req_authenticator; 124639beb93cSSam Leffler _len[1] = MD5_MAC_LEN; 124739beb93cSSam Leffler addr[2] = saltbuf; 124839beb93cSSam Leffler _len[2] = sizeof(saltbuf); 124939beb93cSSam Leffler } else { 125039beb93cSSam Leffler addr[1] = pos - MD5_MAC_LEN; 125139beb93cSSam Leffler _len[1] = MD5_MAC_LEN; 125239beb93cSSam Leffler } 125339beb93cSSam Leffler md5_vector(first ? 3 : 2, addr, _len, hash); 125439beb93cSSam Leffler first = 0; 125539beb93cSSam Leffler 125639beb93cSSam Leffler for (i = 0; i < MD5_MAC_LEN; i++) 125739beb93cSSam Leffler *pos++ ^= hash[i]; 125839beb93cSSam Leffler 125939beb93cSSam Leffler len -= MD5_MAC_LEN; 126039beb93cSSam Leffler } 126139beb93cSSam Leffler } 126239beb93cSSam Leffler 126339beb93cSSam Leffler 126439beb93cSSam Leffler struct radius_ms_mppe_keys * 126539beb93cSSam Leffler radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, 126639beb93cSSam Leffler const u8 *secret, size_t secret_len) 126739beb93cSSam Leffler { 126839beb93cSSam Leffler u8 *key; 126939beb93cSSam Leffler size_t keylen; 127039beb93cSSam Leffler struct radius_ms_mppe_keys *keys; 127139beb93cSSam Leffler 127239beb93cSSam Leffler if (msg == NULL || sent_msg == NULL) 127339beb93cSSam Leffler return NULL; 127439beb93cSSam Leffler 127539beb93cSSam Leffler keys = os_zalloc(sizeof(*keys)); 127639beb93cSSam Leffler if (keys == NULL) 127739beb93cSSam Leffler return NULL; 127839beb93cSSam Leffler 127939beb93cSSam Leffler key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT, 128039beb93cSSam Leffler RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY, 128139beb93cSSam Leffler &keylen); 128239beb93cSSam Leffler if (key) { 128339beb93cSSam Leffler keys->send = decrypt_ms_key(key, keylen, 128439beb93cSSam Leffler sent_msg->hdr->authenticator, 128539beb93cSSam Leffler secret, secret_len, 128639beb93cSSam Leffler &keys->send_len); 12875b9c547cSRui Paulo if (!keys->send) { 12885b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 12895b9c547cSRui Paulo "RADIUS: Failed to decrypt send key"); 12905b9c547cSRui Paulo } 129139beb93cSSam Leffler os_free(key); 129239beb93cSSam Leffler } 129339beb93cSSam Leffler 129439beb93cSSam Leffler key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT, 129539beb93cSSam Leffler RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY, 129639beb93cSSam Leffler &keylen); 129739beb93cSSam Leffler if (key) { 129839beb93cSSam Leffler keys->recv = decrypt_ms_key(key, keylen, 129939beb93cSSam Leffler sent_msg->hdr->authenticator, 130039beb93cSSam Leffler secret, secret_len, 130139beb93cSSam Leffler &keys->recv_len); 13025b9c547cSRui Paulo if (!keys->recv) { 13035b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 13045b9c547cSRui Paulo "RADIUS: Failed to decrypt recv key"); 13055b9c547cSRui Paulo } 130639beb93cSSam Leffler os_free(key); 130739beb93cSSam Leffler } 130839beb93cSSam Leffler 130939beb93cSSam Leffler return keys; 131039beb93cSSam Leffler } 131139beb93cSSam Leffler 131239beb93cSSam Leffler 131339beb93cSSam Leffler struct radius_ms_mppe_keys * 131439beb93cSSam Leffler radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg, 131539beb93cSSam Leffler const u8 *secret, size_t secret_len) 131639beb93cSSam Leffler { 131739beb93cSSam Leffler u8 *key; 131839beb93cSSam Leffler size_t keylen; 131939beb93cSSam Leffler struct radius_ms_mppe_keys *keys; 132039beb93cSSam Leffler 132139beb93cSSam Leffler if (msg == NULL || sent_msg == NULL) 132239beb93cSSam Leffler return NULL; 132339beb93cSSam Leffler 132439beb93cSSam Leffler keys = os_zalloc(sizeof(*keys)); 132539beb93cSSam Leffler if (keys == NULL) 132639beb93cSSam Leffler return NULL; 132739beb93cSSam Leffler 132839beb93cSSam Leffler key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO, 132939beb93cSSam Leffler RADIUS_CISCO_AV_PAIR, &keylen); 133039beb93cSSam Leffler if (key && keylen == 51 && 133139beb93cSSam Leffler os_memcmp(key, "leap:session-key=", 17) == 0) { 133239beb93cSSam Leffler keys->recv = decrypt_ms_key(key + 17, keylen - 17, 133339beb93cSSam Leffler sent_msg->hdr->authenticator, 133439beb93cSSam Leffler secret, secret_len, 133539beb93cSSam Leffler &keys->recv_len); 133639beb93cSSam Leffler } 133739beb93cSSam Leffler os_free(key); 133839beb93cSSam Leffler 133939beb93cSSam Leffler return keys; 134039beb93cSSam Leffler } 134139beb93cSSam Leffler 134239beb93cSSam Leffler 134339beb93cSSam Leffler int radius_msg_add_mppe_keys(struct radius_msg *msg, 134439beb93cSSam Leffler const u8 *req_authenticator, 134539beb93cSSam Leffler const u8 *secret, size_t secret_len, 134639beb93cSSam Leffler const u8 *send_key, size_t send_key_len, 134739beb93cSSam Leffler const u8 *recv_key, size_t recv_key_len) 134839beb93cSSam Leffler { 134939beb93cSSam Leffler struct radius_attr_hdr *attr; 135039beb93cSSam Leffler u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT); 135139beb93cSSam Leffler u8 *buf; 135239beb93cSSam Leffler struct radius_attr_vendor *vhdr; 135339beb93cSSam Leffler u8 *pos; 135439beb93cSSam Leffler size_t elen; 135539beb93cSSam Leffler int hlen; 135639beb93cSSam Leffler u16 salt; 135739beb93cSSam Leffler 135839beb93cSSam Leffler hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2; 135939beb93cSSam Leffler 136039beb93cSSam Leffler /* MS-MPPE-Send-Key */ 136139beb93cSSam Leffler buf = os_malloc(hlen + send_key_len + 16); 136239beb93cSSam Leffler if (buf == NULL) { 136339beb93cSSam Leffler return 0; 136439beb93cSSam Leffler } 136539beb93cSSam Leffler pos = buf; 136639beb93cSSam Leffler os_memcpy(pos, &vendor_id, sizeof(vendor_id)); 136739beb93cSSam Leffler pos += sizeof(vendor_id); 136839beb93cSSam Leffler vhdr = (struct radius_attr_vendor *) pos; 136939beb93cSSam Leffler vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY; 137039beb93cSSam Leffler pos = (u8 *) (vhdr + 1); 1371780fb4a2SCy Schubert if (os_get_random((u8 *) &salt, sizeof(salt)) < 0) { 1372780fb4a2SCy Schubert os_free(buf); 1373780fb4a2SCy Schubert return 0; 1374780fb4a2SCy Schubert } 1375780fb4a2SCy Schubert salt |= 0x8000; 137639beb93cSSam Leffler WPA_PUT_BE16(pos, salt); 137739beb93cSSam Leffler pos += 2; 137839beb93cSSam Leffler encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret, 137939beb93cSSam Leffler secret_len, pos, &elen); 138039beb93cSSam Leffler vhdr->vendor_length = hlen + elen - sizeof(vendor_id); 138139beb93cSSam Leffler 138239beb93cSSam Leffler attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, 138339beb93cSSam Leffler buf, hlen + elen); 138439beb93cSSam Leffler os_free(buf); 138539beb93cSSam Leffler if (attr == NULL) { 138639beb93cSSam Leffler return 0; 138739beb93cSSam Leffler } 138839beb93cSSam Leffler 138939beb93cSSam Leffler /* MS-MPPE-Recv-Key */ 1390325151a3SRui Paulo buf = os_malloc(hlen + recv_key_len + 16); 139139beb93cSSam Leffler if (buf == NULL) { 139239beb93cSSam Leffler return 0; 139339beb93cSSam Leffler } 139439beb93cSSam Leffler pos = buf; 139539beb93cSSam Leffler os_memcpy(pos, &vendor_id, sizeof(vendor_id)); 139639beb93cSSam Leffler pos += sizeof(vendor_id); 139739beb93cSSam Leffler vhdr = (struct radius_attr_vendor *) pos; 139839beb93cSSam Leffler vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY; 139939beb93cSSam Leffler pos = (u8 *) (vhdr + 1); 140039beb93cSSam Leffler salt ^= 1; 140139beb93cSSam Leffler WPA_PUT_BE16(pos, salt); 140239beb93cSSam Leffler pos += 2; 140339beb93cSSam Leffler encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret, 140439beb93cSSam Leffler secret_len, pos, &elen); 140539beb93cSSam Leffler vhdr->vendor_length = hlen + elen - sizeof(vendor_id); 140639beb93cSSam Leffler 140739beb93cSSam Leffler attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, 140839beb93cSSam Leffler buf, hlen + elen); 140939beb93cSSam Leffler os_free(buf); 141039beb93cSSam Leffler if (attr == NULL) { 141139beb93cSSam Leffler return 0; 141239beb93cSSam Leffler } 141339beb93cSSam Leffler 141439beb93cSSam Leffler return 1; 141539beb93cSSam Leffler } 141639beb93cSSam Leffler 141739beb93cSSam Leffler 14185b9c547cSRui Paulo int radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data, 14195b9c547cSRui Paulo size_t len) 142039beb93cSSam Leffler { 14215b9c547cSRui Paulo struct radius_attr_hdr *attr; 14225b9c547cSRui Paulo u8 *buf, *pos; 14235b9c547cSRui Paulo size_t alen; 14245b9c547cSRui Paulo 14255b9c547cSRui Paulo alen = 4 + 2 + len; 14265b9c547cSRui Paulo buf = os_malloc(alen); 14275b9c547cSRui Paulo if (buf == NULL) 14285b9c547cSRui Paulo return 0; 14295b9c547cSRui Paulo pos = buf; 14305b9c547cSRui Paulo WPA_PUT_BE32(pos, RADIUS_VENDOR_ID_WFA); 14315b9c547cSRui Paulo pos += 4; 14325b9c547cSRui Paulo *pos++ = subtype; 14335b9c547cSRui Paulo *pos++ = 2 + len; 14345b9c547cSRui Paulo os_memcpy(pos, data, len); 14355b9c547cSRui Paulo attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, 14365b9c547cSRui Paulo buf, alen); 14375b9c547cSRui Paulo os_free(buf); 14385b9c547cSRui Paulo if (attr == NULL) 14395b9c547cSRui Paulo return 0; 14405b9c547cSRui Paulo 14415b9c547cSRui Paulo return 1; 14425b9c547cSRui Paulo } 14435b9c547cSRui Paulo 14445b9c547cSRui Paulo 1445*a90b9d01SCy Schubert int radius_msg_add_ext_vs(struct radius_msg *msg, u16 type, u32 vendor_id, 1446*a90b9d01SCy Schubert u8 vendor_type, const u8 *data, size_t len) 1447*a90b9d01SCy Schubert { 1448*a90b9d01SCy Schubert struct radius_attr_hdr *attr; 1449*a90b9d01SCy Schubert u8 *buf, *pos; 1450*a90b9d01SCy Schubert size_t alen; 1451*a90b9d01SCy Schubert 1452*a90b9d01SCy Schubert alen = 4 + 1 + len; 1453*a90b9d01SCy Schubert buf = os_malloc(alen); 1454*a90b9d01SCy Schubert if (!buf) 1455*a90b9d01SCy Schubert return 0; 1456*a90b9d01SCy Schubert pos = buf; 1457*a90b9d01SCy Schubert WPA_PUT_BE32(pos, vendor_id); 1458*a90b9d01SCy Schubert pos += 4; 1459*a90b9d01SCy Schubert *pos++ = vendor_type; 1460*a90b9d01SCy Schubert os_memcpy(pos, data, len); 1461*a90b9d01SCy Schubert attr = radius_msg_add_attr(msg, type, buf, alen); 1462*a90b9d01SCy Schubert os_free(buf); 1463*a90b9d01SCy Schubert return attr != NULL; 1464*a90b9d01SCy Schubert } 1465*a90b9d01SCy Schubert 1466*a90b9d01SCy Schubert 14675b9c547cSRui Paulo int radius_user_password_hide(struct radius_msg *msg, 14685b9c547cSRui Paulo const u8 *data, size_t data_len, 14695b9c547cSRui Paulo const u8 *secret, size_t secret_len, 14705b9c547cSRui Paulo u8 *buf, size_t buf_len) 14715b9c547cSRui Paulo { 14725b9c547cSRui Paulo size_t padlen, i, pos; 147339beb93cSSam Leffler const u8 *addr[2]; 147439beb93cSSam Leffler size_t len[2]; 147539beb93cSSam Leffler u8 hash[16]; 147639beb93cSSam Leffler 14775b9c547cSRui Paulo if (data_len + 16 > buf_len) 14785b9c547cSRui Paulo return -1; 147939beb93cSSam Leffler 148039beb93cSSam Leffler os_memcpy(buf, data, data_len); 148139beb93cSSam Leffler 148239beb93cSSam Leffler padlen = data_len % 16; 14835b9c547cSRui Paulo if (padlen && data_len < buf_len) { 148439beb93cSSam Leffler padlen = 16 - padlen; 148539beb93cSSam Leffler os_memset(buf + data_len, 0, padlen); 14865b9c547cSRui Paulo buf_len = data_len + padlen; 14875b9c547cSRui Paulo } else { 14885b9c547cSRui Paulo buf_len = data_len; 148939beb93cSSam Leffler } 149039beb93cSSam Leffler 149139beb93cSSam Leffler addr[0] = secret; 149239beb93cSSam Leffler len[0] = secret_len; 149339beb93cSSam Leffler addr[1] = msg->hdr->authenticator; 149439beb93cSSam Leffler len[1] = 16; 149539beb93cSSam Leffler md5_vector(2, addr, len, hash); 149639beb93cSSam Leffler 149739beb93cSSam Leffler for (i = 0; i < 16; i++) 149839beb93cSSam Leffler buf[i] ^= hash[i]; 149939beb93cSSam Leffler pos = 16; 150039beb93cSSam Leffler 150139beb93cSSam Leffler while (pos < buf_len) { 150239beb93cSSam Leffler addr[0] = secret; 150339beb93cSSam Leffler len[0] = secret_len; 150439beb93cSSam Leffler addr[1] = &buf[pos - 16]; 150539beb93cSSam Leffler len[1] = 16; 150639beb93cSSam Leffler md5_vector(2, addr, len, hash); 150739beb93cSSam Leffler 150839beb93cSSam Leffler for (i = 0; i < 16; i++) 150939beb93cSSam Leffler buf[pos + i] ^= hash[i]; 151039beb93cSSam Leffler 151139beb93cSSam Leffler pos += 16; 151239beb93cSSam Leffler } 151339beb93cSSam Leffler 15145b9c547cSRui Paulo return buf_len; 15155b9c547cSRui Paulo } 15165b9c547cSRui Paulo 15175b9c547cSRui Paulo 15185b9c547cSRui Paulo /* Add User-Password attribute to a RADIUS message and encrypt it as specified 15195b9c547cSRui Paulo * in RFC 2865, Chap. 5.2 */ 15205b9c547cSRui Paulo struct radius_attr_hdr * 15215b9c547cSRui Paulo radius_msg_add_attr_user_password(struct radius_msg *msg, 15225b9c547cSRui Paulo const u8 *data, size_t data_len, 15235b9c547cSRui Paulo const u8 *secret, size_t secret_len) 15245b9c547cSRui Paulo { 15255b9c547cSRui Paulo u8 buf[128]; 15265b9c547cSRui Paulo int res; 15275b9c547cSRui Paulo 15285b9c547cSRui Paulo res = radius_user_password_hide(msg, data, data_len, 15295b9c547cSRui Paulo secret, secret_len, buf, sizeof(buf)); 15305b9c547cSRui Paulo if (res < 0) 15315b9c547cSRui Paulo return NULL; 15325b9c547cSRui Paulo 153339beb93cSSam Leffler return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD, 15345b9c547cSRui Paulo buf, res); 153539beb93cSSam Leffler } 153639beb93cSSam Leffler 153739beb93cSSam Leffler 153839beb93cSSam Leffler int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len) 153939beb93cSSam Leffler { 154039beb93cSSam Leffler struct radius_attr_hdr *attr = NULL, *tmp; 154139beb93cSSam Leffler size_t i, dlen; 154239beb93cSSam Leffler 154339beb93cSSam Leffler for (i = 0; i < msg->attr_used; i++) { 154439beb93cSSam Leffler tmp = radius_get_attr_hdr(msg, i); 154539beb93cSSam Leffler if (tmp->type == type) { 154639beb93cSSam Leffler attr = tmp; 154739beb93cSSam Leffler break; 154839beb93cSSam Leffler } 154939beb93cSSam Leffler } 155039beb93cSSam Leffler 1551f05cddf9SRui Paulo if (!attr || attr->length < sizeof(*attr)) 155239beb93cSSam Leffler return -1; 155339beb93cSSam Leffler 155439beb93cSSam Leffler dlen = attr->length - sizeof(*attr); 155539beb93cSSam Leffler if (buf) 155639beb93cSSam Leffler os_memcpy(buf, (attr + 1), dlen > len ? len : dlen); 155739beb93cSSam Leffler return dlen; 155839beb93cSSam Leffler } 155939beb93cSSam Leffler 156039beb93cSSam Leffler 156139beb93cSSam Leffler int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, 156239beb93cSSam Leffler size_t *len, const u8 *start) 156339beb93cSSam Leffler { 156439beb93cSSam Leffler size_t i; 156539beb93cSSam Leffler struct radius_attr_hdr *attr = NULL, *tmp; 156639beb93cSSam Leffler 156739beb93cSSam Leffler for (i = 0; i < msg->attr_used; i++) { 156839beb93cSSam Leffler tmp = radius_get_attr_hdr(msg, i); 156939beb93cSSam Leffler if (tmp->type == type && 157039beb93cSSam Leffler (start == NULL || (u8 *) tmp > start)) { 157139beb93cSSam Leffler attr = tmp; 157239beb93cSSam Leffler break; 157339beb93cSSam Leffler } 157439beb93cSSam Leffler } 157539beb93cSSam Leffler 1576f05cddf9SRui Paulo if (!attr || attr->length < sizeof(*attr)) 157739beb93cSSam Leffler return -1; 157839beb93cSSam Leffler 157939beb93cSSam Leffler *buf = (u8 *) (attr + 1); 158039beb93cSSam Leffler *len = attr->length - sizeof(*attr); 158139beb93cSSam Leffler return 0; 158239beb93cSSam Leffler } 158339beb93cSSam Leffler 158439beb93cSSam Leffler 158539beb93cSSam Leffler int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len) 158639beb93cSSam Leffler { 158739beb93cSSam Leffler size_t i; 158839beb93cSSam Leffler int count; 158939beb93cSSam Leffler 159039beb93cSSam Leffler for (count = 0, i = 0; i < msg->attr_used; i++) { 159139beb93cSSam Leffler struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); 159239beb93cSSam Leffler if (attr->type == type && 159339beb93cSSam Leffler attr->length >= sizeof(struct radius_attr_hdr) + min_len) 159439beb93cSSam Leffler count++; 159539beb93cSSam Leffler } 159639beb93cSSam Leffler 159739beb93cSSam Leffler return count; 159839beb93cSSam Leffler } 159939beb93cSSam Leffler 160039beb93cSSam Leffler 160139beb93cSSam Leffler struct radius_tunnel_attrs { 160239beb93cSSam Leffler int tag_used; 160339beb93cSSam Leffler int type; /* Tunnel-Type */ 160439beb93cSSam Leffler int medium_type; /* Tunnel-Medium-Type */ 160539beb93cSSam Leffler int vlanid; 160639beb93cSSam Leffler }; 160739beb93cSSam Leffler 160839beb93cSSam Leffler 1609780fb4a2SCy Schubert static int cmp_int(const void *a, const void *b) 1610780fb4a2SCy Schubert { 1611780fb4a2SCy Schubert int x, y; 1612780fb4a2SCy Schubert 1613780fb4a2SCy Schubert x = *((int *) a); 1614780fb4a2SCy Schubert y = *((int *) b); 1615780fb4a2SCy Schubert return (x - y); 1616780fb4a2SCy Schubert } 1617780fb4a2SCy Schubert 1618780fb4a2SCy Schubert 161939beb93cSSam Leffler /** 162039beb93cSSam Leffler * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information 1621780fb4a2SCy Schubert * The k tagged vlans found are sorted by vlan_id and stored in the first k 1622780fb4a2SCy Schubert * items of tagged. 1623780fb4a2SCy Schubert * 162439beb93cSSam Leffler * @msg: RADIUS message 1625780fb4a2SCy Schubert * @untagged: Pointer to store untagged vid 1626780fb4a2SCy Schubert * @numtagged: Size of tagged 1627780fb4a2SCy Schubert * @tagged: Pointer to store tagged list 1628780fb4a2SCy Schubert * 1629780fb4a2SCy Schubert * Returns: 0 if neither tagged nor untagged configuration is found, 1 otherwise 163039beb93cSSam Leffler */ 1631780fb4a2SCy Schubert int radius_msg_get_vlanid(struct radius_msg *msg, int *untagged, int numtagged, 1632780fb4a2SCy Schubert int *tagged) 163339beb93cSSam Leffler { 163439beb93cSSam Leffler struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun; 163539beb93cSSam Leffler size_t i; 163639beb93cSSam Leffler struct radius_attr_hdr *attr = NULL; 163739beb93cSSam Leffler const u8 *data; 163839beb93cSSam Leffler char buf[10]; 163939beb93cSSam Leffler size_t dlen; 1640780fb4a2SCy Schubert int j, taggedidx = 0, vlan_id; 164139beb93cSSam Leffler 164239beb93cSSam Leffler os_memset(&tunnel, 0, sizeof(tunnel)); 1643780fb4a2SCy Schubert for (j = 0; j < numtagged; j++) 1644780fb4a2SCy Schubert tagged[j] = 0; 1645780fb4a2SCy Schubert *untagged = 0; 164639beb93cSSam Leffler 164739beb93cSSam Leffler for (i = 0; i < msg->attr_used; i++) { 164839beb93cSSam Leffler attr = radius_get_attr_hdr(msg, i); 1649f05cddf9SRui Paulo if (attr->length < sizeof(*attr)) 1650f05cddf9SRui Paulo return -1; 165139beb93cSSam Leffler data = (const u8 *) (attr + 1); 165239beb93cSSam Leffler dlen = attr->length - sizeof(*attr); 165339beb93cSSam Leffler if (attr->length < 3) 165439beb93cSSam Leffler continue; 165539beb93cSSam Leffler if (data[0] >= RADIUS_TUNNEL_TAGS) 165639beb93cSSam Leffler tun = &tunnel[0]; 165739beb93cSSam Leffler else 165839beb93cSSam Leffler tun = &tunnel[data[0]]; 165939beb93cSSam Leffler 166039beb93cSSam Leffler switch (attr->type) { 166139beb93cSSam Leffler case RADIUS_ATTR_TUNNEL_TYPE: 166239beb93cSSam Leffler if (attr->length != 6) 166339beb93cSSam Leffler break; 166439beb93cSSam Leffler tun->tag_used++; 166539beb93cSSam Leffler tun->type = WPA_GET_BE24(data + 1); 166639beb93cSSam Leffler break; 166739beb93cSSam Leffler case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE: 166839beb93cSSam Leffler if (attr->length != 6) 166939beb93cSSam Leffler break; 167039beb93cSSam Leffler tun->tag_used++; 167139beb93cSSam Leffler tun->medium_type = WPA_GET_BE24(data + 1); 167239beb93cSSam Leffler break; 167339beb93cSSam Leffler case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID: 167439beb93cSSam Leffler if (data[0] < RADIUS_TUNNEL_TAGS) { 167539beb93cSSam Leffler data++; 167639beb93cSSam Leffler dlen--; 167739beb93cSSam Leffler } 167839beb93cSSam Leffler if (dlen >= sizeof(buf)) 167939beb93cSSam Leffler break; 168039beb93cSSam Leffler os_memcpy(buf, data, dlen); 168139beb93cSSam Leffler buf[dlen] = '\0'; 1682780fb4a2SCy Schubert vlan_id = atoi(buf); 1683780fb4a2SCy Schubert if (vlan_id <= 0) 1684780fb4a2SCy Schubert break; 168539beb93cSSam Leffler tun->tag_used++; 1686780fb4a2SCy Schubert tun->vlanid = vlan_id; 1687780fb4a2SCy Schubert break; 1688780fb4a2SCy Schubert case RADIUS_ATTR_EGRESS_VLANID: /* RFC 4675 */ 1689780fb4a2SCy Schubert if (attr->length != 6) 1690780fb4a2SCy Schubert break; 1691780fb4a2SCy Schubert vlan_id = WPA_GET_BE24(data + 1); 1692780fb4a2SCy Schubert if (vlan_id <= 0) 1693780fb4a2SCy Schubert break; 1694780fb4a2SCy Schubert if (data[0] == 0x32) 1695780fb4a2SCy Schubert *untagged = vlan_id; 1696780fb4a2SCy Schubert else if (data[0] == 0x31 && tagged && 1697780fb4a2SCy Schubert taggedidx < numtagged) 1698780fb4a2SCy Schubert tagged[taggedidx++] = vlan_id; 169939beb93cSSam Leffler break; 170039beb93cSSam Leffler } 170139beb93cSSam Leffler } 170239beb93cSSam Leffler 1703780fb4a2SCy Schubert /* Use tunnel with the lowest tag for untagged VLAN id */ 170439beb93cSSam Leffler for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) { 170539beb93cSSam Leffler tun = &tunnel[i]; 170639beb93cSSam Leffler if (tun->tag_used && 170739beb93cSSam Leffler tun->type == RADIUS_TUNNEL_TYPE_VLAN && 170839beb93cSSam Leffler tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 && 1709780fb4a2SCy Schubert tun->vlanid > 0) { 1710780fb4a2SCy Schubert *untagged = tun->vlanid; 1711780fb4a2SCy Schubert break; 1712780fb4a2SCy Schubert } 171339beb93cSSam Leffler } 171439beb93cSSam Leffler 1715780fb4a2SCy Schubert if (taggedidx) 1716780fb4a2SCy Schubert qsort(tagged, taggedidx, sizeof(int), cmp_int); 1717780fb4a2SCy Schubert 1718780fb4a2SCy Schubert if (*untagged > 0 || taggedidx) 1719780fb4a2SCy Schubert return 1; 1720325151a3SRui Paulo return 0; 172139beb93cSSam Leffler } 1722e28a4053SRui Paulo 1723e28a4053SRui Paulo 1724f05cddf9SRui Paulo /** 1725f05cddf9SRui Paulo * radius_msg_get_tunnel_password - Parse RADIUS attribute Tunnel-Password 1726f05cddf9SRui Paulo * @msg: Received RADIUS message 1727f05cddf9SRui Paulo * @keylen: Length of returned password 1728f05cddf9SRui Paulo * @secret: RADIUS shared secret 1729f05cddf9SRui Paulo * @secret_len: Length of secret 1730f05cddf9SRui Paulo * @sent_msg: Sent RADIUS message 1731f05cddf9SRui Paulo * @n: Number of password attribute to return (starting with 0) 1732f05cddf9SRui Paulo * Returns: Pointer to n-th password (free with os_free) or %NULL 1733f05cddf9SRui Paulo */ 1734f05cddf9SRui Paulo char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen, 1735f05cddf9SRui Paulo const u8 *secret, size_t secret_len, 1736f05cddf9SRui Paulo struct radius_msg *sent_msg, size_t n) 1737f05cddf9SRui Paulo { 1738f05cddf9SRui Paulo u8 *buf = NULL; 1739f05cddf9SRui Paulo size_t buflen; 1740f05cddf9SRui Paulo const u8 *salt; 1741f05cddf9SRui Paulo u8 *str; 1742f05cddf9SRui Paulo const u8 *addr[3]; 1743f05cddf9SRui Paulo size_t len[3]; 1744f05cddf9SRui Paulo u8 hash[16]; 1745f05cddf9SRui Paulo u8 *pos; 1746f05cddf9SRui Paulo size_t i, j = 0; 1747f05cddf9SRui Paulo struct radius_attr_hdr *attr; 1748f05cddf9SRui Paulo const u8 *data; 1749f05cddf9SRui Paulo size_t dlen; 1750f05cddf9SRui Paulo const u8 *fdata = NULL; /* points to found item */ 1751f05cddf9SRui Paulo size_t fdlen = -1; 1752f05cddf9SRui Paulo char *ret = NULL; 1753f05cddf9SRui Paulo 1754f05cddf9SRui Paulo /* find n-th valid Tunnel-Password attribute */ 1755f05cddf9SRui Paulo for (i = 0; i < msg->attr_used; i++) { 1756f05cddf9SRui Paulo attr = radius_get_attr_hdr(msg, i); 1757f05cddf9SRui Paulo if (attr == NULL || 1758f05cddf9SRui Paulo attr->type != RADIUS_ATTR_TUNNEL_PASSWORD) { 1759f05cddf9SRui Paulo continue; 1760f05cddf9SRui Paulo } 1761f05cddf9SRui Paulo if (attr->length <= 5) 1762f05cddf9SRui Paulo continue; 1763f05cddf9SRui Paulo data = (const u8 *) (attr + 1); 1764f05cddf9SRui Paulo dlen = attr->length - sizeof(*attr); 1765f05cddf9SRui Paulo if (dlen <= 3 || dlen % 16 != 3) 1766f05cddf9SRui Paulo continue; 1767f05cddf9SRui Paulo j++; 1768f05cddf9SRui Paulo if (j <= n) 1769f05cddf9SRui Paulo continue; 1770f05cddf9SRui Paulo 1771f05cddf9SRui Paulo fdata = data; 1772f05cddf9SRui Paulo fdlen = dlen; 1773f05cddf9SRui Paulo break; 1774f05cddf9SRui Paulo } 1775f05cddf9SRui Paulo if (fdata == NULL) 1776f05cddf9SRui Paulo goto out; 1777f05cddf9SRui Paulo 1778f05cddf9SRui Paulo /* alloc writable memory for decryption */ 177985732ac8SCy Schubert buf = os_memdup(fdata, fdlen); 1780f05cddf9SRui Paulo if (buf == NULL) 1781f05cddf9SRui Paulo goto out; 1782f05cddf9SRui Paulo buflen = fdlen; 1783f05cddf9SRui Paulo 1784f05cddf9SRui Paulo /* init pointers */ 1785f05cddf9SRui Paulo salt = buf + 1; 1786f05cddf9SRui Paulo str = buf + 3; 1787f05cddf9SRui Paulo 1788f05cddf9SRui Paulo /* decrypt blocks */ 1789f05cddf9SRui Paulo pos = buf + buflen - 16; /* last block */ 1790f05cddf9SRui Paulo while (pos >= str + 16) { /* all but the first block */ 1791f05cddf9SRui Paulo addr[0] = secret; 1792f05cddf9SRui Paulo len[0] = secret_len; 1793f05cddf9SRui Paulo addr[1] = pos - 16; 1794f05cddf9SRui Paulo len[1] = 16; 1795f05cddf9SRui Paulo md5_vector(2, addr, len, hash); 1796f05cddf9SRui Paulo 1797f05cddf9SRui Paulo for (i = 0; i < 16; i++) 1798f05cddf9SRui Paulo pos[i] ^= hash[i]; 1799f05cddf9SRui Paulo 1800f05cddf9SRui Paulo pos -= 16; 1801f05cddf9SRui Paulo } 1802f05cddf9SRui Paulo 1803f05cddf9SRui Paulo /* decrypt first block */ 1804f05cddf9SRui Paulo if (str != pos) 1805f05cddf9SRui Paulo goto out; 1806f05cddf9SRui Paulo addr[0] = secret; 1807f05cddf9SRui Paulo len[0] = secret_len; 1808f05cddf9SRui Paulo addr[1] = sent_msg->hdr->authenticator; 1809f05cddf9SRui Paulo len[1] = 16; 1810f05cddf9SRui Paulo addr[2] = salt; 1811f05cddf9SRui Paulo len[2] = 2; 1812f05cddf9SRui Paulo md5_vector(3, addr, len, hash); 1813f05cddf9SRui Paulo 1814f05cddf9SRui Paulo for (i = 0; i < 16; i++) 1815f05cddf9SRui Paulo pos[i] ^= hash[i]; 1816f05cddf9SRui Paulo 1817f05cddf9SRui Paulo /* derive plaintext length from first subfield */ 1818f05cddf9SRui Paulo *keylen = (unsigned char) str[0]; 1819f05cddf9SRui Paulo if ((u8 *) (str + *keylen) >= (u8 *) (buf + buflen)) { 1820f05cddf9SRui Paulo /* decryption error - invalid key length */ 1821f05cddf9SRui Paulo goto out; 1822f05cddf9SRui Paulo } 1823f05cddf9SRui Paulo if (*keylen == 0) { 1824f05cddf9SRui Paulo /* empty password */ 1825f05cddf9SRui Paulo goto out; 1826f05cddf9SRui Paulo } 1827f05cddf9SRui Paulo 1828f05cddf9SRui Paulo /* copy passphrase into new buffer */ 1829f05cddf9SRui Paulo ret = os_malloc(*keylen); 1830f05cddf9SRui Paulo if (ret) 1831f05cddf9SRui Paulo os_memcpy(ret, str + 1, *keylen); 1832f05cddf9SRui Paulo 1833f05cddf9SRui Paulo out: 1834f05cddf9SRui Paulo /* return new buffer */ 1835f05cddf9SRui Paulo os_free(buf); 1836f05cddf9SRui Paulo return ret; 1837f05cddf9SRui Paulo } 1838f05cddf9SRui Paulo 1839f05cddf9SRui Paulo 1840e28a4053SRui Paulo void radius_free_class(struct radius_class_data *c) 1841e28a4053SRui Paulo { 1842e28a4053SRui Paulo size_t i; 1843e28a4053SRui Paulo if (c == NULL) 1844e28a4053SRui Paulo return; 1845e28a4053SRui Paulo for (i = 0; i < c->count; i++) 1846e28a4053SRui Paulo os_free(c->attr[i].data); 1847e28a4053SRui Paulo os_free(c->attr); 1848e28a4053SRui Paulo c->attr = NULL; 1849e28a4053SRui Paulo c->count = 0; 1850e28a4053SRui Paulo } 1851e28a4053SRui Paulo 1852e28a4053SRui Paulo 1853e28a4053SRui Paulo int radius_copy_class(struct radius_class_data *dst, 1854e28a4053SRui Paulo const struct radius_class_data *src) 1855e28a4053SRui Paulo { 1856e28a4053SRui Paulo size_t i; 1857e28a4053SRui Paulo 1858e28a4053SRui Paulo if (src->attr == NULL) 1859e28a4053SRui Paulo return 0; 1860e28a4053SRui Paulo 1861f05cddf9SRui Paulo dst->attr = os_calloc(src->count, sizeof(struct radius_attr_data)); 1862e28a4053SRui Paulo if (dst->attr == NULL) 1863e28a4053SRui Paulo return -1; 1864e28a4053SRui Paulo 1865e28a4053SRui Paulo dst->count = 0; 1866e28a4053SRui Paulo 1867e28a4053SRui Paulo for (i = 0; i < src->count; i++) { 186885732ac8SCy Schubert dst->attr[i].data = os_memdup(src->attr[i].data, 186985732ac8SCy Schubert src->attr[i].len); 1870e28a4053SRui Paulo if (dst->attr[i].data == NULL) 1871e28a4053SRui Paulo break; 1872e28a4053SRui Paulo dst->count++; 1873e28a4053SRui Paulo dst->attr[i].len = src->attr[i].len; 1874e28a4053SRui Paulo } 1875e28a4053SRui Paulo 1876e28a4053SRui Paulo return 0; 1877e28a4053SRui Paulo } 1878f05cddf9SRui Paulo 1879f05cddf9SRui Paulo 1880f05cddf9SRui Paulo u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs) 1881f05cddf9SRui Paulo { 1882f05cddf9SRui Paulo size_t i, j; 1883f05cddf9SRui Paulo struct radius_attr_hdr *attr; 1884f05cddf9SRui Paulo 1885f05cddf9SRui Paulo for (i = 0; i < msg->attr_used; i++) { 1886f05cddf9SRui Paulo attr = radius_get_attr_hdr(msg, i); 1887f05cddf9SRui Paulo 1888f05cddf9SRui Paulo for (j = 0; attrs[j]; j++) { 1889f05cddf9SRui Paulo if (attr->type == attrs[j]) 1890f05cddf9SRui Paulo break; 1891f05cddf9SRui Paulo } 1892f05cddf9SRui Paulo 1893f05cddf9SRui Paulo if (attrs[j] == 0) 1894f05cddf9SRui Paulo return attr->type; /* unlisted attr */ 1895f05cddf9SRui Paulo } 1896f05cddf9SRui Paulo 1897f05cddf9SRui Paulo return 0; 1898f05cddf9SRui Paulo } 1899780fb4a2SCy Schubert 1900780fb4a2SCy Schubert 1901780fb4a2SCy Schubert int radius_gen_session_id(u8 *id, size_t len) 1902780fb4a2SCy Schubert { 1903780fb4a2SCy Schubert /* 1904780fb4a2SCy Schubert * Acct-Session-Id and Acct-Multi-Session-Id should be globally and 1905780fb4a2SCy Schubert * temporarily unique. A high quality random number is required 1906780fb4a2SCy Schubert * therefore. This could be be improved by switching to a GUID. 1907780fb4a2SCy Schubert */ 1908780fb4a2SCy Schubert return os_get_random(id, len); 1909780fb4a2SCy Schubert } 1910