X-Git-Url: https://gitweb.dragonflybsd.org/~tuxillo/dragonfly.git/blobdiff_plain/097a4fd11fac7bc82d1b152e3259da24a019f7b8..e191bdcb8e772c038df688c4d50fa7f3c9f61562:/contrib/hostapd/src/wps/wps_enrollee.c diff --git a/contrib/hostapd/src/wps/wps_enrollee.c b/contrib/hostapd/src/wps/wps_enrollee.c index 5cb3e1e011..9d48ca51e8 100644 --- a/contrib/hostapd/src/wps/wps_enrollee.c +++ b/contrib/hostapd/src/wps/wps_enrollee.c @@ -2,34 +2,20 @@ * Wi-Fi Protected Setup - Enrollee * Copyright (c) 2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "sha256.h" +#include "crypto/crypto.h" +#include "crypto/sha256.h" +#include "crypto/random.h" #include "wps_i.h" #include "wps_dev_attr.h" -static int wps_build_mac_addr(struct wps_data *wps, struct wpabuf *msg) -{ - wpa_printf(MSG_DEBUG, "WPS: * MAC Address"); - wpabuf_put_be16(msg, ATTR_MAC_ADDR); - wpabuf_put_be16(msg, ETH_ALEN); - wpabuf_put_data(msg, wps->mac_addr_e, ETH_ALEN); - return 0; -} - - static int wps_build_wps_state(struct wps_data *wps, struct wpabuf *msg) { u8 state; @@ -52,7 +38,7 @@ static int wps_build_e_hash(struct wps_data *wps, struct wpabuf *msg) const u8 *addr[4]; size_t len[4]; - if (os_get_random(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0) + if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0) return -1; wpa_hexdump(MSG_DEBUG, "WPS: E-S1", wps->snonce, WPS_SECRET_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "WPS: E-S2", @@ -118,9 +104,9 @@ static int wps_build_e_snonce2(struct wps_data *wps, struct wpabuf *msg) static struct wpabuf * wps_build_m1(struct wps_data *wps) { struct wpabuf *msg; - u16 methods; + u16 config_methods; - if (os_get_random(wps->nonce_e, WPS_NONCE_LEN) < 0) + if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0) return NULL; wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce", wps->nonce_e, WPS_NONCE_LEN); @@ -130,27 +116,46 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps) if (msg == NULL) return NULL; - methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; - if (wps->pbc) - methods |= WPS_CONFIG_PUSHBUTTON; + config_methods = wps->wps->config_methods; + if (wps->wps->ap && !wps->pbc_in_m1 && + (wps->dev_password_len != 0 || + (config_methods & WPS_CONFIG_DISPLAY))) { + /* + * These are the methods that the AP supports as an Enrollee + * for adding external Registrars, so remove PushButton. + * + * As a workaround for Windows 7 mechanism for probing WPS + * capabilities from M1, leave PushButton option if no PIN + * method is available or if WPS configuration enables PBC + * workaround. + */ + config_methods &= ~WPS_CONFIG_PUSHBUTTON; +#ifdef CONFIG_WPS2 + config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | + WPS_CONFIG_PHY_PUSHBUTTON); +#endif /* CONFIG_WPS2 */ + } if (wps_build_version(msg) || wps_build_msg_type(msg, WPS_M1) || wps_build_uuid_e(msg, wps->uuid_e) || - wps_build_mac_addr(wps, msg) || + wps_build_mac_addr(msg, wps->mac_addr_e) || wps_build_enrollee_nonce(wps, msg) || wps_build_public_key(wps, msg) || wps_build_auth_type_flags(wps, msg) || wps_build_encr_type_flags(wps, msg) || wps_build_conn_type_flags(wps, msg) || - wps_build_config_methods(msg, methods) || + wps_build_config_methods(msg, config_methods) || wps_build_wps_state(wps, msg) || wps_build_device_attrs(&wps->wps->dev, msg) || - wps_build_rf_bands(&wps->wps->dev, msg) || + wps_build_rf_bands(&wps->wps->dev, msg, + wps->wps->rf_band_cb(wps->wps->cb_ctx)) || wps_build_assoc_state(wps, msg) || wps_build_dev_password_id(msg, wps->dev_pw_id) || wps_build_config_error(msg, WPS_CFG_NO_ERROR) || - wps_build_os_version(&wps->wps->dev, msg)) { + wps_build_os_version(&wps->wps->dev, msg) || + wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_vendor_ext_m1(&wps->wps->dev, msg)) { wpabuf_free(msg); return NULL; } @@ -180,6 +185,7 @@ static struct wpabuf * wps_build_m3(struct wps_data *wps) wps_build_msg_type(msg, WPS_M3) || wps_build_registrar_nonce(wps, msg) || wps_build_e_hash(wps, msg) || + wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { wpabuf_free(msg); return NULL; @@ -212,6 +218,7 @@ static struct wpabuf * wps_build_m5(struct wps_data *wps) wps_build_e_snonce1(wps, plain) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || + wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { wpabuf_free(plain); wpabuf_free(msg); @@ -236,20 +243,47 @@ static int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg) static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg) { - wpa_printf(MSG_DEBUG, "WPS: * Authentication Type"); + u16 auth_type = wps->wps->auth_types; + + /* Select the best authentication type */ + if (auth_type & WPS_AUTH_WPA2PSK) + auth_type = WPS_AUTH_WPA2PSK; + else if (auth_type & WPS_AUTH_WPAPSK) + auth_type = WPS_AUTH_WPAPSK; + else if (auth_type & WPS_AUTH_OPEN) + auth_type = WPS_AUTH_OPEN; + else if (auth_type & WPS_AUTH_SHARED) + auth_type = WPS_AUTH_SHARED; + + wpa_printf(MSG_DEBUG, "WPS: * Authentication Type (0x%x)", auth_type); wpabuf_put_be16(msg, ATTR_AUTH_TYPE); wpabuf_put_be16(msg, 2); - wpabuf_put_be16(msg, wps->wps->auth_types); + wpabuf_put_be16(msg, auth_type); return 0; } static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg) { - wpa_printf(MSG_DEBUG, "WPS: * Encryption Type"); + u16 encr_type = wps->wps->encr_types; + + /* Select the best encryption type */ + if (wps->wps->auth_types & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) { + if (encr_type & WPS_ENCR_AES) + encr_type = WPS_ENCR_AES; + else if (encr_type & WPS_ENCR_TKIP) + encr_type = WPS_ENCR_TKIP; + } else { + if (encr_type & WPS_ENCR_WEP) + encr_type = WPS_ENCR_WEP; + else if (encr_type & WPS_ENCR_NONE) + encr_type = WPS_ENCR_NONE; + } + + wpa_printf(MSG_DEBUG, "WPS: * Encryption Type (0x%x)", encr_type); wpabuf_put_be16(msg, ATTR_ENCR_TYPE); wpabuf_put_be16(msg, 2); - wpabuf_put_be16(msg, wps->wps->encr_types); + wpabuf_put_be16(msg, encr_type); return 0; } @@ -314,6 +348,7 @@ static struct wpabuf * wps_build_m7(struct wps_data *wps) (wps->wps->ap && wps_build_ap_settings(wps, plain)) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || + wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { wpabuf_free(plain); wpabuf_free(msg); @@ -321,6 +356,16 @@ static struct wpabuf * wps_build_m7(struct wps_data *wps) } wpabuf_free(plain); + if (wps->wps->ap && wps->wps->registrar) { + /* + * If the Registrar is only learning our current configuration, + * it may not continue protocol run to successful completion. + * Store information here to make sure it remains available. + */ + wps_device_store(wps->wps->registrar, &wps->peer_dev, + wps->uuid_r); + } + wps->state = RECV_M8; return msg; } @@ -339,7 +384,8 @@ static struct wpabuf * wps_build_wsc_done(struct wps_data *wps) if (wps_build_version(msg) || wps_build_msg_type(msg, WPS_WSC_DONE) || wps_build_enrollee_nonce(wps, msg) || - wps_build_registrar_nonce(wps, msg)) { + wps_build_registrar_nonce(wps, msg) || + wps_build_wfa_ext(msg, 0, NULL, 0)) { wpabuf_free(msg); return NULL; } @@ -347,58 +393,13 @@ static struct wpabuf * wps_build_wsc_done(struct wps_data *wps) if (wps->wps->ap) wps->state = RECV_ACK; else { - wps_success_event(wps->wps); + wps_success_event(wps->wps, wps->peer_dev.mac_addr); wps->state = WPS_FINISHED; } return msg; } -static struct wpabuf * wps_build_wsc_ack(struct wps_data *wps) -{ - struct wpabuf *msg; - - wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK"); - - msg = wpabuf_alloc(1000); - if (msg == NULL) - return NULL; - - if (wps_build_version(msg) || - wps_build_msg_type(msg, WPS_WSC_ACK) || - wps_build_enrollee_nonce(wps, msg) || - wps_build_registrar_nonce(wps, msg)) { - wpabuf_free(msg); - return NULL; - } - - return msg; -} - - -static struct wpabuf * wps_build_wsc_nack(struct wps_data *wps) -{ - struct wpabuf *msg; - - wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK"); - - msg = wpabuf_alloc(1000); - if (msg == NULL) - return NULL; - - if (wps_build_version(msg) || - wps_build_msg_type(msg, WPS_WSC_NACK) || - wps_build_enrollee_nonce(wps, msg) || - wps_build_registrar_nonce(wps, msg) || - wps_build_config_error(msg, wps->config_error)) { - wpabuf_free(msg); - return NULL; - } - - return msg; -} - - struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps, enum wsc_op_code *op_code) { @@ -513,6 +514,24 @@ static int wps_process_pubkey(struct wps_data *wps, const u8 *pk, return -1; } + if (wps->peer_pubkey_hash_set) { + u8 hash[WPS_HASH_LEN]; + sha256_vector(1, &pk, &pk_len, hash); + if (os_memcmp(hash, wps->peer_pubkey_hash, + WPS_OOB_PUBKEY_HASH_LEN) != 0) { + wpa_printf(MSG_ERROR, "WPS: Public Key hash mismatch"); + wpa_hexdump(MSG_DEBUG, "WPS: Received public key", + pk, pk_len); + wpa_hexdump(MSG_DEBUG, "WPS: Calculated public key " + "hash", hash, WPS_OOB_PUBKEY_HASH_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: Expected public key hash", + wps->peer_pubkey_hash, + WPS_OOB_PUBKEY_HASH_LEN); + wps->config_error = WPS_CFG_PUBLIC_KEY_HASH_MISMATCH; + return -1; + } + } + wpabuf_free(wps->dh_pubkey_r); wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len); if (wps->dh_pubkey_r == NULL) @@ -582,7 +601,7 @@ static int wps_process_r_snonce1(struct wps_data *wps, const u8 *r_snonce1) wpa_printf(MSG_DEBUG, "WPS: R-Hash1 derived from R-S1 does " "not match with the pre-committed value"); wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE; - wps_pwd_auth_fail_event(wps->wps, 1, 1); + wps_pwd_auth_fail_event(wps->wps, 1, 1, wps->peer_dev.mac_addr); return -1; } @@ -622,7 +641,7 @@ static int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2) wpa_printf(MSG_DEBUG, "WPS: R-Hash2 derived from R-S2 does " "not match with the pre-committed value"); wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE; - wps_pwd_auth_fail_event(wps->wps, 1, 2); + wps_pwd_auth_fail_event(wps->wps, 1, 2, wps->peer_dev.mac_addr); return -1; } @@ -634,10 +653,11 @@ static int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2) static int wps_process_cred_e(struct wps_data *wps, const u8 *cred, - size_t cred_len) + size_t cred_len, int wps2) { struct wps_parse_attr attr; struct wpabuf msg; + int ret = 0; wpa_printf(MSG_DEBUG, "WPS: Received Credential"); os_memset(&wps->cred, 0, sizeof(wps->cred)); @@ -659,24 +679,48 @@ static int wps_process_cred_e(struct wps_data *wps, const u8 *cred, * reasons, allow this to be processed since we do not really * use the MAC Address information for anything. */ +#ifdef CONFIG_WPS_STRICT + if (wps2) { + wpa_printf(MSG_INFO, "WPS: Do not accept incorrect " + "MAC Address in AP Settings"); + return -1; + } +#endif /* CONFIG_WPS_STRICT */ + } + +#ifdef CONFIG_WPS2 + if (!(wps->cred.encr_type & + (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) { + if (wps->cred.encr_type & WPS_ENCR_WEP) { + wpa_printf(MSG_INFO, "WPS: Reject Credential " + "due to WEP configuration"); + wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED; + return -2; + } + + wpa_printf(MSG_INFO, "WPS: Reject Credential due to " + "invalid encr_type 0x%x", wps->cred.encr_type); + return -1; } +#endif /* CONFIG_WPS2 */ if (wps->wps->cred_cb) { wps->cred.cred_attr = cred - 4; wps->cred.cred_attr_len = cred_len + 4; - wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred); + ret = wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred); wps->cred.cred_attr = NULL; wps->cred.cred_attr_len = 0; } - return 0; + return ret; } static int wps_process_creds(struct wps_data *wps, const u8 *cred[], - size_t cred_len[], size_t num_cred) + size_t cred_len[], size_t num_cred, int wps2) { size_t i; + int ok = 0; if (wps->wps->ap) return 0; @@ -688,17 +732,29 @@ static int wps_process_creds(struct wps_data *wps, const u8 *cred[], } for (i = 0; i < num_cred; i++) { - if (wps_process_cred_e(wps, cred[i], cred_len[i])) + int res; + res = wps_process_cred_e(wps, cred[i], cred_len[i], wps2); + if (res == 0) + ok++; + else if (res == -2) + wpa_printf(MSG_DEBUG, "WPS: WEP credential skipped"); + else return -1; } + if (ok == 0) { + wpa_printf(MSG_DEBUG, "WPS: No valid Credential attribute " + "received"); + return -1; + } + return 0; } static int wps_process_ap_settings_e(struct wps_data *wps, struct wps_parse_attr *attr, - struct wpabuf *attrs) + struct wpabuf *attrs, int wps2) { struct wps_credential cred; @@ -724,8 +780,62 @@ static int wps_process_ap_settings_e(struct wps_data *wps, * reasons, allow this to be processed since we do not really * use the MAC Address information for anything. */ +#ifdef CONFIG_WPS_STRICT + if (wps2) { + wpa_printf(MSG_INFO, "WPS: Do not accept incorrect " + "MAC Address in AP Settings"); + return -1; + } +#endif /* CONFIG_WPS_STRICT */ } +#ifdef CONFIG_WPS2 + if (!(cred.encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) + { + if (cred.encr_type & WPS_ENCR_WEP) { + wpa_printf(MSG_INFO, "WPS: Reject new AP settings " + "due to WEP configuration"); + wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED; + return -1; + } + + wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to " + "invalid encr_type 0x%x", cred.encr_type); + return -1; + } +#endif /* CONFIG_WPS2 */ + +#ifdef CONFIG_WPS_STRICT + if (wps2) { + if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == + WPS_ENCR_TKIP || + (cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) == + WPS_AUTH_WPAPSK) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC 2.0 " + "AP Settings: WPA-Personal/TKIP only"); + wps->error_indication = + WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED; + return -1; + } + } +#endif /* CONFIG_WPS_STRICT */ + +#ifdef CONFIG_WPS2 + if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP) + { + wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> " + "TKIP+AES"); + cred.encr_type |= WPS_ENCR_AES; + } + + if ((cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) == + WPS_AUTH_WPAPSK) { + wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> " + "WPAPSK+WPA2PSK"); + cred.auth_type |= WPS_AUTH_WPA2PSK; + } +#endif /* CONFIG_WPS2 */ + if (wps->wps->cred_cb) { cred.cred_attr = wpabuf_head(attrs); cred.cred_attr_len = wpabuf_len(attrs); @@ -736,6 +846,63 @@ static int wps_process_ap_settings_e(struct wps_data *wps, } +static int wps_process_dev_pw_id(struct wps_data *wps, const u8 *dev_pw_id) +{ + u16 id; + + if (dev_pw_id == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Device Password ID"); + return -1; + } + + id = WPA_GET_BE16(dev_pw_id); + if (wps->dev_pw_id == id) { + wpa_printf(MSG_DEBUG, "WPS: Device Password ID %u", id); + return 0; + } + +#ifdef CONFIG_P2P + if ((id == DEV_PW_DEFAULT && + wps->dev_pw_id == DEV_PW_REGISTRAR_SPECIFIED) || + (id == DEV_PW_REGISTRAR_SPECIFIED && + wps->dev_pw_id == DEV_PW_DEFAULT)) { + /* + * Common P2P use cases indicate whether the PIN is from the + * client or GO using Device Password Id in M1/M2 in a way that + * does not look fully compliant with WSC specification. Anyway, + * this is deployed and needs to be allowed, so ignore changes + * between Registrar-Specified and Default PIN. + */ + wpa_printf(MSG_DEBUG, "WPS: Allow PIN Device Password ID " + "change"); + return 0; + } +#endif /* CONFIG_P2P */ + + wpa_printf(MSG_DEBUG, "WPS: Registrar trying to change Device Password " + "ID from %u to %u", wps->dev_pw_id, id); + + if (wps->dev_pw_id == DEV_PW_PUSHBUTTON && id == DEV_PW_DEFAULT) { + wpa_printf(MSG_DEBUG, + "WPS: Workaround - ignore PBC-to-PIN change"); + return 0; + } + + if (wps->alt_dev_password && wps->alt_dev_pw_id == id) { + wpa_printf(MSG_DEBUG, "WPS: Found a matching Device Password"); + os_free(wps->dev_password); + wps->dev_pw_id = wps->alt_dev_pw_id; + wps->dev_password = wps->alt_dev_password; + wps->dev_password_len = wps->alt_dev_password_len; + wps->alt_dev_password = NULL; + wps->alt_dev_password_len = 0; + return 0; + } + + return -1; +} + + static enum wps_process_res wps_process_m2(struct wps_data *wps, const struct wpabuf *msg, struct wps_parse_attr *attr) @@ -752,13 +919,20 @@ static enum wps_process_res wps_process_m2(struct wps_data *wps, if (wps_process_registrar_nonce(wps, attr->registrar_nonce) || wps_process_enrollee_nonce(wps, attr->enrollee_nonce) || wps_process_uuid_r(wps, attr->uuid_r) || - wps_process_pubkey(wps, attr->public_key, attr->public_key_len) || - wps_process_authenticator(wps, attr->authenticator, msg)) { + wps_process_dev_pw_id(wps, attr->dev_password_id)) { wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } - if (wps->wps->ap && wps->wps->ap_setup_locked) { + /* + * Stop here on an AP as an Enrollee if AP Setup is locked unless the + * special locked mode is used to allow protocol run up to M7 in order + * to support external Registrars that only learn the current AP + * configuration without changing it. + */ + if (wps->wps->ap && + ((wps->wps->ap_setup_locked && wps->wps->ap_setup_locked != 2) || + wps->dev_password == NULL)) { wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse " "registration of a new Registrar"); wps->config_error = WPS_CFG_SETUP_LOCKED; @@ -766,6 +940,45 @@ static enum wps_process_res wps_process_m2(struct wps_data *wps, return WPS_CONTINUE; } + if (wps_process_pubkey(wps, attr->public_key, attr->public_key_len) || + wps_process_authenticator(wps, attr->authenticator, msg) || + wps_process_device_attrs(&wps->peer_dev, attr)) { + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + +#ifdef CONFIG_WPS_NFC + if (wps->peer_pubkey_hash_set) { + struct wpabuf *decrypted; + struct wps_parse_attr eattr; + + decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings, + attr->encr_settings_len); + if (decrypted == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Failed to decrypt " + "Encrypted Settings attribute"); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted " + "Settings attribute"); + if (wps_parse_msg(decrypted, &eattr) < 0 || + wps_process_key_wrap_auth(wps, decrypted, + eattr.key_wrap_auth) || + wps_process_creds(wps, eattr.cred, eattr.cred_len, + eattr.num_cred, attr->version2 != NULL)) { + wpabuf_free(decrypted); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + wpabuf_free(decrypted); + + wps->state = WPS_MSG_DONE; + return WPS_CONTINUE; + } +#endif /* CONFIG_WPS_NFC */ + wps->state = SEND_M3; return WPS_CONTINUE; } @@ -859,6 +1072,12 @@ static enum wps_process_res wps_process_m4(struct wps_data *wps, return WPS_CONTINUE; } + if (wps_validate_m4_encr(decrypted, attr->version2 != NULL) < 0) { + wpabuf_free(decrypted); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " "attribute"); if (wps_parse_msg(decrypted, &eattr) < 0 || @@ -906,6 +1125,12 @@ static enum wps_process_res wps_process_m6(struct wps_data *wps, return WPS_CONTINUE; } + if (wps_validate_m6_encr(decrypted, attr->version2 != NULL) < 0) { + wpabuf_free(decrypted); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " "attribute"); if (wps_parse_msg(decrypted, &eattr) < 0 || @@ -917,6 +1142,10 @@ static enum wps_process_res wps_process_m6(struct wps_data *wps, } wpabuf_free(decrypted); + if (wps->wps->ap) + wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_AP_PIN_SUCCESS, + NULL); + wps->state = SEND_M7; return WPS_CONTINUE; } @@ -944,6 +1173,19 @@ static enum wps_process_res wps_process_m8(struct wps_data *wps, return WPS_CONTINUE; } + if (wps->wps->ap && wps->wps->ap_setup_locked) { + /* + * Stop here if special ap_setup_locked == 2 mode allowed the + * protocol to continue beyond M2. This allows ER to learn the + * current AP settings without changing them. + */ + wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse " + "registration of a new Registrar"); + wps->config_error = WPS_CFG_SETUP_LOCKED; + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings, attr->encr_settings_len); if (decrypted == NULL) { @@ -953,13 +1195,21 @@ static enum wps_process_res wps_process_m8(struct wps_data *wps, return WPS_CONTINUE; } + if (wps_validate_m8_encr(decrypted, wps->wps->ap, + attr->version2 != NULL) < 0) { + wpabuf_free(decrypted); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " "attribute"); if (wps_parse_msg(decrypted, &eattr) < 0 || wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) || wps_process_creds(wps, eattr.cred, eattr.cred_len, - eattr.num_cred) || - wps_process_ap_settings_e(wps, &eattr, decrypted)) { + eattr.num_cred, attr->version2 != NULL) || + wps_process_ap_settings_e(wps, &eattr, decrypted, + attr->version2 != NULL)) { wpabuf_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; @@ -982,44 +1232,55 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps, if (wps_parse_msg(msg, &attr) < 0) return WPS_FAILURE; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", - attr.version ? *attr.version : 0); - return WPS_FAILURE; - } - if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } if (attr.msg_type == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); - return WPS_FAILURE; + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; } switch (*attr.msg_type) { case WPS_M2: + if (wps_validate_m2(msg) < 0) + return WPS_FAILURE; ret = wps_process_m2(wps, msg, &attr); break; case WPS_M2D: + if (wps_validate_m2d(msg) < 0) + return WPS_FAILURE; ret = wps_process_m2d(wps, &attr); break; case WPS_M4: + if (wps_validate_m4(msg) < 0) + return WPS_FAILURE; ret = wps_process_m4(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) - wps_fail_event(wps->wps, WPS_M4); + wps_fail_event(wps->wps, WPS_M4, wps->config_error, + wps->error_indication, + wps->peer_dev.mac_addr); break; case WPS_M6: + if (wps_validate_m6(msg) < 0) + return WPS_FAILURE; ret = wps_process_m6(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) - wps_fail_event(wps->wps, WPS_M6); + wps_fail_event(wps->wps, WPS_M6, wps->config_error, + wps->error_indication, + wps->peer_dev.mac_addr); break; case WPS_M8: + if (wps_validate_m8(msg) < 0) + return WPS_FAILURE; ret = wps_process_m8(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) - wps_fail_event(wps->wps, WPS_M8); + wps_fail_event(wps->wps, WPS_M8, wps->config_error, + wps->error_indication, + wps->peer_dev.mac_addr); break; default: wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d", @@ -1055,12 +1316,6 @@ static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps, if (wps_parse_msg(msg, &attr) < 0) return WPS_FAILURE; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", - attr.version ? *attr.version : 0); - return WPS_FAILURE; - } - if (attr.msg_type == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); return WPS_FAILURE; @@ -1073,14 +1328,14 @@ static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps, } if (attr.registrar_nonce == NULL || - os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } @@ -1088,7 +1343,7 @@ static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps, if (wps->state == RECV_ACK && wps->wps->ap) { wpa_printf(MSG_DEBUG, "WPS: External Registrar registration " "completed successfully"); - wps_success_event(wps->wps); + wps_success_event(wps->wps, wps->peer_dev.mac_addr); wps->state = WPS_FINISHED; return WPS_DONE; } @@ -1101,18 +1356,13 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, const struct wpabuf *msg) { struct wps_parse_attr attr; + u16 config_error; wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK"); if (wps_parse_msg(msg, &attr) < 0) return WPS_FAILURE; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", - attr.version ? *attr.version : 0); - return WPS_FAILURE; - } - if (attr.msg_type == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); return WPS_FAILURE; @@ -1125,7 +1375,7 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, } if (attr.registrar_nonce == NULL || - os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce", @@ -1136,7 +1386,7 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, } if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce", attr.enrollee_nonce, WPS_NONCE_LEN); @@ -1151,18 +1401,22 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, return WPS_FAILURE; } + config_error = WPA_GET_BE16(attr.config_error); wpa_printf(MSG_DEBUG, "WPS: Registrar terminated negotiation with " - "Configuration Error %d", WPA_GET_BE16(attr.config_error)); + "Configuration Error %d", config_error); switch (wps->state) { case RECV_M4: - wps_fail_event(wps->wps, WPS_M3); + wps_fail_event(wps->wps, WPS_M3, config_error, + wps->error_indication, wps->peer_dev.mac_addr); break; case RECV_M6: - wps_fail_event(wps->wps, WPS_M5); + wps_fail_event(wps->wps, WPS_M5, config_error, + wps->error_indication, wps->peer_dev.mac_addr); break; case RECV_M8: - wps_fail_event(wps->wps, WPS_M7); + wps_fail_event(wps->wps, WPS_M7, config_error, + wps->error_indication, wps->peer_dev.mac_addr); break; default: break; @@ -1201,8 +1455,12 @@ enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps, case WSC_UPnP: return wps_process_wsc_msg(wps, msg); case WSC_ACK: + if (wps_validate_wsc_ack(msg) < 0) + return WPS_FAILURE; return wps_process_wsc_ack(wps, msg); case WSC_NACK: + if (wps_validate_wsc_nack(msg) < 0) + return WPS_FAILURE; return wps_process_wsc_nack(wps, msg); default: wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);