WPS: Add UFD support (USBA out-of-band mechanism)
authorMasashi Honma <honma@ictec.co.jp>
Thu, 26 Feb 2009 19:57:38 +0000 (21:57 +0200)
committerJouni Malinen <j@w1.fi>
Thu, 26 Feb 2009 19:57:38 +0000 (21:57 +0200)
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

20 files changed:
hostapd/Makefile
hostapd/ctrl_iface.c
hostapd/hostapd_cli.c
hostapd/wps_hostapd.c
hostapd/wps_hostapd.h
src/wps/wps.c
src/wps/wps.h
src/wps/wps_attr_build.c
src/wps/wps_attr_parse.c
src/wps/wps_common.c
src/wps/wps_defs.h
src/wps/wps_enrollee.c
src/wps/wps_i.h
src/wps/wps_registrar.c
src/wps/wps_ufd.c [new file with mode: 0644]
wpa_supplicant/Makefile
wpa_supplicant/ctrl_iface.c
wpa_supplicant/wpa_cli.c
wpa_supplicant/wps_supplicant.c
wpa_supplicant/wps_supplicant.h

index c8f9dfb..c5f12f7 100644 (file)
@@ -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
index 4e31fb3..9e6505a 100644 (file)
@@ -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);
index 2614113..d4d7a3f 100644 (file)
@@ -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 },
index e0e7c0d..c470a36 100644 (file)
@@ -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)
 {
index 6615c62..0d39797 100644 (file)
@@ -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);
 
index 395eba6..d26cb3b 100644 (file)
@@ -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) ||
index e0f2b2d..e255e43 100644 (file)
@@ -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 */
index edeff5c..0466d13 100644 (file)
@@ -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;
+}
index 25ff251..34057c9 100644 (file)
@@ -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 "
index 48af303..3e4ec55 100644 (file)
@@ -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;
+}
index bf6ccc5..ab9536a 100644 (file)
@@ -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 {
index 179f7db..2c6d404 100644 (file)
@@ -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)
index 85adf28..e4c0b35 100644 (file)
@@ -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)
index f7eebdd..fb5579b 100644 (file)
@@ -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 (file)
index 0000000..f76379e
--- /dev/null
@@ -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,
+};
index 47d8489..d7f2182 100644 (file)
@@ -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
index 7bb871a..ae01173 100644 (file)
@@ -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;
index ea9aac6..bae0d6f 100644 (file)
@@ -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" },
index c2c1dd0..88407ea 100644 (file)
@@ -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;
index 8f81dc4..ad55301 100644 (file)
@@ -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,