aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--hostapd/ChangeLog11
-rw-r--r--hostapd/Makefile15
-rw-r--r--hostapd/README-WPS173
-rw-r--r--hostapd/ap.h2
-rw-r--r--hostapd/beacon.c19
-rw-r--r--hostapd/config.c74
-rw-r--r--hostapd/config.h16
-rw-r--r--hostapd/ctrl_iface.c21
-rw-r--r--hostapd/defconfig3
-rw-r--r--hostapd/driver.h25
-rw-r--r--hostapd/driver_hostap.c41
-rw-r--r--hostapd/driver_madwifi.c118
-rw-r--r--hostapd/driver_test.c107
-rw-r--r--hostapd/eapol_sm.c2
-rw-r--r--hostapd/eapol_sm.h1
-rw-r--r--hostapd/hostapd.c88
-rw-r--r--hostapd/hostapd.conf79
-rw-r--r--hostapd/hostapd.h9
-rw-r--r--hostapd/hostapd_cli.c31
-rw-r--r--hostapd/ieee802_11.c15
-rw-r--r--hostapd/ieee802_1x.c41
-rw-r--r--hostapd/wpa.c10
-rw-r--r--hostapd/wps_hostapd.c604
-rw-r--r--hostapd/wps_hostapd.h48
-rw-r--r--src/Makefile2
-rw-r--r--src/common/defs.h4
-rw-r--r--src/common/ieee802_11_common.c5
-rw-r--r--src/common/ieee802_11_common.h2
-rw-r--r--src/common/wpa_ctrl.h6
-rw-r--r--src/drivers/Apple80211.h2
-rw-r--r--src/drivers/driver.h5
-rw-r--r--src/drivers/driver_test.c47
-rw-r--r--src/eap_common/eap_wsc_common.c39
-rw-r--r--src/eap_common/eap_wsc_common.h33
-rw-r--r--src/eap_peer/eap.c27
-rw-r--r--src/eap_peer/eap.h17
-rw-r--r--src/eap_peer/eap_config.h3
-rw-r--r--src/eap_peer/eap_methods.c7
-rw-r--r--src/eap_peer/eap_wsc.c552
-rw-r--r--src/eap_server/eap.c1
-rw-r--r--src/eap_server/eap.h1
-rw-r--r--src/eap_server/eap_i.h1
-rw-r--r--src/eap_server/eap_methods.c7
-rw-r--r--src/eap_server/eap_wsc.c461
-rw-r--r--src/eapol_supp/eapol_supp_sm.c16
-rw-r--r--src/eapol_supp/eapol_supp_sm.h18
-rw-r--r--src/radius/radius_server.c3
-rw-r--r--src/radius/radius_server.h1
-rw-r--r--src/rsn_supp/preauth.c1
-rw-r--r--src/wps/Makefile6
-rw-r--r--src/wps/wps.c158
-rw-r--r--src/wps/wps.h132
-rw-r--r--src/wps/wps_common.c839
-rw-r--r--src/wps/wps_defs.h294
-rw-r--r--src/wps/wps_dev_attr.c316
-rw-r--r--src/wps/wps_dev_attr.h29
-rw-r--r--src/wps/wps_enrollee.c1454
-rw-r--r--src/wps/wps_i.h184
-rw-r--r--src/wps/wps_registrar.c1982
-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
71 files changed, 8602 insertions, 47 deletions
diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog
index 8ee6d7b..0cadf18 100644
--- a/hostapd/ChangeLog
+++ b/hostapd/ChangeLog
@@ -1,5 +1,16 @@
ChangeLog for hostapd
+????-??-?? - v0.6.7
+ * added support for Wi-Fi Protected Setup (WPS)
+ (hostapd can now be configured to act as an integrated WPS Registrar
+ and provision credentials for WPS Enrollees using PIN and PBC
+ methods; external WPS Registrars are not supported); WPS support can
+ be enabled by adding CONFIG_WPS=y into .config and setting the
+ runtime configuration variables in hostapd.conf (see WPS section in
+ the example configuration file); new hostapd_cli commands wps_pin and
+ wps_pbc are used to configuration WPS negotiation; see README-WPS for
+ more details
+
2008-11-23 - v0.6.6
* added a new configuration option, wpa_ptk_rekey, that can be used to
enforce frequent PTK rekeying, e.g., to mitigate some attacks against
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 4a39ee7..d1c42d6 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -258,6 +258,21 @@ TLS_FUNCS=y
NEED_T_PRF=y
endif
+ifdef CONFIG_WPS
+CFLAGS += -DCONFIG_WPS -DEAP_WSC
+OBJS += ../src/utils/uuid.o
+OBJS += wps_hostapd.o
+OBJS += ../src/eap_server/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
+NEED_DH_GROUPS=y
+NEED_SHA256=y
+NEED_CRYPTO=y
+endif
+
ifdef CONFIG_EAP_IKEV2
CFLAGS += -DEAP_IKEV2
OBJS += ../src/eap_server/eap_ikev2.o ../src/eap_server/ikev2.o
diff --git a/hostapd/README-WPS b/hostapd/README-WPS
new file mode 100644
index 0000000..5e8a68d
--- /dev/null
+++ b/hostapd/README-WPS
@@ -0,0 +1,173 @@
+hostapd and Wi-Fi Protected Setup (WPS)
+=======================================
+
+This document describes how the WPS implementation in hostapd can be
+configured and how an external component on an AP (e.g., web UI) is
+used to enable enrollment of client devices.
+
+
+Introduction to WPS
+-------------------
+
+Wi-Fi Protected Setup (WPS) is a mechanism for easy configuration of a
+wireless network. It allows automated generation of random keys (WPA
+passphrase/PSK) and configuration of an access point and client
+devices. WPS includes number of methods for setting up connections
+with PIN method and push-button configuration (PBC) being the most
+commonly deployed options.
+
+While WPS can enable more home networks to use encryption in the
+wireless network, it should be noted that the use of the PIN and
+especially PBC mechanisms for authenticating the initial key setup is
+not very secure. As such, use of WPS may not be suitable for
+environments that require secure network access without chance for
+allowing outsiders to gain access during the setup phase.
+
+WPS uses following terms to describe the entities participating in the
+network setup:
+- access point: the WLAN access point
+- Registrar: a device that control a network and can authorize
+ addition of new devices); this may be either in the AP ("internal
+ Registrar") or in an external device, e.g., a laptop, ("external
+ Registrar")
+- Enrollee: a device that is being authorized to use the network
+
+It should also be noted that the AP and a client device may change
+roles (i.e., AP acts as an Enrollee and client device as a Registrar)
+when WPS is used to configure the access point.
+
+
+More information about WPS is available from Wi-Fi Alliance:
+http://www.wi-fi.org/wifi-protected-setup
+
+
+hostapd implementation
+----------------------
+
+hostapd includes an optional WPS component that can be used as an
+internal WPS Registrar to manage addition of new WPS enabled clients
+to the network. In addition, WPS Enrollee functionality in hostapd can
+be used to allow external WPS Registrars to configure the access
+point, e.g., for initial network setup. The current version of hostapd
+does not support use of external WPS Registrars for adding new client
+devices.
+
+
+hostapd configuration
+---------------------
+
+WPS is an optional component that needs to be enabled in hostapd build
+configuration (.config). Here is an example configuration that
+includes WPS support and uses madwifi driver interface:
+
+CONFIG_DRIVER_MADWIFI=y
+CFLAGS += -I/usr/src/madwifi-0.9.3
+CONFIG_EAP=y
+CONFIG_WPS=y
+
+
+Following section shows an example runtime configuration
+(hostapd.conf) that enables WPS:
+
+# Configure the driver and network interface
+driver=madwifi
+interface=ath0
+
+# WPA2-Personal configuration for the AP
+ssid=wps-test
+wpa=2
+wpa_key_mgmt=WPA-PSK
+wpa_pairwise=CCMP
+# Default WPA passphrase for legacy (non-WPS) clients
+wpa_passphrase=12345678
+# Enable random per-device PSK generation for WPS clients
+# Please note that the file has to exists for hostapd to start (i.e., create an
+# empty file as a starting point).
+wpa_psk_file=/etc/hostapd.psk
+
+# Enable control interface for PBC/PIN entry
+ctrl_interface=/var/run/hostapd
+
+# Enable internal EAP server for EAP-WSC (part of Wi-Fi Protected Setup)
+eap_server=1
+
+# WPS configuration (AP configured, do not allow external WPS Registrars)
+wps_state=2
+ap_setup_locked=1
+uuid=87654321-9abc-def0-1234-56789abc0000
+wps_pin_requests=/var/run/hostapd.pin-req
+device_name=Wireless AP
+manufacturer=Company
+model_name=WAP
+model_number=123
+serial_number=12345
+device_type=6-0050F204-1
+os_version=01020300
+config_methods=label display push_button keypad
+
+
+External operations
+-------------------
+
+WPS requires either a device PIN code (usually, 8-digit number) or a
+pushbutton event (for PBC) to allow a new WPS Enrollee to join the
+network. hostapd uses the control interface as an input channel for
+these events.
+
+When a client device (WPS Enrollee) connects to hostapd (WPS
+Registrar) in order to start PIN mode negotiation for WPS, an
+identifier (Enrollee UUID) is sent. hostapd will need to be configured
+with a device password (PIN) for this Enrollee. This is an operation
+that requires user interaction (assuming there are no pre-configured
+PINs on the AP for a set of Enrollee).
+
+The PIN request with information about the device is appended to the
+wps_pin_requests file (/var/run/hostapd.pin-req in this example). In
+addition, hostapd control interface event is sent as a notification of
+a new device. The AP could use, e.g., a web UI for showing active
+Enrollees to the user and request a PIN for an Enrollee.
+
+The PIN request file has one line for every Enrollee that connected to
+the AP, but for which there was no PIN. Following information is
+provided for each Enrollee (separated with tabulators):
+- timestamp (seconds from 1970-01-01)
+- Enrollee UUID
+- MAC address
+- Device name
+- Manufacturer
+- Model Name
+- Model Number
+- Serial Number
+- Device category
+
+Example line in the /var/run/hostapd.pin-req file:
+1200188391 53b63a98-d29e-4457-a2ed-094d7e6a669c Intel(R) Centrino(R) Intel Corporation Intel(R) Centrino(R) - - 1-0050F204-1
+
+
+When the user enters a PIN for a pending Enrollee, e.g., on the web
+UI), hostapd needs to be notified of the new PIN over the control
+interface. This can be done either by using the UNIX domain socket
+-based control interface directly (src/common/wpa_ctrl.c provides
+helper functions for using the interface) or by calling hostapd_cli.
+
+Example command to add a PIN (12345670) for an Enrollee:
+
+hostapd_cli wps_pin 53b63a98-d29e-4457-a2ed-094d7e6a669c 12345670
+
+After this, the Enrollee can connect to the AP again and complete WPS
+negotiation. At that point, a new, random WPA PSK is generated for the
+client device and the client can then use that key to connect to the
+AP to access the network.
+
+
+If the AP includes a pushbutton, WPS PBC mode can be used. It is
+enabled by pushing a button on both the AP and the client at about the
+same time (2 minute window). hostapd needs to be notified about the AP
+button pushed event over the control interface, e.g., by calling
+hostapd_cli:
+
+hostapd_cli wps_pbc
+
+At this point, the client has two minutes to complete WPS negotiation
+which will generate a new WPA PSK in the same way as the PIN method
+described above.
diff --git a/hostapd/ap.h b/hostapd/ap.h
index 340b97c..f6596fe 100644
--- a/hostapd/ap.h
+++ b/hostapd/ap.h
@@ -33,6 +33,8 @@
#define WLAN_STA_WME BIT(9)
#define WLAN_STA_MFP BIT(10)
#define WLAN_STA_HT BIT(11)
+#define WLAN_STA_WPS BIT(12)
+#define WLAN_STA_MAYBE_WPS BIT(13)
#define WLAN_STA_NONERP BIT(31)
/* Maximum number of supported rates (from both Supported Rates and Extended
diff --git a/hostapd/beacon.c b/hostapd/beacon.c
index b720489..a464db7 100644
--- a/hostapd/beacon.c
+++ b/hostapd/beacon.c
@@ -27,6 +27,7 @@
#include "hw_features.h"
#include "driver.h"
#include "sta_info.h"
+#include "wps_hostapd.h"
static u8 ieee802_11_erp_info(struct hostapd_data *hapd)
@@ -145,6 +146,8 @@ void handle_probe_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt,
ie = mgmt->u.probe_req.variable;
ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
+ hostapd_wps_probe_req_rx(hapd, mgmt->sa, ie, ie_len);
+
if (!hapd->iconf->send_probe_response)
return;
@@ -243,6 +246,14 @@ void handle_probe_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt,
pos = hostapd_eid_ht_capabilities_info(hapd, pos);
pos = hostapd_eid_ht_operation(hapd, pos);
+#ifdef CONFIG_WPS
+ if (hapd->conf->wps_state && hapd->wps_probe_resp_ie) {
+ os_memcpy(pos, hapd->wps_probe_resp_ie,
+ hapd->wps_probe_resp_ie_len);
+ pos += hapd->wps_probe_resp_ie_len;
+ }
+#endif /* CONFIG_WPS */
+
if (hostapd_send_mgmt_frame(hapd, resp, pos - (u8 *) resp, 0) < 0)
perror("handle_probe_req: send");
@@ -349,6 +360,14 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
}
#endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_WPS
+ if (hapd->conf->wps_state && hapd->wps_beacon_ie) {
+ os_memcpy(tailpos, hapd->wps_beacon_ie,
+ hapd->wps_beacon_ie_len);
+ tailpos += hapd->wps_beacon_ie_len;
+ }
+#endif /* CONFIG_WPS */
+
tail_len = tailpos > tail ? tailpos - tail : 0;
if (hostapd_set_beacon(hapd->conf->iface, hapd, (u8 *) head, head_len,
diff --git a/hostapd/config.c b/hostapd/config.c
index 2e890b4..ec5b0b6 100644
--- a/hostapd/config.c
+++ b/hostapd/config.c
@@ -26,6 +26,7 @@
#include "wpa_common.h"
#include "wpa.h"
#include "uuid.h"
+#include "eap_common/eap_wsc_common.h"
#define MAX_STA_COUNT 2007
@@ -2023,6 +2024,44 @@ struct hostapd_config * hostapd_config_read(const char *fname)
bss->max_listen_interval = atoi(pos);
} else if (os_strcmp(buf, "okc") == 0) {
bss->okc = atoi(pos);
+#ifdef CONFIG_WPS
+ } else if (os_strcmp(buf, "wps_state") == 0) {
+ bss->wps_state = atoi(pos);
+ if (bss->wps_state < 0 || bss->wps_state > 2) {
+ printf("Line %d: invalid wps_state\n", line);
+ errors++;
+ }
+ } else if (os_strcmp(buf, "ap_setup_locked") == 0) {
+ bss->ap_setup_locked = atoi(pos);
+ } else if (os_strcmp(buf, "uuid") == 0) {
+ if (uuid_str2bin(pos, bss->uuid)) {
+ printf("Line %d: invalid UUID\n", line);
+ errors++;
+ }
+ } else if (os_strcmp(buf, "wps_pin_requests") == 0) {
+ bss->wps_pin_requests = os_strdup(pos);
+ } else if (os_strcmp(buf, "device_name") == 0) {
+ bss->device_name = os_strdup(pos);
+ } else if (os_strcmp(buf, "manufacturer") == 0) {
+ bss->manufacturer = os_strdup(pos);
+ } else if (os_strcmp(buf, "model_name") == 0) {
+ bss->model_name = os_strdup(pos);
+ } else if (os_strcmp(buf, "model_number") == 0) {
+ bss->model_number = os_strdup(pos);
+ } else if (os_strcmp(buf, "serial_number") == 0) {
+ bss->serial_number = os_strdup(pos);
+ } else if (os_strcmp(buf, "device_type") == 0) {
+ bss->device_type = os_strdup(pos);
+ } else if (os_strcmp(buf, "config_methods") == 0) {
+ bss->config_methods = os_strdup(pos);
+ } else if (os_strcmp(buf, "os_version") == 0) {
+ if (hexstr2bin(pos, bss->os_version, 4)) {
+ printf("Line %d: invalid os_version\n", line);
+ errors++;
+ }
+ } else if (os_strcmp(buf, "ap_pin") == 0) {
+ bss->ap_pin = os_strdup(pos);
+#endif /* CONFIG_WPS */
} else {
printf("Line %d: unknown configuration item '%s'\n",
line, buf);
@@ -2216,6 +2255,18 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
}
}
#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_WPS
+ os_free(conf->wps_pin_requests);
+ os_free(conf->device_name);
+ os_free(conf->manufacturer);
+ os_free(conf->model_name);
+ os_free(conf->model_number);
+ os_free(conf->serial_number);
+ os_free(conf->device_type);
+ os_free(conf->config_methods);
+ os_free(conf->ap_pin);
+#endif /* CONFIG_WPS */
}
@@ -2314,6 +2365,29 @@ hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity,
{
struct hostapd_eap_user *user = conf->eap_user;
+#ifdef CONFIG_WPS
+ if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN &&
+ os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) {
+ static struct hostapd_eap_user wsc_enrollee;
+ os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee));
+ wsc_enrollee.methods[0].method = eap_server_get_type(
+ "WSC", &wsc_enrollee.methods[0].vendor);
+ return &wsc_enrollee;
+ }
+
+ if (conf->wps_state && conf->ap_pin &&
+ identity_len == WSC_ID_REGISTRAR_LEN &&
+ os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) {
+ static struct hostapd_eap_user wsc_registrar;
+ os_memset(&wsc_registrar, 0, sizeof(wsc_registrar));
+ wsc_registrar.methods[0].method = eap_server_get_type(
+ "WSC", &wsc_registrar.methods[0].vendor);
+ wsc_registrar.password = (u8 *) conf->ap_pin;
+ wsc_registrar.password_len = os_strlen(conf->ap_pin);
+ return &wsc_registrar;
+ }
+#endif /* CONFIG_WPS */
+
while (user) {
if (!phase2 && user->identity == NULL) {
/* Wildcard match */
diff --git a/hostapd/config.h b/hostapd/config.h
index 86f88c1..324349d 100644
--- a/hostapd/config.h
+++ b/hostapd/config.h
@@ -285,6 +285,22 @@ struct hostapd_bss_config {
u16 max_listen_interval;
int okc; /* Opportunistic Key Caching */
+
+ int wps_state;
+#ifdef CONFIG_WPS
+ int ap_setup_locked;
+ u8 uuid[16];
+ char *wps_pin_requests;
+ char *device_name;
+ char *manufacturer;
+ char *model_name;
+ char *model_number;
+ char *serial_number;
+ char *device_type;
+ char *config_methods;
+ u8 os_version[4];
+ char *ap_pin;
+#endif /* CONFIG_WPS */
};
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index b978f96..f8b3a1d 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -29,6 +29,7 @@
#include "ctrl_iface.h"
#include "sta_info.h"
#include "accounting.h"
+#include "wps_hostapd.h"
struct wpa_ctrl_dst {
@@ -217,6 +218,18 @@ static int hostapd_ctrl_iface_new_sta(struct hostapd_data *hapd,
}
+#ifdef CONFIG_WPS
+static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt)
+{
+ char *pin = os_strchr(txt, ' ');
+ if (pin == NULL)
+ return -1;
+ *pin++ = '\0';
+ return hostapd_wps_add_pin(hapd, txt, pin);
+}
+#endif /* CONFIG_WPS */
+
+
static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
void *sock_ctx)
{
@@ -300,6 +313,14 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
} else if (os_strncmp(buf, "NEW_STA ", 8) == 0) {
if (hostapd_ctrl_iface_new_sta(hapd, buf + 8))
reply_len = -1;
+#ifdef CONFIG_WPS
+ } else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
+ if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "WPS_PBC") == 0) {
+ if (hostapd_wps_button_pushed(hapd))
+ reply_len = -1;
+#endif /* CONFIG_WPS */
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
reply_len = 16;
diff --git a/hostapd/defconfig b/hostapd/defconfig
index 709fb07..cbbd193 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -101,6 +101,9 @@ CONFIG_EAP_TTLS=y
# to add the needed functions.
#CONFIG_EAP_FAST=y
+# Wi-Fi Protected Setup (WPS)
+#CONFIG_WPS=y
+
# EAP-IKEv2
#CONFIG_EAP_IKEV2=y
diff --git a/hostapd/driver.h b/hostapd/driver.h
index 76e1990..47d0e76 100644
--- a/hostapd/driver.h
+++ b/hostapd/driver.h
@@ -180,6 +180,11 @@ struct wpa_driver_ops {
const u8 *data, size_t data_len);
int (*set_ht_operation)(const char *ifname, void *priv,
const u8 *data, size_t data_len);
+
+ int (*set_wps_beacon_ie)(const char *ifname, void *priv,
+ const u8 *ie, size_t len);
+ int (*set_wps_probe_resp_ie)(const char *ifname, void *priv,
+ const u8 *ie, size_t len);
};
static inline void *
@@ -758,4 +763,24 @@ hostapd_drv_none(struct hostapd_data *hapd)
return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0;
}
+static inline int
+hostapd_set_wps_beacon_ie(struct hostapd_data *hapd, const u8 *ie, size_t len)
+{
+ if (hapd->driver == NULL || hapd->driver->set_wps_beacon_ie == NULL)
+ return 0;
+ return hapd->driver->set_wps_beacon_ie(hapd->conf->iface,
+ hapd->drv_priv, ie, len);
+}
+
+static inline int
+hostapd_set_wps_probe_resp_ie(struct hostapd_data *hapd, const u8 *ie,
+ size_t len)
+{
+ if (hapd->driver == NULL ||
+ hapd->driver->set_wps_probe_resp_ie == NULL)
+ return 0;
+ return hapd->driver->set_wps_probe_resp_ie(hapd->conf->iface,
+ hapd->drv_priv, ie, len);
+}
+
#endif /* DRIVER_H */
diff --git a/hostapd/driver_hostap.c b/hostapd/driver_hostap.c
index 04b0b94..ceff099 100644
--- a/hostapd/driver_hostap.c
+++ b/hostapd/driver_hostap.c
@@ -55,6 +55,8 @@ struct hostap_driver_data {
u8 *generic_ie;
size_t generic_ie_len;
+ u8 *wps_ie;
+ size_t wps_ie_len;
};
@@ -770,7 +772,7 @@ static int hostapd_ioctl_set_generic_elem(struct hostap_driver_data *drv)
int res;
size_t blen, elem_len;
- elem_len = drv->generic_ie_len;
+ elem_len = drv->generic_ie_len + drv->wps_ie_len;
blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + elem_len;
if (blen < sizeof(*param))
blen = sizeof(*param);
@@ -785,6 +787,10 @@ static int hostapd_ioctl_set_generic_elem(struct hostap_driver_data *drv)
os_memcpy(param->u.generic_elem.data, drv->generic_ie,
drv->generic_ie_len);
}
+ if (drv->wps_ie) {
+ os_memcpy(&param->u.generic_elem.data[drv->generic_ie_len],
+ drv->wps_ie, drv->wps_ie_len);
+ }
wpa_hexdump(MSG_DEBUG, "hostap: Set generic IE",
param->u.generic_elem.data, elem_len);
res = hostapd_ioctl(drv, param, blen);
@@ -815,6 +821,36 @@ static int hostap_set_generic_elem(const char *ifname, void *priv,
}
+static int hostap_set_wps_beacon_ie(const char *ifname, void *priv,
+ const u8 *ie, size_t len)
+{
+ /* Host AP driver supports only one set of extra IEs, so we need to
+ * use the ProbeResp IEs also for Beacon frames since they include more
+ * information. */
+ return 0;
+}
+
+
+static int hostap_set_wps_probe_resp_ie(const char *ifname, void *priv,
+ const u8 *ie, size_t len)
+{
+ struct hostap_driver_data *drv = priv;
+
+ os_free(drv->wps_ie);
+ drv->wps_ie = NULL;
+ drv->wps_ie_len = 0;
+ if (ie) {
+ drv->wps_ie = os_malloc(len);
+ if (drv->wps_ie == NULL)
+ return -1;
+ os_memcpy(drv->wps_ie, ie, len);
+ drv->wps_ie_len = len;
+ }
+
+ return hostapd_ioctl_set_generic_elem(drv);
+}
+
+
static void
hostapd_wireless_event_wireless_custom(struct hostap_driver_data *drv,
char *custom)
@@ -1120,6 +1156,7 @@ static void hostap_driver_deinit(void *priv)
close(drv->sock);
os_free(drv->generic_ie);
+ os_free(drv->wps_ie);
free(drv);
}
@@ -1237,4 +1274,6 @@ const struct wpa_driver_ops wpa_driver_hostap_ops = {
.get_inact_sec = hostap_get_inact_sec,
.sta_clear_stats = hostap_sta_clear_stats,
.get_hw_feature_data = hostap_get_hw_feature_data,
+ .set_wps_beacon_ie = hostap_set_wps_beacon_ie,
+ .set_wps_probe_resp_ie = hostap_set_wps_probe_resp_ie,
};
diff --git a/hostapd/driver_madwifi.c b/hostapd/driver_madwifi.c
index d7d6041..dfdf79c 100644
--- a/hostapd/driver_madwifi.c
+++ b/hostapd/driver_madwifi.c
@@ -28,6 +28,16 @@
#include <net80211/ieee80211_crypto.h>
#include <net80211/ieee80211_ioctl.h>
+#ifdef CONFIG_WPS
+#ifdef IEEE80211_IOCTL_FILTERFRAME
+#include <netpacket/packet.h>
+
+#ifndef ETH_P_80211_RAW
+#define ETH_P_80211_RAW 0x0019
+#endif
+#endif /* IEEE80211_IOCTL_FILTERFRAME */
+#endif /* CONFIG_WPS */
+
/*
* Avoid conflicts with hostapd definitions by undefining couple of defines
* from madwifi header files.
@@ -58,6 +68,7 @@
#include "ieee802_11.h"
#include "accounting.h"
#include "common.h"
+#include "wps_hostapd.h"
struct madwifi_driver_data {
@@ -729,6 +740,61 @@ madwifi_sta_disassoc(void *priv, const u8 *addr, int reason_code)
return ret;
}
+#ifdef CONFIG_WPS
+#ifdef IEEE80211_IOCTL_FILTERFRAME
+static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
+ size_t len)
+{
+ struct madwifi_driver_data *drv = ctx;
+ const struct ieee80211_mgmt *mgmt;
+ const u8 *end, *ie;
+ u16 fc;
+ size_t ie_len;
+
+ /* Send Probe Request information to WPS processing */
+
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
+ return;
+ mgmt = (const struct ieee80211_mgmt *) buf;
+
+ fc = le_to_host16(mgmt->frame_control);
+ if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ||
+ WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_PROBE_REQ)
+ return;
+
+ end = buf + len;
+ ie = mgmt->u.probe_req.variable;
+ ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
+
+ hostapd_wps_probe_req_rx(drv->hapd, mgmt->sa, ie, ie_len);
+}
+#endif /* IEEE80211_IOCTL_FILTERFRAME */
+#endif /* CONFIG_WPS */
+
+static int madwifi_receive_probe_req(struct madwifi_driver_data *drv)
+{
+ int ret = 0;
+#ifdef CONFIG_WPS
+#ifdef IEEE80211_IOCTL_FILTERFRAME
+ struct ieee80211req_set_filter filt;
+
+ wpa_printf(MSG_DEBUG, "%s Enter", __func__);
+ filt.app_filterype = IEEE80211_FILTER_TYPE_PROBE_REQ;
+
+ ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt,
+ sizeof(struct ieee80211req_set_filter));
+ if (ret)
+ return ret;
+
+ drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW,
+ madwifi_raw_receive, drv, 1);
+ if (drv->sock_raw == NULL)
+ return -1;
+#endif /* IEEE80211_IOCTL_FILTERFRAME */
+#endif /* CONFIG_WPS */
+ return ret;
+}
+
static int
madwifi_del_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
{
@@ -749,6 +815,45 @@ madwifi_del_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
return 0;
}
+#ifdef CONFIG_WPS
+static int
+madwifi_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype)
+{
+ struct madwifi_driver_data *drv = priv;
+ u8 buf[256];
+ struct ieee80211req_getset_appiebuf *beac_ie;
+
+ wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__,
+ (unsigned long) len);
+
+ beac_ie = (struct ieee80211req_getset_appiebuf *) buf;
+ beac_ie->app_frmtype = frametype;
+ beac_ie->app_buflen = len;
+ memcpy(&(beac_ie->app_buf[0]), ie, len);
+
+ return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie,
+ sizeof(struct ieee80211req_getset_appiebuf) + len);
+}
+
+static int
+madwifi_set_wps_beacon_ie(const char *ifname, void *priv, const u8 *ie,
+ size_t len)
+{
+ return madwifi_set_wps_ie(priv, ie, len, IEEE80211_APPIE_FRAME_BEACON);
+}
+
+static int
+madwifi_set_wps_probe_resp_ie(const char *ifname, void *priv, const u8 *ie,
+ size_t len)
+{
+ return madwifi_set_wps_ie(priv, ie, len,
+ IEEE80211_APPIE_FRAME_PROBE_RESP);
+}
+#else /* CONFIG_WPS */
+#define madwifi_set_wps_beacon_ie NULL
+#define madwifi_set_wps_probe_resp_ie NULL
+#endif /* CONFIG_WPS */
+
static int
madwifi_process_wpa_ie(struct madwifi_driver_data *drv, struct sta_info *sta)
{
@@ -788,6 +893,15 @@ madwifi_process_wpa_ie(struct madwifi_driver_data *drv, struct sta_info *sta)
#endif /* MADWIFI_NG */
ielen = iebuf[1];
if (ielen == 0) {
+#ifdef CONFIG_WPS
+ if (hapd->conf->wps_state) {
+ wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE "
+ "in (Re)Association Request - possible WPS "
+ "use");
+ sta->flags |= WLAN_STA_MAYBE_WPS;
+ return 0;
+ }
+#endif /* CONFIG_WPS */
printf("No WPA/RSN information element for station!?\n");
return -1; /* XXX not right */
}
@@ -1254,6 +1368,8 @@ madwifi_init(struct hostapd_data *hapd)
madwifi_set_iface_flags(drv, 0); /* mark down during setup */
madwifi_set_privacy(drv->iface, drv, 0); /* default to no privacy */
+ madwifi_receive_probe_req(drv);
+
return drv;
bad:
if (drv->sock_xmit != NULL)
@@ -1360,4 +1476,6 @@ const struct wpa_driver_ops wpa_driver_madwifi_ops = {
.set_countermeasures = madwifi_set_countermeasures,
.sta_clear_stats = madwifi_sta_clear_stats,
.commit = madwifi_commit,
+ .set_wps_beacon_ie = madwifi_set_wps_beacon_ie,
+ .set_wps_probe_resp_ie = madwifi_set_wps_probe_resp_ie,
};
diff --git a/hostapd/driver_test.c b/hostapd/driver_test.c
index fb7bf05..d524083 100644
--- a/hostapd/driver_test.c
+++ b/hostapd/driver_test.c
@@ -28,6 +28,7 @@
#include "l2_packet/l2_packet.h"
#include "ieee802_11.h"
#include "hw_features.h"
+#include "wps_hostapd.h"
struct test_client_socket {
@@ -44,6 +45,10 @@ struct test_driver_bss {
u8 bssid[ETH_ALEN];
u8 *ie;
size_t ielen;
+ u8 *wps_beacon_ie;
+ size_t wps_beacon_ie_len;
+ u8 *wps_probe_resp_ie;
+ size_t wps_probe_resp_ie_len;
u8 ssid[32];
size_t ssid_len;
int privacy;
@@ -62,6 +67,8 @@ struct test_driver_data {
static void test_driver_free_bss(struct test_driver_bss *bss)
{
free(bss->ie);
+ free(bss->wps_beacon_ie);
+ free(bss->wps_probe_resp_ie);
free(bss);
}
@@ -321,14 +328,44 @@ static int test_driver_send_mgmt_frame(void *priv, const void *buf,
static void test_driver_scan(struct test_driver_data *drv,
- struct sockaddr_un *from, socklen_t fromlen)
+ struct sockaddr_un *from, socklen_t fromlen,
+ char *data)
{
char buf[512], *pos, *end;
int ret;
struct test_driver_bss *bss;
+ u8 sa[ETH_ALEN];
+ u8 ie[512];
+ size_t ielen;
+
+ /* data: optional [ ' ' | STA-addr | ' ' | IEs(hex) ] */
wpa_printf(MSG_DEBUG, "test_driver: SCAN");
+ if (*data) {
+ if (*data != ' ' ||
+ hwaddr_aton(data + 1, sa)) {
+ wpa_printf(MSG_DEBUG, "test_driver: Unexpected SCAN "
+ "command format");
+ return;
+ }
+
+ data += 18;
+ while (*data == ' ')
+ data++;
+ ielen = os_strlen(data) / 2;
+ if (ielen > sizeof(ie))
+ ielen = sizeof(ie);
+ if (hexstr2bin(data, ie, ielen) < 0)
+ ielen = 0;
+
+ wpa_printf(MSG_DEBUG, "test_driver: Scan from " MACSTR,
+ MAC2STR(sa));
+ wpa_hexdump(MSG_MSGDUMP, "test_driver: scan IEs", ie, ielen);
+
+ hostapd_wps_probe_req_rx(drv->hapd, sa, ie, ielen);
+ }
+
for (bss = drv->bss; bss; bss = bss->next) {
pos = buf;
end = buf + sizeof(buf);
@@ -346,6 +383,8 @@ static void test_driver_scan(struct test_driver_data *drv,
return;
pos += ret;
pos += wpa_snprintf_hex(pos, end - pos, bss->ie, bss->ielen);
+ pos += wpa_snprintf_hex(pos, end - pos, bss->wps_probe_resp_ie,
+ bss->wps_probe_resp_ie_len);
if (bss->privacy) {
ret = snprintf(pos, end - pos, " PRIVACY");
@@ -653,8 +692,8 @@ static void test_driver_receive_unix(int sock, void *eloop_ctx, void *sock_ctx)
wpa_printf(MSG_DEBUG, "test_driver: received %u bytes", res);
- if (strcmp(buf, "SCAN") == 0) {
- test_driver_scan(drv, &from, fromlen);
+ if (strncmp(buf, "SCAN", 4) == 0) {
+ test_driver_scan(drv, &from, fromlen, buf + 4);
} else if (strncmp(buf, "ASSOC ", 6) == 0) {
test_driver_assoc(drv, &from, fromlen, buf + 6);
} else if (strcmp(buf, "DISASSOC") == 0) {
@@ -717,6 +756,66 @@ static int test_driver_set_generic_elem(const char *ifname, void *priv,
}
+static int test_driver_set_wps_beacon_ie(const char *ifname, void *priv,
+ const u8 *ie, size_t len)
+{
+ struct test_driver_data *drv = priv;
+ struct test_driver_bss *bss;
+
+ bss = test_driver_get_bss(drv, ifname);
+ if (bss == NULL)
+ return -1;
+
+ free(bss->wps_beacon_ie);
+
+ if (ie == NULL) {
+ bss->wps_beacon_ie = NULL;
+ bss->wps_beacon_ie_len = 0;
+ return 0;
+ }
+
+ bss->wps_beacon_ie = malloc(len);
+ if (bss->wps_beacon_ie == NULL) {
+ bss->wps_beacon_ie_len = 0;
+ return -1;
+ }
+
+ memcpy(bss->wps_beacon_ie, ie, len);
+ bss->wps_beacon_ie_len = len;
+ return 0;
+}
+
+
+static int test_driver_set_wps_probe_resp_ie(const char *ifname, void *priv,
+ const u8 *ie, size_t len)
+{
+ struct test_driver_data *drv = priv;
+ struct test_driver_bss *bss;
+
+ bss = test_driver_get_bss(drv, ifname);
+ if (bss == NULL)
+ return -1;
+
+ free(bss->wps_probe_resp_ie);
+
+ if (ie == NULL) {
+ bss->wps_probe_resp_ie = NULL;
+ bss->wps_probe_resp_ie_len = 0;
+ return 0;
+ }
+
+ bss->wps_probe_resp_ie = malloc(len);
+ if (bss->wps_probe_resp_ie == NULL) {
+ bss->wps_probe_resp_ie_len = 0;
+ return -1;
+ }
+
+ memcpy(bss->wps_probe_resp_ie, ie, len);
+ bss->wps_probe_resp_ie_len = len;
+ return 0;
+}
+
+
static int test_driver_sta_deauth(void *priv, const u8 *addr, int reason)
{
struct test_driver_data *drv = priv;
@@ -1162,4 +1261,6 @@ const struct wpa_driver_ops wpa_driver_test_ops = {
.set_sta_vlan = test_driver_set_sta_vlan,
.sta_add = test_driver_sta_add,
.send_ether = test_driver_send_ether,
+ .set_wps_beacon_ie = test_driver_set_wps_beacon_ie,
+ .set_wps_probe_resp_ie = test_driver_set_wps_probe_resp_ie,
};
diff --git a/hostapd/eapol_sm.c b/hostapd/eapol_sm.c
index 4f52475..b333db7 100644
--- a/hostapd/eapol_sm.c
+++ b/hostapd/eapol_sm.c
@@ -812,6 +812,7 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
eap_conf.pac_key_refresh_time = eapol->conf.pac_key_refresh_time;
eap_conf.eap_sim_aka_result_ind = eapol->conf.eap_sim_aka_result_ind;
eap_conf.tnc = eapol->conf.tnc;
+ eap_conf.wps = eapol->conf.wps;
sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf);
if (sm->eap == NULL) {
eapol_auth_free(sm);
@@ -1263,6 +1264,7 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst,
dst->pac_key_refresh_time = src->pac_key_refresh_time;
dst->eap_sim_aka_result_ind = src->eap_sim_aka_result_ind;
dst->tnc = src->tnc;
+ dst->wps = src->wps;
return 0;
}
diff --git a/hostapd/eapol_sm.h b/hostapd/eapol_sm.h
index f2ca800..7a13e8e 100644
--- a/hostapd/eapol_sm.h
+++ b/hostapd/eapol_sm.h
@@ -56,6 +56,7 @@ struct eapol_auth_config {
int pac_key_refresh_time;
int eap_sim_aka_result_ind;
int tnc;
+ struct wps_context *wps;
/*
* Pointer to hostapd data. This is a temporary workaround for
diff --git a/hostapd/hostapd.c b/hostapd/hostapd.c
index a4fad98..937e48a 100644
--- a/hostapd/hostapd.c
+++ b/hostapd/hostapd.c
@@ -44,11 +44,15 @@
#include "eap_server/tncs.h"
#include "version.h"
#include "l2_packet/l2_packet.h"
+#include "wps_hostapd.h"
static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity,
size_t identity_len, int phase2,
struct eap_user *user);
+static int hostapd_flush_old_stations(struct hostapd_data *hapd);
+static int hostapd_setup_wpa(struct hostapd_data *hapd);
+static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd);
struct hapd_interfaces {
size_t count;
@@ -326,41 +330,72 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
}
+int hostapd_reload_config(struct hostapd_iface *iface)
+{
+ struct hostapd_data *hapd = iface->bss[0];
+ struct hostapd_config *newconf, *oldconf;
+ struct wpa_auth_config wpa_auth_conf;
+
+ newconf = hostapd_config_read(iface->config_fname);
+ if (newconf == NULL)
+ return -1;
+
+ /*
+ * Deauthenticate all stations since the new configuration may not
+ * allow them to use the BSS anymore.
+ */
+ hostapd_flush_old_stations(hapd);
+
+ /* TODO: update dynamic data based on changed configuration
+ * items (e.g., open/close sockets, etc.) */
+ radius_client_flush(hapd->radius, 0);
+
+ oldconf = hapd->iconf;
+ hapd->iconf = newconf;
+ hapd->conf = &newconf->bss[0];
+ iface->conf = newconf;
+
+ if (hostapd_setup_wpa_psk(hapd->conf)) {
+ wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK "
+ "after reloading configuration");
+ }
+
+ if (hapd->conf->wpa && hapd->wpa_auth == NULL)
+ hostapd_setup_wpa(hapd);
+ else if (hapd->conf->wpa) {
+ hostapd_wpa_auth_conf(&newconf->bss[0], &wpa_auth_conf);
+ wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf);
+ } else if (hapd->wpa_auth) {
+ wpa_deinit(hapd->wpa_auth);
+ hapd->wpa_auth = NULL;
+ hostapd_set_privacy(hapd, 0);
+ hostapd_setup_encryption(hapd->conf->iface, hapd);
+ }
+
+ ieee802_11_set_beacon(hapd);
+
+ hostapd_config_free(oldconf);
+
+ wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface);
+
+ return 0;
+}
+
+
#ifndef CONFIG_NATIVE_WINDOWS
static void handle_reload(int sig, void *eloop_ctx, void *signal_ctx)
{
struct hapd_interfaces *hapds = (struct hapd_interfaces *) eloop_ctx;
- struct hostapd_config *newconf;
size_t i;
- struct wpa_auth_config wpa_auth_conf;
printf("Signal %d received - reloading configuration\n", sig);
for (i = 0; i < hapds->count; i++) {
- struct hostapd_data *hapd = hapds->iface[i]->bss[0];
- newconf = hostapd_config_read(hapds->iface[i]->config_fname);
- if (newconf == NULL) {
+ if (hostapd_reload_config(hapds->iface[i]) < 0) {
printf("Failed to read new configuration file - "
"continuing with old.\n");
continue;
}
- /* TODO: update dynamic data based on changed configuration
- * items (e.g., open/close sockets, remove stations added to
- * deny list, etc.) */
- radius_client_flush(hapd->radius, 0);
- hostapd_config_free(hapd->iconf);
-
- hostapd_wpa_auth_conf(&newconf->bss[0], &wpa_auth_conf);
- wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf);
-
- hapd->iconf = newconf;
- hapd->conf = &newconf->bss[0];
- hapds->iface[i]->conf = newconf;
-
- if (hostapd_setup_wpa_psk(hapd->conf)) {
- wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK "
- "after reloading configuration");
- }
}
}
@@ -400,7 +435,7 @@ static void hostapd_dump_state(struct hostapd_data *hapd)
fprintf(f, "\nSTA=" MACSTR "\n", MAC2STR(sta->addr));
fprintf(f,
- " AID=%d flags=0x%x %s%s%s%s%s%s%s%s%s%s%s%s\n"
+ " AID=%d flags=0x%x %s%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
" capability=0x%x listen_interval=%d\n",
sta->aid,
sta->flags,
@@ -418,6 +453,8 @@ static void hostapd_dump_state(struct hostapd_data *hapd)
(sta->flags & WLAN_STA_PREAUTH ? "[PREAUTH]" : ""),
(sta->flags & WLAN_STA_WME ? "[WME]" : ""),
(sta->flags & WLAN_STA_MFP ? "[MFP]" : ""),
+ (sta->flags & WLAN_STA_WPS ? "[WPS]" : ""),
+ (sta->flags & WLAN_STA_MAYBE_WPS ? "[MAYBE_WPS]" : ""),
(sta->flags & WLAN_STA_NONERP ? "[NonERP]" : ""),
sta->capability,
sta->listen_interval);
@@ -588,6 +625,8 @@ static void hostapd_cleanup(struct hostapd_data *hapd)
l2_packet_deinit(hapd->l2);
#endif /* CONFIG_IEEE80211R */
+ hostapd_deinit_wps(hapd);
+
hostapd_wireless_event_deinit(hapd);
#ifdef EAP_TLS_FUNCS
@@ -1178,6 +1217,7 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd,
srv.pac_key_refresh_time = conf->pac_key_refresh_time;
srv.eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
srv.tnc = conf->tnc;
+ srv.wps = hapd->wps;
srv.ipv6 = conf->radius_server_ipv6;
srv.get_eap_user = hostapd_radius_get_eap_user;
@@ -1310,6 +1350,8 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
printf("ACL initialization failed.\n");
return -1;
}
+ if (hostapd_init_wps(hapd, conf))
+ return -1;
if (ieee802_1x_init(hapd)) {
printf("IEEE 802.1X initialization failed.\n");
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 8bd2c56..2a875cf 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -822,6 +822,85 @@ own_ip_addr=127.0.0.1
#ap_table_expiration_time=3600
+##### Wi-Fi Protected Setup (WPS) #############################################
+
+# WPS state
+# 0 = WPS disabled (default)
+# 1 = WPS enabled, not configured
+# 2 = WPS enabled, configured
+#wps_state=2
+
+# AP can be configured into a locked state where new WPS Registrar are not
+# accepted, but previously authorized Registrars (including the internal one)
+# can continue to add new Enrollees.
+#ap_setup_locked=1
+
+# Universally Unique IDentifier (UUID; see RFC 4122) of the device
+# This value is used as the UUID for the internal WPS Registrar. If the AP
+# is also using UPnP, this value should be set to the device's UPnP UUID.
+#uuid=12345678-9abc-def0-1234-56789abcdef0
+
+# Note: If wpa_psk_file is set, WPS is used to generate random, per-device PSKs
+# that will be appended to the wpa_psk_file. If wpa_psk_file is not set, the
+# default PSK (wpa_psk/wpa_passphrase) will be delivered to Enrollees. Use of
+# per-device PSKs is recommended as the more secure option (i.e., make sure to
+# set wpa_psk_file when using WPS with WPA-PSK).
+
+# When an Enrollee requests access to the network with PIN method, the Enrollee
+# PIN will need to be entered for the Registrar. PIN request notifications are
+# sent to hostapd ctrl_iface monitor. In addition, they can be written to a
+# text file that could be used, e.g., to populate the AP administration UI with
+# pending PIN requests. If the following variable is set, the PIN requests will
+# be written to the configured file.
+#wps_pin_requests=/var/run/hostapd_wps_pin_requests
+
+# Device Name
+# User-friendly description of device; up to 32 octets encoded in UTF-8
+#device_name=Wireless AP
+
+# Manufacturer
+# The manufacturer of the device (up to 64 ASCII characters)
+#manufacturer=Company
+
+# Model Name
+# Model of the device (up to 32 ASCII characters)
+#model_name=WAP
+
+# Model Number
+# Additional device description (up to 32 ASCII characters)
+#model_number=123
+
+# Serial Number
+# Serial number of the device (up to 32 characters)
+#serial_number=12345
+
+# Primary Device Type
+# Used format: <categ>-<OUI>-<subcateg>
+# categ = Category as an integer value
+# OUI = OUI and type octet as a 4-octet hex-encoded value; 0050F204 for
+# default WPS OUI
+# subcateg = OUI-specific Sub Category as an integer value
+# Examples:
+# 1-0050F204-1 (Computer / PC)
+# 1-0050F204-2 (Computer / Server)
+# 5-0050F204-1 (Storage / NAS)
+# 6-0050F204-1 (Network Infrastructure / AP)
+#device_type=6-0050F204-1
+
+# OS Version
+# 4-octet operating system version number (hex string)
+#os_version=01020300
+
+# Config Methods
+# List of the supported configuration methods
+#config_methods=label display push_button keypad
+
+# Access point PIN for initial configuration and adding Registrars
+# If not set, hostapd will not allow external WPS Registrars to control the
+# access point.
+#ap_pin=12345670
+
+
##### Multiple BSSID support ##################################################
#
# Above configuration is using the default interface (wlan#, or multi-SSID VLAN
diff --git a/hostapd/hostapd.h b/hostapd/hostapd.h
index 74a30dc..85bcf0b 100644
--- a/hostapd/hostapd.h
+++ b/hostapd/hostapd.h
@@ -163,6 +163,14 @@ struct hostapd_data {
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
struct l2_packet_data *l2;
+ struct wps_context *wps;
+
+#ifdef CONFIG_WPS
+ u8 *wps_beacon_ie;
+ size_t wps_beacon_ie_len;
+ u8 *wps_probe_resp_ie;
+ size_t wps_probe_resp_ie_len;
+#endif /* CONFIG_WPS */
};
@@ -222,5 +230,6 @@ struct hostapd_iface {
void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
int reassoc);
+int hostapd_reload_config(struct hostapd_iface *iface);
#endif /* HOSTAPD_H */
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index a3cef1b..e5285ae 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -83,6 +83,10 @@ static const char *commands_help =
" sta <addr> get MIB variables for one station\n"
" all_sta get MIB variables for all stations\n"
" new_sta <addr> add a new station\n"
+#ifdef CONFIG_WPS
+" wps_pin <uuid> <pin> add WPS Enrollee PIN (Device Password)\n"
+" wps_pbc indicate button pushed to initiate PBC\n"
+#endif /* CONFIG_WPS */
" help show this usage help\n"
" interface [ifname] show interfaces/select interface\n"
" level <debug level> change debug level\n"
@@ -230,6 +234,29 @@ static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
}
+#ifdef CONFIG_WPS
+static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char buf[64];
+ if (argc != 2) {
+ printf("Invalid 'wps_pin' command - exactly two arguments, "
+ "UUID and PIN, are required.\n");
+ return -1;
+ }
+ snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]);
+ return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "WPS_PBC");
+}
+#endif /* CONFIG_WPS */
+
+
static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
char *addr, size_t addr_len)
{
@@ -378,6 +405,10 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = {
{ "sta", hostapd_cli_cmd_sta },
{ "all_sta", hostapd_cli_cmd_all_sta },
{ "new_sta", hostapd_cli_cmd_new_sta },
+#ifdef CONFIG_WPS
+ { "wps_pin", hostapd_cli_cmd_wps_pin },
+ { "wps_pbc", hostapd_cli_cmd_wps_pbc },
+#endif /* CONFIG_WPS */
{ "help", hostapd_cli_cmd_help },
{ "interface", hostapd_cli_cmd_interface },
{ "level", hostapd_cli_cmd_level },
diff --git a/hostapd/ieee802_11.c b/hostapd/ieee802_11.c
index 19dd680..0d69d1d 100644
--- a/hostapd/ieee802_11.c
+++ b/hostapd/ieee802_11.c
@@ -825,6 +825,21 @@ static void handle_assoc(struct hostapd_data *hapd,
wpa_ie = NULL;
wpa_ie_len = 0;
}
+#ifdef CONFIG_WPS
+ if (hapd->conf->wps_state && wpa_ie == NULL) {
+ if (elems.wps_ie) {
+ wpa_printf(MSG_DEBUG, "STA included WPS IE in "
+ "(Re)Association Request - assume WPS is "
+ "used");
+ sta->flags |= WLAN_STA_WPS;
+ } else {
+ wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE "
+ "in (Re)Association Request - possible WPS "
+ "use");
+ sta->flags |= WLAN_STA_MAYBE_WPS;
+ }
+ } else
+#endif /* CONFIG_WPS */
if (hapd->conf->wpa && wpa_ie == NULL) {
printf("STA " MACSTR ": No WPA/RSN IE in association "
"request\n", MAC2STR(sta->addr));
diff --git a/hostapd/ieee802_1x.c b/hostapd/ieee802_1x.c
index 59c2d8c..5d3cbfb 100644
--- a/hostapd/ieee802_1x.c
+++ b/hostapd/ieee802_1x.c
@@ -670,7 +670,8 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
u16 datalen;
struct rsn_pmksa_cache_entry *pmksa;
- if (!hapd->conf->ieee802_1x && !hapd->conf->wpa)
+ if (!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
+ !hapd->conf->wps_state)
return;
wpa_printf(MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR,
@@ -718,7 +719,8 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
return;
}
- if (!hapd->conf->ieee802_1x ||
+ if ((!hapd->conf->ieee802_1x &&
+ !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) ||
wpa_key_mgmt_wpa_psk(wpa_auth_sta_key_mgmt(sta->wpa_sm)))
return;
@@ -728,6 +730,18 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
sta);
if (!sta->eapol_sm)
return;
+
+#ifdef CONFIG_WPS
+ if (!hapd->conf->ieee802_1x &&
+ ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) ==
+ WLAN_STA_MAYBE_WPS)) {
+ /*
+ * Delay EAPOL frame transmission until a possible WPS
+ * STA initiates the handshake with EAPOL-Start.
+ */
+ sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
+ }
+#endif /* CONFIG_WPS */
}
/* since we support version 1, we can ignore version field and proceed
@@ -801,6 +815,18 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
int reassoc = 1;
int force_1x = 0;
+#ifdef CONFIG_WPS
+ if (hapd->conf->wps_state &&
+ (sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) {
+ /*
+ * Need to enable IEEE 802.1X/EAPOL state machines for possible
+ * WPS handshake even if IEEE 802.1X/EAPOL is not used for
+ * authentication in this BSS.
+ */
+ force_1x = 1;
+ }
+#endif /* CONFIG_WPS */
+
if ((!force_1x && !hapd->conf->ieee802_1x) ||
wpa_key_mgmt_wpa_psk(wpa_auth_sta_key_mgmt(sta->wpa_sm)))
return;
@@ -821,6 +847,16 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
reassoc = 0;
}
+#ifdef CONFIG_WPS
+ if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS)) {
+ /*
+ * Delay EAPOL frame transmission until a possible WPS
+ * initiates the handshake with EAPOL-Start.
+ */
+ sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
+ }
+#endif /* CONFIG_WPS */
+
sta->eapol_sm->eap_if->portEnabled = TRUE;
pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
@@ -1613,6 +1649,7 @@ int ieee802_1x_init(struct hostapd_data *hapd)
conf.pac_key_refresh_time = hapd->conf->pac_key_refresh_time;
conf.eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind;
conf.tnc = hapd->conf->tnc;
+ conf.wps = hapd->wps;
os_memset(&cb, 0, sizeof(cb));
cb.eapol_send = ieee802_1x_eapol_send;
diff --git a/hostapd/wpa.c b/hostapd/wpa.c
index cc01f02..b1f35ad 100644
--- a/hostapd/wpa.c
+++ b/hostapd/wpa.c
@@ -456,11 +456,11 @@ int wpa_reconfig(struct wpa_authenticator *wpa_auth,
return 0;
os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
- /*
- * TODO:
- * Disassociate stations if configuration changed
- * Update WPA/RSN IE
- */
+ if (wpa_auth_gen_wpa_ie(wpa_auth)) {
+ wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
+ return -1;
+ }
+
return 0;
}
diff --git a/hostapd/wps_hostapd.c b/hostapd/wps_hostapd.c
new file mode 100644
index 0000000..be1c886
--- /dev/null
+++ b/hostapd/wps_hostapd.c
@@ -0,0 +1,604 @@
+/*
+ * hostapd / WPS integration
+ * Copyright (c) 2008, 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.
+ */
+
+#include "includes.h"
+
+#include "hostapd.h"
+#include "driver.h"
+#include "eloop.h"
+#include "uuid.h"
+#include "wpa_ctrl.h"
+#include "ctrl_iface.h"
+#include "ieee802_11_defs.h"
+#include "wps/wps.h"
+#include "wps/wps_defs.h"
+#include "wps/wps_dev_attr.h"
+#include "wps_hostapd.h"
+
+
+static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk,
+ size_t psk_len)
+{
+ struct hostapd_data *hapd = ctx;
+ struct hostapd_wpa_psk *p;
+ struct hostapd_ssid *ssid = &hapd->conf->ssid;
+
+ wpa_printf(MSG_DEBUG, "Received new WPA/WPA2-PSK from WPS for STA "
+ MACSTR, MAC2STR(mac_addr));
+ wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len);
+
+ if (psk_len != PMK_LEN) {
+ wpa_printf(MSG_DEBUG, "Unexpected PSK length %lu",
+ (unsigned long) psk_len);
+ return -1;
+ }
+
+ /* Add the new PSK to runtime PSK list */
+ p = os_zalloc(sizeof(*p));
+ if (p == NULL)
+ return -1;
+ os_memcpy(p->addr, mac_addr, ETH_ALEN);
+ os_memcpy(p->psk, psk, PMK_LEN);
+
+ p->next = ssid->wpa_psk;
+ ssid->wpa_psk = p;
+
+ if (ssid->wpa_psk_file) {
+ FILE *f;
+ char hex[PMK_LEN * 2 + 1];
+ /* Add the new PSK to PSK list file */
+ f = fopen(ssid->wpa_psk_file, "a");
+ if (f == NULL) {
+ wpa_printf(MSG_DEBUG, "Failed to add the PSK to "
+ "'%s'", ssid->wpa_psk_file);
+ return -1;
+ }
+
+ wpa_snprintf_hex(hex, sizeof(hex), psk, psk_len);
+ fprintf(f, MACSTR " %s\n", MAC2STR(mac_addr), hex);
+ fclose(f);
+ }
+
+ return 0;
+}
+
+
+static int hostapd_wps_set_ie_cb(void *ctx, const u8 *beacon_ie,
+ size_t beacon_ie_len, const u8 *probe_resp_ie,
+ size_t probe_resp_ie_len)
+{
+ struct hostapd_data *hapd = ctx;
+
+ os_free(hapd->wps_beacon_ie);
+ if (beacon_ie_len == 0) {
+ hapd->wps_beacon_ie = NULL;
+ hapd->wps_beacon_ie_len = 0;
+ } else {
+ hapd->wps_beacon_ie = os_malloc(beacon_ie_len);
+ if (hapd->wps_beacon_ie == NULL) {
+ hapd->wps_beacon_ie_len = 0;
+ return -1;
+ }
+ os_memcpy(hapd->wps_beacon_ie, beacon_ie, beacon_ie_len);
+ hapd->wps_beacon_ie_len = beacon_ie_len;
+ }
+ hostapd_set_wps_beacon_ie(hapd, hapd->wps_beacon_ie,
+ hapd->wps_beacon_ie_len);
+
+ os_free(hapd->wps_probe_resp_ie);
+ if (probe_resp_ie_len == 0) {
+ hapd->wps_probe_resp_ie = NULL;
+ hapd->wps_probe_resp_ie_len = 0;
+ } else {
+ hapd->wps_probe_resp_ie = os_malloc(probe_resp_ie_len);
+ if (hapd->wps_probe_resp_ie == NULL) {
+ hapd->wps_probe_resp_ie_len = 0;
+ return -1;
+ }
+ os_memcpy(hapd->wps_probe_resp_ie, probe_resp_ie,
+ probe_resp_ie_len);
+ hapd->wps_probe_resp_ie_len = probe_resp_ie_len;
+ }
+ hostapd_set_wps_probe_resp_ie(hapd, hapd->wps_probe_resp_ie,
+ hapd->wps_probe_resp_ie_len);
+
+ return 0;
+}
+
+
+static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
+ const struct wps_device_data *dev)
+{
+ struct hostapd_data *hapd = ctx;
+ char uuid[40], txt[400];
+ int len;
+ if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
+ return;
+ wpa_printf(MSG_DEBUG, "WPS: PIN needed for E-UUID %s", uuid);
+ len = os_snprintf(txt, sizeof(txt), WPS_EVENT_PIN_NEEDED
+ "%s " MACSTR " [%s|%s|%s|%s|%s|%d-%08X-%d]",
+ uuid, MAC2STR(dev->mac_addr), dev->device_name,
+ dev->manufacturer, dev->model_name,
+ dev->model_number, dev->serial_number,
+ dev->categ, dev->oui, dev->sub_categ);
+ if (len > 0 && len < (int) sizeof(txt))
+ hostapd_ctrl_iface_send(hapd, MSG_INFO, txt, len);
+
+ if (hapd->conf->wps_pin_requests) {
+ FILE *f;
+ struct os_time t;
+ f = fopen(hapd->conf->wps_pin_requests, "a");
+ if (f == NULL)
+ return;
+ os_get_time(&t);
+ fprintf(f, "%ld\t%s\t" MACSTR "\t%s\t%s\t%s\t%s\t%s"
+ "\t%d-%08X-%d\n",
+ t.sec, uuid, MAC2STR(dev->mac_addr), dev->device_name,
+ dev->manufacturer, dev->model_name, dev->model_number,
+ dev->serial_number,
+ dev->categ, dev->oui, dev->sub_categ);
+ fclose(f);
+ }
+}
+
+
+static int str_starts(const char *str, const char *start)
+{
+ return os_strncmp(str, start, os_strlen(start)) == 0;
+}
+
+
+static void wps_reload_config(void *eloop_data, void *user_ctx)
+{
+ struct hostapd_iface *iface = eloop_data;
+
+ wpa_printf(MSG_DEBUG, "WPS: Reload configuration data");
+ if (hostapd_reload_config(iface) < 0) {
+ wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated "
+ "configuration");
+ }
+}
+
+
+static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
+{
+ struct hostapd_data *hapd = ctx;
+ FILE *oconf, *nconf;
+ size_t len, i;
+ char *tmp_fname;
+ char buf[1024];
+ int multi_bss;
+ int wpa;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received new AP Settings");
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len);
+ wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x",
+ cred->auth_type);
+ wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type);
+ wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key",
+ cred->key, cred->key_len);
+ wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR,
+ MAC2STR(cred->mac_addr));
+
+ hostapd_ctrl_iface_send(hapd, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS,
+ os_strlen(WPS_EVENT_NEW_AP_SETTINGS));
+
+ len = os_strlen(hapd->iface->config_fname) + 5;
+ tmp_fname = os_malloc(len);
+ if (tmp_fname == NULL)
+ return -1;
+ os_snprintf(tmp_fname, len, "%s-new", hapd->iface->config_fname);
+
+ oconf = fopen(hapd->iface->config_fname, "r");
+ if (oconf == NULL) {
+ wpa_printf(MSG_WARNING, "WPS: Could not open current "
+ "configuration file");
+ os_free(tmp_fname);
+ return -1;
+ }
+
+ nconf = fopen(tmp_fname, "w");
+ if (nconf == NULL) {
+ wpa_printf(MSG_WARNING, "WPS: Could not write updated "
+ "configuration file");
+ os_free(tmp_fname);
+ fclose(oconf);
+ return -1;
+ }
+
+ fprintf(nconf, "# WPS configuration - START\n");
+
+ fprintf(nconf, "wps_state=2\n");
+
+ fprintf(nconf, "ssid=");
+ for (i = 0; i < cred->ssid_len; i++)
+ fputc(cred->ssid[i], nconf);
+ fprintf(nconf, "\n");
+
+ if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) &&
+ (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)))
+ wpa = 3;
+ else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK))
+ wpa = 2;
+ else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))
+ wpa = 1;
+ else
+ wpa = 0;
+
+ if (wpa) {
+ char *prefix;
+ fprintf(nconf, "wpa=%d\n", wpa);
+
+ fprintf(nconf, "wpa_key_mgmt=");
+ prefix = "";
+ if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA)) {
+ fprintf(nconf, "WPA-EAP");
+ prefix = " ";
+ }
+ if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK))
+ fprintf(nconf, "%sWPA-PSK", prefix);
+ fprintf(nconf, "\n");
+
+ fprintf(nconf, "wpa_pairwise=");
+ prefix = "";
+ if (cred->encr_type & WPS_ENCR_AES) {
+ fprintf(nconf, "CCMP");
+ prefix = " ";
+ }
+ if (cred->encr_type & WPS_ENCR_TKIP) {
+ fprintf(nconf, "%sTKIP", prefix);
+ }
+ fprintf(nconf, "\n");
+
+ if (cred->key_len >= 8 && cred->key_len < 64) {
+ fprintf(nconf, "wpa_passphrase=");
+ for (i = 0; i < cred->key_len; i++)
+ fputc(cred->key[i], nconf);
+ fprintf(nconf, "\n");
+ } else if (cred->key_len == 64) {
+ fprintf(nconf, "wpa_psk=");
+ for (i = 0; i < cred->key_len; i++)
+ fputc(cred->key[i], nconf);
+ fprintf(nconf, "\n");
+ } else {
+ wpa_printf(MSG_WARNING, "WPS: Invalid key length %lu "
+ "for WPA/WPA2",
+ (unsigned long) cred->key_len);
+ }
+
+ fprintf(nconf, "auth_algs=1\n");
+ } else {
+ if ((cred->auth_type & WPS_AUTH_OPEN) &&
+ (cred->auth_type & WPS_AUTH_SHARED))
+ fprintf(nconf, "auth_algs=3\n");
+ else if (cred->auth_type & WPS_AUTH_SHARED)
+ fprintf(nconf, "auth_algs=2\n");
+ else
+ fprintf(nconf, "auth_algs=1\n");
+
+ if (cred->encr_type & WPS_ENCR_WEP && cred->key_idx < 4) {
+ fprintf(nconf, "wep_default_key=%d\n", cred->key_idx);
+ fprintf(nconf, "wep_key%d=", cred->key_idx);
+ if (cred->key_len != 10 && cred->key_len != 26)
+ fputc('"', nconf);
+ for (i = 0; i < cred->key_len; i++)
+ fputc(cred->key[i], nconf);
+ if (cred->key_len != 10 && cred->key_len != 26)
+ fputc('"', nconf);
+ fprintf(nconf, "\n");
+ }
+ }
+
+ fprintf(nconf, "# WPS configuration - END\n");
+
+ multi_bss = 0;
+ while (fgets(buf, sizeof(buf), oconf)) {
+ if (os_strncmp(buf, "bss=", 4) == 0)
+ multi_bss = 1;
+ if (!multi_bss &&
+ (str_starts(buf, "ssid=") ||
+ str_starts(buf, "auth_algs=") ||
+ str_starts(buf, "wps_state=") ||
+ str_starts(buf, "wpa=") ||
+ str_starts(buf, "wpa_psk=") ||
+ str_starts(buf, "wpa_pairwise=") ||
+ str_starts(buf, "rsn_pairwise=") ||
+ str_starts(buf, "wpa_key_mgmt=") ||
+ str_starts(buf, "wpa_passphrase="))) {
+ fprintf(nconf, "#WPS# %s", buf);
+ } else
+ fprintf(nconf, "%s", buf);
+ }
+
+ fclose(nconf);
+ fclose(oconf);
+
+ if (rename(tmp_fname, hapd->iface->config_fname) < 0) {
+ wpa_printf(MSG_WARNING, "WPS: Failed to rename the updated "
+ "configuration file: %s", strerror(errno));
+ os_free(tmp_fname);
+ return -1;
+ }
+
+ os_free(tmp_fname);
+
+ /* Schedule configuration reload after short period of time to allow
+ * EAP-WSC to be finished.
+ */
+ eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface,
+ NULL);
+
+ /* TODO: dualband AP may need to update multiple configuration files */
+
+ wpa_printf(MSG_DEBUG, "WPS: AP configuration updated");
+
+ return 0;
+}
+
+
+static void hostapd_wps_clear_ies(struct hostapd_data *hapd)
+{
+ os_free(hapd->wps_beacon_ie);
+ hapd->wps_beacon_ie = NULL;
+ hapd->wps_beacon_ie_len = 0;
+ hostapd_set_wps_beacon_ie(hapd, NULL, 0);
+
+ os_free(hapd->wps_probe_resp_ie);
+ hapd->wps_probe_resp_ie = NULL;
+ hapd->wps_probe_resp_ie_len = 0;
+ hostapd_set_wps_probe_resp_ie(hapd, NULL, 0);
+}
+
+
+int hostapd_init_wps(struct hostapd_data *hapd,
+ struct hostapd_bss_config *conf)
+{
+ struct wps_context *wps;
+ struct wps_registrar_config cfg;
+
+ if (conf->wps_state == 0) {
+ hostapd_wps_clear_ies(hapd);
+ return 0;
+ }
+
+ wps = os_zalloc(sizeof(*wps));
+ if (wps == NULL)
+ return -1;
+
+ wps->cred_cb = hostapd_wps_cred_cb;
+ wps->cb_ctx = hapd;
+
+ os_memset(&cfg, 0, sizeof(cfg));
+ wps->wps_state = hapd->conf->wps_state;
+ wps->ap_setup_locked = hapd->conf->ap_setup_locked;
+ os_memcpy(wps->uuid, hapd->conf->uuid, UUID_LEN);
+ wps->ssid_len = hapd->conf->ssid.ssid_len;
+ os_memcpy(wps->ssid, hapd->conf->ssid.ssid, wps->ssid_len);
+ wps->ap = 1;
+ os_memcpy(wps->dev.mac_addr, hapd->own_addr, ETH_ALEN);
+ wps->dev.device_name = hapd->conf->device_name;
+ wps->dev.manufacturer = hapd->conf->manufacturer;
+ wps->dev.model_name = hapd->conf->model_name;
+ wps->dev.model_number = hapd->conf->model_number;
+ wps->dev.serial_number = hapd->conf->serial_number;
+ if (hapd->conf->config_methods) {
+ char *m = hapd->conf->config_methods;
+ if (os_strstr(m, "label"))
+ wps->config_methods |= WPS_CONFIG_LABEL;
+ if (os_strstr(m, "display"))
+ wps->config_methods |= WPS_CONFIG_DISPLAY;
+ if (os_strstr(m, "push_button"))
+ wps->config_methods |= WPS_CONFIG_PUSHBUTTON;
+ if (os_strstr(m, "keypad"))
+ wps->config_methods |= WPS_CONFIG_KEYPAD;
+ }
+ if (hapd->conf->device_type) {
+ char *pos;
+ u8 oui[4];
+ /* <categ>-<OUI>-<subcateg> */
+ wps->dev.categ = atoi(hapd->conf->device_type);
+ pos = os_strchr(hapd->conf->device_type, '-');
+ if (pos == NULL) {
+ wpa_printf(MSG_ERROR, "WPS: Invalid device_type");
+ os_free(wps);
+ return -1;
+ }
+ pos++;
+ if (hexstr2bin(pos, oui, 4)) {
+ wpa_printf(MSG_ERROR, "WPS: Invalid device_type OUI");
+ os_free(wps);
+ return -1;
+ }
+ wps->dev.oui = WPA_GET_BE32(oui);
+ pos = os_strchr(pos, '-');
+ if (pos == NULL) {
+ wpa_printf(MSG_ERROR, "WPS: Invalid device_type");
+ os_free(wps);
+ return -1;
+ }
+ pos++;
+ wps->dev.sub_categ = atoi(pos);
+ }
+ wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version);
+
+ if (conf->wpa & WPA_PROTO_RSN) {
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK)
+ wps->auth_types |= WPS_AUTH_WPA2PSK;
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+ wps->auth_types |= WPS_AUTH_WPA2;
+
+ if (conf->rsn_pairwise & WPA_CIPHER_CCMP)
+ wps->encr_types |= WPS_ENCR_AES;
+ if (conf->rsn_pairwise & WPA_CIPHER_TKIP)
+ wps->encr_types |= WPS_ENCR_TKIP;
+ }
+
+ if (conf->wpa & WPA_PROTO_WPA) {
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK)
+ wps->auth_types |= WPS_AUTH_WPAPSK;
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+ wps->auth_types |= WPS_AUTH_WPA;
+
+ if (conf->wpa_pairwise & WPA_CIPHER_CCMP)
+ wps->encr_types |= WPS_ENCR_AES;
+ if (conf->wpa_pairwise & WPA_CIPHER_TKIP)
+ wps->encr_types |= WPS_ENCR_TKIP;
+ }
+
+ if (conf->ssid.security_policy == SECURITY_PLAINTEXT) {
+ wps->encr_types |= WPS_ENCR_NONE;
+ wps->auth_types |= WPS_AUTH_OPEN;
+ } else if (conf->ssid.security_policy == SECURITY_STATIC_WEP) {
+ wps->encr_types |= WPS_ENCR_WEP;
+ if (conf->auth_algs & WPA_AUTH_ALG_OPEN)
+ wps->auth_types |= WPS_AUTH_OPEN;
+ if (conf->auth_algs & WPA_AUTH_ALG_SHARED)
+ wps->auth_types |= WPS_AUTH_SHARED;
+ } else if (conf->ssid.security_policy == SECURITY_IEEE_802_1X) {
+ wps->auth_types |= WPS_AUTH_OPEN;
+ if (conf->default_wep_key_len)
+ wps->encr_types |= WPS_ENCR_WEP;
+ else
+ wps->encr_types |= WPS_ENCR_NONE;
+ }
+
+ if (conf->ssid.wpa_psk_file) {
+ /* Use per-device PSKs */
+ } else if (conf->ssid.wpa_passphrase) {
+ wps->network_key = (u8 *) os_strdup(conf->ssid.wpa_passphrase);
+ wps->network_key_len = os_strlen(conf->ssid.wpa_passphrase);
+ } else if (conf->ssid.wpa_psk) {
+ wps->network_key = os_malloc(2 * PMK_LEN + 1);
+ if (wps->network_key == NULL) {
+ os_free(wps);
+ return -1;
+ }
+ wpa_snprintf_hex((char *) wps->network_key, 2 * PMK_LEN + 1,
+ conf->ssid.wpa_psk->psk, PMK_LEN);
+ wps->network_key_len = 2 * PMK_LEN;
+ } else if (conf->ssid.wep.keys_set && conf->ssid.wep.key[0]) {
+ wps->network_key = os_malloc(conf->ssid.wep.len[0]);
+ if (wps->network_key == NULL) {
+ os_free(wps);
+ return -1;
+ }
+ os_memcpy(wps->network_key, conf->ssid.wep.key[0],
+ conf->ssid.wep.len[0]);
+ wps->network_key_len = conf->ssid.wep.len[0];
+ }
+
+ if (conf->wps_state == WPS_STATE_NOT_CONFIGURED) {
+ /* Override parameters to enable security by default */
+ wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK;
+ wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP;
+ }
+
+ cfg.new_psk_cb = hostapd_wps_new_psk_cb;
+ cfg.set_ie_cb = hostapd_wps_set_ie_cb;
+ cfg.pin_needed_cb = hostapd_wps_pin_needed_cb;
+ cfg.cb_ctx = hapd;
+
+ wps->registrar = wps_registrar_init(wps, &cfg);
+ if (wps->registrar == NULL) {
+ printf("Failed to initialize WPS Registrar\n");
+ os_free(wps->network_key);
+ os_free(wps);
+ return -1;
+ }
+
+ hapd->wps = wps;
+
+ return 0;
+}
+
+
+void hostapd_deinit_wps(struct hostapd_data *hapd)
+{
+ if (hapd->wps == NULL)
+ return;
+ wps_registrar_deinit(hapd->wps->registrar);
+ os_free(hapd->wps->network_key);
+ os_free(hapd->wps);
+ hapd->wps = NULL;
+ hostapd_wps_clear_ies(hapd);
+}
+
+
+int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid,
+ const char *pin)
+{
+ u8 u[UUID_LEN];
+ if (hapd->wps == NULL || uuid_str2bin(uuid, u))
+ return -1;
+ return wps_registrar_add_pin(hapd->wps->registrar, u,
+ (const u8 *) pin, os_strlen(pin));
+}
+
+
+int hostapd_wps_button_pushed(struct hostapd_data *hapd)
+{
+ if (hapd->wps == NULL)
+ return -1;
+ return wps_registrar_button_pushed(hapd->wps->registrar);
+}
+
+
+void hostapd_wps_probe_req_rx(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *ie, size_t ie_len)
+{
+ struct wpabuf *wps_ie;
+ const u8 *end, *pos, *wps;
+
+ if (hapd->wps == NULL)
+ return;
+
+ pos = ie;
+ end = ie + ie_len;
+ wps = NULL;
+
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ return;
+ if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+ WPA_GET_BE32(&pos[2]) == WPS_DEV_OUI_WFA) {
+ wps = pos;
+ break;
+ }
+ pos += 2 + pos[1];
+ }
+
+ if (wps == NULL)
+ return; /* No WPS IE in Probe Request */
+
+ wps_ie = wpabuf_alloc(ie_len);
+ if (wps_ie == NULL)
+ return;
+
+ /* There may be multiple WPS IEs in the message, so need to concatenate
+ * their WPS Data fields */
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+ WPA_GET_BE32(&pos[2]) == WPS_DEV_OUI_WFA)
+ wpabuf_put_data(wps_ie, pos + 6, pos[1] - 4);
+ pos += 2 + pos[1];
+ }
+
+ if (wpabuf_len(wps_ie) > 0)
+ wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie);
+
+ wpabuf_free(wps_ie);
+}
diff --git a/hostapd/wps_hostapd.h b/hostapd/wps_hostapd.h
new file mode 100644
index 0000000..6615c62
--- /dev/null
+++ b/hostapd/wps_hostapd.h
@@ -0,0 +1,48 @@
+/*
+ * hostapd / WPS integration
+ * Copyright (c) 2008, 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.
+ */
+
+#ifndef WPS_HOSTAPD_H
+#define WPS_HOSTAPD_H
+
+#ifdef CONFIG_WPS
+
+int hostapd_init_wps(struct hostapd_data *hapd,
+ struct hostapd_bss_config *conf);
+void hostapd_deinit_wps(struct hostapd_data *hapd);
+int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid,
+ const char *pin);
+int hostapd_wps_button_pushed(struct hostapd_data *hapd);
+void hostapd_wps_probe_req_rx(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *ie, size_t ie_len);
+
+#else /* CONFIG_WPS */
+
+static inline int hostapd_init_wps(struct hostapd_data *hapd,
+ struct hostapd_bss_config *conf)
+{
+ return 0;
+}
+
+static inline void hostapd_deinit_wps(struct hostapd_data *hapd)
+{
+}
+
+static inline void hostapd_wps_probe_req_rx(struct hostapd_data *hapd,
+ const u8 *addr,
+ const u8 *ie, size_t ie_len)
+{
+}
+#endif /* CONFIG_WPS */
+
+#endif /* WPS_HOSTAPD_H */
diff --git a/src/Makefile b/src/Makefile
index 3ff6948..028af4d 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,4 +1,4 @@
-SUBDIRS=common crypto drivers hlr_auc_gw eapol_supp eap_common eap_peer eap_server l2_packet radius rsn_supp tls utils
+SUBDIRS=common crypto drivers hlr_auc_gw eapol_supp eap_common eap_peer eap_server l2_packet radius rsn_supp tls utils wps
all:
@echo Nothing to be made.
diff --git a/src/common/defs.h b/src/common/defs.h
index 9adc2ac..4930e73 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -42,6 +42,7 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean;
#define WPA_KEY_MGMT_FT_PSK BIT(6)
#define WPA_KEY_MGMT_IEEE8021X_SHA256 BIT(7)
#define WPA_KEY_MGMT_PSK_SHA256 BIT(8)
+#define WPA_KEY_MGMT_WPS BIT(9)
static inline int wpa_key_mgmt_wpa_ieee8021x(int akm)
{
@@ -85,7 +86,8 @@ typedef enum { CIPHER_NONE, CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP,
typedef enum { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE,
KEY_MGMT_802_1X_NO_WPA, KEY_MGMT_WPA_NONE,
KEY_MGMT_FT_802_1X, KEY_MGMT_FT_PSK,
- KEY_MGMT_802_1X_SHA256, KEY_MGMT_PSK_SHA256
+ KEY_MGMT_802_1X_SHA256, KEY_MGMT_PSK_SHA256,
+ KEY_MGMT_WPS
} wpa_key_mgmt;
/**
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index eed3b0f..8d3fe1a 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -75,6 +75,11 @@ static int ieee802_11_parse_vendor_specific(u8 *pos, size_t elen,
return -1;
}
break;
+ case 4:
+ /* Wi-Fi Protected Setup (WPS) IE */
+ elems->wps_ie = pos;
+ elems->wps_ie_len = elen;
+ break;
default:
wpa_printf(MSG_MSGDUMP, "Unknown Microsoft "
"information element ignored "
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 2cb063e..4f3f4be 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -45,6 +45,8 @@ struct ieee802_11_elems {
u8 wme_len;
u8 *wme_tspec;
u8 wme_tspec_len;
+ u8 *wps_ie;
+ u8 wps_ie_len;
u8 *power_cap;
u8 power_cap_len;
u8 *supp_channels;
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 05d7a4e..26b2d2f 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -49,6 +49,12 @@ extern "C" {
/** New scan results available */
#define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS "
+#define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED "
+
+/* hostapd control interface - fixed message prefixes */
+#define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED "
+#define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS "
+
/* wpa_supplicant/hostapd control interface access */
diff --git a/src/drivers/Apple80211.h b/src/drivers/Apple80211.h
index f72b9b9..2a612e7 100644
--- a/src/drivers/Apple80211.h
+++ b/src/drivers/Apple80211.h
@@ -105,11 +105,13 @@ WirelessError WirelessDisassociate(WirelessRef ref);
* channel: CFNumber(kCFNumberSInt32Type)
* signal: CFNumber(kCFNumberSInt32Type)
* appleIE: CFData
+ * WPSNOPINRequired: CFBoolean
* noise: CFNumber(kCFNumberSInt32Type)
* capability: CFNumber(kCFNumberSInt32Type)
* uniCipher: CFArray of CFNumber(kCFNumberSInt32Type)
* appleIE_Version: CFNumber(kCFNumberSInt32Type)
* appleIE_Robust: CFBoolean
+ * WPSConfigured: CFBoolean
* scanWasDirected: CFBoolean
* appleIE_Product: CFNumber(kCFNumberSInt32Type)
* authModes: CFArray of CFNumber(kCFNumberSInt32Type)
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 7668996..378042e 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -164,6 +164,8 @@ struct wpa_driver_associate_params {
* instead. The driver can determine which version is used by
* looking at the first byte of the IE (0xdd for WPA, 0x30 for
* WPA2/RSN).
+ *
+ * When using WPS, wpa_ie is used for WPS IE instead of WPA/RSN IE.
*/
const u8 *wpa_ie;
/**
@@ -928,7 +930,7 @@ struct wpa_driver_ops {
* @ies_len: Length of the IE buffer in octets
* Returns: 0 on success, -1 on failure
*/
- int (*set_probe_req_ie)(void *, const u8 *ies, size_t ies_len);
+ int (*set_probe_req_ie)(void *priv, const u8 *ies, size_t ies_len);
/**
* set_mode - Request driver to set the operating mode
@@ -1239,6 +1241,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_test.c b/src/drivers/driver_test.c
index 8c0b430..2e293ae 100644
--- a/src/drivers/driver_test.c
+++ b/src/drivers/driver_test.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant - testing driver interface
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2008, 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
@@ -44,6 +44,8 @@ struct wpa_driver_test_data {
size_t assoc_wpa_ie_len;
int use_mlme;
int associated;
+ u8 *probe_req_ie;
+ size_t probe_req_ie_len;
};
@@ -85,11 +87,28 @@ static void wpa_driver_scan_dir(struct wpa_driver_test_data *drv,
struct dirent *dent;
DIR *dir;
struct sockaddr_un addr;
+ char cmd[512], *pos, *end;
+ int ret;
dir = opendir(path);
if (dir == NULL)
return;
+ end = cmd + sizeof(cmd);
+ pos = cmd;
+ ret = os_snprintf(pos, end - pos, "SCAN " MACSTR,
+ MAC2STR(drv->own_addr));
+ if (ret >= 0 && ret < end - pos)
+ pos += ret;
+ if (drv->probe_req_ie) {
+ ret = os_snprintf(pos, end - pos, " ");
+ if (ret >= 0 && ret < end - pos)
+ pos += ret;
+ pos += wpa_snprintf_hex(pos, end - pos, drv->probe_req_ie,
+ drv->probe_req_ie_len);
+ }
+ end[-1] = '\0';
+
while ((dent = readdir(dir))) {
if (os_strncmp(dent->d_name, "AP-", 3) != 0)
continue;
@@ -100,7 +119,7 @@ static void wpa_driver_scan_dir(struct wpa_driver_test_data *drv,
os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s",
path, dent->d_name);
- if (sendto(drv->test_socket, "SCAN", 4, 0,
+ if (sendto(drv->test_socket, cmd, os_strlen(cmd), 0,
(struct sockaddr *) &addr, sizeof(addr)) < 0) {
perror("sendto(test_socket)");
}
@@ -564,6 +583,7 @@ static void wpa_driver_test_deinit(void *priv)
os_free(drv->test_dir);
for (i = 0; i < MAX_SCAN_RESULTS; i++)
os_free(drv->scanres[i]);
+ os_free(drv->probe_req_ie);
os_free(drv);
}
@@ -936,6 +956,27 @@ int wpa_driver_test_set_bssid(void *priv, const u8 *bssid)
#endif /* CONFIG_CLIENT_MLME */
+int wpa_driver_set_probe_req_ie(void *priv, const u8 *ies, size_t ies_len)
+{
+ struct wpa_driver_test_data *drv = priv;
+
+ os_free(drv->probe_req_ie);
+ if (ies) {
+ drv->probe_req_ie = os_malloc(ies_len);
+ if (drv->probe_req_ie == NULL) {
+ drv->probe_req_ie_len = 0;
+ return -1;
+ }
+ os_memcpy(drv->probe_req_ie, ies, ies_len);
+ drv->probe_req_ie_len = ies_len;
+ } else {
+ drv->probe_req_ie = NULL;
+ drv->probe_req_ie_len = 0;
+ }
+ return 0;
+}
+
+
const struct wpa_driver_ops wpa_driver_test_ops = {
"test",
"wpa_supplicant test driver",
@@ -984,6 +1025,6 @@ const struct wpa_driver_ops wpa_driver_test_ops = {
NULL /* update_ft_ies */,
NULL /* send_ft_action */,
wpa_driver_test_get_scan_results2,
- NULL /* set_probe_req_ie */,
+ wpa_driver_set_probe_req_ie,
NULL /* set_mode */
};
diff --git a/src/eap_common/eap_wsc_common.c b/src/eap_common/eap_wsc_common.c
new file mode 100644
index 0000000..5d4e8cc
--- /dev/null
+++ b/src/eap_common/eap_wsc_common.c
@@ -0,0 +1,39 @@
+/*
+ * EAP-WSC common routines for Wi-Fi Protected Setup
+ * Copyright (c) 2007, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_defs.h"
+#include "eap_common.h"
+#include "wps/wps.h"
+#include "eap_wsc_common.h"
+
+struct wpabuf * eap_wsc_build_frag_ack(u8 id, u8 code)
+{
+ struct wpabuf *msg;
+
+ msg = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 2, code, id);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for "
+ "FRAG_ACK");
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Send WSC/FRAG_ACK");
+ wpabuf_put_u8(msg, WSC_FRAG_ACK); /* Op-Code */
+ wpabuf_put_u8(msg, 0); /* Flags */
+
+ return msg;
+}
diff --git a/src/eap_common/eap_wsc_common.h b/src/eap_common/eap_wsc_common.h
new file mode 100644
index 0000000..fdf61d3
--- /dev/null
+++ b/src/eap_common/eap_wsc_common.h
@@ -0,0 +1,33 @@
+/*
+ * EAP-WSC definitions for Wi-Fi Protected Setup
+ * Copyright (c) 2007, 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.
+ */
+
+#ifndef EAP_WSC_COMMON_H
+#define EAP_WSC_COMMON_H
+
+#define EAP_VENDOR_TYPE_WSC 1
+
+#define WSC_FLAGS_MF 0x01
+#define WSC_FLAGS_LF 0x02
+
+#define WSC_ID_REGISTRAR "WFA-SimpleConfig-Registrar-1-0"
+#define WSC_ID_REGISTRAR_LEN 30
+#define WSC_ID_ENROLLEE "WFA-SimpleConfig-Enrollee-1-0"
+#define WSC_ID_ENROLLEE_LEN 29
+
+#define WSC_FRAGMENT_SIZE 1400
+
+
+struct wpabuf * eap_wsc_build_frag_ack(u8 id, u8 code);
+
+#endif /* EAP_WSC_COMMON_H */
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index e518ec3..2627faa 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -31,6 +31,7 @@
#include "pcsc_funcs.h"
#include "wpa_ctrl.h"
#include "state_machine.h"
+#include "eap_common/eap_wsc_common.h"
#define STATE_MACHINE_DATA struct eap_sm
#define STATE_MACHINE_DEBUG_PREFIX "EAP"
@@ -2043,3 +2044,29 @@ void eap_invalidate_cached_session(struct eap_sm *sm)
if (sm)
eap_deinit_prev_method(sm, "invalidate");
}
+
+
+int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf)
+{
+ if (conf->identity_len != WSC_ID_ENROLLEE_LEN ||
+ os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN))
+ return 0; /* Not a WPS Enrollee */
+
+ if (conf->phase1 == NULL || os_strstr(conf->phase1, "pbc=1") == NULL)
+ return 0; /* Not using PBC */
+
+ return 1;
+}
+
+
+int eap_is_wps_pin_enrollee(struct eap_peer_config *conf)
+{
+ if (conf->identity_len != WSC_ID_ENROLLEE_LEN ||
+ os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN))
+ return 0; /* Not a WPS Enrollee */
+
+ if (conf->phase1 == NULL || os_strstr(conf->phase1, "pin=") == NULL)
+ return 0; /* Not using PIN */
+
+ return 1;
+}
diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h
index d3db7d6..cd08032 100644
--- a/src/eap_peer/eap.h
+++ b/src/eap_peer/eap.h
@@ -22,6 +22,7 @@
struct eap_sm;
struct wpa_config_blob;
struct wpabuf;
+struct wps_credential;
struct eap_method_type {
int vendor;
@@ -214,6 +215,17 @@ struct eapol_callbacks {
void (*notify_pending)(void *ctx);
/**
+ * wps_cred - Notify that new credential was received from WPS
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * Returns: 0 on success (credential stored), -1 on failure
+ *
+ * This callback is only needed when using WPS Enrollee to configure
+ * new credentials. This can be left %NULL if no WPS functionality is
+ * enabled.
+ */
+ int (*wps_cred)(void *ctx, struct wps_credential *cred);
+
+ /**
* eap_param_needed - Notify that EAP parameter is needed
* @ctx: eapol_ctx from eap_peer_sm_init() call
* @field: Field name (e.g., "IDENTITY")
@@ -248,7 +260,7 @@ struct eap_config {
/**
* mac_addr - MAC address of the peer
*
- * This can be left %NULL if not available.
+ * This is only used by EAP-WSC and can be left %NULL if not available.
*/
const u8 *mac_addr;
};
@@ -283,6 +295,9 @@ struct wpabuf * eap_get_eapRespData(struct eap_sm *sm);
void eap_register_scard_ctx(struct eap_sm *sm, void *ctx);
void eap_invalidate_cached_session(struct eap_sm *sm);
+int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf);
+int eap_is_wps_pin_enrollee(struct eap_peer_config *conf);
+
#endif /* IEEE8021X_EAPOL */
#endif /* EAP_H */
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index f6126bb..6e4919c 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -356,6 +356,9 @@ struct eap_peer_config {
* 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>
*/
char *phase1;
diff --git a/src/eap_peer/eap_methods.c b/src/eap_peer/eap_methods.c
index 0973b2f..727049e 100644
--- a/src/eap_peer/eap_methods.c
+++ b/src/eap_peer/eap_methods.c
@@ -455,6 +455,13 @@ int eap_peer_register_methods(void)
}
#endif /* EAP_GPSK */
+#ifdef EAP_WSC
+ if (ret == 0) {
+ int eap_peer_wsc_register(void);
+ ret = eap_peer_wsc_register();
+ }
+#endif /* EAP_WSC */
+
#ifdef EAP_IKEV2
if (ret == 0) {
int eap_peer_ikev2_register(void);
diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c
new file mode 100644
index 0000000..e80b7c6
--- /dev/null
+++ b/src/eap_peer/eap_wsc.c
@@ -0,0 +1,552 @@
+/*
+ * EAP-WSC peer for Wi-Fi Protected Setup
+ * Copyright (c) 2007-2008, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "uuid.h"
+#include "eap_i.h"
+#include "eap_common/eap_wsc_common.h"
+#include "wps/wps.h"
+#include "wps/wps_defs.h"
+
+
+struct eap_wsc_data {
+ enum { WAIT_START, MSG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state;
+ int registrar;
+ struct wpabuf *in_buf;
+ struct wpabuf *out_buf;
+ u8 in_op_code, out_op_code;
+ size_t out_used;
+ size_t fragment_size;
+ struct wps_data *wps;
+ struct wps_context *wps_ctx;
+};
+
+
+static const char * eap_wsc_state_txt(int state)
+{
+ switch (state) {
+ case WAIT_START:
+ return "WAIT_START";
+ case MSG:
+ return "MSG";
+ case FRAG_ACK:
+ return "FRAG_ACK";
+ case WAIT_FRAG_ACK:
+ return "WAIT_FRAG_ACK";
+ case DONE:
+ return "DONE";
+ case FAIL:
+ return "FAIL";
+ default:
+ return "?";
+ }
+}
+
+
+static void eap_wsc_state(struct eap_wsc_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s",
+ eap_wsc_state_txt(data->state),
+ eap_wsc_state_txt(state));
+ data->state = state;
+}
+
+
+static int eap_wsc_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk,
+ size_t psk_len)
+{
+ /* struct eap_wsc_data *data = ctx; */
+
+ wpa_printf(MSG_DEBUG, "EAP-SC: Received new WPA/WPA2-PSK from WPS for "
+ "STA " MACSTR, MAC2STR(mac_addr));
+ wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len);
+
+ /* TODO */
+
+ return 0;
+}
+
+
+static void eap_wsc_pin_needed_cb(void *ctx, const u8 *uuid_e,
+ const struct wps_device_data *dev)
+{
+ /* struct eap_wsc_data *data = ctx; */
+ char uuid[40], txt[400];
+ int len;
+ if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
+ return;
+ wpa_printf(MSG_DEBUG, "EAP-WSC: PIN needed for E-UUID %s", uuid);
+ len = os_snprintf(txt, sizeof(txt), "WPS-EVENT-PIN-NEEDED "
+ "%s " MACSTR " [%s|%s|%s|%s|%s|%d-%08X-%d]",
+ uuid, MAC2STR(dev->mac_addr), dev->device_name,
+ dev->manufacturer, dev->model_name,
+ dev->model_number, dev->serial_number,
+ dev->categ, dev->oui, dev->sub_categ);
+ if (len > 0 && len < (int) sizeof(txt))
+ wpa_printf(MSG_INFO, "%s", txt);
+}
+
+
+static void * eap_wsc_init(struct eap_sm *sm)
+{
+ struct eap_wsc_data *data;
+ const u8 *identity;
+ size_t identity_len;
+ int registrar;
+ struct wps_config cfg;
+ u8 uuid[UUID_LEN];
+ const char *pos;
+ const char *phase1;
+ struct wps_context *wps = NULL;
+
+ identity = eap_get_config_identity(sm, &identity_len);
+
+ if (identity && identity_len == WSC_ID_REGISTRAR_LEN &&
+ os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0)
+ registrar = 1; /* Supplicant is Registrar */
+ else if (identity && identity_len == WSC_ID_ENROLLEE_LEN &&
+ os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0)
+ registrar = 0; /* Supplicant is Enrollee */
+ else {
+ wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity",
+ identity, identity_len);
+ return NULL;
+ }
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = registrar ? MSG : WAIT_START;
+ data->registrar = registrar;
+
+ if (registrar) {
+ struct wps_registrar_config rcfg;
+
+ wps = os_zalloc(sizeof(*wps));
+ if (wps == NULL) {
+ os_free(data);
+ return NULL;
+ }
+
+ wps->cb_ctx = data;
+
+ /* TODO: configure.. */
+ wps->auth_types = WPS_AUTH_WPA2PSK;
+ wps->encr_types = WPS_ENCR_AES;
+ os_memcpy(wps->ssid, "test", 4);
+ wps->ssid_len = 4;
+
+ os_memset(&rcfg, 0, sizeof(rcfg));
+ rcfg.new_psk_cb = eap_wsc_new_psk_cb;
+ rcfg.pin_needed_cb = eap_wsc_pin_needed_cb;
+ rcfg.cb_ctx = data;
+
+ wps->registrar = wps_registrar_init(wps, &rcfg);
+ if (wps->registrar == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to initialize "
+ "WPS Registrar");
+ os_free(wps->network_key);
+ os_free(wps);
+ os_free(data);
+ return NULL;
+ }
+
+ data->wps_ctx = wps;
+ }
+
+ os_memset(&cfg, 0, sizeof(cfg));
+ cfg.authenticator = 0;
+ cfg.wps = wps;
+ cfg.registrar = data->wps_ctx ? data->wps_ctx->registrar : NULL;
+ cfg.enrollee_mac_addr = sm->mac_addr;
+
+ phase1 = eap_get_config_phase1(sm);
+ if (phase1 == NULL) {
+ wpa_printf(MSG_INFO, "EAP-WSC: phase1 configuration data not "
+ "set");
+ os_free(data);
+ return NULL;
+ }
+
+ pos = os_strstr(phase1, "pin=");
+ if (pos) {
+ pos += 4;
+ cfg.pin = (const u8 *) pos;
+ while (*pos != '\0' && *pos != ' ')
+ pos++;
+ cfg.pin_len = pos - (const char *) cfg.pin;
+ } else {
+ pos = os_strstr(phase1, "pbc=1");
+ if (pos)
+ cfg.pbc = 1;
+ }
+
+ if (cfg.pin == NULL && !cfg.pbc) {
+ wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 "
+ "configuration data");
+ os_free(data);
+ return NULL;
+ }
+
+ pos = os_strstr(phase1, "uuid=");
+ if (pos == NULL) {
+ wpa_printf(MSG_INFO, "EAP-WSC: UUID not set in phase1 "
+ "configuration data");
+ os_free(data);
+ return NULL;
+ }
+ if (uuid_str2bin(pos + 5, uuid)) {
+ wpa_printf(MSG_INFO, "EAP-WSC: Invalid UUID in phase1 "
+ "configuration data");
+ os_free(data);
+ return NULL;
+ }
+ if (registrar && wps)
+ os_memcpy(wps->uuid, uuid, UUID_LEN);
+ else
+ cfg.uuid = uuid;
+ cfg.wps_cred_cb = sm->eapol_cb->wps_cred;
+ cfg.cb_ctx = sm->eapol_ctx;
+ data->wps = wps_init(&cfg);
+ if (data->wps == NULL) {
+ os_free(data);
+ return NULL;
+ }
+ data->fragment_size = WSC_FRAGMENT_SIZE;
+
+
+ if (registrar) {
+ /* Testing */
+ wpa_printf(MSG_INFO, "EAP-WSC: Registrar functionality not "
+ "yet fully supported - using test values");
+ u8 uuid_e[UUID_LEN];
+ os_memset(uuid_e, 0, UUID_LEN);
+ wps_registrar_add_pin(data->wps_ctx->registrar, uuid_e,
+ (const u8 *) "12345670", 8);
+ }
+
+ return data;
+}
+
+
+static void eap_wsc_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_wsc_data *data = priv;
+ wpabuf_free(data->in_buf);
+ wpabuf_free(data->out_buf);
+ wps_deinit(data->wps);
+ if (data->wps_ctx) {
+ wps_registrar_deinit(data->wps_ctx->registrar);
+ os_free(data->wps_ctx->network_key);
+ os_free(data->wps_ctx);
+ }
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data,
+ struct eap_method_ret *ret, u8 id)
+{
+ struct wpabuf *resp;
+ u8 flags;
+ size_t send_len, plen;
+
+ ret->ignore = FALSE;
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Generating Response");
+ ret->allowNotifications = TRUE;
+
+ flags = 0;
+ send_len = wpabuf_len(data->out_buf) - data->out_used;
+ if (2 + send_len > data->fragment_size) {
+ send_len = data->fragment_size - 2;
+ flags |= WSC_FLAGS_MF;
+ if (data->out_used == 0) {
+ flags |= WSC_FLAGS_LF;
+ send_len -= 2;
+ }
+ }
+ plen = 2 + send_len;
+ if (flags & WSC_FLAGS_LF)
+ plen += 2;
+ resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ wpabuf_put_u8(resp, data->out_op_code); /* Op-Code */
+ wpabuf_put_u8(resp, flags); /* Flags */
+ if (flags & WSC_FLAGS_LF)
+ wpabuf_put_be16(resp, wpabuf_len(data->out_buf));
+
+ wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
+ send_len);
+ data->out_used += send_len;
+
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+
+ if (data->out_used == wpabuf_len(data->out_buf)) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
+ "(message sent completely)",
+ (unsigned long) send_len);
+ wpabuf_free(data->out_buf);
+ data->out_buf = NULL;
+ data->out_used = 0;
+ if ((data->state == FAIL && data->out_op_code == WSC_ACK) ||
+ data->out_op_code == WSC_NACK ||
+ data->out_op_code == WSC_Done) {
+ eap_wsc_state(data, FAIL);
+ ret->methodState = METHOD_DONE;
+ } else
+ eap_wsc_state(data, MSG);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
+ "(%lu more to send)", (unsigned long) send_len,
+ (unsigned long) wpabuf_len(data->out_buf) -
+ data->out_used);
+ eap_wsc_state(data, WAIT_FRAG_ACK);
+ }
+
+ return resp;
+}
+
+
+static int eap_wsc_process_cont(struct eap_wsc_data *data,
+ const u8 *buf, size_t len, u8 op_code)
+{
+ /* Process continuation of a pending message */
+ if (op_code != data->in_op_code) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in "
+ "fragment (expected %d)",
+ op_code, data->in_op_code);
+ return -1;
+ }
+
+ if (len > wpabuf_tailroom(data->in_buf)) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow");
+ eap_wsc_state(data, FAIL);
+ return -1;
+ }
+
+ wpabuf_put_data(data->in_buf, buf, len);
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting "
+ "for %lu bytes more", (unsigned long) len,
+ (unsigned long) wpabuf_tailroom(data->in_buf));
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_wsc_process_fragment(struct eap_wsc_data *data,
+ struct eap_method_ret *ret,
+ u8 id, u8 flags, u8 op_code,
+ u16 message_length,
+ const u8 *buf, size_t len)
+{
+ /* Process a fragment that is not the last one of the message */
+ if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length field in a "
+ "fragmented packet");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (data->in_buf == NULL) {
+ /* First fragment of the message */
+ data->in_buf = wpabuf_alloc(message_length);
+ if (data->in_buf == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for "
+ "message");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ data->in_op_code = op_code;
+ wpabuf_put_data(data->in_buf, buf, len);
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in first "
+ "fragment, waiting for %lu bytes more",
+ (unsigned long) len,
+ (unsigned long) wpabuf_tailroom(data->in_buf));
+ }
+
+ return eap_wsc_build_frag_ack(id, EAP_CODE_RESPONSE);
+}
+
+
+static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_wsc_data *data = priv;
+ const u8 *start, *pos, *end;
+ size_t len;
+ u8 op_code, flags, id;
+ u16 message_length = 0;
+ enum wps_process_res res;
+ struct wpabuf tmpbuf;
+
+ pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData,
+ &len);
+ if (pos == NULL || len < 2) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ id = eap_get_id(reqData);
+
+ start = pos;
+ end = start + len;
+
+ op_code = *pos++;
+ flags = *pos++;
+ if (flags & WSC_FLAGS_LF) {
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ message_length = WPA_GET_BE16(pos);
+ pos += 2;
+
+ if (message_length < end - pos) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
+ "Length");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d "
+ "Flags 0x%x Message Length %d",
+ op_code, flags, message_length);
+
+ if (data->state == WAIT_FRAG_ACK) {
+ if (op_code != WSC_FRAG_ACK) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
+ "in WAIT_FRAG_ACK state", op_code);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged");
+ eap_wsc_state(data, MSG);
+ return eap_wsc_build_msg(data, ret, id);
+ }
+
+ if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG &&
+ op_code != WSC_Done && op_code != WSC_Start) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
+ op_code);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (data->state == WAIT_START) {
+ if (op_code != WSC_Start) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
+ "in WAIT_START state", op_code);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Received start");
+ eap_wsc_state(data, MSG);
+ /* Start message has empty payload, skip processing */
+ goto send_msg;
+ } else if (op_code == WSC_Start) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
+ op_code);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (data->in_buf &&
+ eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (flags & WSC_FLAGS_MF) {
+ return eap_wsc_process_fragment(data, ret, id, flags, op_code,
+ message_length, pos,
+ end - pos);
+ }
+
+ if (data->in_buf == NULL) {
+ /* Wrap unfragmented messages as wpabuf without extra copy */
+ wpabuf_set(&tmpbuf, pos, end - pos);
+ data->in_buf = &tmpbuf;
+ }
+
+ res = wps_process_msg(data->wps, op_code, data->in_buf);
+ switch (res) {
+ case WPS_DONE:
+ wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed "
+ "successfully - wait for EAP failure");
+ eap_wsc_state(data, FAIL);
+ break;
+ case WPS_CONTINUE:
+ eap_wsc_state(data, MSG);
+ break;
+ case WPS_FAILURE:
+ wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
+ eap_wsc_state(data, FAIL);
+ break;
+ case WPS_PENDING:
+ wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing pending");
+ ret->ignore = TRUE;
+ if (data->in_buf == &tmpbuf)
+ data->in_buf = NULL;
+ return NULL;
+ }
+
+ if (data->in_buf != &tmpbuf)
+ wpabuf_free(data->in_buf);
+ data->in_buf = NULL;
+
+send_msg:
+ if (data->out_buf == NULL) {
+ data->out_buf = wps_get_msg(data->wps, &data->out_op_code);
+ if (data->out_buf == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive "
+ "message from WPS");
+ return NULL;
+ }
+ data->out_used = 0;
+ }
+
+ eap_wsc_state(data, MSG);
+ return eap_wsc_build_msg(data, ret, id);
+}
+
+
+int eap_peer_wsc_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
+ "WSC");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_wsc_init;
+ eap->deinit = eap_wsc_deinit;
+ eap->process = eap_wsc_process;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_server/eap.c b/src/eap_server/eap.c
index 0786f56..37b136b 100644
--- a/src/eap_server/eap.c
+++ b/src/eap_server/eap.c
@@ -1166,6 +1166,7 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx,
sm->pac_key_refresh_time = conf->pac_key_refresh_time;
sm->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
sm->tnc = conf->tnc;
+ sm->wps = conf->wps;
wpa_printf(MSG_DEBUG, "EAP: Server state machine created");
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index 0dfca33..8a918eb 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -103,6 +103,7 @@ struct eap_config {
int pac_key_refresh_time;
int eap_sim_aka_result_ind;
int tnc;
+ struct wps_context *wps;
};
diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h
index d955ae9..439e1ef 100644
--- a/src/eap_server/eap_i.h
+++ b/src/eap_server/eap_i.h
@@ -181,6 +181,7 @@ struct eap_sm {
int pac_key_refresh_time;
int eap_sim_aka_result_ind;
int tnc;
+ struct wps_context *wps;
};
int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
diff --git a/src/eap_server/eap_methods.c b/src/eap_server/eap_methods.c
index dc47e00..a7d7738 100644
--- a/src/eap_server/eap_methods.c
+++ b/src/eap_server/eap_methods.c
@@ -254,6 +254,13 @@ int eap_server_register_methods(void)
}
#endif /* EAP_FAST */
+#ifdef EAP_WSC
+ if (ret == 0) {
+ int eap_server_wsc_register(void);
+ ret = eap_server_wsc_register();
+ }
+#endif /* EAP_WSC */
+
#ifdef EAP_IKEV2
if (ret == 0) {
int eap_server_ikev2_register(void);
diff --git a/src/eap_server/eap_wsc.c b/src/eap_server/eap_wsc.c
new file mode 100644
index 0000000..b81b539
--- /dev/null
+++ b/src/eap_server/eap_wsc.c
@@ -0,0 +1,461 @@
+/*
+ * EAP-WSC server for Wi-Fi Protected Setup
+ * Copyright (c) 2007, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_common/eap_wsc_common.h"
+#include "wps/wps.h"
+
+
+struct eap_wsc_data {
+ enum { START, MSG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state;
+ int registrar;
+ struct wpabuf *in_buf;
+ struct wpabuf *out_buf;
+ u8 in_op_code, out_op_code;
+ size_t out_used;
+ size_t fragment_size;
+ struct wps_data *wps;
+};
+
+
+static const char * eap_wsc_state_txt(int state)
+{
+ switch (state) {
+ case START:
+ return "START";
+ case MSG:
+ return "MSG";
+ case FRAG_ACK:
+ return "FRAG_ACK";
+ case WAIT_FRAG_ACK:
+ return "WAIT_FRAG_ACK";
+ case DONE:
+ return "DONE";
+ case FAIL:
+ return "FAIL";
+ default:
+ return "?";
+ }
+}
+
+
+static void eap_wsc_state(struct eap_wsc_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s",
+ eap_wsc_state_txt(data->state),
+ eap_wsc_state_txt(state));
+ data->state = state;
+}
+
+
+static void * eap_wsc_init(struct eap_sm *sm)
+{
+ struct eap_wsc_data *data;
+ int registrar;
+ struct wps_config cfg;
+
+ if (sm->identity && sm->identity_len == WSC_ID_REGISTRAR_LEN &&
+ os_memcmp(sm->identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) ==
+ 0)
+ registrar = 0; /* Supplicant is Registrar */
+ else if (sm->identity && sm->identity_len == WSC_ID_ENROLLEE_LEN &&
+ os_memcmp(sm->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN)
+ == 0)
+ registrar = 1; /* Supplicant is Enrollee */
+ else {
+ wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity",
+ sm->identity, sm->identity_len);
+ return NULL;
+ }
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = registrar ? START : MSG;
+ data->registrar = registrar;
+
+ os_memset(&cfg, 0, sizeof(cfg));
+ cfg.authenticator = 1;
+ cfg.wps = sm->wps;
+ if (registrar) {
+ if (sm->wps == NULL || sm->wps->registrar == NULL) {
+ wpa_printf(MSG_INFO, "EAP-WSC: WPS Registrar not "
+ "initialized");
+ os_free(data);
+ return NULL;
+ }
+ cfg.registrar = sm->wps->registrar;
+ } else {
+ if (sm->user == NULL || sm->user->password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-WSC: No AP PIN (password) "
+ "configured for Enrollee functionality");
+ os_free(data);
+ return NULL;
+ }
+ cfg.pin = sm->user->password;
+ cfg.pin_len = sm->user->password_len;
+ }
+ data->wps = wps_init(&cfg);
+ if (data->wps == NULL) {
+ os_free(data);
+ return NULL;
+ }
+ data->fragment_size = WSC_FRAGMENT_SIZE;
+
+ return data;
+}
+
+
+static void eap_wsc_reset(struct eap_sm *sm, void *priv)
+{
+ struct eap_wsc_data *data = priv;
+ wpabuf_free(data->in_buf);
+ wpabuf_free(data->out_buf);
+ wps_deinit(data->wps);
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_wsc_build_start(struct eap_sm *sm,
+ struct eap_wsc_data *data, u8 id)
+{
+ struct wpabuf *req;
+
+ req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 2,
+ EAP_CODE_REQUEST, id);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for "
+ "request");
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Send WSC/Start");
+ wpabuf_put_u8(req, WSC_Start); /* Op-Code */
+ wpabuf_put_u8(req, 0); /* Flags */
+
+ return req;
+}
+
+
+static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data, u8 id)
+{
+ struct wpabuf *req;
+ u8 flags;
+ size_t send_len, plen;
+
+ flags = 0;
+ send_len = wpabuf_len(data->out_buf) - data->out_used;
+ if (2 + send_len > data->fragment_size) {
+ send_len = data->fragment_size - 2;
+ flags |= WSC_FLAGS_MF;
+ if (data->out_used == 0) {
+ flags |= WSC_FLAGS_LF;
+ send_len -= 2;
+ }
+ }
+ plen = 2 + send_len;
+ if (flags & WSC_FLAGS_LF)
+ plen += 2;
+ req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen,
+ EAP_CODE_REQUEST, id);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for "
+ "request");
+ return NULL;
+ }
+
+ wpabuf_put_u8(req, data->out_op_code); /* Op-Code */
+ wpabuf_put_u8(req, flags); /* Flags */
+ if (flags & WSC_FLAGS_LF)
+ wpabuf_put_be16(req, wpabuf_len(data->out_buf));
+
+ wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used,
+ send_len);
+ data->out_used += send_len;
+
+ if (data->out_used == wpabuf_len(data->out_buf)) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
+ "(message sent completely)",
+ (unsigned long) send_len);
+ wpabuf_free(data->out_buf);
+ data->out_buf = NULL;
+ data->out_used = 0;
+ eap_wsc_state(data, MSG);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
+ "(%lu more to send)", (unsigned long) send_len,
+ (unsigned long) wpabuf_len(data->out_buf) -
+ data->out_used);
+ eap_wsc_state(data, WAIT_FRAG_ACK);
+ }
+
+ return req;
+}
+
+
+static struct wpabuf * eap_wsc_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+ struct eap_wsc_data *data = priv;
+
+ switch (data->state) {
+ case START:
+ return eap_wsc_build_start(sm, data, id);
+ case MSG:
+ if (data->out_buf == NULL) {
+ data->out_buf = wps_get_msg(data->wps,
+ &data->out_op_code);
+ if (data->out_buf == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to "
+ "receive message from WPS");
+ return NULL;
+ }
+ data->out_used = 0;
+ }
+ /* pass through */
+ case WAIT_FRAG_ACK:
+ return eap_wsc_build_msg(data, id);
+ case FRAG_ACK:
+ return eap_wsc_build_frag_ack(id, EAP_CODE_REQUEST);
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected state %d in "
+ "buildReq", data->state);
+ return NULL;
+ }
+}
+
+
+static Boolean eap_wsc_check(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ const u8 *pos;
+ size_t len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
+ respData, &len);
+ if (pos == NULL || len < 2) {
+ wpa_printf(MSG_INFO, "EAP-WSC: Invalid frame");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static int eap_wsc_process_cont(struct eap_wsc_data *data,
+ const u8 *buf, size_t len, u8 op_code)
+{
+ /* Process continuation of a pending message */
+ if (op_code != data->in_op_code) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in "
+ "fragment (expected %d)",
+ op_code, data->in_op_code);
+ eap_wsc_state(data, FAIL);
+ return -1;
+ }
+
+ if (len > wpabuf_tailroom(data->in_buf)) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow");
+ eap_wsc_state(data, FAIL);
+ return -1;
+ }
+
+ wpabuf_put_data(data->in_buf, buf, len);
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting for %lu "
+ "bytes more", (unsigned long) len,
+ (unsigned long) wpabuf_tailroom(data->in_buf));
+
+ return 0;
+}
+
+
+static int eap_wsc_process_fragment(struct eap_wsc_data *data,
+ u8 flags, u8 op_code, u16 message_length,
+ const u8 *buf, size_t len)
+{
+ /* Process a fragment that is not the last one of the message */
+ if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length "
+ "field in a fragmented packet");
+ return -1;
+ }
+
+ if (data->in_buf == NULL) {
+ /* First fragment of the message */
+ data->in_buf = wpabuf_alloc(message_length);
+ if (data->in_buf == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for "
+ "message");
+ return -1;
+ }
+ data->in_op_code = op_code;
+ wpabuf_put_data(data->in_buf, buf, len);
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in "
+ "first fragment, waiting for %lu bytes more",
+ (unsigned long) len,
+ (unsigned long) wpabuf_tailroom(data->in_buf));
+ }
+
+ return 0;
+}
+
+
+static void eap_wsc_process(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_wsc_data *data = priv;
+ const u8 *start, *pos, *end;
+ size_t len;
+ u8 op_code, flags;
+ u16 message_length = 0;
+ enum wps_process_res res;
+ struct wpabuf tmpbuf;
+
+ pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
+ respData, &len);
+ if (pos == NULL || len < 2)
+ return; /* Should not happen; message already verified */
+
+ start = pos;
+ end = start + len;
+
+ op_code = *pos++;
+ flags = *pos++;
+ if (flags & WSC_FLAGS_LF) {
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow");
+ return;
+ }
+ message_length = WPA_GET_BE16(pos);
+ pos += 2;
+
+ if (message_length < end - pos) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
+ "Length");
+ return;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d "
+ "Flags 0x%x Message Length %d",
+ op_code, flags, message_length);
+
+ if (data->state == WAIT_FRAG_ACK) {
+ if (op_code != WSC_FRAG_ACK) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
+ "in WAIT_FRAG_ACK state", op_code);
+ eap_wsc_state(data, FAIL);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged");
+ eap_wsc_state(data, MSG);
+ return;
+ }
+
+ if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG &&
+ op_code != WSC_Done) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
+ op_code);
+ eap_wsc_state(data, FAIL);
+ return;
+ }
+
+ if (data->in_buf &&
+ eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) {
+ eap_wsc_state(data, FAIL);
+ return;
+ }
+
+ if (flags & WSC_FLAGS_MF) {
+ if (eap_wsc_process_fragment(data, flags, op_code,
+ message_length, pos, end - pos) <
+ 0)
+ eap_wsc_state(data, FAIL);
+ else
+ eap_wsc_state(data, FRAG_ACK);
+ return;
+ }
+
+ if (data->in_buf == NULL) {
+ /* Wrap unfragmented messages as wpabuf without extra copy */
+ wpabuf_set(&tmpbuf, pos, end - pos);
+ data->in_buf = &tmpbuf;
+ }
+
+ res = wps_process_msg(data->wps, op_code, data->in_buf);
+ switch (res) {
+ case WPS_DONE:
+ wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed "
+ "successfully - report EAP failure");
+ eap_wsc_state(data, FAIL);
+ break;
+ case WPS_CONTINUE:
+ eap_wsc_state(data, MSG);
+ break;
+ case WPS_FAILURE:
+ wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
+ eap_wsc_state(data, FAIL);
+ break;
+ case WPS_PENDING:
+ wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing pending");
+ sm->method_pending = METHOD_PENDING_WAIT;
+ break;
+ }
+
+ if (data->in_buf != &tmpbuf)
+ wpabuf_free(data->in_buf);
+ data->in_buf = NULL;
+}
+
+
+static Boolean eap_wsc_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_wsc_data *data = priv;
+ return data->state == FAIL;
+}
+
+
+static Boolean eap_wsc_isSuccess(struct eap_sm *sm, void *priv)
+{
+ /* EAP-WSC will always result in EAP-Failure */
+ return FALSE;
+}
+
+
+int eap_server_wsc_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
+ "WSC");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_wsc_init;
+ eap->reset = eap_wsc_reset;
+ eap->buildReq = eap_wsc_buildReq;
+ eap->check = eap_wsc_check;
+ eap->process = eap_wsc_process;
+ eap->isDone = eap_wsc_isDone;
+ eap->isSuccess = eap_wsc_isSuccess;
+
+ ret = eap_server_method_register(eap);
+ if (ret)
+ eap_server_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..9af1d6d 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -1742,6 +1742,20 @@ static void eapol_sm_notify_pending(void *ctx)
}
+#ifdef CONFIG_WPS
+static int eapol_sm_wps_cred(void *ctx, struct wps_credential *cred)
+{
+ struct eapol_sm *sm = ctx;
+ wpa_printf(MSG_DEBUG, "EAPOL: received new WPS credential");
+ if (sm->ctx->wps_cred)
+ return sm->ctx->wps_cred(sm->ctx->ctx, cred);
+ return 0;
+}
+#else /* CONFIG_WPS */
+#define eapol_sm_wps_cred NULL
+#endif /* CONFIG_WPS */
+
+
#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
static void eapol_sm_eap_param_needed(void *ctx, const char *field,
const char *txt)
@@ -1767,6 +1781,7 @@ static struct eapol_callbacks eapol_cb =
eapol_sm_set_config_blob,
eapol_sm_get_config_blob,
eapol_sm_notify_pending,
+ eapol_sm_wps_cred,
eapol_sm_eap_param_needed
};
@@ -1804,6 +1819,7 @@ struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
conf.pkcs11_engine_path = ctx->pkcs11_engine_path;
conf.pkcs11_module_path = ctx->pkcs11_module_path;
#endif /* EAP_TLS_OPENSSL */
+ conf.mac_addr = ctx->mac_addr;
sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf);
if (sm->eap == NULL) {
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
index 719fbd3..f297d23 100644
--- a/src/eapol_supp/eapol_supp_sm.h
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -63,6 +63,7 @@ struct eapol_config {
struct eapol_sm;
struct wpa_config_blob;
+struct wps_credential;
/**
* struct eapol_ctx - Global (for all networks) EAPOL state machine context
@@ -199,6 +200,23 @@ struct eapol_ctx {
*/
const char *pkcs11_module_path;
#endif /* EAP_TLS_OPENSSL */
+ /**
+ * mac_addr - MAC address of the peer
+ *
+ * This is only used by EAP-WSC and can be left %NULL if not available.
+ */
+ const u8 *mac_addr;
+
+ /**
+ * wps_cred - Notify that new credential was received from WPS
+ * @ctx: Callback context (ctx)
+ * Returns: 0 on success (credential stored), -1 on failure
+ *
+ * This callback is only needed when using WPS Enrollee to configure
+ * new credentials. This can be left %NULL if no WPS functionality is
+ * enabled.
+ */
+ int (*wps_cred)(void *ctx, struct wps_credential *cred);
/**
* eap_param_needed - Notify that EAP parameter is needed
diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
index 19be48a..11c1b5b 100644
--- a/src/radius/radius_server.c
+++ b/src/radius/radius_server.c
@@ -93,6 +93,7 @@ struct radius_server_data {
int pac_key_refresh_time;
int eap_sim_aka_result_ind;
int tnc;
+ struct wps_context *wps;
int ipv6;
struct os_time start_time;
struct radius_server_counters counters;
@@ -323,6 +324,7 @@ radius_server_get_new_session(struct radius_server_data *data,
eap_conf.pac_key_refresh_time = data->pac_key_refresh_time;
eap_conf.eap_sim_aka_result_ind = data->eap_sim_aka_result_ind;
eap_conf.tnc = data->tnc;
+ eap_conf.wps = data->wps;
sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
&eap_conf);
if (sess->eap == NULL) {
@@ -1040,6 +1042,7 @@ radius_server_init(struct radius_server_conf *conf)
data->get_eap_user = conf->get_eap_user;
data->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
data->tnc = conf->tnc;
+ data->wps = conf->wps;
data->clients = radius_server_read_clients(conf->client_file,
conf->ipv6);
diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h
index 37c6527..2911e28 100644
--- a/src/radius/radius_server.h
+++ b/src/radius/radius_server.h
@@ -33,6 +33,7 @@ struct radius_server_conf {
int pac_key_refresh_time;
int eap_sim_aka_result_ind;
int tnc;
+ struct wps_context *wps;
int ipv6;
int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
int phase2, struct eap_user *user);
diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c
index b00c004..e2cf2a8 100644
--- a/src/rsn_supp/preauth.c
+++ b/src/rsn_supp/preauth.c
@@ -223,6 +223,7 @@ int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst,
ctx->eapol_send_ctx = sm;
ctx->set_config_blob = sm->ctx->set_config_blob;
ctx->get_config_blob = sm->ctx->get_config_blob;
+ ctx->mac_addr = sm->own_addr;
sm->preauth_eapol = eapol_sm_init(ctx);
if (sm->preauth_eapol == NULL) {
diff --git a/src/wps/Makefile b/src/wps/Makefile
new file mode 100644
index 0000000..37d649c
--- /dev/null
+++ b/src/wps/Makefile
@@ -0,0 +1,6 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ for d in $(SUBDIRS); do make -C $$d clean; done
+ rm -f *~ *.o *.d
diff --git a/src/wps/wps.c b/src/wps/wps.c
new file mode 100644
index 0000000..0c95a38
--- /dev/null
+++ b/src/wps/wps.c
@@ -0,0 +1,158 @@
+/*
+ * Wi-Fi Protected Setup
+ * Copyright (c) 2007-2008, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wps_i.h"
+#include "wps_dev_attr.h"
+
+
+struct wps_data * wps_init(const struct wps_config *cfg)
+{
+ struct wps_data *data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->authenticator = cfg->authenticator;
+ data->wps = cfg->wps;
+ data->registrar = cfg->registrar;
+ if (cfg->enrollee_mac_addr)
+ os_memcpy(data->mac_addr_e, cfg->enrollee_mac_addr, ETH_ALEN);
+ if (cfg->uuid) {
+ os_memcpy(cfg->registrar ? data->uuid_r : data->uuid_e,
+ cfg->uuid, WPS_UUID_LEN);
+ }
+ if (cfg->pin) {
+ data->dev_pw_id = DEV_PW_DEFAULT;
+ data->dev_password = os_malloc(cfg->pin_len);
+ if (data->dev_password == NULL) {
+ os_free(data);
+ return NULL;
+ }
+ os_memcpy(data->dev_password, cfg->pin, cfg->pin_len);
+ data->dev_password_len = cfg->pin_len;
+ }
+
+ data->pbc = cfg->pbc;
+ if (cfg->pbc) {
+ /* Use special PIN '00000000' for PBC */
+ data->dev_pw_id = DEV_PW_PUSHBUTTON;
+ os_free(data->dev_password);
+ data->dev_password = os_malloc(8);
+ if (data->dev_password == NULL) {
+ os_free(data);
+ return NULL;
+ }
+ os_memset(data->dev_password, '0', 8);
+ data->dev_password_len = 8;
+ }
+
+ data->wps_cred_cb = cfg->wps_cred_cb;
+ data->cb_ctx = cfg->cb_ctx;
+
+ data->state = data->registrar ? RECV_M1 : SEND_M1;
+
+ return data;
+}
+
+
+void wps_deinit(struct wps_data *data)
+{
+ if (data->wps_pin_revealed) {
+ wpa_printf(MSG_DEBUG, "WPS: Full PIN information revealed and "
+ "negotiation failed");
+ if (data->registrar)
+ wps_registrar_invalidate_pin(data->registrar,
+ data->uuid_e);
+ } else if (data->registrar)
+ wps_registrar_unlock_pin(data->registrar, data->uuid_e);
+
+ wpabuf_free(data->dh_privkey);
+ wpabuf_free(data->dh_pubkey_e);
+ wpabuf_free(data->dh_pubkey_r);
+ wpabuf_free(data->last_msg);
+ os_free(data->dev_password);
+ os_free(data->new_psk);
+ wps_device_data_free(&data->peer_dev);
+ os_free(data);
+}
+
+
+enum wps_process_res wps_process_msg(struct wps_data *wps, u8 op_code,
+ const struct wpabuf *msg)
+{
+ if (wps->registrar)
+ return wps_registrar_process_msg(wps, op_code, msg);
+ else
+ return wps_enrollee_process_msg(wps, op_code, msg);
+}
+
+
+struct wpabuf * wps_get_msg(struct wps_data *wps, u8 *op_code)
+{
+ if (wps->registrar)
+ return wps_registrar_get_msg(wps, op_code);
+ else
+ return wps_enrollee_get_msg(wps, op_code);
+}
+
+
+int wps_is_selected_pbc_registrar(const u8 *buf, size_t len)
+{
+ struct wps_parse_attr attr;
+ struct wpabuf msg;
+
+ wpabuf_set(&msg, buf, len);
+ if (wps_parse_msg(&msg, &attr) < 0 ||
+ !attr.selected_registrar || *attr.selected_registrar == 0 ||
+ !attr.sel_reg_config_methods ||
+ !(WPA_GET_BE16(attr.sel_reg_config_methods) &
+ WPS_CONFIG_PUSHBUTTON) ||
+ !attr.dev_password_id ||
+ WPA_GET_BE16(attr.dev_password_id) != DEV_PW_PUSHBUTTON)
+ return 0;
+
+ return 1;
+}
+
+
+int wps_is_selected_pin_registrar(const u8 *buf, size_t len)
+{
+ struct wps_parse_attr attr;
+ struct wpabuf msg;
+
+ wpabuf_set(&msg, buf, len);
+ if (wps_parse_msg(&msg, &attr) < 0 ||
+ !attr.selected_registrar || *attr.selected_registrar == 0 ||
+ !attr.sel_reg_config_methods ||
+ !(WPA_GET_BE16(attr.sel_reg_config_methods) &
+ (WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD)) ||
+ !attr.dev_password_id ||
+ WPA_GET_BE16(attr.dev_password_id) == DEV_PW_PUSHBUTTON)
+ return 0;
+
+ return 1;
+}
+
+
+const u8 * wps_get_uuid_e(const u8 *buf, size_t len)
+{
+ struct wps_parse_attr attr;
+ struct wpabuf msg;
+
+ wpabuf_set(&msg, buf, len);
+ if (wps_parse_msg(&msg, &attr) < 0)
+ return NULL;
+ return attr.uuid_e;
+}
diff --git a/src/wps/wps.h b/src/wps/wps.h
new file mode 100644
index 0000000..68b000a
--- /dev/null
+++ b/src/wps/wps.h
@@ -0,0 +1,132 @@
+/*
+ * Wi-Fi Protected Setup
+ * Copyright (c) 2007, 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.
+ */
+
+#ifndef WPS_H
+#define WPS_H
+
+enum wsc_op_code {
+ WSC_Start = 0x01,
+ WSC_ACK = 0x02,
+ WSC_NACK = 0x03,
+ WSC_MSG = 0x04,
+ WSC_Done = 0x05,
+ WSC_FRAG_ACK = 0x06
+};
+
+struct wps_registrar;
+
+struct wps_credential {
+ u8 ssid[32];
+ size_t ssid_len;
+ u16 auth_type;
+ u16 encr_type;
+ u8 key_idx;
+ u8 key[64];
+ size_t key_len;
+ u8 mac_addr[ETH_ALEN];
+};
+
+struct wps_config {
+ int authenticator;
+ struct wps_context *wps;
+ struct wps_registrar *registrar; /* NULL for Enrollee */
+ const u8 *enrollee_mac_addr; /* NULL for Registrar */
+ const u8 *pin; /* Enrollee Device Password (NULL for Registrar or PBC)
+ */
+ size_t pin_len;
+ const u8 *uuid; /* 128-bit Enrollee UUID (NULL for Registrar) */
+ int pbc;
+
+ int (*wps_cred_cb)(void *ctx, struct wps_credential *cred);
+ void *cb_ctx;
+};
+
+struct wps_data * wps_init(const struct wps_config *cfg);
+
+void wps_deinit(struct wps_data *data);
+
+enum wps_process_res {
+ WPS_DONE, WPS_CONTINUE, WPS_FAILURE, WPS_PENDING
+};
+enum wps_process_res wps_process_msg(struct wps_data *wps, u8 op_code,
+ const struct wpabuf *msg);
+
+struct wpabuf * wps_get_msg(struct wps_data *wps, u8 *op_code);
+
+int wps_is_selected_pbc_registrar(const u8 *buf, size_t len);
+int wps_is_selected_pin_registrar(const u8 *buf, size_t len);
+const u8 * wps_get_uuid_e(const u8 *buf, size_t len);
+
+
+struct wps_device_data {
+ u8 mac_addr[ETH_ALEN];
+ char *device_name;
+ char *manufacturer;
+ char *model_name;
+ char *model_number;
+ char *serial_number;
+ u16 categ;
+ u32 oui;
+ u16 sub_categ;
+ u32 os_version;
+};
+
+
+struct wps_registrar_config {
+ int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk,
+ size_t psk_len);
+ int (*set_ie_cb)(void *ctx, const u8 *beacon_ie, size_t beacon_ie_len,
+ const u8 *probe_resp_ie, size_t probe_resp_ie_len);
+ void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
+ const struct wps_device_data *dev);
+ void *cb_ctx;
+};
+
+
+struct wps_context {
+ int ap;
+ struct wps_registrar *registrar;
+ int wps_state;
+ int ap_setup_locked;
+ u8 uuid[16];
+ u8 ssid[32];
+ size_t ssid_len;
+ struct wps_device_data dev;
+ u16 config_methods; /* bit field of WPS_CONFIG_* */
+ u16 encr_types; /* bit field of WPS_ENCR_* */
+ u16 auth_types; /* bit field of WPS_AUTH_* */
+ u8 *network_key; /* or NULL to generate per-device PSK */
+ size_t network_key_len;
+
+ int (*cred_cb)(void *ctx, const struct wps_credential *cred);
+ void *cb_ctx;
+};
+
+
+struct wps_registrar *
+wps_registrar_init(struct wps_context *wps,
+ const struct wps_registrar_config *cfg);
+void wps_registrar_deinit(struct wps_registrar *reg);
+int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid,
+ const u8 *pin, size_t pin_len);
+int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid);
+int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid);
+int wps_registrar_button_pushed(struct wps_registrar *reg);
+void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
+ const struct wpabuf *wps_data);
+
+struct wpabuf * wps_enrollee_build_assoc_req_ie(void);
+struct wpabuf * wps_enrollee_build_probe_req_ie(int pbc, const u8 *uuid);
+
+#endif /* WPS_H */
diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c
new file mode 100644
index 0000000..9d76347
--- /dev/null
+++ b/src/wps/wps_common.c
@@ -0,0 +1,839 @@
+/*
+ * Wi-Fi Protected Setup - common functionality
+ * Copyright (c) 2008, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "dh_groups.h"
+#include "sha256.h"
+#include "aes_wrap.h"
+#include "crypto.h"
+#include "wps_i.h"
+
+
+static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
+ const u8 *pos, u16 len)
+{
+ switch (type) {
+ case ATTR_VERSION:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u",
+ len);
+ return -1;
+ }
+ attr->version = pos;
+ break;
+ case ATTR_MSG_TYPE:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type "
+ "length %u", len);
+ return -1;
+ }
+ attr->msg_type = pos;
+ break;
+ case ATTR_ENROLLEE_NONCE:
+ if (len != WPS_NONCE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce "
+ "length %u", len);
+ return -1;
+ }
+ attr->enrollee_nonce = pos;
+ break;
+ case ATTR_REGISTRAR_NONCE:
+ if (len != WPS_NONCE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce "
+ "length %u", len);
+ return -1;
+ }
+ attr->registrar_nonce = pos;
+ break;
+ case ATTR_UUID_E:
+ if (len != WPS_UUID_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u",
+ len);
+ return -1;
+ }
+ attr->uuid_e = pos;
+ break;
+ case ATTR_UUID_R:
+ if (len != WPS_UUID_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u",
+ len);
+ return -1;
+ }
+ attr->uuid_r = pos;
+ break;
+ case ATTR_AUTH_TYPE_FLAGS:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
+ "Type Flags length %u", len);
+ return -1;
+ }
+ attr->auth_type_flags = pos;
+ break;
+ case ATTR_ENCR_TYPE_FLAGS:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type "
+ "Flags length %u", len);
+ return -1;
+ }
+ attr->encr_type_flags = pos;
+ break;
+ case ATTR_CONN_TYPE_FLAGS:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type "
+ "Flags length %u", len);
+ return -1;
+ }
+ attr->conn_type_flags = pos;
+ break;
+ case ATTR_CONFIG_METHODS:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods "
+ "length %u", len);
+ return -1;
+ }
+ attr->config_methods = pos;
+ break;
+ case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Selected "
+ "Registrar Config Methods length %u", len);
+ return -1;
+ }
+ attr->sel_reg_config_methods = pos;
+ break;
+ case ATTR_PRIMARY_DEV_TYPE:
+ if (len != sizeof(struct wps_dev_type)) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device "
+ "Type length %u", len);
+ return -1;
+ }
+ attr->primary_dev_type = pos;
+ break;
+ case ATTR_RF_BANDS:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length "
+ "%u", len);
+ return -1;
+ }
+ attr->rf_bands = pos;
+ break;
+ case ATTR_ASSOC_STATE:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Association State "
+ "length %u", len);
+ return -1;
+ }
+ attr->assoc_state = pos;
+ break;
+ case ATTR_CONFIG_ERROR:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration "
+ "Error length %u", len);
+ return -1;
+ }
+ attr->config_error = pos;
+ break;
+ case ATTR_DEV_PASSWORD_ID:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password "
+ "ID length %u", len);
+ return -1;
+ }
+ attr->dev_password_id = pos;
+ break;
+ case ATTR_OS_VERSION:
+ if (len != 4) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "
+ "%u", len);
+ return -1;
+ }
+ attr->os_version = pos;
+ break;
+ case ATTR_WPS_STATE:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected "
+ "Setup State length %u", len);
+ return -1;
+ }
+ attr->wps_state = pos;
+ break;
+ case ATTR_AUTHENTICATOR:
+ if (len != WPS_AUTHENTICATOR_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator "
+ "length %u", len);
+ return -1;
+ }
+ attr->authenticator = pos;
+ break;
+ case ATTR_R_HASH1:
+ if (len != WPS_HASH_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u",
+ len);
+ return -1;
+ }
+ attr->r_hash1 = pos;
+ break;
+ case ATTR_R_HASH2:
+ if (len != WPS_HASH_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u",
+ len);
+ return -1;
+ }
+ attr->r_hash2 = pos;
+ break;
+ case ATTR_E_HASH1:
+ if (len != WPS_HASH_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u",
+ len);
+ return -1;
+ }
+ attr->e_hash1 = pos;
+ break;
+ case ATTR_E_HASH2:
+ if (len != WPS_HASH_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u",
+ len);
+ return -1;
+ }
+ attr->e_hash2 = pos;
+ break;
+ case ATTR_R_SNONCE1:
+ if (len != WPS_SECRET_NONCE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length "
+ "%u", len);
+ return -1;
+ }
+ attr->r_snonce1 = pos;
+ break;
+ case ATTR_R_SNONCE2:
+ if (len != WPS_SECRET_NONCE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length "
+ "%u", len);
+ return -1;
+ }
+ attr->r_snonce2 = pos;
+ break;
+ case ATTR_E_SNONCE1:
+ if (len != WPS_SECRET_NONCE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length "
+ "%u", len);
+ return -1;
+ }
+ attr->e_snonce1 = pos;
+ break;
+ case ATTR_E_SNONCE2:
+ if (len != WPS_SECRET_NONCE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length "
+ "%u", len);
+ return -1;
+ }
+ attr->e_snonce2 = pos;
+ break;
+ case ATTR_KEY_WRAP_AUTH:
+ if (len != WPS_KWA_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap "
+ "Authenticator length %u", len);
+ return -1;
+ }
+ attr->key_wrap_auth = pos;
+ break;
+ case ATTR_AUTH_TYPE:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
+ "Type length %u", len);
+ return -1;
+ }
+ attr->auth_type = pos;
+ break;
+ case ATTR_ENCR_TYPE:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption "
+ "Type length %u", len);
+ return -1;
+ }
+ attr->encr_type = pos;
+ break;
+ case ATTR_NETWORK_INDEX:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index "
+ "length %u", len);
+ return -1;
+ }
+ attr->network_idx = pos;
+ break;
+ case ATTR_NETWORK_KEY_INDEX:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index "
+ "length %u", len);
+ return -1;
+ }
+ attr->network_key_idx = pos;
+ break;
+ case ATTR_MAC_ADDR:
+ if (len != ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address "
+ "length %u", len);
+ return -1;
+ }
+ attr->mac_addr = pos;
+ break;
+ case ATTR_KEY_PROVIDED_AUTO:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Key Provided "
+ "Automatically length %u", len);
+ return -1;
+ }
+ attr->key_prov_auto = pos;
+ break;
+ case ATTR_802_1X_ENABLED:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid 802.1X Enabled "
+ "length %u", len);
+ return -1;
+ }
+ attr->dot1x_enabled = pos;
+ break;
+ case ATTR_SELECTED_REGISTRAR:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
+ " length %u", len);
+ return -1;
+ }
+ attr->selected_registrar = pos;
+ break;
+ case ATTR_MANUFACTURER:
+ attr->manufacturer = pos;
+ attr->manufacturer_len = len;
+ break;
+ case ATTR_MODEL_NAME:
+ attr->model_name = pos;
+ attr->model_name_len = len;
+ break;
+ case ATTR_MODEL_NUMBER:
+ attr->model_number = pos;
+ attr->model_number_len = len;
+ break;
+ case ATTR_SERIAL_NUMBER:
+ attr->serial_number = pos;
+ attr->serial_number_len = len;
+ break;
+ case ATTR_DEV_NAME:
+ attr->dev_name = pos;
+ attr->dev_name_len = len;
+ break;
+ case ATTR_PUBLIC_KEY:
+ attr->public_key = pos;
+ attr->public_key_len = len;
+ break;
+ case ATTR_ENCR_SETTINGS:
+ attr->encr_settings = pos;
+ attr->encr_settings_len = len;
+ break;
+ case ATTR_CRED:
+ if (attr->num_cred >= MAX_CRED_COUNT) {
+ wpa_printf(MSG_DEBUG, "WPS: Skipped Credential "
+ "attribute (max %d credentials)",
+ MAX_CRED_COUNT);
+ break;
+ }
+ attr->cred[attr->num_cred] = pos;
+ attr->cred_len[attr->num_cred] = len;
+ attr->num_cred++;
+ break;
+ case ATTR_SSID:
+ attr->ssid = pos;
+ attr->ssid_len = len;
+ break;
+ case ATTR_NETWORK_KEY:
+ attr->network_key = pos;
+ attr->network_key_len = len;
+ break;
+ case ATTR_EAP_TYPE:
+ attr->eap_type = pos;
+ attr->eap_type_len = len;
+ break;
+ case ATTR_EAP_IDENTITY:
+ attr->eap_identity = pos;
+ attr->eap_identity_len = len;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
+ "len=%u", type, len);
+ break;
+ }
+
+ return 0;
+}
+
+
+int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
+{
+ const u8 *pos, *end;
+ u16 type, len;
+
+ os_memset(attr, 0, sizeof(*attr));
+ pos = wpabuf_head(msg);
+ end = pos + wpabuf_len(msg);
+
+ while (pos < end) {
+ if (end - pos < 4) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid message - "
+ "%lu bytes remaining",
+ (unsigned long) (end - pos));
+ return -1;
+ }
+
+ type = WPA_GET_BE16(pos);
+ pos += 2;
+ len = WPA_GET_BE16(pos);
+ pos += 2;
+ wpa_printf(MSG_MSGDUMP, "WPS: attr type=0x%x len=%u",
+ type, len);
+ if (len > end - pos) {
+ wpa_printf(MSG_DEBUG, "WPS: Attribute overflow");
+ return -1;
+ }
+
+ if (wps_set_attr(attr, type, pos, len) < 0)
+ return -1;
+
+ pos += len;
+ }
+
+ return 0;
+}
+
+
+void wps_kdf(const u8 *key, const char *label, u8 *res, size_t res_len)
+{
+ u8 i_buf[4], key_bits[4];
+ const u8 *addr[3];
+ size_t len[3];
+ int i, iter;
+ u8 hash[SHA256_MAC_LEN], *opos;
+ size_t left;
+
+ WPA_PUT_BE32(key_bits, res_len * 8);
+
+ addr[0] = i_buf;
+ len[0] = sizeof(i_buf);
+ addr[1] = (const u8 *) label;
+ len[1] = os_strlen(label);
+ addr[2] = key_bits;
+ len[2] = sizeof(key_bits);
+
+ iter = (res_len + SHA256_MAC_LEN - 1) / SHA256_MAC_LEN;
+ opos = res;
+ left = res_len;
+
+ for (i = 1; i <= iter; i++) {
+ WPA_PUT_BE32(i_buf, i);
+ hmac_sha256_vector(key, SHA256_MAC_LEN, 3, addr, len, hash);
+ if (i < iter) {
+ os_memcpy(opos, hash, SHA256_MAC_LEN);
+ opos += SHA256_MAC_LEN;
+ left -= SHA256_MAC_LEN;
+ } else
+ os_memcpy(opos, hash, left);
+ }
+}
+
+
+int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg)
+{
+ struct wpabuf *pubkey;
+
+ wpa_printf(MSG_DEBUG, "WPS: * Public Key");
+ pubkey = dh_init(dh_groups_get(WPS_DH_GROUP), &wps->dh_privkey);
+ if (pubkey == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to initialize "
+ "Diffie-Hellman handshake");
+ return -1;
+ }
+
+ wpabuf_put_be16(msg, ATTR_PUBLIC_KEY);
+ wpabuf_put_be16(msg, wpabuf_len(pubkey));
+ wpabuf_put_buf(msg, pubkey);
+
+ if (wps->registrar) {
+ wpabuf_free(wps->dh_pubkey_r);
+ wps->dh_pubkey_r = pubkey;
+ } else {
+ wpabuf_free(wps->dh_pubkey_e);
+ wps->dh_pubkey_e = pubkey;
+ }
+
+ return 0;
+}
+
+
+int wps_derive_keys(struct wps_data *wps)
+{
+ struct wpabuf *pubkey, *dh_shared;
+ u8 dhkey[SHA256_MAC_LEN], kdk[SHA256_MAC_LEN];
+ const u8 *addr[3];
+ size_t len[3];
+ u8 keys[WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN + WPS_EMSK_LEN];
+
+ if (wps->dh_privkey == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Own DH private key not available");
+ return -1;
+ }
+
+ pubkey = wps->registrar ? wps->dh_pubkey_e : wps->dh_pubkey_r;
+ if (pubkey == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Peer DH public key not available");
+ return -1;
+ }
+
+ dh_shared = dh_derive_shared(pubkey, wps->dh_privkey,
+ dh_groups_get(WPS_DH_GROUP));
+ if (dh_shared == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to derive DH shared key");
+ return -1;
+ }
+
+ /* Own DH private key is not needed anymore */
+ wpabuf_free(wps->dh_privkey);
+ wps->dh_privkey = NULL;
+
+ wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH shared key", dh_shared);
+
+ /* DHKey = SHA-256(g^AB mod p) */
+ addr[0] = wpabuf_head(dh_shared);
+ len[0] = wpabuf_len(dh_shared);
+ sha256_vector(1, addr, len, dhkey);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: DHKey", dhkey, sizeof(dhkey));
+ wpabuf_free(dh_shared);
+
+ /* KDK = HMAC-SHA-256_DHKey(N1 || EnrolleeMAC || N2) */
+ addr[0] = wps->nonce_e;
+ len[0] = WPS_NONCE_LEN;
+ addr[1] = wps->mac_addr_e;
+ len[1] = ETH_ALEN;
+ addr[2] = wps->nonce_r;
+ len[2] = WPS_NONCE_LEN;
+ hmac_sha256_vector(dhkey, sizeof(dhkey), 3, addr, len, kdk);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: KDK", kdk, sizeof(kdk));
+
+ wps_kdf(kdk, "Wi-Fi Easy and Secure Key Derivation",
+ keys, sizeof(keys));
+ os_memcpy(wps->authkey, keys, WPS_AUTHKEY_LEN);
+ os_memcpy(wps->keywrapkey, keys + WPS_AUTHKEY_LEN, WPS_KEYWRAPKEY_LEN);
+ os_memcpy(wps->emsk, keys + WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN,
+ WPS_EMSK_LEN);
+
+ wpa_hexdump_key(MSG_DEBUG, "WPS: AuthKey",
+ wps->authkey, WPS_AUTHKEY_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: KeyWrapKey",
+ wps->keywrapkey, WPS_KEYWRAPKEY_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: EMSK", wps->emsk, WPS_EMSK_LEN);
+
+ return 0;
+}
+
+
+int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg)
+{
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[2];
+ size_t len[2];
+
+ if (wps->last_msg == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Last message not available for "
+ "building authenticator");
+ return -1;
+ }
+
+ /* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*)
+ * (M_curr* is M_curr without the Authenticator attribute)
+ */
+ addr[0] = wpabuf_head(wps->last_msg);
+ len[0] = wpabuf_len(wps->last_msg);
+ addr[1] = wpabuf_head(msg);
+ len[1] = wpabuf_len(msg);
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash);
+
+ wpa_printf(MSG_DEBUG, "WPS: * Authenticator");
+ wpabuf_put_be16(msg, ATTR_AUTHENTICATOR);
+ wpabuf_put_be16(msg, WPS_AUTHENTICATOR_LEN);
+ wpabuf_put_data(msg, hash, WPS_AUTHENTICATOR_LEN);
+
+ return 0;
+}
+
+
+int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
+ const struct wpabuf *msg)
+{
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[2];
+ size_t len[2];
+
+ if (authenticator == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Authenticator attribute "
+ "included");
+ return -1;
+ }
+
+ if (wps->last_msg == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Last message not available for "
+ "validating authenticator");
+ return -1;
+ }
+
+ /* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*)
+ * (M_curr* is M_curr without the Authenticator attribute)
+ */
+ addr[0] = wpabuf_head(wps->last_msg);
+ len[0] = wpabuf_len(wps->last_msg);
+ addr[1] = wpabuf_head(msg);
+ len[1] = wpabuf_len(msg) - 4 - WPS_AUTHENTICATOR_LEN;
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash);
+
+ if (os_memcmp(hash, authenticator, WPS_AUTHENTICATOR_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Incorrect Authenticator");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
+ size_t dev_passwd_len)
+{
+ u8 hash[SHA256_MAC_LEN];
+
+ hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, dev_passwd,
+ (dev_passwd_len + 1) / 2, hash);
+ os_memcpy(wps->psk1, hash, WPS_PSK_LEN);
+ hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN,
+ dev_passwd + (dev_passwd_len + 1) / 2,
+ dev_passwd_len / 2, hash);
+ os_memcpy(wps->psk2, hash, WPS_PSK_LEN);
+
+ wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Device Password",
+ dev_passwd, dev_passwd_len);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: PSK1", wps->psk1, WPS_PSK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: PSK2", wps->psk2, WPS_PSK_LEN);
+}
+
+
+struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
+ size_t encr_len)
+{
+ struct wpabuf *decrypted;
+ const size_t block_size = 16;
+ size_t i;
+ u8 pad;
+ const u8 *pos;
+
+ /* AES-128-CBC */
+ if (encr == NULL || encr_len < 2 * block_size || encr_len % block_size)
+ {
+ wpa_printf(MSG_DEBUG, "WPS: No Encrypted Settings received");
+ return NULL;
+ }
+
+ decrypted = wpabuf_alloc(encr_len - block_size);
+ if (decrypted == NULL)
+ return NULL;
+
+ wpa_hexdump(MSG_MSGDUMP, "WPS: Encrypted Settings", encr, encr_len);
+ wpabuf_put_data(decrypted, encr + block_size, encr_len - block_size);
+ if (aes_128_cbc_decrypt(wps->keywrapkey, encr, wpabuf_mhead(decrypted),
+ wpabuf_len(decrypted))) {
+ wpabuf_free(decrypted);
+ return NULL;
+ }
+
+ wpa_hexdump_buf_key(MSG_MSGDUMP, "WPS: Decrypted Encrypted Settings",
+ decrypted);
+
+ pos = wpabuf_head_u8(decrypted) + wpabuf_len(decrypted) - 1;
+ pad = *pos;
+ if (pad > wpabuf_len(decrypted)) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad value");
+ wpabuf_free(decrypted);
+ return NULL;
+ }
+ for (i = 0; i < pad; i++) {
+ if (*pos-- != pad) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad "
+ "string");
+ wpabuf_free(decrypted);
+ return NULL;
+ }
+ }
+ decrypted->used -= pad;
+
+ return decrypted;
+}
+
+
+int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg,
+ const u8 *key_wrap_auth)
+{
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *head;
+ size_t len;
+
+ if (key_wrap_auth == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No KWA in decrypted attribute");
+ return -1;
+ }
+
+ head = wpabuf_head(msg);
+ len = wpabuf_len(msg) - 4 - WPS_KWA_LEN;
+ if (head + len != key_wrap_auth - 4) {
+ wpa_printf(MSG_DEBUG, "WPS: KWA not in the end of the "
+ "decrypted attribute");
+ return -1;
+ }
+
+ hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, head, len, hash);
+ if (os_memcmp(hash, key_wrap_auth, WPS_KWA_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid KWA");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int wps_build_version(struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Version");
+ wpabuf_put_be16(msg, ATTR_VERSION);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, WPS_VERSION);
+ return 0;
+}
+
+
+int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Message Type (%d)", msg_type);
+ wpabuf_put_be16(msg, ATTR_MSG_TYPE);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, msg_type);
+ return 0;
+}
+
+
+int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Enrollee Nonce");
+ wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE);
+ wpabuf_put_be16(msg, WPS_NONCE_LEN);
+ wpabuf_put_data(msg, wps->nonce_e, WPS_NONCE_LEN);
+ return 0;
+}
+
+
+int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Registrar Nonce");
+ wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
+ wpabuf_put_be16(msg, WPS_NONCE_LEN);
+ wpabuf_put_data(msg, wps->nonce_r, WPS_NONCE_LEN);
+ return 0;
+}
+
+
+int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Authentication Type Flags");
+ wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, WPS_AUTH_TYPES);
+ return 0;
+}
+
+
+int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Encryption Type Flags");
+ wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, WPS_ENCR_TYPES);
+ return 0;
+}
+
+
+int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Connection Type Flags");
+ wpabuf_put_be16(msg, ATTR_CONN_TYPE_FLAGS);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, WPS_CONN_ESS);
+ return 0;
+}
+
+
+int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Association State");
+ wpabuf_put_be16(msg, ATTR_ASSOC_STATE);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, WPS_ASSOC_NOT_ASSOC);
+ return 0;
+}
+
+
+int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg)
+{
+ u8 hash[SHA256_MAC_LEN];
+
+ wpa_printf(MSG_DEBUG, "WPS: * Key Wrap Authenticator");
+ hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, wpabuf_head(msg),
+ wpabuf_len(msg), hash);
+
+ wpabuf_put_be16(msg, ATTR_KEY_WRAP_AUTH);
+ wpabuf_put_be16(msg, WPS_KWA_LEN);
+ wpabuf_put_data(msg, hash, WPS_KWA_LEN);
+ return 0;
+}
+
+
+int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
+ struct wpabuf *plain)
+{
+ size_t pad_len;
+ const size_t block_size = 16;
+ u8 *iv, *data;
+
+ wpa_printf(MSG_DEBUG, "WPS: * Encrypted Settings");
+
+ /* PKCS#5 v2.0 pad */
+ pad_len = block_size - wpabuf_len(plain) % block_size;
+ os_memset(wpabuf_put(plain, pad_len), pad_len, pad_len);
+
+ wpabuf_put_be16(msg, ATTR_ENCR_SETTINGS);
+ wpabuf_put_be16(msg, block_size + wpabuf_len(plain));
+
+ iv = wpabuf_put(msg, block_size);
+ if (os_get_random(iv, block_size) < 0)
+ return -1;
+
+ data = wpabuf_put(msg, 0);
+ wpabuf_put_buf(msg, plain);
+ if (aes_128_cbc_encrypt(wps->keywrapkey, iv, data, wpabuf_len(plain)))
+ return -1;
+
+ return 0;
+}
diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h
new file mode 100644
index 0000000..141efba
--- /dev/null
+++ b/src/wps/wps_defs.h
@@ -0,0 +1,294 @@
+/*
+ * Wi-Fi Protected Setup - message definitions
+ * Copyright (c) 2008, 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.
+ */
+
+#ifndef WPS_DEFS_H
+#define WPS_DEFS_H
+
+#define WPS_VERSION 0x10
+
+/* Diffie-Hellman 1536-bit MODP Group; RFC 3526, Group 5 */
+#define WPS_DH_GROUP 5
+
+#define WPS_UUID_LEN 16
+#define WPS_NONCE_LEN 16
+#define WPS_AUTHENTICATOR_LEN 8
+#define WPS_AUTHKEY_LEN 32
+#define WPS_KEYWRAPKEY_LEN 16
+#define WPS_EMSK_LEN 32
+#define WPS_PSK_LEN 16
+#define WPS_SECRET_NONCE_LEN 16
+#define WPS_HASH_LEN 32
+#define WPS_KWA_LEN 8
+
+/* Attribute Types */
+enum wps_attribute {
+ ATTR_AP_CHANNEL = 0x1001,
+ ATTR_ASSOC_STATE = 0x1002,
+ ATTR_AUTH_TYPE = 0x1003,
+ ATTR_AUTH_TYPE_FLAGS = 0x1004,
+ ATTR_AUTHENTICATOR = 0x1005,
+ ATTR_CONFIG_METHODS = 0x1008,
+ ATTR_CONFIG_ERROR = 0x1009,
+ ATTR_CONFIRM_URL4 = 0x100a,
+ ATTR_CONFIRM_URL6 = 0x100b,
+ ATTR_CONN_TYPE = 0x100c,
+ ATTR_CONN_TYPE_FLAGS = 0x100d,
+ ATTR_CRED = 0x100e,
+ ATTR_ENCR_TYPE = 0x100f,
+ ATTR_ENCR_TYPE_FLAGS = 0x1010,
+ ATTR_DEV_NAME = 0x1011,
+ ATTR_DEV_PASSWORD_ID = 0x1012,
+ ATTR_E_HASH1 = 0x1014,
+ ATTR_E_HASH2 = 0x1015,
+ ATTR_E_SNONCE1 = 0x1016,
+ ATTR_E_SNONCE2 = 0x1017,
+ ATTR_ENCR_SETTINGS = 0x1018,
+ ATTR_ENROLLEE_NONCE = 0x101a,
+ ATTR_FEATURE_ID = 0x101b,
+ ATTR_IDENTITY = 0x101c,
+ ATTR_IDENTITY_PROOF = 0x101d,
+ ATTR_KEY_WRAP_AUTH = 0x101e,
+ ATTR_KEY_ID = 0x101f,
+ ATTR_MAC_ADDR = 0x1020,
+ ATTR_MANUFACTURER = 0x1021,
+ ATTR_MSG_TYPE = 0x1022,
+ ATTR_MODEL_NAME = 0x1023,
+ ATTR_MODEL_NUMBER = 0x1024,
+ ATTR_NETWORK_INDEX = 0x1026,
+ ATTR_NETWORK_KEY = 0x1027,
+ ATTR_NETWORK_KEY_INDEX = 0x1028,
+ ATTR_NEW_DEVICE_NAME = 0x1029,
+ ATTR_NEW_PASSWORD = 0x102a,
+ ATTR_OOB_DEVICE_PASSWORD = 0x102c,
+ ATTR_OS_VERSION = 0x102d,
+ ATTR_POWER_LEVEL = 0x102f,
+ ATTR_PSK_CURRENT = 0x1030,
+ ATTR_PSK_MAX = 0x1031,
+ ATTR_PUBLIC_KEY = 0x1032,
+ ATTR_RADIO_ENABLE = 0x1033,
+ ATTR_REBOOT = 0x1034,
+ ATTR_REGISTRAR_CURRENT = 0x1035,
+ ATTR_REGISTRAR_ESTABLISHED = 0x1036,
+ ATTR_REGISTRAR_LIST = 0x1037,
+ ATTR_REGISTRAR_MAX = 0x1038,
+ ATTR_REGISTRAR_NONCE = 0x1039,
+ ATTR_REQUEST_TYPE = 0x103a,
+ ATTR_RESPONSE_TYPE = 0x103b,
+ ATTR_RF_BANDS = 0x103c,
+ ATTR_R_HASH1 = 0x103d,
+ ATTR_R_HASH2 = 0x103e,
+ ATTR_R_SNONCE1 = 0x103f,
+ ATTR_R_SNONCE2 = 0x1040,
+ ATTR_SELECTED_REGISTRAR = 0x1041,
+ ATTR_SERIAL_NUMBER = 0x1042,
+ ATTR_WPS_STATE = 0x1044,
+ ATTR_SSID = 0x1045,
+ ATTR_TOTAL_NETWORKS = 0x1046,
+ ATTR_UUID_E = 0x1047,
+ ATTR_UUID_R = 0x1048,
+ ATTR_VENDOR_EXT = 0x1049,
+ ATTR_VERSION = 0x104a,
+ ATTR_X509_CERT_REQ = 0x104b,
+ ATTR_X509_CERT = 0x104c,
+ ATTR_EAP_IDENTITY = 0x104d,
+ ATTR_MSG_COUNTER = 0x104e,
+ ATTR_PUBKEY_HASH = 0x104f,
+ ATTR_REKEY_KEY = 0x1050,
+ ATTR_KEY_LIFETIME = 0x1051,
+ ATTR_PERMITTED_CFG_METHODS = 0x1052,
+ ATTR_SELECTED_REGISTRAR_CONFIG_METHODS = 0x1053,
+ ATTR_PRIMARY_DEV_TYPE = 0x1054,
+ ATTR_SECONDARY_DEV_TYP_ELIST = 0x1055,
+ ATTR_PORTABLE_DEV = 0x1056,
+ ATTR_AP_SETUP_LOCKED = 0x1057,
+ ATTR_APPLICATION_EXT = 0x1058,
+ ATTR_EAP_TYPE = 0x1059,
+ ATTR_IV = 0x1060,
+ ATTR_KEY_PROVIDED_AUTO = 0x1061,
+ ATTR_802_1X_ENABLED = 0x1062,
+ ATTR_APPSESSIONKEY = 0x1063,
+ ATTR_WEPTRANSMITKEY = 0x1064
+};
+
+/* Device Password ID */
+enum wps_dev_password_id {
+ DEV_PW_DEFAULT = 0x0000,
+ DEV_PW_USER_SPECIFIED = 0x0001,
+ DEV_PW_MACHINE_SPECIFIED = 0x0002,
+ DEV_PW_REKEY = 0x0003,
+ DEV_PW_PUSHBUTTON = 0x0004,
+ DEV_PW_REGISTRAR_SPECIFIED = 0x0005
+};
+
+/* Message Type */
+enum wps_msg_type {
+ WPS_Beacon = 0x01,
+ WPS_ProbeRequest = 0x02,
+ WPS_ProbeResponse = 0x03,
+ WPS_M1 = 0x04,
+ WPS_M2 = 0x05,
+ WPS_M2D = 0x06,
+ WPS_M3 = 0x07,
+ WPS_M4 = 0x08,
+ WPS_M5 = 0x09,
+ WPS_M6 = 0x0a,
+ WPS_M7 = 0x0b,
+ WPS_M8 = 0x0c,
+ WPS_WSC_ACK = 0x0d,
+ WPS_WSC_NACK = 0x0e,
+ WPS_WSC_DONE = 0x0f
+};
+
+/* Authentication Type Flags */
+#define WPS_AUTH_OPEN 0x0001
+#define WPS_AUTH_WPAPSK 0x0002
+#define WPS_AUTH_SHARED 0x0004
+#define WPS_AUTH_WPA 0x0008
+#define WPS_AUTH_WPA2 0x0010
+#define WPS_AUTH_WPA2PSK 0x0020
+#define WPS_AUTH_TYPES (WPS_AUTH_OPEN | WPS_AUTH_WPAPSK | WPS_AUTH_SHARED | \
+ WPS_AUTH_WPA | WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)
+
+/* Encryption Type Flags */
+#define WPS_ENCR_NONE 0x0001
+#define WPS_ENCR_WEP 0x0002
+#define WPS_ENCR_TKIP 0x0004
+#define WPS_ENCR_AES 0x0008
+#define WPS_ENCR_TYPES (WPS_ENCR_NONE | WPS_ENCR_WEP | WPS_ENCR_TKIP | \
+ WPS_ENCR_AES)
+
+/* Configuration Error */
+enum wps_config_error {
+ WPS_CFG_NO_ERROR = 0,
+ WPS_CFG_OOB_IFACE_READ_ERROR = 1,
+ WPS_CFG_DECRYPTION_CRC_FAILURE = 2,
+ WPS_CFG_24_CHAN_NOT_SUPPORTED = 3,
+ WPS_CFG_50_CHAN_NOT_SUPPORTED = 4,
+ WPS_CFG_SIGNAL_TOO_WEAK = 5,
+ WPS_CFG_NETWORK_AUTH_FAILURE = 6,
+ WPS_CFG_NETWORK_ASSOC_FAILURE = 7,
+ WPS_CFG_NO_DHCP_RESPONSE = 8,
+ WPS_CFG_FAILED_DHCP_CONFIG = 9,
+ WPS_CFG_IP_ADDR_CONFLICT = 10,
+ WPS_CFG_NO_CONN_TO_REGISTRAR = 11,
+ WPS_CFG_MULTIPLE_PBC_DETECTED = 12,
+ WPS_CFG_ROGUE_SUSPECTED = 13,
+ WPS_CFG_DEVICE_BUSY = 14,
+ WPS_CFG_SETUP_LOCKED = 15,
+ WPS_CFG_MSG_TIMEOUT = 16,
+ WPS_CFG_REG_SESS_TIMEOUT = 17,
+ WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18
+};
+
+/* RF Bands */
+#define WPS_RF_24GHZ 0x01
+#define WPS_RF_50GHZ 0x02
+
+/* Config Methods */
+#define WPS_CONFIG_USBA 0x0001
+#define WPS_CONFIG_ETHERNET 0x0002
+#define WPS_CONFIG_LABEL 0x0004
+#define WPS_CONFIG_DISPLAY 0x0008
+#define WPS_CONFIG_EXT_NFC_TOKEN 0x0010
+#define WPS_CONFIG_INT_NFC_TOKEN 0x0020
+#define WPS_CONFIG_NFC_INTERFACE 0x0040
+#define WPS_CONFIG_PUSHBUTTON 0x0080
+#define WPS_CONFIG_KEYPAD 0x0100
+
+/* Connection Type Flags */
+#define WPS_CONN_ESS 0x01
+#define WPS_CONN_IBSS 0x02
+
+/* Wi-Fi Protected Setup State */
+enum wps_state {
+ WPS_STATE_NOT_CONFIGURED = 1,
+ WPS_STATE_CONFIGURED = 2
+};
+
+/* Association State */
+enum wps_assoc_state {
+ WPS_ASSOC_NOT_ASSOC = 0,
+ WPS_ASSOC_CONN_SUCCESS = 1,
+ WPS_ASSOC_CFG_FAILURE = 2,
+ WPS_ASSOC_FAILURE = 3,
+ WPS_ASSOC_IP_FAILURE = 4
+};
+
+
+/* Primary Device Type */
+struct wps_dev_type {
+ u8 categ_id[2];
+ u8 oui[4];
+ u8 sub_categ_id[2];
+};
+
+#define WPS_DEV_OUI_WFA 0x0050f204
+
+enum wps_dev_categ {
+ WPS_DEV_COMPUTER = 1,
+ WPS_DEV_INPUT = 2,
+ WPS_DEV_PRINTER = 3,
+ WPS_DEV_CAMERA = 4,
+ WPS_DEV_STORAGE = 5,
+ WPS_DEV_NETWORK_INFRA = 6,
+ WPS_DEV_DISPLAY = 7,
+ WPS_DEV_MULTIMEDIA = 8,
+ WPS_DEV_GAMING = 9,
+ WPS_DEV_PHONE = 10
+};
+
+enum wps_dev_subcateg {
+ WPS_DEV_COMPUTER_PC = 1,
+ WPS_DEV_COMPUTER_SERVER = 2,
+ WPS_DEV_COMPUTER_MEDIA_CENTER = 3,
+ WPS_DEV_PRINTER_PRINTER = 1,
+ WPS_DEV_PRINTER_SCANNER = 2,
+ WPS_DEV_CAMERA_DIGITAL_STILL_CAMERA = 1,
+ WPS_DEV_STORAGE_NAS = 1,
+ WPS_DEV_NETWORK_INFRA_AP = 1,
+ WPS_DEV_NETWORK_INFRA_ROUTER = 2,
+ WPS_DEV_NETWORK_INFRA_SWITCH = 3,
+ WPS_DEV_DISPLAY_TV = 1,
+ WPS_DEV_DISPLAY_PICTURE_FRAME = 2,
+ WPS_DEV_DISPLAY_PROJECTOR = 3,
+ WPS_DEV_MULTIMEDIA_DAR = 1,
+ WPS_DEV_MULTIMEDIA_PVR = 2,
+ WPS_DEV_MULTIMEDIA_MCX = 3,
+ WPS_DEV_GAMING_XBOX = 1,
+ WPS_DEV_GAMING_XBOX360 = 2,
+ WPS_DEV_GAMING_PLAYSTATION = 3,
+ WPS_DEV_PHONE_WINDOWS_MOBILE = 1
+};
+
+
+/* Request Type */
+enum wps_request_type {
+ WPS_REQ_ENROLLEE_INFO = 0,
+ WPS_REQ_ENROLLEE = 1,
+ WPS_REQ_REGISTRAR = 2,
+ WPS_REQ_WLAN_MANAGER_REGISTRAR = 3
+};
+
+/* Response Type */
+enum wps_response_type {
+ WPS_RESP_ENROLLEE_INFO = 0,
+ WPS_RESP_ENROLLEE = 1,
+ WPS_RESP_REGISTRAR = 2,
+ WPS_RESP_AP = 3
+};
+
+/* Walk Time for push button configuration (in seconds) */
+#define WPS_PBC_WALK_TIME 120
+
+#endif /* WPS_DEFS_H */
diff --git a/src/wps/wps_dev_attr.c b/src/wps/wps_dev_attr.c
new file mode 100644
index 0000000..d559b26
--- /dev/null
+++ b/src/wps/wps_dev_attr.c
@@ -0,0 +1,316 @@
+/*
+ * Wi-Fi Protected Setup - device attributes
+ * Copyright (c) 2008, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wps_i.h"
+#include "wps_dev_attr.h"
+
+
+static int wps_build_manufacturer(struct wps_device_data *dev,
+ struct wpabuf *msg)
+{
+ size_t len;
+ wpa_printf(MSG_DEBUG, "WPS: * Manufacturer");
+ wpabuf_put_be16(msg, ATTR_MANUFACTURER);
+ len = dev->manufacturer ? os_strlen(dev->manufacturer) : 0;
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->manufacturer, len);
+ return 0;
+}
+
+
+static int wps_build_model_name(struct wps_device_data *dev,
+ struct wpabuf *msg)
+{
+ size_t len;
+ wpa_printf(MSG_DEBUG, "WPS: * Model Name");
+ wpabuf_put_be16(msg, ATTR_MODEL_NAME);
+ len = dev->model_name ? os_strlen(dev->model_name) : 0;
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->model_name, len);
+ return 0;
+}
+
+
+static int wps_build_model_number(struct wps_device_data *dev,
+ struct wpabuf *msg)
+{
+ size_t len;
+ wpa_printf(MSG_DEBUG, "WPS: * Model Number");
+ wpabuf_put_be16(msg, ATTR_MODEL_NUMBER);
+ len = dev->model_number ? os_strlen(dev->model_number) : 0;
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->model_number, len);
+ return 0;
+}
+
+
+static int wps_build_serial_number(struct wps_device_data *dev,
+ struct wpabuf *msg)
+{
+ size_t len;
+ wpa_printf(MSG_DEBUG, "WPS: * Serial Number");
+ wpabuf_put_be16(msg, ATTR_SERIAL_NUMBER);
+ len = dev->serial_number ? os_strlen(dev->serial_number) : 0;
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->serial_number, len);
+ return 0;
+}
+
+
+static int wps_build_primary_dev_type(struct wps_device_data *dev,
+ struct wpabuf *msg)
+{
+ struct wps_dev_type *d;
+ wpa_printf(MSG_DEBUG, "WPS: * Primary Device Type");
+ wpabuf_put_be16(msg, ATTR_PRIMARY_DEV_TYPE);
+ wpabuf_put_be16(msg, sizeof(*d));
+ d = wpabuf_put(msg, sizeof(*d));
+ WPA_PUT_BE16(d->categ_id, dev->categ);
+ WPA_PUT_BE32(d->oui, dev->oui);
+ WPA_PUT_BE16(d->sub_categ_id, dev->sub_categ);
+ return 0;
+}
+
+
+static int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ size_t len;
+ wpa_printf(MSG_DEBUG, "WPS: * Device Name");
+ wpabuf_put_be16(msg, ATTR_DEV_NAME);
+ len = dev->device_name ? os_strlen(dev->device_name) : 0;
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->device_name, len);
+ return 0;
+}
+
+
+int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ if (wps_build_manufacturer(dev, msg) ||
+ wps_build_model_name(dev, msg) ||
+ wps_build_model_number(dev, msg) ||
+ wps_build_serial_number(dev, msg) ||
+ wps_build_primary_dev_type(dev, msg) ||
+ wps_build_dev_name(dev, msg))
+ return -1;
+ return 0;
+}
+
+
+int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * OS Version");
+ wpabuf_put_be16(msg, ATTR_OS_VERSION);
+ wpabuf_put_be16(msg, 4);
+ wpabuf_put_be32(msg, 0x80000000 | dev->os_version);
+ return 0;
+}
+
+
+static int wps_process_manufacturer(struct wps_device_data *dev, const u8 *str,
+ size_t str_len)
+{
+ if (str == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Manufacturer received");
+ return -1;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer", str, str_len);
+
+ os_free(dev->manufacturer);
+ dev->manufacturer = os_malloc(str_len + 1);
+ if (dev->manufacturer == NULL)
+ return -1;
+ os_memcpy(dev->manufacturer, str, str_len);
+ dev->manufacturer[str_len] = '\0';
+
+ return 0;
+}
+
+
+static int wps_process_model_name(struct wps_device_data *dev, const u8 *str,
+ size_t str_len)
+{
+ if (str == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Model Name received");
+ return -1;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name", str, str_len);
+
+ os_free(dev->model_name);
+ dev->model_name = os_malloc(str_len + 1);
+ if (dev->model_name == NULL)
+ return -1;
+ os_memcpy(dev->model_name, str, str_len);
+ dev->model_name[str_len] = '\0';
+
+ return 0;
+}
+
+
+static int wps_process_model_number(struct wps_device_data *dev, const u8 *str,
+ size_t str_len)
+{
+ if (str == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Model Number received");
+ return -1;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number", str, str_len);
+
+ os_free(dev->model_number);
+ dev->model_number = os_malloc(str_len + 1);
+ if (dev->model_number == NULL)
+ return -1;
+ os_memcpy(dev->model_number, str, str_len);
+ dev->model_number[str_len] = '\0';
+
+ return 0;
+}
+
+
+static int wps_process_serial_number(struct wps_device_data *dev,
+ const u8 *str, size_t str_len)
+{
+ if (str == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Serial Number received");
+ return -1;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number", str, str_len);
+
+ os_free(dev->serial_number);
+ dev->serial_number = os_malloc(str_len + 1);
+ if (dev->serial_number == NULL)
+ return -1;
+ os_memcpy(dev->serial_number, str, str_len);
+ dev->serial_number[str_len] = '\0';
+
+ return 0;
+}
+
+
+static int wps_process_dev_name(struct wps_device_data *dev, const u8 *str,
+ size_t str_len)
+{
+ if (str == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Device Name received");
+ return -1;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name", str, str_len);
+
+ os_free(dev->device_name);
+ dev->device_name = os_malloc(str_len + 1);
+ if (dev->device_name == NULL)
+ return -1;
+ os_memcpy(dev->device_name, str, str_len);
+ dev->device_name[str_len] = '\0';
+
+ return 0;
+}
+
+
+static int wps_process_primary_dev_type(struct wps_device_data *dev,
+ const u8 *dev_type)
+{
+ struct wps_dev_type *d;
+
+ if (dev_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Primary Device Type received");
+ return -1;
+ }
+
+ d = (struct wps_dev_type *) dev_type;
+ dev->categ = WPA_GET_BE16(d->categ_id);
+ dev->oui = WPA_GET_BE32(d->oui);
+ dev->sub_categ = WPA_GET_BE16(d->sub_categ_id);
+
+ wpa_printf(MSG_DEBUG, "WPS: Primary Device Type: category %d "
+ "OUI %08x sub-category %d",
+ dev->categ, dev->oui, dev->sub_categ);
+
+ return 0;
+}
+
+
+int wps_process_device_attrs(struct wps_device_data *dev,
+ struct wps_parse_attr *attr)
+{
+ if (wps_process_manufacturer(dev, attr->manufacturer,
+ attr->manufacturer_len) ||
+ wps_process_model_name(dev, attr->model_name,
+ attr->model_name_len) ||
+ wps_process_model_number(dev, attr->model_number,
+ attr->model_number_len) ||
+ wps_process_serial_number(dev, attr->serial_number,
+ attr->serial_number_len) ||
+ wps_process_primary_dev_type(dev, attr->primary_dev_type) ||
+ wps_process_dev_name(dev, attr->dev_name, attr->dev_name_len))
+ return -1;
+ return 0;
+}
+
+
+int wps_process_os_version(struct wps_device_data *dev, const u8 *ver)
+{
+ if (ver == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No OS Version received");
+ return -1;
+ }
+
+ dev->os_version = WPA_GET_BE32(ver);
+ wpa_printf(MSG_DEBUG, "WPS: OS Version %08x", dev->os_version);
+
+ return 0;
+}
+
+
+void wps_device_data_dup(struct wps_device_data *dst,
+ const struct wps_device_data *src)
+{
+ if (src->device_name)
+ dst->device_name = os_strdup(src->device_name);
+ if (src->manufacturer)
+ dst->manufacturer = os_strdup(src->manufacturer);
+ if (src->model_name)
+ dst->model_name = os_strdup(src->model_name);
+ if (src->model_number)
+ dst->model_number = os_strdup(src->model_number);
+ if (src->serial_number)
+ dst->serial_number = os_strdup(src->serial_number);
+ dst->categ = src->categ;
+ dst->oui = src->oui;
+ dst->sub_categ = src->sub_categ;
+ dst->os_version = src->os_version;
+}
+
+
+void wps_device_data_free(struct wps_device_data *dev)
+{
+ os_free(dev->device_name);
+ dev->device_name = NULL;
+ os_free(dev->manufacturer);
+ dev->manufacturer = NULL;
+ os_free(dev->model_name);
+ dev->model_name = NULL;
+ os_free(dev->model_number);
+ dev->model_number = NULL;
+ os_free(dev->serial_number);
+ dev->serial_number = NULL;
+}
diff --git a/src/wps/wps_dev_attr.h b/src/wps/wps_dev_attr.h
new file mode 100644
index 0000000..2e2155a
--- /dev/null
+++ b/src/wps/wps_dev_attr.h
@@ -0,0 +1,29 @@
+/*
+ * Wi-Fi Protected Setup - device attributes
+ * Copyright (c) 2008, 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.
+ */
+
+#ifndef WPS_DEV_ATTR_H
+#define WPS_DEV_ATTR_H
+
+struct wps_parse_attr;
+
+int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_process_device_attrs(struct wps_device_data *dev,
+ struct wps_parse_attr *attr);
+int wps_process_os_version(struct wps_device_data *dev, const u8 *ver);
+void wps_device_data_dup(struct wps_device_data *dst,
+ const struct wps_device_data *src);
+void wps_device_data_free(struct wps_device_data *dev);
+
+#endif /* WPS_DEV_ATTR_H */
diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c
new file mode 100644
index 0000000..0eea610
--- /dev/null
+++ b/src/wps/wps_enrollee.c
@@ -0,0 +1,1454 @@
+/*
+ * Wi-Fi Protected Setup - Enrollee
+ * Copyright (c) 2008, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha256.h"
+#include "ieee802_11_defs.h"
+#include "wps_i.h"
+
+
+static int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Request Type");
+ wpabuf_put_be16(msg, ATTR_REQUEST_TYPE);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, type);
+ return 0;
+}
+
+
+static int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * UUID-E");
+ wpabuf_put_be16(msg, ATTR_UUID_E);
+ wpabuf_put_be16(msg, WPS_UUID_LEN);
+ wpabuf_put_data(msg, uuid, WPS_UUID_LEN);
+ return 0;
+}
+
+
+static int wps_build_mac_addr(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * MAC Address");
+ wpabuf_put_be16(msg, ATTR_MAC_ADDR);
+ wpabuf_put_be16(msg, ETH_ALEN);
+ wpabuf_put_data(msg, wps->mac_addr_e, ETH_ALEN);
+ return 0;
+}
+
+
+static int wps_build_config_methods(struct wpabuf *msg, u16 methods)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Config Methods");
+ wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, methods);
+ return 0;
+}
+
+
+static int wps_build_wps_state(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Wi-Fi Protected Setup State");
+ wpabuf_put_be16(msg, ATTR_WPS_STATE);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, WPS_STATE_CONFIGURED);
+ return 0;
+}
+
+
+static int wps_build_manufacturer(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Manufacturer");
+ wpabuf_put_be16(msg, ATTR_MANUFACTURER);
+ wpabuf_put_be16(msg, 5);
+ wpabuf_put_data(msg, "manuf", 5); /* FIX */
+ return 0;
+}
+
+
+static int wps_build_model_name(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Model Name");
+ wpabuf_put_be16(msg, ATTR_MODEL_NAME);
+ wpabuf_put_be16(msg, 10);
+ wpabuf_put_data(msg, "model name", 10); /* FIX */
+ return 0;
+}
+
+
+static int wps_build_model_number(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Model Number");
+ wpabuf_put_be16(msg, ATTR_MODEL_NUMBER);
+ wpabuf_put_be16(msg, 12);
+ wpabuf_put_data(msg, "model number", 12); /* FIX */
+ return 0;
+}
+
+
+static int wps_build_serial_number(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Serial Number");
+ wpabuf_put_be16(msg, ATTR_SERIAL_NUMBER);
+ wpabuf_put_be16(msg, 5);
+ wpabuf_put_data(msg, "12345", 5); /* FIX */
+ return 0;
+}
+
+
+static int wps_build_primary_dev_type(struct wps_data *wps, struct wpabuf *msg)
+{
+ struct wps_dev_type *dev;
+ wpa_printf(MSG_DEBUG, "WPS: * Primary Device Type");
+ wpabuf_put_be16(msg, ATTR_PRIMARY_DEV_TYPE);
+ wpabuf_put_be16(msg, sizeof(*dev));
+ dev = wpabuf_put(msg, sizeof(*dev));
+ WPA_PUT_BE16(dev->categ_id, WPS_DEV_COMPUTER);
+ WPA_PUT_BE32(dev->oui, WPS_DEV_OUI_WFA);
+ WPA_PUT_BE16(dev->sub_categ_id, WPS_DEV_COMPUTER_PC);
+ return 0;
+}
+
+
+static int wps_build_dev_name(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Device Name");
+ wpabuf_put_be16(msg, ATTR_DEV_NAME);
+ wpabuf_put_be16(msg, 8);
+ wpabuf_put_data(msg, "dev name", 8); /* FIX */
+ return 0;
+}
+
+
+static int wps_build_rf_bands(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * RF Bands");
+ wpabuf_put_be16(msg, ATTR_RF_BANDS);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, WPS_RF_24GHZ | WPS_RF_50GHZ);
+ return 0;
+}
+
+
+static int wps_build_dev_password_id(struct wpabuf *msg, u16 id)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Device Password ID");
+ wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, id);
+ return 0;
+}
+
+
+static int wps_build_config_error(struct wps_data *wps, struct wpabuf *msg)
+{
+ u16 err = WPS_CFG_NO_ERROR;
+ wpabuf_put_be16(msg, ATTR_CONFIG_ERROR);
+ wpabuf_put_be16(msg, 2);
+ if (wps && wps->authenticator && wps->wps->ap_setup_locked)
+ err = WPS_CFG_SETUP_LOCKED;
+ wpa_printf(MSG_DEBUG, "WPS: * Configuration Error (%d)", err);
+ wpabuf_put_be16(msg, err);
+ return 0;
+}
+
+
+static int wps_build_os_version(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * OS Version");
+ wpabuf_put_be16(msg, ATTR_OS_VERSION);
+ wpabuf_put_be16(msg, 4);
+ wpabuf_put_be32(msg, 0x80000000); /* FIX */
+ return 0;
+}
+
+
+static int wps_build_e_hash(struct wps_data *wps, struct wpabuf *msg)
+{
+ u8 *hash;
+ const u8 *addr[4];
+ size_t len[4];
+
+ if (os_get_random(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
+ return -1;
+ wpa_hexdump(MSG_DEBUG, "WPS: E-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: E-S2",
+ wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN);
+
+ if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for "
+ "E-Hash derivation");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: * E-Hash1");
+ wpabuf_put_be16(msg, ATTR_E_HASH1);
+ wpabuf_put_be16(msg, SHA256_MAC_LEN);
+ hash = wpabuf_put(msg, SHA256_MAC_LEN);
+ /* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */
+ addr[0] = wps->snonce;
+ len[0] = WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk1;
+ len[1] = WPS_PSK_LEN;
+ addr[2] = wpabuf_head(wps->dh_pubkey_e);
+ len[2] = wpabuf_len(wps->dh_pubkey_e);
+ addr[3] = wpabuf_head(wps->dh_pubkey_r);
+ len[3] = wpabuf_len(wps->dh_pubkey_r);
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+ wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", hash, SHA256_MAC_LEN);
+
+ wpa_printf(MSG_DEBUG, "WPS: * E-Hash2");
+ wpabuf_put_be16(msg, ATTR_E_HASH2);
+ wpabuf_put_be16(msg, SHA256_MAC_LEN);
+ hash = wpabuf_put(msg, SHA256_MAC_LEN);
+ /* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */
+ addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk2;
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+ wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", hash, SHA256_MAC_LEN);
+
+ return 0;
+}
+
+
+static int wps_build_e_snonce1(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * E-SNonce1");
+ wpabuf_put_be16(msg, ATTR_E_SNONCE1);
+ wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+ wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN);
+ return 0;
+}
+
+
+static int wps_build_e_snonce2(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * E-SNonce2");
+ wpabuf_put_be16(msg, ATTR_E_SNONCE2);
+ wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+ wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN,
+ WPS_SECRET_NONCE_LEN);
+ return 0;
+}
+
+
+static struct wpabuf * wps_build_m1(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+ u16 methods;
+
+ if (os_get_random(wps->nonce_e, WPS_NONCE_LEN) < 0)
+ return NULL;
+ wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
+ wps->nonce_e, WPS_NONCE_LEN);
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M1");
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+ if (wps->pbc)
+ methods |= WPS_CONFIG_PUSHBUTTON;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M1) ||
+ wps_build_uuid_e(msg, wps->uuid_e) ||
+ wps_build_mac_addr(wps, msg) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_public_key(wps, msg) ||
+ wps_build_auth_type_flags(wps, msg) ||
+ wps_build_encr_type_flags(wps, msg) ||
+ wps_build_conn_type_flags(wps, msg) ||
+ wps_build_config_methods(msg, methods) ||
+ wps_build_wps_state(wps, msg) ||
+ wps_build_manufacturer(wps, msg) ||
+ wps_build_model_name(wps, msg) ||
+ wps_build_model_number(wps, msg) ||
+ wps_build_serial_number(wps, msg) ||
+ wps_build_primary_dev_type(wps, msg) ||
+ wps_build_dev_name(wps, msg) ||
+ wps_build_rf_bands(wps, msg) ||
+ wps_build_assoc_state(wps, msg) ||
+ wps_build_dev_password_id(msg, wps->dev_pw_id) ||
+ wps_build_config_error(wps, msg) ||
+ wps_build_os_version(wps, msg)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ wps->state = RECV_M2;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_m3(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M3");
+
+ if (wps->dev_password == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Device Password available");
+ return NULL;
+ }
+ wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M3) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_e_hash(wps, msg) ||
+ wps_build_authenticator(wps, msg)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ wps->state = RECV_M4;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_m5(struct wps_data *wps)
+{
+ struct wpabuf *msg, *plain;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M5");
+
+ plain = wpabuf_alloc(200);
+ if (plain == NULL)
+ return NULL;
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL) {
+ wpabuf_free(plain);
+ return NULL;
+ }
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M5) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_e_snonce1(wps, plain) ||
+ wps_build_key_wrap_auth(wps, plain) ||
+ wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_authenticator(wps, msg)) {
+ wpabuf_free(plain);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_free(plain);
+
+ wps->state = RECV_M6;
+ return msg;
+}
+
+
+static int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * SSID");
+ wpabuf_put_be16(msg, ATTR_SSID);
+ wpabuf_put_be16(msg, wps->wps->ssid_len);
+ wpabuf_put_data(msg, wps->wps->ssid, wps->wps->ssid_len);
+ return 0;
+}
+
+
+static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Authentication Type");
+ wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, wps->wps->auth_types);
+ return 0;
+}
+
+
+static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Encryption Type");
+ wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, wps->wps->encr_types);
+ return 0;
+}
+
+
+static int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Network Key");
+ wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
+ wpabuf_put_be16(msg, wps->wps->network_key_len);
+ wpabuf_put_data(msg, wps->wps->network_key, wps->wps->network_key_len);
+ return 0;
+}
+
+
+static int wps_build_cred_mac_addr(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * MAC Address (AP BSSID)");
+ wpabuf_put_be16(msg, ATTR_MAC_ADDR);
+ wpabuf_put_be16(msg, ETH_ALEN);
+ wpabuf_put_data(msg, wps->wps->dev.mac_addr, ETH_ALEN);
+ return 0;
+}
+
+
+static struct wpabuf * wps_build_m7(struct wps_data *wps)
+{
+ struct wpabuf *msg, *plain;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M7");
+
+ plain = wpabuf_alloc(500);
+ if (plain == NULL)
+ return NULL;
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL) {
+ wpabuf_free(plain);
+ return NULL;
+ }
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M7) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_e_snonce2(wps, plain) ||
+ (wps->authenticator &&
+ (wps_build_cred_ssid(wps, plain) ||
+ wps_build_cred_mac_addr(wps, plain) ||
+ wps_build_cred_auth_type(wps, plain) ||
+ wps_build_cred_encr_type(wps, plain) ||
+ wps_build_cred_network_key(wps, plain))) ||
+ wps_build_key_wrap_auth(wps, plain) ||
+ wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_authenticator(wps, msg)) {
+ wpabuf_free(plain);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_free(plain);
+
+ wps->state = RECV_M8;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_wsc_done(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_Done");
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_WSC_DONE) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_registrar_nonce(wps, msg)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ wps->state = wps->authenticator ? RECV_ACK : WPS_FINISHED;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_wsc_ack(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK");
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_WSC_ACK) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_registrar_nonce(wps, msg)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_wsc_nack(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK");
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_WSC_NACK) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_config_error(wps, msg)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+
+struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps, u8 *op_code)
+{
+ struct wpabuf *msg;
+
+ switch (wps->state) {
+ case SEND_M1:
+ msg = wps_build_m1(wps);
+ *op_code = WSC_MSG;
+ break;
+ case SEND_M3:
+ msg = wps_build_m3(wps);
+ *op_code = WSC_MSG;
+ break;
+ case SEND_M5:
+ msg = wps_build_m5(wps);
+ *op_code = WSC_MSG;
+ break;
+ case SEND_M7:
+ msg = wps_build_m7(wps);
+ *op_code = WSC_MSG;
+ break;
+ case RECEIVED_M2D:
+ msg = wps_build_wsc_ack(wps);
+ *op_code = WSC_ACK;
+ if (msg) {
+ /* Another M2/M2D may be received */
+ wps->state = RECV_M2;
+ }
+ break;
+ case SEND_WSC_NACK:
+ msg = wps_build_wsc_nack(wps);
+ *op_code = WSC_NACK;
+ break;
+ case WPS_MSG_DONE:
+ msg = wps_build_wsc_done(wps);
+ *op_code = WSC_Done;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building "
+ "a message", wps->state);
+ msg = NULL;
+ break;
+ }
+
+ if (*op_code == WSC_MSG && msg) {
+ /* Save a copy of the last message for Authenticator derivation
+ */
+ wpabuf_free(wps->last_msg);
+ wps->last_msg = wpabuf_dup(msg);
+ }
+
+ return msg;
+}
+
+
+static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
+{
+ if (r_nonce == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received");
+ return -1;
+ }
+
+ os_memcpy(wps->nonce_r, r_nonce, WPS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
+ wps->nonce_r, WPS_NONCE_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
+{
+ if (e_nonce == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received");
+ return -1;
+ }
+
+ if (os_memcmp(wps->nonce_e, e_nonce, WPS_NONCE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce received");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wps_process_uuid_r(struct wps_data *wps, const u8 *uuid_r)
+{
+ if (uuid_r == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No UUID-R received");
+ return -1;
+ }
+
+ os_memcpy(wps->uuid_r, uuid_r, WPS_UUID_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
+ size_t pk_len)
+{
+ if (pk == NULL || pk_len == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No Public Key received");
+ return -1;
+ }
+
+ wpabuf_free(wps->dh_pubkey_r);
+ wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
+ if (wps->dh_pubkey_r == NULL)
+ return -1;
+
+ return wps_derive_keys(wps);
+}
+
+
+static int wps_process_r_hash1(struct wps_data *wps, const u8 *r_hash1)
+{
+ if (r_hash1 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No R-Hash1 received");
+ return -1;
+ }
+
+ os_memcpy(wps->peer_hash1, r_hash1, WPS_HASH_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", wps->peer_hash1, WPS_HASH_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_r_hash2(struct wps_data *wps, const u8 *r_hash2)
+{
+ if (r_hash2 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No R-Hash2 received");
+ return -1;
+ }
+
+ os_memcpy(wps->peer_hash2, r_hash2, WPS_HASH_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", wps->peer_hash2, WPS_HASH_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_r_snonce1(struct wps_data *wps, const u8 *r_snonce1)
+{
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+
+ if (r_snonce1 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No R-SNonce1 received");
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce1", r_snonce1,
+ WPS_SECRET_NONCE_LEN);
+
+ /* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
+ addr[0] = r_snonce1;
+ len[0] = WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk1;
+ len[1] = WPS_PSK_LEN;
+ addr[2] = wpabuf_head(wps->dh_pubkey_e);
+ len[2] = wpabuf_len(wps->dh_pubkey_e);
+ addr[3] = wpabuf_head(wps->dh_pubkey_r);
+ len[3] = wpabuf_len(wps->dh_pubkey_r);
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+ if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: R-Hash1 derived from R-S1 does "
+ "not match with the pre-committed value");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the first "
+ "half of the device password");
+
+ return 0;
+}
+
+
+static int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2)
+{
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+
+ if (r_snonce2 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No R-SNonce2 received");
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce2", r_snonce2,
+ WPS_SECRET_NONCE_LEN);
+
+ /* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
+ addr[0] = r_snonce2;
+ len[0] = WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk2;
+ len[1] = WPS_PSK_LEN;
+ addr[2] = wpabuf_head(wps->dh_pubkey_e);
+ len[2] = wpabuf_len(wps->dh_pubkey_e);
+ addr[3] = wpabuf_head(wps->dh_pubkey_r);
+ len[3] = wpabuf_len(wps->dh_pubkey_r);
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+ if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: R-Hash2 derived from R-S2 does "
+ "not match with the pre-committed value");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the second "
+ "half of the device password");
+
+ return 0;
+}
+
+
+static int wps_process_cred_network_idx(struct wps_credential *cred,
+ const u8 *idx)
+{
+ if (idx == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+ "Network Index");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Network Index: %d", *idx);
+
+ return 0;
+}
+
+
+static int wps_process_cred_ssid(struct wps_credential *cred, const u8 *ssid,
+ size_t ssid_len)
+{
+ if (ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Credential did not include SSID");
+ return -1;
+ }
+
+ /* Remove zero-padding since some Registrar implementations seem to use
+ * hardcoded 32-octet length for this attribute */
+ while (ssid_len > 0 && ssid[ssid_len - 1] == 0)
+ ssid_len--;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", ssid, ssid_len);
+ if (ssid_len <= sizeof(cred->ssid)) {
+ os_memcpy(cred->ssid, ssid, ssid_len);
+ cred->ssid_len = ssid_len;
+ }
+
+ return 0;
+}
+
+
+static int wps_process_cred_auth_type(struct wps_credential *cred,
+ const u8 *auth_type)
+{
+ if (auth_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+ "Authentication Type");
+ return -1;
+ }
+
+ cred->auth_type = WPA_GET_BE16(auth_type);
+ wpa_printf(MSG_DEBUG, "WPS: Authentication Type: 0x%x",
+ cred->auth_type);
+
+ return 0;
+}
+
+
+static int wps_process_cred_encr_type(struct wps_credential *cred,
+ const u8 *encr_type)
+{
+ if (encr_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+ "Encryption Type");
+ return -1;
+ }
+
+ cred->encr_type = WPA_GET_BE16(encr_type);
+ wpa_printf(MSG_DEBUG, "WPS: Encryption Type: 0x%x",
+ cred->encr_type);
+
+ return 0;
+}
+
+
+static int wps_process_cred_network_key_idx(struct wps_credential *cred,
+ const u8 *key_idx)
+{
+ if (key_idx == NULL)
+ return 0; /* optional attribute */
+
+ wpa_printf(MSG_DEBUG, "WPS: Network Key Index: %d", *key_idx);
+ cred->key_idx = *key_idx;
+
+ return 0;
+}
+
+
+static int wps_process_cred_network_key(struct wps_credential *cred,
+ const u8 *key, size_t key_len)
+{
+ if (key == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+ "Network Key");
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", key, key_len);
+ if (key_len <= sizeof(cred->key)) {
+ os_memcpy(cred->key, key, key_len);
+ cred->key_len = key_len;
+ }
+
+ return 0;
+}
+
+
+static int wps_process_cred_mac_addr(struct wps_credential *cred,
+ const u8 *mac_addr)
+{
+ if (mac_addr == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+ "MAC Address");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR, MAC2STR(mac_addr));
+ os_memcpy(cred->mac_addr, mac_addr, ETH_ALEN);
+
+ return 0;
+}
+
+
+static int wps_process_cred_eap_type(struct wps_credential *cred,
+ const u8 *eap_type, size_t eap_type_len)
+{
+ if (eap_type == NULL)
+ return 0; /* optional attribute */
+
+ wpa_hexdump(MSG_DEBUG, "WPS: EAP Type", eap_type, eap_type_len);
+
+ return 0;
+}
+
+
+static int wps_process_cred_eap_identity(struct wps_credential *cred,
+ const u8 *identity,
+ size_t identity_len)
+{
+ if (identity == NULL)
+ return 0; /* optional attribute */
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: EAP Identity",
+ identity, identity_len);
+
+ return 0;
+}
+
+
+static int wps_process_cred_key_prov_auto(struct wps_credential *cred,
+ const u8 *key_prov_auto)
+{
+ if (key_prov_auto == NULL)
+ return 0; /* optional attribute */
+
+ wpa_printf(MSG_DEBUG, "WPS: Key Provided Automatically: %d",
+ *key_prov_auto);
+
+ return 0;
+}
+
+
+static int wps_process_cred_802_1x_enabled(struct wps_credential *cred,
+ const u8 *dot1x_enabled)
+{
+ if (dot1x_enabled == NULL)
+ return 0; /* optional attribute */
+
+ wpa_printf(MSG_DEBUG, "WPS: 802.1X Enabled: %d", *dot1x_enabled);
+
+ return 0;
+}
+
+
+static int wps_process_cred(struct wps_data *wps, const u8 *cred,
+ size_t cred_len)
+{
+ struct wps_parse_attr attr;
+ struct wpabuf msg;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received Credential");
+ os_memset(&wps->cred, 0, sizeof(wps->cred));
+ wpabuf_set(&msg, cred, cred_len);
+ /* TODO: support multiple Network Keys */
+ if (wps_parse_msg(&msg, &attr) < 0 ||
+ wps_process_cred_network_idx(&wps->cred, attr.network_idx) ||
+ wps_process_cred_ssid(&wps->cred, attr.ssid, attr.ssid_len) ||
+ wps_process_cred_auth_type(&wps->cred, attr.auth_type) ||
+ wps_process_cred_encr_type(&wps->cred, attr.encr_type) ||
+ wps_process_cred_network_key_idx(&wps->cred, attr.network_key_idx)
+ ||
+ wps_process_cred_network_key(&wps->cred, attr.network_key,
+ attr.network_key_len) ||
+ wps_process_cred_mac_addr(&wps->cred, attr.mac_addr) ||
+ wps_process_cred_eap_type(&wps->cred, attr.eap_type,
+ attr.eap_type_len) ||
+ wps_process_cred_eap_identity(&wps->cred, attr.eap_identity,
+ attr.eap_identity_len) ||
+ wps_process_cred_key_prov_auto(&wps->cred, attr.key_prov_auto) ||
+ wps_process_cred_802_1x_enabled(&wps->cred, attr.dot1x_enabled))
+ return -1;
+
+ if (wps->wps_cred_cb)
+ wps->wps_cred_cb(wps->cb_ctx, &wps->cred);
+
+ return 0;
+}
+
+
+static int wps_process_creds(struct wps_data *wps, const u8 *cred[],
+ size_t cred_len[], size_t num_cred)
+{
+ size_t i;
+
+ if (wps->authenticator)
+ return 0;
+
+ if (num_cred == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No Credential attributes "
+ "received");
+ return -1;
+ }
+
+ for (i = 0; i < num_cred; i++) {
+ if (wps_process_cred(wps, cred[i], cred_len[i]))
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wps_process_ap_settings(struct wps_data *wps,
+ struct wps_parse_attr *attr)
+{
+ struct wps_credential cred;
+
+ if (!wps->authenticator)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing AP Settings");
+ os_memset(&cred, 0, sizeof(cred));
+ /* TODO: optional attributes New Password and Device Password ID */
+ if (wps_process_cred_ssid(&cred, attr->ssid, attr->ssid_len) ||
+ wps_process_cred_auth_type(&cred, attr->auth_type) ||
+ wps_process_cred_encr_type(&cred, attr->encr_type) ||
+ wps_process_cred_network_key_idx(&cred, attr->network_key_idx) ||
+ wps_process_cred_network_key(&cred, attr->network_key,
+ attr->network_key_len) ||
+ wps_process_cred_mac_addr(&cred, attr->mac_addr))
+ return -1;
+
+ wpa_printf(MSG_INFO, "WPS: Received new AP configuration from "
+ "Registrar");
+
+ if (wps->wps->cred_cb)
+ wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
+
+ return 0;
+}
+
+
+static enum wps_process_res wps_process_m2(struct wps_data *wps,
+ const struct wpabuf *msg,
+ struct wps_parse_attr *attr)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Received M2");
+
+ if (wps->state != RECV_M2) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M2", wps->state);
+ return WPS_FAILURE;
+ }
+
+ if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+ wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+ wps_process_uuid_r(wps, attr->uuid_r) ||
+ wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
+ wps_process_authenticator(wps, attr->authenticator, msg))
+ return WPS_FAILURE;
+
+ if (wps->authenticator && wps->wps->ap_setup_locked) {
+ wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
+ "registration of a new Registrar");
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ wps->state = SEND_M3;
+ return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m2d(struct wps_data *wps,
+ struct wps_parse_attr *attr)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Received M2D");
+
+ if (wps->state != RECV_M2) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M2D", wps->state);
+ return WPS_FAILURE;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer",
+ attr->manufacturer, attr->manufacturer_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name",
+ attr->model_name, attr->model_name_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number",
+ attr->model_number, attr->model_number_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number",
+ attr->serial_number, attr->serial_number_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name",
+ attr->dev_name, attr->dev_name_len);
+
+ /*
+ * TODO: notify monitor programs (cli/gui/etc.) of the M2D and provide
+ * user information about the registrar properties.
+ */
+
+ wps->state = RECEIVED_M2D;
+ return WPS_FAILURE;
+}
+
+
+static enum wps_process_res wps_process_m4(struct wps_data *wps,
+ const struct wpabuf *msg,
+ struct wps_parse_attr *attr)
+{
+ struct wpabuf *decrypted;
+ struct wps_parse_attr eattr;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received M4");
+
+ if (wps->state != RECV_M4) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M4", wps->state);
+ return WPS_FAILURE;
+ }
+
+ if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+ wps_process_authenticator(wps, attr->authenticator, msg) ||
+ wps_process_r_hash1(wps, attr->r_hash1) ||
+ wps_process_r_hash2(wps, attr->r_hash2))
+ return WPS_FAILURE;
+
+ decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+ attr->encr_settings_len);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+ "Settings attribute");
+ return WPS_FAILURE;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+ "attribute");
+ if (wps_parse_msg(decrypted, &eattr) < 0 ||
+ wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+ wps_process_r_snonce1(wps, eattr.r_snonce1)) {
+ wpabuf_free(decrypted);
+ return WPS_FAILURE;
+ }
+ wpabuf_free(decrypted);
+
+ wps->state = SEND_M5;
+ return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m6(struct wps_data *wps,
+ const struct wpabuf *msg,
+ struct wps_parse_attr *attr)
+{
+ struct wpabuf *decrypted;
+ struct wps_parse_attr eattr;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received M6");
+
+ if (wps->state != RECV_M6) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M6", wps->state);
+ return WPS_FAILURE;
+ }
+
+ if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+ wps_process_authenticator(wps, attr->authenticator, msg))
+ return WPS_FAILURE;
+
+ decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+ attr->encr_settings_len);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+ "Settings attribute");
+ return WPS_FAILURE;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+ "attribute");
+ if (wps_parse_msg(decrypted, &eattr) < 0 ||
+ wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+ wps_process_r_snonce2(wps, eattr.r_snonce2)) {
+ wpabuf_free(decrypted);
+ return WPS_FAILURE;
+ }
+ wpabuf_free(decrypted);
+
+ wps->state = SEND_M7;
+ return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m8(struct wps_data *wps,
+ const struct wpabuf *msg,
+ struct wps_parse_attr *attr)
+{
+ struct wpabuf *decrypted;
+ struct wps_parse_attr eattr;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received M8");
+
+ if (wps->state != RECV_M8) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M8", wps->state);
+ return WPS_FAILURE;
+ }
+
+ if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+ wps_process_authenticator(wps, attr->authenticator, msg))
+ return WPS_FAILURE;
+
+ decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+ attr->encr_settings_len);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+ "Settings attribute");
+ return WPS_FAILURE;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+ "attribute");
+ if (wps_parse_msg(decrypted, &eattr) < 0 ||
+ wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+ wps_process_creds(wps, eattr.cred, eattr.cred_len,
+ eattr.num_cred) ||
+ wps_process_ap_settings(wps, &eattr)) {
+ wpabuf_free(decrypted);
+ return WPS_FAILURE;
+ }
+ wpabuf_free(decrypted);
+
+ wps->state = WPS_MSG_DONE;
+ return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+ enum wps_process_res ret = WPS_CONTINUE;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG");
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return WPS_FAILURE;
+
+ if (attr.version == NULL || *attr.version != WPS_VERSION) {
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
+ attr.version ? *attr.version : 0);
+ return WPS_FAILURE;
+ }
+
+ if (attr.enrollee_nonce == NULL ||
+ os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+ return WPS_FAILURE;
+ }
+
+ if (attr.msg_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+ return WPS_FAILURE;
+ }
+
+ switch (*attr.msg_type) {
+ case WPS_M2:
+ ret = wps_process_m2(wps, msg, &attr);
+ break;
+ case WPS_M2D:
+ ret = wps_process_m2d(wps, &attr);
+ break;
+ case WPS_M4:
+ ret = wps_process_m4(wps, msg, &attr);
+ break;
+ case WPS_M6:
+ ret = wps_process_m6(wps, msg, &attr);
+ break;
+ case WPS_M8:
+ ret = wps_process_m8(wps, msg, &attr);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
+ *attr.msg_type);
+ return WPS_FAILURE;
+ }
+
+ if (ret == WPS_CONTINUE) {
+ /* Save a copy of the last message for Authenticator derivation
+ */
+ wpabuf_free(wps->last_msg);
+ wps->last_msg = wpabuf_dup(msg);
+ }
+
+ return ret;
+}
+
+
+static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK");
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return WPS_FAILURE;
+
+ if (attr.version == NULL || *attr.version != WPS_VERSION) {
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
+ attr.version ? *attr.version : 0);
+ return WPS_FAILURE;
+ }
+
+ if (attr.msg_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+ return WPS_FAILURE;
+ }
+
+ if (*attr.msg_type != WPS_WSC_ACK) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+ *attr.msg_type);
+ return WPS_FAILURE;
+ }
+
+ if (attr.registrar_nonce == NULL ||
+ os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+ {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+ return WPS_FAILURE;
+ }
+
+ if (attr.enrollee_nonce == NULL ||
+ os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+ return WPS_FAILURE;
+ }
+
+ if (wps->state == RECV_ACK && wps->authenticator) {
+ wpa_printf(MSG_DEBUG, "WPS: External Registrar registration "
+ "completed successfully");
+ wps->state = WPS_FINISHED;
+ return WPS_DONE;
+ }
+
+ return WPS_FAILURE;
+}
+
+
+static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return WPS_FAILURE;
+
+ if (attr.version == NULL || *attr.version != WPS_VERSION) {
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
+ attr.version ? *attr.version : 0);
+ return WPS_FAILURE;
+ }
+
+ if (attr.msg_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+ return WPS_FAILURE;
+ }
+
+ if (*attr.msg_type != WPS_WSC_NACK) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+ *attr.msg_type);
+ return WPS_FAILURE;
+ }
+
+ if (attr.registrar_nonce == NULL ||
+ os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+ {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+ wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce",
+ attr.registrar_nonce, WPS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Expected Registrar Nonce",
+ wps->nonce_r, WPS_NONCE_LEN);
+ return WPS_FAILURE;
+ }
+
+ if (attr.enrollee_nonce == NULL ||
+ os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+ wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce",
+ attr.enrollee_nonce, WPS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Expected Enrollee Nonce",
+ wps->nonce_e, WPS_NONCE_LEN);
+ return WPS_FAILURE;
+ }
+
+ if (attr.config_error == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute "
+ "in WSC_NACK");
+ return WPS_FAILURE;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee terminated negotiation with "
+ "Configuration Error %d", WPA_GET_BE16(attr.config_error));
+
+ return WPS_FAILURE;
+}
+
+
+enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps, u8 op_code,
+ const struct wpabuf *msg)
+{
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
+ "op_code=%d)",
+ (unsigned long) wpabuf_len(msg), op_code);
+
+ switch (op_code) {
+ case WSC_MSG:
+ return wps_process_wsc_msg(wps, msg);
+ case WSC_ACK:
+ return wps_process_wsc_ack(wps, msg);
+ case WSC_NACK:
+ return wps_process_wsc_nack(wps, msg);
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
+ return WPS_FAILURE;
+ }
+}
+
+
+struct wpabuf * wps_enrollee_build_assoc_req_ie(void)
+{
+ struct wpabuf *ie;
+ u8 *len;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for (Re)Association "
+ "Request");
+ ie = wpabuf_alloc(100);
+ if (ie == NULL)
+ return NULL;
+
+ wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+ len = wpabuf_put(ie, 1);
+ wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
+
+ if (wps_build_version(ie) ||
+ wps_build_req_type(ie, WPS_REQ_ENROLLEE)) {
+ wpabuf_free(ie);
+ return NULL;
+ }
+
+ *len = wpabuf_len(ie) - 2;
+
+ return ie;
+}
+
+
+struct wpabuf * wps_enrollee_build_probe_req_ie(int pbc, const u8 *uuid)
+{
+ struct wpabuf *ie;
+ u8 *len;
+ u16 methods;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for Probe Request");
+ ie = wpabuf_alloc(200);
+ if (ie == NULL)
+ return NULL;
+
+ wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+ len = wpabuf_put(ie, 1);
+ wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
+
+ if (pbc)
+ methods = WPS_CONFIG_PUSHBUTTON;
+ else
+ methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY |
+ WPS_CONFIG_KEYPAD;
+
+ if (wps_build_version(ie) ||
+ wps_build_req_type(ie, WPS_REQ_ENROLLEE) ||
+ wps_build_config_methods(ie, methods) ||
+ wps_build_uuid_e(ie, uuid) ||
+ wps_build_primary_dev_type(NULL, ie) ||
+ wps_build_rf_bands(NULL, ie) ||
+ wps_build_assoc_state(NULL, ie) ||
+ wps_build_config_error(NULL, ie) ||
+ wps_build_dev_password_id(ie, pbc ? DEV_PW_PUSHBUTTON :
+ DEV_PW_DEFAULT)) {
+ wpabuf_free(ie);
+ return NULL;
+ }
+
+ *len = wpabuf_len(ie) - 2;
+
+ return ie;
+}
diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h
new file mode 100644
index 0000000..462535f
--- /dev/null
+++ b/src/wps/wps_i.h
@@ -0,0 +1,184 @@
+/*
+ * Wi-Fi Protected Setup - internal definitions
+ * Copyright (c) 2008, 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.
+ */
+
+#ifndef WPS_I_H
+#define WPS_I_H
+
+#include "wps.h"
+#include "wps_defs.h"
+
+struct wps_data {
+ int authenticator;
+ struct wps_context *wps;
+ struct wps_registrar *registrar;
+ enum {
+ /* Enrollee states */
+ SEND_M1, RECV_M2, SEND_M3, RECV_M4, SEND_M5, RECV_M6, SEND_M7,
+ RECV_M8, RECEIVED_M2D, WPS_MSG_DONE, RECV_ACK, WPS_FINISHED,
+ SEND_WSC_NACK,
+
+ /* Registrar states */
+ RECV_M1, SEND_M2, RECV_M3, SEND_M4, RECV_M5, SEND_M6,
+ RECV_M7, SEND_M8, RECV_DONE, SEND_M2D, RECV_M2D_ACK
+ } state;
+
+ u8 uuid_e[WPS_UUID_LEN];
+ u8 uuid_r[WPS_UUID_LEN];
+ u8 mac_addr_e[ETH_ALEN];
+ u8 nonce_e[WPS_NONCE_LEN];
+ u8 nonce_r[WPS_NONCE_LEN];
+ u8 psk1[WPS_PSK_LEN];
+ u8 psk2[WPS_PSK_LEN];
+ u8 snonce[2 * WPS_SECRET_NONCE_LEN];
+ u8 peer_hash1[WPS_HASH_LEN];
+ u8 peer_hash2[WPS_HASH_LEN];
+
+ struct wpabuf *dh_privkey;
+ struct wpabuf *dh_pubkey_e;
+ struct wpabuf *dh_pubkey_r;
+ u8 authkey[WPS_AUTHKEY_LEN];
+ u8 keywrapkey[WPS_KEYWRAPKEY_LEN];
+ u8 emsk[WPS_EMSK_LEN];
+
+ struct wpabuf *last_msg;
+
+ u8 *dev_password;
+ size_t dev_password_len;
+ u16 dev_pw_id;
+ int pbc;
+
+ u16 encr_type; /* available encryption types */
+ u16 auth_type; /* available authentication types */
+
+ u8 *new_psk;
+ size_t new_psk_len;
+
+ int wps_pin_revealed;
+ struct wps_credential cred;
+
+ int (*wps_cred_cb)(void *ctx, struct wps_credential *cred);
+ void *cb_ctx;
+
+ struct wps_device_data peer_dev;
+};
+
+
+struct wps_parse_attr {
+ /* fixed length fields */
+ const u8 *version; /* 1 octet */
+ const u8 *msg_type; /* 1 octet */
+ const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */
+ const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */
+ const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */
+ const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */
+ const u8 *auth_type_flags; /* 2 octets */
+ const u8 *encr_type_flags; /* 2 octets */
+ const u8 *conn_type_flags; /* 1 octet */
+ const u8 *config_methods; /* 2 octets */
+ const u8 *sel_reg_config_methods; /* 2 octets */
+ const u8 *primary_dev_type; /* 8 octets */
+ const u8 *rf_bands; /* 1 octet */
+ const u8 *assoc_state; /* 2 octets */
+ const u8 *config_error; /* 2 octets */
+ const u8 *dev_password_id; /* 2 octets */
+ const u8 *os_version; /* 4 octets */
+ const u8 *wps_state; /* 1 octet */
+ const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */
+ const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */
+ const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */
+ const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */
+ const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */
+ const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
+ const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
+ const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
+ const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
+ const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */
+ const u8 *auth_type; /* 2 octets */
+ const u8 *encr_type; /* 2 octets */
+ const u8 *network_idx; /* 1 octet */
+ const u8 *network_key_idx; /* 1 octet */
+ const u8 *mac_addr; /* ETH_ALEN (6) octets */
+ const u8 *key_prov_auto; /* 1 octet (Bool) */
+ const u8 *dot1x_enabled; /* 1 octet (Bool) */
+ const u8 *selected_registrar; /* 1 octet (Bool) */
+
+ /* variable length fields */
+ const u8 *manufacturer;
+ size_t manufacturer_len;
+ const u8 *model_name;
+ size_t model_name_len;
+ const u8 *model_number;
+ size_t model_number_len;
+ const u8 *serial_number;
+ size_t serial_number_len;
+ const u8 *dev_name;
+ size_t dev_name_len;
+ const u8 *public_key;
+ size_t public_key_len;
+ const u8 *encr_settings;
+ size_t encr_settings_len;
+ const u8 *ssid; /* <= 32 octets */
+ size_t ssid_len;
+ const u8 *network_key; /* <= 64 octets */
+ size_t network_key_len;
+ const u8 *eap_type; /* <= 8 octets */
+ size_t eap_type_len;
+ const u8 *eap_identity; /* <= 64 octets */
+ size_t eap_identity_len;
+
+ /* attributes that can occur multiple times */
+#define MAX_CRED_COUNT 10
+ const u8 *cred[MAX_CRED_COUNT];
+ size_t cred_len[MAX_CRED_COUNT];
+ size_t num_cred;
+};
+
+/* wps_common.c */
+int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
+void wps_kdf(const u8 *key, const char *label, u8 *res, size_t res_len);
+int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg);
+int wps_derive_keys(struct wps_data *wps);
+int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg);
+int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
+ const struct wpabuf *msg);
+void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
+ size_t dev_passwd_len);
+struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
+ size_t encr_len);
+int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg,
+ const u8 *key_wrap_auth);
+int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
+ struct wpabuf *plain);
+int wps_build_version(struct wpabuf *msg);
+int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type);
+int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg);
+
+/* wps_enrollee.c */
+struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps, u8 *op_code);
+enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps, u8 op_code,
+ const struct wpabuf *msg);
+
+/* wps_registrar.c */
+struct wpabuf * wps_registrar_get_msg(struct wps_data *wps, u8 *op_code);
+enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
+ u8 op_code,
+ const struct wpabuf *msg);
+
+#endif /* WPS_I_H */
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
new file mode 100644
index 0000000..7982b98
--- /dev/null
+++ b/src/wps/wps_registrar.c
@@ -0,0 +1,1982 @@
+/*
+ * Wi-Fi Protected Setup - Registrar
+ * Copyright (c) 2008, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha256.h"
+#include "base64.h"
+#include "ieee802_11_defs.h"
+#include "eloop.h"
+#include "wps_i.h"
+#include "wps_dev_attr.h"
+
+
+struct wps_uuid_pin {
+ struct wps_uuid_pin *next;
+ u8 uuid[WPS_UUID_LEN];
+ u8 *pin;
+ size_t pin_len;
+ int locked;
+};
+
+
+static void wps_free_pin(struct wps_uuid_pin *pin)
+{
+ os_free(pin->pin);
+ os_free(pin);
+}
+
+
+static void wps_free_pins(struct wps_uuid_pin *pins)
+{
+ struct wps_uuid_pin *pin, *prev;
+
+ pin = pins;
+ while (pin) {
+ prev = pin;
+ pin = pin->next;
+ wps_free_pin(prev);
+ }
+}
+
+
+struct wps_pbc_session {
+ struct wps_pbc_session *next;
+ u8 addr[ETH_ALEN];
+ u8 uuid_e[WPS_UUID_LEN];
+ struct os_time timestamp;
+};
+
+
+static void wps_free_pbc_sessions(struct wps_pbc_session *pbc)
+{
+ struct wps_pbc_session *prev;
+
+ while (pbc) {
+ prev = pbc;
+ pbc = pbc->next;
+ os_free(prev);
+ }
+}
+
+
+struct wps_registrar {
+ struct wps_context *wps;
+
+ int pbc;
+ int selected_registrar;
+
+ int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk,
+ size_t psk_len);
+ int (*set_ie_cb)(void *ctx, const u8 *beacon_ie, size_t beacon_ie_len,
+ const u8 *probe_resp_ie, size_t probe_resp_ie_len);
+ void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
+ const struct wps_device_data *dev);
+ void *cb_ctx;
+
+ struct wps_uuid_pin *pins;
+ struct wps_pbc_session *pbc_sessions;
+};
+
+
+static int wps_set_ie(struct wps_registrar *reg);
+static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx);
+
+
+static void wps_registrar_add_pbc_session(struct wps_registrar *reg,
+ const u8 *addr, const u8 *uuid_e)
+{
+ struct wps_pbc_session *pbc, *prev = NULL;
+ struct os_time now;
+
+ os_get_time(&now);
+
+ pbc = reg->pbc_sessions;
+ while (pbc) {
+ if (os_memcmp(pbc->addr, addr, ETH_ALEN) == 0 &&
+ os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) {
+ if (prev)
+ prev->next = pbc->next;
+ else
+ reg->pbc_sessions = pbc->next;
+ break;
+ }
+ prev = pbc;
+ pbc = pbc->next;
+ }
+
+ if (!pbc) {
+ pbc = os_zalloc(sizeof(*pbc));
+ if (pbc == NULL)
+ return;
+ os_memcpy(pbc->addr, addr, ETH_ALEN);
+ if (uuid_e)
+ os_memcpy(pbc->uuid_e, uuid_e, WPS_UUID_LEN);
+ }
+
+ pbc->next = reg->pbc_sessions;
+ reg->pbc_sessions = pbc;
+ pbc->timestamp = now;
+
+ /* remove entries that have timed out */
+ prev = pbc;
+ pbc = pbc->next;
+
+ while (pbc) {
+ if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) {
+ prev->next = NULL;
+ wps_free_pbc_sessions(pbc);
+ break;
+ }
+ prev = pbc;
+ pbc = pbc->next;
+ }
+}
+
+
+static void wps_registrar_remove_pbc_session(struct wps_registrar *reg,
+ const u8 *addr, const u8 *uuid_e)
+{
+ struct wps_pbc_session *pbc, *prev = NULL;
+
+ pbc = reg->pbc_sessions;
+ while (pbc) {
+ if (os_memcmp(pbc->addr, addr, ETH_ALEN) == 0 &&
+ os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) {
+ if (prev)
+ prev->next = pbc->next;
+ else
+ reg->pbc_sessions = pbc->next;
+ os_free(pbc);
+ break;
+ }
+ prev = pbc;
+ pbc = pbc->next;
+ }
+}
+
+
+int wps_registrar_pbc_overlap(struct wps_registrar *reg,
+ const u8 *addr, const u8 *uuid_e)
+{
+ int count = 0;
+ struct wps_pbc_session *pbc;
+ struct os_time now;
+
+ os_get_time(&now);
+
+ for (pbc = reg->pbc_sessions; pbc; pbc = pbc->next) {
+ if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME)
+ break;
+ if (addr == NULL || os_memcmp(addr, pbc->addr, ETH_ALEN) ||
+ uuid_e == NULL ||
+ os_memcmp(uuid_e, pbc->uuid_e, WPS_UUID_LEN))
+ count++;
+ }
+
+ if (addr || uuid_e)
+ count++;
+
+ return count > 1 ? 1 : 0;
+}
+
+
+static int wps_build_wps_state(struct wps_context *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Wi-Fi Protected Setup State (%d)",
+ wps->wps_state);
+ wpabuf_put_be16(msg, ATTR_WPS_STATE);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, wps->wps_state);
+ return 0;
+}
+
+
+static int wps_build_ap_setup_locked(struct wps_context *wps,
+ struct wpabuf *msg)
+{
+ if (wps->ap_setup_locked) {
+ wpa_printf(MSG_DEBUG, "WPS: * AP Setup Locked");
+ wpabuf_put_be16(msg, ATTR_AP_SETUP_LOCKED);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, 1);
+ }
+ return 0;
+}
+
+
+static int wps_build_selected_registrar(struct wps_registrar *reg,
+ struct wpabuf *msg)
+{
+ if (!reg->selected_registrar)
+ return 0;
+ wpa_printf(MSG_DEBUG, "WPS: * Selected Registrar");
+ wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, 1);
+ return 0;
+}
+
+
+static int wps_build_sel_reg_dev_password_id(struct wps_registrar *reg,
+ struct wpabuf *msg)
+{
+ u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT;
+ if (!reg->selected_registrar)
+ return 0;
+ wpa_printf(MSG_DEBUG, "WPS: * Device Password ID (%d)", id);
+ wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, id);
+ return 0;
+}
+
+
+static int wps_build_sel_reg_config_methods(struct wps_registrar *reg,
+ struct wpabuf *msg)
+{
+ u16 methods;
+ if (!reg->selected_registrar)
+ return 0;
+ methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+ if (reg->pbc)
+ methods |= WPS_CONFIG_PUSHBUTTON;
+ wpa_printf(MSG_DEBUG, "WPS: * Selected Registrar Config Methods (%x)",
+ methods);
+ wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, methods);
+ return 0;
+}
+
+
+static int wps_build_probe_config_methods(struct wps_registrar *reg,
+ struct wpabuf *msg)
+{
+ u16 methods;
+ methods = 0;
+ wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods);
+ wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, methods);
+ return 0;
+}
+
+
+static int wps_build_config_methods(struct wps_registrar *reg,
+ struct wpabuf *msg)
+{
+ u16 methods;
+ methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+ if (reg->pbc)
+ methods |= WPS_CONFIG_PUSHBUTTON;
+ wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods);
+ wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, methods);
+ return 0;
+}
+
+
+static int wps_build_rf_bands(struct wps_registrar *reg, struct wpabuf *msg)
+{
+ u8 bands = WPS_RF_24GHZ /* TODO: | WPS_RF_50GHZ */;
+ wpa_printf(MSG_DEBUG, "WPS: * RF Bands (%x)", bands);
+ wpabuf_put_be16(msg, ATTR_RF_BANDS);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, bands);
+ return 0;
+}
+
+
+static int wps_build_uuid_e(struct wps_registrar *reg, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * UUID-E");
+ wpabuf_put_be16(msg, ATTR_UUID_E);
+ wpabuf_put_be16(msg, WPS_UUID_LEN);
+ wpabuf_put_data(msg, reg->wps->uuid, WPS_UUID_LEN);
+ return 0;
+}
+
+
+static int wps_build_resp_type(struct wps_registrar *reg, struct wpabuf *msg)
+{
+ u8 resp = reg->wps->ap ? WPS_RESP_AP : WPS_RESP_REGISTRAR;
+ wpa_printf(MSG_DEBUG, "WPS: * Response Type (%d)", resp);
+ wpabuf_put_be16(msg, ATTR_RESPONSE_TYPE);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, resp);
+ return 0;
+}
+
+
+struct wps_registrar *
+wps_registrar_init(struct wps_context *wps,
+ const struct wps_registrar_config *cfg)
+{
+ struct wps_registrar *reg = os_zalloc(sizeof(*reg));
+ if (reg == NULL)
+ return NULL;
+
+ reg->wps = wps;
+ reg->new_psk_cb = cfg->new_psk_cb;
+ reg->set_ie_cb = cfg->set_ie_cb;
+ reg->pin_needed_cb = cfg->pin_needed_cb;
+ reg->cb_ctx = cfg->cb_ctx;
+
+ if (wps_set_ie(reg)) {
+ wps_registrar_deinit(reg);
+ return NULL;
+ }
+
+ return reg;
+}
+
+
+void wps_registrar_deinit(struct wps_registrar *reg)
+{
+ if (reg == NULL)
+ return;
+ eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+ wps_free_pins(reg->pins);
+ wps_free_pbc_sessions(reg->pbc_sessions);
+ os_free(reg);
+}
+
+
+int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid,
+ const u8 *pin, size_t pin_len)
+{
+ struct wps_uuid_pin *p;
+
+ p = os_zalloc(sizeof(*p));
+ if (p == NULL)
+ return -1;
+ os_memcpy(p->uuid, uuid, WPS_UUID_LEN);
+ p->pin = os_malloc(pin_len);
+ if (p->pin == NULL) {
+ os_free(p);
+ return -1;
+ }
+ os_memcpy(p->pin, pin, pin_len);
+ p->pin_len = pin_len;
+
+ p->next = reg->pins;
+ reg->pins = p;
+
+ wpa_printf(MSG_DEBUG, "WPS: A new PIN configured");
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID", uuid, WPS_UUID_LEN);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: PIN", pin, pin_len);
+ reg->selected_registrar = 1;
+ reg->pbc = 0;
+ wps_set_ie(reg);
+
+ return 0;
+}
+
+
+int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid)
+{
+ struct wps_uuid_pin *pin, *prev;
+
+ prev = NULL;
+ pin = reg->pins;
+ while (pin) {
+ if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
+ if (prev == NULL)
+ reg->pins = pin->next;
+ else
+ prev->next = pin->next;
+ wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID",
+ pin->uuid, WPS_UUID_LEN);
+ wps_free_pin(pin);
+ return 0;
+ }
+ prev = pin;
+ pin = pin->next;
+ }
+
+ return -1;
+}
+
+
+static const u8 * wps_registrar_get_pin(struct wps_registrar *reg,
+ const u8 *uuid, size_t *pin_len)
+{
+ struct wps_uuid_pin *pin;
+
+ pin = reg->pins;
+ while (pin) {
+ if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
+ /*
+ * Lock the PIN to avoid attacks based on concurrent
+ * re-use of the PIN that could otherwise avoid PIN
+ * invalidations.
+ */
+ if (pin->locked) {
+ wpa_printf(MSG_DEBUG, "WPS: Selected PIN "
+ "locked - do not allow concurrent "
+ "re-use");
+ return NULL;
+ }
+ *pin_len = pin->pin_len;
+ pin->locked = 1;
+ return pin->pin;
+ }
+ pin = pin->next;
+ }
+
+ return NULL;
+}
+
+
+int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid)
+{
+ struct wps_uuid_pin *pin;
+
+ pin = reg->pins;
+ while (pin) {
+ if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
+ pin->locked = 0;
+ return 0;
+ }
+ pin = pin->next;
+ }
+
+ return -1;
+}
+
+
+static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wps_registrar *reg = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "WPS: PBC timed out - disable PBC mode");
+ reg->selected_registrar = 0;
+ reg->pbc = 0;
+ wps_set_ie(reg);
+}
+
+
+int wps_registrar_button_pushed(struct wps_registrar *reg)
+{
+ if (wps_registrar_pbc_overlap(reg, NULL, NULL)) {
+ wpa_printf(MSG_DEBUG, "WPS: PBC overlap - do not start PBC "
+ "mode");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "WPS: Button pushed - PBC mode started");
+ reg->selected_registrar = 1;
+ reg->pbc = 1;
+ wps_set_ie(reg);
+
+ eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+ eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout,
+ reg, NULL);
+ return 0;
+}
+
+
+static void wps_registrar_pbc_completed(struct wps_registrar *reg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: PBC completed - stopping PBC mode");
+ eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+ reg->selected_registrar = 0;
+ reg->pbc = 0;
+ wps_set_ie(reg);
+}
+
+
+void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
+ const struct wpabuf *wps_data)
+{
+ struct wps_parse_attr attr;
+ u16 methods;
+
+ wpa_hexdump_buf(MSG_MSGDUMP,
+ "WPS: Probe Request with WPS data received",
+ wps_data);
+
+ if (wps_parse_msg(wps_data, &attr) < 0 ||
+ attr.version == NULL || *attr.version != WPS_VERSION) {
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported ProbeReq WPS IE "
+ "version 0x%x", attr.version ? *attr.version : 0);
+ return;
+ }
+
+ if (attr.config_methods == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Config Methods attribute in "
+ "Probe Request");
+ return;
+ }
+
+ methods = WPA_GET_BE16(attr.config_methods);
+ if (!(methods & WPS_CONFIG_PUSHBUTTON))
+ return; /* Not PBC */
+
+ wpa_printf(MSG_DEBUG, "WPS: Probe Request for PBC received from "
+ MACSTR, MAC2STR(addr));
+
+ wps_registrar_add_pbc_session(reg, addr, attr.uuid_e);
+}
+
+
+static int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr,
+ const u8 *psk, size_t psk_len)
+{
+ if (reg->new_psk_cb == NULL)
+ return 0;
+
+ return reg->new_psk_cb(reg->cb_ctx, mac_addr, psk, psk_len);
+}
+
+
+static void wps_cb_pin_needed(struct wps_registrar *reg, const u8 *uuid_e,
+ const struct wps_device_data *dev)
+{
+ if (reg->pin_needed_cb == NULL)
+ return;
+
+ reg->pin_needed_cb(reg->cb_ctx, uuid_e, dev);
+}
+
+
+static int wps_cb_set_ie(struct wps_registrar *reg,
+ const struct wpabuf *beacon_ie,
+ const struct wpabuf *probe_resp_ie)
+{
+ if (reg->set_ie_cb == NULL)
+ return 0;
+
+ return reg->set_ie_cb(reg->cb_ctx, wpabuf_head(beacon_ie),
+ wpabuf_len(beacon_ie),
+ wpabuf_head(probe_resp_ie),
+ wpabuf_len(probe_resp_ie));
+}
+
+
+static int wps_set_ie(struct wps_registrar *reg)
+{
+ struct wpabuf *beacon;
+ struct wpabuf *probe;
+ int ret;
+ u8 *blen, *plen;
+
+ wpa_printf(MSG_DEBUG, "WPS: Build Beacon and Probe Response IEs");
+
+ beacon = wpabuf_alloc(300);
+ if (beacon == NULL)
+ return -1;
+ probe = wpabuf_alloc(300);
+ if (probe == NULL) {
+ wpabuf_free(beacon);
+ return -1;
+ }
+
+ wpabuf_put_u8(beacon, WLAN_EID_VENDOR_SPECIFIC);
+ blen = wpabuf_put(beacon, 1);
+ wpabuf_put_be32(beacon, WPS_DEV_OUI_WFA);
+
+ wpabuf_put_u8(probe, WLAN_EID_VENDOR_SPECIFIC);
+ plen = wpabuf_put(probe, 1);
+ wpabuf_put_be32(probe, WPS_DEV_OUI_WFA);
+
+ if (wps_build_version(beacon) ||
+ wps_build_wps_state(reg->wps, beacon) ||
+ wps_build_ap_setup_locked(reg->wps, beacon) ||
+ wps_build_selected_registrar(reg, beacon) ||
+ wps_build_sel_reg_dev_password_id(reg, beacon) ||
+ wps_build_sel_reg_config_methods(reg, beacon) ||
+ wps_build_version(probe) ||
+ wps_build_wps_state(reg->wps, probe) ||
+ wps_build_ap_setup_locked(reg->wps, probe) ||
+ wps_build_selected_registrar(reg, probe) ||
+ wps_build_sel_reg_dev_password_id(reg, probe) ||
+ wps_build_sel_reg_config_methods(reg, probe) ||
+ wps_build_resp_type(reg, probe) ||
+ wps_build_uuid_e(reg, probe) ||
+ wps_build_device_attrs(&reg->wps->dev, probe) ||
+ wps_build_probe_config_methods(reg, probe) ||
+ wps_build_rf_bands(reg, probe)) {
+ wpabuf_free(beacon);
+ wpabuf_free(probe);
+ return -1;
+ }
+
+ *blen = wpabuf_len(beacon) - 2;
+ *plen = wpabuf_len(probe) - 2;
+
+ ret = wps_cb_set_ie(reg, beacon, probe);
+ wpabuf_free(beacon);
+ wpabuf_free(probe);
+
+ return ret;
+}
+
+
+static int wps_get_dev_password(struct wps_data *wps)
+{
+ const u8 *pin;
+ size_t pin_len;
+
+ os_free(wps->dev_password);
+ wps->dev_password = NULL;
+
+ if (wps->pbc) {
+ wpa_printf(MSG_DEBUG, "WPS: Use default PIN for PBC");
+ pin = (const u8 *) "00000000";
+ pin_len = 8;
+ } else {
+ pin = wps_registrar_get_pin(wps->registrar, wps->uuid_e,
+ &pin_len);
+ }
+ if (pin == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Device Password available for "
+ "the Enrollee");
+ wps_cb_pin_needed(wps->registrar, wps->uuid_e, &wps->peer_dev);
+ return -1;
+ }
+
+ wps->dev_password = os_malloc(pin_len);
+ if (wps->dev_password == NULL)
+ return -1;
+ os_memcpy(wps->dev_password, pin, pin_len);
+ wps->dev_password_len = pin_len;
+
+ return 0;
+}
+
+
+static int wps_build_uuid_r(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * UUID-R");
+ wpabuf_put_be16(msg, ATTR_UUID_R);
+ wpabuf_put_be16(msg, WPS_UUID_LEN);
+ wpabuf_put_data(msg, wps->uuid_r, WPS_UUID_LEN);
+ return 0;
+}
+
+
+static int wps_build_dev_password_id(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Device Password ID");
+ wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, DEV_PW_DEFAULT);
+ return 0;
+}
+
+
+static int wps_build_config_error(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Configuration Error");
+ wpabuf_put_be16(msg, ATTR_CONFIG_ERROR);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, WPS_CFG_NO_ERROR);
+ return 0;
+}
+
+
+static int wps_build_r_hash(struct wps_data *wps, struct wpabuf *msg)
+{
+ u8 *hash;
+ const u8 *addr[4];
+ size_t len[4];
+
+ if (os_get_random(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
+ return -1;
+ wpa_hexdump(MSG_DEBUG, "WPS: R-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: R-S2",
+ wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN);
+
+ if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for "
+ "R-Hash derivation");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: * R-Hash1");
+ wpabuf_put_be16(msg, ATTR_R_HASH1);
+ wpabuf_put_be16(msg, SHA256_MAC_LEN);
+ hash = wpabuf_put(msg, SHA256_MAC_LEN);
+ /* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
+ addr[0] = wps->snonce;
+ len[0] = WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk1;
+ len[1] = WPS_PSK_LEN;
+ addr[2] = wpabuf_head(wps->dh_pubkey_e);
+ len[2] = wpabuf_len(wps->dh_pubkey_e);
+ addr[3] = wpabuf_head(wps->dh_pubkey_r);
+ len[3] = wpabuf_len(wps->dh_pubkey_r);
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+ wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", hash, SHA256_MAC_LEN);
+
+ wpa_printf(MSG_DEBUG, "WPS: * R-Hash2");
+ wpabuf_put_be16(msg, ATTR_R_HASH2);
+ wpabuf_put_be16(msg, SHA256_MAC_LEN);
+ hash = wpabuf_put(msg, SHA256_MAC_LEN);
+ /* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
+ addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk2;
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+ wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", hash, SHA256_MAC_LEN);
+
+ return 0;
+}
+
+
+static int wps_build_r_snonce1(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * R-SNonce1");
+ wpabuf_put_be16(msg, ATTR_R_SNONCE1);
+ wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+ wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN);
+ return 0;
+}
+
+
+static int wps_build_r_snonce2(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * R-SNonce2");
+ wpabuf_put_be16(msg, ATTR_R_SNONCE2);
+ wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+ wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN,
+ WPS_SECRET_NONCE_LEN);
+ return 0;
+}
+
+
+static int wps_build_cred_network_idx(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Network Index");
+ wpabuf_put_be16(msg, ATTR_NETWORK_INDEX);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, 0);
+ return 0;
+}
+
+
+static int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * SSID");
+ wpabuf_put_be16(msg, ATTR_SSID);
+ wpabuf_put_be16(msg, wps->wps->ssid_len);
+ wpabuf_put_data(msg, wps->wps->ssid, wps->wps->ssid_len);
+ return 0;
+}
+
+
+static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Authentication Type (0x%x)",
+ wps->auth_type);
+ wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, wps->auth_type);
+ return 0;
+}
+
+
+static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Encryption Type (0x%x)",
+ wps->encr_type);
+ wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, wps->encr_type);
+ return 0;
+}
+
+
+static int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Network Key");
+ wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
+ if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->wps->ap) {
+ u8 r[16];
+ /* Generate a random passphrase */
+ if (os_get_random(r, sizeof(r)) < 0)
+ return -1;
+ os_free(wps->new_psk);
+ wps->new_psk = base64_encode(r, sizeof(r), &wps->new_psk_len);
+ if (wps->new_psk == NULL)
+ return -1;
+ wps->new_psk_len--; /* remove newline */
+ while (wps->new_psk_len &&
+ wps->new_psk[wps->new_psk_len - 1] == '=')
+ wps->new_psk_len--;
+ wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Generated passphrase",
+ wps->new_psk, wps->new_psk_len);
+ wpabuf_put_be16(msg, wps->new_psk_len);
+ wpabuf_put_data(msg, wps->new_psk, wps->new_psk_len);
+ } else if (wps->wps->network_key) {
+ wpabuf_put_be16(msg, wps->wps->network_key_len);
+ wpabuf_put_data(msg, wps->wps->network_key,
+ wps->wps->network_key_len);
+ } else if (wps->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
+ char hex[65];
+ /* Generate a random per-device PSK */
+ os_free(wps->new_psk);
+ wps->new_psk_len = 32;
+ wps->new_psk = os_malloc(wps->new_psk_len);
+ if (wps->new_psk == NULL)
+ return -1;
+ if (os_get_random(wps->new_psk, wps->new_psk_len) < 0) {
+ os_free(wps->new_psk);
+ wps->new_psk = NULL;
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Generated per-device PSK",
+ wps->new_psk, wps->new_psk_len);
+ wpa_snprintf_hex(hex, sizeof(hex), wps->new_psk,
+ wps->new_psk_len);
+ wpabuf_put_be16(msg, wps->new_psk_len * 2);
+ wpabuf_put_data(msg, hex, wps->new_psk_len * 2);
+ } else {
+ /* No Network Key */
+ wpabuf_put_be16(msg, 0);
+ }
+ return 0;
+}
+
+
+static int wps_build_cred_mac_addr(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * MAC Address");
+ wpabuf_put_be16(msg, ATTR_MAC_ADDR);
+ wpabuf_put_be16(msg, ETH_ALEN);
+ wpabuf_put_data(msg, wps->mac_addr_e, ETH_ALEN);
+ return 0;
+}
+
+
+static int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
+{
+ struct wpabuf *cred;
+ int ap_settings;
+
+ ap_settings = !wps->wps->ap;
+
+ if (ap_settings)
+ wpa_printf(MSG_DEBUG, "WPS: * AP Settings");
+ else
+ wpa_printf(MSG_DEBUG, "WPS: * Credential");
+
+ /* Select the best authentication and encryption type */
+ if (wps->auth_type & WPS_AUTH_WPA2PSK)
+ wps->auth_type = WPS_AUTH_WPA2PSK;
+ else if (wps->auth_type & WPS_AUTH_WPAPSK)
+ wps->auth_type = WPS_AUTH_WPAPSK;
+ else if (wps->auth_type & WPS_AUTH_OPEN)
+ wps->auth_type = WPS_AUTH_OPEN;
+ else if (wps->auth_type & WPS_AUTH_SHARED)
+ wps->auth_type = WPS_AUTH_SHARED;
+ else {
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported auth_type 0x%x",
+ wps->auth_type);
+ return -1;
+ }
+
+ if (wps->auth_type == WPS_AUTH_WPA2PSK ||
+ wps->auth_type == WPS_AUTH_WPAPSK) {
+ if (wps->encr_type & WPS_ENCR_AES)
+ wps->encr_type = WPS_ENCR_AES;
+ else if (wps->encr_type & WPS_ENCR_TKIP)
+ wps->encr_type = WPS_ENCR_TKIP;
+ else {
+ wpa_printf(MSG_DEBUG, "WPS: No suitable encryption "
+ "type for WPA/WPA2");
+ return -1;
+ }
+ } else {
+ if (wps->encr_type & WPS_ENCR_WEP)
+ wps->encr_type = WPS_ENCR_WEP;
+ else if (wps->encr_type & WPS_ENCR_NONE)
+ wps->encr_type = WPS_ENCR_NONE;
+ else {
+ wpa_printf(MSG_DEBUG, "WPS: No suitable encryption "
+ "type for non-WPA/WPA2 mode");
+ return -1;
+ }
+ }
+
+ cred = wpabuf_alloc(200);
+ if (cred == NULL)
+ return -1;
+
+ if (wps_build_cred_network_idx(wps, cred) ||
+ wps_build_cred_ssid(wps, cred) ||
+ wps_build_cred_auth_type(wps, cred) ||
+ wps_build_cred_encr_type(wps, cred) ||
+ wps_build_cred_network_key(wps, cred) ||
+ wps_build_cred_mac_addr(wps, cred)) {
+ wpabuf_free(cred);
+ return -1;
+ }
+
+ if (ap_settings) {
+ wpabuf_put_buf(msg, cred);
+ wpabuf_free(cred);
+ } else {
+ wpabuf_put_be16(msg, ATTR_CRED);
+ wpabuf_put_be16(msg, wpabuf_len(cred));
+ wpabuf_put_buf(msg, cred);
+ wpabuf_free(cred);
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * wps_build_m2(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+
+ if (os_get_random(wps->nonce_r, WPS_NONCE_LEN) < 0)
+ return NULL;
+ wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
+ wps->nonce_r, WPS_NONCE_LEN);
+ os_memcpy(wps->uuid_r, wps->wps->uuid, WPS_UUID_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M2");
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M2) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_uuid_r(wps, msg) ||
+ wps_build_public_key(wps, msg) ||
+ wps_derive_keys(wps) ||
+ wps_build_auth_type_flags(wps, msg) ||
+ wps_build_encr_type_flags(wps, msg) ||
+ wps_build_conn_type_flags(wps, msg) ||
+ wps_build_config_methods(wps->registrar, msg) ||
+ wps_build_device_attrs(&wps->wps->dev, msg) ||
+ wps_build_rf_bands(wps->registrar, msg) ||
+ wps_build_assoc_state(wps, msg) ||
+ wps_build_config_error(wps, msg) ||
+ wps_build_dev_password_id(wps, msg) ||
+ wps_build_os_version(&wps->wps->dev, msg) ||
+ wps_build_authenticator(wps, msg)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ wps->state = RECV_M3;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_m2d(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M2D");
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M2D) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_uuid_r(wps, msg) ||
+ wps_build_auth_type_flags(wps, msg) ||
+ wps_build_encr_type_flags(wps, msg) ||
+ wps_build_conn_type_flags(wps, msg) ||
+ wps_build_config_methods(wps->registrar, msg) ||
+ wps_build_device_attrs(&wps->wps->dev, msg) ||
+ wps_build_rf_bands(wps->registrar, msg) ||
+ wps_build_assoc_state(wps, msg) ||
+ wps_build_config_error(wps, msg) ||
+ wps_build_os_version(&wps->wps->dev, msg)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ wps->state = RECV_M2D_ACK;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_m4(struct wps_data *wps)
+{
+ struct wpabuf *msg, *plain;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M4");
+
+ wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
+
+ plain = wpabuf_alloc(200);
+ if (plain == NULL)
+ return NULL;
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL) {
+ wpabuf_free(plain);
+ return NULL;
+ }
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M4) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_r_hash(wps, msg) ||
+ wps_build_r_snonce1(wps, plain) ||
+ wps_build_key_wrap_auth(wps, plain) ||
+ wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_authenticator(wps, msg)) {
+ wpabuf_free(plain);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_free(plain);
+
+ wps->state = RECV_M5;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_m6(struct wps_data *wps)
+{
+ struct wpabuf *msg, *plain;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M6");
+
+ plain = wpabuf_alloc(200);
+ if (plain == NULL)
+ return NULL;
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL) {
+ wpabuf_free(plain);
+ return NULL;
+ }
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M6) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_r_snonce2(wps, plain) ||
+ wps_build_key_wrap_auth(wps, plain) ||
+ wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_authenticator(wps, msg)) {
+ wpabuf_free(plain);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_free(plain);
+
+ wps->wps_pin_revealed = 1;
+ wps->state = RECV_M7;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_m8(struct wps_data *wps)
+{
+ struct wpabuf *msg, *plain;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M8");
+
+ plain = wpabuf_alloc(500);
+ if (plain == NULL)
+ return NULL;
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL) {
+ wpabuf_free(plain);
+ return NULL;
+ }
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M8) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_cred(wps, plain) ||
+ wps_build_key_wrap_auth(wps, plain) ||
+ wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_authenticator(wps, msg)) {
+ wpabuf_free(plain);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_free(plain);
+
+ wps->state = RECV_DONE;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_wsc_ack(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK");
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_WSC_ACK) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_registrar_nonce(wps, msg)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+
+struct wpabuf * wps_registrar_get_msg(struct wps_data *wps, u8 *op_code)
+{
+ struct wpabuf *msg;
+
+ switch (wps->state) {
+ case SEND_M2:
+ if (wps_get_dev_password(wps) < 0)
+ msg = wps_build_m2d(wps);
+ else
+ msg = wps_build_m2(wps);
+ *op_code = WSC_MSG;
+ break;
+ case SEND_M2D:
+ msg = wps_build_m2d(wps);
+ *op_code = WSC_MSG;
+ break;
+ case SEND_M4:
+ msg = wps_build_m4(wps);
+ *op_code = WSC_MSG;
+ break;
+ case SEND_M6:
+ msg = wps_build_m6(wps);
+ *op_code = WSC_MSG;
+ break;
+ case SEND_M8:
+ msg = wps_build_m8(wps);
+ *op_code = WSC_MSG;
+ break;
+ case RECV_DONE:
+ msg = wps_build_wsc_ack(wps);
+ *op_code = WSC_ACK;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building "
+ "a message", wps->state);
+ msg = NULL;
+ break;
+ }
+
+ if (*op_code == WSC_MSG && msg) {
+ /* Save a copy of the last message for Authenticator derivation
+ */
+ wpabuf_free(wps->last_msg);
+ wps->last_msg = wpabuf_dup(msg);
+ }
+
+ return msg;
+}
+
+
+static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
+{
+ if (e_nonce == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received");
+ return -1;
+ }
+
+ os_memcpy(wps->nonce_e, e_nonce, WPS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
+ wps->nonce_e, WPS_NONCE_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
+{
+ if (r_nonce == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received");
+ return -1;
+ }
+
+ if (os_memcmp(wps->nonce_r, r_nonce, WPS_NONCE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce received");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wps_process_uuid_e(struct wps_data *wps, const u8 *uuid_e)
+{
+ if (uuid_e == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No UUID-E received");
+ return -1;
+ }
+
+ os_memcpy(wps->uuid_e, uuid_e, WPS_UUID_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", wps->uuid_e, WPS_UUID_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_dev_password_id(struct wps_data *wps, const u8 *pw_id)
+{
+ if (pw_id == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Device Password ID received");
+ return -1;
+ }
+
+ wps->dev_pw_id = WPA_GET_BE16(pw_id);
+ wpa_printf(MSG_DEBUG, "WPS: Device Password ID %d", wps->dev_pw_id);
+
+ return 0;
+}
+
+
+static int wps_process_e_hash1(struct wps_data *wps, const u8 *e_hash1)
+{
+ if (e_hash1 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No E-Hash1 received");
+ return -1;
+ }
+
+ os_memcpy(wps->peer_hash1, e_hash1, WPS_HASH_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", wps->peer_hash1, WPS_HASH_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_e_hash2(struct wps_data *wps, const u8 *e_hash2)
+{
+ if (e_hash2 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No E-Hash2 received");
+ return -1;
+ }
+
+ os_memcpy(wps->peer_hash2, e_hash2, WPS_HASH_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", wps->peer_hash2, WPS_HASH_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_e_snonce1(struct wps_data *wps, const u8 *e_snonce1)
+{
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+
+ if (e_snonce1 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No E-SNonce1 received");
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "WPS: E-SNonce1", e_snonce1,
+ WPS_SECRET_NONCE_LEN);
+
+ /* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */
+ addr[0] = e_snonce1;
+ len[0] = WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk1;
+ len[1] = WPS_PSK_LEN;
+ addr[2] = wpabuf_head(wps->dh_pubkey_e);
+ len[2] = wpabuf_len(wps->dh_pubkey_e);
+ addr[3] = wpabuf_head(wps->dh_pubkey_r);
+ len[3] = wpabuf_len(wps->dh_pubkey_r);
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+ if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: E-Hash1 derived from E-S1 does "
+ "not match with the pre-committed value");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee proved knowledge of the first "
+ "half of the device password");
+
+ return 0;
+}
+
+
+static int wps_process_e_snonce2(struct wps_data *wps, const u8 *e_snonce2)
+{
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+
+ if (e_snonce2 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No E-SNonce2 received");
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "WPS: E-SNonce2", e_snonce2,
+ WPS_SECRET_NONCE_LEN);
+
+ /* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */
+ addr[0] = e_snonce2;
+ len[0] = WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk2;
+ len[1] = WPS_PSK_LEN;
+ addr[2] = wpabuf_head(wps->dh_pubkey_e);
+ len[2] = wpabuf_len(wps->dh_pubkey_e);
+ addr[3] = wpabuf_head(wps->dh_pubkey_r);
+ len[3] = wpabuf_len(wps->dh_pubkey_r);
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+ if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: E-Hash2 derived from E-S2 does "
+ "not match with the pre-committed value");
+ wps_registrar_invalidate_pin(wps->registrar, wps->uuid_e);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee proved knowledge of the second "
+ "half of the device password");
+ wps->wps_pin_revealed = 0;
+ wps_registrar_unlock_pin(wps->registrar, wps->uuid_e);
+
+ return 0;
+}
+
+
+static int wps_process_mac_addr(struct wps_data *wps, const u8 *mac_addr)
+{
+ if (mac_addr == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No MAC Address received");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee MAC Address " MACSTR,
+ MAC2STR(mac_addr));
+ os_memcpy(wps->mac_addr_e, mac_addr, ETH_ALEN);
+ os_memcpy(wps->peer_dev.mac_addr, mac_addr, ETH_ALEN);
+
+ return 0;
+}
+
+
+static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
+ size_t pk_len)
+{
+ if (pk == NULL || pk_len == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No Public Key received");
+ return -1;
+ }
+
+ wpabuf_free(wps->dh_pubkey_e);
+ wps->dh_pubkey_e = wpabuf_alloc_copy(pk, pk_len);
+ if (wps->dh_pubkey_e == NULL)
+ return -1;
+
+ return 0;
+}
+
+
+static int wps_process_auth_type_flags(struct wps_data *wps, const u8 *auth)
+{
+ u16 auth_types;
+
+ if (auth == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Authentication Type flags "
+ "received");
+ return -1;
+ }
+
+ auth_types = WPA_GET_BE16(auth);
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee Authentication Type flags 0x%x",
+ auth_types);
+ wps->auth_type = wps->wps->auth_types & auth_types;
+ if (wps->auth_type == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No match in supported "
+ "authentication types");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wps_process_encr_type_flags(struct wps_data *wps, const u8 *encr)
+{
+ u16 encr_types;
+
+ if (encr == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Encryption Type flags "
+ "received");
+ return -1;
+ }
+
+ encr_types = WPA_GET_BE16(encr);
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee Encryption Type flags 0x%x",
+ encr_types);
+ wps->encr_type = wps->wps->encr_types & encr_types;
+ if (wps->encr_type == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No match in supported "
+ "encryption types");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wps_process_conn_type_flags(struct wps_data *wps, const u8 *conn)
+{
+ if (conn == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Connection Type flags "
+ "received");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee Connection Type flags 0x%x",
+ *conn);
+
+ return 0;
+}
+
+
+static int wps_process_config_methods(struct wps_data *wps, const u8 *methods)
+{
+ u16 m;
+
+ if (methods == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Config Methods received");
+ return -1;
+ }
+
+ m = WPA_GET_BE16(methods);
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee Config Methods 0x%x", m);
+
+ return 0;
+}
+
+
+static int wps_process_wps_state(struct wps_data *wps, const u8 *state)
+{
+ if (state == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Wi-Fi Protected Setup State "
+ "received");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee Wi-Fi Protected Setup State %d",
+ *state);
+
+ return 0;
+}
+
+
+static int wps_process_rf_bands(struct wps_data *wps, const u8 *bands)
+{
+ if (bands == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No RF Bands received");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee RF Bands 0x%x", *bands);
+
+ return 0;
+}
+
+
+static int wps_process_assoc_state(struct wps_data *wps, const u8 *assoc)
+{
+ u16 a;
+
+ if (assoc == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Association State received");
+ return -1;
+ }
+
+ a = WPA_GET_BE16(assoc);
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee Association State %d", a);
+
+ return 0;
+}
+
+
+static int wps_process_config_error(struct wps_data *wps, const u8 *err)
+{
+ u16 e;
+
+ if (err == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Configuration Error received");
+ return -1;
+ }
+
+ e = WPA_GET_BE16(err);
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee Configuration Error %d", e);
+
+ return 0;
+}
+
+
+static enum wps_process_res wps_process_m1(struct wps_data *wps,
+ struct wps_parse_attr *attr)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Received M1");
+
+ if (wps->state != RECV_M1) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M1", wps->state);
+ return WPS_FAILURE;
+ }
+
+ if (wps_process_uuid_e(wps, attr->uuid_e) ||
+ wps_process_mac_addr(wps, attr->mac_addr) ||
+ wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+ wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
+ wps_process_auth_type_flags(wps, attr->auth_type_flags) ||
+ wps_process_encr_type_flags(wps, attr->encr_type_flags) ||
+ wps_process_conn_type_flags(wps, attr->conn_type_flags) ||
+ wps_process_config_methods(wps, attr->config_methods) ||
+ wps_process_wps_state(wps, attr->wps_state) ||
+ wps_process_device_attrs(&wps->peer_dev, attr) ||
+ wps_process_rf_bands(wps, attr->rf_bands) ||
+ wps_process_assoc_state(wps, attr->assoc_state) ||
+ wps_process_dev_password_id(wps, attr->dev_password_id) ||
+ wps_process_config_error(wps, attr->config_error) ||
+ wps_process_os_version(&wps->peer_dev, attr->os_version))
+ return WPS_FAILURE;
+
+ if (wps->dev_pw_id != DEV_PW_DEFAULT &&
+ wps->dev_pw_id != DEV_PW_USER_SPECIFIED &&
+ wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED &&
+ wps->dev_pw_id != DEV_PW_REGISTRAR_SPECIFIED &&
+ (wps->dev_pw_id != DEV_PW_PUSHBUTTON || !wps->registrar->pbc)) {
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported Device Password ID %d",
+ wps->dev_pw_id);
+ wps->state = SEND_M2D;
+ return WPS_CONTINUE;
+ }
+
+ if (wps->dev_pw_id == DEV_PW_PUSHBUTTON) {
+ if (wps_registrar_pbc_overlap(wps->registrar, wps->mac_addr_e,
+ wps->uuid_e)) {
+ wpa_printf(MSG_DEBUG, "WPS: PBC overlap - deny PBC "
+ "negotiation");
+ wps->state = SEND_M2D;
+ return WPS_CONTINUE;
+ }
+ wps_registrar_add_pbc_session(wps->registrar, wps->mac_addr_e,
+ wps->uuid_e);
+ wps->pbc = 1;
+ }
+
+ wps->state = SEND_M2;
+ return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m3(struct wps_data *wps,
+ const struct wpabuf *msg,
+ struct wps_parse_attr *attr)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Received M3");
+
+ if (wps->state != RECV_M3) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M3", wps->state);
+ return WPS_FAILURE;
+ }
+
+ if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+ wps_process_authenticator(wps, attr->authenticator, msg) ||
+ wps_process_e_hash1(wps, attr->e_hash1) ||
+ wps_process_e_hash2(wps, attr->e_hash2))
+ return WPS_FAILURE;
+
+ wps->state = SEND_M4;
+ return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m5(struct wps_data *wps,
+ const struct wpabuf *msg,
+ struct wps_parse_attr *attr)
+{
+ struct wpabuf *decrypted;
+ struct wps_parse_attr eattr;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received M5");
+
+ if (wps->state != RECV_M5) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M5", wps->state);
+ return WPS_FAILURE;
+ }
+
+ if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+ wps_process_authenticator(wps, attr->authenticator, msg))
+ return WPS_FAILURE;
+
+ decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+ attr->encr_settings_len);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+ "Settings attribute");
+ return WPS_FAILURE;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+ "attribute");
+ if (wps_parse_msg(decrypted, &eattr) < 0 ||
+ wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+ wps_process_e_snonce1(wps, eattr.e_snonce1)) {
+ wpabuf_free(decrypted);
+ return WPS_FAILURE;
+ }
+ wpabuf_free(decrypted);
+
+ wps->state = SEND_M6;
+ return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m7(struct wps_data *wps,
+ const struct wpabuf *msg,
+ struct wps_parse_attr *attr)
+{
+ struct wpabuf *decrypted;
+ struct wps_parse_attr eattr;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received M7");
+
+ if (wps->state != RECV_M7) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M7", wps->state);
+ return WPS_FAILURE;
+ }
+
+ if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+ wps_process_authenticator(wps, attr->authenticator, msg))
+ return WPS_FAILURE;
+
+ decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+ attr->encr_settings_len);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+ "Settings attribute");
+ return WPS_FAILURE;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+ "attribute");
+ if (wps_parse_msg(decrypted, &eattr) < 0 ||
+ wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+ wps_process_e_snonce2(wps, eattr.e_snonce2)) {
+ wpabuf_free(decrypted);
+ return WPS_FAILURE;
+ }
+ wpabuf_free(decrypted);
+
+ wps->state = SEND_M8;
+ return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+ enum wps_process_res ret = WPS_CONTINUE;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG");
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return WPS_FAILURE;
+
+ if (attr.version == NULL || *attr.version != WPS_VERSION) {
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
+ attr.version ? *attr.version : 0);
+ return WPS_FAILURE;
+ }
+
+ if (attr.msg_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+ return WPS_FAILURE;
+ }
+
+ if (*attr.msg_type != WPS_M1 &&
+ (attr.registrar_nonce == NULL ||
+ os_memcmp(wps->nonce_r, attr.registrar_nonce,
+ WPS_NONCE_LEN != 0))) {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+ return WPS_FAILURE;
+ }
+
+ switch (*attr.msg_type) {
+ case WPS_M1:
+ ret = wps_process_m1(wps, &attr);
+ break;
+ case WPS_M3:
+ ret = wps_process_m3(wps, msg, &attr);
+ break;
+ case WPS_M5:
+ ret = wps_process_m5(wps, msg, &attr);
+ break;
+ case WPS_M7:
+ ret = wps_process_m7(wps, msg, &attr);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
+ *attr.msg_type);
+ return WPS_FAILURE;
+ }
+
+ if (ret == WPS_CONTINUE) {
+ /* Save a copy of the last message for Authenticator derivation
+ */
+ wpabuf_free(wps->last_msg);
+ wps->last_msg = wpabuf_dup(msg);
+ }
+
+ return ret;
+}
+
+
+static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK");
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return WPS_FAILURE;
+
+ if (attr.version == NULL || *attr.version != WPS_VERSION) {
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
+ attr.version ? *attr.version : 0);
+ return WPS_FAILURE;
+ }
+
+ if (attr.msg_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+ return WPS_FAILURE;
+ }
+
+ if (*attr.msg_type != WPS_WSC_ACK) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+ *attr.msg_type);
+ return WPS_FAILURE;
+ }
+
+ if (attr.registrar_nonce == NULL ||
+ os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+ {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+ return WPS_FAILURE;
+ }
+
+ if (attr.enrollee_nonce == NULL ||
+ os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+ return WPS_FAILURE;
+ }
+
+ if (wps->state == RECV_M2D_ACK) {
+ /* TODO: support for multiple registrars and sending of
+ * multiple M2/M2D messages */
+
+ wpa_printf(MSG_DEBUG, "WPS: No more registrars available - "
+ "terminate negotiation");
+ }
+
+ return WPS_FAILURE;
+}
+
+
+static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return WPS_FAILURE;
+
+ if (attr.version == NULL || *attr.version != WPS_VERSION) {
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
+ attr.version ? *attr.version : 0);
+ return WPS_FAILURE;
+ }
+
+ if (attr.msg_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+ return WPS_FAILURE;
+ }
+
+ if (*attr.msg_type != WPS_WSC_NACK) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+ *attr.msg_type);
+ return WPS_FAILURE;
+ }
+
+ if (attr.registrar_nonce == NULL ||
+ os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+ {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+ return WPS_FAILURE;
+ }
+
+ if (attr.enrollee_nonce == NULL ||
+ os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+ return WPS_FAILURE;
+ }
+
+ if (attr.config_error == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute "
+ "in WSC_NACK");
+ return WPS_FAILURE;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee terminated negotiation with "
+ "Configuration Error %d", WPA_GET_BE16(attr.config_error));
+
+ return WPS_FAILURE;
+}
+
+
+static enum wps_process_res wps_process_wsc_done(struct wps_data *wps,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received WSC_Done");
+
+ if (wps->state != RECV_DONE) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving WSC_Done", wps->state);
+ return WPS_FAILURE;
+ }
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return WPS_FAILURE;
+
+ if (attr.version == NULL || *attr.version != WPS_VERSION) {
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
+ attr.version ? *attr.version : 0);
+ return WPS_FAILURE;
+ }
+
+ if (attr.msg_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+ return WPS_FAILURE;
+ }
+
+ if (*attr.msg_type != WPS_WSC_DONE) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+ *attr.msg_type);
+ return WPS_FAILURE;
+ }
+
+ if (attr.registrar_nonce == NULL ||
+ os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+ {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+ return WPS_FAILURE;
+ }
+
+ if (attr.enrollee_nonce == NULL ||
+ os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+ return WPS_FAILURE;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Negotiation completed successfully");
+
+ if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->new_psk &&
+ wps->wps->ap) {
+ struct wps_credential cred;
+
+ wpa_printf(MSG_DEBUG, "WPS: Moving to Configured state based "
+ "on first Enrollee connection");
+
+ os_memset(&cred, 0, sizeof(cred));
+ os_memcpy(cred.ssid, wps->wps->ssid, wps->wps->ssid_len);
+ cred.ssid_len = wps->wps->ssid_len;
+ cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK;
+ cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES;
+ os_memcpy(cred.key, wps->new_psk, wps->new_psk_len);
+ cred.key_len = wps->new_psk_len;
+
+ wps->wps->wps_state = WPS_STATE_CONFIGURED;
+ wpa_hexdump_ascii_key(MSG_DEBUG,
+ "WPS: Generated random passphrase",
+ wps->new_psk, wps->new_psk_len);
+ if (wps->wps->cred_cb)
+ wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
+
+ os_free(wps->new_psk);
+ wps->new_psk = NULL;
+ }
+
+ if (wps->new_psk) {
+ if (wps_cb_new_psk(wps->registrar, wps->mac_addr_e,
+ wps->new_psk, wps->new_psk_len)) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to configure the "
+ "new PSK");
+ }
+ os_free(wps->new_psk);
+ wps->new_psk = NULL;
+ }
+
+ if (wps->pbc) {
+ wps_registrar_remove_pbc_session(wps->registrar,
+ wps->mac_addr_e, wps->uuid_e);
+ wps_registrar_pbc_completed(wps->registrar);
+ }
+
+ return WPS_DONE;
+}
+
+
+enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
+ u8 op_code,
+ const struct wpabuf *msg)
+{
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
+ "op_code=%d)",
+ (unsigned long) wpabuf_len(msg), op_code);
+
+ switch (op_code) {
+ case WSC_MSG:
+ return wps_process_wsc_msg(wps, msg);
+ case WSC_ACK:
+ return wps_process_wsc_ack(wps, msg);
+ case WSC_NACK:
+ return wps_process_wsc_nack(wps, msg);
+ case WSC_Done:
+ return wps_process_wsc_done(wps, msg);
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
+ return WPS_FAILURE;
+ }
+}
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;