diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d935435 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.o +.*.swp +tags +cscope.out diff --git a/src/common/defs.h b/src/common/defs.h index 9adc2ac..097e5fc 100644 --- a/src/common/defs.h +++ b/src/common/defs.h @@ -72,6 +72,7 @@ static inline int wpa_key_mgmt_sha256(int akm) #define WPA_PROTO_WPA BIT(0) #define WPA_PROTO_RSN BIT(1) +#define WPA_PROTO_WPS BIT(2) #define WPA_AUTH_ALG_OPEN BIT(0) #define WPA_AUTH_ALG_SHARED BIT(1) diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 05d7a4e..247de59 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -49,6 +49,10 @@ extern "C" { /** New scan results available */ #define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS " +#define WPA_EVENT_WPS_ERROR "CTRL-EVENT-WPS-ERROR " +#define WPA_EVENT_WPS_OVERLAP "CTRL-EVENT-WPS-OVERLAP " +#define WPA_EVENT_WPS_SUCCESS "CTRL-EVENT-WPS-SUCCESS " + /* wpa_supplicant/hostapd control interface access */ diff --git a/src/crypto/crypto_internal.c b/src/crypto/crypto_internal.c index 9324c6e..073d2d8 100644 --- a/src/crypto/crypto_internal.c +++ b/src/crypto/crypto_internal.c @@ -790,7 +790,7 @@ void crypto_global_deinit(void) } -#ifdef EAP_FAST +#if defined(EAP_FAST) || defined(EAP_WPS) int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, diff --git a/src/crypto/crypto_libtomcrypt.c b/src/crypto/crypto_libtomcrypt.c index e82097f..d0f645d 100644 --- a/src/crypto/crypto_libtomcrypt.c +++ b/src/crypto/crypto_libtomcrypt.c @@ -697,7 +697,7 @@ void crypto_global_deinit(void) } -#ifdef EAP_FAST +#if defined(EAP_FAST) || defined(EAP_WPS) int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 77a2ceb..fbdb594 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -31,6 +31,7 @@ #define IEEE80211_CAP_PRIVACY 0x0010 #define SSID_MAX_WPA_IE_LEN 40 +#define SSID_MAX_WPS_IE_LEN 256 /** * struct wpa_scan_result - Scan results (old structure) * @bssid: BSSID @@ -204,6 +205,15 @@ struct wpa_driver_associate_params { int wep_tx_keyidx; /** + * wps_ie - WPS information element for Association/Probe Request + */ + const u8 *wps_ie; + /** + * wps_ie_len - length of wps_ie + */ + size_t wps_ie_len; + + /** * mgmt_frame_protection - IEEE 802.11w management frame protection */ enum { @@ -1232,6 +1242,7 @@ void wpa_supplicant_sta_free_hw_features(struct wpa_hw_modes *hw_features, const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie); #define WPA_IE_VENDOR_TYPE 0x0050f201 +#define WPS_IE_VENDOR_TYPE 0x0050f204 const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res, u32 vendor_type); int wpa_scan_get_max_rate(const struct wpa_scan_res *res); diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c index a618a74..5d3215d 100644 --- a/src/drivers/driver_wext.c +++ b/src/drivers/driver_wext.c @@ -2209,7 +2209,10 @@ int wpa_driver_wext_associate(void *priv, /* TODO: should consider getting wpa version and cipher/key_mgmt suites * from configuration, not from here, where only the selected suite is * available */ - if (wpa_driver_wext_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len) + if (0 != params->wps_ie_len) { + if (wpa_driver_wext_set_gen_ie(drv, params->wps_ie, params->wps_ie_len) < 0) + ret = -1; + } else if (wpa_driver_wext_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0) ret = -1; if (params->wpa_ie == NULL || params->wpa_ie_len == 0) @@ -2760,6 +2763,11 @@ static int wpa_driver_wext_set_param(void *priv, const char *param) return 0; } +int wpa_driver_wext_set_ie(void *priv, const u8 *ie, size_t ie_len) +{ + return wpa_driver_wext_set_gen_ie(priv, ie, ie_len); +} + int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv) { @@ -2800,4 +2808,5 @@ const struct wpa_driver_ops wpa_driver_wext_ops = { .mlme_add_sta = wpa_driver_wext_mlme_add_sta, .mlme_remove_sta = wpa_driver_wext_mlme_remove_sta, #endif /* CONFIG_CLIENT_MLME */ + .set_probe_req_ie = wpa_driver_wext_set_ie, }; diff --git a/src/eap_common/eap_common.c b/src/eap_common/eap_common.c index 4afa1dd..5c896af 100644 --- a/src/eap_common/eap_common.c +++ b/src/eap_common/eap_common.c @@ -41,6 +41,9 @@ const u8 * eap_hdr_validate(int vendor, EapType eap_type, const u8 *pos; size_t len; + if (NULL == msg) + return NULL; + hdr = wpabuf_head(msg); if (wpabuf_len(msg) < sizeof(*hdr)) { diff --git a/src/eap_common/eap_defs.h b/src/eap_common/eap_defs.h index 2cb2bb7..e3aba39 100644 --- a/src/eap_common/eap_defs.h +++ b/src/eap_common/eap_defs.h @@ -45,6 +45,7 @@ enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3, typedef enum { EAP_TYPE_NONE = 0, EAP_TYPE_IDENTITY = 1 /* RFC 3748 */, + EAP_TYPE_SIMPLE_CONFIG = 1, EAP_TYPE_NOTIFICATION = 2 /* RFC 3748 */, EAP_TYPE_NAK = 3 /* Response only, RFC 3748 */, EAP_TYPE_MD5 = 4, /* RFC 3748 */ @@ -79,6 +80,8 @@ enum { EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */ }; +#define WPS_EAP_IDENTITY "WFA-SimpleConfig-Enrollee-1-0" + #define EAP_MSK_LEN 64 #define EAP_EMSK_LEN 64 diff --git a/src/eap_common/eap_wps_common.c b/src/eap_common/eap_wps_common.c new file mode 100644 index 0000000..a533baa --- /dev/null +++ b/src/eap_common/eap_wps_common.c @@ -0,0 +1,339 @@ +/* + * EAP common method: EAP-WPS + * Copyright (c) 2008, Chuck Tuffli + * + * 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. + */ + +#include "includes.h" + +#include "common.h" +#include "tlv.h" +#include "sha256.h" +#include "aes_wrap.h" +#include "crypto.h" +#include "dh_groups.h" +#include "wps.h" +#include "eap_wps_common.h" +#include "wpa_ctrl.h" + + +const char *config_err_str[] = { + "No Error", + "OOB Interface Read Error", + "Decryption CRC Failure", + "2.4 channel not supported", + "5.0 channel not supported", + "Signal too weak", + "Network auth failure", + "Network association failure", + "No DHCP response", + "Failed DHCP config", + "IP address conflict", + "Couldn't connect to Registrar", + "Multiple PBC sessions detected", + "Rogue activity suspected", + "Device busy", + "Setup locked", + "Message Timeout", + "Registration Session Timeout", + "Device Password Auth Failure" +}; + +const size_t config_err_str_len = sizeof(config_err_str)/sizeof(char *); + + +int eap_wps_kdk(struct wpabuf *PKR, struct wpabuf *SKE, + u8 *N1, u8 *N2, u8 *enrolleeMac, u8 *kdk) +{ + struct wpabuf *dh_key; + const u8 *addr[3]; + size_t len[3]; + int ret = 0; + + /* KDK = HMAC-SHA-256 dh_key (N1 || EnrolleeMAC || N2) */ + + dh_key = dh_derive_shared(PKR, SKE, dh_groups_get(5)); + wpa_hexdump_key(MSG_DEBUG, "EAP-WPS: dh_key", wpabuf_head(dh_key), + wpabuf_len(dh_key)); + + addr[0] = N1; + len[0] = WPS_NONCE_LEN; + addr[1] = enrolleeMac; + len[1] = ETH_ALEN; + addr[2] = N2; + len[2] = WPS_NONCE_LEN; + + hmac_sha256_vector(wpabuf_head(dh_key), wpabuf_len(dh_key), + 3, addr, len, kdk); + + return ret; +} + +#define WPS_TOTAL_KEY_BITS 640 +int eap_wps_kdf(u8 *authKey, u8 *keyWrapKey, u8 *emsk, const u8 *kdk) +{ + const char personalization[] = "Wi-Fi Easy and Secure Key Derivation"; + /* #define for the PRF digest size (ie HMAC-SHA-256) is in bytes */ + const u32 iterations = + (WPS_TOTAL_KEY_BITS + (SHA256_MAC_LEN * 8) - 1)/(SHA256_MAC_LEN * 8); + size_t i; + u8 *keys = NULL, *k; + const u8 *addr[3]; + size_t len[3]; + u32 iterations_be; + u32 total_key_bits_be; + + if (NULL == (keys = os_zalloc(iterations * SHA256_MAC_LEN))) { + wpa_printf(MSG_ERROR, "EAP-WPS: Couldn't allocate KDF buffer"); + return -1; + } + + addr[0] = (u8 *)&iterations_be; + len[0] = sizeof(u32); + addr[1] = (u8 *)personalization; + len[1] = os_strlen(personalization); + addr[2] = (u8 *)&total_key_bits_be; + len[2] = sizeof(u32); + + k = keys; + total_key_bits_be = host_to_be32(WPS_TOTAL_KEY_BITS); + for (i=1; i<=iterations; i++) { + iterations_be = host_to_be32(i); + hmac_sha256_vector(kdk, SHA256_MAC_LEN, 3, addr, len, k); + k += SHA256_MAC_LEN; + } + + k = keys; + + os_memcpy(authKey, k, WPS_AUTHKEY_LEN); + k += WPS_AUTHKEY_LEN; + + os_memcpy(keyWrapKey, k, WPS_KEYWRAPKEY_LEN); + k += WPS_KEYWRAPKEY_LEN; + + os_memcpy(emsk, k, WPS_EMSK_LEN); + + os_free(keys); + + return 0; +} + +int eap_wps_hash(u8 *authKey, u8 *PSK, const u8 *PKE, const u8 *PKR, + u8 *ES, + char *dev_passwd, size_t dev_passwd_len, + u8 *hash) +{ + const u8 *addr[4]; + size_t len[4]; + + if (os_get_random(ES, WPS_S_NONCE_LEN)) { + wpa_printf(MSG_ERROR, "EAP-WPS: Failed to get random data for E-Sn"); + return -1; + } + + hmac_sha256(authKey, WPS_AUTHKEY_LEN, (u8 *)dev_passwd, + dev_passwd_len, PSK); + + addr[0] = ES; + len[0] = WPS_S_NONCE_LEN; + addr[1] = PSK; + len[1] = SHA256_MAC_LEN/2; /* only use the first 128-bits */ + addr[2] = PKE; + len[2] = WPS_PK_LEN; + addr[3] = PKR; + len[3] = WPS_PK_LEN; + + hmac_sha256_vector(authKey, WPS_AUTHKEY_LEN, 4, addr, len, hash); + + return 0; +} + +int eap_wps_authenticator(u8 *authKey, + const u8 *d1, size_t d1_len, + u8 *d2, size_t d2_len, + u8 *auth) +{ + const u8 *addr[2]; + size_t len[2]; + u8 hmac[SHA256_MAC_LEN]; + size_t n_vector = 2; + + addr[0] = d1; + len[0] = d1_len; + addr[1] = d2; + len[1] = d2_len; + + if (NULL == d2) { + n_vector = 1; + } + hmac_sha256_vector(authKey, WPS_AUTHKEY_LEN, n_vector, addr, len, hmac); + + os_memcpy(auth, hmac, WPS_AUTHENTICATOR_LEN); + + return 0; +} + +/** + * eap_wps_authenticator_check - validate authenticator value + * @authKey: Pointer to AuthKey + * @prv: Pointer to the previous frame (Opcode and Flags not included) + * @prv_len: Length of previous frame + * @cur: Pointer to the current frame (Opcode and Flags not included) + * @cur_len: Length of current frame + * @auth: Pointer to the expected authenticator value + * Returns: 0 if authenticator value matches, non-zero otherwise + * + * Calculate the authenticator value for the previous and current + * frames and compare it to the expected value. + */ +int eap_wps_authenticator_check(u8 *authKey, + const u8 *prv, size_t prv_len, + const u8 *cur, size_t cur_len, + const u8 *auth) +{ + u8 hmac[SHA256_MAC_LEN]; + + /* + * Message Authenticator is a keyed hash (HMAC-SHA_256) of (M(n-1) | Mn*) + * using the AuthKey. + * Note that M* should not include the Opcode or Flags + * Also note that the calculation does not include the authenticator + * TLV from the current frame. + */ + cur_len -= TLV_LEN + WPS_AUTHENTICATOR_LEN; + eap_wps_authenticator(authKey, prv, prv_len, (u8 *)cur, cur_len, + hmac); + + return os_memcmp(hmac, auth, WPS_AUTHENTICATOR_LEN); +} + + +int eap_wps_encrypted_settings(u8 *authKey, u8 *keyWrapKey, + u16 type, u8 *data, size_t data_len, + u8 *encrypted) +{ + u8 *e = encrypted; + u8 authenticator[WPS_KWA_LEN]; + size_t i; + size_t len = 0; + + if (os_get_random(e, WPS_IV_LEN)) { + wpa_printf(MSG_ERROR, "EAP-WPS: Failed to get random data for IV"); + return -1; + } + + e = tlv_write(e + WPS_IV_LEN, type, data_len, data); + + /* + * Key Wrap Authenticator + */ + eap_wps_authenticator(authKey, encrypted + WPS_IV_LEN, data_len + TLV_LEN, + NULL, 0, authenticator); + e = tlv_write(e, WPS_ELEM_KEY_WRAP_AUTHENTICATOR, WPS_KWA_LEN, + authenticator); + + /* Pad out the data re. PKCS #5 V2.0 */ + for (i=0; i<16; i++) { + *e++ = 0x10; + } + + len = (size_t)e - (size_t)encrypted - WPS_IV_LEN; + + /* encrypted starts with the IV */ + return aes_128_cbc_encrypt(keyWrapKey, encrypted, encrypted + WPS_IV_LEN, len); +} + + +int eap_wps_decrypt_settings(u8 *keyWrapKey, u8 *iv, + u8 *encrypted, size_t encrypted_len, + u8 *decrypted, size_t *decrypted_len) +{ + int ret = 0; + u8 pad_len = 0; + + *decrypted_len = 0; + os_memcpy(decrypted, encrypted, encrypted_len); + if (aes_128_cbc_decrypt(keyWrapKey, iv, decrypted, encrypted_len)) { + return -1; + } + + /* + * Last byte value in the decrypted data is length of padding at + * end of buffer. The value should be between 1 and 16. Return + * -1 if pad value is out of range. Otherwise adjust the value + * of decrytped length + */ + pad_len = *(decrypted + encrypted_len - 1); + if (pad_len > 16) { + ret = -1; + } else { + *decrypted_len = encrypted_len - pad_len; + ret = 0; + } + + wpa_hexdump(MSG_DEBUG, "EAP-WPS: decrypted settings", decrypted, *decrypted_len); + + return ret; +} + + +u8 *eap_wps_ack(u8 *N1, u8 *N2, size_t *len) +{ + u8 *payload, *p; + + *len = 0; + + if (NULL != (p = payload = os_malloc(WPS_MSG_BUF_LEN))) { + *p++ = WPS_OPCODE_WSC_ACK; + *p++ = 0; /* Flags */ + + p = tlv_write_u8(p, WPS_ELEM_VERSION, WPS_VERSION_1_0); + + p = tlv_write_u8(p, WPS_ELEM_MESSAGE_TYPE, + WPS_MESSAGE_TYPE_WSC_ACK); + + p = tlv_write(p, WPS_ELEM_ENROLLEE_NONCE, WPS_NONCE_LEN, N1); + + p = tlv_write(p, WPS_ELEM_REGISTRAR_NONCE, WPS_NONCE_LEN, N2); + + *len = (size_t)p - (size_t)payload; + } + + return payload; +} + +u8 *eap_wps_nack(u8 *N1, u8 *N2, size_t *len, u16 error) +{ + u8 *payload, *p; + + *len = 0; + + if (NULL != (p = payload = os_malloc(WPS_MSG_BUF_LEN))) { + *p++ = WPS_OPCODE_WSC_NACK; + *p++ = 0; /* Flags */ + + p = tlv_write_u8(p, WPS_ELEM_VERSION, WPS_VERSION_1_0); + + p = tlv_write_u8(p, WPS_ELEM_MESSAGE_TYPE, + WPS_MESSAGE_TYPE_WSC_NACK); + + p = tlv_write(p, WPS_ELEM_ENROLLEE_NONCE, WPS_NONCE_LEN, N1); + + p = tlv_write(p, WPS_ELEM_REGISTRAR_NONCE, WPS_NONCE_LEN, N2); + + p = tlv_write_u16(p, WPS_ELEM_CONFIGURATION_ERROR, error); + + *len = (size_t)p - (size_t)payload; + } + + return payload; +} + diff --git a/src/eap_common/eap_wps_common.h b/src/eap_common/eap_wps_common.h new file mode 100644 index 0000000..8f76669 --- /dev/null +++ b/src/eap_common/eap_wps_common.h @@ -0,0 +1,115 @@ +/* + * EAP common method: EAP-WPS + * Copyright (c) 2008, Chuck Tuffli + * + * 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. + */ + +#ifndef EAP_WPS_COMMON_H +#define EAP_WPS_COMMON_H + +/** + * WPS_PARSE_ERR - Incoming message did not parse correctly + */ +#define WPS_PARSE_ERR -1 + +/** + * WPS_AUTHENTICATOR_ERR - Incoming message's authenticator invalid + */ +#define WPS_AUTHENTICATOR_ERR -2 + +/** + * WPS_M2D_ERR - Received a M2D message instead of M2 + */ +#define WPS_M2D_ERR -3 + +/** + * WPS_NONCE_LEN - Byte length of 128-bit random number (nonce) + */ +#define WPS_NONCE_LEN (128/8) + +/** + * EAP_WPS_KDK_LEN - Byte length of 256-bit HMAC-SHA-256 Key Derivation Key + */ +#define EAP_WPS_KDK_LEN (256/8) + +/** + * WPS_AUTHKEY_LEN - AuthKey byte length (256-bits) + */ +#define WPS_AUTHKEY_LEN (256/8) + +/** + * WPS_KEYWRAPKEY_LEN - KeyWrapKey byte length (128-bits) + */ +#define WPS_KEYWRAPKEY_LEN (128/8) + +/** + * WPS_EMSK_LEN - EMSK byte length (256-bits) + */ +#define WPS_EMSK_LEN (256/8) + +/** + * WPS_S_NONCE_LEN - Secret nonces (ie E-S1, E-S2, R-S1, R-S2) byte length (128-bits) + */ +#define WPS_S_NONCE_LEN (128/8) + +/** + * WPS_AUTHENTICATOR_LEN - Authenticator element byte length (64-bits) + */ +#define WPS_AUTHENTICATOR_LEN (64/8) + +/** + * WPS_IV_LEN - Initialization Vector Length (128-bits) + */ +#define WPS_IV_LEN (128/8) + +/** + * WPS_KWA_LEN - Key Wrap Authenticator Length (64-bits) + */ +#define WPS_KWA_LEN (64/8) + +/** + * WPS_ENCRYPTED_SETTINGS_LEN - Encrypted Settings Length + */ +#define WPS_ENCRYPTED_SETTINGS_LEN 64 + +/** + * WPS_PK_LEN - Enrollee/Registrar's Public Key length (1536-bits) + */ +#define WPS_PK_LEN (1536/8) + +/** + * WPS_MSG_BUF_LEN - Default allocation size of the message buffer + */ +#define WPS_MSG_BUF_LEN 512 + +extern const char *config_err_str[]; +extern const size_t config_err_str_len; + +extern int eap_wps_kdk(struct wpabuf *PKR, struct wpabuf *SKE, + u8 *N1, u8 *N2, u8 *enrolleeMac, u8 *kdk); +extern int eap_wps_kdf(u8 *authKey, u8 *keyWrapKey, u8 *emsk, const u8 *kdk); +extern int eap_wps_hash(u8 *authKey, u8 *PSK, const u8 *PKE, const u8 *PKR, + u8 *ES, char *dev_passwd, size_t dev_passwd_len, u8 *hash); +extern int eap_wps_authenticator(u8 *authKey, const u8 *d1, size_t d1_len, + u8 *d2, size_t d2_len, u8 *auth); +extern int eap_wps_authenticator_check(u8 *authKey, + const u8 *prv, size_t prv_len, + const u8 *cur, size_t cur_len, + const u8 *auth); +extern int eap_wps_encrypted_settings(u8 *authKey, u8 *keyWrapKey, + u16 type, u8 *data, size_t data_len, u8 *encrypted); +extern int eap_wps_decrypt_settings(u8 *keyWrapKey, u8 *iv, + u8 *encrypted, size_t encrypted_len, + u8 *decrypted, size_t *decrypted_len); +extern u8 *eap_wps_ack(u8 *N1, u8 *N2, size_t *len); +extern u8 *eap_wps_nack(u8 *N1, u8 *N2, size_t *len, u16 error); + +#endif /* EAP_WPS_COMMON_H */ diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c index 71bb07f..ef61a7e 100644 --- a/src/eap_peer/eap.c +++ b/src/eap_peer/eap.c @@ -674,7 +674,11 @@ static void eap_peer_sm_step_local(struct eap_sm *sm) SM_ENTER(EAP, SEND_RESPONSE); break; case EAP_SEND_RESPONSE: - SM_ENTER(EAP, IDLE); + if (sm->decision == DECISION_FAIL && sm->methodState == + METHOD_DONE) + SM_ENTER(EAP, FAILURE); + else + SM_ENTER(EAP, IDLE); break; case EAP_DISCARD: SM_ENTER(EAP, IDLE); diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h index 3fd3783..8313a64 100644 --- a/src/eap_peer/eap_config.h +++ b/src/eap_peer/eap_config.h @@ -561,6 +561,14 @@ struct eap_peer_config { size_t new_password_len; /** + * assoc_state - The WPS association state + * + * Set by the EAP method and used by the supplicant to determine + * if an association attempt failed + */ + u16 assoc_state; + + /** * fragment_size - Maximum EAP fragment size in bytes (default 1398) * * This value limits the fragment size for EAP methods that support diff --git a/src/eap_peer/eap_methods.c b/src/eap_peer/eap_methods.c index 0973b2f..704b1ee 100644 --- a/src/eap_peer/eap_methods.c +++ b/src/eap_peer/eap_methods.c @@ -476,6 +476,13 @@ int eap_peer_register_methods(void) } #endif /* EAP_TNC */ +#ifdef EAP_WPS + if (ret == 0) { + int eap_peer_wps_register(void); + ret = eap_peer_wps_register(); + } +#endif + return ret; } diff --git a/src/eap_peer/eap_wps.c b/src/eap_peer/eap_wps.c new file mode 100644 index 0000000..04ea9b2 --- /dev/null +++ b/src/eap_peer/eap_wps.c @@ -0,0 +1,1373 @@ +/* + * EAP peer method: EAP-WPS + * Copyright (c) 2008, Chuck Tuffli + * + * 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. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_config.h" +#include "tlv.h" +#include "eap_i.h" +#include "sha256.h" +#include "crypto.h" +#include "dh_groups.h" +#include "wps.h" +#include "eap_common/eap_wps_common.h" +#include "wpa_ctrl.h" + + +struct eap_wps_data { + enum { WPS_START, WPS_M2, WPS_M4, WPS_M6, WPS_M8 } state; + + /* Enrollee's nonce */ + u8 N1[WPS_NONCE_LEN]; + + /* Enrollee's Diffie-Hellman private (secret) key */ + struct wpabuf *SKE; + + /* Enrollee's Diffie-Hellman public key */ + struct wpabuf *PKE; + + /* Enrollee's PSK values derived from Device Password */ + u8 PSK1[SHA256_MAC_LEN]; + u8 PSK2[SHA256_MAC_LEN]; + + /* Enrollee's secret nonces */ + u8 ES1[WPS_S_NONCE_LEN]; + u8 ES2[WPS_S_NONCE_LEN]; + + /* Registrar's nonce */ + u8 N2[WPS_NONCE_LEN]; + + /* Registrar's Diffie-Hellman public key */ + struct wpabuf *PKR; + + /* Registrar's Hashes */ + u8 RHASH1[SHA256_MAC_LEN]; + u8 RHASH2[SHA256_MAC_LEN]; + + u8 authKey[WPS_AUTHKEY_LEN]; + u8 keyWrapKey[WPS_KEYWRAPKEY_LEN]; + u8 emsk[WPS_EMSK_LEN]; + + u8 *prv_msg; + size_t prv_msg_len; + + u8 uuid[WPS_UUID_LEN]; + u8 own_addr[ETH_ALEN]; + char pin[WPS_PIN_LEN]; + u16 auth_type; + u16 encryption_type; + u16 assoc_state; + + struct wps_credential { + struct wps_credential *next; + char *ssid; + size_t ssid_len; + u8 bssid[ETH_ALEN]; + u16 auth_type; + u16 encryption_type; + u8 *network_key; + size_t network_key_len; + } *credential; +}; + + +static int eap_wps_get_req(struct eap_wps_data *data, + const struct wpa_config_blob *blob) +{ + const u8 *pos; + const u8 *end; + u16 tlv_type, tlv_len; + + if (NULL == blob) + return -1; + + pos = blob->data; + end = pos + blob->len; + + do { + tlv_type = TLV_GET_TYPE(pos); + tlv_len = TLV_GET_LEN(pos); + pos = TLV_GET_VAL(pos); + + switch (tlv_type) { + case WPS_ELEM_AUTHENTICATION_TYPE_FLAG: + data->auth_type = WPA_GET_BE16(pos); + break; + case WPS_ELEM_ENCRYPTION_TYPE_FLAG: + data->encryption_type = WPA_GET_BE16(pos); + break; + case WPS_ELEM_MAC_ADDRESS: + os_memcpy(data->own_addr, pos, ETH_ALEN); + break; + case WPS_ELEM_UUID_E: + os_memcpy(data->uuid, pos, WPS_UUID_LEN); + break; + case WPS_ELEM_VENDOR_EXTENSION: + os_memcpy(data->pin, pos, WPS_PIN_LEN); + break; + } + + pos += tlv_len; + } while (pos < end); + + return 0; +} + + +/** + * wpa_config_blob + */ +static struct wpa_config_blob *eap_wps_set_res(struct eap_wps_data *data) +{ + struct wps_credential *c; + size_t n_credential = 0; + struct wpa_config_blob *blob; + u8 *pos = NULL; + u8 index; + + blob = os_zalloc(sizeof(struct wpa_config_blob)); + if (NULL != blob) { + blob->name = os_strdup(WPS_BLOB_NAME_RESULT); + + /* calculate the size of one credential */ + blob->len = TLV_BYTES(WPS_ELEM_LEN_NETWORK_INDEX) + + TLV_BYTES(WPS_ELEM_LEN_SSID) + + TLV_BYTES(WPS_ELEM_LEN_AUTHENTICATION_TYPE_FLAG) + + TLV_BYTES(WPS_ELEM_LEN_ENCRYPTION_TYPE_FLAG) + + TLV_BYTES(WPS_ELEM_LEN_NETWORK_KEY); + + c = data->credential; + while (c) { + n_credential++; + c = c->next; + } + + blob->len *= n_credential; + + if (NULL == (blob->data = os_malloc(blob->len))) { + os_free(blob); + return NULL; + } + + pos = blob->data; + c = data->credential; + index = 0; + while (c) { + pos = tlv_write_u8(pos, WPS_ELEM_NETWORK_INDEX, index); + pos = tlv_write(pos, WPS_ELEM_SSID, c->ssid_len, (u8 *)c->ssid); + pos = tlv_write_u16(pos, WPS_ELEM_AUTHENTICATION_TYPE_FLAG, + c->auth_type); + pos = tlv_write_u16(pos, WPS_ELEM_ENCRYPTION_TYPE_FLAG, + c->encryption_type); + pos = tlv_write(pos, WPS_ELEM_NETWORK_KEY, + c->network_key_len, c->network_key); + + index++; + c = c->next; + } + } + + return blob; +} + +static void eap_wps_credential_free(struct wps_credential *credential) +{ + if (NULL == credential) { + return; + } + + os_free(credential->ssid); + os_free(credential->network_key); + os_free(credential); +} + +static void eap_wps_credential_free_all(struct wps_credential *credential) +{ + struct wps_credential *c_next; + + while (credential) { + c_next = credential->next; + eap_wps_credential_free(credential); + credential = c_next; + } +} + +/** + * eap_wps_msg_M1 - Create the payload for a M1 message + */ +static u8 *eap_wps_msg_M1(struct eap_wps_data *data, size_t *len) +{ + u8 *payload, *p; + + *len = 0; + + wpa_hexdump(MSG_DEBUG, "EAP-WPS: UUID", data->uuid, WPS_UUID_LEN); + + if (NULL != (p = payload = os_malloc(WPS_MSG_BUF_LEN))) { + *p++ = WPS_OPCODE_WSC_MSG; + *p++ = 0; /* Flags */ + + p = tlv_write_u8(p, WPS_ELEM_VERSION, WPS_VERSION_1_0); + + p = tlv_write_u8(p, WPS_ELEM_MESSAGE_TYPE, WPS_MESSAGE_TYPE_M1); + + p = tlv_write(p, WPS_ELEM_UUID_E, WPS_UUID_LEN, data->uuid); + + p = tlv_write(p, WPS_ELEM_MAC_ADDRESS, ETH_ALEN, + data->own_addr); + + p = tlv_write(p, WPS_ELEM_ENROLLEE_NONCE, WPS_NONCE_LEN, + data->N1); + + /* group 5 is the 1536 bit prime that WPS uses */ + data->PKE = dh_init(dh_groups_get(5), &data->SKE); + if (NULL == data->PKE) { + os_free(payload); + return NULL; + } + p = tlv_write(p, WPS_ELEM_PUBLIC_KEY, WPS_PK_LEN, + wpabuf_head(data->PKE)); + + p = tlv_write_u16(p, WPS_ELEM_AUTHENTICATION_TYPE_FLAG, + data->auth_type); + + p = tlv_write_u16(p, WPS_ELEM_ENCRYPTION_TYPE_FLAG, + data->encryption_type); + + p = tlv_write_u8(p, WPS_ELEM_CONNECTION_TYPE_FLAG, + WPS_CONNECTION_TYPE_ESS); + + p = tlv_write_u16(p, WPS_ELEM_CONFIG_METHODS, + WPS_CONFIG_METHOD_LABEL | + WPS_CONFIG_METHOD_DISPLAY | + WPS_CONFIG_METHOD_PBC); + + p = tlv_write_u8(p, WPS_ELEM_WPS_STATE, + WPS_SETUP_STATE_NOT_CONFIGURED); + + /* TODO get these somehow instead of using "None" and PC type */ + p = tlv_write(p, WPS_ELEM_MANUFACTURER, 4, (u8 *)"None"); + + p = tlv_write(p, WPS_ELEM_MODEL_NAME, 4, (u8 *)"None"); + + p = tlv_write(p, WPS_ELEM_MODEL_NUMBER, 4, (u8 *)"None"); + + p = tlv_write(p, WPS_ELEM_SERIAL_NUMBER, 4, (u8 *)"None"); + + p = tlv_write(p, WPS_ELEM_PRIMARY_DEVICE_TYPE, 8, + (u8 *)"\x00\x01\x00\x50\xf2\x04\x00\x01"); + + p = tlv_write(p, WPS_ELEM_DEVICE_NAME, 4, (u8 *)"None"); + + /* TODO get this somehow instead of assuming a dual-mode device */ + p = tlv_write_u8(p, WPS_ELEM_RF_BANDS, + WPS_RF_BAND_2_4GHZ | WPS_RF_BAND_5_0GHZ); + + p = tlv_write_u16(p, WPS_ELEM_ASSOCIATION_STATE, + WPS_ASSOCIATION_STATE_NOT_ASSOCIATED); + + p = tlv_write_u16(p, WPS_ELEM_DEVICE_PASSWORD_ID, + 0 == os_strncmp(data->pin, WPS_PIN_PBC, WPS_PIN_LEN) ? + WPS_DEVICE_PASSWORD_ID_PBC : WPS_DEVICE_PASSWORD_ID_PIN); + + p = tlv_write_u16(p, WPS_ELEM_CONFIGURATION_ERROR, + WPS_CONFIGURATION_ERROR_NO_ERROR); + + p = tlv_write(p, WPS_ELEM_OS_VERSION, 4, (u8 *)"\x00\x00\x00\x01"); + } + + *len = (size_t)p - (size_t)payload; + + return payload; +} + + +/** + * eap_wps_parse_M2 - Parse the payload for a M2 message + */ +static int eap_wps_parse_M2(struct eap_sm *sm, struct eap_wps_data *data, + const u8 *payload, size_t len, u8 **auth) +{ + const u8 *pos = payload; + const u8 *end = pos + len; + u16 tlv_type, tlv_len, tlv_val; + int ret = WPS_PARSE_ERR; + + *auth = NULL; + + if (WPS_OPCODE_WSC_MSG != *pos) { + wpa_printf(MSG_WARNING, "EAP-WPS: Message is not WSC_MSG (%#02x)", + *pos); + wpa_hexdump(MSG_WARNING, "message:", pos, len); + return -1; + } + + pos++; /* Opcode */ + pos++; /* Flags */ + + do { + tlv_type = TLV_GET_TYPE(pos); + tlv_len = TLV_GET_LEN(pos); + pos = TLV_GET_VAL(pos); + + switch (tlv_type) { + case WPS_ELEM_MESSAGE_TYPE: + switch (pos[0]) { + case WPS_MESSAGE_TYPE_M2: + ret = 0; + break; + case WPS_MESSAGE_TYPE_M2D: + wpa_printf(MSG_DEBUG, "EAP-WPS: received M2D"); + ret = WPS_M2D_ERR; + break; + default: + wpa_printf(MSG_WARNING, "EAP-WPS: Expecting message M2 but got %#x", + pos[0]); + return WPS_PARSE_ERR; + } + break; + case WPS_ELEM_REGISTRAR_NONCE: + os_memcpy(data->N2, pos, WPS_NONCE_LEN); + break; + case WPS_ELEM_PUBLIC_KEY: + if (NULL == (data->PKR = wpabuf_alloc_copy(pos, WPS_PK_LEN))) { + wpa_printf(MSG_ERROR, "EAP-WPS: can't allocate PKR"); + return -1; + } + break; + case WPS_ELEM_CONFIGURATION_ERROR: + tlv_val = WPA_GET_BE16(pos); + if (tlv_val > config_err_str_len) { + wpa_printf(MSG_ERROR, "EAP-WPS: Bad M2 Configuration error value %#x", + tlv_val); + return -1; + } else if (WPS_CONFIGURATION_ERROR_MULTI_PBC == tlv_val) { + wpa_msg(sm->msg_ctx, MSG_ERROR, WPA_EVENT_WPS_OVERLAP); + return -1; + } else if (WPS_CONFIGURATION_ERROR_NO_ERROR != tlv_val) { + wpa_msg(sm->msg_ctx, MSG_ERROR, WPA_EVENT_WPS_ERROR); + wpa_printf(MSG_WARNING, "EAP-WPS: M2 Configuration error %s", + config_err_str[tlv_val]); + return -1; + } + break; + case WPS_ELEM_AUTHENTICATOR: + *auth = (u8 *)pos; + break; + } + + pos += tlv_len; + } while (pos < end); + + return ret; +} + +/** + * eap_wps_msg_M3 - Create the payload for a M3 message + */ +static u8 *eap_wps_msg_M3(struct eap_wps_data *data, const u8 *M2, + size_t M2_len, u8 *addr, char *pin, size_t *len) +{ + size_t half_pin_len; + u8 *payload, *p; + u8 kdk[EAP_WPS_KDK_LEN]; + u8 hash[SHA256_MAC_LEN]; + u8 authenticator[WPS_AUTHENTICATOR_LEN]; + + *len = 0; + + if (NULL == pin) { + wpa_printf(MSG_ERROR, "EAP-WPS: PIN is NULL?!?"); + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-WPS: pin %s", pin); + + half_pin_len = os_strlen(pin) / 2; + + if (NULL != (p = payload = os_malloc(WPS_MSG_BUF_LEN))) { + *p++ = WPS_OPCODE_WSC_MSG; + *p++ = 0; /* Flags */ + + p = tlv_write_u8(p, WPS_ELEM_VERSION, WPS_VERSION_1_0); + + p = tlv_write_u8(p, WPS_ELEM_MESSAGE_TYPE, WPS_MESSAGE_TYPE_M3); + + p = tlv_write(p, WPS_ELEM_REGISTRAR_NONCE, WPS_NONCE_LEN, + data->N2); + + /* Generate a KDK used to create the AuthKey, KeyWrapKey, and EMSK */ + if (eap_wps_kdk(data->PKR, data->SKE, data->N1, data->N2, addr, kdk)) { + os_free(payload); + return NULL; + } + + eap_wps_kdf(data->authKey, data->keyWrapKey, data->emsk, kdk); + + eap_wps_hash(data->authKey, data->PSK1, wpabuf_head(data->PKE), + wpabuf_head(data->PKR), data->ES1, + pin, half_pin_len, + hash); + p = tlv_write(p, WPS_ELEM_E_HASH1, sizeof(hash), hash); + + eap_wps_hash(data->authKey, data->PSK2, wpabuf_head(data->PKE), + wpabuf_head(data->PKR), data->ES2, + pin + half_pin_len, half_pin_len, + hash); + p = tlv_write(p, WPS_ELEM_E_HASH2, sizeof(hash), hash); + + *len = (size_t)p - (size_t)payload; + + /* + * Message Authenticator is a keyed hash (HMAC-SHA_256) of (M2 | M3*) + * using the AuthKey. + * Note that M2 and M3 do not include the Opcode or Flags (thus +/- 2) + */ + eap_wps_authenticator(data->authKey, M2 + 2, M2_len - 2, payload + 2, + *len - 2, authenticator); + p = tlv_write(p, WPS_ELEM_AUTHENTICATOR, WPS_AUTHENTICATOR_LEN, + authenticator); + + *len = (size_t)p - (size_t)payload; + } + + return payload; +} + + +/** + * eap_wps_parse_encrypted_settings_snonce - Parse the encrypted settings + * + * TODO fail if any required element is missing (?) + */ +static int eap_wps_parse_encrypted_settings_snonce(struct eap_wps_data *data, + const u8 *settings, size_t len) +{ + const u8 *pos = settings; + const u8 *end; + u16 tlv_type, tlv_len; + const u8 *addr[4]; + size_t vlen[4]; + u8 hash[SHA256_MAC_LEN]; + u8 *expect = NULL; + char *nonce_str = NULL; + + end = settings + len; + + do { + tlv_type = TLV_GET_TYPE(pos); + tlv_len = TLV_GET_LEN(pos); + pos = TLV_GET_VAL(pos); + + switch (tlv_type) { + case WPS_ELEM_R_SNONCE1: + addr[0] = pos; + addr[1] = data->PSK1; + expect = data->RHASH1; + nonce_str = "R-SNonce1"; + break; + case WPS_ELEM_R_SNONCE2: + addr[0] = pos; + addr[1] = data->PSK2; + expect = data->RHASH2; + nonce_str = "R-SNonce2"; + break; + case WPS_ELEM_KEY_WRAP_AUTHENTICATOR: + if (eap_wps_authenticator_check(data->authKey, + settings, len - TLV_LEN - WPS_AUTHENTICATOR_LEN, + NULL, 0, pos)) + { + wpa_printf(MSG_ERROR, + "EAP-WPS: Key wrap authenticator check fail " + "Encrypted Settings %s", nonce_str); + return -1; + } + break; + default: + wpa_printf(MSG_DEBUG, "EAP-WPS: unknown type (%#x)", tlv_type); + } + + pos += tlv_len; + } while (pos < end); + + vlen[0] = WPS_S_NONCE_LEN; + vlen[1] = SHA256_MAC_LEN/2; /* only use the first 128-bits */ + addr[2] = wpabuf_head(data->PKE); + vlen[2] = WPS_PK_LEN; + addr[3] = wpabuf_head(data->PKR); + vlen[3] = WPS_PK_LEN; + + hmac_sha256_vector(data->authKey, WPS_AUTHKEY_LEN, 4, addr, vlen, hash); + + return os_memcmp(expect, hash, SHA256_MAC_LEN); +} + + +/** + * eap_wps_parse_M4 - Parse the payload for a M4 message + * @data: Pointer to WPS data + * @payload: Message buffer to parse + * @len: Length of message buffer + * Returns: 0 if payload is a valid M4 message, -1 if not a valid M4 + * message, or -2 if the Authenticator check fails + */ +static int eap_wps_parse_M4(struct eap_wps_data *data, const u8 *payload, + size_t len) +{ + const u8 *pos = payload; + const u8 *end = pos + len; + u16 tlv_type, tlv_len; + u8 *settings; + size_t settings_len; + int ret = -1; + + if (WPS_OPCODE_WSC_MSG != *pos) { + wpa_printf(MSG_WARNING, "EAP-WPS: Message is not WSC_MSG (%#02x)", + *pos); + wpa_hexdump(MSG_WARNING, "message:", pos, len); + return WPS_PARSE_ERR; + } + + pos++; /* Opcode */ + pos++; /* Flags */ + + do { + tlv_type = TLV_GET_TYPE(pos); + tlv_len = TLV_GET_LEN(pos); + pos = TLV_GET_VAL(pos); + + switch (tlv_type) { + case WPS_ELEM_MESSAGE_TYPE: + if (WPS_MESSAGE_TYPE_M4 != pos[0]) { + wpa_printf(MSG_WARNING, "EAP-WPS: Expecting message M4 but got %#x", + pos[0]); + return WPS_PARSE_ERR; + } else { + ret = 0; + } + break; + case WPS_ELEM_R_HASH1: + os_memcpy(data->RHASH1, pos, SHA256_MAC_LEN); + break; + case WPS_ELEM_R_HASH2: + os_memcpy(data->RHASH2, pos, SHA256_MAC_LEN); + break; + case WPS_ELEM_ENCRYPTED_SETTINGS: + if (NULL != (settings = os_malloc(tlv_len))) { + /* first 128 bits are IV. remaining bits are the encrypted data */ + eap_wps_decrypt_settings(data->keyWrapKey, (u8 *)pos, + (u8 *)pos + WPS_IV_LEN, tlv_len - WPS_IV_LEN, + settings, &settings_len); + + ret = eap_wps_parse_encrypted_settings_snonce(data, settings, + settings_len); + os_free(settings); + settings = NULL; + settings_len = 0; + if (ret) { + wpa_printf(MSG_ERROR, "EAP-WPS: Incorrect encrypted settings M4"); + return WPS_PARSE_ERR; + } + } + break; + case WPS_ELEM_AUTHENTICATOR: + if (eap_wps_authenticator_check(data->authKey, + data->prv_msg + 2, data->prv_msg_len - 2, + payload + 2, len - 2, pos)) + { + wpa_printf(MSG_ERROR, "EAP-WPS: M4 authenticator check fail"); + return WPS_AUTHENTICATOR_ERR; + } + break; + } + + pos += tlv_len; + } while (pos < end); + + return ret; +} + +/** + * eap_wps_msg_M5 - Create the payload for a M5 message + */ +static u8 *eap_wps_msg_M5(struct eap_wps_data *data, const u8 *M4, size_t M4_len, size_t *len) +{ + u8 *payload, *p; + u8 encSetting[WPS_ENCRYPTED_SETTINGS_LEN]; + u8 authenticator[WPS_AUTHENTICATOR_LEN]; + + *len = 0; + + if (NULL != (p = payload = os_malloc(WPS_MSG_BUF_LEN))) { + *p++ = WPS_OPCODE_WSC_MSG; + *p++ = 0; /* Flags */ + + p = tlv_write_u8(p, WPS_ELEM_VERSION, WPS_VERSION_1_0); + + p = tlv_write_u8(p, WPS_ELEM_MESSAGE_TYPE, WPS_MESSAGE_TYPE_M5); + + p = tlv_write(p, WPS_ELEM_REGISTRAR_NONCE, WPS_NONCE_LEN, + data->N2); + + eap_wps_encrypted_settings(data->authKey, data->keyWrapKey, WPS_ELEM_E_SNONCE1, + data->ES1, WPS_S_NONCE_LEN, encSetting); + p = tlv_write(p, WPS_ELEM_ENCRYPTED_SETTINGS, sizeof(encSetting), + encSetting); + + *len = (size_t)p - (size_t)payload; + + /* + * Message Authenticator is a keyed hash (HMAC-SHA_256) of (M4 | M5*) + * using the AuthKey. + * Note that M4 and M5 do not include the Opcode or Flags (thus +/- 2) + */ + eap_wps_authenticator(data->authKey, M4 + 2, M4_len - 2, payload + 2, *len - 2, + authenticator); + p = tlv_write(p, WPS_ELEM_AUTHENTICATOR, WPS_AUTHENTICATOR_LEN, + authenticator); + + *len = (size_t)p - (size_t)payload; + } + + return payload; +} + +/** + * eap_wps_parse_M6 - Parse the payload for a M6 message + * @data: Pointer to WPS data + * @payload: Message buffer to parse + * @len: Length of message buffer + * Returns: 0 if payload is a valid M6 message, -1 if not a valid M6 + * message, or -2 if the Authenticator check fails + */ +static int eap_wps_parse_M6(struct eap_wps_data *data, const u8 *payload, + size_t len) +{ + const u8 *pos = payload; + const u8 *end = pos + len; + u16 tlv_type, tlv_len; + u8 *settings; + size_t settings_len; + int ret = -1; + + if (WPS_OPCODE_WSC_MSG != *pos) { + wpa_printf(MSG_WARNING, "EAP-WPS: Message is not WSC_MSG (%#02x)", + *pos); + wpa_hexdump(MSG_WARNING, "message:", pos, len); + return WPS_PARSE_ERR; + } + + pos++; /* Opcode */ + pos++; /* Flags */ + + do { + tlv_type = TLV_GET_TYPE(pos); + tlv_len = TLV_GET_LEN(pos); + pos = TLV_GET_VAL(pos); + + switch (tlv_type) { + case WPS_ELEM_MESSAGE_TYPE: + if (WPS_MESSAGE_TYPE_M6 != pos[0]) { + wpa_printf(MSG_WARNING, "EAP-WPS: Expecting message M6 but got %#x", + pos[0]); + return WPS_PARSE_ERR; + } else { + ret = 0; + } + break; + case WPS_ELEM_ENCRYPTED_SETTINGS: + if (NULL != (settings = os_malloc(tlv_len))) { + /* first 128 bits are IV. remaining bits are the encrypted data */ + eap_wps_decrypt_settings(data->keyWrapKey, (u8 *)pos, + (u8 *)pos + WPS_IV_LEN, tlv_len - WPS_IV_LEN, + settings, &settings_len); + + ret = eap_wps_parse_encrypted_settings_snonce(data, settings, + settings_len); + os_free(settings); + settings = NULL; + settings_len = 0; + if (ret) { + wpa_printf(MSG_ERROR, "EAP-WPS: Incorrect encrypted settings M6"); + return WPS_PARSE_ERR; + } + } + break; + case WPS_ELEM_AUTHENTICATOR: + if (eap_wps_authenticator_check(data->authKey, + data->prv_msg + 2, data->prv_msg_len - 2, + payload + 2, len - 2, pos)) + { + wpa_printf(MSG_ERROR, "EAP-WPS: M6 authenticator check fail"); + return WPS_AUTHENTICATOR_ERR; + } + break; + } + + pos += tlv_len; + } while (pos < end); + + return ret; +} + +/** + * eap_wps_msg_M7 - Create the payload for a M7 message + */ +static u8 *eap_wps_msg_M7(struct eap_wps_data *data, const u8 *M6, size_t M6_len, size_t *len) +{ + u8 *payload, *p; + u8 encSetting[WPS_ENCRYPTED_SETTINGS_LEN]; + u8 authenticator[WPS_AUTHENTICATOR_LEN]; + + *len = 0; + + if (NULL != (p = payload = os_malloc(WPS_MSG_BUF_LEN))) { + *p++ = WPS_OPCODE_WSC_MSG; + *p++ = 0; /* Flags */ + + p = tlv_write_u8(p, WPS_ELEM_VERSION, WPS_VERSION_1_0); + + p = tlv_write_u8(p, WPS_ELEM_MESSAGE_TYPE, WPS_MESSAGE_TYPE_M7); + + p = tlv_write(p, WPS_ELEM_REGISTRAR_NONCE, WPS_NONCE_LEN, + data->N2); + + eap_wps_encrypted_settings(data->authKey, data->keyWrapKey, WPS_ELEM_E_SNONCE2, + data->ES2, WPS_S_NONCE_LEN, encSetting); + p = tlv_write(p, WPS_ELEM_ENCRYPTED_SETTINGS, sizeof(encSetting), + encSetting); + + *len = (size_t)p - (size_t)payload; + + /* + * Message Authenticator is a keyed hash (HMAC-SHA_256) of (M6 | M7*) + * using the AuthKey. + * Note that M6 and M7 do not include the Opcode or Flags (thus +/- 2) + */ + eap_wps_authenticator(data->authKey, M6 + 2, M6_len - 2, payload + 2, *len - 2, + authenticator); + p = tlv_write(p, WPS_ELEM_AUTHENTICATOR, WPS_AUTHENTICATOR_LEN, + authenticator); + + *len = (size_t)p - (size_t)payload; + } + + return payload; +} + + +/** + * eap_wps_parse_credential - Parse the Credentials element + */ +static struct wps_credential * eap_wps_parse_credential(struct eap_wps_data *data, + const u8 *elem, size_t len) +{ + struct wps_credential *credential = NULL, *c = NULL; + void *tmp = NULL; + const u8 *pos = elem; + const u8 *end; + u16 tlv_type, tlv_len; + + end = elem + len; + + do { + tlv_type = TLV_GET_TYPE(pos); + tlv_len = TLV_GET_LEN(pos); + pos = TLV_GET_VAL(pos); + + switch (tlv_type) { + case WPS_ELEM_NETWORK_INDEX: + if (NULL == (c = os_zalloc(sizeof(struct wps_credential)))) { + wpa_printf(MSG_ERROR, "EAP-WPS: Cannot allocate credential"); + return credential; + } + break; + case WPS_ELEM_SSID: + tmp = c->ssid; + c->ssid = os_realloc(c->ssid, tlv_len); + if (NULL != c->ssid) { + os_memcpy(c->ssid, pos, tlv_len); + c->ssid_len = tlv_len; + } else { + os_free(tmp); + c->ssid_len = 0; + } + wpa_printf(MSG_WARNING, "EAP-WPS: Credential SSID %s", + wpa_ssid_txt((u8 *)pos, tlv_len)); + break; + case WPS_ELEM_AUTHENTICATION_TYPE: + c->auth_type = WPA_GET_BE16(pos); + wpa_printf(MSG_DEBUG, "EAP-WPS: auth_type=0x%04x", c->auth_type); + break; + case WPS_ELEM_ENCRYPTION_TYPE: + c->encryption_type = WPA_GET_BE16(pos); + wpa_printf(MSG_DEBUG, "EAP-WPS: encryption_type=0x%04x", c->encryption_type); + break; + case WPS_ELEM_NETWORK_KEY: + tmp = c->network_key; + if (NULL == (c->network_key = os_realloc(c->network_key, + tlv_len))) { + os_free(tmp); + eap_wps_credential_free(c); + wpa_printf(MSG_ERROR, "EAP-WPS: Cannot allocate network key"); + return NULL; + } + os_memcpy(c->network_key, pos, tlv_len); + c->network_key_len = tlv_len; + break; + case WPS_ELEM_MAC_ADDRESS: + os_memcpy(c->bssid, pos, ETH_ALEN); + + /* Add new credential to the linked list of credentials */ + c->next = credential; + credential = c; + c = NULL; + break; + default: + wpa_printf(MSG_DEBUG, "EAP-WPS: unknown type %#x (credential)", + tlv_type); + } + + pos += tlv_len; + } while (pos < end); + + return credential; +} + +/** + * eap_wps_parse_encrypted_settings_sta - Parse the encrypted settings + * + * TODO fail if any required element is missing (?) + */ +static struct wps_credential * eap_wps_parse_encrypted_settings_sta(struct eap_wps_data *data, + const u8 *settings, size_t len) +{ + struct wps_credential *credential = NULL, *c = NULL, *last = NULL; + const u8 *pos = settings; + const u8 *end; + u16 tlv_type, tlv_len; + + end = settings + len; + + do { + tlv_type = TLV_GET_TYPE(pos); + tlv_len = TLV_GET_LEN(pos); + pos = TLV_GET_VAL(pos); + + switch (tlv_type) { + case WPS_ELEM_CREDENTIAL: + c = eap_wps_parse_credential(data, pos, tlv_len); + if (c) { + for (last = c; last->next; last = last->next) + ; + last->next = credential; + credential = c; + c = NULL; + } + break; + case WPS_ELEM_KEY_WRAP_AUTHENTICATOR: + if (eap_wps_authenticator_check(data->authKey, + settings, len - TLV_LEN - WPS_AUTHENTICATOR_LEN, + NULL, 0, pos)) + { + wpa_printf(MSG_ERROR, + "EAP-WPS: Key wrap authenticator check fail " + "Encrypted Settings STA"); + eap_wps_credential_free_all(credential); + return NULL; + } + + /* Setting pos exits while loop to avoid PKCS#5 v2.0 padding */ + pos = end; + break; + default: + wpa_printf(MSG_DEBUG, "EAP-WPS: unknown type (%#x) STA", tlv_type); + } + + pos += tlv_len; + } while (pos < end); + + return credential; +} + +/** + * eap_wps_parse_M8 - Parse the payload for a M8 message + * @data: Pointer to WPS data + * @payload: Message buffer to parse + * @len: Length of message buffer + * Returns: 0 if payload is a valid M8 message, -1 if not a valid M8 + * message, or -2 if the Authenticator check fails + */ +static int eap_wps_parse_M8(struct eap_wps_data *data, const u8 *payload, + size_t len) +{ + struct wps_credential *credential = NULL; + const u8 *pos = payload; + const u8 *end = pos + len; + u16 tlv_type, tlv_len; + u8 *settings; + size_t settings_len; + int ret = -1; + + if (WPS_OPCODE_WSC_MSG != *pos) { + wpa_printf(MSG_WARNING, "EAP-WPS: Message is not WSC_MSG (%#02x)", + *pos); + wpa_hexdump(MSG_WARNING, "message:", pos, len); + return WPS_PARSE_ERR; + } + + pos++; /* Opcode */ + pos++; /* Flags */ + + do { + tlv_type = TLV_GET_TYPE(pos); + tlv_len = TLV_GET_LEN(pos); + pos = TLV_GET_VAL(pos); + + switch (tlv_type) { + case WPS_ELEM_MESSAGE_TYPE: + if (WPS_MESSAGE_TYPE_M8 != pos[0]) { + wpa_printf(MSG_WARNING, "EAP-WPS: Expecting message M8 but got %#x", + pos[0]); + return WPS_PARSE_ERR; + } else { + ret = 0; + } + break; + case WPS_ELEM_ENCRYPTED_SETTINGS: + if (NULL != (settings = os_malloc(tlv_len))) { + /* first 128 bits are IV. remaining bits are the encrypted data */ + eap_wps_decrypt_settings(data->keyWrapKey, (u8 *)pos, + (u8 *)pos + WPS_IV_LEN, tlv_len - WPS_IV_LEN, + settings, &settings_len); + + credential = eap_wps_parse_encrypted_settings_sta(data, settings, + settings_len); + os_free(settings); + settings = NULL; + settings_len = 0; + if (NULL == credential) { + wpa_printf(MSG_ERROR, "EAP-WPS: Incorrect encrypted settings M8"); + return WPS_PARSE_ERR; + } + } + break; + case WPS_ELEM_AUTHENTICATOR: + if (eap_wps_authenticator_check(data->authKey, + data->prv_msg + 2, data->prv_msg_len - 2, + payload + 2, len - 2, pos)) + { + wpa_printf(MSG_ERROR, "EAP-WPS: M8 authenticator check fail"); + eap_wps_credential_free_all(credential); + return WPS_AUTHENTICATOR_ERR; + } + + if (credential) { + data->credential = credential; + } + + break; + } + + pos += tlv_len; + } while (pos < end); + + return ret; +} + +/** + * eap_wps_msg_Done - Create the payload for a WSC_Done message + */ +static u8 * eap_wps_msg_Done(struct eap_wps_data *data, size_t *len) +{ + u8 *payload = NULL, *p; + + *len = 0; + + if (NULL != (p = payload = os_malloc(WPS_MSG_BUF_LEN))) { + *p++ = WPS_OPCODE_WSC_Done; + *p++ = 0; /* Flags */ + + p = tlv_write_u8(p, WPS_ELEM_VERSION, WPS_VERSION_1_0); + + p = tlv_write_u8(p, WPS_ELEM_MESSAGE_TYPE, WPS_MESSAGE_TYPE_WSC_Done); + + p = tlv_write(p, WPS_ELEM_ENROLLEE_NONCE, WPS_NONCE_LEN, + data->N1); + + p = tlv_write(p, WPS_ELEM_REGISTRAR_NONCE, WPS_NONCE_LEN, + data->N2); + + *len = (size_t)p - (size_t)payload; + } + + return payload; +} + + +/** + * eap_wps_data_free - free data in the WPS data structure + */ +static void eap_wps_data_free(struct eap_wps_data *data) +{ + if (NULL == data) + return; + + wpabuf_free(data->SKE); + wpabuf_free(data->PKE); + wpabuf_free(data->PKR); + + os_free(data->prv_msg); + + eap_wps_credential_free_all(data->credential); +} + + +static void * eap_wps_init(struct eap_sm *sm) +{ + struct eap_wps_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + data->state = WPS_START; + + return data; +} + + +static void eap_wps_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_wps_data *data = priv; + + eap_wps_data_free(data); + os_free(data); +} + + +static struct wpabuf * eap_wps_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_wps_data *data = priv; + struct eap_peer_config *config = NULL; + const struct wpa_config_blob *blob = NULL; + const u8 *pos; + size_t len; + u8 *msg = NULL; + size_t msg_len; + struct wpabuf *resp = NULL; + u8 *auth = NULL; + int err = 0; + + pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_TYPE_SIMPLE_CONFIG, + reqData, &len); + + if (!sm->rxFailure && ((NULL == pos) || (0 == len))) { + wpa_printf(MSG_WARNING, "EAP-WPS: Invalid frame (pos=%p len=%lu)", + pos, (unsigned long) len); + ret->ignore = TRUE; + return NULL; + } + + if (NULL == (config = eap_get_config(sm))) { + wpa_printf(MSG_WARNING, "EAP-WPS: Cannot get configuration"); + ret->ignore = TRUE; + return NULL; + } + + if (WPS_OPCODE_WSC_NACK == *pos) { + ret->ignore = FALSE; + ret->decision = DECISION_FAIL; + ret->methodState = METHOD_DONE; + + if (NULL == (msg = eap_wps_nack(data->N1, data->N2, &msg_len, + WPS_CONFIGURATION_ERROR_NO_ERROR))) { + wpa_printf(MSG_WARNING, "EAP-WPS: Cannot create WSC_NACK"); + ret->ignore = TRUE; + } + + data->state = WPS_START; + } else switch (data->state) { + case WPS_START: + /* Clear all state including keys and nonces */ + config->assoc_state = WPS_ASSOCIATION_STATE_CONNECTION_FAILURE; + eap_wps_data_free(data); + os_memset(data, 0, sizeof(struct eap_wps_data)); + + data->state = WPS_START; + + if (WPS_OPCODE_WSC_Start != *pos) { + wpa_printf(MSG_WARNING, "EAP-WPS: Message is not WSC_Start (%#02x)", + *pos); + ret->ignore = TRUE; + break; + } + + wpa_printf(MSG_DEBUG, "EAP-WPS: Received WSC_Start"); + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + + blob = eap_get_config_blob(sm, WPS_BLOB_NAME_REQUIRE); + if (NULL == blob) { + wpa_printf(MSG_ERROR, "%s: Named configuration blob '%s' not " + "found", __func__, WPS_BLOB_NAME_REQUIRE); + break; + } + + if (eap_wps_get_req(data, blob)) { + wpa_printf(MSG_ERROR, "EAP-WPS: error with requirement blob"); + break; + } + + if (os_get_random(data->N1, WPS_NONCE_LEN)) { + wpa_printf(MSG_ERROR, "EAP-WPS: Failed to get random data for N1"); + break; + } + wpa_hexdump(MSG_DEBUG, "EAP-WPS: N1 (Enrollee nonce)", + data->N1, WPS_NONCE_LEN); + + if (NULL == (msg = eap_wps_msg_M1(data, &msg_len))) { + wpa_printf(MSG_WARNING, "EAP-WPS: Cannot create message M1"); + ret->ignore = TRUE; + break; + } + + data->state = WPS_M2; + break; + case WPS_M2: + /* Validate that this is a M2 message */ + err = eap_wps_parse_M2(sm, data, pos, len, &auth); + if (WPS_PARSE_ERR == err) { + wpa_printf(MSG_WARNING, "EAP-WPS: Message is not M2"); + ret->ignore = TRUE; + break; + } else if (WPS_M2D_ERR == err) { + if (NULL == (msg = eap_wps_ack(data->N1, data->N2, &msg_len))) { + wpa_printf(MSG_WARNING, "EAP-WPS: Cannot create WSC_ACK"); + } + ret->ignore = FALSE; + ret->decision = DECISION_FAIL; + ret->methodState = METHOD_DONE; + break; + } + + wpa_printf(MSG_DEBUG, "EAP-WPS: Received M2"); + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + + /* build message M3 */ + if (NULL == (msg = eap_wps_msg_M3(data, pos, len, data->own_addr, + data->pin, &msg_len))) { + wpa_printf(MSG_WARNING, "EAP-WPS: Cannot create message M3"); + ret->ignore = TRUE; + break; + } + + /* + * Normally you would want to verify the authenticator in the + * parse routine above, but the AuthKey can't be calculated + * until after parsing the incoming message for N2 + */ + if (eap_wps_authenticator_check(data->authKey, + data->prv_msg + 2, data->prv_msg_len - 2, + pos + 2, len - 2, auth)) + { + wpa_printf(MSG_ERROR, "EAP-WPS: M2 authenticator check fail"); + os_free(msg); + msg = NULL; + msg_len = 0; + ret->ignore = TRUE; + break; + } + + data->state = WPS_M4; + break; + case WPS_M4: + err = eap_wps_parse_M4(data, pos, len); + if (WPS_PARSE_ERR == err) { + wpa_printf(MSG_WARNING, "EAP-WPS: Message is not M4"); + ret->ignore = FALSE; + ret->decision = DECISION_FAIL; + ret->methodState = METHOD_DONE; + + if (NULL == (msg = eap_wps_nack(data->N1, data->N2, &msg_len, + WPS_CONFIGURATION_ERROR_NO_ERROR))) { + wpa_printf(MSG_WARNING, "EAP-WPS: Cannot create WSC_NACK"); + ret->ignore = TRUE; + } + + data->state = WPS_START; + break; + } else if (WPS_AUTHENTICATOR_ERR == err) { + wpa_printf(MSG_WARNING, "EAP-WPS: M4 authenticator invalid"); + ret->ignore = TRUE; + ret->methodState = METHOD_MAY_CONT; + break; + } + + wpa_printf(MSG_DEBUG, "EAP-WPS: Received M4"); + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + + /* build message M5 */ + if (NULL == (msg = eap_wps_msg_M5(data, pos, len, &msg_len))) { + wpa_printf(MSG_WARNING, "EAP-WPS: Cannot create message M5"); + ret->ignore = TRUE; + break; + } + + data->state = WPS_M6; + break; + case WPS_M6: + err = eap_wps_parse_M6(data, pos, len); + if (WPS_PARSE_ERR == err) { + wpa_printf(MSG_WARNING, "EAP-WPS: Message is not M6"); + ret->ignore = FALSE; + ret->decision = DECISION_FAIL; + ret->methodState = METHOD_DONE; + + if (NULL == (msg = eap_wps_nack(data->N1, data->N2, &msg_len, + WPS_CONFIGURATION_ERROR_NO_ERROR))) { + wpa_printf(MSG_WARNING, "EAP-WPS: Cannot create WSC_NACK"); + ret->ignore = TRUE; + } + + data->state = WPS_START; + break; + } else if (WPS_AUTHENTICATOR_ERR == err) { + wpa_printf(MSG_WARNING, "EAP-WPS: M6 authenticator invalid"); + ret->ignore = TRUE; + ret->methodState = METHOD_MAY_CONT; + break; + } + + wpa_printf(MSG_DEBUG, "EAP-WPS: Received M6"); + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + + /* build message M7 */ + if (NULL == (msg = eap_wps_msg_M7(data, pos, len, &msg_len))) { + wpa_printf(MSG_WARNING, "EAP-WPS: Cannot create message M7"); + ret->ignore = TRUE; + break; + } + + data->state = WPS_M8; + break; + case WPS_M8: + err = eap_wps_parse_M8(data, pos, len); + if (WPS_PARSE_ERR == err) { + wpa_printf(MSG_WARNING, "EAP-WPS: Message is not M8"); + ret->ignore = FALSE; + ret->decision = DECISION_FAIL; + ret->methodState = METHOD_DONE; + + /* TODO can we do better than NO_ERROR */ + if (NULL == (msg = eap_wps_nack(data->N1, data->N2, &msg_len, + WPS_CONFIGURATION_ERROR_NO_ERROR))) { + wpa_printf(MSG_WARNING, "EAP-WPS: Cannot create WSC_NACK"); + ret->ignore = TRUE; + } + + data->state = WPS_START; + break; + } else if (WPS_AUTHENTICATOR_ERR == err) { + wpa_printf(MSG_WARNING, "EAP-WPS: M8 authenticator invalid"); + ret->ignore = TRUE; + ret->methodState = METHOD_MAY_CONT; + break; + } + + wpa_printf(MSG_DEBUG, "EAP-WPS: Received M8"); + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + + /* build message WSC_Done */ + if (NULL == (msg = eap_wps_msg_Done(data, &msg_len))) { + wpa_printf(MSG_WARNING, "EAP-WPS: Cannot create message WSC_Done"); + ret->ignore = TRUE; + break; + } + + if (NULL != (blob = eap_wps_set_res(data))) { + eap_set_config_blob(sm, (struct wpa_config_blob *)blob); + } + + config->assoc_state = WPS_ASSOCIATION_STATE_CONNECTION_SUCCESS; + + data->state = WPS_START; + break; + default: + wpa_printf(MSG_WARNING, "EAP-WPS: Unhandled state %d", data->state); + ret->ignore = TRUE; + } + + if (msg) { + void *tmp; + + resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_TYPE_SIMPLE_CONFIG, + msg_len, EAP_CODE_RESPONSE, + eap_get_id(reqData)); + + if (resp) { + wpabuf_put_data(resp, msg, msg_len); + } + /* + * Save a copy of our current message to use in the validation + * of the next received message. + */ + tmp = data->prv_msg; + if (NULL != (data->prv_msg = os_realloc(data->prv_msg, msg_len))) { + os_memcpy(data->prv_msg, msg, msg_len); + data->prv_msg_len = msg_len; + } else { + data->prv_msg_len = 0; + os_free(tmp); + } + + os_free(msg); + } + + return resp; +} + + +int eap_peer_wps_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_WFA, EAP_TYPE_SIMPLE_CONFIG, "WPS"); + if (eap == NULL) + return -1; + + eap->init = eap_wps_init; + eap->deinit = eap_wps_deinit; + eap->process = eap_wps_process; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + + return ret; +} diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c index ea65d27..05ed6fd 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -400,6 +400,11 @@ SM_STEP(SUPP_PAE) if (sm->ctx->eapol_done_cb) sm->ctx->eapol_done_cb(sm->ctx->ctx); } + if (sm->eapFail && sm->conf.expect_fail) { + if (sm->ctx->eapol_done_cb) + sm->ctx->eapol_done_cb(sm->ctx->ctx); + SM_ENTER(SUPP_PAE, HELD); + } if (sm->eapSuccess && sm->portValid) SM_ENTER(SUPP_PAE, AUTHENTICATED); else if (sm->eapFail || (sm->keyDone && !sm->portValid)) @@ -1349,6 +1354,7 @@ void eapol_sm_notify_config(struct eapol_sm *sm, sm->conf.accept_802_1x_keys = conf->accept_802_1x_keys; sm->conf.required_keys = conf->required_keys; sm->conf.fast_reauth = conf->fast_reauth; + sm->conf.expect_fail = conf->expect_fail; if (sm->eap) { eap_set_fast_reauth(sm->eap, conf->fast_reauth); eap_set_workaround(sm->eap, conf->workaround); @@ -1706,6 +1712,7 @@ static void eapol_sm_set_config_blob(void *ctx, struct wpa_config_blob *blob) { #ifndef CONFIG_NO_CONFIG_BLOBS struct eapol_sm *sm = ctx; + if (sm && sm->ctx && sm->ctx->set_config_blob) sm->ctx->set_config_blob(sm->ctx->ctx, blob); #endif /* CONFIG_NO_CONFIG_BLOBS */ diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h index 719fbd3..21d49da 100644 --- a/src/eapol_supp/eapol_supp_sm.h +++ b/src/eapol_supp/eapol_supp_sm.h @@ -59,6 +59,13 @@ struct eapol_config { * eap_disabled - Whether EAP is disabled */ int eap_disabled; + + /** + * expect_fail - Whether EAP-Failure is actually an indication of success + * + * This is only used for WPS + */ + int expect_fail; }; struct eapol_sm; diff --git a/src/rsn_supp/wps.c b/src/rsn_supp/wps.c new file mode 100644 index 0000000..14a4f5c --- /dev/null +++ b/src/rsn_supp/wps.c @@ -0,0 +1,369 @@ +/* + * WiFi Protected Setup - WPS specific processing + * Copyright (c) 2008, Chuck Tuffli + * + * 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. + */ + +#include "includes.h" + +#include "common.h" +#include "tlv.h" +#include "wps.h" +#include "eloop.h" +#include "wps_i.h" +#include "ieee802_11_defs.h" +#include "uuid.h" + +static const int WPS_SELECTOR_LEN = 4; +static const u8 WPS_OUI_TYPE[] = { 0x00, 0x50, 0xf2, 0x4 }; +static const u16 WPS_VERSION = 0x10; + +/* WPS IE version 1.0 + * + * Required fields for all IE + * 00-50-f2:04 (OUI:OUI type) + * 0x10 (version) + * 0x01 0x02 (wiFi Protected Setup State; unconfigured, configured) + * (following fields are optional:) + * (AP Setup Locked; BOOL ) + * (Selected Registrar; BOOL) + * (Device Password ID; ) + * (Selected Registrar Config Methods; ) + * (following fields are optional in some management packets) + * (Response Type; required=Probe Resp) + * (UUID-R; optional=Beacon, required=Probe Resp) + * (Manufacturer; required=Probe Resp) + * (Model Name; required=Probe Resp) + * (Model Number; required=Probe Resp) + * (Serial Number; required=Probe Resp) + * (Primary Device Type; required=Probe Resp) + * (Device Name; required=Probe Resp) + * (Config Methodes; required=Probe Resp) + * (RF Bands; optional=Beacon and Probe Resp) + */ + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct wps_ie_hdr { + u8 elem_id; + u8 len; + u8 oui[3]; + u8 oui_type; + u16 type_version; + u16 len_version; + u8 version; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +int wpa_parse_wps_ie(const u8 *wps_ie, size_t wps_ie_len, + struct wps_ie_data *data) +{ + const struct wps_ie_hdr *hdr; + const u8 *pos; + int left; + u16 elem, len; + + data->proto = WPA_PROTO_WPS; + + if (0 == wps_ie_len) { + /* No WPS IE - fail silently */ + return -1; + } + + if (wps_ie_len < sizeof(struct wps_ie_hdr)) { + wpa_printf(MSG_DEBUG, "%s: ie len too short %lu", + __func__, (unsigned long) wps_ie_len); + return -1; + } + + hdr = (const struct wps_ie_hdr *) wps_ie; + + if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC || + os_memcmp(hdr->oui, WPS_OUI_TYPE, WPS_SELECTOR_LEN) != 0 || + hdr->version != WPS_VERSION) { + wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", + __func__); + return -1; + } + + pos = (const u8 *) (hdr + 1); + left = wps_ie_len - sizeof(*hdr); + + while (left >= 2) { + elem = WPA_GET_BE16(pos); + len = WPA_GET_BE16(pos+2); + pos += 4; + left -= 4; + + if ((0 == len) || (len > left)) { + wpa_printf(MSG_DEBUG, "%s: bad length %d (left %d)", + __func__, len, left); + return -1; + } + switch (elem) { + case WPS_ELEM_DEVICE_PASSWORD_ID: + if (data->selected_registrar) + data->device_password_id = WPA_GET_BE16(pos); + break; + case WPS_ELEM_CONFIG_METHODS: + data->config_method = WPA_GET_BE16(pos); + break; + case WPS_ELEM_SELECTED_REGISTRAR: + data->selected_registrar = *pos; + break; + case WPS_ELEM_UUID_E: + os_memcpy(data->uuid, pos, sizeof(data->uuid)); + break; + } + pos += len; + left -= len; + } + return 0; +} + + +int wpa_gen_wps_ie(struct wps_sm *sm, char *uuid, u8 *wps_ie, size_t *wps_ie_len) +{ + u8 *pos = wps_ie; + u8 uuid_e[WPS_UUID_LEN]; + + *wps_ie_len = 0; + + if (WPS_CONFIG_METHOD_NONE == sm->config_method) + return 0; + + if (uuid) { + if (uuid_str2bin(uuid, uuid_e)) { + wpa_printf(MSG_ERROR, "EAP-WPS: bad UUID format %s", uuid); + return -1; + } + } + + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + pos++; /* skip length initially */ + os_memcpy(pos, WPS_OUI_TYPE, WPS_SELECTOR_LEN); + pos += WPS_SELECTOR_LEN; + pos = tlv_write_u8(pos, WPS_ELEM_VERSION, WPS_VERSION); + pos = tlv_write_u8(pos, WPS_ELEM_REQUEST_TYPE, + WPS_REQUEST_TYPE_ENROLLEE_OPEN); + pos = tlv_write_u16(pos, WPS_ELEM_CONFIG_METHODS, + WPS_CONFIG_METHOD_LABEL | + WPS_CONFIG_METHOD_DISPLAY | + WPS_CONFIG_METHOD_PBC); + pos = tlv_write(pos, WPS_ELEM_UUID_E, WPS_UUID_LEN, uuid_e); + pos = tlv_write(pos, WPS_ELEM_PRIMARY_DEVICE_TYPE, 8, + (u8 *)"\x00\x01\x00\x50\xf2\x04\x00\x01"); + pos = tlv_write_u8(pos, WPS_ELEM_RF_BANDS, + WPS_RF_BAND_2_4GHZ | WPS_RF_BAND_5_0GHZ); + pos = tlv_write_u16(pos, WPS_ELEM_ASSOCIATION_STATE, + WPS_ASSOCIATION_STATE_NOT_ASSOCIATED); + pos = tlv_write_u16(pos, WPS_ELEM_CONFIGURATION_ERROR, + WPS_CONFIGURATION_ERROR_NO_ERROR); + pos = tlv_write_u16(pos, WPS_ELEM_DEVICE_PASSWORD_ID, + 0 == os_strncmp(sm->wps_pin, WPS_PIN_PBC, sizeof(WPS_PIN_PBC)) ? + WPS_DEVICE_PASSWORD_ID_PBC : WPS_DEVICE_PASSWORD_ID_PIN); + + *wps_ie_len = (size_t)pos - (size_t)wps_ie; + + wps_ie[1] = (u8)(*wps_ie_len) - 2; + + return 0; +} + + +static void wps_walktime_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wps_sm *sm = eloop_ctx; + + wpa_msg(sm->ctx, MSG_DEBUG, "WPS method timed out"); + sm->config_method = 0; +} + +static void wps_config_common(struct wps_sm *sm, int method) +{ + sm->config_method = method; + + eloop_cancel_timeout(wps_walktime_timeout, sm, NULL); + eloop_register_timeout(WPS_WALK_TIME, 0, wps_walktime_timeout, sm, NULL); +} + + +int wps_config_pbc(struct wps_sm *sm) +{ + if (NULL == sm) + return -1; + + os_free(sm->wps_pin); + sm->wps_pin = os_strdup(WPS_PIN_PBC); + + wps_config_common(sm, WPS_CONFIG_METHOD_PBC); + + return 0; +} + + +int wps_cancel_walktime_timeout(struct wps_sm *sm) +{ + eloop_cancel_timeout(wps_walktime_timeout, sm, NULL); + sm->config_method = 0; + + return 0; +} + + +/** + * wps_generate_pin - Generate a Device Password (aka PIN) + * + * Creates an 8 digit pin by generating a 7 digit random number + * and appending a checksum digit to the end of the random number + * (WPS Section 6.4.1) + */ +static u32 wps_generate_pin() +{ + u32 rand; + u32 pin; + u32 accum = 0; + +#define PIN_MIN 1 +#define PIN_MAX 9999999 + + os_get_random((unsigned char *)&rand, sizeof(rand)); + pin = PIN_MIN + (rand / ((u32)(-1) / PIN_MAX + 1)); + + pin *= 10; + accum += 3 * ((pin / 10000000) % 10); + accum += 1 * ((pin / 1000000) % 10); + accum += 3 * ((pin / 100000) % 10); + accum += 1 * ((pin / 10000) % 10); + accum += 3 * ((pin / 1000) % 10); + accum += 1 * ((pin / 100) % 10); + accum += 3 * ((pin / 10) % 10); + + pin += (10 - (accum % 10)) % 10; + + return pin; +} + + +int wps_get_pin(struct wps_sm *sm, char *buf, size_t buflen) +{ + u32 pin; + + os_free(sm->wps_pin); + if (NULL == (sm->wps_pin = os_malloc(sizeof(WPS_PIN_PBC)))) { + wpa_msg(sm->ctx, MSG_ERROR, "WPS PIN method no memory"); + return -1; + } + + pin = wps_generate_pin(); + os_snprintf(sm->wps_pin, sizeof(WPS_PIN_PBC), "%08d", pin); + + os_snprintf(buf, buflen, "%08d", pin); + buf[buflen - 1] = '\0'; + + return os_strlen(buf); +} + + +int wps_config_pin(struct wps_sm *sm) +{ + if (NULL == sm) + return -1; + + wps_config_common(sm, WPS_CONFIG_METHOD_DISPLAY); + + return 0; +} + + +struct wps_sm * wps_sm_init(void *ctx) +{ + struct wps_sm *sm; + + sm = os_zalloc(sizeof(*sm)); + if (NULL == sm) + return NULL; + sm->ctx = ctx; + + return sm; +} + + +void wps_sm_deinit(struct wps_sm *sm) +{ + if (NULL == sm) + return; + eloop_cancel_timeout(wps_walktime_timeout, sm, NULL); + os_free(sm->wps_pin); + os_free(sm->uuid_e); + /* ctx is not ours to free */ + os_free(sm); +} + + +int wps_sm_set_param(struct wps_sm *sm, enum wps_sm_conf_params param, + unsigned int value) +{ + int ret = 0; + + if (NULL == sm) + return -1; + + switch (param) { + case WPS_PARAM_CONFIG_METHOD: + sm->config_method = value; + break; + case WPS_PARAM_PAIRWISE: + sm->pairwise_cipher = value; + break; + case WPS_PARAM_GROUP: + sm->group_cipher = value; + break; + case WPS_PARAM_KEY_MGMT: + sm->key_mgmt = value; + break; + default: + break; + } + + return ret; +} + + +int wps_sm_get_param(struct wps_sm *sm, enum wps_sm_conf_params param) +{ + int ret = 0; + + if (NULL != sm) + switch (param) { + case WPS_PARAM_CONFIG_METHOD: + ret = sm->config_method; + break; + case WPS_PARAM_PAIRWISE: + ret = sm->pairwise_cipher; + break; + case WPS_PARAM_GROUP: + ret = sm->group_cipher; + break; + case WPS_PARAM_KEY_MGMT: + ret = sm->key_mgmt; + break; + default: + break; + } + + return ret; +} diff --git a/src/rsn_supp/wps.h b/src/rsn_supp/wps.h new file mode 100644 index 0000000..502448a --- /dev/null +++ b/src/rsn_supp/wps.h @@ -0,0 +1,392 @@ +/* + * WiFi Protected Setup - WPS definitions + * Copyright (c) 2008, Chuck Tuffli + * + * 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. + */ + +#ifndef WPS_H +#define WPS_H + +#include "defs.h" + +#ifndef BIT +#define BIT(n) (1 << (n)) +#endif + +#define WPS_UUID_LEN 16 + +struct wps_ie_data { + struct wpa_scan_res *bss; + int proto; + int device_password_id; + int config_method; + Boolean selected_registrar; + u8 uuid[WPS_UUID_LEN]; +}; + +enum wps_sm_conf_params { + WPS_PARAM_CONFIG_METHOD, + WPS_PARAM_PAIRWISE, + WPS_PARAM_GROUP, + WPS_PARAM_KEY_MGMT +}; + +/** + * WPS Op-Codes + */ +#define WPS_OPCODE_WSC_Start 0x01 +#define WPS_OPCODE_WSC_ACK 0x02 +#define WPS_OPCODE_WSC_NACK 0x03 +#define WPS_OPCODE_WSC_MSG 0x04 +#define WPS_OPCODE_WSC_Done 0x05 +#define WPS_OPCODE_WSC_FRAG_ACK 0x06 + +/** + * WPS EAP Flags + * MF = more fragments + * LF = length field + */ +#define WPS_EAP_FLAG_MF 0x01 +#define WPS_EAP_FLAG_LF 0x02 + +#define WPS_ELEM_AP_CHANNEL 0x1001 +#define WPS_ELEM_ASSOCIATION_STATE 0x1002 +#define WPS_ELEM_AUTHENTICATION_TYPE 0x1003 +#define WPS_ELEM_AUTHENTICATION_TYPE_FLAG 0x1004 +#define WPS_ELEM_AUTHENTICATOR 0x1005 +#define WPS_ELEM_CONFIG_METHODS 0x1008 +#define WPS_ELEM_CONFIGURATION_ERROR 0x1009 +#define WPS_ELEM_CONFIRMATION_URL4 0x100a +#define WPS_ELEM_CONFIRMATION_URL6 0x100b +#define WPS_ELEM_CONNECTION_TYPE 0x100c +#define WPS_ELEM_CONNECTION_TYPE_FLAG 0x100d +#define WPS_ELEM_CREDENTIAL 0x100e +#define WPS_ELEM_DEVICE_NAME 0x1011 +#define WPS_ELEM_DEVICE_PASSWORD_ID 0x1012 +#define WPS_ELEM_E_HASH1 0x1014 +#define WPS_ELEM_E_HASH2 0x1015 +#define WPS_ELEM_E_SNONCE1 0x1016 +#define WPS_ELEM_E_SNONCE2 0x1017 +#define WPS_ELEM_ENCRYPTED_SETTINGS 0x1018 +#define WPS_ELEM_ENCRYPTION_TYPE 0x100f +#define WPS_ELEM_ENCRYPTION_TYPE_FLAG 0x1010 +#define WPS_ELEM_ENROLLEE_NONCE 0x101a +#define WPS_ELEM_FEATURE_ID 0x101b +#define WPS_ELEM_IDENTITY 0x101c +#define WPS_ELEM_IDENTITY_PROOF 0x101d +#define WPS_ELEM_KEY_WRAP_AUTHENTICATOR 0x101e +#define WPS_ELEM_KEY_IDENTIFIER 0x101f +#define WPS_ELEM_MAC_ADDRESS 0x1020 +#define WPS_ELEM_MANUFACTURER 0x1021 +#define WPS_ELEM_MESSAGE_TYPE 0x1022 +#define WPS_ELEM_MODEL_NAME 0x1023 +#define WPS_ELEM_MODEL_NUMBER 0x1024 +#define WPS_ELEM_NETWORK_INDEX 0x1026 +#define WPS_ELEM_NETWORK_KEY 0x1027 +#define WPS_ELEM_NETWORK_KEY_INDEX 0x1028 +#define WPS_ELEM_NEW_DEVICE_NAME 0x1029 +#define WPS_ELEM_NEW_PASSWORD 0x102a +#define WPS_ELEM_OOB_DEVICE_PASSWORD 0x102c +#define WPS_ELEM_OS_VERSION 0x102d +#define WPS_ELEM_POWER_LEVEL 0x102f +#define WPS_ELEM_PSK_CURRENT 0x1030 +#define WPS_ELEM_PSK_MAX 0x1031 +#define WPS_ELEM_PUBLIC_KEY 0x1032 +#define WPS_ELEM_RADIO_ENABLED 0x1033 +#define WPS_ELEM_REBOOT 0x1034 +#define WPS_ELEM_REGISTRAR_CURRENT 0x1035 +#define WPS_ELEM_REGISTRAR_ESTABLISHED 0x1036 +#define WPS_ELEM_REGISTRAR_LIST 0x1037 +#define WPS_ELEM_REGISTRAR_MAX 0x1038 +#define WPS_ELEM_REGISTRAR_NONCE 0x1039 +#define WPS_ELEM_REQUEST_TYPE 0x103a +#define WPS_ELEM_RESPONSE_TYPE 0x103b +#define WPS_ELEM_RF_BANDS 0x103c +#define WPS_ELEM_R_HASH1 0x103d +#define WPS_ELEM_R_HASH2 0x103e +#define WPS_ELEM_R_SNONCE1 0x103f +#define WPS_ELEM_R_SNONCE2 0x1040 +#define WPS_ELEM_SELECTED_REGISTRAR 0x1041 +#define WPS_ELEM_SERIAL_NUMBER 0x1042 +#define WPS_ELEM_WPS_STATE 0x1044 +#define WPS_ELEM_SSID 0x1045 +#define WPS_ELEM_TOTAL_NETWORKS 0x1046 +#define WPS_ELEM_UUID_E 0x1047 +#define WPS_ELEM_UUID_R 0x1048 +#define WPS_ELEM_VENDOR_EXTENSION 0x1049 +#define WPS_ELEM_VERSION 0x104a +#define WPS_ELEM_X509_CERT_REQUEST 0x104b +#define WPS_ELEM_X509_CERTIFICATE 0x104c +#define WPS_ELEM_EAP_IDENTITY 0x104d +#define WPS_ELEM_MESSAGE_COUNTER 0x104e +#define WPS_ELEM_PUBLIC_KEY_HASH 0x104f +#define WPS_ELEM_REKEY_KEY 0x1050 +#define WPS_ELEM_KEY_LIFETIME 0x1051 +#define WPS_ELEM_PERMITTED_CONFIG_METHODS 0x1052 +#define WPS_ELEM_SELECTED_REGISTRAR_CONFIG_METHODS 0x1053 +#define WPS_ELEM_PRIMARY_DEVICE_TYPE 0x1054 +#define WPS_ELEM_SECONDARY_DEVICE_TYPE_LIST 0x1055 +#define WPS_ELEM_PORTABLE_DEVICE 0x1056 +#define WPS_ELEM_AP_SETUP_LOCKED 0x1057 +#define WPS_ELEM_APPLICATION_EXTENSION 0x1058 +#define WPS_ELEM_EAP_TYPE 0x1059 +#define WPS_ELEM_INITIALIZATION_VECTOR 0x1060 +#define WPS_ELEM_KEY_PROVIDED_AUTOMATICALLY 0x1061 +#define WPS_ELEM_8021X_ENABLED 0x1062 +#define WPS_ELEM_APPSESSIONKEY 0x1063 +#define WPS_ELEM_WEPTRANSMITKEY 0x1064 + + +#define WPS_ELEM_LEN_AP_CHANNEL 2 +#define WPS_ELEM_LEN_ASSOCIATION_STATE 2 +#define WPS_ELEM_LEN_AUTHENTICATION_TYPE 2 +#define WPS_ELEM_LEN_AUTHENTICATION_TYPE_FLAG 2 +#define WPS_ELEM_LEN_AUTHENTICATOR 8 +#define WPS_ELEM_LEN_CONFIG_METHODS 2 +#define WPS_ELEM_LEN_CONFIGURATION_ERROR 2 +#define WPS_ELEM_LEN_CONFIRMATION_URL4 64 /* <= this MAX */ +#define WPS_ELEM_LEN_CONFIRMATION_URL6 76 /* <= this MAX */ +#define WPS_ELEM_LEN_CONNECTION_TYPE 1 +#define WPS_ELEM_LEN_CONNECTION_TYPE_FLAG 1 +/* #define WPS_ELEM_LEN_CREDENTIAL */ +#define WPS_ELEM_LEN_DEVICE_NAME 32 /* <= this MAX */ +#define WPS_ELEM_LEN_DEVICE_PASSWORD_ID 2 +#define WPS_ELEM_LEN_E_HASH1 32 +#define WPS_ELEM_LEN_E_HASH2 32 +#define WPS_ELEM_LEN_E_SNONCE1 16 +#define WPS_ELEM_LEN_E_SNONCE2 16 +/* #define WPS_ELEM_LEN_ENCRYPTED_SETTINGS */ +#define WPS_ELEM_LEN_ENCRYPTION_TYPE 2 +#define WPS_ELEM_LEN_ENCRYPTION_TYPE_FLAG 2 +#define WPS_ELEM_LEN_ENROLLEE_NONCE 16 +#define WPS_ELEM_LEN_FEATURE_ID 4 +#define WPS_ELEM_LEN_IDENTITY 80 /* <= this MAX */ +/* #define WPS_ELEM_LEN_IDENTITY_PROOF */ +#define WPS_ELEM_LEN_KEY_WRAP_AUTHENTICATOR 8 +#define WPS_ELEM_LEN_KEY_IDENTIFIER 16 +#define WPS_ELEM_LEN_MAC_ADDRESS 6 +#define WPS_ELEM_LEN_MANUFACTURER 64 /* <= this MAX */ +#define WPS_ELEM_LEN_MESSAGE_TYPE 1 +#define WPS_ELEM_LEN_MODEL_NAME 32 /* <= this MAX */ +#define WPS_ELEM_LEN_MODEL_NUMBER 32 /* <= this MAX */ +#define WPS_ELEM_LEN_NETWORK_INDEX 1 +#define WPS_ELEM_LEN_NETWORK_KEY 64 /* <= this MAX */ +#define WPS_ELEM_LEN_NETWORK_KEY_INDEX 1 +#define WPS_ELEM_LEN_NEW_DEVICE_NAME 32 /* <= this MAX */ +#define WPS_ELEM_LEN_NEW_PASSWORD 64 /* <= this MAX */ +#define WPS_ELEM_LEN_OOB_DEVICE_PASSWORD 58 /* <= this MAX */ +#define WPS_ELEM_LEN_OS_VERSION 4 +#define WPS_ELEM_LEN_POWER_LEVEL 1 +#define WPS_ELEM_LEN_PSK_CURRENT 1 +#define WPS_ELEM_LEN_PSK_MAX 1 +#define WPS_ELEM_LEN_PUBLIC_KEY 192 +#define WPS_ELEM_LEN_RADIO_ENABLED 1 +#define WPS_ELEM_LEN_REBOOT 1 +#define WPS_ELEM_LEN_REGISTRAR_CURRENT 1 +#define WPS_ELEM_LEN_REGISTRAR_ESTABLISHED 1 +#define WPS_ELEM_LEN_REGISTRAR_LIST 512 /* <= this MAX */ +#define WPS_ELEM_LEN_REGISTRAR_MAX 1 +#define WPS_ELEM_LEN_REGISTRAR_NONCE 16 +#define WPS_ELEM_LEN_REQUEST_TYPE 1 +#define WPS_ELEM_LEN_RESPONSE_TYPE 1 +#define WPS_ELEM_LEN_RF_BANDS 1 +#define WPS_ELEM_LEN_R_HASH1 32 +#define WPS_ELEM_LEN_R_HASH2 32 +#define WPS_ELEM_LEN_R_SNONCE1 16 +#define WPS_ELEM_LEN_R_SNONCE2 16 +#define WPS_ELEM_LEN_SELECTED_REGISTRAR 1 +#define WPS_ELEM_LEN_SERIAL_NUMBER 32 /* <= this MAX */ +#define WPS_ELEM_LEN_WPS_STATE 1 +#define WPS_ELEM_LEN_SSID 32 /* <= this MAX */ +#define WPS_ELEM_LEN_TOTAL_NETWORKS 1 +#define WPS_ELEM_LEN_UUID_E 16 +#define WPS_ELEM_LEN_UUID_R 16 +#define WPS_ELEM_LEN_VENDOR_EXTENSION 1024 /* <= this MAX */ +#define WPS_ELEM_LEN_VERSION 1 +/* #define WPS_ELEM_LEN_X509_CERT_REQUEST */ +/* #define WPS_ELEM_LEN_X509_CERTIFICATE */ +#define WPS_ELEM_LEN_EAP_IDENTITY 64 /* <= this MAX */ +#define WPS_ELEM_LEN_MESSAGE_COUNTER 8 +#define WPS_ELEM_LEN_PUBLIC_KEY_HASH 20 +#define WPS_ELEM_LEN_REKEY_KEY 32 +#define WPS_ELEM_LEN_KEY_LIFETIME 4 +#define WPS_ELEM_LEN_PERMITTED_CONFIG_METHODS 2 +#define WPS_ELEM_LEN_SELECTED_REGISTRAR_CONFIG_METHODS 2 +#define WPS_ELEM_LEN_PRIMARY_DEVICE_TYPE 8 +#define WPS_ELEM_LEN_SECONDARY_DEVICE_TYPE_LIST 128 /* <= this MAX */ +#define WPS_ELEM_LEN_PORTABLE_DEVICE 1 +#define WPS_ELEM_LEN_AP_SETUP_LOCKED 1 +#define WPS_ELEM_LEN_APPLICATION_EXTENSION 512 /* <= this MAX */ +#define WPS_ELEM_LEN_EAP_TYPE 8 /* <= this MAX */ +#define WPS_ELEM_LEN_INITIALIZATION_VECTOR 32 +#define WPS_ELEM_LEN_KEY_PROVIDED_AUTOMATICALLY 1 +#define WPS_ELEM_LEN_8021X_ENABLED 1 +#define WPS_ELEM_LEN_APPSESSIONKEY 128 +#define WPS_ELEM_LEN_WEPTRANSMITKEY 1 + + +#define WPS_ASSOCIATION_STATE_NOT_ASSOCIATED 0x0000 +#define WPS_ASSOCIATION_STATE_CONNECTION_SUCCESS 0x0001 +#define WPS_ASSOCIATION_STATE_CONNECTION_FAILURE 0x0002 +#define WPS_ASSOCIATION_STATE_ASSOCIATION_FAILURE 0x0003 +#define WPS_ASSOCIATION_STATE_IP_FAILURE 0x0004 + +#define WPS_AUTHENTICATION_TYPE_OPEN 0x0001 +#define WPS_AUTHENTICATION_TYPE_WPAPSK 0x0002 +#define WPS_AUTHENTICATION_TYPE_Shared 0x0004 +#define WPS_AUTHENTICATION_TYPE_WPA 0x0008 +#define WPS_AUTHENTICATION_TYPE_WPA2 0x0010 +#define WPS_AUTHENTICATION_TYPE_WPA2PSK 0x0020 + +#define WPS_CONFIG_METHOD_NONE 0x0000 /* NB not defined by spec */ +#define WPS_CONFIG_METHOD_USBA 0x0001 +#define WPS_CONFIG_METHOD_ETHERNET 0x0002 +#define WPS_CONFIG_METHOD_LABEL 0x0004 +#define WPS_CONFIG_METHOD_DISPLAY 0x0008 +#define WPS_CONFIG_METHOD_EXTERN_NFC 0x0010 +#define WPS_CONFIG_METHOD_INTERN_NFC 0x0020 +#define WPS_CONFIG_METHOD_NFC_IFACE 0x0040 +#define WPS_CONFIG_METHOD_PBC 0x0080 +#define WPS_CONFIG_METHOD_KEYPAD 0x0100 + +#define WPS_CONFIGURATION_ERROR_NO_ERROR 0 +#define WPS_CONFIGURATION_ERROR_OOB_IF_READ 1 +#define WPS_CONFIGURATION_ERROR_DECRYPT_CRC 2 +#define WPS_CONFIGURATION_ERROR_24_NOT_SUP 3 +#define WPS_CONFIGURATION_ERROR_50_NOT_SUP 4 +#define WPS_CONFIGURATION_ERROR_SIG_WEAK 5 +#define WPS_CONFIGURATION_ERROR_NET_AUTH 6 +#define WPS_CONFIGURATION_ERROR_NET_ASSOC 7 +#define WPS_CONFIGURATION_ERROR_NO_DHCP 8 +#define WPS_CONFIGURATION_ERROR_DHCP_CONF 9 +#define WPS_CONFIGURATION_ERROR_IP_CONFLICT 10 +#define WPS_CONFIGURATION_ERROR_CONN_REG 11 +#define WPS_CONFIGURATION_ERROR_MULTI_PBC 12 +#define WPS_CONFIGURATION_ERROR_ROUGE_ACT 13 +#define WPS_CONFIGURATION_ERROR_DEV_BUSY 14 +#define WPS_CONFIGURATION_ERROR_SETUP_LOCK 15 +#define WPS_CONFIGURATION_ERROR_MSG_TIMEOUT 16 +#define WPS_CONFIGURATION_ERROR_REG_TIMEOUT 17 +#define WPS_CONFIGURATION_ERROR_DEV_PASSWD 18 + +#define WPS_CONNECTION_TYPE_ESS 0x01 +#define WPS_CONNECTION_TYPE_IBSS 0x02 + +#define WPS_DEVICE_PASSWORD_ID_PIN 0x0000 +#define WPS_DEVICE_PASSWORD_ID_USER 0x0001 +#define WPS_DEVICE_PASSWORD_ID_MACHINE 0x0002 +#define WPS_DEVICE_PASSWORD_ID_REKEY 0x0003 +#define WPS_DEVICE_PASSWORD_ID_PBC 0x0004 +#define WPS_DEVICE_PASSWORD_ID_REGISTRAR 0x0005 + +#define WPS_ENCRYPTION_TYPE_NONE 0x0001 +#define WPS_ENCRYPTION_TYPE_WEP 0x0002 +#define WPS_ENCRYPTION_TYPE_TKIP 0x0004 +#define WPS_ENCRYPTION_TYPE_AES 0x0008 + +#define WPS_MESSAGE_TYPE_M1 0x04 +#define WPS_MESSAGE_TYPE_M2 0x05 +#define WPS_MESSAGE_TYPE_M2D 0x06 +#define WPS_MESSAGE_TYPE_M3 0x07 +#define WPS_MESSAGE_TYPE_M4 0x08 +#define WPS_MESSAGE_TYPE_M5 0x09 +#define WPS_MESSAGE_TYPE_M6 0x0a +#define WPS_MESSAGE_TYPE_M7 0x0b +#define WPS_MESSAGE_TYPE_M8 0x0c +#define WPS_MESSAGE_TYPE_WSC_ACK 0x0d +#define WPS_MESSAGE_TYPE_WSC_NACK 0x0e +#define WPS_MESSAGE_TYPE_WSC_Done 0x0f + +#define WPS_CATEGORY_COMPUTER 1 +#define WPS_CATEGORY_IO_DEVICE 2 +#define WPS_CATEGORY_PRINTER_SCANNER 3 +#define WPS_CATEGORY_CAMERA 4 +#define WPS_CATEGORY_STORAGE 5 +#define WPS_CATEGORY_NETWORK 6 +#define WPS_CATEGORY_DISPLAYS 7 +#define WPS_CATEGORY_MULTIMEDIA 8 +#define WPS_CATEGORY_GAMING 9 +#define WPS_CATEGORY_TELEPHONE 10 + +#define WPS_REQUEST_TYPE_ENROLLEE_INFO 0x00 +#define WPS_REQUEST_TYPE_ENROLLEE_OPEN 0x01 +#define WPS_REQUEST_TYPE_REGISTRAR 0x02 +#define WPS_REQUEST_TYPE_WLAN_MANAGER 0x03 + +#define WPS_RF_BAND_2_4GHZ 0x01 +#define WPS_RF_BAND_5_0GHZ 0x02 + +#define WPS_SUBCATEGORY_PC 1 +#define WPS_SUBCATEGORY_SERVER 2 +#define WPS_SUBCATEGORY_MEDIA_CENTER 3 +#define WPS_SUBCATEGORY_PRINTER 1 +#define WPS_SUBCATEGORY_SCANNER 2 +#define WPS_SUBCATEGORY_STILL_CAMERA 1 +#define WPS_SUBCATEGORY_NAS 1 +#define WPS_SUBCATEGORY_AP 1 +#define WPS_SUBCATEGORY_ROUTER 2 +#define WPS_SUBCATEGORY_SWITCH 3 +#define WPS_SUBCATEGORY_TELEVISION 1 +#define WPS_SUBCATEGORY_PICTURE_FRAME 2 +#define WPS_SUBCATEGORY_PROJECTOR 3 +#define WPS_SUBCATEGORY_DAR 1 +#define WPS_SUBCATEGORY_PVR 2 +#define WPS_SUBCATEGORY_MCX 3 +#define WPS_SUBCATEGORY_XBOX 1 +#define WPS_SUBCATEGORY_XBOX360 2 +#define WPS_SUBCATEGORY_PLAYSTATION 3 +#define WPS_SUBCATEGORY_WINDOWS_MOBILE 1 + +#define WPS_SETUP_STATE_NOT_CONFIGURED 0x01 +#define WPS_SETUP_STATE_CONFIGURED 0x02 + +#define WPS_VERSION_1_0 0x10 + +/** + * WPS_WALK_TIME - Amount of time allowed to start PBC configuration + * Time in seconds + */ +#define WPS_WALK_TIME 120 + + +/** + * WPS_PIN_PBC - PIN value for PBC + */ +#define WPS_PIN_PBC "00000000" +#define WPS_PIN_LEN sizeof(WPS_PIN_PBC) + +/** + * The WPS implementation uses two named blobs: "WPS-REQ" and "WPS-RES". + * The first supplies the configuration constraints to the EAP method + * (e.g. are both TKIP and CCMP allowed). This blob is written by the + * upper layer (supplicant or registrar) and read by the EAP method + * (peer or server). + * + * The second contains the results of the WPS negotiation between the + * enrollee and the registrar. It is written by the EAP method and read + * by the upper layer. If the protocol completes but this named blob + * does not exist, the upper layer can infer that the negotiation failed. + * + * The format of both blobs is identical, consisting of a + * type / length / value triplet (TLV). The type field uses the WPS + * element values and is 16 bits in host order. The length field is the + * length in bytes of the value field and is also in host order. + * + * A blob can contain multiple network specifications, each begining with + * a Network Index element TLV. + */ + +#define WPS_BLOB_NAME_REQUIRE "WPS-REQ" +#define WPS_BLOB_NAME_RESULT "WPS-RES" + +#define WPS_WPAPSK_PASSPHRASE_LEN 64 + +#endif /* WPS_H */ diff --git a/src/rsn_supp/wps_i.h b/src/rsn_supp/wps_i.h new file mode 100644 index 0000000..6cd54ac --- /dev/null +++ b/src/rsn_supp/wps_i.h @@ -0,0 +1,64 @@ +/* + * WiFi Protected Setup - WPS interface definitions + * Copyright (c) 2008, Chuck Tuffli + * + * 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. + */ + +#ifndef WPS_I_H +#define WPS_I_H + +/** + * struct wps_sm - Internal WPS state machine data + */ +struct wps_sm { + int config_method; + char *wps_pin; + char *uuid_e; // TODO change to just uuid + void *ctx; /* pointer to arbitrary upper level context */ + + char ssid[32]; + size_t ssid_len; + u8 own_addr[ETH_ALEN]; + unsigned int pairwise_cipher; + unsigned int group_cipher; + unsigned int key_mgmt; +}; + + +static inline char * wps_sm_get_pin(struct wps_sm *sm) +{ + if (sm) + return sm->wps_pin; + else + return NULL; +} + +static inline void wps_sm_set_own_addr(struct wps_sm *sm, const u8 *addr) +{ + if (sm) + os_memcpy(sm->own_addr, addr, ETH_ALEN); +} + +int wpa_parse_wps_ie(const u8 *wps_ie, size_t wps_ie_len, + struct wps_ie_data *data); +int wpa_gen_wps_ie(struct wps_sm *sm, char *uuid, u8 *wps_ie, + size_t *wps_ie_len); +int wps_config_pbc(struct wps_sm *sm); +int wps_cancel_walktime_timeout(struct wps_sm *sm); +int wps_get_pin(struct wps_sm *sm, char *buf, size_t buflen); +int wps_config_pin(struct wps_sm *sm); +struct wps_sm * wps_sm_init(void *ctx); +void wps_sm_deinit(struct wps_sm *sm); +int wps_sm_set_param(struct wps_sm *sm, enum wps_sm_conf_params param, + unsigned int value); +int wps_sm_get_param(struct wps_sm *sm, enum wps_sm_conf_params param); + +#endif /* WPS_I_H */ diff --git a/src/utils/tlv.c b/src/utils/tlv.c new file mode 100644 index 0000000..11cb0ed --- /dev/null +++ b/src/utils/tlv.c @@ -0,0 +1,34 @@ +/* + * Type, Length, Value + * Copyright (c) 2008, Chuck Tuffli + * + * 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. + */ + +#include "includes.h" + +#include "common.h" +#include "tlv.h" + + +u8 * tlv_write(u8 *buf, u16 type, u16 len, const u8 *val) +{ + u8 *start; + + WPA_PUT_BE16(buf, type); + WPA_PUT_BE16(buf + 2, len); + + start = TLV_GET_VAL(buf); + os_memcpy(start, val, len); + start += len; + + return start; +} + diff --git a/src/utils/tlv.h b/src/utils/tlv.h new file mode 100644 index 0000000..15c25c8 --- /dev/null +++ b/src/utils/tlv.h @@ -0,0 +1,37 @@ +/* + * Type, Length, Value + * Copyright (c) 2008, Chuck Tuffli + * + * 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. + */ + +#define TLV_LEN 4 +#define TLV_GET_TYPE(p) WPA_GET_BE16(p) +#define TLV_GET_LEN(p) WPA_GET_BE16(((u8 *)(p)) + 2) +#define TLV_GET_VAL(p) (((u8 *)(p)) + TLV_LEN) +#define TLV_BYTES(e) ((e) + TLV_LEN) + + +extern u8 * tlv_write(u8 *buf, u16 type, u16 len, const u8 *val); + + +static inline u8 * tlv_write_u8(u8 *buf, u16 type, u8 val) +{ + return tlv_write(buf, type, 1, &val); +} + + +static inline u8 * tlv_write_u16(u8 *buf, u16 type, u16 val) +{ + u16 be_val = host_to_be16(val); + + return tlv_write(buf, type, 2, (u8 *) &be_val); +} + diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 1bdac6c..8fbce24 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -525,6 +525,18 @@ OBJS += ../src/eap_peer/tncc.o NEED_BASE64=y endif +ifdef CONFIG_EAP_WPS +# EAP-WPS +CFLAGS += -DEAP_WPS +OBJS += ../src/utils/tlv.o +OBJS += ../src/eap_common/eap_wps_common.o +OBJS += ../src/eap_peer/eap_wps.o +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_SHA256=y +endif + ifdef CONFIG_IEEE8021X_EAPOL # IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication) CFLAGS += -DIEEE8021X_EAPOL @@ -874,11 +886,13 @@ endif ifndef CONFIG_NO_WPA OBJS += ../src/rsn_supp/wpa.o +OBJS += ../src/rsn_supp/wps.o OBJS += ../src/rsn_supp/preauth.o OBJS += ../src/rsn_supp/pmksa_cache.o OBJS += ../src/rsn_supp/peerkey.o OBJS += ../src/rsn_supp/wpa_ie.o OBJS += ../src/common/wpa_common.o +OBJS += ../src/utils/uuid.o NEED_AES=y else CFLAGS += -DCONFIG_NO_WPA -DCONFIG_NO_WPA2 diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index fc64be1..61d7d5c 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -414,6 +414,8 @@ static int wpa_config_parse_proto(const struct parse_data *data, else if (os_strcmp(start, "RSN") == 0 || os_strcmp(start, "WPA2") == 0) val |= WPA_PROTO_RSN; + else if (os_strcmp(start, "WPS") == 0) + val |= WPA_PROTO_WPS; else { wpa_printf(MSG_ERROR, "Line %d: invalid proto '%s'", line, start); @@ -466,6 +468,14 @@ static char * wpa_config_write_proto(const struct parse_data *data, first = 0; } + if (ssid->proto & WPA_PROTO_WPS) { + ret = os_snprintf(pos, end - pos, "%sWPS", first ? "" : " "); + if (ret < 0 || ret >= end - pos) + return buf; + pos += ret; + first = 0; + } + return buf; } #endif /* NO_CONFIG_WRITE */ diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index 1eae050..4b38f70 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -244,6 +244,11 @@ struct wpa_config { int update_config; /** + * uuid_e - Universally Unique ID + */ + char *uuid_e; + + /** * blobs - Configuration blobs */ struct wpa_config_blob *blobs; diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 60650ae..66309ba 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -106,7 +106,12 @@ static int wpa_config_validate_network(struct wpa_ssid *ssid, int line) if ((ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_PSK_SHA256)) && - !ssid->psk_set) { + !ssid->psk_set +#ifdef EAP_WPS + /* PSK not set with WPS */ + && !(ssid->proto & WPA_PROTO_WPS) +#endif + ) { wpa_printf(MSG_ERROR, "Line %d: WPA-PSK accepted for key " "management, but no PSK configured.", line); errors++; @@ -427,6 +432,15 @@ static int wpa_config_process_load_dynamic_eap(int line, char *so) } +static int wpa_config_process_uuid_e(struct wpa_config *config, char *pos) +{ + os_free(config->uuid_e); + config->uuid_e = os_strdup(pos); + wpa_printf(MSG_DEBUG, "uuid_e=%s", config->uuid_e); + return 0; +} + + static int wpa_config_process_global(struct wpa_config *config, char *pos, int line) { @@ -481,6 +495,9 @@ static int wpa_config_process_global(struct wpa_config *config, char *pos, if (os_strncmp(pos, "load_dynamic_eap=", 17) == 0) return wpa_config_process_load_dynamic_eap(line, pos + 17); + if (os_strncmp(pos, "uuid=", 5) == 0) + return wpa_config_process_uuid_e(config, pos + 5); + return -1; } @@ -842,6 +859,8 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) config->dot11RSNAConfigSATimeout); if (config->update_config) fprintf(f, "update_config=%d\n", config->update_config); + if (config->uuid_e) + fprintf(f, "uuid=%s\n", config->uuid_e); } #endif /* CONFIG_NO_CONFIG_WRITE */ diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 036365d..174faf5 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -17,9 +17,11 @@ #include "common.h" #include "eloop.h" #include "wpa.h" +#include "wps.h" #include "config.h" #include "eapol_supp/eapol_supp_sm.h" #include "wpa_supplicant_i.h" +#include "wps_i.h" #include "ctrl_iface.h" #include "l2_packet/l2_packet.h" #include "preauth.h" @@ -528,6 +530,30 @@ static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto, return pos; } +static char * wps_ie_txt(char *pos, char *end, const char *proto, + const u8 *ie, size_t ie_len) +{ + struct wps_ie_data data; + int first, ret; + + ret = os_snprintf(pos, end - pos, "[%s-", proto); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + + if (wpa_parse_wps_ie(ie, ie_len, &data) < 0) { + ret = os_snprintf(pos, end - pos, "?]"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + return pos; + } + + first = 1; + + return pos; +} + /* Format one result on one text line into a buffer. */ static int wpa_supplicant_ctrl_iface_scan_result( @@ -535,7 +561,7 @@ static int wpa_supplicant_ctrl_iface_scan_result( { char *pos, *end; int ret; - const u8 *ie, *ie2; + const u8 *ie, *ie2, *ie3; pos = buf; end = buf + buflen; @@ -551,6 +577,9 @@ static int wpa_supplicant_ctrl_iface_scan_result( ie2 = wpa_scan_get_ie(res, WLAN_EID_RSN); if (ie2) pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]); + ie3 = wpa_scan_get_vendor_ie(res, WPS_IE_VENDOR_TYPE); + if (ie3) + pos = wps_ie_txt(pos, end, "WPS", ie, 2 + ie[1]); if (!ie && !ie2 && res->caps & IEEE80211_CAP_PRIVACY) { ret = os_snprintf(pos, end - pos, "[WEP]"); if (ret < 0 || ret >= end - pos) @@ -1494,6 +1523,12 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "BSS ", 4) == 0) { reply_len = wpa_supplicant_ctrl_iface_bss( wpa_s, buf + 4, reply, reply_size); + } else if (os_strcmp(buf, "PBC") == 0) { + wps_config_pbc(wpa_s->wps); + } else if (os_strcmp(buf, "PIN_GET") == 0) { + reply_len = wps_get_pin(wpa_s->wps, reply, reply_size); + } else if (os_strcmp(buf, "PIN_ENTERED") == 0) { + wps_config_pin(wpa_s->wps); } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig index 2ad2bb8..298bc05 100644 --- a/wpa_supplicant/defconfig +++ b/wpa_supplicant/defconfig @@ -167,6 +167,13 @@ CONFIG_EAP_LEAP=y # EAP-IKEv2 #CONFIG_EAP_IKEV2=y +# EAP-WPS (enable Wifi Protected Setup, sometimes refered to as Simple Config) +# This EAP method requires CONFIG_IEEE8021X_EAPOL as well as +# CONFIG_TLS=internal +CONFIG_EAP_WPS=y +CONFIG_TLS=internal +CONFIG_INTERNAL_LIBTOMMATH=y + # PKCS#12 (PFX) support (used to read private key and certificate file from # a file that usually has extension .p12 or .pfx) CONFIG_PKCS12=y diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 45b5ac3..fa36df6 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -17,11 +17,13 @@ #include "common.h" #include "eapol_supp/eapol_supp_sm.h" #include "wpa.h" +#include "wps.h" #include "eloop.h" #include "drivers/driver.h" #include "config.h" #include "l2_packet/l2_packet.h" #include "wpa_supplicant_i.h" +#include "wps_i.h" #include "pcsc_funcs.h" #include "preauth.h" #include "pmksa_cache.h" @@ -364,6 +366,215 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_ssid *ssid, } +static struct wpa_ssid * +wpa_supplicant_wps_match(struct wpa_supplicant *wpa_s, + struct wpa_ssid *group, struct wpa_scan_res *bss) +{ + const u8 *ie, *ssid_; + u8 wps_ie_len, ssid_len; + struct wpa_ssid *ssid, *selected_ssid = NULL; + + ie = wpa_scan_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE); + wps_ie_len = ie ? ie[1] + 2 : 0; + + for (ssid = group; ssid; ssid = ssid->pnext) { + if (ssid->disabled) { + wpa_printf(MSG_DEBUG, " skip - disabled"); + continue; + } + + ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); + ssid_ = ie ? ie + 2 : (u8 *) ""; + ssid_len = ie ? ie[1] : 0; + + if ((ssid->proto & WPA_PROTO_WPS) && wps_ie_len > 0) { + selected_ssid = ssid; + if (ssid->ssid) { + os_free(ssid->ssid); + } + if (NULL == (ssid->ssid = os_malloc(ssid_len))) { + wpa_printf(MSG_ERROR, "EAP-WPS: Cannot allocate SSID"); + break; + } + os_memcpy(ssid->ssid, ssid_, ssid_len); + ssid->ssid_len = ssid_len; + wpa_printf(MSG_DEBUG, " selected WPS AP " + MACSTR " ssid='%s'", + MAC2STR(bss->bssid), + wpa_ssid_txt(ssid_, ssid_len)); + + if (NULL != ssid->eap.identity) + os_free(ssid->eap.identity); + ssid->eap.identity = (u8 *)os_strdup(WPS_EAP_IDENTITY); + ssid->eap.identity_len = os_strlen(WPS_EAP_IDENTITY); + } + } + + return selected_ssid; +} + + +static struct wpa_scan_res * +wpa_supplicant_select_bss_wps(struct wpa_supplicant *wpa_s, + struct wpa_ssid *group, + struct wpa_ssid **selected_ssid) +{ + struct wpa_scan_res *bss = NULL; + struct wps_ie_data *wps_res = NULL; + struct wps_ie_data *pbc_bss = NULL; + struct wps_ie_data *pin_bss = NULL; + size_t wps_res_count = 0; + size_t pbc_count = 0; + size_t i; + struct wpa_blacklist *e; + const u8 *ie; + const u8 *ssid_ = NULL; + u16 config_meth; + u8 wps_ie_len = 0, ssid_len = 0; + + *selected_ssid = NULL; + config_meth = wps_sm_get_param(wpa_s->wps, WPS_PARAM_CONFIG_METHOD); + + if (WPS_CONFIG_METHOD_NONE == config_meth) + return NULL; + + wps_res = os_zalloc(wpa_s->scan_res->num * sizeof(struct wps_ie_data)); + + wpa_printf(MSG_DEBUG, "Try to find WPS-selected AP"); + for (i = 0; i < wpa_s->scan_res->num; i++) { + struct wpa_scan_res *bss_ = wpa_s->scan_res->res[i]; + + ie = wpa_scan_get_ie(bss_, WLAN_EID_SSID); + ssid_ = ie ? ie + 2 : (u8 *) ""; + ssid_len = ie ? ie[1] : 0; + + ie = wpa_scan_get_vendor_ie(bss_, WPS_IE_VENDOR_TYPE); + wps_ie_len = ie ? ie[1] + 2 : 0; + + wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' " + "wps_ie_len=%u caps=0x%x", + i, MAC2STR(bss_->bssid), + wpa_ssid_txt(ssid_, ssid_len), + wps_ie_len, bss_->caps); + + if (0 == wps_ie_len) { + wpa_printf(MSG_DEBUG, " skip - no WPS IE"); + continue; + } + + if (wpa_parse_wps_ie(ie, wps_ie_len, &wps_res[wps_res_count])) { + wpa_printf(MSG_DEBUG, " skip - bad WPS IE"); + continue; + } + + e = wpa_blacklist_get(wpa_s, bss_->bssid); + if (e && e->count > 1) { + wpa_printf(MSG_DEBUG, " skip - blacklisted"); + continue; + } + + /* + * Cache BSS' advertising a WPS IE. This speeds up the search + * for unselected external registrars. + */ + wps_res[wps_res_count].bss = bss_; + + /* + * Selected Registrar is an optional element which if TRUE + * indicates Device Password ID is present and valid. + * + * Note that some external registrars like ********* (removed + * to protect the bone-headed) won't cause the AP to set + * Selected Registrar to TRUE until after an enrollee has + * attempted to start the WPS protocol (i.e. sent M1). See + * below for a work around. + */ + if (wps_res[wps_res_count].selected_registrar) { + u16 dev_pass_id = wps_res[wps_res_count].device_password_id; + + if (WPS_DEVICE_PASSWORD_ID_PBC == dev_pass_id) { + pbc_count++; + if (NULL == pbc_bss) { + pbc_bss = &wps_res[wps_res_count]; + wpa_printf(MSG_DEBUG, " pending PBC"); + } else if (0 == os_memcmp(pbc_bss->uuid, + wps_res[wps_res_count].uuid, WPS_UUID_LEN)) { + pbc_count--; + wpa_printf(MSG_DEBUG, " skip - matching UUID_E PBC"); + } else { + wpa_printf(MSG_DEBUG, " skip - overlapping PBC"); + } + } else if (WPS_DEVICE_PASSWORD_ID_PIN == dev_pass_id) { + if (NULL == pin_bss) { + pin_bss = &wps_res[wps_res_count]; + wpa_printf(MSG_DEBUG, " pending PIN"); + } + } else { + wpa_printf(MSG_DEBUG, " only PBC / PIN supported"); + } + } else { + wpa_printf(MSG_DEBUG, " skip - registrar not selected"); + } + + wps_res_count++; + } + + switch (config_meth) { + case WPS_CONFIG_METHOD_PBC: + if (1 == pbc_count) { + bss = pbc_bss->bss; + } else if (pbc_count > 1) { + /* only one registrar can use PBC at a time */ + wps_cancel_walktime_timeout(wpa_s->wps); + wpa_msg(wpa_s, MSG_ERROR, WPA_EVENT_WPS_OVERLAP); + } + break; + case WPS_CONFIG_METHOD_DISPLAY: + case WPS_CONFIG_METHOD_LABEL: + /* + * Look for a selected registrar using PIN. + * Otherwise, choose the first non-black listed BSS + * supporting PIN. + */ + if (pin_bss) { + bss = pin_bss->bss; + } else { + wpa_printf(MSG_DEBUG, "Try to find WPS-capable AP"); + for (i = 0; i < wps_res_count; i++) { + struct wpa_scan_res *bss_ = wps_res[i].bss; + + ie = wpa_scan_get_ie(bss_, WLAN_EID_SSID); + ssid_ = ie ? ie + 2 : (u8 *) ""; + ssid_len = ie ? ie[1] : 0; + + wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' ", + i, + MAC2STR(bss_->bssid), + wpa_ssid_txt(ssid_, ssid_len)); + + if (wps_res[i].config_method & + (WPS_CONFIG_METHOD_LABEL | + WPS_CONFIG_METHOD_DISPLAY) + ) { + wpa_printf(MSG_DEBUG, " pending PIN"); + bss = bss_; + } else { + wpa_printf(MSG_DEBUG, " skip - not using PIN"); + } + } + } + break; + } + + if (bss) + *selected_ssid = wpa_supplicant_wps_match(wpa_s, group, bss); + + os_free(wps_res); + + return bss; +} + + static struct wpa_scan_res * wpa_supplicant_select_bss_wpa(struct wpa_supplicant *wpa_s, struct wpa_ssid *group, @@ -559,7 +770,16 @@ wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_ssid *group, wpa_printf(MSG_DEBUG, "Selecting BSS from priority group %d", group->priority); - /* First, try to find WPA-enabled AP */ +#ifdef EAP_WPS + /* First, try to find WPS-enabled AP */ + selected = wpa_supplicant_select_bss_wps(wpa_s, group, selected_ssid); + if (selected) { + wpa_supplicant_wps_supp_set_config(wpa_s, *selected_ssid); + return selected; + } +#endif + + /* Next, try to find WPA-enabled AP */ selected = wpa_supplicant_select_bss_wpa(wpa_s, group, selected_ssid); if (selected) return selected; diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index 8767109..26a0511 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -18,6 +18,8 @@ #include "eloop.h" #include "config.h" #include "wpa_supplicant_i.h" +#include "wps.h" +#include "wps_i.h" #include "mlme.h" #include "uuid.h" @@ -46,7 +48,7 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) struct wpa_supplicant *wpa_s = eloop_ctx; struct wpa_ssid *ssid; int enabled, scan_req = 0, ret; - const u8 *extra_ie = NULL; + u8 *extra_ie = NULL; size_t extra_ie_len = 0; if (wpa_s->disconnected && !wpa_s->scan_req) @@ -145,6 +147,23 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) return; } + if (WPS_CONFIG_METHOD_NONE != wps_sm_get_param(wpa_s->wps, + WPS_PARAM_CONFIG_METHOD)) { + u8 wps_ie[SSID_MAX_WPS_IE_LEN]; + size_t wps_ie_len; + + if (wpa_gen_wps_ie(wpa_s->wps, wpa_s->conf->uuid_e, wps_ie, + &wps_ie_len)) { + wpa_printf(MSG_WARNING, "WPA: Failed to create WPS IE"); + return; + } + extra_ie = os_malloc(extra_ie_len + wps_ie_len); + if (extra_ie) { + os_memcpy(extra_ie + extra_ie_len, wps_ie, wps_ie_len); + extra_ie_len += wps_ie_len; + } + } + if (wpa_s->use_client_mlme) { ieee80211_sta_set_probe_req_ie(wpa_s, extra_ie, extra_ie_len); ret = ieee80211_sta_req_scan(wpa_s, ssid ? ssid->ssid : NULL, @@ -159,6 +178,8 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) wpa_printf(MSG_WARNING, "Failed to initiate AP scan."); wpa_supplicant_req_scan(wpa_s, 10, 0); } + + os_free(extra_ie); } diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 46ec736..44f3871 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -133,6 +133,11 @@ static const char *commands_help = " stkstart = request STK negotiation with \n" " ft_ds = request over-the-DS FT with \n" " terminate = terminate wpa_supplicant\n" +#ifdef EAP_WPS +" pbc = start Push Button Configuration\n" +" pin_get = get the PIN for this device\n" +" pin_entered = PIN has been entered into registrar\n" +#endif " quit = exit wpa_cli\n"; static struct wpa_ctrl *ctrl_conn; @@ -438,6 +443,24 @@ static int wpa_cli_cmd_ft_ds(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int wpa_cli_cmd_pbc(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PBC"); +} + + +static int wpa_cli_cmd_pin_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PIN_GET"); +} + + +static int wpa_cli_cmd_pin_entered(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PIN_ENTERED"); +} + + static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char cmd[256]; @@ -1091,6 +1114,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "ap_scan", wpa_cli_cmd_ap_scan }, { "stkstart", wpa_cli_cmd_stkstart }, { "ft_ds", wpa_cli_cmd_ft_ds }, + { "pbc", wpa_cli_cmd_pbc }, + { "pin_get", wpa_cli_cmd_pin_get }, + { "pin_entered", wpa_cli_cmd_pin_entered }, { NULL, NULL } }; diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 59024d8..989bfae 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -22,11 +22,13 @@ #include "eapol_supp/eapol_supp_sm.h" #include "eap_peer/eap.h" #include "wpa.h" +#include "wps.h" #include "eloop.h" #include "drivers/driver.h" #include "config.h" #include "l2_packet/l2_packet.h" #include "wpa_supplicant_i.h" +#include "wps_i.h" #include "ctrl_iface.h" #include "ctrl_iface_dbus.h" #include "pcsc_funcs.h" @@ -278,6 +280,10 @@ void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s) eapol_conf.required_keys = 0; } } + if (WPS_CONFIG_METHOD_NONE != wps_sm_get_param(wpa_s->wps, + WPS_PARAM_CONFIG_METHOD)) { + eapol_conf.expect_fail = 1; + } if (wpa_s->conf) eapol_conf.fast_reauth = wpa_s->conf->fast_reauth; eapol_conf.workaround = ssid->eap_workaround; @@ -898,7 +904,9 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, struct wpa_scan_res *bss, struct wpa_ssid *ssid) { u8 wpa_ie[80]; + u8 wps_ie[SSID_MAX_WPS_IE_LEN]; size_t wpa_ie_len; + size_t wps_ie_len; int use_crypt, ret, i; int algs = AUTH_ALG_OPEN_SYSTEM; wpa_cipher cipher_pairwise, cipher_group; @@ -968,7 +976,16 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, } wpa_drv_set_auth_alg(wpa_s, algs); - if (bss && (wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) || + if ((ssid->proto & WPA_PROTO_WPS) && bss && + wpa_scan_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE)) { + cipher_pairwise = WPA_CIPHER_NONE; + cipher_group = WPA_CIPHER_NONE; + + wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, WPA_PROTO_WPS); + } else if (bss && (wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) || wpa_scan_get_ie(bss, WLAN_EID_RSN)) && (ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_IEEE8021X | @@ -1093,6 +1110,14 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_IEEE80211W */ + if (wpa_gen_wps_ie(wpa_s->wps, wpa_s->conf->uuid_e, wps_ie, &wps_ie_len)) { + wpa_printf(MSG_WARNING, "WPA: Failed to create WPS IE"); + return; + } + + params.wps_ie = wps_ie; + params.wps_ie_len = wps_ie_len; + if (wpa_s->use_client_mlme) ret = ieee80211_sta_associate(wpa_s, ¶ms); else diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index b639d39..12a68ec 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -142,6 +142,11 @@ fast_reauth=1 # Timeout for security association negotiation in seconds; default 60 #dot11RSNAConfigSATimeout=60 +# Universally Unique Identifier (UUID) for WPS +# Use a UUID from the system (if available) or generate one using an app +# such as uuidgen +#uuid=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + # network block # # Each network (usually AP's sharing the same SSID) is configured as a separate @@ -761,6 +766,11 @@ blob-base64-exampleblob={ SGVsbG8gV29ybGQhCg== } +# Example of EAP-WPS (aka Wifi Protected Setup) +network={ + proto=WPS + eap=WPS +} # Wildcard match for SSID (plaintext APs only). This example select any # open AP regardless of its SSID. diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 9afae2a..5e74514 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -305,6 +305,7 @@ struct wpa_supplicant { int interface_removed; /* whether the network interface has been * removed */ struct wpa_sm *wpa; + struct wps_sm *wps; struct eapol_sm *eapol; struct ctrl_iface_priv *ctrl_iface; diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c index f0c1cda..f0aa72a 100644 --- a/wpa_supplicant/wpas_glue.c +++ b/wpa_supplicant/wpas_glue.c @@ -15,13 +15,17 @@ #include "includes.h" #include "common.h" +#include "tlv.h" #include "eapol_supp/eapol_supp_sm.h" #include "wpa.h" +#include "wps.h" #include "eloop.h" #include "config.h" +#include "uuid.h" #include "l2_packet/l2_packet.h" #include "wpa_common.h" #include "wpa_supplicant_i.h" +#include "wps_i.h" #include "pmksa_cache.h" #include "mlme.h" #include "ieee802_11_defs.h" @@ -279,7 +283,22 @@ static void wpa_supplicant_notify_eapol_done(void *ctx) { struct wpa_supplicant *wpa_s = ctx; wpa_msg(wpa_s, MSG_DEBUG, "WPA: EAPOL processing complete"); - if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { + if (wps_sm_get_param(wpa_s->wps, WPS_PARAM_CONFIG_METHOD)) { + if (wpa_s->current_ssid && (wpa_s->current_ssid->eap.assoc_state == + WPS_ASSOCIATION_STATE_CONNECTION_SUCCESS)) { + wps_cancel_walktime_timeout(wpa_s->wps); + wpa_supplicant_wps_supp_get_config(wpa_s); + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_WPS_SUCCESS); + } else { + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_WPS_ERROR); + } + wpa_supplicant_cancel_auth_timeout(wpa_s); + + wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED); + wpa_s->reassociate = 1; + + wpa_supplicant_req_scan(wpa_s, 0, 0); + } else if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { wpa_supplicant_set_state(wpa_s, WPA_4WAY_HANDSHAKE); } else { wpa_supplicant_cancel_auth_timeout(wpa_s); @@ -605,6 +624,13 @@ int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s) "machine"); return -1; } + + wpa_s->wps = wps_sm_init(ctx); + if (wpa_s->wps == NULL) { + wpa_printf(MSG_ERROR, "Failed to initialize WPS state " + "machine"); + return -1; + } #endif /* CONFIG_NO_WPA */ return 0; @@ -629,3 +655,260 @@ void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s, } wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL); } + +int wpa_supplicant_wps_supp_set_config(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct wpa_config_blob *blob; + u8 *pos = NULL; + u16 auth = 0, encr = 0; + u8 uuid_e[WPS_UUID_LEN]; + + if (NULL == ssid) { + return -1; + } + + wpa_config_remove_blob(wpa_s->conf, WPS_BLOB_NAME_REQUIRE); + + blob = os_zalloc(sizeof(struct wpa_config_blob)); + if (NULL == blob) { + return -1; + } + blob->name = os_strdup(WPS_BLOB_NAME_REQUIRE); + + blob->len = TLV_BYTES(WPS_ELEM_LEN_NETWORK_INDEX) + + TLV_BYTES(WPS_ELEM_LEN_MAC_ADDRESS) + + TLV_BYTES(WPS_ELEM_LEN_AUTHENTICATION_TYPE_FLAG) + + TLV_BYTES(WPS_ELEM_LEN_ENCRYPTION_TYPE_FLAG) + + TLV_BYTES(WPS_ELEM_LEN_NETWORK_KEY); + + if (NULL == (blob->data = os_malloc(blob->len))) { + os_free(blob); + return -1; + } + + pos = blob->data; + pos = tlv_write_u8(pos, WPS_ELEM_NETWORK_INDEX, ssid->id & 0xff); + pos = tlv_write(pos, WPS_ELEM_MAC_ADDRESS, ETH_ALEN, + wpa_s->own_addr); + + if (wpa_s->conf->uuid_e) { + if (uuid_str2bin(wpa_s->conf->uuid_e, uuid_e)) { + wpa_printf(MSG_ERROR, "EAP-WPS: bad UUID format %s", + wpa_s->conf->uuid_e); + wpa_hexdump(MSG_DEBUG, "EAP-WPS:", uuid_e, WPS_UUID_LEN); + return -1; + } + } + pos = tlv_write(pos, WPS_ELEM_UUID_E, WPS_ELEM_LEN_UUID_E, uuid_e); + /* + * Borrow the vendor extension element to pass down the PIN. + * Note that this doesn't follow the WPS format + */ + pos = tlv_write(pos, WPS_ELEM_VENDOR_EXTENSION, WPS_PIN_LEN, + (u8 *)wps_sm_get_pin(wpa_s->wps)); + + if (WPA_KEY_MGMT_NONE & ssid->key_mgmt) + auth |= WPS_AUTHENTICATION_TYPE_OPEN; + if (WPA_KEY_MGMT_PSK & ssid->key_mgmt) { + if (WPA_PROTO_WPA & ssid->proto) + auth |= WPS_AUTHENTICATION_TYPE_WPAPSK; + if (WPA_PROTO_RSN & ssid->proto) + auth |= WPS_AUTHENTICATION_TYPE_WPA2PSK; + } + if (WPA_KEY_MGMT_IEEE8021X & ssid->key_mgmt) { + if (WPA_PROTO_WPA & ssid->proto) + auth |= WPS_AUTHENTICATION_TYPE_WPA; + if (WPA_PROTO_RSN & ssid->proto) + auth |= WPS_AUTHENTICATION_TYPE_WPA2; + } + pos = tlv_write_u16(pos, WPS_ELEM_AUTHENTICATION_TYPE_FLAG, auth); + + if ((WPA_CIPHER_NONE & ssid->pairwise_cipher) || + (WPA_CIPHER_NONE & ssid->group_cipher)) + encr |= WPS_ENCRYPTION_TYPE_NONE; + if (((WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104) & ssid->pairwise_cipher) || + ((WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104) & ssid->group_cipher)) + encr |= WPS_ENCRYPTION_TYPE_WEP; + if ((WPA_CIPHER_TKIP & ssid->pairwise_cipher) || + (WPA_CIPHER_TKIP & ssid->group_cipher)) + encr |= WPS_ENCRYPTION_TYPE_TKIP; + if ((WPA_CIPHER_CCMP & ssid->pairwise_cipher) || + (WPA_CIPHER_CCMP & ssid->group_cipher)) + encr |= WPS_ENCRYPTION_TYPE_AES; + pos = tlv_write_u16(pos, WPS_ELEM_ENCRYPTION_TYPE_FLAG, encr); + + pos = tlv_write(pos, WPS_ELEM_NETWORK_KEY, sizeof(ssid->psk), ssid->psk); + + wpa_config_set_blob(wpa_s->conf, blob); + + return 0; +} + + +/** + * wpa_supplicant_wps_supp_get_config - convert a WPS blob into wpa_ssid('s) + */ +int wpa_supplicant_wps_supp_get_config(struct wpa_supplicant *wpa_s) +{ + const struct wpa_config_blob *blob; + const u8 *pos; + const u8 *end; + u16 tlv_type, tlv_len; + u16 auth_type = 0; + u16 encryption_type = 0; + u8 *network_key = NULL; + size_t network_key_len = 0; + struct wpa_ssid *ssid = NULL; + void *tmp = NULL; + + blob = wpa_config_get_blob(wpa_s->conf, WPS_BLOB_NAME_RESULT); + if (NULL == blob) + return -1; + + pos = blob->data; + end = pos + blob->len; + + do { + tlv_type = TLV_GET_TYPE(pos); + tlv_len = TLV_GET_LEN(pos); + pos = TLV_GET_VAL(pos); + + switch (tlv_type) { + case WPS_ELEM_NETWORK_INDEX: + ssid = wpa_config_add_network(wpa_s->conf); + if (NULL == ssid) { + wpa_printf(MSG_ERROR, "Can't add network %s", __func__); + return -1; + } + wpa_config_set_network_defaults(ssid); + + ssid->disabled = 1; + ssid->proto = 0; + ssid->key_mgmt = 0; + ssid->pairwise_cipher = 0; + ssid->group_cipher = 0; + break; + case WPS_ELEM_SSID: + tmp = ssid->ssid; + ssid->ssid = os_realloc(ssid->ssid, tlv_len); + if (NULL != ssid->ssid) { + os_memcpy(ssid->ssid, pos, tlv_len); + ssid->ssid_len = tlv_len; + } else { + os_free(tmp); + ssid->ssid_len = 0; + } + break; + wpa_printf(MSG_WARNING, "EAP-WPS: SSID %s", + wpa_ssid_txt((u8 *)pos, tlv_len)); + case WPS_ELEM_AUTHENTICATION_TYPE_FLAG: + auth_type = WPA_GET_BE16(pos); + + if (auth_type & WPS_AUTHENTICATION_TYPE_Shared) { + ssid->auth_alg = WPA_AUTH_ALG_SHARED; + wpa_printf(MSG_DEBUG, "EAP-WPS: shared key authentication"); + } else { + ssid->auth_alg = WPA_AUTH_ALG_OPEN; + wpa_printf(MSG_DEBUG, "EAP-WPS: open system authentication"); + } + + if (auth_type & WPS_AUTHENTICATION_TYPE_OPEN) { + ssid->key_mgmt = WPA_KEY_MGMT_NONE; + } else { + if (auth_type & + (WPS_AUTHENTICATION_TYPE_WPAPSK | + WPS_AUTHENTICATION_TYPE_WPA)) { + ssid->proto |= WPA_PROTO_WPA; + wpa_printf(MSG_DEBUG, "EAP-WPS: protocol WPA"); + if (auth_type & WPS_AUTHENTICATION_TYPE_WPAPSK) { + ssid->key_mgmt |= WPA_KEY_MGMT_PSK; + wpa_printf(MSG_DEBUG, "EAP-WPS: key management PSK"); + } + } + + if (auth_type & + (WPS_AUTHENTICATION_TYPE_WPA2PSK | + WPS_AUTHENTICATION_TYPE_WPA2)) { + ssid->proto |= WPA_PROTO_RSN; + wpa_printf(MSG_DEBUG, "EAP-WPS: protocol RSN"); + if (auth_type & WPS_AUTHENTICATION_TYPE_WPA2PSK) { + ssid->key_mgmt |= WPA_KEY_MGMT_PSK; + wpa_printf(MSG_DEBUG, "EAP-WPS: key management PSK"); + } + } + } + break; + case WPS_ELEM_ENCRYPTION_TYPE_FLAG: + encryption_type = WPA_GET_BE16(pos); + + if (encryption_type & WPS_ENCRYPTION_TYPE_NONE) { + ssid->pairwise_cipher |= WPA_CIPHER_NONE; + ssid->group_cipher |= WPA_CIPHER_NONE; + wpa_printf(MSG_DEBUG, "EAP-WPS: cipher NONE"); + } + + if (encryption_type & WPS_ENCRYPTION_TYPE_WEP) { + ssid->pairwise_cipher |= WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104; + ssid->group_cipher |= WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104; + wpa_printf(MSG_DEBUG, "EAP-WPS: cipher WEP"); + } + + /* + * WPS has a protocol problem in that a Credential contains a + * single encryption type, but this doesn't account for AP that + * use different ciphers for group and pairwise keys. The safest + * approach is to enable both TKIP and CCMP, and let the upper + * layer sort things out based on the advertised WPA/RSN IE. + */ + if (encryption_type & WPS_ENCRYPTION_TYPE_TKIP || + encryption_type & WPS_ENCRYPTION_TYPE_AES) { + ssid->pairwise_cipher |= WPA_CIPHER_TKIP | WPA_CIPHER_CCMP; + ssid->group_cipher |= WPA_CIPHER_TKIP | WPA_CIPHER_CCMP; + wpa_printf(MSG_DEBUG, "EAP-WPS: cipher TKIP/CCMP"); + } + break; + case WPS_ELEM_NETWORK_KEY: + network_key = (u8 *)pos; + network_key_len = tlv_len; + + if (network_key) { + wpa_hexdump_key(MSG_DEBUG, "EAP-WPS: Network key", + network_key, network_key_len); + if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) { + if (network_key_len < WPS_WPAPSK_PASSPHRASE_LEN) { + size_t len = network_key_len; + + ssid->psk_set = 0; + os_free(ssid->passphrase); + ssid->passphrase = os_malloc(len +1); + if (NULL == ssid->passphrase) { + wpa_printf(MSG_DEBUG, "EAP-WPS: alloc failed for passphrase"); + } else { + os_memcpy(ssid->passphrase, network_key, len); + ssid->passphrase[len] = '\0'; + } + wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-WPS: passphrase", + (u8 *) ssid->passphrase, + os_strlen(ssid->passphrase)); + wpa_config_update_psk(ssid); + } else { + ssid->psk_set = 1; + hexstr2bin((const char *)network_key, ssid->psk, + PMK_LEN); + } + wpa_hexdump_key(MSG_DEBUG, "EAP-WPS: psk", ssid->psk, + PMK_LEN); + } else { + wpa_printf(MSG_ERROR, "EAP-WPS: key management not PSK"); + } + ssid->disabled = 0; + } + break; + } + + pos += tlv_len; + } while (pos < end); + + return 0; +} diff --git a/wpa_supplicant/wpas_glue.h b/wpa_supplicant/wpas_glue.h index b571e4d..f5ea4a4 100644 --- a/wpa_supplicant/wpas_glue.h +++ b/wpa_supplicant/wpas_glue.h @@ -19,5 +19,8 @@ int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s); int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s); void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +int wpa_supplicant_wps_supp_set_config(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +int wpa_supplicant_wps_supp_get_config(struct wpa_supplicant *wpa_s); #endif /* WPAS_GLUE_H */