aboutsummaryrefslogtreecommitdiffstats
path: root/wpa_supplicant
diff options
context:
space:
mode:
Diffstat (limited to 'wpa_supplicant')
-rw-r--r--wpa_supplicant/Makefile24
-rw-r--r--wpa_supplicant/config.c4
-rw-r--r--wpa_supplicant/config_file.c2
-rw-r--r--wpa_supplicant/config_winreg.c2
-rw-r--r--wpa_supplicant/ctrl_iface_dbus_handlers.c8
-rw-r--r--wpa_supplicant/defconfig3
-rw-r--r--wpa_supplicant/eapol_test.c1
-rw-r--r--wpa_supplicant/events.c172
-rw-r--r--wpa_supplicant/scan.c51
-rw-r--r--wpa_supplicant/wpa_supplicant.c22
-rw-r--r--wpa_supplicant/wpa_supplicant.conf2
-rw-r--r--wpa_supplicant/wpas_glue.c150
12 files changed, 435 insertions, 6 deletions
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index a835008..2a068d7 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -493,6 +493,26 @@ endif
NEED_SHA256=y
endif
+ifdef CONFIG_WPS
+# EAP-WSC
+ifeq ($(CONFIG_EAP_WSC), dyn)
+CFLAGS += -DCONFIG_WPS -DEAP_WSC_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_wsc.so
+else
+CFLAGS += -DCONFIG_WPS -DEAP_WSC
+OBJS += ../src/utils/uuid.o
+OBJS += ../src/eap_peer/eap_wsc.o ../src/eap_common/eap_wsc_common.o
+OBJS += ../src/wps/wps.o
+OBJS += ../src/wps/wps_common.o
+OBJS += ../src/wps/wps_dev_attr.o
+OBJS += ../src/wps/wps_enrollee.o
+OBJS += ../src/wps/wps_registrar.o
+OBJS_h += ../src/eap_server/eap_wsc.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+NEED_DH_GROUPS=y
+endif
+
ifdef CONFIG_EAP_IKEV2
# EAP-IKEv2
ifeq ($(CONFIG_EAP_IKEV2), dyn)
@@ -1080,6 +1100,10 @@ eap_sake.so: ../src/eap_peer/eap_sake.c ../src/eap_common/eap_sake_common.c
$(CC) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
-Deap_peer_sake_register=eap_peer_method_dynamic_init
+eap_wsc.so: ../src/eap_peer/eap_wsc.c ../src/eap_common/eap_wsc_common.c ../src/wps/wps.c
+ $(CC) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
+ -Deap_peer_wsc_register=eap_peer_method_dynamic_init
+
eap_ikev2.so: ../src/eap_peer/eap_ikev2.c ../src/eap_peer/ikev2.c ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.c
$(CC) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
-Deap_peer_ikev2_register=eap_peer_method_dynamic_init
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index c95e7a0..c5e300e 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -515,6 +515,10 @@ static int wpa_config_parse_key_mgmt(const struct parse_data *data,
else if (os_strcmp(start, "WPA-EAP-SHA256") == 0)
val |= WPA_KEY_MGMT_IEEE8021X_SHA256;
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_WPS
+ else if (os_strcmp(start, "WPS") == 0)
+ val |= WPA_KEY_MGMT_WPS;
+#endif /* CONFIG_WPS */
else {
wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
line, start);
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 359c5f1..3153214 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -871,6 +871,8 @@ int wpa_config_write(const char *name, struct wpa_config *config)
wpa_config_write_global(f, config);
for (ssid = config->ssid; ssid; ssid = ssid->next) {
+ if (ssid->key_mgmt == WPA_KEY_MGMT_WPS)
+ continue; /* do not save temporary WPS networks */
fprintf(f, "\nnetwork={\n");
wpa_config_write_network(f, ssid);
fprintf(f, "}\n");
diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c
index 4ec50f6..48e5c80 100644
--- a/wpa_supplicant/config_winreg.c
+++ b/wpa_supplicant/config_winreg.c
@@ -874,6 +874,8 @@ int wpa_config_write(const char *name, struct wpa_config *config)
wpa_config_delete_subkeys(hk, TEXT("networks"));
for (ssid = config->ssid, id = 0; ssid; ssid = ssid->next, id++) {
+ if (ssid->key_mgmt == WPA_KEY_MGMT_WPS)
+ continue; /* do not save temporary WPS networks */
if (wpa_config_write_network(hk, ssid, id))
errors++;
}
diff --git a/wpa_supplicant/ctrl_iface_dbus_handlers.c b/wpa_supplicant/ctrl_iface_dbus_handlers.c
index 62751ad..015eb54 100644
--- a/wpa_supplicant/ctrl_iface_dbus_handlers.c
+++ b/wpa_supplicant/ctrl_iface_dbus_handlers.c
@@ -420,6 +420,14 @@ DBusMessage * wpas_dbus_bssid_properties(DBusMessage *message,
goto error;
}
+ ie = wpa_scan_get_vendor_ie(res, WPS_IE_VENDOR_TYPE);
+ if (ie) {
+ if (!wpa_dbus_dict_append_byte_array(&iter_dict, "wpsie",
+ (const char *) ie,
+ ie[1] + 2))
+ goto error;
+ }
+
if (res->freq) {
if (!wpa_dbus_dict_append_int32(&iter_dict, "frequency",
res->freq))
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index abf2408..e62b9b3 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -170,6 +170,9 @@ CONFIG_EAP_LEAP=y
# EAP-TNC and related Trusted Network Connect support (experimental)
#CONFIG_EAP_TNC=y
+# Wi-Fi Protected Setup (WPS)
+#CONFIG_WPS=y
+
# EAP-IKEv2
#CONFIG_EAP_IKEV2=y
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index 4d96785..f650c85 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -336,6 +336,7 @@ static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s,
ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
#endif /* EAP_TLS_OPENSSL */
+ ctx->mac_addr = wpa_s->own_addr;
wpa_s->eapol = eapol_sm_init(ctx);
if (wpa_s->eapol == NULL) {
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 27dfd55..34b06e3 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -31,6 +31,7 @@
#include "ieee802_11_defs.h"
#include "blacklist.h"
#include "wpas_glue.h"
+#include "wps/wps.h"
static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
@@ -276,6 +277,53 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_ssid *ssid,
int proto_match = 0;
const u8 *rsn_ie, *wpa_ie;
+#ifdef CONFIG_WPS
+ if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
+ const u8 *wps_ie;
+ wps_ie = wpa_scan_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE);
+ if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
+ if (!wps_ie) {
+ wpa_printf(MSG_DEBUG, " skip - non-WPS AP");
+ return 0;
+ }
+
+ if (!wps_is_selected_pbc_registrar(wps_ie + 6,
+ wps_ie[1] - 4)) {
+ wpa_printf(MSG_DEBUG, " skip - WPS AP "
+ "without active PBC Registrar");
+ return 0;
+ }
+
+ /* TODO: overlap detection */
+ wpa_printf(MSG_DEBUG, " selected based on WPS IE "
+ "(Active PBC)");
+ return 1;
+ }
+
+ if (eap_is_wps_pin_enrollee(&ssid->eap)) {
+ if (!wps_ie) {
+ wpa_printf(MSG_DEBUG, " skip - non-WPS AP");
+ return 0;
+ }
+
+ if (!wps_is_selected_pin_registrar(wps_ie + 6,
+ wps_ie[1] - 4)) {
+ wpa_printf(MSG_DEBUG, " skip - WPS AP "
+ "without active PIN Registrar");
+ return 0;
+ }
+ wpa_printf(MSG_DEBUG, " selected based on WPS IE "
+ "(Active PIN)");
+ return 1;
+ }
+
+ if (wps_ie) {
+ wpa_printf(MSG_DEBUG, " selected based on WPS IE");
+ return 1;
+ }
+ }
+#endif /* CONFIG_WPS */
+
rsn_ie = wpa_scan_get_ie(bss, WLAN_EID_RSN);
while ((ssid->proto & WPA_PROTO_RSN) && rsn_ie) {
proto_match++;
@@ -364,6 +412,34 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_ssid *ssid,
}
+#ifdef CONFIG_WPS
+static int wps_ssid_wildcard_ok(struct wpa_ssid *ssid,
+ struct wpa_scan_res *bss)
+{
+ const u8 *wps_ie;
+
+ if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
+ wps_ie = wpa_scan_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE);
+ if (wps_ie &&
+ wps_is_selected_pbc_registrar(wps_ie + 6, wps_ie[1] - 4)) {
+ /* allow wildcard SSID for WPS PBC */
+ return 1;
+ }
+ }
+
+ if (eap_is_wps_pin_enrollee(&ssid->eap)) {
+ wps_ie = wpa_scan_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE);
+ if (wps_ie &&
+ wps_is_selected_pin_registrar(wps_ie + 6, wps_ie[1] - 4)) {
+ /* allow wildcard SSID for WPS PIN */
+ return 1;
+ }
+ }
+
+ return 0;
+}
+#endif /* CONFIG_WPS */
+
static struct wpa_scan_res *
wpa_supplicant_select_bss_wpa(struct wpa_supplicant *wpa_s,
struct wpa_ssid *group,
@@ -409,13 +485,22 @@ wpa_supplicant_select_bss_wpa(struct wpa_supplicant *wpa_s,
}
for (ssid = group; ssid; ssid = ssid->pnext) {
+ int check_ssid = 1;
+
if (ssid->disabled) {
wpa_printf(MSG_DEBUG, " skip - disabled");
continue;
}
- if (ssid_len != ssid->ssid_len ||
- os_memcmp(ssid_, ssid->ssid, ssid_len) != 0) {
+#ifdef CONFIG_WPS
+ if (ssid->ssid_len == 0 &&
+ wps_ssid_wildcard_ok(ssid, bss))
+ check_ssid = 0;
+#endif /* CONFIG_WPS */
+
+ if (check_ssid &&
+ (ssid_len != ssid->ssid_len ||
+ os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) {
wpa_printf(MSG_DEBUG, " skip - "
"SSID mismatch");
continue;
@@ -485,12 +570,26 @@ wpa_supplicant_select_bss_non_wpa(struct wpa_supplicant *wpa_s,
}
for (ssid = group; ssid; ssid = ssid->pnext) {
+ int check_ssid = ssid->ssid_len != 0;
+
if (ssid->disabled) {
wpa_printf(MSG_DEBUG, " skip - disabled");
continue;
}
- if (ssid->ssid_len != 0 &&
+#ifdef CONFIG_WPS
+ if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
+ /* Only allow wildcard SSID match if an AP
+ * advertises active WPS operation that matches
+ * with our mode. */
+ check_ssid = 1;
+ if (ssid->ssid_len == 0 &&
+ wps_ssid_wildcard_ok(ssid, bss))
+ check_ssid = 0;
+ }
+#endif /* CONFIG_WPS */
+
+ if (check_ssid &&
(ssid_len != ssid->ssid_len ||
os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) {
wpa_printf(MSG_DEBUG, " skip - "
@@ -507,6 +606,7 @@ wpa_supplicant_select_bss_non_wpa(struct wpa_supplicant *wpa_s,
}
if (!(ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
+ !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
!(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA))
{
wpa_printf(MSG_DEBUG, " skip - "
@@ -570,6 +670,65 @@ wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_ssid *group,
}
+#ifdef CONFIG_WPS
+
+static int wpa_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_res *selected,
+ struct wpa_ssid *ssid)
+{
+ const u8 *sel_uuid, *uuid;
+ size_t i;
+ const u8 *wps_ie;
+
+ if (!eap_is_wps_pbc_enrollee(&ssid->eap))
+ return 0;
+
+ /* Make sure that only one AP is in active PBC mode */
+ wps_ie = wpa_scan_get_vendor_ie(selected, WPS_IE_VENDOR_TYPE);
+ if (wps_ie)
+ sel_uuid = wps_get_uuid_e(wps_ie + 6, wps_ie[1] - 4);
+ else
+ sel_uuid = NULL;
+ if (!sel_uuid) {
+ wpa_printf(MSG_DEBUG, "WPS: UUID-E not "
+ "available for PBC overlap "
+ "detection");
+ return 1;
+ }
+
+ for (i = 0; i < wpa_s->scan_res->num; i++) {
+ struct wpa_scan_res *bss = wpa_s->scan_res->res[i];
+ if (bss == selected)
+ continue;
+ wps_ie = wpa_scan_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE);
+ if (!wps_ie)
+ continue;
+ if (!wps_is_selected_pbc_registrar(wps_ie + 6,
+ wps_ie[1] - 4))
+ continue;
+ uuid = wps_get_uuid_e(wps_ie + 6, wps_ie[1] - 4);
+ if (uuid == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: UUID-E not "
+ "available for PBC overlap "
+ "detection (other BSS)");
+ return 1;
+ }
+ if (os_memcmp(sel_uuid, uuid, 16) != 0)
+ return 1; /* PBC overlap */
+
+ /* TODO: verify that this is reasonable dual-band situation */
+ }
+
+ return 0;
+}
+
+#else /* CONFIG_WPS */
+
+#define wpa_scan_pbc_overlap(w, s, i) 0
+
+#endif /* CONFIG_WPS */
+
+
static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s)
{
int prio, timeout;
@@ -619,6 +778,13 @@ static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s)
}
if (selected) {
+ if (wpa_scan_pbc_overlap(wpa_s, selected, ssid)) {
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP
+ "PBC session overlap");
+ timeout = 10;
+ goto req_scan;
+ }
+
/* Do not trigger new association unless the BSSID has changed
* or if reassociation is requested. If we are in process of
* associating with the selected BSSID, do not trigger new
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 3c5bad9..04b6d44 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -20,6 +20,7 @@
#include "wpa_supplicant_i.h"
#include "mlme.h"
#include "uuid.h"
+#include "wps/wps.h"
static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s)
@@ -41,13 +42,45 @@ static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s)
}
+#ifdef CONFIG_WPS
+static int wpas_wps_in_use(struct wpa_config *conf, u8 *uuid)
+{
+ struct wpa_ssid *ssid;
+ int wps = 0;
+ const char *pos;
+
+ for (ssid = conf->ssid; ssid; ssid = ssid->next) {
+ if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
+ continue;
+
+ wps = 1;
+ if (!ssid->eap.phase1)
+ continue;
+
+ pos = os_strstr(ssid->eap.phase1, "uuid=");
+ if (pos)
+ uuid_str2bin(pos + 5, uuid);
+
+ if (os_strstr(ssid->eap.phase1, "pbc=1"))
+ return 2;
+ }
+
+ return wps;
+}
+#endif /* CONFIG_WPS */
+
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;
+ struct wpabuf *wps_ie = NULL;
const u8 *extra_ie = NULL;
size_t extra_ie_len = 0;
+ int wps = 0;
+#ifdef CONFIG_WPS
+ u8 uuid[UUID_LEN];
+#endif /* CONFIG_WPS */
if (wpa_s->disconnected && !wpa_s->scan_req)
return;
@@ -134,8 +167,12 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
} else
wpa_s->prev_scan_ssid = BROADCAST_SSID_SCAN;
+#ifdef CONFIG_WPS
+ wps = wpas_wps_in_use(wpa_s->conf, uuid);
+#endif /* CONFIG_WPS */
+
if (wpa_s->scan_res_tried == 0 && wpa_s->conf->ap_scan == 1 &&
- !wpa_s->use_client_mlme) {
+ !wpa_s->use_client_mlme && wps != 2) {
wpa_s->scan_res_tried++;
wpa_s->scan_req = scan_req;
wpa_printf(MSG_DEBUG, "Trying to get current scan results "
@@ -145,6 +182,16 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
return;
}
+#ifdef CONFIG_WPS
+ if (wps) {
+ wps_ie = wps_enrollee_build_probe_req_ie(wps == 2, uuid);
+ if (wps_ie) {
+ extra_ie = wpabuf_head(wps_ie);
+ extra_ie_len = wpabuf_len(wps_ie);
+ }
+ }
+#endif /* CONFIG_WPS */
+
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,
@@ -155,6 +202,8 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
ssid ? ssid->ssid_len : 0);
}
+ wpabuf_free(wps_ie);
+
if (ret) {
wpa_printf(MSG_WARNING, "Failed to initiate AP scan.");
wpa_supplicant_req_scan(wpa_s, 10, 0);
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index dba3f13..e9c9228 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -38,6 +38,7 @@
#include "ieee802_11_defs.h"
#include "blacklist.h"
#include "wpas_glue.h"
+#include "wps/wps.h"
const char *wpa_supplicant_version =
"wpa_supplicant v" VERSION_STR "\n"
@@ -282,7 +283,8 @@ void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s)
eapol_conf.workaround = ssid->eap_workaround;
eapol_conf.eap_disabled =
!wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) &&
- wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA;
+ wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
+ wpa_s->key_mgmt != WPA_KEY_MGMT_WPS;
eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
#endif /* IEEE8021X_EAPOL */
}
@@ -302,7 +304,9 @@ void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s,
{
int i;
- if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)
+ if (ssid->key_mgmt & WPA_KEY_MGMT_WPS)
+ wpa_s->key_mgmt = WPA_KEY_MGMT_WPS;
+ else if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)
wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA;
else
wpa_s->key_mgmt = WPA_KEY_MGMT_NONE;
@@ -635,6 +639,8 @@ static wpa_key_mgmt key_mgmt2driver(int key_mgmt)
return KEY_MGMT_802_1X_SHA256;
case WPA_KEY_MGMT_PSK_SHA256:
return KEY_MGMT_PSK_SHA256;
+ case WPA_KEY_MGMT_WPS:
+ return KEY_MGMT_WPS;
case WPA_KEY_MGMT_PSK:
default:
return KEY_MGMT_PSK;
@@ -1001,6 +1007,16 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
"results)");
return;
}
+#ifdef CONFIG_WPS
+ } else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
+ struct wpabuf *wps_ie = wps_enrollee_build_assoc_req_ie();
+ if (wps_ie && wpabuf_len(wps_ie) <= sizeof(wpa_ie)) {
+ wpa_ie_len = wpabuf_len(wps_ie);
+ os_memcpy(wpa_ie, wpabuf_head(wps_ie), wpa_ie_len);
+ }
+ wpabuf_free(wps_ie);
+ wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+#endif /* CONFIG_WPS */
} else {
wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
wpa_ie_len = 0;
@@ -1019,6 +1035,8 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
wep_keys_set = 1;
}
}
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS)
+ use_crypt = 0;
#ifdef IEEE8021X_EAPOL
if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index dd4f579..a4c1ca7 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -397,6 +397,8 @@ fast_reauth=1
# * 0 = do not use cryptobinding (default)
# * 1 = use cryptobinding if server supports it
# * 2 = require cryptobinding
+# EAP-WSC (WPS) uses following options: pin=<Device Password> and
+# uuid=<Device UUID>.
# phase2: Phase2 (inner authentication with TLS tunnel) parameters
# (string with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or
# "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS)
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index e1e2cd6..41159bb 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -25,6 +25,8 @@
#include "pmksa_cache.h"
#include "mlme.h"
#include "ieee802_11_defs.h"
+#include "wps/wps.h"
+#include "wps/wps_defs.h"
#include "wpa_ctrl.h"
#include "wpas_glue.h"
@@ -228,6 +230,19 @@ static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol, int success,
wpa_printf(MSG_DEBUG, "EAPOL authentication completed %ssuccessfully",
success ? "" : "un");
+#ifdef CONFIG_WPS
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid &&
+ !(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
+ wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - "
+ "try to associate with the received credential");
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ wpa_s->reassociate = 1;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ return;
+ }
+#endif /* CONFIG_WPS */
+
if (!success) {
/*
* Make sure we do not get stuck here waiting for long EAPOL
@@ -491,6 +506,139 @@ static int wpa_supplicant_send_ft_action(void *ctx, u8 action,
#endif /* CONFIG_NO_WPA */
+#ifdef CONFIG_WPS
+static int wpa_supplicant_wps_cred(void *ctx, struct wps_credential *cred)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ wpa_msg(wpa_s, MSG_INFO, "WPS: New credential received");
+
+ if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
+ wpa_printf(MSG_DEBUG, "WPS: Replace WPS network block based "
+ "on the received credential");
+ os_free(ssid->eap.identity);
+ ssid->eap.identity = NULL;
+ ssid->eap.identity_len = 0;
+ os_free(ssid->eap.phase1);
+ ssid->eap.phase1 = NULL;
+ os_free(ssid->eap.eap_methods);
+ ssid->eap.eap_methods = NULL;
+ } else {
+ wpa_printf(MSG_DEBUG, "WPS: Create a new network based on the "
+ "received credential");
+ ssid = wpa_config_add_network(wpa_s->conf);
+ if (ssid == NULL)
+ return -1;
+ }
+
+ wpa_config_set_network_defaults(ssid);
+
+ os_free(ssid->ssid);
+ ssid->ssid = os_malloc(cred->ssid_len);
+ if (ssid->ssid) {
+ os_memcpy(ssid->ssid, cred->ssid, cred->ssid_len);
+ ssid->ssid_len = cred->ssid_len;
+ }
+
+ switch (cred->encr_type) {
+ case WPS_ENCR_NONE:
+ ssid->pairwise_cipher = ssid->group_cipher = WPA_CIPHER_NONE;
+ break;
+ case WPS_ENCR_WEP:
+ ssid->pairwise_cipher = ssid->group_cipher =
+ WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104;
+ if (cred->key_len > 0 && cred->key_len <= MAX_WEP_KEY_LEN &&
+ cred->key_idx < NUM_WEP_KEYS) {
+ os_memcpy(ssid->wep_key[cred->key_idx], cred->key,
+ cred->key_len);
+ ssid->wep_key_len[cred->key_idx] = cred->key_len;
+ ssid->wep_tx_keyidx = cred->key_idx;
+ }
+ break;
+ case WPS_ENCR_TKIP:
+ ssid->pairwise_cipher = WPA_CIPHER_TKIP;
+ ssid->group_cipher = WPA_CIPHER_TKIP;
+ break;
+ case WPS_ENCR_AES:
+ ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+ ssid->group_cipher = WPA_CIPHER_CCMP | WPA_CIPHER_TKIP;
+ break;
+ }
+
+ switch (cred->auth_type) {
+ case WPS_AUTH_OPEN:
+ ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+ ssid->key_mgmt = WPA_KEY_MGMT_NONE;
+ ssid->proto = 0;
+ break;
+ case WPS_AUTH_SHARED:
+ ssid->auth_alg = WPA_AUTH_ALG_SHARED;
+ ssid->key_mgmt = WPA_KEY_MGMT_NONE;
+ ssid->proto = 0;
+ break;
+ case WPS_AUTH_WPAPSK:
+ ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+ ssid->key_mgmt = WPA_KEY_MGMT_PSK;
+ ssid->proto = WPA_PROTO_WPA;
+ break;
+ case WPS_AUTH_WPA:
+ ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+ ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+ ssid->proto = WPA_PROTO_WPA;
+ break;
+ case WPS_AUTH_WPA2:
+ ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+ ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+ ssid->proto = WPA_PROTO_RSN;
+ break;
+ case WPS_AUTH_WPA2PSK:
+ ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+ ssid->key_mgmt = WPA_KEY_MGMT_PSK;
+ ssid->proto = WPA_PROTO_RSN;
+ break;
+ }
+
+ if (ssid->key_mgmt == WPA_KEY_MGMT_PSK) {
+ if (cred->key_len == 2 * PMK_LEN) {
+ if (hexstr2bin((const char *) cred->key, ssid->psk,
+ PMK_LEN)) {
+ wpa_printf(MSG_ERROR, "WPS: Invalid Network "
+ "Key");
+ return -1;
+ }
+ ssid->psk_set = 1;
+ } else if (cred->key_len >= 8 && cred->key_len < 2 * PMK_LEN) {
+ os_free(ssid->passphrase);
+ ssid->passphrase = os_malloc(cred->key_len + 1);
+ if (ssid->passphrase == NULL)
+ return -1;
+ os_memcpy(ssid->passphrase, cred->key, cred->key_len);
+ ssid->passphrase[cred->key_len] = '\0';
+ wpa_config_update_psk(ssid);
+ } else {
+ wpa_printf(MSG_ERROR, "WPS: Invalid Network Key "
+ "length %lu",
+ (unsigned long) cred->key_len);
+ return -1;
+ }
+ }
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+ if (wpa_s->conf->update_config &&
+ wpa_config_write(wpa_s->confname, wpa_s->conf)) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to update configuration");
+ return -1;
+ }
+#endif /* CONFIG_NO_CONFIG_WRITE */
+
+ return 0;
+}
+#else /* CONFIG_WPS */
+#define wpa_supplicant_wps_cred NULL
+#endif /* CONFIG_WPS */
+
+
#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
static void wpa_supplicant_eap_param_needed(void *ctx, const char *field,
const char *txt)
@@ -554,6 +702,8 @@ int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s)
ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
#endif /* EAP_TLS_OPENSSL */
+ ctx->mac_addr = wpa_s->own_addr;
+ ctx->wps_cred = wpa_supplicant_wps_cred;
ctx->eap_param_needed = wpa_supplicant_eap_param_needed;
ctx->cb = wpa_supplicant_eapol_cb;
ctx->cb_ctx = wpa_s;