aboutsummaryrefslogtreecommitdiffstats
path: root/hostapd
diff options
context:
space:
mode:
authorJouni Malinen <j@w1.fi>2008-11-23 17:34:26 (GMT)
committerJouni Malinen <j@w1.fi>2008-11-23 17:34:26 (GMT)
commitad08c3633cc2858d28b30a3545341e1e4dda4a90 (patch)
tree90b8f0ed5762d876df61013e851acc539f29f320 /hostapd
parent6e89cc438e1af4b9b23341faee40bbe271b3de8d (diff)
downloadhostap-06-ad08c3633cc2858d28b30a3545341e1e4dda4a90.zip
hostap-06-ad08c3633cc2858d28b30a3545341e1e4dda4a90.tar.gz
hostap-06-ad08c3633cc2858d28b30a3545341e1e4dda4a90.tar.bz2
Added preliminary Wi-Fi Protected Setup (WPS) implementation
This adds WPS support for both hostapd and wpa_supplicant. Both programs can be configured to act as WPS Enrollee and Registrar. Both PBC and PIN methods are supported. Currently, hostapd has more complete configuration option for WPS parameters and wpa_supplicant configuration style will likely change in the future. External Registrars are not yet supported in hostapd or wpa_supplicant. While wpa_supplicant has initial support for acting as an Registrar to configure an AP, this is still using number of hardcoded parameters which will need to be made configurable for proper operation.
Diffstat (limited to 'hostapd')
-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
24 files changed, 1519 insertions, 34 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 */