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
parent
6e89cc438e
commit
ad08c3633c
@ -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.
|
@ -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 */
|
@ -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 */
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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
Loading…
Reference in New Issue