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.
master
Jouni Malinen 16 years ago
parent 6e89cc438e
commit ad08c3633c

@ -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

@ -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

@ -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.

@ -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

@ -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,

@ -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 */

@ -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 */
};

@ -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;

@ -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

@ -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 */

@ -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,
};

@ -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,
};

@ -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,
};

@ -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;
}

@ -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

@ -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");

@ -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

@ -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 */

@ -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 },

@ -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));

@ -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;

@ -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;
}

@ -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);
}

@ -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 */

@ -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.

@ -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;
/**

@ -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 "

@ -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;

@ -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 */

@ -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)

@ -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);

@ -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 */
};

@ -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;
}

@ -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 */

@ -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;
}

@ -22,6 +22,7 @@
struct eap_sm;
struct wpa_config_blob;
struct wpabuf;
struct wps_credential;
struct eap_method_type {
int vendor;
@ -213,6 +214,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
@ -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 */

@ -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;

@ -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);

@ -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;
}

@ -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");

@ -103,6 +103,7 @@ struct eap_config {
int pac_key_refresh_time;
int eap_sim_aka_result_ind;
int tnc;
struct wps_context *wps;
};

@ -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,

@ -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);

@ -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;
}

@ -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) {

@ -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

@ -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);

@ -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);

@ -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) {

@ -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

@ -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;
}

@ -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 */

@ -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;
}

@ -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 */

@ -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;
}

@ -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 */

File diff suppressed because it is too large Load Diff

@ -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 */

File diff suppressed because it is too large Load Diff

@ -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

@ -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);

@ -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");

@ -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++;
}

@ -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))

@ -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

@ -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) {

@ -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

@ -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);

@ -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) {

@ -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)

@ -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;

Loading…
Cancel
Save