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,