diff --git a/hostapd/Makefile b/hostapd/Makefile index c8f9dfb37..c5f12f776 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 4e31fb3e3..9e6505a77 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 26141130d..d4d7a3fa9 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 add WPS Enrollee PIN (Device Password)\n" " wps_pbc indicate button pushed to initiate PBC\n" +" wps_oob 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 e0e7c0d89..c470a3644 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 6615c62a1..0d39797ff 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 395eba6af..d26cb3bc8 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 e0f2b2d6e..e255e4382 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 */ @@ -397,6 +408,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 * @@ -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 edeff5c58..0466d13a2 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 25ff25189..34057c922 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 48af3032e..3e4ec558d 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 bf6ccc5f3..ab9536ad9 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 179f7dbf2..2c6d40486 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 85adf28d4..e4c0b359f 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 f7eebddd6..fb5579b82 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 000000000..f76379ec5 --- /dev/null +++ b/src/wps/wps_ufd.c @@ -0,0 +1,193 @@ +/* + * UFD routines for Wi-Fi Protected Setup + * Copyright (c) 2009, Masashi Honma + * + * 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 +#include +#include +#include +#include + +#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 47d8489bf..d7f21820a 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 7bb871a97..ae01173b6 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 ea9aac685..bae0d6f40 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, " [PIN] = start WPS PIN method (returns PIN, if not " "hardcoded)" }, + { "wps_oob", wpa_cli_cmd_wps_oob, + cli_cmd_flag_sensitive, + " = start WPS OOB" }, { "wps_reg", wpa_cli_cmd_wps_reg, cli_cmd_flag_sensitive, " = start WPS Registrar to configure an AP" }, diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index c2c1dd064..88407eaf8 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 8f81dc4bd..ad5530144 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,