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
This commit is contained in:
Masashi Honma 2009-02-26 21:57:38 +02:00 committed by Jouni Malinen
parent b414900a90
commit 46bdb83acd
20 changed files with 734 additions and 10 deletions

View file

@ -313,6 +313,7 @@ OBJS += ../src/wps/wps_attr_process.o
OBJS += ../src/wps/wps_dev_attr.o OBJS += ../src/wps/wps_dev_attr.o
OBJS += ../src/wps/wps_enrollee.o OBJS += ../src/wps/wps_enrollee.o
OBJS += ../src/wps/wps_registrar.o OBJS += ../src/wps/wps_registrar.o
OBJS += ../src/wps/wps_ufd.o
NEED_DH_GROUPS=y NEED_DH_GROUPS=y
NEED_SHA256=y NEED_SHA256=y
NEED_CRYPTO=y NEED_CRYPTO=y

View file

@ -252,6 +252,24 @@ static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt)
*pin++ = '\0'; *pin++ = '\0';
return hostapd_wps_add_pin(hapd, txt, pin); 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 */ #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) { } else if (os_strcmp(buf, "WPS_PBC") == 0) {
if (hostapd_wps_button_pushed(hapd)) if (hostapd_wps_button_pushed(hapd))
reply_len = -1; 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 */ #endif /* CONFIG_WPS */
} else { } else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16); os_memcpy(reply, "UNKNOWN COMMAND\n", 16);

View file

@ -89,6 +89,7 @@ static const char *commands_help =
#ifdef CONFIG_WPS #ifdef CONFIG_WPS
" wps_pin <uuid> <pin> add WPS Enrollee PIN (Device Password)\n" " wps_pin <uuid> <pin> add WPS Enrollee PIN (Device Password)\n"
" wps_pbc indicate button pushed to initiate PBC\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 */ #endif /* CONFIG_WPS */
" help show this usage help\n" " help show this usage help\n"
" interface [ifname] show interfaces/select interface\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"); 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 */ #endif /* CONFIG_WPS */
@ -432,6 +458,7 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = {
#ifdef CONFIG_WPS #ifdef CONFIG_WPS
{ "wps_pin", hostapd_cli_cmd_wps_pin }, { "wps_pin", hostapd_cli_cmd_wps_pin },
{ "wps_pbc", hostapd_cli_cmd_wps_pbc }, { "wps_pbc", hostapd_cli_cmd_wps_pbc },
{ "wps_oob", hostapd_cli_cmd_wps_oob },
#endif /* CONFIG_WPS */ #endif /* CONFIG_WPS */
{ "help", hostapd_cli_cmd_help }, { "help", hostapd_cli_cmd_help },
{ "interface", hostapd_cli_cmd_interface }, { "interface", hostapd_cli_cmd_interface },

View file

@ -26,6 +26,7 @@
#include "wps/wps_defs.h" #include "wps/wps_defs.h"
#include "wps/wps_dev_attr.h" #include "wps/wps_dev_attr.h"
#include "wps_hostapd.h" #include "wps_hostapd.h"
#include "dh_groups.h"
#ifdef CONFIG_WPS_UPNP #ifdef CONFIG_WPS_UPNP
@ -648,6 +649,16 @@ int hostapd_init_wps(struct hostapd_data *hapd,
} }
#endif /* CONFIG_WPS_UPNP */ #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; hapd->wps = wps;
return 0; return 0;
@ -664,6 +675,8 @@ void hostapd_deinit_wps(struct hostapd_data *hapd)
wps_registrar_deinit(hapd->wps->registrar); wps_registrar_deinit(hapd->wps->registrar);
os_free(hapd->wps->network_key); os_free(hapd->wps->network_key);
wps_device_data_free(&hapd->wps->dev); 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); wps_free_pending_msgs(hapd->wps->upnp_msgs);
os_free(hapd->wps); os_free(hapd->wps);
hapd->wps = NULL; 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, void hostapd_wps_probe_req_rx(struct hostapd_data *hapd, const u8 *addr,
const u8 *ie, size_t ie_len) const u8 *ie, size_t ie_len)
{ {

View 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, int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid,
const char *pin); const char *pin);
int hostapd_wps_button_pushed(struct hostapd_data *hapd); 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, void hostapd_wps_probe_req_rx(struct hostapd_data *hapd, const u8 *addr,
const u8 *ie, size_t ie_len); const u8 *ie, size_t ie_len);

View 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); os_memcpy(data->uuid_e, cfg->wps->uuid, WPS_UUID_LEN);
} }
if (cfg->pin) { 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); data->dev_password = os_malloc(cfg->pin_len);
if (data->dev_password == NULL) { if (data->dev_password == NULL) {
os_free(data); 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; methods = WPS_CONFIG_PUSHBUTTON;
else else
methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY |
WPS_CONFIG_KEYPAD; WPS_CONFIG_KEYPAD | WPS_CONFIG_USBA;
if (wps_build_version(ie) || if (wps_build_version(ie) ||
wps_build_req_type(ie, req_type) || wps_build_req_type(ie, req_type) ||

View file

@ -88,6 +88,17 @@ struct wps_device_data {
u8 rf_bands; 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 * struct wps_config - WPS configuration for a single registration protocol run
*/ */
@ -397,6 +408,31 @@ struct wps_context {
*/ */
struct wps_device_data dev; 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 * config_methods - Enabled configuration methods
* *
@ -494,6 +530,13 @@ struct wps_context {
struct upnp_pending_message *upnp_msgs; 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 * struct wps_registrar *
wps_registrar_init(struct wps_context *wps, 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); unsigned int wps_generate_pin(void);
void wps_free_pending_msgs(struct upnp_pending_message *msgs); 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 */ #endif /* WPS_H */

View file

@ -15,7 +15,7 @@
#include "includes.h" #include "includes.h"
#include "common.h" #include "common.h"
#include "dh_groups.h" #include "crypto.h"
#include "sha256.h" #include "sha256.h"
#include "aes_wrap.h" #include "aes_wrap.h"
#include "wps_i.h" #include "wps_i.h"
@ -26,11 +26,13 @@ int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg)
struct wpabuf *pubkey; struct wpabuf *pubkey;
wpa_printf(MSG_DEBUG, "WPS: * Public Key"); wpa_printf(MSG_DEBUG, "WPS: * Public Key");
pubkey = dh_init(dh_groups_get(WPS_DH_GROUP), &wps->dh_privkey); wpabuf_free(wps->dh_privkey);
pubkey = wpabuf_zeropad(pubkey, 192); wps->dh_privkey = wpabuf_dup(wps->wps->dh_privkey);
if (pubkey == NULL) { pubkey = wpabuf_dup(wps->wps->dh_pubkey);
if (wps->dh_privkey == NULL || pubkey == NULL) {
wpa_printf(MSG_DEBUG, "WPS: Failed to initialize " wpa_printf(MSG_DEBUG, "WPS: Failed to initialize "
"Diffie-Hellman handshake"); "Diffie-Hellman handshake");
wpabuf_free(pubkey);
return -1; return -1;
} }
@ -252,3 +254,45 @@ int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
return 0; 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;
}

View file

@ -150,6 +150,14 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
} }
attr->dev_password_id = pos; attr->dev_password_id = pos;
break; 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: case ATTR_OS_VERSION:
if (len != 4) { if (len != 4) {
wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length " wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "

View 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; data.pwd_auth_fail.part = part;
wps->event_cb(wps->cb_ctx, WPS_EV_PWD_AUTH_FAIL, &data); 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;
}

View file

@ -33,6 +33,9 @@
#define WPS_MGMTAUTHKEY_LEN 32 #define WPS_MGMTAUTHKEY_LEN 32
#define WPS_MGMTENCKEY_LEN 16 #define WPS_MGMTENCKEY_LEN 16
#define WPS_MGMT_KEY_ID_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 */ /* Attribute Types */
enum wps_attribute { enum wps_attribute {

View file

@ -18,6 +18,7 @@
#include "sha256.h" #include "sha256.h"
#include "wps_i.h" #include "wps_i.h"
#include "wps_dev_attr.h" #include "wps_dev_attr.h"
#include "crypto.h"
static int wps_build_mac_addr(struct wps_data *wps, struct wpabuf *msg) 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) if (msg == NULL)
return 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) if (wps->pbc)
methods |= WPS_CONFIG_PUSHBUTTON; methods |= WPS_CONFIG_PUSHBUTTON;
@ -513,6 +515,20 @@ static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
return -1; 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); wpabuf_free(wps->dh_pubkey_r);
wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len); wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
if (wps->dh_pubkey_r == NULL) if (wps->dh_pubkey_r == NULL)

View file

@ -124,6 +124,8 @@ struct wps_parse_attr {
const u8 *assoc_state; /* 2 octets */ const u8 *assoc_state; /* 2 octets */
const u8 *config_error; /* 2 octets */ const u8 *config_error; /* 2 octets */
const u8 *dev_password_id; /* 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 *os_version; /* 4 octets */
const u8 *wps_state; /* 1 octet */ const u8 *wps_state; /* 1 octet */
const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */ 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_success_event(struct wps_context *wps);
void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part); 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 */ /* wps_attr_parse.c */
int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr); 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_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_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_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 */ /* wps_attr_process.c */
int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator, 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 wps_process_res wps_registrar_process_msg(struct wps_data *wps,
enum wsc_op_code op_code, enum wsc_op_code op_code,
const struct wpabuf *msg); const struct wpabuf *msg);
int wps_build_cred(struct wps_data *wps, struct wpabuf *msg);
static inline int wps_version_supported(const u8 *version) static inline int wps_version_supported(const u8 *version)

View file

@ -22,6 +22,7 @@
#include "wps_i.h" #include "wps_i.h"
#include "wps_dev_attr.h" #include "wps_dev_attr.h"
#include "wps_upnp.h" #include "wps_upnp.h"
#include "crypto.h"
struct wps_uuid_pin { 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; struct wpabuf *cred;
@ -1626,6 +1627,20 @@ static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
return -1; 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); wpabuf_free(wps->dh_pubkey_e);
wps->dh_pubkey_e = wpabuf_alloc_copy(pk, pk_len); wps->dh_pubkey_e = wpabuf_alloc_copy(pk, pk_len);
if (wps->dh_pubkey_e == NULL) 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)) wps_process_os_version(&wps->peer_dev, attr->os_version))
return WPS_FAILURE; 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_USER_SPECIFIED &&
wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED && wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED &&
wps->dev_pw_id != DEV_PW_REGISTRAR_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; 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->dev_pw_id == DEV_PW_PUSHBUTTON) {
if (wps_registrar_pbc_overlap(wps->wps->registrar, if (wps_registrar_pbc_overlap(wps->wps->registrar,
wps->mac_addr_e, wps->uuid_e)) { wps->mac_addr_e, wps->uuid_e)) {

193
src/wps/wps_ufd.c Normal file
View file

@ -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,
};

View file

@ -521,6 +521,7 @@ OBJS += ../src/wps/wps_attr_process.o
OBJS += ../src/wps/wps_dev_attr.o OBJS += ../src/wps/wps_dev_attr.o
OBJS += ../src/wps/wps_enrollee.o OBJS += ../src/wps/wps_enrollee.o
OBJS += ../src/wps/wps_registrar.o OBJS += ../src/wps/wps_registrar.o
OBJS += ../src/wps/wps_ufd.o
OBJS_h += ../src/eap_server/eap_wsc.o OBJS_h += ../src/eap_server/eap_wsc.o
CONFIG_IEEE8021X_EAPOL=y CONFIG_IEEE8021X_EAPOL=y
NEED_DH_GROUPS=y NEED_DH_GROUPS=y

View 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, static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
char *cmd) 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_len = wpa_supplicant_ctrl_iface_wps_pin(wpa_s, buf + 8,
reply, reply,
reply_size); 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) { } else if (os_strncmp(buf, "WPS_REG ", 8) == 0) {
if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8)) if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8))
reply_len = -1; reply_len = -1;

View 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[]) static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[])
{ {
char cmd[256]; char cmd[256];
@ -1258,6 +1282,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
cli_cmd_flag_sensitive, cli_cmd_flag_sensitive,
"<BSSID> [PIN] = start WPS PIN method (returns PIN, if not " "<BSSID> [PIN] = start WPS PIN method (returns PIN, if not "
"hardcoded)" }, "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, { "wps_reg", wpa_cli_cmd_wps_reg,
cli_cmd_flag_sensitive, cli_cmd_flag_sensitive,
"<BSSID> <AP PIN> = start WPS Registrar to configure an AP" }, "<BSSID> <AP PIN> = start WPS Registrar to configure an AP" },

View file

@ -27,6 +27,7 @@
#include "eap_common/eap_wsc_common.h" #include "eap_common/eap_wsc_common.h"
#include "blacklist.h" #include "blacklist.h"
#include "wps_supplicant.h" #include "wps_supplicant.h"
#include "dh_groups.h"
#define WPS_PIN_SCAN_IGNORE_SEL_REG 3 #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) const char *pin)
{ {
struct wpa_ssid *ssid; struct wpa_ssid *ssid;
char val[30]; char val[128];
unsigned int rpin = 0; unsigned int rpin = 0;
wpas_clear_wps(wpa_s); 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, int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
const char *pin) const char *pin)
{ {
@ -584,6 +612,16 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s)
return -1; 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; wpa_s->wps = wps;
return 0; return 0;
@ -598,6 +636,10 @@ void wpas_wps_deinit(struct wpa_supplicant *wpa_s)
return; return;
wps_registrar_deinit(wpa_s->wps->registrar); 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->network_key);
os_free(wpa_s->wps); os_free(wpa_s->wps);
wpa_s->wps = NULL; wpa_s->wps = NULL;

View 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_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid);
int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
const char *pin); 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, int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
const char *pin); const char *pin);
int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,