aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMasashi Honma <honma@ictec.co.jp>2009-02-26 19:57:38 (GMT)
committerJouni Malinen <j@w1.fi>2009-02-26 19:57:38 (GMT)
commit46bdb83acdb9c2f1a031971e665b75fce6bcbe47 (patch)
treebf3fdd443275779adc6329be9201b111407bf07d
parentb414900a90829d3d3a58a45f29be708a6757b258 (diff)
downloadhostap-46bdb83acdb9c2f1a031971e665b75fce6bcbe47.zip
hostap-46bdb83acdb9c2f1a031971e665b75fce6bcbe47.tar.gz
hostap-46bdb83acdb9c2f1a031971e665b75fce6bcbe47.tar.bz2
WPS: Add UFD support (USBA out-of-band mechanism)
This patch is only for the following use case: - Enrollee = wpa_supplicant - Registrar = hostapd internal Registrar Following UFD methods can be used: - Enrollee PIN with UFD - Registrar PIN with UFD - unencrypted credential with UFD Encrypted credentials are not supported. Enrollee side operation: wpa_cli -i ath0 wps_oob <device type> <mount point> <oob method> oob method = pin-e/pin-r/cred wpa_cli -i ath0 wps_oob ufd /mnt/ pin-r Registrar side operation: ./hostapd_cli -i ath0 wps_oob <device type> <mount point> <oob method> oob method = pin-e/pin-r/cred hostapd_cli -i ath0 wps_oob ufd /mnt/ cred
-rw-r--r--hostapd/Makefile1
-rw-r--r--hostapd/ctrl_iface.c21
-rw-r--r--hostapd/hostapd_cli.c27
-rw-r--r--hostapd/wps_hostapd.c37
-rw-r--r--hostapd/wps_hostapd.h2
-rw-r--r--src/wps/wps.c5
-rw-r--r--src/wps/wps.h47
-rw-r--r--src/wps/wps_attr_build.c52
-rw-r--r--src/wps/wps_attr_parse.c8
-rw-r--r--src/wps/wps_common.c200
-rw-r--r--src/wps/wps_defs.h3
-rw-r--r--src/wps/wps_enrollee.c18
-rw-r--r--src/wps/wps_i.h6
-rw-r--r--src/wps/wps_registrar.c28
-rw-r--r--src/wps/wps_ufd.c193
-rw-r--r--wpa_supplicant/Makefile1
-rw-r--r--wpa_supplicant/ctrl_iface.c22
-rw-r--r--wpa_supplicant/wpa_cli.c27
-rw-r--r--wpa_supplicant/wps_supplicant.c44
-rw-r--r--wpa_supplicant/wps_supplicant.h2
20 files changed, 734 insertions, 10 deletions
diff --git a/hostapd/Makefile b/hostapd/Makefile
index c8f9dfb..c5f12f7 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -313,6 +313,7 @@ OBJS += ../src/wps/wps_attr_process.o
OBJS += ../src/wps/wps_dev_attr.o
OBJS += ../src/wps/wps_enrollee.o
OBJS += ../src/wps/wps_registrar.o
+OBJS += ../src/wps/wps_ufd.o
NEED_DH_GROUPS=y
NEED_SHA256=y
NEED_CRYPTO=y
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index 4e31fb3..9e6505a 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -252,6 +252,24 @@ static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt)
*pin++ = '\0';
return hostapd_wps_add_pin(hapd, txt, pin);
}
+
+
+static int hostapd_ctrl_iface_wps_oob(struct hostapd_data *hapd, char *txt)
+{
+ char *path, *method;
+
+ path = os_strchr(txt, ' ');
+ if (path == NULL)
+ return -1;
+ *path++ = '\0';
+
+ method = os_strchr(path, ' ');
+ if (method == NULL)
+ return -1;
+ *method++ = '\0';
+
+ return hostapd_wps_start_oob(hapd, txt, path, method);
+}
#endif /* CONFIG_WPS */
@@ -350,6 +368,9 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
} else if (os_strcmp(buf, "WPS_PBC") == 0) {
if (hostapd_wps_button_pushed(hapd))
reply_len = -1;
+ } else if (os_strncmp(buf, "WPS_OOB ", 8) == 0) {
+ if (hostapd_ctrl_iface_wps_oob(hapd, buf + 8))
+ reply_len = -1;
#endif /* CONFIG_WPS */
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index 2614113..d4d7a3f 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -89,6 +89,7 @@ static const char *commands_help =
#ifdef CONFIG_WPS
" wps_pin <uuid> <pin> add WPS Enrollee PIN (Device Password)\n"
" wps_pbc indicate button pushed to initiate PBC\n"
+" wps_oob <type> <path> <method> use WPS with out-of-band (UFD)\n"
#endif /* CONFIG_WPS */
" help show this usage help\n"
" interface [ifname] show interfaces/select interface\n"
@@ -275,6 +276,31 @@ static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
{
return wpa_ctrl_command(ctrl, "WPS_PBC");
}
+
+
+static int hostapd_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[256];
+ int res;
+
+ if (argc != 3) {
+ printf("Invalid WPS_OOB command: need three arguments:\n"
+ "- OOB_DEV_TYPE: use 'ufd'\n"
+ "- OOB_PATH: path of OOB device like '/mnt'\n"
+ "- OOB_METHOD: OOB method 'pin-e' or 'pin-r', "
+ "'cred'\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s",
+ argv[0], argv[1], argv[2]);
+ if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+ printf("Too long WPS_OOB command.\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
#endif /* CONFIG_WPS */
@@ -432,6 +458,7 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = {
#ifdef CONFIG_WPS
{ "wps_pin", hostapd_cli_cmd_wps_pin },
{ "wps_pbc", hostapd_cli_cmd_wps_pbc },
+ { "wps_oob", hostapd_cli_cmd_wps_oob },
#endif /* CONFIG_WPS */
{ "help", hostapd_cli_cmd_help },
{ "interface", hostapd_cli_cmd_interface },
diff --git a/hostapd/wps_hostapd.c b/hostapd/wps_hostapd.c
index e0e7c0d..c470a36 100644
--- a/hostapd/wps_hostapd.c
+++ b/hostapd/wps_hostapd.c
@@ -26,6 +26,7 @@
#include "wps/wps_defs.h"
#include "wps/wps_dev_attr.h"
#include "wps_hostapd.h"
+#include "dh_groups.h"
#ifdef CONFIG_WPS_UPNP
@@ -648,6 +649,16 @@ int hostapd_init_wps(struct hostapd_data *hapd,
}
#endif /* CONFIG_WPS_UPNP */
+ wps->dh_pubkey = dh_init(dh_groups_get(WPS_DH_GROUP),
+ &wps->dh_privkey);
+ wps->dh_pubkey = wpabuf_zeropad(wps->dh_pubkey, 192);
+ if (wps->dh_pubkey == NULL) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to initialize "
+ "Diffie-Hellman handshake");
+ os_free(wps);
+ return -1;
+ }
+
hapd->wps = wps;
return 0;
@@ -664,6 +675,8 @@ void hostapd_deinit_wps(struct hostapd_data *hapd)
wps_registrar_deinit(hapd->wps->registrar);
os_free(hapd->wps->network_key);
wps_device_data_free(&hapd->wps->dev);
+ wpabuf_free(hapd->wps->dh_pubkey);
+ wpabuf_free(hapd->wps->dh_privkey);
wps_free_pending_msgs(hapd->wps->upnp_msgs);
os_free(hapd->wps);
hapd->wps = NULL;
@@ -696,6 +709,30 @@ int hostapd_wps_button_pushed(struct hostapd_data *hapd)
}
+int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type,
+ char *path, char *method)
+{
+ struct wps_context *wps = hapd->wps;
+
+ wps->oob_dev = wps_get_oob_device(device_type);
+ if (wps->oob_dev == NULL)
+ return -1;
+ wps->oob_dev->device_path = path;
+ wps->oob_conf.oob_method = wps_get_oob_method(method);
+
+ if (wps_process_oob(wps, 1) < 0)
+ return -1;
+
+ if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ||
+ wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) &&
+ hostapd_wps_add_pin(hapd, "any",
+ wpabuf_head(wps->oob_conf.dev_password)) < 0)
+ return -1;
+
+ return 0;
+}
+
+
void hostapd_wps_probe_req_rx(struct hostapd_data *hapd, const u8 *addr,
const u8 *ie, size_t ie_len)
{
diff --git a/hostapd/wps_hostapd.h b/hostapd/wps_hostapd.h
index 6615c62..0d39797 100644
--- a/hostapd/wps_hostapd.h
+++ b/hostapd/wps_hostapd.h
@@ -23,6 +23,8 @@ 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);
+int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type,
+ char *path, char *method);
void hostapd_wps_probe_req_rx(struct hostapd_data *hapd, const u8 *addr,
const u8 *ie, size_t ie_len);
diff --git a/src/wps/wps.c b/src/wps/wps.c
index 395eba6..d26cb3b 100644
--- a/src/wps/wps.c
+++ b/src/wps/wps.c
@@ -44,7 +44,8 @@ struct wps_data * wps_init(const struct wps_config *cfg)
os_memcpy(data->uuid_e, cfg->wps->uuid, WPS_UUID_LEN);
}
if (cfg->pin) {
- data->dev_pw_id = DEV_PW_DEFAULT;
+ data->dev_pw_id = data->wps->oob_dev_pw_id == 0 ?
+ DEV_PW_DEFAULT : data->wps->oob_dev_pw_id;
data->dev_password = os_malloc(cfg->pin_len);
if (data->dev_password == NULL) {
os_free(data);
@@ -300,7 +301,7 @@ struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev,
methods = WPS_CONFIG_PUSHBUTTON;
else
methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY |
- WPS_CONFIG_KEYPAD;
+ WPS_CONFIG_KEYPAD | WPS_CONFIG_USBA;
if (wps_build_version(ie) ||
wps_build_req_type(ie, req_type) ||
diff --git a/src/wps/wps.h b/src/wps/wps.h
index e0f2b2d..e255e43 100644
--- a/src/wps/wps.h
+++ b/src/wps/wps.h
@@ -88,6 +88,17 @@ struct wps_device_data {
u8 rf_bands;
};
+struct oob_conf_data {
+ enum {
+ OOB_METHOD_UNKNOWN = 0,
+ OOB_METHOD_DEV_PWD_E,
+ OOB_METHOD_DEV_PWD_R,
+ OOB_METHOD_CRED,
+ } oob_method;
+ struct wpabuf *dev_password;
+ struct wpabuf *pubkey_hash;
+};
+
/**
* struct wps_config - WPS configuration for a single registration protocol run
*/
@@ -398,6 +409,31 @@ struct wps_context {
struct wps_device_data dev;
/**
+ * oob_dev - OOB Device data
+ */
+ struct oob_device_data *oob_dev;
+
+ /**
+ * oob_conf - OOB Config data
+ */
+ struct oob_conf_data oob_conf;
+
+ /**
+ * oob_dev_pw_id - OOB Device password id
+ */
+ u16 oob_dev_pw_id;
+
+ /**
+ * dh_privkey - Diffie-Hellman private key
+ */
+ struct wpabuf *dh_privkey;
+
+ /**
+ * dh_pubkey_oob - Diffie-Hellman public key
+ */
+ struct wpabuf *dh_pubkey;
+
+ /**
* config_methods - Enabled configuration methods
*
* Bit field of WPS_CONFIG_*
@@ -494,6 +530,13 @@ struct wps_context {
struct upnp_pending_message *upnp_msgs;
};
+struct oob_device_data {
+ char *device_path;
+ int (*init_func)(struct wps_context *, int);
+ struct wpabuf * (*read_func)(void);
+ int (*write_func)(struct wpabuf *);
+ int (*deinit_func)(void);
+};
struct wps_registrar *
wps_registrar_init(struct wps_context *wps,
@@ -515,4 +558,8 @@ unsigned int wps_pin_valid(unsigned int pin);
unsigned int wps_generate_pin(void);
void wps_free_pending_msgs(struct upnp_pending_message *msgs);
+struct oob_device_data * wps_get_oob_device(char *device_type);
+int wps_get_oob_method(char *method);
+int wps_process_oob(struct wps_context *wps, int registrar);
+
#endif /* WPS_H */
diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c
index edeff5c..0466d13 100644
--- a/src/wps/wps_attr_build.c
+++ b/src/wps/wps_attr_build.c
@@ -15,7 +15,7 @@
#include "includes.h"
#include "common.h"
-#include "dh_groups.h"
+#include "crypto.h"
#include "sha256.h"
#include "aes_wrap.h"
#include "wps_i.h"
@@ -26,11 +26,13 @@ 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);
- pubkey = wpabuf_zeropad(pubkey, 192);
- if (pubkey == NULL) {
+ wpabuf_free(wps->dh_privkey);
+ wps->dh_privkey = wpabuf_dup(wps->wps->dh_privkey);
+ pubkey = wpabuf_dup(wps->wps->dh_pubkey);
+ if (wps->dh_privkey == NULL || pubkey == NULL) {
wpa_printf(MSG_DEBUG, "WPS: Failed to initialize "
"Diffie-Hellman handshake");
+ wpabuf_free(pubkey);
return -1;
}
@@ -252,3 +254,45 @@ int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
return 0;
}
+
+
+int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps)
+{
+ size_t hash_len;
+ const u8 *addr[1];
+ u8 pubkey_hash[WPS_HASH_LEN];
+ u8 dev_password_bin[WPS_OOB_DEVICE_PASSWORD_LEN];
+
+ wpa_printf(MSG_DEBUG, "WPS: * OOB Device Password");
+
+ addr[0] = wpabuf_head(wps->dh_pubkey);
+ hash_len = wpabuf_len(wps->dh_pubkey);
+ sha256_vector(1, addr, &hash_len, pubkey_hash);
+
+ if (os_get_random((u8 *) &wps->oob_dev_pw_id, sizeof(u16)) < 0) {
+ wpa_printf(MSG_ERROR, "WPS: device password id "
+ "generation error");
+ return -1;
+ }
+ wps->oob_dev_pw_id |= 0x0010;
+
+ if (os_get_random(dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN) < 0) {
+ wpa_printf(MSG_ERROR, "WPS: OOB device password "
+ "generation error");
+ return -1;
+ }
+
+ wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD);
+ wpabuf_put_be16(msg, WPS_OOB_DEVICE_PASSWORD_ATTR_LEN);
+ wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+ wpabuf_put_be16(msg, wps->oob_dev_pw_id);
+ wpabuf_put_data(msg, dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN);
+
+ wpa_snprintf_hex_uppercase(
+ wpabuf_put(wps->oob_conf.dev_password,
+ wpabuf_size(wps->oob_conf.dev_password)),
+ wpabuf_size(wps->oob_conf.dev_password),
+ dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN);
+
+ return 0;
+}
diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c
index 25ff251..34057c9 100644
--- a/src/wps/wps_attr_parse.c
+++ b/src/wps/wps_attr_parse.c
@@ -150,6 +150,14 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
}
attr->dev_password_id = pos;
break;
+ case ATTR_OOB_DEVICE_PASSWORD:
+ if (len != WPS_OOB_DEVICE_PASSWORD_ATTR_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
+ "Password length %u", len);
+ return -1;
+ }
+ attr->oob_dev_password = pos;
+ break;
case ATTR_OS_VERSION:
if (len != 4) {
wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "
diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c
index 48af303..3e4ec55 100644
--- a/src/wps/wps_common.c
+++ b/src/wps/wps_common.c
@@ -335,3 +335,203 @@ void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part)
data.pwd_auth_fail.part = part;
wps->event_cb(wps->cb_ctx, WPS_EV_PWD_AUTH_FAIL, &data);
}
+
+
+static struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
+{
+ struct wps_data data;
+ struct wpabuf *plain;
+
+ plain = wpabuf_alloc(500);
+ if (plain == NULL) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+ "credential");
+ return NULL;
+ }
+
+ os_memset(&data, 0, sizeof(data));
+ data.wps = wps;
+ data.auth_type = wps->auth_types;
+ data.encr_type = wps->encr_types;
+ if (wps_build_version(plain) || wps_build_cred(&data, plain)) {
+ wpabuf_free(plain);
+ return NULL;
+ }
+
+ return plain;
+}
+
+
+static struct wpabuf * wps_get_oob_dev_pwd(struct wps_context *wps)
+{
+ struct wpabuf *data;
+
+ data = wpabuf_alloc(9 + WPS_OOB_DEVICE_PASSWORD_ATTR_LEN);
+ if (data == NULL) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+ "device password attribute");
+ return NULL;
+ }
+
+ wpabuf_free(wps->oob_conf.dev_password);
+ wps->oob_conf.dev_password =
+ wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1);
+ if (wps->oob_conf.dev_password == NULL) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+ "device password");
+ wpabuf_free(data);
+ return NULL;
+ }
+
+ if (wps_build_version(data) ||
+ wps_build_oob_dev_password(data, wps)) {
+ wpa_printf(MSG_ERROR, "WPS: Build OOB device password "
+ "attribute error");
+ wpabuf_free(data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static int wps_parse_oob_dev_pwd(struct wps_context *wps,
+ struct wpabuf *data)
+{
+ struct oob_conf_data *oob_conf = &wps->oob_conf;
+ struct wps_parse_attr attr;
+ const u8 *pos;
+
+ if (wps_parse_msg(data, &attr) < 0 ||
+ attr.oob_dev_password == NULL) {
+ wpa_printf(MSG_ERROR, "WPS: OOB device password not found");
+ return -1;
+ }
+
+ pos = attr.oob_dev_password;
+
+ oob_conf->pubkey_hash =
+ wpabuf_alloc_copy(pos, WPS_OOB_PUBKEY_HASH_LEN);
+ if (oob_conf->pubkey_hash == NULL) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+ "public key hash");
+ return -1;
+ }
+ pos += WPS_OOB_PUBKEY_HASH_LEN;
+
+ wps->oob_dev_pw_id = WPA_GET_BE16(pos);
+ pos += sizeof(wps->oob_dev_pw_id);
+
+ oob_conf->dev_password =
+ wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1);
+ if (oob_conf->dev_password == NULL) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+ "device password");
+ return -1;
+ }
+ wpa_snprintf_hex_uppercase(wpabuf_put(oob_conf->dev_password,
+ wpabuf_size(oob_conf->dev_password)),
+ wpabuf_size(oob_conf->dev_password), pos,
+ WPS_OOB_DEVICE_PASSWORD_LEN);
+
+ return 0;
+}
+
+
+static int wps_parse_oob_cred(struct wps_context *wps, struct wpabuf *data)
+{
+ struct wpabuf msg;
+ struct wps_parse_attr attr;
+ size_t i;
+
+ if (wps_parse_msg(data, &attr) < 0 || attr.num_cred <= 0) {
+ wpa_printf(MSG_ERROR, "WPS: OOB credential not found");
+ return -1;
+ }
+
+ for (i = 0; i < attr.num_cred; i++) {
+ struct wps_credential local_cred;
+ struct wps_parse_attr cattr;
+
+ os_memset(&local_cred, 0, sizeof(local_cred));
+ wpabuf_set(&msg, attr.cred[i], attr.cred_len[i]);
+ if (wps_parse_msg(&msg, &cattr) < 0 ||
+ wps_process_cred(&cattr, &local_cred)) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to parse OOB "
+ "credential");
+ return -1;
+ }
+ wps->cred_cb(wps->cb_ctx, &local_cred);
+ }
+
+ return 0;
+}
+
+
+int wps_process_oob(struct wps_context *wps, int registrar)
+{
+ struct oob_device_data *oob_dev = wps->oob_dev;
+ struct wpabuf *data;
+ int ret, write_f, oob_method = wps->oob_conf.oob_method;
+
+ write_f = oob_method == OOB_METHOD_DEV_PWD_E ? !registrar : registrar;
+
+ if (oob_dev->init_func(wps, registrar) < 0) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to initialize OOB device");
+ return -1;
+ }
+
+ if (write_f) {
+ if (oob_method == OOB_METHOD_CRED)
+ data = wps_get_oob_cred(wps);
+ else
+ data = wps_get_oob_dev_pwd(wps);
+
+ ret = 0;
+ if (data == NULL || wps->oob_dev->write_func(data) < 0)
+ ret = -1;
+ } else {
+ data = oob_dev->read_func();
+ if (data == NULL)
+ return -1;
+
+ if (oob_method == OOB_METHOD_CRED)
+ ret = wps_parse_oob_cred(wps, data);
+ else
+ ret = wps_parse_oob_dev_pwd(wps, data);
+ }
+ wpabuf_free(data);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to process OOB data");
+ return -1;
+ }
+
+ if (oob_dev->deinit_func() < 0) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to deinitialize OOB "
+ "device");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+struct oob_device_data * wps_get_oob_device(char *device_type)
+{
+ if (os_strstr(device_type, "ufd") != NULL)
+ return &oob_ufd_device_data;
+
+ return NULL;
+}
+
+
+int wps_get_oob_method(char *method)
+{
+ if (os_strstr(method, "pin-e") != NULL)
+ return OOB_METHOD_DEV_PWD_E;
+ if (os_strstr(method, "pin-r") != NULL)
+ return OOB_METHOD_DEV_PWD_R;
+ if (os_strstr(method, "cred") != NULL)
+ return OOB_METHOD_CRED;
+ return OOB_METHOD_UNKNOWN;
+}
diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h
index bf6ccc5..ab9536a 100644
--- a/src/wps/wps_defs.h
+++ b/src/wps/wps_defs.h
@@ -33,6 +33,9 @@
#define WPS_MGMTAUTHKEY_LEN 32
#define WPS_MGMTENCKEY_LEN 16
#define WPS_MGMT_KEY_ID_LEN 16
+#define WPS_OOB_DEVICE_PASSWORD_ATTR_LEN 54
+#define WPS_OOB_DEVICE_PASSWORD_LEN 32
+#define WPS_OOB_PUBKEY_HASH_LEN 20
/* Attribute Types */
enum wps_attribute {
diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c
index 179f7db..2c6d404 100644
--- a/src/wps/wps_enrollee.c
+++ b/src/wps/wps_enrollee.c
@@ -18,6 +18,7 @@
#include "sha256.h"
#include "wps_i.h"
#include "wps_dev_attr.h"
+#include "crypto.h"
static int wps_build_mac_addr(struct wps_data *wps, struct wpabuf *msg)
@@ -130,7 +131,8 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps)
if (msg == NULL)
return NULL;
- methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+ methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD |
+ WPS_CONFIG_USBA;
if (wps->pbc)
methods |= WPS_CONFIG_PUSHBUTTON;
@@ -513,6 +515,20 @@ static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
return -1;
}
+ if (wps->wps->oob_conf.pubkey_hash != NULL) {
+ const u8 *addr[1];
+ u8 hash[WPS_HASH_LEN];
+
+ addr[0] = pk;
+ sha256_vector(1, addr, &pk_len, hash);
+ if (os_memcmp(hash,
+ wpabuf_head(wps->wps->oob_conf.pubkey_hash),
+ WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+ wpa_printf(MSG_ERROR, "WPS: Public Key hash error");
+ return -1;
+ }
+ }
+
wpabuf_free(wps->dh_pubkey_r);
wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
if (wps->dh_pubkey_r == NULL)
diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h
index 85adf28..e4c0b35 100644
--- a/src/wps/wps_i.h
+++ b/src/wps/wps_i.h
@@ -124,6 +124,8 @@ struct wps_parse_attr {
const u8 *assoc_state; /* 2 octets */
const u8 *config_error; /* 2 octets */
const u8 *dev_password_id; /* 2 octets */
+ const u8 *oob_dev_password; /* WPS_OOB_DEVICE_PASSWORD_ATTR_LEN (54)
+ * octets */
const u8 *os_version; /* 4 octets */
const u8 *wps_state; /* 1 octet */
const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */
@@ -191,6 +193,8 @@ void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg);
void wps_success_event(struct wps_context *wps);
void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part);
+extern struct oob_device_data oob_ufd_device_data;
+
/* wps_attr_parse.c */
int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
@@ -213,6 +217,7 @@ 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);
+int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps);
/* wps_attr_process.c */
int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
@@ -237,6 +242,7 @@ struct wpabuf * wps_registrar_get_msg(struct wps_data *wps,
enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
enum wsc_op_code op_code,
const struct wpabuf *msg);
+int wps_build_cred(struct wps_data *wps, struct wpabuf *msg);
static inline int wps_version_supported(const u8 *version)
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index f7eebdd..fb5579b 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -22,6 +22,7 @@
#include "wps_i.h"
#include "wps_dev_attr.h"
#include "wps_upnp.h"
+#include "crypto.h"
struct wps_uuid_pin {
@@ -981,7 +982,7 @@ static int wps_build_credential(struct wpabuf *msg,
}
-static int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
+int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
{
struct wpabuf *cred;
@@ -1626,6 +1627,20 @@ static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
return -1;
}
+ if (wps->wps->oob_conf.pubkey_hash != NULL) {
+ const u8 *addr[1];
+ u8 hash[WPS_HASH_LEN];
+
+ addr[0] = pk;
+ sha256_vector(1, addr, &pk_len, hash);
+ if (os_memcmp(hash,
+ wpabuf_head(wps->wps->oob_conf.pubkey_hash),
+ WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+ wpa_printf(MSG_ERROR, "WPS: Public Key hash error");
+ return -1;
+ }
+ }
+
wpabuf_free(wps->dh_pubkey_e);
wps->dh_pubkey_e = wpabuf_alloc_copy(pk, pk_len);
if (wps->dh_pubkey_e == NULL)
@@ -1793,7 +1808,8 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps,
wps_process_os_version(&wps->peer_dev, attr->os_version))
return WPS_FAILURE;
- if (wps->dev_pw_id != DEV_PW_DEFAULT &&
+ if (wps->dev_pw_id < 0x10 &&
+ wps->dev_pw_id != DEV_PW_DEFAULT &&
wps->dev_pw_id != DEV_PW_USER_SPECIFIED &&
wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED &&
wps->dev_pw_id != DEV_PW_REGISTRAR_SPECIFIED &&
@@ -1805,6 +1821,14 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps,
return WPS_CONTINUE;
}
+ if (wps->dev_pw_id >= 0x10 &&
+ wps->dev_pw_id != wps->wps->oob_dev_pw_id) {
+ wpa_printf(MSG_DEBUG, "WPS: OOB Device Password ID "
+ "%d mismatch", wps->dev_pw_id);
+ wps->state = SEND_M2D;
+ return WPS_CONTINUE;
+ }
+
if (wps->dev_pw_id == DEV_PW_PUSHBUTTON) {
if (wps_registrar_pbc_overlap(wps->wps->registrar,
wps->mac_addr_e, wps->uuid_e)) {
diff --git a/src/wps/wps_ufd.c b/src/wps/wps_ufd.c
new file mode 100644
index 0000000..f76379e
--- /dev/null
+++ b/src/wps/wps_ufd.c
@@ -0,0 +1,193 @@
+/*
+ * UFD routines for Wi-Fi Protected Setup
+ * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
+ *
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#include "wps/wps.h"
+
+static int ufd_fd = -1;
+
+
+static int dev_pwd_e_file_filter(const struct dirent *entry)
+{
+ unsigned int prefix;
+ char ext[5];
+
+ if (sscanf(entry->d_name, "%8x.%4s", &prefix, ext) != 2)
+ return 0;
+ if (prefix == 0)
+ return 0;
+ if (os_strcasecmp(ext, "WFA") != 0)
+ return 0;
+
+ return 1;
+}
+
+
+static int wps_get_dev_pwd_e_file_name(char *path, char *file_name)
+{
+ struct dirent **namelist;
+ int i, file_num;
+
+ file_num = scandir(path, &namelist, &dev_pwd_e_file_filter,
+ alphasort);
+ if (file_num <= 0) {
+ wpa_printf(MSG_ERROR, "WPS: OOB file not found");
+ return -1;
+ }
+ os_strlcpy(file_name, namelist[0]->d_name, 13);
+ for (i = 0; i < file_num; i++)
+ os_free(namelist[i]);
+ os_free(namelist);
+ return 0;
+}
+
+
+static int get_file_name(struct wps_context *wps, int registrar,
+ char *file_name)
+{
+ switch (wps->oob_conf.oob_method) {
+ case OOB_METHOD_CRED:
+ os_snprintf(file_name, 13, "00000000.WSC");
+ break;
+ case OOB_METHOD_DEV_PWD_E:
+ if (registrar) {
+ char temp[128];
+
+ os_snprintf(temp, sizeof(temp), "%s/SMRTNTKY/WFAWSC",
+ wps->oob_dev->device_path);
+ if (wps_get_dev_pwd_e_file_name(temp, file_name) < 0)
+ return -1;
+ } else {
+ u8 *mac_addr = wps->dev.mac_addr;
+
+ os_snprintf(file_name, 13, "%02X%02X%02X%02X.WFA",
+ mac_addr[2], mac_addr[3], mac_addr[4],
+ mac_addr[5]);
+ }
+ break;
+ case OOB_METHOD_DEV_PWD_R:
+ os_snprintf(file_name, 13, "00000000.WFA");
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "WPS: Invalid USBA OOB method");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int init_ufd(struct wps_context *wps, int registrar)
+{
+ int write_f;
+ char temp[128];
+ char *path = wps->oob_dev->device_path;
+ char filename[13];
+
+ write_f = wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ?
+ !registrar : registrar;
+
+ if (get_file_name(wps, registrar, filename) < 0) {
+ wpa_printf(MSG_ERROR, "WPS (UFD): Failed to get file name");
+ return -1;
+ }
+
+ if (write_f) {
+ os_snprintf(temp, sizeof(temp),
+ "mkdir -p %s/SMRTNTKY/WFAWSC", path);
+ if (system(temp) != 0) {
+ wpa_printf(MSG_ERROR, "WPS (UFD): Failed "
+ "to mkdir");
+ return -1;
+ }
+ }
+
+ os_snprintf(temp, sizeof(temp), "%s/SMRTNTKY/WFAWSC/%s", path,
+ filename);
+ if (write_f)
+ ufd_fd = open(temp, O_WRONLY | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IWUSR);
+ else
+ ufd_fd = open(temp, O_RDONLY);
+ if (ufd_fd < 0) {
+ wpa_printf(MSG_ERROR, "WPS (UFD): Failed to open %s: %s",
+ temp, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * read_ufd(void)
+{
+ struct wpabuf *buf;
+ struct stat s;
+ size_t file_size;
+
+ if (fstat(ufd_fd, &s) < 0) {
+ wpa_printf(MSG_ERROR, "WPS (UFD): Failed to get file size");
+ return NULL;
+ }
+
+ file_size = s.st_size;
+ buf = wpabuf_alloc(file_size);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "WPS (UFD): Failed to alloc read "
+ "buffer");
+ return NULL;
+ }
+
+ if (read(ufd_fd, wpabuf_mhead(buf), file_size) != (int) file_size) {
+ wpabuf_free(buf);
+ wpa_printf(MSG_ERROR, "WPS (UFD): Failed to read");
+ return NULL;
+ }
+ wpabuf_put(buf, file_size);
+ return buf;
+}
+
+
+static int write_ufd(struct wpabuf *buf)
+{
+ if (write(ufd_fd, wpabuf_mhead(buf), wpabuf_len(buf)) !=
+ (int) wpabuf_len(buf)) {
+ wpa_printf(MSG_ERROR, "WPS (UFD): Failed to write");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int deinit_ufd(void)
+{
+ close(ufd_fd);
+ ufd_fd = -1;
+ return 0;
+}
+
+
+struct oob_device_data oob_ufd_device_data = {
+ .device_path = NULL,
+ .init_func = init_ufd,
+ .read_func = read_ufd,
+ .write_func = write_ufd,
+ .deinit_func = deinit_ufd,
+};
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 47d8489..d7f2182 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -521,6 +521,7 @@ OBJS += ../src/wps/wps_attr_process.o
OBJS += ../src/wps/wps_dev_attr.o
OBJS += ../src/wps/wps_enrollee.o
OBJS += ../src/wps/wps_registrar.o
+OBJS += ../src/wps/wps_ufd.o
OBJS_h += ../src/eap_server/eap_wsc.o
CONFIG_IEEE8021X_EAPOL=y
NEED_DH_GROUPS=y
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 7bb871a..ae01173 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -204,6 +204,25 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s,
}
+static int wpa_supplicant_ctrl_iface_wps_oob(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ char *path, *method;
+
+ path = os_strchr(cmd, ' ');
+ if (path == NULL)
+ return -1;
+ *path++ = '\0';
+
+ method = os_strchr(path, ' ');
+ if (method == NULL)
+ return -1;
+ *method++ = '\0';
+
+ return wpas_wps_start_oob(wpa_s, cmd, path, method);
+}
+
+
static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
char *cmd)
{
@@ -1583,6 +1602,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
reply_len = wpa_supplicant_ctrl_iface_wps_pin(wpa_s, buf + 8,
reply,
reply_size);
+ } else if (os_strncmp(buf, "WPS_OOB ", 8) == 0) {
+ if (wpa_supplicant_ctrl_iface_wps_oob(wpa_s, buf + 8))
+ reply_len = -1;
} else if (os_strncmp(buf, "WPS_REG ", 8) == 0) {
if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8))
reply_len = -1;
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index ea9aac6..bae0d6f 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -446,6 +446,30 @@ static int wpa_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[])
}
+static int wpa_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char cmd[256];
+ int res;
+
+ if (argc != 3) {
+ printf("Invalid WPS_OOB command: need three arguments:\n"
+ "- OOB_DEV_TYPE: use 'ufd'\n"
+ "- OOB_PATH: path of OOB device like '/mnt'\n"
+ "- OOB_METHOD: OOB method 'pin-e' or 'pin-r', "
+ "'cred'\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s",
+ argv[0], argv[1], argv[2]);
+ if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+ printf("Too long WPS_OOB command.\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char cmd[256];
@@ -1258,6 +1282,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
cli_cmd_flag_sensitive,
"<BSSID> [PIN] = start WPS PIN method (returns PIN, if not "
"hardcoded)" },
+ { "wps_oob", wpa_cli_cmd_wps_oob,
+ cli_cmd_flag_sensitive,
+ "<OOB_DEV_TYPE> <OOB_PATH> <OOB_METHOD> = start WPS OOB" },
{ "wps_reg", wpa_cli_cmd_wps_reg,
cli_cmd_flag_sensitive,
"<BSSID> <AP PIN> = start WPS Registrar to configure an AP" },
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index c2c1dd0..88407ea 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -27,6 +27,7 @@
#include "eap_common/eap_wsc_common.h"
#include "blacklist.h"
#include "wps_supplicant.h"
+#include "dh_groups.h"
#define WPS_PIN_SCAN_IGNORE_SEL_REG 3
@@ -440,7 +441,7 @@ int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
const char *pin)
{
struct wpa_ssid *ssid;
- char val[30];
+ char val[128];
unsigned int rpin = 0;
wpas_clear_wps(wpa_s);
@@ -461,6 +462,33 @@ 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)
+{
+ struct wps_context *wps = wpa_s->wps;
+
+ wps->oob_dev = wps_get_oob_device(device_type);
+ if (wps->oob_dev == NULL)
+ return -1;
+ wps->oob_dev->device_path = path;
+ wps->oob_conf.oob_method = wps_get_oob_method(method);
+
+ if (wps->oob_conf.oob_method == OOB_METHOD_CRED)
+ wpas_clear_wps(wpa_s);
+
+ if (wps_process_oob(wps, 0) < 0)
+ return -1;
+
+ if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ||
+ wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) &&
+ wpas_wps_start_pin(wpa_s, NULL,
+ wpabuf_head(wps->oob_conf.dev_password)) < 0)
+ return -1;
+
+ return 0;
+}
+
+
int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
const char *pin)
{
@@ -584,6 +612,16 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s)
return -1;
}
+ wps->dh_pubkey = dh_init(dh_groups_get(WPS_DH_GROUP),
+ &wps->dh_privkey);
+ wps->dh_pubkey = wpabuf_zeropad(wps->dh_pubkey, 192);
+ if (wps->dh_pubkey == NULL) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to initialize "
+ "Diffie-Hellman handshake");
+ os_free(wps);
+ return -1;
+ }
+
wpa_s->wps = wps;
return 0;
@@ -598,6 +636,10 @@ void wpas_wps_deinit(struct wpa_supplicant *wpa_s)
return;
wps_registrar_deinit(wpa_s->wps->registrar);
+ wpabuf_free(wpa_s->wps->dh_pubkey);
+ wpabuf_free(wpa_s->wps->dh_privkey);
+ wpabuf_free(wpa_s->wps->oob_conf.pubkey_hash);
+ wpabuf_free(wpa_s->wps->oob_conf.dev_password);
os_free(wpa_s->wps->network_key);
os_free(wpa_s->wps);
wpa_s->wps = NULL;
diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h
index 8f81dc4..ad55301 100644
--- a/wpa_supplicant/wps_supplicant.h
+++ b/wpa_supplicant/wps_supplicant.h
@@ -27,6 +27,8 @@ enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid);
int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid);
int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
const char *pin);
+int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type,
+ char *path, char *method);
int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
const char *pin);
int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,