1189251Ssam/* 2189251Ssam * Wi-Fi Protected Setup - Enrollee 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" 12214734Srpaulo#include "crypto/crypto.h" 13214734Srpaulo#include "crypto/sha256.h" 14252726Srpaulo#include "crypto/random.h" 15189251Ssam#include "wps_i.h" 16189251Ssam#include "wps_dev_attr.h" 17189251Ssam 18189251Ssam 19189251Ssamstatic int wps_build_mac_addr(struct wps_data *wps, struct wpabuf *msg) 20189251Ssam{ 21189251Ssam wpa_printf(MSG_DEBUG, "WPS: * MAC Address"); 22189251Ssam wpabuf_put_be16(msg, ATTR_MAC_ADDR); 23189251Ssam wpabuf_put_be16(msg, ETH_ALEN); 24189251Ssam wpabuf_put_data(msg, wps->mac_addr_e, ETH_ALEN); 25189251Ssam return 0; 26189251Ssam} 27189251Ssam 28189251Ssam 29189251Ssamstatic int wps_build_wps_state(struct wps_data *wps, struct wpabuf *msg) 30189251Ssam{ 31189251Ssam u8 state; 32189251Ssam if (wps->wps->ap) 33189251Ssam state = wps->wps->wps_state; 34189251Ssam else 35189251Ssam state = WPS_STATE_NOT_CONFIGURED; 36189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Wi-Fi Protected Setup State (%d)", 37189251Ssam state); 38189251Ssam wpabuf_put_be16(msg, ATTR_WPS_STATE); 39189251Ssam wpabuf_put_be16(msg, 1); 40209158Srpaulo wpabuf_put_u8(msg, state); 41189251Ssam return 0; 42189251Ssam} 43189251Ssam 44189251Ssam 45189251Ssamstatic int wps_build_e_hash(struct wps_data *wps, struct wpabuf *msg) 46189251Ssam{ 47189251Ssam u8 *hash; 48189251Ssam const u8 *addr[4]; 49189251Ssam size_t len[4]; 50189251Ssam 51252726Srpaulo if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0) 52189251Ssam return -1; 53189251Ssam wpa_hexdump(MSG_DEBUG, "WPS: E-S1", wps->snonce, WPS_SECRET_NONCE_LEN); 54189251Ssam wpa_hexdump(MSG_DEBUG, "WPS: E-S2", 55189251Ssam wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN); 56189251Ssam 57189251Ssam if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) { 58189251Ssam wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for " 59189251Ssam "E-Hash derivation"); 60189251Ssam return -1; 61189251Ssam } 62189251Ssam 63189251Ssam wpa_printf(MSG_DEBUG, "WPS: * E-Hash1"); 64189251Ssam wpabuf_put_be16(msg, ATTR_E_HASH1); 65189251Ssam wpabuf_put_be16(msg, SHA256_MAC_LEN); 66189251Ssam hash = wpabuf_put(msg, SHA256_MAC_LEN); 67189251Ssam /* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */ 68189251Ssam addr[0] = wps->snonce; 69189251Ssam len[0] = WPS_SECRET_NONCE_LEN; 70189251Ssam addr[1] = wps->psk1; 71189251Ssam len[1] = WPS_PSK_LEN; 72189251Ssam addr[2] = wpabuf_head(wps->dh_pubkey_e); 73189251Ssam len[2] = wpabuf_len(wps->dh_pubkey_e); 74189251Ssam addr[3] = wpabuf_head(wps->dh_pubkey_r); 75189251Ssam len[3] = wpabuf_len(wps->dh_pubkey_r); 76189251Ssam hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash); 77189251Ssam wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", hash, SHA256_MAC_LEN); 78189251Ssam 79189251Ssam wpa_printf(MSG_DEBUG, "WPS: * E-Hash2"); 80189251Ssam wpabuf_put_be16(msg, ATTR_E_HASH2); 81189251Ssam wpabuf_put_be16(msg, SHA256_MAC_LEN); 82189251Ssam hash = wpabuf_put(msg, SHA256_MAC_LEN); 83189251Ssam /* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */ 84189251Ssam addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN; 85189251Ssam addr[1] = wps->psk2; 86189251Ssam hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash); 87189251Ssam wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", hash, SHA256_MAC_LEN); 88189251Ssam 89189251Ssam return 0; 90189251Ssam} 91189251Ssam 92189251Ssam 93189251Ssamstatic int wps_build_e_snonce1(struct wps_data *wps, struct wpabuf *msg) 94189251Ssam{ 95189251Ssam wpa_printf(MSG_DEBUG, "WPS: * E-SNonce1"); 96189251Ssam wpabuf_put_be16(msg, ATTR_E_SNONCE1); 97189251Ssam wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN); 98189251Ssam wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN); 99189251Ssam return 0; 100189251Ssam} 101189251Ssam 102189251Ssam 103189251Ssamstatic int wps_build_e_snonce2(struct wps_data *wps, struct wpabuf *msg) 104189251Ssam{ 105189251Ssam wpa_printf(MSG_DEBUG, "WPS: * E-SNonce2"); 106189251Ssam wpabuf_put_be16(msg, ATTR_E_SNONCE2); 107189251Ssam wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN); 108189251Ssam wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN, 109189251Ssam WPS_SECRET_NONCE_LEN); 110189251Ssam return 0; 111189251Ssam} 112189251Ssam 113189251Ssam 114189251Ssamstatic struct wpabuf * wps_build_m1(struct wps_data *wps) 115189251Ssam{ 116189251Ssam struct wpabuf *msg; 117252726Srpaulo u16 config_methods; 118189251Ssam 119252726Srpaulo if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0) 120189251Ssam return NULL; 121189251Ssam wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce", 122189251Ssam wps->nonce_e, WPS_NONCE_LEN); 123189251Ssam 124189251Ssam wpa_printf(MSG_DEBUG, "WPS: Building Message M1"); 125189251Ssam msg = wpabuf_alloc(1000); 126189251Ssam if (msg == NULL) 127189251Ssam return NULL; 128189251Ssam 129252726Srpaulo config_methods = wps->wps->config_methods; 130252726Srpaulo if (wps->wps->ap && !wps->pbc_in_m1 && 131252726Srpaulo (wps->dev_password_len != 0 || 132252726Srpaulo (config_methods & WPS_CONFIG_DISPLAY))) { 133252726Srpaulo /* 134252726Srpaulo * These are the methods that the AP supports as an Enrollee 135252726Srpaulo * for adding external Registrars, so remove PushButton. 136252726Srpaulo * 137252726Srpaulo * As a workaround for Windows 7 mechanism for probing WPS 138252726Srpaulo * capabilities from M1, leave PushButton option if no PIN 139252726Srpaulo * method is available or if WPS configuration enables PBC 140252726Srpaulo * workaround. 141252726Srpaulo */ 142252726Srpaulo config_methods &= ~WPS_CONFIG_PUSHBUTTON; 143252726Srpaulo#ifdef CONFIG_WPS2 144252726Srpaulo config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | 145252726Srpaulo WPS_CONFIG_PHY_PUSHBUTTON); 146252726Srpaulo#endif /* CONFIG_WPS2 */ 147252726Srpaulo } 148252726Srpaulo 149189251Ssam if (wps_build_version(msg) || 150189251Ssam wps_build_msg_type(msg, WPS_M1) || 151189251Ssam wps_build_uuid_e(msg, wps->uuid_e) || 152189251Ssam wps_build_mac_addr(wps, msg) || 153189251Ssam wps_build_enrollee_nonce(wps, msg) || 154189251Ssam wps_build_public_key(wps, msg) || 155189251Ssam wps_build_auth_type_flags(wps, msg) || 156189251Ssam wps_build_encr_type_flags(wps, msg) || 157189251Ssam wps_build_conn_type_flags(wps, msg) || 158252726Srpaulo wps_build_config_methods(msg, config_methods) || 159189251Ssam wps_build_wps_state(wps, msg) || 160189251Ssam wps_build_device_attrs(&wps->wps->dev, msg) || 161189251Ssam wps_build_rf_bands(&wps->wps->dev, msg) || 162189251Ssam wps_build_assoc_state(wps, msg) || 163189251Ssam wps_build_dev_password_id(msg, wps->dev_pw_id) || 164189251Ssam wps_build_config_error(msg, WPS_CFG_NO_ERROR) || 165252726Srpaulo wps_build_os_version(&wps->wps->dev, msg) || 166252726Srpaulo wps_build_wfa_ext(msg, 0, NULL, 0) || 167252726Srpaulo wps_build_vendor_ext_m1(&wps->wps->dev, msg)) { 168189251Ssam wpabuf_free(msg); 169189251Ssam return NULL; 170189251Ssam } 171189251Ssam 172189251Ssam wps->state = RECV_M2; 173189251Ssam return msg; 174189251Ssam} 175189251Ssam 176189251Ssam 177189251Ssamstatic struct wpabuf * wps_build_m3(struct wps_data *wps) 178189251Ssam{ 179189251Ssam struct wpabuf *msg; 180189251Ssam 181189251Ssam wpa_printf(MSG_DEBUG, "WPS: Building Message M3"); 182189251Ssam 183189251Ssam if (wps->dev_password == NULL) { 184189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Device Password available"); 185189251Ssam return NULL; 186189251Ssam } 187189251Ssam wps_derive_psk(wps, wps->dev_password, wps->dev_password_len); 188189251Ssam 189189251Ssam msg = wpabuf_alloc(1000); 190189251Ssam if (msg == NULL) 191189251Ssam return NULL; 192189251Ssam 193189251Ssam if (wps_build_version(msg) || 194189251Ssam wps_build_msg_type(msg, WPS_M3) || 195189251Ssam wps_build_registrar_nonce(wps, msg) || 196189251Ssam wps_build_e_hash(wps, msg) || 197252726Srpaulo wps_build_wfa_ext(msg, 0, NULL, 0) || 198189251Ssam wps_build_authenticator(wps, msg)) { 199189251Ssam wpabuf_free(msg); 200189251Ssam return NULL; 201189251Ssam } 202189251Ssam 203189251Ssam wps->state = RECV_M4; 204189251Ssam return msg; 205189251Ssam} 206189251Ssam 207189251Ssam 208189251Ssamstatic struct wpabuf * wps_build_m5(struct wps_data *wps) 209189251Ssam{ 210189251Ssam struct wpabuf *msg, *plain; 211189251Ssam 212189251Ssam wpa_printf(MSG_DEBUG, "WPS: Building Message M5"); 213189251Ssam 214189251Ssam plain = wpabuf_alloc(200); 215189251Ssam if (plain == NULL) 216189251Ssam return NULL; 217189251Ssam 218189251Ssam msg = wpabuf_alloc(1000); 219189251Ssam if (msg == NULL) { 220189251Ssam wpabuf_free(plain); 221189251Ssam return NULL; 222189251Ssam } 223189251Ssam 224189251Ssam if (wps_build_version(msg) || 225189251Ssam wps_build_msg_type(msg, WPS_M5) || 226189251Ssam wps_build_registrar_nonce(wps, msg) || 227189251Ssam wps_build_e_snonce1(wps, plain) || 228189251Ssam wps_build_key_wrap_auth(wps, plain) || 229189251Ssam wps_build_encr_settings(wps, msg, plain) || 230252726Srpaulo wps_build_wfa_ext(msg, 0, NULL, 0) || 231189251Ssam wps_build_authenticator(wps, msg)) { 232189251Ssam wpabuf_free(plain); 233189251Ssam wpabuf_free(msg); 234189251Ssam return NULL; 235189251Ssam } 236189251Ssam wpabuf_free(plain); 237189251Ssam 238189251Ssam wps->state = RECV_M6; 239189251Ssam return msg; 240189251Ssam} 241189251Ssam 242189251Ssam 243189251Ssamstatic int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg) 244189251Ssam{ 245189251Ssam wpa_printf(MSG_DEBUG, "WPS: * SSID"); 246189251Ssam wpabuf_put_be16(msg, ATTR_SSID); 247189251Ssam wpabuf_put_be16(msg, wps->wps->ssid_len); 248189251Ssam wpabuf_put_data(msg, wps->wps->ssid, wps->wps->ssid_len); 249189251Ssam return 0; 250189251Ssam} 251189251Ssam 252189251Ssam 253189251Ssamstatic int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg) 254189251Ssam{ 255252726Srpaulo u16 auth_type = wps->wps->auth_types; 256252726Srpaulo 257252726Srpaulo /* Select the best authentication type */ 258252726Srpaulo if (auth_type & WPS_AUTH_WPA2PSK) 259252726Srpaulo auth_type = WPS_AUTH_WPA2PSK; 260252726Srpaulo else if (auth_type & WPS_AUTH_WPAPSK) 261252726Srpaulo auth_type = WPS_AUTH_WPAPSK; 262252726Srpaulo else if (auth_type & WPS_AUTH_OPEN) 263252726Srpaulo auth_type = WPS_AUTH_OPEN; 264252726Srpaulo else if (auth_type & WPS_AUTH_SHARED) 265252726Srpaulo auth_type = WPS_AUTH_SHARED; 266252726Srpaulo 267252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: * Authentication Type (0x%x)", auth_type); 268189251Ssam wpabuf_put_be16(msg, ATTR_AUTH_TYPE); 269189251Ssam wpabuf_put_be16(msg, 2); 270252726Srpaulo wpabuf_put_be16(msg, auth_type); 271189251Ssam return 0; 272189251Ssam} 273189251Ssam 274189251Ssam 275189251Ssamstatic int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg) 276189251Ssam{ 277252726Srpaulo u16 encr_type = wps->wps->encr_types; 278252726Srpaulo 279252726Srpaulo /* Select the best encryption type */ 280252726Srpaulo if (wps->wps->auth_types & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) { 281252726Srpaulo if (encr_type & WPS_ENCR_AES) 282252726Srpaulo encr_type = WPS_ENCR_AES; 283252726Srpaulo else if (encr_type & WPS_ENCR_TKIP) 284252726Srpaulo encr_type = WPS_ENCR_TKIP; 285252726Srpaulo } else { 286252726Srpaulo if (encr_type & WPS_ENCR_WEP) 287252726Srpaulo encr_type = WPS_ENCR_WEP; 288252726Srpaulo else if (encr_type & WPS_ENCR_NONE) 289252726Srpaulo encr_type = WPS_ENCR_NONE; 290252726Srpaulo } 291252726Srpaulo 292252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: * Encryption Type (0x%x)", encr_type); 293189251Ssam wpabuf_put_be16(msg, ATTR_ENCR_TYPE); 294189251Ssam wpabuf_put_be16(msg, 2); 295252726Srpaulo wpabuf_put_be16(msg, encr_type); 296189251Ssam return 0; 297189251Ssam} 298189251Ssam 299189251Ssam 300189251Ssamstatic int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg) 301189251Ssam{ 302189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Network Key"); 303189251Ssam wpabuf_put_be16(msg, ATTR_NETWORK_KEY); 304189251Ssam wpabuf_put_be16(msg, wps->wps->network_key_len); 305189251Ssam wpabuf_put_data(msg, wps->wps->network_key, wps->wps->network_key_len); 306189251Ssam return 0; 307189251Ssam} 308189251Ssam 309189251Ssam 310189251Ssamstatic int wps_build_cred_mac_addr(struct wps_data *wps, struct wpabuf *msg) 311189251Ssam{ 312189251Ssam wpa_printf(MSG_DEBUG, "WPS: * MAC Address (AP BSSID)"); 313189251Ssam wpabuf_put_be16(msg, ATTR_MAC_ADDR); 314189251Ssam wpabuf_put_be16(msg, ETH_ALEN); 315189251Ssam wpabuf_put_data(msg, wps->wps->dev.mac_addr, ETH_ALEN); 316189251Ssam return 0; 317189251Ssam} 318189251Ssam 319189251Ssam 320189251Ssamstatic int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *plain) 321189251Ssam{ 322189251Ssam if (wps->wps->ap_settings) { 323189251Ssam wpa_printf(MSG_DEBUG, "WPS: * AP Settings (pre-configured)"); 324189251Ssam wpabuf_put_data(plain, wps->wps->ap_settings, 325189251Ssam wps->wps->ap_settings_len); 326189251Ssam return 0; 327189251Ssam } 328189251Ssam 329189251Ssam return wps_build_cred_ssid(wps, plain) || 330189251Ssam wps_build_cred_mac_addr(wps, plain) || 331189251Ssam wps_build_cred_auth_type(wps, plain) || 332189251Ssam wps_build_cred_encr_type(wps, plain) || 333189251Ssam wps_build_cred_network_key(wps, plain); 334189251Ssam} 335189251Ssam 336189251Ssam 337189251Ssamstatic struct wpabuf * wps_build_m7(struct wps_data *wps) 338189251Ssam{ 339189251Ssam struct wpabuf *msg, *plain; 340189251Ssam 341189251Ssam wpa_printf(MSG_DEBUG, "WPS: Building Message M7"); 342189251Ssam 343189251Ssam plain = wpabuf_alloc(500 + wps->wps->ap_settings_len); 344189251Ssam if (plain == NULL) 345189251Ssam return NULL; 346189251Ssam 347189251Ssam msg = wpabuf_alloc(1000 + wps->wps->ap_settings_len); 348189251Ssam if (msg == NULL) { 349189251Ssam wpabuf_free(plain); 350189251Ssam return NULL; 351189251Ssam } 352189251Ssam 353189251Ssam if (wps_build_version(msg) || 354189251Ssam wps_build_msg_type(msg, WPS_M7) || 355189251Ssam wps_build_registrar_nonce(wps, msg) || 356189251Ssam wps_build_e_snonce2(wps, plain) || 357189251Ssam (wps->wps->ap && wps_build_ap_settings(wps, plain)) || 358189251Ssam wps_build_key_wrap_auth(wps, plain) || 359189251Ssam wps_build_encr_settings(wps, msg, plain) || 360252726Srpaulo wps_build_wfa_ext(msg, 0, NULL, 0) || 361189251Ssam wps_build_authenticator(wps, msg)) { 362189251Ssam wpabuf_free(plain); 363189251Ssam wpabuf_free(msg); 364189251Ssam return NULL; 365189251Ssam } 366189251Ssam wpabuf_free(plain); 367189251Ssam 368214734Srpaulo if (wps->wps->ap && wps->wps->registrar) { 369214734Srpaulo /* 370214734Srpaulo * If the Registrar is only learning our current configuration, 371214734Srpaulo * it may not continue protocol run to successful completion. 372214734Srpaulo * Store information here to make sure it remains available. 373214734Srpaulo */ 374214734Srpaulo wps_device_store(wps->wps->registrar, &wps->peer_dev, 375214734Srpaulo wps->uuid_r); 376214734Srpaulo } 377214734Srpaulo 378189251Ssam wps->state = RECV_M8; 379189251Ssam return msg; 380189251Ssam} 381189251Ssam 382189251Ssam 383189251Ssamstatic struct wpabuf * wps_build_wsc_done(struct wps_data *wps) 384189251Ssam{ 385189251Ssam struct wpabuf *msg; 386189251Ssam 387189251Ssam wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_Done"); 388189251Ssam 389189251Ssam msg = wpabuf_alloc(1000); 390189251Ssam if (msg == NULL) 391189251Ssam return NULL; 392189251Ssam 393189251Ssam if (wps_build_version(msg) || 394189251Ssam wps_build_msg_type(msg, WPS_WSC_DONE) || 395189251Ssam wps_build_enrollee_nonce(wps, msg) || 396252726Srpaulo wps_build_registrar_nonce(wps, msg) || 397252726Srpaulo wps_build_wfa_ext(msg, 0, NULL, 0)) { 398189251Ssam wpabuf_free(msg); 399189251Ssam return NULL; 400189251Ssam } 401189251Ssam 402189251Ssam if (wps->wps->ap) 403189251Ssam wps->state = RECV_ACK; 404189251Ssam else { 405189251Ssam wps_success_event(wps->wps); 406189251Ssam wps->state = WPS_FINISHED; 407189251Ssam } 408189251Ssam return msg; 409189251Ssam} 410189251Ssam 411189251Ssam 412189251Ssamstruct wpabuf * wps_enrollee_get_msg(struct wps_data *wps, 413189251Ssam enum wsc_op_code *op_code) 414189251Ssam{ 415189251Ssam struct wpabuf *msg; 416189251Ssam 417189251Ssam switch (wps->state) { 418189251Ssam case SEND_M1: 419189251Ssam msg = wps_build_m1(wps); 420189251Ssam *op_code = WSC_MSG; 421189251Ssam break; 422189251Ssam case SEND_M3: 423189251Ssam msg = wps_build_m3(wps); 424189251Ssam *op_code = WSC_MSG; 425189251Ssam break; 426189251Ssam case SEND_M5: 427189251Ssam msg = wps_build_m5(wps); 428189251Ssam *op_code = WSC_MSG; 429189251Ssam break; 430189251Ssam case SEND_M7: 431189251Ssam msg = wps_build_m7(wps); 432189251Ssam *op_code = WSC_MSG; 433189251Ssam break; 434189251Ssam case RECEIVED_M2D: 435189251Ssam if (wps->wps->ap) { 436189251Ssam msg = wps_build_wsc_nack(wps); 437189251Ssam *op_code = WSC_NACK; 438189251Ssam break; 439189251Ssam } 440189251Ssam msg = wps_build_wsc_ack(wps); 441189251Ssam *op_code = WSC_ACK; 442189251Ssam if (msg) { 443189251Ssam /* Another M2/M2D may be received */ 444189251Ssam wps->state = RECV_M2; 445189251Ssam } 446189251Ssam break; 447189251Ssam case SEND_WSC_NACK: 448189251Ssam msg = wps_build_wsc_nack(wps); 449189251Ssam *op_code = WSC_NACK; 450189251Ssam break; 451189251Ssam case WPS_MSG_DONE: 452189251Ssam msg = wps_build_wsc_done(wps); 453189251Ssam *op_code = WSC_Done; 454189251Ssam break; 455189251Ssam default: 456189251Ssam wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building " 457189251Ssam "a message", wps->state); 458189251Ssam msg = NULL; 459189251Ssam break; 460189251Ssam } 461189251Ssam 462189251Ssam if (*op_code == WSC_MSG && msg) { 463189251Ssam /* Save a copy of the last message for Authenticator derivation 464189251Ssam */ 465189251Ssam wpabuf_free(wps->last_msg); 466189251Ssam wps->last_msg = wpabuf_dup(msg); 467189251Ssam } 468189251Ssam 469189251Ssam return msg; 470189251Ssam} 471189251Ssam 472189251Ssam 473189251Ssamstatic int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce) 474189251Ssam{ 475189251Ssam if (r_nonce == NULL) { 476189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received"); 477189251Ssam return -1; 478189251Ssam } 479189251Ssam 480189251Ssam os_memcpy(wps->nonce_r, r_nonce, WPS_NONCE_LEN); 481189251Ssam wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce", 482189251Ssam wps->nonce_r, WPS_NONCE_LEN); 483189251Ssam 484189251Ssam return 0; 485189251Ssam} 486189251Ssam 487189251Ssam 488189251Ssamstatic int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce) 489189251Ssam{ 490189251Ssam if (e_nonce == NULL) { 491189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received"); 492189251Ssam return -1; 493189251Ssam } 494189251Ssam 495189251Ssam if (os_memcmp(wps->nonce_e, e_nonce, WPS_NONCE_LEN) != 0) { 496189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce received"); 497189251Ssam return -1; 498189251Ssam } 499189251Ssam 500189251Ssam return 0; 501189251Ssam} 502189251Ssam 503189251Ssam 504189251Ssamstatic int wps_process_uuid_r(struct wps_data *wps, const u8 *uuid_r) 505189251Ssam{ 506189251Ssam if (uuid_r == NULL) { 507189251Ssam wpa_printf(MSG_DEBUG, "WPS: No UUID-R received"); 508189251Ssam return -1; 509189251Ssam } 510189251Ssam 511189251Ssam os_memcpy(wps->uuid_r, uuid_r, WPS_UUID_LEN); 512189251Ssam wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN); 513189251Ssam 514189251Ssam return 0; 515189251Ssam} 516189251Ssam 517189251Ssam 518189251Ssamstatic int wps_process_pubkey(struct wps_data *wps, const u8 *pk, 519189251Ssam size_t pk_len) 520189251Ssam{ 521189251Ssam if (pk == NULL || pk_len == 0) { 522189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Public Key received"); 523189251Ssam return -1; 524189251Ssam } 525189251Ssam 526189251Ssam wpabuf_free(wps->dh_pubkey_r); 527189251Ssam wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len); 528189251Ssam if (wps->dh_pubkey_r == NULL) 529189251Ssam return -1; 530189251Ssam 531189251Ssam if (wps_derive_keys(wps) < 0) 532189251Ssam return -1; 533189251Ssam 534189251Ssam return 0; 535189251Ssam} 536189251Ssam 537189251Ssam 538189251Ssamstatic int wps_process_r_hash1(struct wps_data *wps, const u8 *r_hash1) 539189251Ssam{ 540189251Ssam if (r_hash1 == NULL) { 541189251Ssam wpa_printf(MSG_DEBUG, "WPS: No R-Hash1 received"); 542189251Ssam return -1; 543189251Ssam } 544189251Ssam 545189251Ssam os_memcpy(wps->peer_hash1, r_hash1, WPS_HASH_LEN); 546189251Ssam wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", wps->peer_hash1, WPS_HASH_LEN); 547189251Ssam 548189251Ssam return 0; 549189251Ssam} 550189251Ssam 551189251Ssam 552189251Ssamstatic int wps_process_r_hash2(struct wps_data *wps, const u8 *r_hash2) 553189251Ssam{ 554189251Ssam if (r_hash2 == NULL) { 555189251Ssam wpa_printf(MSG_DEBUG, "WPS: No R-Hash2 received"); 556189251Ssam return -1; 557189251Ssam } 558189251Ssam 559189251Ssam os_memcpy(wps->peer_hash2, r_hash2, WPS_HASH_LEN); 560189251Ssam wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", wps->peer_hash2, WPS_HASH_LEN); 561189251Ssam 562189251Ssam return 0; 563189251Ssam} 564189251Ssam 565189251Ssam 566189251Ssamstatic int wps_process_r_snonce1(struct wps_data *wps, const u8 *r_snonce1) 567189251Ssam{ 568189251Ssam u8 hash[SHA256_MAC_LEN]; 569189251Ssam const u8 *addr[4]; 570189251Ssam size_t len[4]; 571189251Ssam 572189251Ssam if (r_snonce1 == NULL) { 573189251Ssam wpa_printf(MSG_DEBUG, "WPS: No R-SNonce1 received"); 574189251Ssam return -1; 575189251Ssam } 576189251Ssam 577189251Ssam wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce1", r_snonce1, 578189251Ssam WPS_SECRET_NONCE_LEN); 579189251Ssam 580189251Ssam /* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */ 581189251Ssam addr[0] = r_snonce1; 582189251Ssam len[0] = WPS_SECRET_NONCE_LEN; 583189251Ssam addr[1] = wps->psk1; 584189251Ssam len[1] = WPS_PSK_LEN; 585189251Ssam addr[2] = wpabuf_head(wps->dh_pubkey_e); 586189251Ssam len[2] = wpabuf_len(wps->dh_pubkey_e); 587189251Ssam addr[3] = wpabuf_head(wps->dh_pubkey_r); 588189251Ssam len[3] = wpabuf_len(wps->dh_pubkey_r); 589189251Ssam hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash); 590189251Ssam 591189251Ssam if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) { 592189251Ssam wpa_printf(MSG_DEBUG, "WPS: R-Hash1 derived from R-S1 does " 593189251Ssam "not match with the pre-committed value"); 594189251Ssam wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE; 595189251Ssam wps_pwd_auth_fail_event(wps->wps, 1, 1); 596189251Ssam return -1; 597189251Ssam } 598189251Ssam 599189251Ssam wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the first " 600189251Ssam "half of the device password"); 601189251Ssam 602189251Ssam return 0; 603189251Ssam} 604189251Ssam 605189251Ssam 606189251Ssamstatic int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2) 607189251Ssam{ 608189251Ssam u8 hash[SHA256_MAC_LEN]; 609189251Ssam const u8 *addr[4]; 610189251Ssam size_t len[4]; 611189251Ssam 612189251Ssam if (r_snonce2 == NULL) { 613189251Ssam wpa_printf(MSG_DEBUG, "WPS: No R-SNonce2 received"); 614189251Ssam return -1; 615189251Ssam } 616189251Ssam 617189251Ssam wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce2", r_snonce2, 618189251Ssam WPS_SECRET_NONCE_LEN); 619189251Ssam 620189251Ssam /* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */ 621189251Ssam addr[0] = r_snonce2; 622189251Ssam len[0] = WPS_SECRET_NONCE_LEN; 623189251Ssam addr[1] = wps->psk2; 624189251Ssam len[1] = WPS_PSK_LEN; 625189251Ssam addr[2] = wpabuf_head(wps->dh_pubkey_e); 626189251Ssam len[2] = wpabuf_len(wps->dh_pubkey_e); 627189251Ssam addr[3] = wpabuf_head(wps->dh_pubkey_r); 628189251Ssam len[3] = wpabuf_len(wps->dh_pubkey_r); 629189251Ssam hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash); 630189251Ssam 631189251Ssam if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) { 632189251Ssam wpa_printf(MSG_DEBUG, "WPS: R-Hash2 derived from R-S2 does " 633189251Ssam "not match with the pre-committed value"); 634189251Ssam wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE; 635189251Ssam wps_pwd_auth_fail_event(wps->wps, 1, 2); 636189251Ssam return -1; 637189251Ssam } 638189251Ssam 639189251Ssam wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the second " 640189251Ssam "half of the device password"); 641189251Ssam 642189251Ssam return 0; 643189251Ssam} 644189251Ssam 645189251Ssam 646189251Ssamstatic int wps_process_cred_e(struct wps_data *wps, const u8 *cred, 647252726Srpaulo size_t cred_len, int wps2) 648189251Ssam{ 649189251Ssam struct wps_parse_attr attr; 650189251Ssam struct wpabuf msg; 651252726Srpaulo int ret = 0; 652189251Ssam 653189251Ssam wpa_printf(MSG_DEBUG, "WPS: Received Credential"); 654189251Ssam os_memset(&wps->cred, 0, sizeof(wps->cred)); 655189251Ssam wpabuf_set(&msg, cred, cred_len); 656189251Ssam if (wps_parse_msg(&msg, &attr) < 0 || 657189251Ssam wps_process_cred(&attr, &wps->cred)) 658189251Ssam return -1; 659189251Ssam 660209158Srpaulo if (os_memcmp(wps->cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) != 661209158Srpaulo 0) { 662209158Srpaulo wpa_printf(MSG_DEBUG, "WPS: MAC Address in the Credential (" 663209158Srpaulo MACSTR ") does not match with own address (" MACSTR 664209158Srpaulo ")", MAC2STR(wps->cred.mac_addr), 665209158Srpaulo MAC2STR(wps->wps->dev.mac_addr)); 666209158Srpaulo /* 667209158Srpaulo * In theory, this could be consider fatal error, but there are 668209158Srpaulo * number of deployed implementations using other address here 669209158Srpaulo * due to unclarity in the specification. For interoperability 670209158Srpaulo * reasons, allow this to be processed since we do not really 671209158Srpaulo * use the MAC Address information for anything. 672209158Srpaulo */ 673252726Srpaulo#ifdef CONFIG_WPS_STRICT 674252726Srpaulo if (wps2) { 675252726Srpaulo wpa_printf(MSG_INFO, "WPS: Do not accept incorrect " 676252726Srpaulo "MAC Address in AP Settings"); 677252726Srpaulo return -1; 678252726Srpaulo } 679252726Srpaulo#endif /* CONFIG_WPS_STRICT */ 680209158Srpaulo } 681209158Srpaulo 682252726Srpaulo#ifdef CONFIG_WPS2 683252726Srpaulo if (!(wps->cred.encr_type & 684252726Srpaulo (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) { 685252726Srpaulo if (wps->cred.encr_type & WPS_ENCR_WEP) { 686252726Srpaulo wpa_printf(MSG_INFO, "WPS: Reject Credential " 687252726Srpaulo "due to WEP configuration"); 688252726Srpaulo wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED; 689252726Srpaulo return -2; 690252726Srpaulo } 691252726Srpaulo 692252726Srpaulo wpa_printf(MSG_INFO, "WPS: Reject Credential due to " 693252726Srpaulo "invalid encr_type 0x%x", wps->cred.encr_type); 694252726Srpaulo return -1; 695252726Srpaulo } 696252726Srpaulo#endif /* CONFIG_WPS2 */ 697252726Srpaulo 698189251Ssam if (wps->wps->cred_cb) { 699189251Ssam wps->cred.cred_attr = cred - 4; 700189251Ssam wps->cred.cred_attr_len = cred_len + 4; 701252726Srpaulo ret = wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred); 702189251Ssam wps->cred.cred_attr = NULL; 703189251Ssam wps->cred.cred_attr_len = 0; 704189251Ssam } 705189251Ssam 706252726Srpaulo return ret; 707189251Ssam} 708189251Ssam 709189251Ssam 710189251Ssamstatic int wps_process_creds(struct wps_data *wps, const u8 *cred[], 711252726Srpaulo size_t cred_len[], size_t num_cred, int wps2) 712189251Ssam{ 713189251Ssam size_t i; 714252726Srpaulo int ok = 0; 715189251Ssam 716189251Ssam if (wps->wps->ap) 717189251Ssam return 0; 718189251Ssam 719189251Ssam if (num_cred == 0) { 720189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Credential attributes " 721189251Ssam "received"); 722189251Ssam return -1; 723189251Ssam } 724189251Ssam 725189251Ssam for (i = 0; i < num_cred; i++) { 726252726Srpaulo int res; 727252726Srpaulo res = wps_process_cred_e(wps, cred[i], cred_len[i], wps2); 728252726Srpaulo if (res == 0) 729252726Srpaulo ok++; 730252726Srpaulo else if (res == -2) 731252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: WEP credential skipped"); 732252726Srpaulo else 733189251Ssam return -1; 734189251Ssam } 735189251Ssam 736252726Srpaulo if (ok == 0) { 737252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: No valid Credential attribute " 738252726Srpaulo "received"); 739252726Srpaulo return -1; 740252726Srpaulo } 741252726Srpaulo 742189251Ssam return 0; 743189251Ssam} 744189251Ssam 745189251Ssam 746189251Ssamstatic int wps_process_ap_settings_e(struct wps_data *wps, 747189251Ssam struct wps_parse_attr *attr, 748252726Srpaulo struct wpabuf *attrs, int wps2) 749189251Ssam{ 750189251Ssam struct wps_credential cred; 751189251Ssam 752189251Ssam if (!wps->wps->ap) 753189251Ssam return 0; 754189251Ssam 755189251Ssam if (wps_process_ap_settings(attr, &cred) < 0) 756189251Ssam return -1; 757189251Ssam 758189251Ssam wpa_printf(MSG_INFO, "WPS: Received new AP configuration from " 759189251Ssam "Registrar"); 760189251Ssam 761209158Srpaulo if (os_memcmp(cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) != 762209158Srpaulo 0) { 763209158Srpaulo wpa_printf(MSG_DEBUG, "WPS: MAC Address in the AP Settings (" 764209158Srpaulo MACSTR ") does not match with own address (" MACSTR 765209158Srpaulo ")", MAC2STR(cred.mac_addr), 766209158Srpaulo MAC2STR(wps->wps->dev.mac_addr)); 767209158Srpaulo /* 768209158Srpaulo * In theory, this could be consider fatal error, but there are 769209158Srpaulo * number of deployed implementations using other address here 770209158Srpaulo * due to unclarity in the specification. For interoperability 771209158Srpaulo * reasons, allow this to be processed since we do not really 772209158Srpaulo * use the MAC Address information for anything. 773209158Srpaulo */ 774252726Srpaulo#ifdef CONFIG_WPS_STRICT 775252726Srpaulo if (wps2) { 776252726Srpaulo wpa_printf(MSG_INFO, "WPS: Do not accept incorrect " 777252726Srpaulo "MAC Address in AP Settings"); 778252726Srpaulo return -1; 779252726Srpaulo } 780252726Srpaulo#endif /* CONFIG_WPS_STRICT */ 781209158Srpaulo } 782209158Srpaulo 783252726Srpaulo#ifdef CONFIG_WPS2 784252726Srpaulo if (!(cred.encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) 785252726Srpaulo { 786252726Srpaulo if (cred.encr_type & WPS_ENCR_WEP) { 787252726Srpaulo wpa_printf(MSG_INFO, "WPS: Reject new AP settings " 788252726Srpaulo "due to WEP configuration"); 789252726Srpaulo wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED; 790252726Srpaulo return -1; 791252726Srpaulo } 792252726Srpaulo 793252726Srpaulo wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to " 794252726Srpaulo "invalid encr_type 0x%x", cred.encr_type); 795252726Srpaulo return -1; 796252726Srpaulo } 797252726Srpaulo#endif /* CONFIG_WPS2 */ 798252726Srpaulo 799252726Srpaulo#ifdef CONFIG_WPS_STRICT 800252726Srpaulo if (wps2) { 801252726Srpaulo if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == 802252726Srpaulo WPS_ENCR_TKIP || 803252726Srpaulo (cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) == 804252726Srpaulo WPS_AUTH_WPAPSK) { 805252726Srpaulo wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC 2.0 " 806252726Srpaulo "AP Settings: WPA-Personal/TKIP only"); 807252726Srpaulo wps->error_indication = 808252726Srpaulo WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED; 809252726Srpaulo return -1; 810252726Srpaulo } 811252726Srpaulo } 812252726Srpaulo#endif /* CONFIG_WPS_STRICT */ 813252726Srpaulo 814252726Srpaulo#ifdef CONFIG_WPS2 815252726Srpaulo if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP) 816252726Srpaulo { 817252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> " 818252726Srpaulo "TKIP+AES"); 819252726Srpaulo cred.encr_type |= WPS_ENCR_AES; 820252726Srpaulo } 821252726Srpaulo 822252726Srpaulo if ((cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) == 823252726Srpaulo WPS_AUTH_WPAPSK) { 824252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> " 825252726Srpaulo "WPAPSK+WPA2PSK"); 826252726Srpaulo cred.auth_type |= WPS_AUTH_WPA2PSK; 827252726Srpaulo } 828252726Srpaulo#endif /* CONFIG_WPS2 */ 829252726Srpaulo 830189251Ssam if (wps->wps->cred_cb) { 831189251Ssam cred.cred_attr = wpabuf_head(attrs); 832189251Ssam cred.cred_attr_len = wpabuf_len(attrs); 833189251Ssam wps->wps->cred_cb(wps->wps->cb_ctx, &cred); 834189251Ssam } 835189251Ssam 836189251Ssam return 0; 837189251Ssam} 838189251Ssam 839189251Ssam 840189251Ssamstatic enum wps_process_res wps_process_m2(struct wps_data *wps, 841189251Ssam const struct wpabuf *msg, 842189251Ssam struct wps_parse_attr *attr) 843189251Ssam{ 844189251Ssam wpa_printf(MSG_DEBUG, "WPS: Received M2"); 845189251Ssam 846189251Ssam if (wps->state != RECV_M2) { 847189251Ssam wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for " 848189251Ssam "receiving M2", wps->state); 849189251Ssam wps->state = SEND_WSC_NACK; 850189251Ssam return WPS_CONTINUE; 851189251Ssam } 852189251Ssam 853189251Ssam if (wps_process_registrar_nonce(wps, attr->registrar_nonce) || 854189251Ssam wps_process_enrollee_nonce(wps, attr->enrollee_nonce) || 855214734Srpaulo wps_process_uuid_r(wps, attr->uuid_r)) { 856189251Ssam wps->state = SEND_WSC_NACK; 857189251Ssam return WPS_CONTINUE; 858189251Ssam } 859189251Ssam 860252726Srpaulo /* 861252726Srpaulo * Stop here on an AP as an Enrollee if AP Setup is locked unless the 862252726Srpaulo * special locked mode is used to allow protocol run up to M7 in order 863252726Srpaulo * to support external Registrars that only learn the current AP 864252726Srpaulo * configuration without changing it. 865252726Srpaulo */ 866214734Srpaulo if (wps->wps->ap && 867252726Srpaulo ((wps->wps->ap_setup_locked && wps->wps->ap_setup_locked != 2) || 868252726Srpaulo wps->dev_password == NULL)) { 869189251Ssam wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse " 870189251Ssam "registration of a new Registrar"); 871189251Ssam wps->config_error = WPS_CFG_SETUP_LOCKED; 872189251Ssam wps->state = SEND_WSC_NACK; 873189251Ssam return WPS_CONTINUE; 874189251Ssam } 875189251Ssam 876214734Srpaulo if (wps_process_pubkey(wps, attr->public_key, attr->public_key_len) || 877214734Srpaulo wps_process_authenticator(wps, attr->authenticator, msg) || 878214734Srpaulo wps_process_device_attrs(&wps->peer_dev, attr)) { 879214734Srpaulo wps->state = SEND_WSC_NACK; 880214734Srpaulo return WPS_CONTINUE; 881214734Srpaulo } 882214734Srpaulo 883189251Ssam wps->state = SEND_M3; 884189251Ssam return WPS_CONTINUE; 885189251Ssam} 886189251Ssam 887189251Ssam 888189251Ssamstatic enum wps_process_res wps_process_m2d(struct wps_data *wps, 889189251Ssam struct wps_parse_attr *attr) 890189251Ssam{ 891189251Ssam wpa_printf(MSG_DEBUG, "WPS: Received M2D"); 892189251Ssam 893189251Ssam if (wps->state != RECV_M2) { 894189251Ssam wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for " 895189251Ssam "receiving M2D", wps->state); 896189251Ssam wps->state = SEND_WSC_NACK; 897189251Ssam return WPS_CONTINUE; 898189251Ssam } 899189251Ssam 900189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer", 901189251Ssam attr->manufacturer, attr->manufacturer_len); 902189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name", 903189251Ssam attr->model_name, attr->model_name_len); 904189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number", 905189251Ssam attr->model_number, attr->model_number_len); 906189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number", 907189251Ssam attr->serial_number, attr->serial_number_len); 908189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name", 909189251Ssam attr->dev_name, attr->dev_name_len); 910189251Ssam 911189251Ssam if (wps->wps->event_cb) { 912189251Ssam union wps_event_data data; 913189251Ssam struct wps_event_m2d *m2d = &data.m2d; 914189251Ssam os_memset(&data, 0, sizeof(data)); 915189251Ssam if (attr->config_methods) 916189251Ssam m2d->config_methods = 917189251Ssam WPA_GET_BE16(attr->config_methods); 918189251Ssam m2d->manufacturer = attr->manufacturer; 919189251Ssam m2d->manufacturer_len = attr->manufacturer_len; 920189251Ssam m2d->model_name = attr->model_name; 921189251Ssam m2d->model_name_len = attr->model_name_len; 922189251Ssam m2d->model_number = attr->model_number; 923189251Ssam m2d->model_number_len = attr->model_number_len; 924189251Ssam m2d->serial_number = attr->serial_number; 925189251Ssam m2d->serial_number_len = attr->serial_number_len; 926189251Ssam m2d->dev_name = attr->dev_name; 927189251Ssam m2d->dev_name_len = attr->dev_name_len; 928189251Ssam m2d->primary_dev_type = attr->primary_dev_type; 929189251Ssam if (attr->config_error) 930189251Ssam m2d->config_error = 931189251Ssam WPA_GET_BE16(attr->config_error); 932189251Ssam if (attr->dev_password_id) 933189251Ssam m2d->dev_password_id = 934189251Ssam WPA_GET_BE16(attr->dev_password_id); 935189251Ssam wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_M2D, &data); 936189251Ssam } 937189251Ssam 938189251Ssam wps->state = RECEIVED_M2D; 939189251Ssam return WPS_CONTINUE; 940189251Ssam} 941189251Ssam 942189251Ssam 943189251Ssamstatic enum wps_process_res wps_process_m4(struct wps_data *wps, 944189251Ssam const struct wpabuf *msg, 945189251Ssam struct wps_parse_attr *attr) 946189251Ssam{ 947189251Ssam struct wpabuf *decrypted; 948189251Ssam struct wps_parse_attr eattr; 949189251Ssam 950189251Ssam wpa_printf(MSG_DEBUG, "WPS: Received M4"); 951189251Ssam 952189251Ssam if (wps->state != RECV_M4) { 953189251Ssam wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for " 954189251Ssam "receiving M4", wps->state); 955189251Ssam wps->state = SEND_WSC_NACK; 956189251Ssam return WPS_CONTINUE; 957189251Ssam } 958189251Ssam 959189251Ssam if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) || 960189251Ssam wps_process_authenticator(wps, attr->authenticator, msg) || 961189251Ssam wps_process_r_hash1(wps, attr->r_hash1) || 962189251Ssam wps_process_r_hash2(wps, attr->r_hash2)) { 963189251Ssam wps->state = SEND_WSC_NACK; 964189251Ssam return WPS_CONTINUE; 965189251Ssam } 966189251Ssam 967189251Ssam decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings, 968189251Ssam attr->encr_settings_len); 969189251Ssam if (decrypted == NULL) { 970189251Ssam wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted " 971189251Ssam "Settings attribute"); 972189251Ssam wps->state = SEND_WSC_NACK; 973189251Ssam return WPS_CONTINUE; 974189251Ssam } 975189251Ssam 976252726Srpaulo if (wps_validate_m4_encr(decrypted, attr->version2 != NULL) < 0) { 977252726Srpaulo wpabuf_free(decrypted); 978252726Srpaulo wps->state = SEND_WSC_NACK; 979252726Srpaulo return WPS_CONTINUE; 980252726Srpaulo } 981252726Srpaulo 982189251Ssam wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " 983189251Ssam "attribute"); 984189251Ssam if (wps_parse_msg(decrypted, &eattr) < 0 || 985189251Ssam wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) || 986189251Ssam wps_process_r_snonce1(wps, eattr.r_snonce1)) { 987189251Ssam wpabuf_free(decrypted); 988189251Ssam wps->state = SEND_WSC_NACK; 989189251Ssam return WPS_CONTINUE; 990189251Ssam } 991189251Ssam wpabuf_free(decrypted); 992189251Ssam 993189251Ssam wps->state = SEND_M5; 994189251Ssam return WPS_CONTINUE; 995189251Ssam} 996189251Ssam 997189251Ssam 998189251Ssamstatic enum wps_process_res wps_process_m6(struct wps_data *wps, 999189251Ssam const struct wpabuf *msg, 1000189251Ssam struct wps_parse_attr *attr) 1001189251Ssam{ 1002189251Ssam struct wpabuf *decrypted; 1003189251Ssam struct wps_parse_attr eattr; 1004189251Ssam 1005189251Ssam wpa_printf(MSG_DEBUG, "WPS: Received M6"); 1006189251Ssam 1007189251Ssam if (wps->state != RECV_M6) { 1008189251Ssam wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for " 1009189251Ssam "receiving M6", wps->state); 1010189251Ssam wps->state = SEND_WSC_NACK; 1011189251Ssam return WPS_CONTINUE; 1012189251Ssam } 1013189251Ssam 1014189251Ssam if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) || 1015189251Ssam wps_process_authenticator(wps, attr->authenticator, msg)) { 1016189251Ssam wps->state = SEND_WSC_NACK; 1017189251Ssam return WPS_CONTINUE; 1018189251Ssam } 1019189251Ssam 1020189251Ssam decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings, 1021189251Ssam attr->encr_settings_len); 1022189251Ssam if (decrypted == NULL) { 1023189251Ssam wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted " 1024189251Ssam "Settings attribute"); 1025189251Ssam wps->state = SEND_WSC_NACK; 1026189251Ssam return WPS_CONTINUE; 1027189251Ssam } 1028189251Ssam 1029252726Srpaulo if (wps_validate_m6_encr(decrypted, attr->version2 != NULL) < 0) { 1030252726Srpaulo wpabuf_free(decrypted); 1031252726Srpaulo wps->state = SEND_WSC_NACK; 1032252726Srpaulo return WPS_CONTINUE; 1033252726Srpaulo } 1034252726Srpaulo 1035189251Ssam wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " 1036189251Ssam "attribute"); 1037189251Ssam if (wps_parse_msg(decrypted, &eattr) < 0 || 1038189251Ssam wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) || 1039189251Ssam wps_process_r_snonce2(wps, eattr.r_snonce2)) { 1040189251Ssam wpabuf_free(decrypted); 1041189251Ssam wps->state = SEND_WSC_NACK; 1042189251Ssam return WPS_CONTINUE; 1043189251Ssam } 1044189251Ssam wpabuf_free(decrypted); 1045189251Ssam 1046252726Srpaulo if (wps->wps->ap) 1047252726Srpaulo wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_AP_PIN_SUCCESS, 1048252726Srpaulo NULL); 1049252726Srpaulo 1050189251Ssam wps->state = SEND_M7; 1051189251Ssam return WPS_CONTINUE; 1052189251Ssam} 1053189251Ssam 1054189251Ssam 1055189251Ssamstatic enum wps_process_res wps_process_m8(struct wps_data *wps, 1056189251Ssam const struct wpabuf *msg, 1057189251Ssam struct wps_parse_attr *attr) 1058189251Ssam{ 1059189251Ssam struct wpabuf *decrypted; 1060189251Ssam struct wps_parse_attr eattr; 1061189251Ssam 1062189251Ssam wpa_printf(MSG_DEBUG, "WPS: Received M8"); 1063189251Ssam 1064189251Ssam if (wps->state != RECV_M8) { 1065189251Ssam wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for " 1066189251Ssam "receiving M8", wps->state); 1067189251Ssam wps->state = SEND_WSC_NACK; 1068189251Ssam return WPS_CONTINUE; 1069189251Ssam } 1070189251Ssam 1071189251Ssam if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) || 1072189251Ssam wps_process_authenticator(wps, attr->authenticator, msg)) { 1073189251Ssam wps->state = SEND_WSC_NACK; 1074189251Ssam return WPS_CONTINUE; 1075189251Ssam } 1076189251Ssam 1077252726Srpaulo if (wps->wps->ap && wps->wps->ap_setup_locked) { 1078252726Srpaulo /* 1079252726Srpaulo * Stop here if special ap_setup_locked == 2 mode allowed the 1080252726Srpaulo * protocol to continue beyond M2. This allows ER to learn the 1081252726Srpaulo * current AP settings without changing them. 1082252726Srpaulo */ 1083252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse " 1084252726Srpaulo "registration of a new Registrar"); 1085252726Srpaulo wps->config_error = WPS_CFG_SETUP_LOCKED; 1086252726Srpaulo wps->state = SEND_WSC_NACK; 1087252726Srpaulo return WPS_CONTINUE; 1088252726Srpaulo } 1089252726Srpaulo 1090189251Ssam decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings, 1091189251Ssam attr->encr_settings_len); 1092189251Ssam if (decrypted == NULL) { 1093189251Ssam wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted " 1094189251Ssam "Settings attribute"); 1095189251Ssam wps->state = SEND_WSC_NACK; 1096189251Ssam return WPS_CONTINUE; 1097189251Ssam } 1098189251Ssam 1099252726Srpaulo if (wps_validate_m8_encr(decrypted, wps->wps->ap, 1100252726Srpaulo attr->version2 != NULL) < 0) { 1101252726Srpaulo wpabuf_free(decrypted); 1102252726Srpaulo wps->state = SEND_WSC_NACK; 1103252726Srpaulo return WPS_CONTINUE; 1104252726Srpaulo } 1105252726Srpaulo 1106189251Ssam wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " 1107189251Ssam "attribute"); 1108189251Ssam if (wps_parse_msg(decrypted, &eattr) < 0 || 1109189251Ssam wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) || 1110189251Ssam wps_process_creds(wps, eattr.cred, eattr.cred_len, 1111252726Srpaulo eattr.num_cred, attr->version2 != NULL) || 1112252726Srpaulo wps_process_ap_settings_e(wps, &eattr, decrypted, 1113252726Srpaulo attr->version2 != NULL)) { 1114189251Ssam wpabuf_free(decrypted); 1115189251Ssam wps->state = SEND_WSC_NACK; 1116189251Ssam return WPS_CONTINUE; 1117189251Ssam } 1118189251Ssam wpabuf_free(decrypted); 1119189251Ssam 1120189251Ssam wps->state = WPS_MSG_DONE; 1121189251Ssam return WPS_CONTINUE; 1122189251Ssam} 1123189251Ssam 1124189251Ssam 1125189251Ssamstatic enum wps_process_res wps_process_wsc_msg(struct wps_data *wps, 1126189251Ssam const struct wpabuf *msg) 1127189251Ssam{ 1128189251Ssam struct wps_parse_attr attr; 1129189251Ssam enum wps_process_res ret = WPS_CONTINUE; 1130189251Ssam 1131189251Ssam wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG"); 1132189251Ssam 1133189251Ssam if (wps_parse_msg(msg, &attr) < 0) 1134189251Ssam return WPS_FAILURE; 1135189251Ssam 1136189251Ssam if (attr.enrollee_nonce == NULL || 1137252726Srpaulo os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { 1138189251Ssam wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); 1139189251Ssam return WPS_FAILURE; 1140189251Ssam } 1141189251Ssam 1142189251Ssam if (attr.msg_type == NULL) { 1143189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); 1144252726Srpaulo wps->state = SEND_WSC_NACK; 1145252726Srpaulo return WPS_CONTINUE; 1146189251Ssam } 1147189251Ssam 1148189251Ssam switch (*attr.msg_type) { 1149189251Ssam case WPS_M2: 1150252726Srpaulo if (wps_validate_m2(msg) < 0) 1151252726Srpaulo return WPS_FAILURE; 1152189251Ssam ret = wps_process_m2(wps, msg, &attr); 1153189251Ssam break; 1154189251Ssam case WPS_M2D: 1155252726Srpaulo if (wps_validate_m2d(msg) < 0) 1156252726Srpaulo return WPS_FAILURE; 1157189251Ssam ret = wps_process_m2d(wps, &attr); 1158189251Ssam break; 1159189251Ssam case WPS_M4: 1160252726Srpaulo if (wps_validate_m4(msg) < 0) 1161252726Srpaulo return WPS_FAILURE; 1162189251Ssam ret = wps_process_m4(wps, msg, &attr); 1163189251Ssam if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) 1164252726Srpaulo wps_fail_event(wps->wps, WPS_M4, wps->config_error, 1165252726Srpaulo wps->error_indication); 1166189251Ssam break; 1167189251Ssam case WPS_M6: 1168252726Srpaulo if (wps_validate_m6(msg) < 0) 1169252726Srpaulo return WPS_FAILURE; 1170189251Ssam ret = wps_process_m6(wps, msg, &attr); 1171189251Ssam if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) 1172252726Srpaulo wps_fail_event(wps->wps, WPS_M6, wps->config_error, 1173252726Srpaulo wps->error_indication); 1174189251Ssam break; 1175189251Ssam case WPS_M8: 1176252726Srpaulo if (wps_validate_m8(msg) < 0) 1177252726Srpaulo return WPS_FAILURE; 1178189251Ssam ret = wps_process_m8(wps, msg, &attr); 1179189251Ssam if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) 1180252726Srpaulo wps_fail_event(wps->wps, WPS_M8, wps->config_error, 1181252726Srpaulo wps->error_indication); 1182189251Ssam break; 1183189251Ssam default: 1184189251Ssam wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d", 1185189251Ssam *attr.msg_type); 1186189251Ssam return WPS_FAILURE; 1187189251Ssam } 1188189251Ssam 1189189251Ssam /* 1190189251Ssam * Save a copy of the last message for Authenticator derivation if we 1191189251Ssam * are continuing. However, skip M2D since it is not authenticated and 1192189251Ssam * neither is the ACK/NACK response frame. This allows the possibly 1193189251Ssam * following M2 to be processed correctly by using the previously sent 1194189251Ssam * M1 in Authenticator derivation. 1195189251Ssam */ 1196189251Ssam if (ret == WPS_CONTINUE && *attr.msg_type != WPS_M2D) { 1197189251Ssam /* Save a copy of the last message for Authenticator derivation 1198189251Ssam */ 1199189251Ssam wpabuf_free(wps->last_msg); 1200189251Ssam wps->last_msg = wpabuf_dup(msg); 1201189251Ssam } 1202189251Ssam 1203189251Ssam return ret; 1204189251Ssam} 1205189251Ssam 1206189251Ssam 1207189251Ssamstatic enum wps_process_res wps_process_wsc_ack(struct wps_data *wps, 1208189251Ssam const struct wpabuf *msg) 1209189251Ssam{ 1210189251Ssam struct wps_parse_attr attr; 1211189251Ssam 1212189251Ssam wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK"); 1213189251Ssam 1214189251Ssam if (wps_parse_msg(msg, &attr) < 0) 1215189251Ssam return WPS_FAILURE; 1216189251Ssam 1217189251Ssam if (attr.msg_type == NULL) { 1218189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); 1219189251Ssam return WPS_FAILURE; 1220189251Ssam } 1221189251Ssam 1222189251Ssam if (*attr.msg_type != WPS_WSC_ACK) { 1223189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d", 1224189251Ssam *attr.msg_type); 1225189251Ssam return WPS_FAILURE; 1226189251Ssam } 1227189251Ssam 1228189251Ssam if (attr.registrar_nonce == NULL || 1229252726Srpaulo os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) 1230189251Ssam { 1231189251Ssam wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); 1232189251Ssam return WPS_FAILURE; 1233189251Ssam } 1234189251Ssam 1235189251Ssam if (attr.enrollee_nonce == NULL || 1236252726Srpaulo os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { 1237189251Ssam wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); 1238189251Ssam return WPS_FAILURE; 1239189251Ssam } 1240189251Ssam 1241189251Ssam if (wps->state == RECV_ACK && wps->wps->ap) { 1242189251Ssam wpa_printf(MSG_DEBUG, "WPS: External Registrar registration " 1243189251Ssam "completed successfully"); 1244189251Ssam wps_success_event(wps->wps); 1245189251Ssam wps->state = WPS_FINISHED; 1246189251Ssam return WPS_DONE; 1247189251Ssam } 1248189251Ssam 1249189251Ssam return WPS_FAILURE; 1250189251Ssam} 1251189251Ssam 1252189251Ssam 1253189251Ssamstatic enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, 1254189251Ssam const struct wpabuf *msg) 1255189251Ssam{ 1256189251Ssam struct wps_parse_attr attr; 1257252726Srpaulo u16 config_error; 1258189251Ssam 1259189251Ssam wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK"); 1260189251Ssam 1261189251Ssam if (wps_parse_msg(msg, &attr) < 0) 1262189251Ssam return WPS_FAILURE; 1263189251Ssam 1264189251Ssam if (attr.msg_type == NULL) { 1265189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); 1266189251Ssam return WPS_FAILURE; 1267189251Ssam } 1268189251Ssam 1269189251Ssam if (*attr.msg_type != WPS_WSC_NACK) { 1270189251Ssam wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d", 1271189251Ssam *attr.msg_type); 1272189251Ssam return WPS_FAILURE; 1273189251Ssam } 1274189251Ssam 1275189251Ssam if (attr.registrar_nonce == NULL || 1276252726Srpaulo os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) 1277189251Ssam { 1278189251Ssam wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); 1279189251Ssam wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce", 1280189251Ssam attr.registrar_nonce, WPS_NONCE_LEN); 1281189251Ssam wpa_hexdump(MSG_DEBUG, "WPS: Expected Registrar Nonce", 1282189251Ssam wps->nonce_r, WPS_NONCE_LEN); 1283189251Ssam return WPS_FAILURE; 1284189251Ssam } 1285189251Ssam 1286189251Ssam if (attr.enrollee_nonce == NULL || 1287252726Srpaulo os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { 1288189251Ssam wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); 1289189251Ssam wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce", 1290189251Ssam attr.enrollee_nonce, WPS_NONCE_LEN); 1291189251Ssam wpa_hexdump(MSG_DEBUG, "WPS: Expected Enrollee Nonce", 1292189251Ssam wps->nonce_e, WPS_NONCE_LEN); 1293189251Ssam return WPS_FAILURE; 1294189251Ssam } 1295189251Ssam 1296189251Ssam if (attr.config_error == NULL) { 1297189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute " 1298189251Ssam "in WSC_NACK"); 1299189251Ssam return WPS_FAILURE; 1300189251Ssam } 1301189251Ssam 1302252726Srpaulo config_error = WPA_GET_BE16(attr.config_error); 1303189251Ssam wpa_printf(MSG_DEBUG, "WPS: Registrar terminated negotiation with " 1304252726Srpaulo "Configuration Error %d", config_error); 1305189251Ssam 1306189251Ssam switch (wps->state) { 1307189251Ssam case RECV_M4: 1308252726Srpaulo wps_fail_event(wps->wps, WPS_M3, config_error, 1309252726Srpaulo wps->error_indication); 1310189251Ssam break; 1311189251Ssam case RECV_M6: 1312252726Srpaulo wps_fail_event(wps->wps, WPS_M5, config_error, 1313252726Srpaulo wps->error_indication); 1314189251Ssam break; 1315189251Ssam case RECV_M8: 1316252726Srpaulo wps_fail_event(wps->wps, WPS_M7, config_error, 1317252726Srpaulo wps->error_indication); 1318189251Ssam break; 1319189251Ssam default: 1320189251Ssam break; 1321189251Ssam } 1322189251Ssam 1323189251Ssam /* Followed by NACK if Enrollee is Supplicant or EAP-Failure if 1324189251Ssam * Enrollee is Authenticator */ 1325189251Ssam wps->state = SEND_WSC_NACK; 1326189251Ssam 1327189251Ssam return WPS_FAILURE; 1328189251Ssam} 1329189251Ssam 1330189251Ssam 1331189251Ssamenum wps_process_res wps_enrollee_process_msg(struct wps_data *wps, 1332189251Ssam enum wsc_op_code op_code, 1333189251Ssam const struct wpabuf *msg) 1334189251Ssam{ 1335189251Ssam 1336189251Ssam wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu " 1337189251Ssam "op_code=%d)", 1338189251Ssam (unsigned long) wpabuf_len(msg), op_code); 1339189251Ssam 1340209158Srpaulo if (op_code == WSC_UPnP) { 1341209158Srpaulo /* Determine the OpCode based on message type attribute */ 1342209158Srpaulo struct wps_parse_attr attr; 1343209158Srpaulo if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) { 1344209158Srpaulo if (*attr.msg_type == WPS_WSC_ACK) 1345209158Srpaulo op_code = WSC_ACK; 1346209158Srpaulo else if (*attr.msg_type == WPS_WSC_NACK) 1347209158Srpaulo op_code = WSC_NACK; 1348209158Srpaulo } 1349209158Srpaulo } 1350209158Srpaulo 1351189251Ssam switch (op_code) { 1352189251Ssam case WSC_MSG: 1353189251Ssam case WSC_UPnP: 1354189251Ssam return wps_process_wsc_msg(wps, msg); 1355189251Ssam case WSC_ACK: 1356252726Srpaulo if (wps_validate_wsc_ack(msg) < 0) 1357252726Srpaulo return WPS_FAILURE; 1358189251Ssam return wps_process_wsc_ack(wps, msg); 1359189251Ssam case WSC_NACK: 1360252726Srpaulo if (wps_validate_wsc_nack(msg) < 0) 1361252726Srpaulo return WPS_FAILURE; 1362189251Ssam return wps_process_wsc_nack(wps, msg); 1363189251Ssam default: 1364189251Ssam wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code); 1365189251Ssam return WPS_FAILURE; 1366189251Ssam } 1367189251Ssam} 1368