aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/wps/wps.h20
-rw-r--r--src/wps/wps_er.c213
-rw-r--r--src/wps/wps_registrar.c27
-rw-r--r--wpa_supplicant/ctrl_iface.c20
-rw-r--r--wpa_supplicant/wpa_cli.c26
-rw-r--r--wpa_supplicant/wps_supplicant.c32
-rw-r--r--wpa_supplicant/wps_supplicant.h2
7 files changed, 304 insertions, 36 deletions
diff --git a/src/wps/wps.h b/src/wps/wps.h
index 76b818e..7798ad8 100644
--- a/src/wps/wps.h
+++ b/src/wps/wps.h
@@ -253,6 +253,24 @@ struct wps_registrar_config {
const u8 *uuid_e);
/**
+ * set_sel_reg_cb - Callback for reporting selected registrar changes
+ * @ctx: Higher layer context data (cb_ctx)
+ * @sel_reg: Whether the Registrar is selected
+ * @dev_passwd_id: Device Password ID to indicate with method or
+ * specific password the Registrar intends to use
+ * @sel_reg_config_methods: Bit field of active config methods
+ *
+ * This callback is called whenever the Selected Registrar state
+ * changes (e.g., a new PIN becomes available or PBC is invoked). This
+ * callback is only used by External Registrar implementation;
+ * set_ie_cb() is used by AP implementation in similar caes, but it
+ * provides the full WPS IE data instead of just the minimal Registrar
+ * state information.
+ */
+ void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id,
+ u16 sel_reg_config_methods);
+
+ /**
* cb_ctx: Higher layer context data for Registrar callbacks
*/
void *cb_ctx;
@@ -609,5 +627,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end);
struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname);
void wps_er_deinit(struct wps_er *er);
+void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
+ u16 sel_reg_config_methods);
#endif /* WPS_H */
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
index 6bfdac4..c86ea26 100644
--- a/src/wps/wps_er.c
+++ b/src/wps/wps_er.c
@@ -741,41 +741,24 @@ static const char *soap_postfix =
static const char *urn_wfawlanconfig =
"urn:schemas-wifialliance-org:service:WFAWLANConfig:1";
-static void wps_er_sta_send_msg(struct wps_er_sta *sta, struct wpabuf *msg)
+static struct wpabuf * wps_er_soap_hdr(const struct wpabuf *msg,
+ const char *name, const char *path,
+ const struct sockaddr_in *dst,
+ char **len_ptr, char **body_ptr)
{
unsigned char *encoded;
size_t encoded_len;
struct wpabuf *buf;
- char *len_ptr, *body_ptr;
- char len_buf[10];
- struct sockaddr_in dst;
- char *url, *path;
-
- if (sta->http) {
- wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for STA - "
- "ignore new request");
- return;
- }
-
- url = http_client_url_parse(sta->ap->control_url, &dst, &path);
- if (url == NULL) {
- wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse eventSubURL");
- return;
- }
encoded = base64_encode(wpabuf_head(msg), wpabuf_len(msg),
&encoded_len);
- wpabuf_free(msg);
- if (encoded == NULL) {
- os_free(url);
- return;
- }
+ if (encoded == NULL)
+ return NULL;
buf = wpabuf_alloc(1000 + encoded_len);
if (buf == NULL) {
os_free(encoded);
- os_free(url);
- return;
+ return NULL;
}
wpabuf_printf(buf,
@@ -783,33 +766,79 @@ static void wps_er_sta_send_msg(struct wps_er_sta *sta, struct wpabuf *msg)
"Host: %s:%d\r\n"
"Content-Type: text/xml; charset=\"utf-8\"\r\n"
"Content-Length: ",
- path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port));
- os_free(url);
- len_ptr = wpabuf_put(buf, 0);
+ path, inet_ntoa(dst->sin_addr), ntohs(dst->sin_port));
+
+ *len_ptr = wpabuf_put(buf, 0);
wpabuf_printf(buf,
" \r\n"
- "SOAPACTION: \"%s#PutWLANResponse\"\r\n"
+ "SOAPACTION: \"%s#%s\"\r\n"
"\r\n",
- urn_wfawlanconfig);
+ urn_wfawlanconfig, name);
- body_ptr = wpabuf_put(buf, 0);
+ *body_ptr = wpabuf_put(buf, 0);
wpabuf_put_str(buf, soap_prefix);
- wpabuf_put_str(buf, "<u:PutWLANResponse xmlns:u=\"");
+ wpabuf_printf(buf, "<u:%s xmlns:u=\"", name);
wpabuf_put_str(buf, urn_wfawlanconfig);
wpabuf_put_str(buf, "\">\n");
wpabuf_printf(buf, "<NewMessage>%s</NewMessage>\n", (char *) encoded);
os_free(encoded);
+
+ return buf;
+}
+
+
+static void wps_er_soap_end(struct wpabuf *buf, const char *name,
+ char *len_ptr, char *body_ptr)
+{
+ char len_buf[10];
+ wpabuf_printf(buf, "</u:%s>\n", name);
+ wpabuf_put_str(buf, soap_postfix);
+ os_snprintf(len_buf, sizeof(len_buf), "%d",
+ (int) ((char *) wpabuf_put(buf, 0) - body_ptr));
+ os_memcpy(len_ptr, len_buf, os_strlen(len_buf));
+}
+
+
+static void wps_er_sta_send_msg(struct wps_er_sta *sta, struct wpabuf *msg)
+{
+ struct wpabuf *buf;
+ char *len_ptr, *body_ptr;
+ struct sockaddr_in dst;
+ char *url, *path;
+
+ if (sta->http) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for STA - "
+ "ignore new request");
+ wpabuf_free(msg);
+ return;
+ }
+
+ if (sta->ap->control_url == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
+ wpabuf_free(msg);
+ return;
+ }
+
+ url = http_client_url_parse(sta->ap->control_url, &dst, &path);
+ if (url == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
+ wpabuf_free(msg);
+ return;
+ }
+
+ buf = wps_er_soap_hdr(msg, "PutWLANResponse", path, &dst, &len_ptr,
+ &body_ptr);
+ wpabuf_free(msg);
+ os_free(url);
+ if (buf == NULL)
+ return;
wpabuf_printf(buf, "<NewWLANEventType>%d</NewWLANEventType>\n",
UPNP_WPS_WLANEVENT_TYPE_EAP);
wpabuf_printf(buf, "<NewWLANEventMAC>" MACSTR "</NewWLANEventMAC>\n",
MAC2STR(sta->addr));
- wpabuf_put_str(buf, "</u:PutWLANResponse>\n");
- wpabuf_put_str(buf, soap_postfix);
- os_snprintf(len_buf, sizeof(len_buf), "%d",
- (int) ((char *) wpabuf_put(buf, 0) - body_ptr));
- os_memcpy(len_ptr, len_buf, os_strlen(len_buf));
+ wps_er_soap_end(buf, "PutWLANResponse", len_ptr, body_ptr);
sta->http = http_client_addr(&dst, buf, 1000,
wps_er_http_put_wlan_response_cb, sta);
@@ -1088,3 +1117,115 @@ void wps_er_deinit(struct wps_er *er)
os_free(er->mac_addr_text);
os_free(er);
}
+
+
+static void wps_er_http_set_sel_reg_cb(void *ctx, struct http_client *c,
+ enum http_client_event event)
+{
+ struct wps_er_ap *ap = ctx;
+
+ switch (event) {
+ case HTTP_CLIENT_OK:
+ wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar OK");
+ break;
+ case HTTP_CLIENT_FAILED:
+ case HTTP_CLIENT_INVALID_REPLY:
+ case HTTP_CLIENT_TIMEOUT:
+ wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar failed");
+ break;
+ }
+ http_client_free(ap->http);
+ ap->http = NULL;
+}
+
+
+static void wps_er_send_set_sel_reg(struct wps_er_ap *ap, struct wpabuf *msg)
+{
+ struct wpabuf *buf;
+ char *len_ptr, *body_ptr;
+ struct sockaddr_in dst;
+ char *url, *path;
+
+ if (ap->control_url == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
+ return;
+ }
+
+ if (ap->http) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for AP - "
+ "ignore new request");
+ return;
+ }
+
+ url = http_client_url_parse(ap->control_url, &dst, &path);
+ if (url == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
+ return;
+ }
+
+ buf = wps_er_soap_hdr(msg, "SetSelectedRegistrar", path, &dst,
+ &len_ptr, &body_ptr);
+ os_free(url);
+ if (buf == NULL)
+ return;
+
+ wps_er_soap_end(buf, "SetSelectedRegistrar", len_ptr, body_ptr);
+
+ ap->http = http_client_addr(&dst, buf, 1000,
+ wps_er_http_set_sel_reg_cb, ap);
+ if (ap->http == NULL)
+ wpabuf_free(buf);
+}
+
+
+static int wps_er_build_selected_registrar(struct wpabuf *msg, int sel_reg)
+{
+ wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, !!sel_reg);
+ return 0;
+}
+
+
+static int wps_er_build_dev_password_id(struct wpabuf *msg, u16 dev_passwd_id)
+{
+ wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, dev_passwd_id);
+ return 0;
+}
+
+
+static int wps_er_build_sel_reg_config_methods(struct wpabuf *msg,
+ u16 sel_reg_config_methods)
+{
+ wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, sel_reg_config_methods);
+ return 0;
+}
+
+
+void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
+ u16 sel_reg_config_methods)
+{
+ struct wpabuf *msg;
+ struct wps_er_ap *ap;
+
+ msg = wpabuf_alloc(500);
+ if (msg == NULL)
+ return;
+
+ if (wps_build_version(msg) ||
+ wps_er_build_selected_registrar(msg, sel_reg) ||
+ wps_er_build_dev_password_id(msg, dev_passwd_id) ||
+ wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods)) {
+ wpabuf_free(msg);
+ return;
+ }
+
+ for (ap = er->ap; ap; ap = ap->next)
+ wps_er_send_set_sel_reg(ap, msg);
+
+ wpabuf_free(msg);
+}
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index 6bd9dc1..d8053d4 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -101,6 +101,8 @@ struct wps_registrar {
const struct wps_device_data *dev);
void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
const u8 *uuid_e);
+ void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id,
+ u16 sel_reg_config_methods);
void *cb_ctx;
struct wps_uuid_pin *pins;
@@ -120,6 +122,7 @@ struct wps_registrar {
static int wps_set_ie(struct wps_registrar *reg);
+static void wps_cb_set_sel_reg(struct wps_registrar *reg);
static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx);
static void wps_registrar_set_selected_timeout(void *eloop_ctx,
void *timeout_ctx);
@@ -449,6 +452,7 @@ wps_registrar_init(struct wps_context *wps,
reg->set_ie_cb = cfg->set_ie_cb;
reg->pin_needed_cb = cfg->pin_needed_cb;
reg->reg_success_cb = cfg->reg_success_cb;
+ reg->set_sel_reg_cb = cfg->set_sel_reg_cb;
reg->cb_ctx = cfg->cb_ctx;
reg->skip_cred_build = cfg->skip_cred_build;
if (cfg->extra_cred) {
@@ -536,6 +540,7 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid,
reg->selected_registrar = 1;
reg->pbc = 0;
wps_set_ie(reg);
+ wps_cb_set_sel_reg(reg);
eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
wps_registrar_set_selected_timeout,
@@ -690,6 +695,7 @@ static void wps_registrar_stop_pbc(struct wps_registrar *reg)
reg->selected_registrar = 0;
reg->pbc = 0;
wps_set_ie(reg);
+ wps_cb_set_sel_reg(reg);
}
@@ -725,6 +731,7 @@ int wps_registrar_button_pushed(struct wps_registrar *reg)
reg->selected_registrar = 1;
reg->pbc = 1;
wps_set_ie(reg);
+ wps_cb_set_sel_reg(reg);
eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout,
@@ -746,6 +753,7 @@ static void wps_registrar_pin_completed(struct wps_registrar *reg)
eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
reg->selected_registrar = 0;
wps_set_ie(reg);
+ wps_cb_set_sel_reg(reg);
}
@@ -843,6 +851,24 @@ static int wps_cb_set_ie(struct wps_registrar *reg,
}
+static void wps_cb_set_sel_reg(struct wps_registrar *reg)
+{
+ u16 methods = 0;
+ if (reg->set_sel_reg_cb == NULL)
+ return;
+
+ if (reg->selected_registrar) {
+ methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+ if (reg->pbc)
+ methods |= WPS_CONFIG_PUSHBUTTON;
+ }
+
+ reg->set_sel_reg_cb(reg->cb_ctx, reg->selected_registrar,
+ reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT,
+ methods);
+}
+
+
/* Encapsulate WPS IE data with one (or more, if needed) IE headers */
static struct wpabuf * wps_ie_encapsulate(struct wpabuf *data)
{
@@ -2691,6 +2717,7 @@ static void wps_registrar_set_selected_timeout(void *eloop_ctx,
reg->sel_reg_dev_password_id_override = -1;
reg->sel_reg_config_methods_override = -1;
wps_set_ie(reg);
+ wps_cb_set_sel_reg(reg);
}
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index c00169c..9451bca 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -295,6 +295,21 @@ static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
ap.key_hex = new_key;
return wpas_wps_start_reg(wpa_s, _bssid, pin, &ap);
}
+
+
+#ifdef CONFIG_WPS_ER
+static int wpa_supplicant_ctrl_iface_wps_er_pin(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ char *uuid = cmd, *pin;
+ pin = os_strchr(uuid, ' ');
+ if (pin == NULL)
+ return -1;
+ *pin++ = '\0';
+ return wpas_wps_er_add_pin(wpa_s, uuid, pin);
+}
+#endif /* CONFIG_WPS_ER */
+
#endif /* CONFIG_WPS */
@@ -1632,12 +1647,17 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
} else if (os_strncmp(buf, "WPS_REG ", 8) == 0) {
if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8))
reply_len = -1;
+#ifdef CONFIG_WPS_ER
} else if (os_strcmp(buf, "WPS_ER_START") == 0) {
if (wpas_wps_er_start(wpa_s))
reply_len = -1;
} else if (os_strcmp(buf, "WPS_ER_STOP") == 0) {
if (wpas_wps_er_stop(wpa_s))
reply_len = -1;
+ } else if (os_strncmp(buf, "WPS_ER_PIN ", 11) == 0) {
+ if (wpa_supplicant_ctrl_iface_wps_er_pin(wpa_s, buf + 11))
+ reply_len = -1;
+#endif /* CONFIG_WPS_ER */
#endif /* CONFIG_WPS */
#ifdef CONFIG_IBSS_RSN
} else if (os_strncmp(buf, "IBSS_RSN ", 9) == 0) {
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index f19a603..84dc8c0 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -549,6 +549,29 @@ static int wpa_cli_cmd_wps_er_stop(struct wpa_ctrl *ctrl, int argc,
}
+static int wpa_cli_cmd_wps_er_pin(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[256];
+ int res;
+
+ if (argc != 2) {
+ printf("Invalid WPS_ER_PIN command: need two arguments:\n"
+ "- UUID: use 'any' to select any\n"
+ "- PIN: Enrollee PIN\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s",
+ argv[0], argv[1]);
+ if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+ printf("Too long WPS_ER_PIN command.\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char cmd[256];
@@ -1419,6 +1442,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
{ "wps_er_stop", wpa_cli_cmd_wps_er_stop,
cli_cmd_flag_none,
"= stop Wi-Fi Protected Setup External Registrar" },
+ { "wps_er_pin", wpa_cli_cmd_wps_er_pin,
+ cli_cmd_flag_sensitive,
+ "<UUID> <PIN> = add an Enrollee PIN to External Registrar" },
{ "ibss_rsn", wpa_cli_cmd_ibss_rsn,
cli_cmd_flag_none,
"<addr> = request RSN authentication with <addr> in IBSS" },
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index d903128..ea9dbf7 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -723,6 +723,20 @@ static void wpas_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
}
+static void wpas_wps_set_sel_reg_cb(void *ctx, int sel_reg, u16 dev_passwd_id,
+ u16 sel_reg_config_methods)
+{
+#ifdef CONFIG_WPS_ER
+ struct wpa_supplicant *wpa_s = ctx;
+
+ if (wpa_s->wps_er == NULL)
+ return;
+ wps_er_set_sel_reg(wpa_s->wps_er, sel_reg, dev_passwd_id,
+ sel_reg_config_methods);
+#endif /* CONFIG_WPS_ER */
+}
+
+
int wpas_wps_init(struct wpa_supplicant *wpa_s)
{
struct wps_context *wps;
@@ -784,6 +798,7 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s)
os_memset(&rcfg, 0, sizeof(rcfg));
rcfg.new_psk_cb = wpas_wps_new_psk_cb;
rcfg.pin_needed_cb = wpas_wps_pin_needed_cb;
+ rcfg.set_sel_reg_cb = wpas_wps_set_sel_reg_cb;
rcfg.cb_ctx = wpa_s;
wps->registrar = wps_registrar_init(wps, &rcfg);
@@ -1053,3 +1068,20 @@ int wpas_wps_er_stop(struct wpa_supplicant *wpa_s)
#endif /* CONFIG_WPS_ER */
return 0;
}
+
+
+#ifdef CONFIG_WPS_ER
+int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const char *uuid,
+ const char *pin)
+{
+ u8 u[UUID_LEN];
+ int any = 0;
+
+ if (os_strcmp(uuid, "any") == 0)
+ any = 1;
+ else if (uuid_str2bin(uuid, u))
+ return -1;
+ return wps_registrar_add_pin(wpa_s->wps->registrar, any ? NULL : u,
+ (const u8 *) pin, os_strlen(pin), 300);
+}
+#endif /* CONFIG_WPS_ER */
diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h
index e75b3be..503a4cd 100644
--- a/wpa_supplicant/wps_supplicant.h
+++ b/wpa_supplicant/wps_supplicant.h
@@ -51,6 +51,8 @@ int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *pos,
char *end);
int wpas_wps_er_start(struct wpa_supplicant *wpa_s);
int wpas_wps_er_stop(struct wpa_supplicant *wpa_s);
+int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const char *uuid,
+ const char *pin);
#else /* CONFIG_WPS */