From 52eb293dd25aec61cf0ada27c073b04fde15e563 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sun, 6 Sep 2009 13:58:15 +0300 Subject: [PATCH] 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. --- src/eap_peer/eap_wsc.c | 81 ++++++++++++++++++++++- src/wps/wps.c | 14 +++- src/wps/wps.h | 12 +++- src/wps/wps_i.h | 4 +- src/wps/wps_registrar.c | 43 +++++++----- wpa_supplicant/README-WPS | 24 +++++-- wpa_supplicant/ctrl_iface.c | 34 +++++++++- wpa_supplicant/ctrl_iface_dbus_handlers.c | 4 +- wpa_supplicant/wpa_cli.c | 37 ++++++++++- wpa_supplicant/wps_supplicant.c | 25 ++++++- wpa_supplicant/wps_supplicant.h | 11 ++- 11 files changed, 252 insertions(+), 37 deletions(-) diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c index 7c8ad2fda..9a0354e10 100644 --- a/src/eap_peer/eap_wsc.c +++ b/src/eap_peer/eap_wsc.c @@ -1,6 +1,6 @@ /* * EAP-WSC peer for Wi-Fi Protected Setup - * Copyright (c) 2007-2008, Jouni Malinen + * Copyright (c) 2007-2009, Jouni Malinen * * 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); diff --git a/src/wps/wps.c b/src/wps/wps.c index dde16d17a..e0e19c888 100644 --- a/src/wps/wps.c +++ b/src/wps/wps.c @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - * Copyright (c) 2007-2008, Jouni Malinen + * Copyright (c) 2007-2009, Jouni Malinen * * 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); } diff --git a/src/wps/wps.h b/src/wps/wps.h index 9807ad425..c33e8013a 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - * Copyright (c) 2007-2008, Jouni Malinen + * Copyright (c) 2007-2009, Jouni Malinen * * 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); diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h index e3cf23654..b79c5291f 100644 --- a/src/wps/wps_i.h +++ b/src/wps/wps_i.h @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - internal definitions - * Copyright (c) 2008, Jouni Malinen + * Copyright (c) 2008-2009, Jouni Malinen * * 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; }; diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index 185db8c0c..b625d21c4 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - Registrar - * Copyright (c) 2008, Jouni Malinen + * Copyright (c) 2008-2009, Jouni Malinen * * 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. - */ - - return 0; -#else - /* - * For now, use the AP PIN only to receive the current AP settings, - * not to reconfigure the AP. - */ - wps_sta_cred_cb(wps); - return 1; -#endif + 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 { + /* + * Use the AP PIN only to receive the current AP settings, not + * to reconfigure the AP. + */ + wps_sta_cred_cb(wps); + return 1; + } } diff --git a/wpa_supplicant/README-WPS b/wpa_supplicant/README-WPS index 6b826a794..2b1ded0e0 100644 --- a/wpa_supplicant/README-WPS +++ b/wpa_supplicant/README-WPS @@ -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 (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 +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 "" + + must be one of the following: OPEN WPAPSK WPA2PSK + must be one of the following: NONE WEP TKIP CCMP Scanning diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 08121b6dd..5e2030ec1 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / Control interface (shared code for all backends) - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2009, Jouni Malinen * * 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 */ diff --git a/wpa_supplicant/ctrl_iface_dbus_handlers.c b/wpa_supplicant/ctrl_iface_dbus_handlers.c index 53a18467f..f9bd7ed4f 100644 --- a/wpa_supplicant/ctrl_iface_dbus_handlers.c +++ b/wpa_supplicant/ctrl_iface_dbus_handlers.c @@ -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"); diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 73937d71e..b5b346939 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -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; diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index 6fb1aa333..0ca67459d 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -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); diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h index abc4b9225..47253126f 100644 --- a/wpa_supplicant/wps_supplicant.h +++ b/wpa_supplicant/wps_supplicant.h @@ -1,6 +1,6 @@ /* * wpa_supplicant / WPS integration - * Copyright (c) 2008, Jouni Malinen + * Copyright (c) 2008-2009, Jouni Malinen * * 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,