/*
* wpa_supplicant / WPS integration
- * Copyright (c) 2008-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "eloop.h"
#include "uuid.h"
+#include "crypto/random.h"
#include "crypto/dh_group5.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
#include "eap_common/eap_wsc_common.h"
#include "eap_peer/eap.h"
+#include "eapol_supp/eapol_supp_sm.h"
#include "rsn_supp/wpa.h"
+#include "wps/wps_attr_parse.h"
#include "config.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid &&
!(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
int disabled = wpa_s->current_ssid->disabled;
+ unsigned int freq = wpa_s->assoc_freq;
wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - "
- "try to associate with the received credential");
+ "try to associate with the received credential "
+ "(freq=%u)", freq);
wpa_supplicant_deauthenticate(wpa_s,
WLAN_REASON_DEAUTH_LEAVING);
if (disabled) {
return 1;
}
wpa_s->after_wps = 5;
- wpa_s->wps_freq = wpa_s->assoc_freq;
+ wpa_s->wps_freq = freq;
+ wpa_s->normal_scans = 0;
wpa_s->reassociate = 1;
wpa_supplicant_req_scan(wpa_s, 0, 0);
return 1;
if (wpa_drv_get_capa(wpa_s, &capa))
return; /* Unknown what driver supports */
+ if (ssid->ssid == NULL)
+ return;
bss = wpa_bss_get(wpa_s, cred->mac_addr, ssid->ssid, ssid->ssid_len);
if (bss == NULL) {
wpa_printf(MSG_DEBUG, "WPS: The AP was not found from BSS "
struct wpa_ssid *ssid = wpa_s->current_ssid;
u8 key_idx = 0;
u16 auth_type;
+#ifdef CONFIG_WPS_REG_DISABLE_OPEN
int registrar = 0;
+#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
if ((wpa_s->conf->wps_cred_processing == 1 ||
wpa_s->conf->wps_cred_processing == 2) && cred->cred_attr) {
if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
wpa_printf(MSG_DEBUG, "WPS: Replace WPS network block based "
"on the received credential");
+#ifdef CONFIG_WPS_REG_DISABLE_OPEN
if (ssid->eap.identity &&
ssid->eap.identity_len == WSC_ID_REGISTRAR_LEN &&
os_memcmp(ssid->eap.identity, WSC_ID_REGISTRAR,
WSC_ID_REGISTRAR_LEN) == 0)
registrar = 1;
+#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
os_free(ssid->eap.identity);
ssid->eap.identity = NULL;
ssid->eap.identity_len = 0;
ssid->eap.eap_methods = NULL;
if (!ssid->p2p_group)
ssid->temporary = 0;
+ ssid->bssid_set = 0;
} else {
wpa_printf(MSG_DEBUG, "WPS: Create a new network based on the "
"received credential");
}
wpas_clear_wps(wpa_s);
wpas_notify_wps_event_fail(wpa_s, fail);
+#ifdef CONFIG_P2P
+ wpas_p2p_wps_failed(wpa_s, fail);
+#endif /* CONFIG_P2P */
}
wpa_supplicant_wps_event_er_set_sel_reg(wpa_s,
&data->set_sel_reg);
break;
+ case WPS_EV_AP_PIN_SUCCESS:
+ break;
}
}
static void wpas_clear_wps(struct wpa_supplicant *wpa_s)
{
int id;
- struct wpa_ssid *ssid, *remove_ssid = NULL;
+ struct wpa_ssid *ssid, *remove_ssid = NULL, *prev_current;
+
+ prev_current = wpa_s->current_ssid;
eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
id = -1;
ssid = ssid->next;
if (id >= 0) {
+ if (prev_current == remove_ssid) {
+ wpa_sm_set_config(wpa_s->wpa, NULL);
+ eapol_sm_notify_config(wpa_s->eapol, NULL,
+ NULL);
+ }
wpas_notify_network_removed(wpa_s, remove_ssid);
wpa_config_remove_network(wpa_s->conf, id);
}
static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
- struct wpa_ssid *selected)
+ struct wpa_ssid *selected, const u8 *bssid)
{
struct wpa_ssid *ssid;
+ struct wpa_bss *bss;
+
+ wpa_s->known_wps_freq = 0;
+ if (bssid) {
+ bss = wpa_bss_get_bssid(wpa_s, bssid);
+ if (bss && bss->freq > 0) {
+ wpa_s->known_wps_freq = 1;
+ wpa_s->wps_freq = bss->freq;
+ }
+ }
+
+ if (wpa_s->current_ssid)
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_DEAUTH_LEAVING);
/* Mark all other networks disabled and trigger reassociation */
ssid = wpa_s->conf->ssid;
while (ssid) {
int was_disabled = ssid->disabled;
- ssid->disabled = ssid != selected;
- if (was_disabled != ssid->disabled)
- wpas_notify_network_enabled_changed(wpa_s, ssid);
+ /*
+ * In case the network object corresponds to a persistent group
+ * then do not send out network disabled signal. In addition,
+ * do not change disabled status of persistent network objects
+ * from 2 to 1 should we connect to another network.
+ */
+ if (was_disabled != 2) {
+ ssid->disabled = ssid != selected;
+ if (was_disabled != ssid->disabled)
+ wpas_notify_network_enabled_changed(wpa_s,
+ ssid);
+ }
ssid = ssid->next;
}
wpa_s->disconnected = 0;
wpa_s->reassociate = 1;
wpa_s->scan_runs = 0;
+ wpa_s->normal_scans = 0;
wpa_s->wps_success = 0;
wpa_s->blacklist_cleared = 0;
wpa_supplicant_req_scan(wpa_s, 0, 0);
ssid->eap.fragment_size = wpa_s->wps_fragment_size;
eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
wpa_s, NULL);
- wpas_wps_reassoc(wpa_s, ssid);
+ wpas_wps_reassoc(wpa_s, ssid, bssid);
return 0;
}
ssid->eap.fragment_size = wpa_s->wps_fragment_size;
eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
wpa_s, NULL);
- wpas_wps_reassoc(wpa_s, ssid);
+ wpas_wps_reassoc(wpa_s, ssid, bssid);
return rpin;
}
}
#endif /* CONFIG_AP */
- if (wpa_s->wpa_state == WPA_SCANNING) {
+ if (wpa_s->wpa_state == WPA_SCANNING ||
+ wpa_s->wpa_state == WPA_DISCONNECTED) {
wpa_printf(MSG_DEBUG, "WPS: Cancel operation - cancel scan");
wpa_supplicant_cancel_scan(wpa_s);
wpas_clear_wps(wpa_s);
ssid->eap.fragment_size = wpa_s->wps_fragment_size;
eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
wpa_s, NULL);
- wpas_wps_reassoc(wpa_s, ssid);
+ wpas_wps_reassoc(wpa_s, ssid, bssid);
return 0;
}
while (first && first->next)
first = first->next;
if (first && first != wpa_s) {
- os_memcpy(wps->uuid, wpa_s->global->ifaces->wps->uuid,
- WPS_UUID_LEN);
+ if (wps != wpa_s->global->ifaces->wps)
+ os_memcpy(wps->uuid,
+ wpa_s->global->ifaces->wps->uuid,
+ WPS_UUID_LEN);
wpa_hexdump(MSG_DEBUG, "WPS: UUID from the first "
"interface", wps->uuid, WPS_UUID_LEN);
} else {
}
+static void wpas_wps_set_vendor_ext_m1(struct wpa_supplicant *wpa_s,
+ struct wps_context *wps)
+{
+ wpabuf_free(wps->dev.vendor_ext_m1);
+ wps->dev.vendor_ext_m1 = NULL;
+
+ if (wpa_s->conf->wps_vendor_ext_m1) {
+ wps->dev.vendor_ext_m1 =
+ wpabuf_dup(wpa_s->conf->wps_vendor_ext_m1);
+ if (!wps->dev.vendor_ext_m1) {
+ wpa_printf(MSG_ERROR, "WPS: Cannot "
+ "allocate memory for vendor_ext_m1");
+ }
+ }
+}
+
+
int wpas_wps_init(struct wpa_supplicant *wpa_s)
{
struct wps_context *wps;
struct wps_registrar_config rcfg;
- int i;
+ struct hostapd_hw_modes *modes;
+ u16 m;
wps = os_zalloc(sizeof(*wps));
if (wps == NULL)
return -1;
}
wps->config_methods = wps_fix_config_methods(wps->config_methods);
- if (wpa_s->conf->device_type &&
- wps_dev_type_str2bin(wpa_s->conf->device_type,
- wps->dev.pri_dev_type) < 0) {
- wpa_printf(MSG_ERROR, "WPS: Invalid device_type");
- os_free(wps);
- return -1;
- }
+ wps->dev.config_methods = wps->config_methods;
+ os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type,
+ WPS_DEV_TYPE_LEN);
- for (i = 0; i < MAX_SEC_DEVICE_TYPES; i++) {
- if (wpa_s->conf->sec_device_type[i] == NULL)
- continue;
- if (wps_dev_type_str2bin(
- wpa_s->conf->sec_device_type[i],
- wps->dev.sec_dev_type[wps->dev.num_sec_dev_types])
- < 0) {
- wpa_printf(MSG_ERROR, "WPS: Invalid sec_device_type");
- return -1;
- }
- wps->dev.num_sec_dev_types++;
- if (wps->dev.num_sec_dev_types == WPS_SEC_DEVICE_TYPES)
- break;
- }
+ wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
+ os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type,
+ WPS_DEV_TYPE_LEN * wps->dev.num_sec_dev_types);
+
+ wpas_wps_set_vendor_ext_m1(wpa_s, wps);
wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
- wps->dev.rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ; /* TODO: config */
+ modes = wpa_s->hw.modes;
+ if (modes) {
+ for (m = 0; m < wpa_s->hw.num_modes; m++) {
+ if (modes[m].mode == HOSTAPD_MODE_IEEE80211B ||
+ modes[m].mode == HOSTAPD_MODE_IEEE80211G)
+ wps->dev.rf_bands |= WPS_RF_24GHZ;
+ else if (modes[m].mode == HOSTAPD_MODE_IEEE80211A)
+ wps->dev.rf_bands |= WPS_RF_50GHZ;
+ }
+ }
+ if (wps->dev.rf_bands == 0) {
+ /*
+ * Default to claiming support for both bands if the driver
+ * does not provide support for fetching supported bands.
+ */
+ wps->dev.rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ;
+ }
os_memcpy(wps->dev.mac_addr, wpa_s->own_addr, ETH_ALEN);
wpas_wps_set_uuid(wpa_s, wps);
wpabuf_free(wpa_s->wps->dh_privkey);
wpabuf_free(wpa_s->wps->oob_conf.pubkey_hash);
wpabuf_free(wpa_s->wps->oob_conf.dev_password);
+ wpabuf_free(wpa_s->wps->dev.vendor_ext_m1);
os_free(wpa_s->wps->network_key);
os_free(wpa_s->wps);
wpa_s->wps = NULL;
}
+#ifdef CONFIG_WPS_NFC
+struct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s,
+ int ndef, const char *uuid)
+{
+ struct wpabuf *ret;
+ u8 u[UUID_LEN];
+
+ if (!wpa_s->wps_er)
+ return NULL;
+
+ if (uuid_str2bin(uuid, u))
+ return NULL;
+
+ ret = wps_er_nfc_config_token(wpa_s->wps_er, u);
+ if (ndef && ret) {
+ struct wpabuf *tmp;
+ tmp = ndef_build_wifi(ret);
+ wpabuf_free(ret);
+ if (tmp == NULL)
+ return NULL;
+ ret = tmp;
+ }
+
+ return ret;
+}
+#endif /* CONFIG_WPS_NFC */
+
+
static int callbacks_pending = 0;
static void wpas_wps_terminate_cb(void *ctx)
}
wps->config_methods = wps_fix_config_methods(wps->config_methods);
- if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE) {
- if (wpa_s->conf->device_type &&
- wps_dev_type_str2bin(wpa_s->conf->device_type,
- wps->dev.pri_dev_type) < 0)
- wpa_printf(MSG_ERROR, "WPS: Invalid device_type");
- }
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE)
+ os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type,
+ WPS_DEV_TYPE_LEN);
if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE) {
- int i;
-
- wps->dev.num_sec_dev_types = 0;
-
- for (i = 0; i < MAX_SEC_DEVICE_TYPES; i++) {
- if (wpa_s->conf->sec_device_type[i] == NULL)
- continue;
- if (wps_dev_type_str2bin(
- wpa_s->conf->sec_device_type[i],
- wps->dev.sec_dev_type[
- wps->dev.num_sec_dev_types]) < 0) {
- wpa_printf(MSG_ERROR,
- "WPS: Invalid sec_device_type");
- continue;
- }
- wps->dev.num_sec_dev_types++;
- if (wps->dev.num_sec_dev_types == WPS_SEC_DEVICE_TYPES)
- break;
- }
+ wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
+ os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type,
+ wps->dev.num_sec_dev_types * WPS_DEV_TYPE_LEN);
}
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_VENDOR_EXTENSION)
+ wpas_wps_set_vendor_ext_m1(wpa_s, wps);
+
if (wpa_s->conf->changed_parameters & CFG_CHANGED_OS_VERSION)
wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
wps->dev.serial_number = wpa_s->conf->serial_number;
}
}
+
+
+#ifdef CONFIG_WPS_NFC
+
+struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef)
+{
+ struct wpabuf *priv = NULL, *pub = NULL, *pw;
+ void *dh_ctx;
+ struct wpabuf *ret;
+
+ pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN);
+ if (pw == NULL)
+ return NULL;
+
+ if (random_get_bytes(wpabuf_put(pw, WPS_OOB_DEVICE_PASSWORD_LEN),
+ WPS_OOB_DEVICE_PASSWORD_LEN)) {
+ wpabuf_free(pw);
+ return NULL;
+ }
+
+ dh_ctx = dh5_init(&priv, &pub);
+ if (dh_ctx == NULL) {
+ wpabuf_free(pw);
+ return NULL;
+ }
+ dh5_free(dh_ctx);
+
+ wpa_s->conf->wps_nfc_dev_pw_id = 0x10 + os_random() % 0xfff0;
+ wpabuf_free(wpa_s->conf->wps_nfc_dh_pubkey);
+ wpa_s->conf->wps_nfc_dh_pubkey = pub;
+ wpabuf_free(wpa_s->conf->wps_nfc_dh_privkey);
+ wpa_s->conf->wps_nfc_dh_privkey = priv;
+ wpabuf_free(wpa_s->conf->wps_nfc_dev_pw);
+ wpa_s->conf->wps_nfc_dev_pw = pw;
+
+ ret = wps_build_nfc_pw_token(wpa_s->conf->wps_nfc_dev_pw_id,
+ wpa_s->conf->wps_nfc_dh_pubkey,
+ wpa_s->conf->wps_nfc_dev_pw);
+ if (ndef && ret) {
+ struct wpabuf *tmp;
+ tmp = ndef_build_wifi(ret);
+ wpabuf_free(ret);
+ if (tmp == NULL)
+ return NULL;
+ ret = tmp;
+ }
+
+ return ret;
+}
+
+
+int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ struct wps_context *wps = wpa_s->wps;
+ char pw[32 * 2 + 1];
+
+ if (wpa_s->conf->wps_nfc_dh_pubkey == NULL ||
+ wpa_s->conf->wps_nfc_dh_privkey == NULL ||
+ wpa_s->conf->wps_nfc_dev_pw == NULL)
+ return -1;
+
+ dh5_free(wps->dh_ctx);
+ wpabuf_free(wps->dh_pubkey);
+ wpabuf_free(wps->dh_privkey);
+ wps->dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
+ wps->dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey);
+ if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) {
+ wps->dh_ctx = NULL;
+ wpabuf_free(wps->dh_pubkey);
+ wps->dh_pubkey = NULL;
+ wpabuf_free(wps->dh_privkey);
+ wps->dh_privkey = NULL;
+ return -1;
+ }
+ wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey);
+ if (wps->dh_ctx == NULL)
+ return -1;
+
+ wpa_snprintf_hex_uppercase(pw, sizeof(pw),
+ wpabuf_head(wpa_s->conf->wps_nfc_dev_pw),
+ wpabuf_len(wpa_s->conf->wps_nfc_dev_pw));
+ return wpas_wps_start_pin(wpa_s, bssid, pw, 0,
+ wpa_s->conf->wps_nfc_dev_pw_id);
+}
+
+
+static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s,
+ struct wps_parse_attr *attr)
+{
+ if (wps_oob_use_cred(wpa_s->wps, attr) < 0)
+ return -1;
+
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network "
+ "based on the received credential added");
+ wpa_s->normal_scans = 0;
+ wpa_supplicant_reinit_autoscan(wpa_s);
+ wpa_s->disconnected = 0;
+ wpa_s->reassociate = 1;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+ return 0;
+}
+
+
+static int wpas_wps_add_nfc_password_token(struct wpa_supplicant *wpa_s,
+ struct wps_parse_attr *attr)
+{
+ return wps_registrar_add_nfc_password_token(
+ wpa_s->wps->registrar, attr->oob_dev_password,
+ attr->oob_dev_password_len);
+}
+
+
+static int wpas_wps_nfc_tag_process(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *wps)
+{
+ struct wps_parse_attr attr;
+
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps);
+
+ if (wps_parse_msg(wps, &attr)) {
+ wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag");
+ return -1;
+ }
+
+ if (attr.num_cred)
+ return wpas_wps_use_cred(wpa_s, &attr);
+
+#ifdef CONFIG_WPS_ER
+ if (attr.oob_dev_password)
+ return wpas_wps_add_nfc_password_token(wpa_s, &attr);
+#endif /* CONFIG_WPS_ER */
+
+ wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag");
+ return -1;
+}
+
+
+int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *data)
+{
+ const struct wpabuf *wps = data;
+ struct wpabuf *tmp = NULL;
+ int ret;
+
+ if (wpabuf_len(data) < 4)
+ return -1;
+
+ if (*wpabuf_head_u8(data) != 0x10) {
+ /* Assume this contains full NDEF record */
+ tmp = ndef_parse_wifi(data);
+ if (tmp == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF");
+ return -1;
+ }
+ wps = tmp;
+ }
+
+ ret = wpas_wps_nfc_tag_process(wpa_s, wps);
+ wpabuf_free(tmp);
+ return ret;
+}
+
+#endif /* CONFIG_WPS_NFC */