1189251Ssam/* 2189251Ssam * Wi-Fi Protected Setup - attribute parsing 3189251Ssam * Copyright (c) 2008, Jouni Malinen <j@w1.fi> 4189251Ssam * 5189251Ssam * This program is free software; you can redistribute it and/or modify 6189251Ssam * it under the terms of the GNU General Public License version 2 as 7189251Ssam * published by the Free Software Foundation. 8189251Ssam * 9189251Ssam * Alternatively, this software may be distributed under the terms of BSD 10189251Ssam * license. 11189251Ssam * 12189251Ssam * See README and COPYING for more details. 13189251Ssam */ 14189251Ssam 15189251Ssam#include "includes.h" 16189251Ssam 17189251Ssam#include "common.h" 18189251Ssam#include "wps_i.h" 19189251Ssam 20214734Srpaulo#define WPS_WORKAROUNDS 21189251Ssam 22214734Srpaulo 23189251Ssamstatic int wps_set_attr(struct wps_parse_attr *attr, u16 type, 24189251Ssam const u8 *pos, u16 len) 25189251Ssam{ 26189251Ssam switch (type) { 27189251Ssam case ATTR_VERSION: 28189251Ssam if (len != 1) { 29189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u", 30189251Ssam len); 31189251Ssam return -1; 32189251Ssam } 33189251Ssam attr->version = pos; 34189251Ssam break; 35189251Ssam case ATTR_MSG_TYPE: 36189251Ssam if (len != 1) { 37189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type " 38189251Ssam "length %u", len); 39189251Ssam return -1; 40189251Ssam } 41189251Ssam attr->msg_type = pos; 42189251Ssam break; 43189251Ssam case ATTR_ENROLLEE_NONCE: 44189251Ssam if (len != WPS_NONCE_LEN) { 45189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce " 46189251Ssam "length %u", len); 47189251Ssam return -1; 48189251Ssam } 49189251Ssam attr->enrollee_nonce = pos; 50189251Ssam break; 51189251Ssam case ATTR_REGISTRAR_NONCE: 52189251Ssam if (len != WPS_NONCE_LEN) { 53189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce " 54189251Ssam "length %u", len); 55189251Ssam return -1; 56189251Ssam } 57189251Ssam attr->registrar_nonce = pos; 58189251Ssam break; 59189251Ssam case ATTR_UUID_E: 60189251Ssam if (len != WPS_UUID_LEN) { 61189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u", 62189251Ssam len); 63189251Ssam return -1; 64189251Ssam } 65189251Ssam attr->uuid_e = pos; 66189251Ssam break; 67189251Ssam case ATTR_UUID_R: 68189251Ssam if (len != WPS_UUID_LEN) { 69189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u", 70189251Ssam len); 71189251Ssam return -1; 72189251Ssam } 73189251Ssam attr->uuid_r = pos; 74189251Ssam break; 75189251Ssam case ATTR_AUTH_TYPE_FLAGS: 76189251Ssam if (len != 2) { 77189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication " 78189251Ssam "Type Flags length %u", len); 79189251Ssam return -1; 80189251Ssam } 81189251Ssam attr->auth_type_flags = pos; 82189251Ssam break; 83189251Ssam case ATTR_ENCR_TYPE_FLAGS: 84189251Ssam if (len != 2) { 85189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type " 86189251Ssam "Flags length %u", len); 87189251Ssam return -1; 88189251Ssam } 89189251Ssam attr->encr_type_flags = pos; 90189251Ssam break; 91189251Ssam case ATTR_CONN_TYPE_FLAGS: 92189251Ssam if (len != 1) { 93189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type " 94189251Ssam "Flags length %u", len); 95189251Ssam return -1; 96189251Ssam } 97189251Ssam attr->conn_type_flags = pos; 98189251Ssam break; 99189251Ssam case ATTR_CONFIG_METHODS: 100189251Ssam if (len != 2) { 101189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods " 102189251Ssam "length %u", len); 103189251Ssam return -1; 104189251Ssam } 105189251Ssam attr->config_methods = pos; 106189251Ssam break; 107189251Ssam case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS: 108189251Ssam if (len != 2) { 109189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Selected " 110189251Ssam "Registrar Config Methods length %u", len); 111189251Ssam return -1; 112189251Ssam } 113189251Ssam attr->sel_reg_config_methods = pos; 114189251Ssam break; 115189251Ssam case ATTR_PRIMARY_DEV_TYPE: 116214734Srpaulo if (len != WPS_DEV_TYPE_LEN) { 117189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device " 118189251Ssam "Type length %u", len); 119189251Ssam return -1; 120189251Ssam } 121189251Ssam attr->primary_dev_type = pos; 122189251Ssam break; 123189251Ssam case ATTR_RF_BANDS: 124189251Ssam if (len != 1) { 125189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length " 126189251Ssam "%u", len); 127189251Ssam return -1; 128189251Ssam } 129189251Ssam attr->rf_bands = pos; 130189251Ssam break; 131189251Ssam case ATTR_ASSOC_STATE: 132189251Ssam if (len != 2) { 133189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Association State " 134189251Ssam "length %u", len); 135189251Ssam return -1; 136189251Ssam } 137189251Ssam attr->assoc_state = pos; 138189251Ssam break; 139189251Ssam case ATTR_CONFIG_ERROR: 140189251Ssam if (len != 2) { 141189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration " 142189251Ssam "Error length %u", len); 143189251Ssam return -1; 144189251Ssam } 145189251Ssam attr->config_error = pos; 146189251Ssam break; 147189251Ssam case ATTR_DEV_PASSWORD_ID: 148189251Ssam if (len != 2) { 149189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password " 150189251Ssam "ID length %u", len); 151189251Ssam return -1; 152189251Ssam } 153189251Ssam attr->dev_password_id = pos; 154189251Ssam break; 155214734Srpaulo case ATTR_OOB_DEVICE_PASSWORD: 156214734Srpaulo if (len != WPS_OOB_DEVICE_PASSWORD_ATTR_LEN) { 157214734Srpaulo wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device " 158214734Srpaulo "Password length %u", len); 159214734Srpaulo return -1; 160214734Srpaulo } 161214734Srpaulo attr->oob_dev_password = pos; 162214734Srpaulo break; 163189251Ssam case ATTR_OS_VERSION: 164189251Ssam if (len != 4) { 165189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length " 166189251Ssam "%u", len); 167189251Ssam return -1; 168189251Ssam } 169189251Ssam attr->os_version = pos; 170189251Ssam break; 171189251Ssam case ATTR_WPS_STATE: 172189251Ssam if (len != 1) { 173189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected " 174189251Ssam "Setup State length %u", len); 175189251Ssam return -1; 176189251Ssam } 177189251Ssam attr->wps_state = pos; 178189251Ssam break; 179189251Ssam case ATTR_AUTHENTICATOR: 180189251Ssam if (len != WPS_AUTHENTICATOR_LEN) { 181189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator " 182189251Ssam "length %u", len); 183189251Ssam return -1; 184189251Ssam } 185189251Ssam attr->authenticator = pos; 186189251Ssam break; 187189251Ssam case ATTR_R_HASH1: 188189251Ssam if (len != WPS_HASH_LEN) { 189189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u", 190189251Ssam len); 191189251Ssam return -1; 192189251Ssam } 193189251Ssam attr->r_hash1 = pos; 194189251Ssam break; 195189251Ssam case ATTR_R_HASH2: 196189251Ssam if (len != WPS_HASH_LEN) { 197189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u", 198189251Ssam len); 199189251Ssam return -1; 200189251Ssam } 201189251Ssam attr->r_hash2 = pos; 202189251Ssam break; 203189251Ssam case ATTR_E_HASH1: 204189251Ssam if (len != WPS_HASH_LEN) { 205189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u", 206189251Ssam len); 207189251Ssam return -1; 208189251Ssam } 209189251Ssam attr->e_hash1 = pos; 210189251Ssam break; 211189251Ssam case ATTR_E_HASH2: 212189251Ssam if (len != WPS_HASH_LEN) { 213189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u", 214189251Ssam len); 215189251Ssam return -1; 216189251Ssam } 217189251Ssam attr->e_hash2 = pos; 218189251Ssam break; 219189251Ssam case ATTR_R_SNONCE1: 220189251Ssam if (len != WPS_SECRET_NONCE_LEN) { 221189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length " 222189251Ssam "%u", len); 223189251Ssam return -1; 224189251Ssam } 225189251Ssam attr->r_snonce1 = pos; 226189251Ssam break; 227189251Ssam case ATTR_R_SNONCE2: 228189251Ssam if (len != WPS_SECRET_NONCE_LEN) { 229189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length " 230189251Ssam "%u", len); 231189251Ssam return -1; 232189251Ssam } 233189251Ssam attr->r_snonce2 = pos; 234189251Ssam break; 235189251Ssam case ATTR_E_SNONCE1: 236189251Ssam if (len != WPS_SECRET_NONCE_LEN) { 237189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length " 238189251Ssam "%u", len); 239189251Ssam return -1; 240189251Ssam } 241189251Ssam attr->e_snonce1 = pos; 242189251Ssam break; 243189251Ssam case ATTR_E_SNONCE2: 244189251Ssam if (len != WPS_SECRET_NONCE_LEN) { 245189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length " 246189251Ssam "%u", len); 247189251Ssam return -1; 248189251Ssam } 249189251Ssam attr->e_snonce2 = pos; 250189251Ssam break; 251189251Ssam case ATTR_KEY_WRAP_AUTH: 252189251Ssam if (len != WPS_KWA_LEN) { 253189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap " 254189251Ssam "Authenticator length %u", len); 255189251Ssam return -1; 256189251Ssam } 257189251Ssam attr->key_wrap_auth = pos; 258189251Ssam break; 259189251Ssam case ATTR_AUTH_TYPE: 260189251Ssam if (len != 2) { 261189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication " 262189251Ssam "Type length %u", len); 263189251Ssam return -1; 264189251Ssam } 265189251Ssam attr->auth_type = pos; 266189251Ssam break; 267189251Ssam case ATTR_ENCR_TYPE: 268189251Ssam if (len != 2) { 269189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption " 270189251Ssam "Type length %u", len); 271189251Ssam return -1; 272189251Ssam } 273189251Ssam attr->encr_type = pos; 274189251Ssam break; 275189251Ssam case ATTR_NETWORK_INDEX: 276189251Ssam if (len != 1) { 277189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index " 278189251Ssam "length %u", len); 279189251Ssam return -1; 280189251Ssam } 281189251Ssam attr->network_idx = pos; 282189251Ssam break; 283189251Ssam case ATTR_NETWORK_KEY_INDEX: 284189251Ssam if (len != 1) { 285189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index " 286189251Ssam "length %u", len); 287189251Ssam return -1; 288189251Ssam } 289189251Ssam attr->network_key_idx = pos; 290189251Ssam break; 291189251Ssam case ATTR_MAC_ADDR: 292189251Ssam if (len != ETH_ALEN) { 293189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address " 294189251Ssam "length %u", len); 295189251Ssam return -1; 296189251Ssam } 297189251Ssam attr->mac_addr = pos; 298189251Ssam break; 299189251Ssam case ATTR_KEY_PROVIDED_AUTO: 300189251Ssam if (len != 1) { 301189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Key Provided " 302189251Ssam "Automatically length %u", len); 303189251Ssam return -1; 304189251Ssam } 305189251Ssam attr->key_prov_auto = pos; 306189251Ssam break; 307189251Ssam case ATTR_802_1X_ENABLED: 308189251Ssam if (len != 1) { 309189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid 802.1X Enabled " 310189251Ssam "length %u", len); 311189251Ssam return -1; 312189251Ssam } 313189251Ssam attr->dot1x_enabled = pos; 314189251Ssam break; 315189251Ssam case ATTR_SELECTED_REGISTRAR: 316189251Ssam if (len != 1) { 317189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar" 318189251Ssam " length %u", len); 319189251Ssam return -1; 320189251Ssam } 321189251Ssam attr->selected_registrar = pos; 322189251Ssam break; 323189251Ssam case ATTR_REQUEST_TYPE: 324189251Ssam if (len != 1) { 325189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Request Type " 326189251Ssam "length %u", len); 327189251Ssam return -1; 328189251Ssam } 329189251Ssam attr->request_type = pos; 330189251Ssam break; 331189251Ssam case ATTR_RESPONSE_TYPE: 332189251Ssam if (len != 1) { 333189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Response Type " 334189251Ssam "length %u", len); 335189251Ssam return -1; 336189251Ssam } 337214734Srpaulo attr->response_type = pos; 338189251Ssam break; 339189251Ssam case ATTR_MANUFACTURER: 340189251Ssam attr->manufacturer = pos; 341189251Ssam attr->manufacturer_len = len; 342189251Ssam break; 343189251Ssam case ATTR_MODEL_NAME: 344189251Ssam attr->model_name = pos; 345189251Ssam attr->model_name_len = len; 346189251Ssam break; 347189251Ssam case ATTR_MODEL_NUMBER: 348189251Ssam attr->model_number = pos; 349189251Ssam attr->model_number_len = len; 350189251Ssam break; 351189251Ssam case ATTR_SERIAL_NUMBER: 352189251Ssam attr->serial_number = pos; 353189251Ssam attr->serial_number_len = len; 354189251Ssam break; 355189251Ssam case ATTR_DEV_NAME: 356189251Ssam attr->dev_name = pos; 357189251Ssam attr->dev_name_len = len; 358189251Ssam break; 359189251Ssam case ATTR_PUBLIC_KEY: 360189251Ssam attr->public_key = pos; 361189251Ssam attr->public_key_len = len; 362189251Ssam break; 363189251Ssam case ATTR_ENCR_SETTINGS: 364189251Ssam attr->encr_settings = pos; 365189251Ssam attr->encr_settings_len = len; 366189251Ssam break; 367189251Ssam case ATTR_CRED: 368189251Ssam if (attr->num_cred >= MAX_CRED_COUNT) { 369189251Ssam wpa_printf(MSG_DEBUG, "WPS: Skipped Credential " 370189251Ssam "attribute (max %d credentials)", 371189251Ssam MAX_CRED_COUNT); 372189251Ssam break; 373189251Ssam } 374189251Ssam attr->cred[attr->num_cred] = pos; 375189251Ssam attr->cred_len[attr->num_cred] = len; 376189251Ssam attr->num_cred++; 377189251Ssam break; 378189251Ssam case ATTR_SSID: 379189251Ssam attr->ssid = pos; 380189251Ssam attr->ssid_len = len; 381189251Ssam break; 382189251Ssam case ATTR_NETWORK_KEY: 383189251Ssam attr->network_key = pos; 384189251Ssam attr->network_key_len = len; 385189251Ssam break; 386189251Ssam case ATTR_EAP_TYPE: 387189251Ssam attr->eap_type = pos; 388189251Ssam attr->eap_type_len = len; 389189251Ssam break; 390189251Ssam case ATTR_EAP_IDENTITY: 391189251Ssam attr->eap_identity = pos; 392189251Ssam attr->eap_identity_len = len; 393189251Ssam break; 394209158Srpaulo case ATTR_AP_SETUP_LOCKED: 395209158Srpaulo if (len != 1) { 396209158Srpaulo wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked " 397209158Srpaulo "length %u", len); 398209158Srpaulo return -1; 399209158Srpaulo } 400209158Srpaulo attr->ap_setup_locked = pos; 401209158Srpaulo break; 402189251Ssam default: 403189251Ssam wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x " 404189251Ssam "len=%u", type, len); 405189251Ssam break; 406189251Ssam } 407189251Ssam 408189251Ssam return 0; 409189251Ssam} 410189251Ssam 411189251Ssam 412189251Ssamint wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr) 413189251Ssam{ 414189251Ssam const u8 *pos, *end; 415189251Ssam u16 type, len; 416189251Ssam 417189251Ssam os_memset(attr, 0, sizeof(*attr)); 418189251Ssam pos = wpabuf_head(msg); 419189251Ssam end = pos + wpabuf_len(msg); 420189251Ssam 421189251Ssam while (pos < end) { 422189251Ssam if (end - pos < 4) { 423189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid message - " 424189251Ssam "%lu bytes remaining", 425189251Ssam (unsigned long) (end - pos)); 426189251Ssam return -1; 427189251Ssam } 428189251Ssam 429189251Ssam type = WPA_GET_BE16(pos); 430189251Ssam pos += 2; 431189251Ssam len = WPA_GET_BE16(pos); 432189251Ssam pos += 2; 433189251Ssam wpa_printf(MSG_MSGDUMP, "WPS: attr type=0x%x len=%u", 434189251Ssam type, len); 435189251Ssam if (len > end - pos) { 436189251Ssam wpa_printf(MSG_DEBUG, "WPS: Attribute overflow"); 437189251Ssam return -1; 438189251Ssam } 439189251Ssam 440214734Srpaulo#ifdef WPS_WORKAROUNDS 441214734Srpaulo if (type == 0 && len == 0) { 442214734Srpaulo /* 443214734Srpaulo * Mac OS X 10.6 seems to be adding 0x00 padding to the 444214734Srpaulo * end of M1. Skip those to avoid interop issues. 445214734Srpaulo */ 446214734Srpaulo int i; 447214734Srpaulo for (i = 0; i < end - pos; i++) { 448214734Srpaulo if (pos[i]) 449214734Srpaulo break; 450214734Srpaulo } 451214734Srpaulo if (i == end - pos) { 452214734Srpaulo wpa_printf(MSG_DEBUG, "WPS: Workaround - skip " 453214734Srpaulo "unexpected message padding"); 454214734Srpaulo break; 455214734Srpaulo } 456214734Srpaulo } 457214734Srpaulo#endif /* WPS_WORKAROUNDS */ 458214734Srpaulo 459189251Ssam if (wps_set_attr(attr, type, pos, len) < 0) 460189251Ssam return -1; 461189251Ssam 462189251Ssam pos += len; 463189251Ssam } 464189251Ssam 465189251Ssam return 0; 466189251Ssam} 467