X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=libeap%2Fsrc%2Fwps%2Fwps_attr_parse.c;fp=libeap%2Fsrc%2Fwps%2Fwps_attr_parse.c;h=756d57e876c55186fc3c5097854f220c0f2046b8;hb=88e34eecb5769da6526361b35df00fdbe823eac5;hp=0000000000000000000000000000000000000000;hpb=2394142e4bf89e0a9e2a0091ac16bd6442e3d4c7;p=mech_eap.git diff --git a/libeap/src/wps/wps_attr_parse.c b/libeap/src/wps/wps_attr_parse.c new file mode 100644 index 0000000..756d57e --- /dev/null +++ b/libeap/src/wps/wps_attr_parse.c @@ -0,0 +1,663 @@ +/* + * Wi-Fi Protected Setup - attribute parsing + * Copyright (c) 2008, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wps_defs.h" +#include "wps_attr_parse.h" + +#ifndef CONFIG_WPS_STRICT +#define WPS_WORKAROUNDS +#endif /* CONFIG_WPS_STRICT */ + + +static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr, + u8 id, u8 len, const u8 *pos) +{ + wpa_printf(MSG_EXCESSIVE, "WPS: WFA subelement id=%u len=%u", + id, len); + switch (id) { + case WFA_ELEM_VERSION2: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Version2 length " + "%u", len); + return -1; + } + attr->version2 = pos; + break; + case WFA_ELEM_AUTHORIZEDMACS: + attr->authorized_macs = pos; + attr->authorized_macs_len = len; + break; + case WFA_ELEM_NETWORK_KEY_SHAREABLE: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key " + "Shareable length %u", len); + return -1; + } + attr->network_key_shareable = pos; + break; + case WFA_ELEM_REQUEST_TO_ENROLL: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Request to Enroll " + "length %u", len); + return -1; + } + attr->request_to_enroll = pos; + break; + case WFA_ELEM_SETTINGS_DELAY_TIME: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Settings Delay " + "Time length %u", len); + return -1; + } + attr->settings_delay_time = pos; + break; + case WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Configuration Methods length %u", + len); + return -1; + } + attr->registrar_configuration_methods = pos; + break; + default: + wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor " + "Extension subelement %u", id); + break; + } + + return 0; +} + + +static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos, + u16 len) +{ + const u8 *end = pos + len; + u8 id, elen; + + while (end - pos >= 2) { + id = *pos++; + elen = *pos++; + if (elen > end - pos) + break; + if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0) + return -1; + pos += elen; + } + + return 0; +} + + +static int wps_parse_vendor_ext(struct wps_parse_attr *attr, const u8 *pos, + u16 len) +{ + u32 vendor_id; + + if (len < 3) { + wpa_printf(MSG_DEBUG, "WPS: Skip invalid Vendor Extension"); + return 0; + } + + vendor_id = WPA_GET_BE24(pos); + switch (vendor_id) { + case WPS_VENDOR_ID_WFA: + return wps_parse_vendor_ext_wfa(attr, pos + 3, len - 3); + } + + /* Handle unknown vendor extensions */ + + wpa_printf(MSG_MSGDUMP, "WPS: Unknown Vendor Extension (Vendor ID %u)", + vendor_id); + + if (len > WPS_MAX_VENDOR_EXT_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Too long Vendor Extension (%u)", + len); + return -1; + } + + if (attr->num_vendor_ext >= MAX_WPS_PARSE_VENDOR_EXT) { + wpa_printf(MSG_DEBUG, "WPS: Skipped Vendor Extension " + "attribute (max %d vendor extensions)", + MAX_WPS_PARSE_VENDOR_EXT); + return -1; + } + attr->vendor_ext[attr->num_vendor_ext] = pos; + attr->vendor_ext_len[attr->num_vendor_ext] = len; + attr->num_vendor_ext++; + + return 0; +} + + +static int wps_set_attr(struct wps_parse_attr *attr, u16 type, + const u8 *pos, u16 len) +{ + switch (type) { + case ATTR_VERSION: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u", + len); + return -1; + } + attr->version = pos; + break; + case ATTR_MSG_TYPE: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type " + "length %u", len); + return -1; + } + attr->msg_type = pos; + break; + case ATTR_ENROLLEE_NONCE: + if (len != WPS_NONCE_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce " + "length %u", len); + return -1; + } + attr->enrollee_nonce = pos; + break; + case ATTR_REGISTRAR_NONCE: + if (len != WPS_NONCE_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce " + "length %u", len); + return -1; + } + attr->registrar_nonce = pos; + break; + case ATTR_UUID_E: + if (len != WPS_UUID_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u", + len); + return -1; + } + attr->uuid_e = pos; + break; + case ATTR_UUID_R: + if (len != WPS_UUID_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u", + len); + return -1; + } + attr->uuid_r = pos; + break; + case ATTR_AUTH_TYPE_FLAGS: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication " + "Type Flags length %u", len); + return -1; + } + attr->auth_type_flags = pos; + break; + case ATTR_ENCR_TYPE_FLAGS: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type " + "Flags length %u", len); + return -1; + } + attr->encr_type_flags = pos; + break; + case ATTR_CONN_TYPE_FLAGS: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type " + "Flags length %u", len); + return -1; + } + attr->conn_type_flags = pos; + break; + case ATTR_CONFIG_METHODS: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods " + "length %u", len); + return -1; + } + attr->config_methods = pos; + break; + case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Selected " + "Registrar Config Methods length %u", len); + return -1; + } + attr->sel_reg_config_methods = pos; + break; + case ATTR_PRIMARY_DEV_TYPE: + if (len != WPS_DEV_TYPE_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device " + "Type length %u", len); + return -1; + } + attr->primary_dev_type = pos; + break; + case ATTR_RF_BANDS: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length " + "%u", len); + return -1; + } + attr->rf_bands = pos; + break; + case ATTR_ASSOC_STATE: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Association State " + "length %u", len); + return -1; + } + attr->assoc_state = pos; + break; + case ATTR_CONFIG_ERROR: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration " + "Error length %u", len); + return -1; + } + attr->config_error = pos; + break; + case ATTR_DEV_PASSWORD_ID: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password " + "ID length %u", len); + return -1; + } + attr->dev_password_id = pos; + break; + case ATTR_OOB_DEVICE_PASSWORD: + if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 || + len > WPS_OOB_PUBKEY_HASH_LEN + 2 + + WPS_OOB_DEVICE_PASSWORD_LEN || + (len < WPS_OOB_PUBKEY_HASH_LEN + 2 + + WPS_OOB_DEVICE_PASSWORD_MIN_LEN && + WPA_GET_BE16(pos + WPS_OOB_PUBKEY_HASH_LEN) != + DEV_PW_NFC_CONNECTION_HANDOVER)) { + wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device " + "Password length %u", len); + return -1; + } + attr->oob_dev_password = pos; + attr->oob_dev_password_len = len; + break; + case ATTR_OS_VERSION: + if (len != 4) { + wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length " + "%u", len); + return -1; + } + attr->os_version = pos; + break; + case ATTR_WPS_STATE: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected " + "Setup State length %u", len); + return -1; + } + attr->wps_state = pos; + break; + case ATTR_AUTHENTICATOR: + if (len != WPS_AUTHENTICATOR_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator " + "length %u", len); + return -1; + } + attr->authenticator = pos; + break; + case ATTR_R_HASH1: + if (len != WPS_HASH_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u", + len); + return -1; + } + attr->r_hash1 = pos; + break; + case ATTR_R_HASH2: + if (len != WPS_HASH_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u", + len); + return -1; + } + attr->r_hash2 = pos; + break; + case ATTR_E_HASH1: + if (len != WPS_HASH_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u", + len); + return -1; + } + attr->e_hash1 = pos; + break; + case ATTR_E_HASH2: + if (len != WPS_HASH_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u", + len); + return -1; + } + attr->e_hash2 = pos; + break; + case ATTR_R_SNONCE1: + if (len != WPS_SECRET_NONCE_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length " + "%u", len); + return -1; + } + attr->r_snonce1 = pos; + break; + case ATTR_R_SNONCE2: + if (len != WPS_SECRET_NONCE_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length " + "%u", len); + return -1; + } + attr->r_snonce2 = pos; + break; + case ATTR_E_SNONCE1: + if (len != WPS_SECRET_NONCE_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length " + "%u", len); + return -1; + } + attr->e_snonce1 = pos; + break; + case ATTR_E_SNONCE2: + if (len != WPS_SECRET_NONCE_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length " + "%u", len); + return -1; + } + attr->e_snonce2 = pos; + break; + case ATTR_KEY_WRAP_AUTH: + if (len != WPS_KWA_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap " + "Authenticator length %u", len); + return -1; + } + attr->key_wrap_auth = pos; + break; + case ATTR_AUTH_TYPE: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication " + "Type length %u", len); + return -1; + } + attr->auth_type = pos; + break; + case ATTR_ENCR_TYPE: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption " + "Type length %u", len); + return -1; + } + attr->encr_type = pos; + break; + case ATTR_NETWORK_INDEX: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index " + "length %u", len); + return -1; + } + attr->network_idx = pos; + break; + case ATTR_NETWORK_KEY_INDEX: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index " + "length %u", len); + return -1; + } + attr->network_key_idx = pos; + break; + case ATTR_MAC_ADDR: + if (len != ETH_ALEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address " + "length %u", len); + return -1; + } + attr->mac_addr = pos; + break; + case ATTR_SELECTED_REGISTRAR: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar" + " length %u", len); + return -1; + } + attr->selected_registrar = pos; + break; + case ATTR_REQUEST_TYPE: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Request Type " + "length %u", len); + return -1; + } + attr->request_type = pos; + break; + case ATTR_RESPONSE_TYPE: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Response Type " + "length %u", len); + return -1; + } + attr->response_type = pos; + break; + case ATTR_MANUFACTURER: + attr->manufacturer = pos; + if (len > WPS_MANUFACTURER_MAX_LEN) + attr->manufacturer_len = WPS_MANUFACTURER_MAX_LEN; + else + attr->manufacturer_len = len; + break; + case ATTR_MODEL_NAME: + attr->model_name = pos; + if (len > WPS_MODEL_NAME_MAX_LEN) + attr->model_name_len = WPS_MODEL_NAME_MAX_LEN; + else + attr->model_name_len = len; + break; + case ATTR_MODEL_NUMBER: + attr->model_number = pos; + if (len > WPS_MODEL_NUMBER_MAX_LEN) + attr->model_number_len = WPS_MODEL_NUMBER_MAX_LEN; + else + attr->model_number_len = len; + break; + case ATTR_SERIAL_NUMBER: + attr->serial_number = pos; + if (len > WPS_SERIAL_NUMBER_MAX_LEN) + attr->serial_number_len = WPS_SERIAL_NUMBER_MAX_LEN; + else + attr->serial_number_len = len; + break; + case ATTR_DEV_NAME: + if (len > WPS_DEV_NAME_MAX_LEN) { + wpa_printf(MSG_DEBUG, + "WPS: Ignore too long Device Name (len=%u)", + len); + break; + } + attr->dev_name = pos; + attr->dev_name_len = len; + break; + case ATTR_PUBLIC_KEY: + /* + * The Public Key attribute is supposed to be exactly 192 bytes + * in length. Allow couple of bytes shorter one to try to + * interoperate with implementations that do not use proper + * zero-padding. + */ + if (len < 190 || len > 192) { + wpa_printf(MSG_DEBUG, + "WPS: Ignore Public Key with unexpected length %u", + len); + break; + } + attr->public_key = pos; + attr->public_key_len = len; + break; + case ATTR_ENCR_SETTINGS: + attr->encr_settings = pos; + attr->encr_settings_len = len; + break; + case ATTR_CRED: + if (attr->num_cred >= MAX_CRED_COUNT) { + wpa_printf(MSG_DEBUG, "WPS: Skipped Credential " + "attribute (max %d credentials)", + MAX_CRED_COUNT); + break; + } + attr->cred[attr->num_cred] = pos; + attr->cred_len[attr->num_cred] = len; + attr->num_cred++; + break; + case ATTR_SSID: + if (len > SSID_MAX_LEN) { + wpa_printf(MSG_DEBUG, + "WPS: Ignore too long SSID (len=%u)", len); + break; + } + attr->ssid = pos; + attr->ssid_len = len; + break; + case ATTR_NETWORK_KEY: + attr->network_key = pos; + attr->network_key_len = len; + break; + case ATTR_AP_SETUP_LOCKED: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked " + "length %u", len); + return -1; + } + attr->ap_setup_locked = pos; + break; + case ATTR_REQUESTED_DEV_TYPE: + if (len != WPS_DEV_TYPE_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Requested Device " + "Type length %u", len); + return -1; + } + if (attr->num_req_dev_type >= MAX_REQ_DEV_TYPE_COUNT) { + wpa_printf(MSG_DEBUG, "WPS: Skipped Requested Device " + "Type attribute (max %u types)", + MAX_REQ_DEV_TYPE_COUNT); + break; + } + attr->req_dev_type[attr->num_req_dev_type] = pos; + attr->num_req_dev_type++; + break; + case ATTR_SECONDARY_DEV_TYPE_LIST: + if (len > WPS_SEC_DEV_TYPE_MAX_LEN || + (len % WPS_DEV_TYPE_LEN) > 0) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Secondary Device " + "Type length %u", len); + return -1; + } + attr->sec_dev_type_list = pos; + attr->sec_dev_type_list_len = len; + break; + case ATTR_VENDOR_EXT: + if (wps_parse_vendor_ext(attr, pos, len) < 0) + return -1; + break; + case ATTR_AP_CHANNEL: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid AP Channel " + "length %u", len); + return -1; + } + attr->ap_channel = pos; + break; + default: + wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x " + "len=%u", type, len); + break; + } + + return 0; +} + + +int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr) +{ + const u8 *pos, *end; + u16 type, len; +#ifdef WPS_WORKAROUNDS + u16 prev_type = 0; +#endif /* WPS_WORKAROUNDS */ + + os_memset(attr, 0, sizeof(*attr)); + pos = wpabuf_head(msg); + end = pos + wpabuf_len(msg); + + while (pos < end) { + if (end - pos < 4) { + wpa_printf(MSG_DEBUG, "WPS: Invalid message - " + "%lu bytes remaining", + (unsigned long) (end - pos)); + return -1; + } + + type = WPA_GET_BE16(pos); + pos += 2; + len = WPA_GET_BE16(pos); + pos += 2; + wpa_printf(MSG_EXCESSIVE, "WPS: attr type=0x%x len=%u", + type, len); + if (len > end - pos) { + wpa_printf(MSG_DEBUG, "WPS: Attribute overflow"); + wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Message data", msg); +#ifdef WPS_WORKAROUNDS + /* + * Some deployed APs seem to have a bug in encoding of + * Network Key attribute in the Credential attribute + * where they add an extra octet after the Network Key + * attribute at least when open network is being + * provisioned. + */ + if ((type & 0xff00) != 0x1000 && + prev_type == ATTR_NETWORK_KEY) { + wpa_printf(MSG_DEBUG, "WPS: Workaround - try " + "to skip unexpected octet after " + "Network Key"); + pos -= 3; + continue; + } +#endif /* WPS_WORKAROUNDS */ + return -1; + } + +#ifdef WPS_WORKAROUNDS + if (type == 0 && len == 0) { + /* + * Mac OS X 10.6 seems to be adding 0x00 padding to the + * end of M1. Skip those to avoid interop issues. + */ + int i; + for (i = 0; i < end - pos; i++) { + if (pos[i]) + break; + } + if (i == end - pos) { + wpa_printf(MSG_DEBUG, "WPS: Workaround - skip " + "unexpected message padding"); + break; + } + } +#endif /* WPS_WORKAROUNDS */ + + if (wps_set_attr(attr, type, pos, len) < 0) + return -1; + +#ifdef WPS_WORKAROUNDS + prev_type = type; +#endif /* WPS_WORKAROUNDS */ + pos += len; + } + + return 0; +}