1189251Ssam/* 2189251Ssam * Wi-Fi Protected Setup - attribute parsing 3189251Ssam * Copyright (c) 2008, Jouni Malinen <j@w1.fi> 4189251Ssam * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7189251Ssam */ 8189251Ssam 9189251Ssam#include "includes.h" 10189251Ssam 11189251Ssam#include "common.h" 12252726Srpaulo#include "wps_defs.h" 13252726Srpaulo#include "wps_attr_parse.h" 14189251Ssam 15252726Srpaulo#ifndef CONFIG_WPS_STRICT 16214734Srpaulo#define WPS_WORKAROUNDS 17252726Srpaulo#endif /* CONFIG_WPS_STRICT */ 18189251Ssam 19214734Srpaulo 20252726Srpaulostatic int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr, 21252726Srpaulo u8 id, u8 len, const u8 *pos) 22252726Srpaulo{ 23252726Srpaulo wpa_printf(MSG_EXCESSIVE, "WPS: WFA subelement id=%u len=%u", 24252726Srpaulo id, len); 25252726Srpaulo switch (id) { 26252726Srpaulo case WFA_ELEM_VERSION2: 27252726Srpaulo if (len != 1) { 28252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: Invalid Version2 length " 29252726Srpaulo "%u", len); 30252726Srpaulo return -1; 31252726Srpaulo } 32252726Srpaulo attr->version2 = pos; 33252726Srpaulo break; 34252726Srpaulo case WFA_ELEM_AUTHORIZEDMACS: 35252726Srpaulo attr->authorized_macs = pos; 36252726Srpaulo attr->authorized_macs_len = len; 37252726Srpaulo break; 38252726Srpaulo case WFA_ELEM_NETWORK_KEY_SHAREABLE: 39252726Srpaulo if (len != 1) { 40252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key " 41252726Srpaulo "Shareable length %u", len); 42252726Srpaulo return -1; 43252726Srpaulo } 44252726Srpaulo attr->network_key_shareable = pos; 45252726Srpaulo break; 46252726Srpaulo case WFA_ELEM_REQUEST_TO_ENROLL: 47252726Srpaulo if (len != 1) { 48252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: Invalid Request to Enroll " 49252726Srpaulo "length %u", len); 50252726Srpaulo return -1; 51252726Srpaulo } 52252726Srpaulo attr->request_to_enroll = pos; 53252726Srpaulo break; 54252726Srpaulo case WFA_ELEM_SETTINGS_DELAY_TIME: 55252726Srpaulo if (len != 1) { 56252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: Invalid Settings Delay " 57252726Srpaulo "Time length %u", len); 58252726Srpaulo return -1; 59252726Srpaulo } 60252726Srpaulo attr->settings_delay_time = pos; 61252726Srpaulo break; 62252726Srpaulo default: 63252726Srpaulo wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor " 64252726Srpaulo "Extension subelement %u", id); 65252726Srpaulo break; 66252726Srpaulo } 67252726Srpaulo 68252726Srpaulo return 0; 69252726Srpaulo} 70252726Srpaulo 71252726Srpaulo 72252726Srpaulostatic int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos, 73252726Srpaulo u16 len) 74252726Srpaulo{ 75252726Srpaulo const u8 *end = pos + len; 76252726Srpaulo u8 id, elen; 77252726Srpaulo 78252726Srpaulo while (pos + 2 < end) { 79252726Srpaulo id = *pos++; 80252726Srpaulo elen = *pos++; 81252726Srpaulo if (pos + elen > end) 82252726Srpaulo break; 83252726Srpaulo if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0) 84252726Srpaulo return -1; 85252726Srpaulo pos += elen; 86252726Srpaulo } 87252726Srpaulo 88252726Srpaulo return 0; 89252726Srpaulo} 90252726Srpaulo 91252726Srpaulo 92252726Srpaulostatic int wps_parse_vendor_ext(struct wps_parse_attr *attr, const u8 *pos, 93252726Srpaulo u16 len) 94252726Srpaulo{ 95252726Srpaulo u32 vendor_id; 96252726Srpaulo 97252726Srpaulo if (len < 3) { 98252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: Skip invalid Vendor Extension"); 99252726Srpaulo return 0; 100252726Srpaulo } 101252726Srpaulo 102252726Srpaulo vendor_id = WPA_GET_BE24(pos); 103252726Srpaulo switch (vendor_id) { 104252726Srpaulo case WPS_VENDOR_ID_WFA: 105252726Srpaulo return wps_parse_vendor_ext_wfa(attr, pos + 3, len - 3); 106252726Srpaulo } 107252726Srpaulo 108252726Srpaulo /* Handle unknown vendor extensions */ 109252726Srpaulo 110252726Srpaulo wpa_printf(MSG_MSGDUMP, "WPS: Unknown Vendor Extension (Vendor ID %u)", 111252726Srpaulo vendor_id); 112252726Srpaulo 113252726Srpaulo if (len > WPS_MAX_VENDOR_EXT_LEN) { 114252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: Too long Vendor Extension (%u)", 115252726Srpaulo len); 116252726Srpaulo return -1; 117252726Srpaulo } 118252726Srpaulo 119252726Srpaulo if (attr->num_vendor_ext >= MAX_WPS_PARSE_VENDOR_EXT) { 120252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: Skipped Vendor Extension " 121252726Srpaulo "attribute (max %d vendor extensions)", 122252726Srpaulo MAX_WPS_PARSE_VENDOR_EXT); 123252726Srpaulo return -1; 124252726Srpaulo } 125252726Srpaulo attr->vendor_ext[attr->num_vendor_ext] = pos; 126252726Srpaulo attr->vendor_ext_len[attr->num_vendor_ext] = len; 127252726Srpaulo attr->num_vendor_ext++; 128252726Srpaulo 129252726Srpaulo return 0; 130252726Srpaulo} 131252726Srpaulo 132252726Srpaulo 133189251Ssamstatic int wps_set_attr(struct wps_parse_attr *attr, u16 type, 134189251Ssam const u8 *pos, u16 len) 135189251Ssam{ 136189251Ssam switch (type) { 137189251Ssam case ATTR_VERSION: 138189251Ssam if (len != 1) { 139189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u", 140189251Ssam len); 141189251Ssam return -1; 142189251Ssam } 143189251Ssam attr->version = pos; 144189251Ssam break; 145189251Ssam case ATTR_MSG_TYPE: 146189251Ssam if (len != 1) { 147189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type " 148189251Ssam "length %u", len); 149189251Ssam return -1; 150189251Ssam } 151189251Ssam attr->msg_type = pos; 152189251Ssam break; 153189251Ssam case ATTR_ENROLLEE_NONCE: 154189251Ssam if (len != WPS_NONCE_LEN) { 155189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce " 156189251Ssam "length %u", len); 157189251Ssam return -1; 158189251Ssam } 159189251Ssam attr->enrollee_nonce = pos; 160189251Ssam break; 161189251Ssam case ATTR_REGISTRAR_NONCE: 162189251Ssam if (len != WPS_NONCE_LEN) { 163189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce " 164189251Ssam "length %u", len); 165189251Ssam return -1; 166189251Ssam } 167189251Ssam attr->registrar_nonce = pos; 168189251Ssam break; 169189251Ssam case ATTR_UUID_E: 170189251Ssam if (len != WPS_UUID_LEN) { 171189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u", 172189251Ssam len); 173189251Ssam return -1; 174189251Ssam } 175189251Ssam attr->uuid_e = pos; 176189251Ssam break; 177189251Ssam case ATTR_UUID_R: 178189251Ssam if (len != WPS_UUID_LEN) { 179189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u", 180189251Ssam len); 181189251Ssam return -1; 182189251Ssam } 183189251Ssam attr->uuid_r = pos; 184189251Ssam break; 185189251Ssam case ATTR_AUTH_TYPE_FLAGS: 186189251Ssam if (len != 2) { 187189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication " 188189251Ssam "Type Flags length %u", len); 189189251Ssam return -1; 190189251Ssam } 191189251Ssam attr->auth_type_flags = pos; 192189251Ssam break; 193189251Ssam case ATTR_ENCR_TYPE_FLAGS: 194189251Ssam if (len != 2) { 195189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type " 196189251Ssam "Flags length %u", len); 197189251Ssam return -1; 198189251Ssam } 199189251Ssam attr->encr_type_flags = pos; 200189251Ssam break; 201189251Ssam case ATTR_CONN_TYPE_FLAGS: 202189251Ssam if (len != 1) { 203189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type " 204189251Ssam "Flags length %u", len); 205189251Ssam return -1; 206189251Ssam } 207189251Ssam attr->conn_type_flags = pos; 208189251Ssam break; 209189251Ssam case ATTR_CONFIG_METHODS: 210189251Ssam if (len != 2) { 211189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods " 212189251Ssam "length %u", len); 213189251Ssam return -1; 214189251Ssam } 215189251Ssam attr->config_methods = pos; 216189251Ssam break; 217189251Ssam case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS: 218189251Ssam if (len != 2) { 219189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Selected " 220189251Ssam "Registrar Config Methods length %u", len); 221189251Ssam return -1; 222189251Ssam } 223189251Ssam attr->sel_reg_config_methods = pos; 224189251Ssam break; 225189251Ssam case ATTR_PRIMARY_DEV_TYPE: 226214734Srpaulo if (len != WPS_DEV_TYPE_LEN) { 227189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device " 228189251Ssam "Type length %u", len); 229189251Ssam return -1; 230189251Ssam } 231189251Ssam attr->primary_dev_type = pos; 232189251Ssam break; 233189251Ssam case ATTR_RF_BANDS: 234189251Ssam if (len != 1) { 235189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length " 236189251Ssam "%u", len); 237189251Ssam return -1; 238189251Ssam } 239189251Ssam attr->rf_bands = pos; 240189251Ssam break; 241189251Ssam case ATTR_ASSOC_STATE: 242189251Ssam if (len != 2) { 243189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Association State " 244189251Ssam "length %u", len); 245189251Ssam return -1; 246189251Ssam } 247189251Ssam attr->assoc_state = pos; 248189251Ssam break; 249189251Ssam case ATTR_CONFIG_ERROR: 250189251Ssam if (len != 2) { 251189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration " 252189251Ssam "Error length %u", len); 253189251Ssam return -1; 254189251Ssam } 255189251Ssam attr->config_error = pos; 256189251Ssam break; 257189251Ssam case ATTR_DEV_PASSWORD_ID: 258189251Ssam if (len != 2) { 259189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password " 260189251Ssam "ID length %u", len); 261189251Ssam return -1; 262189251Ssam } 263189251Ssam attr->dev_password_id = pos; 264189251Ssam break; 265214734Srpaulo case ATTR_OOB_DEVICE_PASSWORD: 266252726Srpaulo if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 + 267252726Srpaulo WPS_OOB_DEVICE_PASSWORD_MIN_LEN || 268252726Srpaulo len > WPS_OOB_PUBKEY_HASH_LEN + 2 + 269252726Srpaulo WPS_OOB_DEVICE_PASSWORD_LEN) { 270214734Srpaulo wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device " 271214734Srpaulo "Password length %u", len); 272214734Srpaulo return -1; 273214734Srpaulo } 274214734Srpaulo attr->oob_dev_password = pos; 275252726Srpaulo attr->oob_dev_password_len = len; 276214734Srpaulo break; 277189251Ssam case ATTR_OS_VERSION: 278189251Ssam if (len != 4) { 279189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length " 280189251Ssam "%u", len); 281189251Ssam return -1; 282189251Ssam } 283189251Ssam attr->os_version = pos; 284189251Ssam break; 285189251Ssam case ATTR_WPS_STATE: 286189251Ssam if (len != 1) { 287189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected " 288189251Ssam "Setup State length %u", len); 289189251Ssam return -1; 290189251Ssam } 291189251Ssam attr->wps_state = pos; 292189251Ssam break; 293189251Ssam case ATTR_AUTHENTICATOR: 294189251Ssam if (len != WPS_AUTHENTICATOR_LEN) { 295189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator " 296189251Ssam "length %u", len); 297189251Ssam return -1; 298189251Ssam } 299189251Ssam attr->authenticator = pos; 300189251Ssam break; 301189251Ssam case ATTR_R_HASH1: 302189251Ssam if (len != WPS_HASH_LEN) { 303189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u", 304189251Ssam len); 305189251Ssam return -1; 306189251Ssam } 307189251Ssam attr->r_hash1 = pos; 308189251Ssam break; 309189251Ssam case ATTR_R_HASH2: 310189251Ssam if (len != WPS_HASH_LEN) { 311189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u", 312189251Ssam len); 313189251Ssam return -1; 314189251Ssam } 315189251Ssam attr->r_hash2 = pos; 316189251Ssam break; 317189251Ssam case ATTR_E_HASH1: 318189251Ssam if (len != WPS_HASH_LEN) { 319189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u", 320189251Ssam len); 321189251Ssam return -1; 322189251Ssam } 323189251Ssam attr->e_hash1 = pos; 324189251Ssam break; 325189251Ssam case ATTR_E_HASH2: 326189251Ssam if (len != WPS_HASH_LEN) { 327189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u", 328189251Ssam len); 329189251Ssam return -1; 330189251Ssam } 331189251Ssam attr->e_hash2 = pos; 332189251Ssam break; 333189251Ssam case ATTR_R_SNONCE1: 334189251Ssam if (len != WPS_SECRET_NONCE_LEN) { 335189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length " 336189251Ssam "%u", len); 337189251Ssam return -1; 338189251Ssam } 339189251Ssam attr->r_snonce1 = pos; 340189251Ssam break; 341189251Ssam case ATTR_R_SNONCE2: 342189251Ssam if (len != WPS_SECRET_NONCE_LEN) { 343189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length " 344189251Ssam "%u", len); 345189251Ssam return -1; 346189251Ssam } 347189251Ssam attr->r_snonce2 = pos; 348189251Ssam break; 349189251Ssam case ATTR_E_SNONCE1: 350189251Ssam if (len != WPS_SECRET_NONCE_LEN) { 351189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length " 352189251Ssam "%u", len); 353189251Ssam return -1; 354189251Ssam } 355189251Ssam attr->e_snonce1 = pos; 356189251Ssam break; 357189251Ssam case ATTR_E_SNONCE2: 358189251Ssam if (len != WPS_SECRET_NONCE_LEN) { 359189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length " 360189251Ssam "%u", len); 361189251Ssam return -1; 362189251Ssam } 363189251Ssam attr->e_snonce2 = pos; 364189251Ssam break; 365189251Ssam case ATTR_KEY_WRAP_AUTH: 366189251Ssam if (len != WPS_KWA_LEN) { 367189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap " 368189251Ssam "Authenticator length %u", len); 369189251Ssam return -1; 370189251Ssam } 371189251Ssam attr->key_wrap_auth = pos; 372189251Ssam break; 373189251Ssam case ATTR_AUTH_TYPE: 374189251Ssam if (len != 2) { 375189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication " 376189251Ssam "Type length %u", len); 377189251Ssam return -1; 378189251Ssam } 379189251Ssam attr->auth_type = pos; 380189251Ssam break; 381189251Ssam case ATTR_ENCR_TYPE: 382189251Ssam if (len != 2) { 383189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption " 384189251Ssam "Type length %u", len); 385189251Ssam return -1; 386189251Ssam } 387189251Ssam attr->encr_type = pos; 388189251Ssam break; 389189251Ssam case ATTR_NETWORK_INDEX: 390189251Ssam if (len != 1) { 391189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index " 392189251Ssam "length %u", len); 393189251Ssam return -1; 394189251Ssam } 395189251Ssam attr->network_idx = pos; 396189251Ssam break; 397189251Ssam case ATTR_NETWORK_KEY_INDEX: 398189251Ssam if (len != 1) { 399189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index " 400189251Ssam "length %u", len); 401189251Ssam return -1; 402189251Ssam } 403189251Ssam attr->network_key_idx = pos; 404189251Ssam break; 405189251Ssam case ATTR_MAC_ADDR: 406189251Ssam if (len != ETH_ALEN) { 407189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address " 408189251Ssam "length %u", len); 409189251Ssam return -1; 410189251Ssam } 411189251Ssam attr->mac_addr = pos; 412189251Ssam break; 413189251Ssam case ATTR_KEY_PROVIDED_AUTO: 414189251Ssam if (len != 1) { 415189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Key Provided " 416189251Ssam "Automatically length %u", len); 417189251Ssam return -1; 418189251Ssam } 419189251Ssam attr->key_prov_auto = pos; 420189251Ssam break; 421189251Ssam case ATTR_802_1X_ENABLED: 422189251Ssam if (len != 1) { 423189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid 802.1X Enabled " 424189251Ssam "length %u", len); 425189251Ssam return -1; 426189251Ssam } 427189251Ssam attr->dot1x_enabled = pos; 428189251Ssam break; 429189251Ssam case ATTR_SELECTED_REGISTRAR: 430189251Ssam if (len != 1) { 431189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar" 432189251Ssam " length %u", len); 433189251Ssam return -1; 434189251Ssam } 435189251Ssam attr->selected_registrar = pos; 436189251Ssam break; 437189251Ssam case ATTR_REQUEST_TYPE: 438189251Ssam if (len != 1) { 439189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Request Type " 440189251Ssam "length %u", len); 441189251Ssam return -1; 442189251Ssam } 443189251Ssam attr->request_type = pos; 444189251Ssam break; 445189251Ssam case ATTR_RESPONSE_TYPE: 446189251Ssam if (len != 1) { 447189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Response Type " 448189251Ssam "length %u", len); 449189251Ssam return -1; 450189251Ssam } 451214734Srpaulo attr->response_type = pos; 452189251Ssam break; 453189251Ssam case ATTR_MANUFACTURER: 454189251Ssam attr->manufacturer = pos; 455189251Ssam attr->manufacturer_len = len; 456189251Ssam break; 457189251Ssam case ATTR_MODEL_NAME: 458189251Ssam attr->model_name = pos; 459189251Ssam attr->model_name_len = len; 460189251Ssam break; 461189251Ssam case ATTR_MODEL_NUMBER: 462189251Ssam attr->model_number = pos; 463189251Ssam attr->model_number_len = len; 464189251Ssam break; 465189251Ssam case ATTR_SERIAL_NUMBER: 466189251Ssam attr->serial_number = pos; 467189251Ssam attr->serial_number_len = len; 468189251Ssam break; 469189251Ssam case ATTR_DEV_NAME: 470189251Ssam attr->dev_name = pos; 471189251Ssam attr->dev_name_len = len; 472189251Ssam break; 473189251Ssam case ATTR_PUBLIC_KEY: 474189251Ssam attr->public_key = pos; 475189251Ssam attr->public_key_len = len; 476189251Ssam break; 477189251Ssam case ATTR_ENCR_SETTINGS: 478189251Ssam attr->encr_settings = pos; 479189251Ssam attr->encr_settings_len = len; 480189251Ssam break; 481189251Ssam case ATTR_CRED: 482189251Ssam if (attr->num_cred >= MAX_CRED_COUNT) { 483189251Ssam wpa_printf(MSG_DEBUG, "WPS: Skipped Credential " 484189251Ssam "attribute (max %d credentials)", 485189251Ssam MAX_CRED_COUNT); 486189251Ssam break; 487189251Ssam } 488189251Ssam attr->cred[attr->num_cred] = pos; 489189251Ssam attr->cred_len[attr->num_cred] = len; 490189251Ssam attr->num_cred++; 491189251Ssam break; 492189251Ssam case ATTR_SSID: 493189251Ssam attr->ssid = pos; 494189251Ssam attr->ssid_len = len; 495189251Ssam break; 496189251Ssam case ATTR_NETWORK_KEY: 497189251Ssam attr->network_key = pos; 498189251Ssam attr->network_key_len = len; 499189251Ssam break; 500189251Ssam case ATTR_EAP_TYPE: 501189251Ssam attr->eap_type = pos; 502189251Ssam attr->eap_type_len = len; 503189251Ssam break; 504189251Ssam case ATTR_EAP_IDENTITY: 505189251Ssam attr->eap_identity = pos; 506189251Ssam attr->eap_identity_len = len; 507189251Ssam break; 508209158Srpaulo case ATTR_AP_SETUP_LOCKED: 509209158Srpaulo if (len != 1) { 510209158Srpaulo wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked " 511209158Srpaulo "length %u", len); 512209158Srpaulo return -1; 513209158Srpaulo } 514209158Srpaulo attr->ap_setup_locked = pos; 515209158Srpaulo break; 516252726Srpaulo case ATTR_REQUESTED_DEV_TYPE: 517252726Srpaulo if (len != WPS_DEV_TYPE_LEN) { 518252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: Invalid Requested Device " 519252726Srpaulo "Type length %u", len); 520252726Srpaulo return -1; 521252726Srpaulo } 522252726Srpaulo if (attr->num_req_dev_type >= MAX_REQ_DEV_TYPE_COUNT) { 523252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: Skipped Requested Device " 524252726Srpaulo "Type attribute (max %u types)", 525252726Srpaulo MAX_REQ_DEV_TYPE_COUNT); 526252726Srpaulo break; 527252726Srpaulo } 528252726Srpaulo attr->req_dev_type[attr->num_req_dev_type] = pos; 529252726Srpaulo attr->num_req_dev_type++; 530252726Srpaulo break; 531252726Srpaulo case ATTR_SECONDARY_DEV_TYPE_LIST: 532252726Srpaulo if (len > WPS_SEC_DEV_TYPE_MAX_LEN || 533252726Srpaulo (len % WPS_DEV_TYPE_LEN) > 0) { 534252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: Invalid Secondary Device " 535252726Srpaulo "Type length %u", len); 536252726Srpaulo return -1; 537252726Srpaulo } 538252726Srpaulo attr->sec_dev_type_list = pos; 539252726Srpaulo attr->sec_dev_type_list_len = len; 540252726Srpaulo break; 541252726Srpaulo case ATTR_VENDOR_EXT: 542252726Srpaulo if (wps_parse_vendor_ext(attr, pos, len) < 0) 543252726Srpaulo return -1; 544252726Srpaulo break; 545252726Srpaulo case ATTR_AP_CHANNEL: 546252726Srpaulo if (len != 2) { 547252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: Invalid AP Channel " 548252726Srpaulo "length %u", len); 549252726Srpaulo return -1; 550252726Srpaulo } 551252726Srpaulo attr->ap_channel = pos; 552252726Srpaulo break; 553189251Ssam default: 554189251Ssam wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x " 555189251Ssam "len=%u", type, len); 556189251Ssam break; 557189251Ssam } 558189251Ssam 559189251Ssam return 0; 560189251Ssam} 561189251Ssam 562189251Ssam 563189251Ssamint wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr) 564189251Ssam{ 565189251Ssam const u8 *pos, *end; 566189251Ssam u16 type, len; 567252726Srpaulo#ifdef WPS_WORKAROUNDS 568252726Srpaulo u16 prev_type = 0; 569252726Srpaulo#endif /* WPS_WORKAROUNDS */ 570189251Ssam 571189251Ssam os_memset(attr, 0, sizeof(*attr)); 572189251Ssam pos = wpabuf_head(msg); 573189251Ssam end = pos + wpabuf_len(msg); 574189251Ssam 575189251Ssam while (pos < end) { 576189251Ssam if (end - pos < 4) { 577189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid message - " 578189251Ssam "%lu bytes remaining", 579189251Ssam (unsigned long) (end - pos)); 580189251Ssam return -1; 581189251Ssam } 582189251Ssam 583189251Ssam type = WPA_GET_BE16(pos); 584189251Ssam pos += 2; 585189251Ssam len = WPA_GET_BE16(pos); 586189251Ssam pos += 2; 587252726Srpaulo wpa_printf(MSG_EXCESSIVE, "WPS: attr type=0x%x len=%u", 588189251Ssam type, len); 589189251Ssam if (len > end - pos) { 590189251Ssam wpa_printf(MSG_DEBUG, "WPS: Attribute overflow"); 591252726Srpaulo wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Message data", msg); 592252726Srpaulo#ifdef WPS_WORKAROUNDS 593252726Srpaulo /* 594252726Srpaulo * Some deployed APs seem to have a bug in encoding of 595252726Srpaulo * Network Key attribute in the Credential attribute 596252726Srpaulo * where they add an extra octet after the Network Key 597252726Srpaulo * attribute at least when open network is being 598252726Srpaulo * provisioned. 599252726Srpaulo */ 600252726Srpaulo if ((type & 0xff00) != 0x1000 && 601252726Srpaulo prev_type == ATTR_NETWORK_KEY) { 602252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: Workaround - try " 603252726Srpaulo "to skip unexpected octet after " 604252726Srpaulo "Network Key"); 605252726Srpaulo pos -= 3; 606252726Srpaulo continue; 607252726Srpaulo } 608252726Srpaulo#endif /* WPS_WORKAROUNDS */ 609189251Ssam return -1; 610189251Ssam } 611189251Ssam 612214734Srpaulo#ifdef WPS_WORKAROUNDS 613214734Srpaulo if (type == 0 && len == 0) { 614214734Srpaulo /* 615214734Srpaulo * Mac OS X 10.6 seems to be adding 0x00 padding to the 616214734Srpaulo * end of M1. Skip those to avoid interop issues. 617214734Srpaulo */ 618214734Srpaulo int i; 619214734Srpaulo for (i = 0; i < end - pos; i++) { 620214734Srpaulo if (pos[i]) 621214734Srpaulo break; 622214734Srpaulo } 623214734Srpaulo if (i == end - pos) { 624214734Srpaulo wpa_printf(MSG_DEBUG, "WPS: Workaround - skip " 625214734Srpaulo "unexpected message padding"); 626214734Srpaulo break; 627214734Srpaulo } 628214734Srpaulo } 629214734Srpaulo#endif /* WPS_WORKAROUNDS */ 630214734Srpaulo 631189251Ssam if (wps_set_attr(attr, type, pos, len) < 0) 632189251Ssam return -1; 633189251Ssam 634252726Srpaulo#ifdef WPS_WORKAROUNDS 635252726Srpaulo prev_type = type; 636252726Srpaulo#endif /* WPS_WORKAROUNDS */ 637189251Ssam pos += len; 638189251Ssam } 639189251Ssam 640189251Ssam return 0; 641189251Ssam} 642