WPS: Add support for AP reconfiguration with wps_reg

wpa_supplicant can now reconfigure the AP by acting as an External
Registrar with the wps_reg command. Previously, this was only used
to fetch the current AP settings, but now the wps_reg command has
optional arguments which can be used to provide the new AP
configuration. When the new parameters are set, the WPS protocol run
is allowed to continue through M8 to reconfigure the AP instead of
stopping at M7.
This commit is contained in:
Jouni Malinen 2009-09-06 13:58:15 +03:00
parent e6965d4e5d
commit 52eb293dd2
11 changed files with 252 additions and 37 deletions

View file

@ -1,6 +1,6 @@
/*
* EAP-WSC peer for Wi-Fi Protected Setup
* Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
* Copyright (c) 2007-2009, 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
@ -65,6 +65,72 @@ static void eap_wsc_state(struct eap_wsc_data *data, int state)
}
static int eap_wsc_new_ap_settings(struct wps_credential *cred,
const char *params)
{
const char *pos, *end;
size_t len;
os_memset(cred, 0, sizeof(*cred));
pos = os_strstr(params, "new_ssid=");
if (pos == NULL)
return 0;
pos += 9;
end = os_strchr(pos, ' ');
if (end == NULL)
len = os_strlen(pos);
else
len = end - pos;
if ((len & 1) || len > 2 * sizeof(cred->ssid) ||
hexstr2bin(pos, cred->ssid, len / 2))
return -1;
cred->ssid_len = len / 2;
pos = os_strstr(params, "new_auth=");
if (pos == NULL)
return -1;
if (os_strncmp(pos + 9, "OPEN", 4) == 0)
cred->auth_type = WPS_AUTH_OPEN;
else if (os_strncmp(pos + 9, "WPAPSK", 6) == 0)
cred->auth_type = WPS_AUTH_WPAPSK;
else if (os_strncmp(pos + 9, "WPA2PSK", 7) == 0)
cred->auth_type = WPS_AUTH_WPA2PSK;
else
return -1;
pos = os_strstr(params, "new_encr=");
if (pos == NULL)
return -1;
if (os_strncmp(pos + 9, "NONE", 4) == 0)
cred->encr_type = WPS_ENCR_NONE;
else if (os_strncmp(pos + 9, "WEP", 3) == 0)
cred->encr_type = WPS_ENCR_WEP;
else if (os_strncmp(pos + 9, "TKIP", 4) == 0)
cred->encr_type = WPS_ENCR_TKIP;
else if (os_strncmp(pos + 9, "CCMP", 4) == 0)
cred->encr_type = WPS_ENCR_AES;
else
return -1;
pos = os_strstr(params, "new_key=");
if (pos == NULL)
return 0;
pos += 8;
end = os_strchr(pos, ' ');
if (end == NULL)
len = os_strlen(pos);
else
len = end - pos;
if ((len & 1) || len > 2 * sizeof(cred->key) ||
hexstr2bin(pos, cred->key, len / 2))
return -1;
cred->key_len = len / 2;
return 1;
}
static void * eap_wsc_init(struct eap_sm *sm)
{
struct eap_wsc_data *data;
@ -75,6 +141,8 @@ static void * eap_wsc_init(struct eap_sm *sm)
const char *pos;
const char *phase1;
struct wps_context *wps;
struct wps_credential new_ap_settings;
int res;
wps = sm->wps;
if (wps == NULL) {
@ -135,6 +203,17 @@ static void * eap_wsc_init(struct eap_sm *sm)
return NULL;
}
res = eap_wsc_new_ap_settings(&new_ap_settings, phase1);
if (res < 0) {
os_free(data);
return NULL;
}
if (res == 1) {
wpa_printf(MSG_DEBUG, "EAP-WSC: Provide new AP settings for "
"WPS");
cfg.new_ap_settings = &new_ap_settings;
}
data->wps = wps_init(&cfg);
if (data->wps == NULL) {
os_free(data);

View file

@ -1,6 +1,6 @@
/*
* Wi-Fi Protected Setup
* Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
* Copyright (c) 2007-2009, 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
@ -89,6 +89,17 @@ struct wps_data * wps_init(const struct wps_config *cfg)
}
}
if (cfg->new_ap_settings) {
data->new_ap_settings =
os_malloc(sizeof(*data->new_ap_settings));
if (data->new_ap_settings == NULL) {
os_free(data);
return NULL;
}
os_memcpy(data->new_ap_settings, cfg->new_ap_settings,
sizeof(*data->new_ap_settings));
}
return data;
}
@ -115,6 +126,7 @@ void wps_deinit(struct wps_data *data)
os_free(data->dev_password);
os_free(data->new_psk);
wps_device_data_free(&data->peer_dev);
os_free(data->new_ap_settings);
os_free(data);
}

View file

@ -1,6 +1,6 @@
/*
* Wi-Fi Protected Setup
* Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
* Copyright (c) 2007-2009, 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
@ -132,6 +132,16 @@ struct wps_config {
* assoc_wps_ie: (Re)AssocReq WPS IE (in AP; %NULL if not AP)
*/
const struct wpabuf *assoc_wps_ie;
/**
* new_ap_settings - New AP settings (%NULL if not used)
*
* This parameter provides new AP settings when using a wireless
* stations as a Registrar to configure the AP. %NULL means that AP
* will not be reconfigured, i.e., the station will only learn the
* current AP settings by using AP PIN.
*/
const struct wps_credential *new_ap_settings;
};
struct wps_data * wps_init(const struct wps_config *cfg);

View file

@ -1,6 +1,6 @@
/*
* Wi-Fi Protected Setup - internal definitions
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
* Copyright (c) 2008-2009, 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
@ -103,6 +103,8 @@ struct wps_data {
u16 config_error;
int ext_reg;
struct wps_credential *new_ap_settings;
};

View file

@ -1,6 +1,6 @@
/*
* Wi-Fi Protected Setup - Registrar
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
* Copyright (c) 2008-2009, 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
@ -2041,6 +2041,19 @@ static void wps_sta_cred_cb(struct wps_data *wps)
}
static void wps_cred_update(struct wps_credential *dst,
struct wps_credential *src)
{
os_memcpy(dst->ssid, src->ssid, sizeof(dst->ssid));
dst->ssid_len = src->ssid_len;
dst->auth_type = src->auth_type;
dst->encr_type = src->encr_type;
dst->key_idx = src->key_idx;
os_memcpy(dst->key, src->key, sizeof(dst->key));
dst->key_len = src->key_len;
}
static int wps_process_ap_settings_r(struct wps_data *wps,
struct wps_parse_attr *attr)
{
@ -2053,21 +2066,19 @@ static int wps_process_ap_settings_r(struct wps_data *wps,
wpa_printf(MSG_INFO, "WPS: Received old AP configuration from AP");
#if 0
/*
* TODO: Provide access to AP settings and allow changes before sending
* out M8. For now, just copy the settings unchanged into M8.
*/
if (wps->new_ap_settings) {
wpa_printf(MSG_INFO, "WPS: Update AP configuration based on "
"new settings");
wps_cred_update(&wps->cred, wps->new_ap_settings);
return 0;
#else
} else {
/*
* For now, use the AP PIN only to receive the current AP settings,
* not to reconfigure the AP.
* Use the AP PIN only to receive the current AP settings, not
* to reconfigure the AP.
*/
wps_sta_cred_cb(wps);
return 1;
#endif
}
}

View file

@ -131,17 +131,29 @@ negotiation which will generate a new WPA PSK in the same way as the
PIN method described above.
If the client wants to operate in the Registrar role to configure an
AP, wpa_supplicant is notified over the control interface, e.g., with
If the client wants to operate in the Registrar role to learn the
current AP configuration and optionally, to configure an AP,
wpa_supplicant is notified over the control interface, e.g., with
wpa_cli:
wpa_cli wps_reg <AP BSSID> <AP PIN>
(example: wpa_cli wps_reg 02:34:56:78:9a:bc 12345670)
This is currently only used to fetch the current AP settings instead
of actually changing them. The main difference with the wps_pin
command is that wps_reg uses the AP PIN (e.g., from a label on the AP)
instead of a PIN generated at the client.
This is used to fetch the current AP settings instead of actually
changing them. The main difference with the wps_pin command is that
wps_reg uses the AP PIN (e.g., from a label on the AP) instead of a
PIN generated at the client.
In order to change the AP configuration, the new configuration
parameters are given to the wps_reg command:
wpa_cli wps_reg <AP BSSID> <AP PIN> <new SSID> <auth> <encr> <new key>
examples:
wpa_cli wps_reg 02:34:56:78:9a:bc 12345670 testing WPA2PSK CCMP 12345678
wpa_cli wps_reg 02:34:56:78:9a:bc 12345670 clear OPEN NONE ""
<auth> must be one of the following: OPEN WPAPSK WPA2PSK
<encr> must be one of the following: NONE WEP TKIP CCMP
Scanning

View file

@ -1,6 +1,6 @@
/*
* WPA Supplicant / Control interface (shared code for all backends)
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
* Copyright (c) 2004-2009, 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
@ -248,6 +248,11 @@ static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
{
u8 bssid[ETH_ALEN], *_bssid = bssid;
char *pin;
char *new_ssid;
char *new_auth;
char *new_encr;
char *new_key;
struct wps_new_ap_settings ap;
pin = os_strchr(cmd, ' ');
if (pin == NULL)
@ -262,7 +267,32 @@ static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
return -1;
}
return wpas_wps_start_reg(wpa_s, _bssid, pin);
new_ssid = os_strchr(pin, ' ');
if (new_ssid == NULL)
return wpas_wps_start_reg(wpa_s, _bssid, pin, NULL);
*new_ssid++ = '\0';
new_auth = os_strchr(new_ssid, ' ');
if (new_auth == NULL)
return -1;
*new_auth++ = '\0';
new_encr = os_strchr(new_auth, ' ');
if (new_encr == NULL)
return -1;
*new_encr++ = '\0';
new_key = os_strchr(new_encr, ' ');
if (new_key == NULL)
return -1;
*new_key++ = '\0';
os_memset(&ap, 0, sizeof(ap));
ap.ssid_hex = new_ssid;
ap.auth = new_auth;
ap.encr = new_encr;
ap.key_hex = new_key;
return wpas_wps_start_reg(wpa_s, _bssid, pin, &ap);
}
#endif /* CONFIG_WPS */

View file

@ -1615,9 +1615,9 @@ DBusMessage * wpas_dbus_iface_wps_reg(DBusMessage *message,
return wpas_dbus_new_invalid_opts_error(message, NULL);
if (!os_strcmp(arg_bssid, "any"))
ret = wpas_wps_start_reg(wpa_s, NULL, pin);
ret = wpas_wps_start_reg(wpa_s, NULL, pin, NULL);
else if (!hwaddr_aton(arg_bssid, bssid))
ret = wpas_wps_start_reg(wpa_s, bssid, pin);
ret = wpas_wps_start_reg(wpa_s, bssid, pin, NULL);
else {
return wpas_dbus_new_invalid_opts_error(message,
"Invalid BSSID");

View file

@ -484,14 +484,47 @@ static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[])
char cmd[256];
int res;
if (argc != 2) {
if (argc == 2)
res = os_snprintf(cmd, sizeof(cmd), "WPS_REG %s %s",
argv[0], argv[1]);
else if (argc == 6) {
char ssid_hex[2 * 32 + 1];
char key_hex[2 * 64 + 1];
int i;
ssid_hex[0] = '\0';
for (i = 0; i < 32; i++) {
if (argv[2][i] == '\0')
break;
os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[2][i]);
}
key_hex[0] = '\0';
for (i = 0; i < 64; i++) {
if (argv[5][i] == '\0')
break;
os_snprintf(&key_hex[i * 2], 3, "%02x", argv[5][i]);
}
res = os_snprintf(cmd, sizeof(cmd),
"WPS_REG %s %s %s %s %s %s",
argv[0], argv[1], ssid_hex, argv[3], argv[4],
key_hex);
} else {
printf("Invalid WPS_REG command: need two arguments:\n"
"- BSSID: use 'any' to select any\n"
"- AP PIN\n");
printf("Alternatively, six arguments can be used to "
"reconfigure the AP:\n"
"- BSSID: use 'any' to select any\n"
"- AP PIN\n"
"- new SSID\n"
"- new auth (OPEN, WPAPSK, WPA2PSK)\n"
"- new encr (NONE, WEP, TKIP, CCMP)\n"
"- new key\n");
return -1;
}
res = os_snprintf(cmd, sizeof(cmd), "WPS_REG %s %s", argv[0], argv[1]);
if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
printf("Too long WPS_REG command.\n");
return -1;

View file

@ -630,10 +630,12 @@ int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type,
int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
const char *pin)
const char *pin, struct wps_new_ap_settings *settings)
{
struct wpa_ssid *ssid;
char val[30];
char val[200];
char *pos, *end;
int res;
if (!pin)
return -1;
@ -641,7 +643,24 @@ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
ssid = wpas_wps_add_network(wpa_s, 1, bssid);
if (ssid == NULL)
return -1;
os_snprintf(val, sizeof(val), "\"pin=%s\"", pin);
pos = val;
end = pos + sizeof(val);
res = os_snprintf(pos, end - pos, "\"pin=%s", pin);
if (res < 0 || res >= end - pos)
return -1;
pos += res;
if (settings) {
res = os_snprintf(pos, end - pos, " new_ssid=%s new_auth=%s "
"new_encr=%s new_key=%s",
settings->ssid_hex, settings->auth,
settings->encr, settings->key_hex);
if (res < 0 || res >= end - pos)
return -1;
pos += res;
}
res = os_snprintf(pos, end - pos, "\"");
if (res < 0 || res >= end - pos)
return -1;
wpa_config_set(ssid, "phase1", val, 0);
eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
wpa_s, NULL);

View file

@ -1,6 +1,6 @@
/*
* wpa_supplicant / WPS integration
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
* Copyright (c) 2008-2009, 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
@ -20,6 +20,13 @@
#include "wps/wps.h"
#include "wps/wps_defs.h"
struct wps_new_ap_settings {
const char *ssid_hex;
const char *auth;
const char *encr;
const char *key_hex;
};
int wpas_wps_init(struct wpa_supplicant *wpa_s);
void wpas_wps_deinit(struct wpa_supplicant *wpa_s);
int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s);
@ -30,7 +37,7 @@ int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type,
char *path, char *method, char *name);
int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
const char *pin);
const char *pin, struct wps_new_ap_settings *settings);
int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid, struct wpa_scan_res *bss);
int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,