/*
* Wi-Fi Protected Setup - Registrar
*
* 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 "crypto/sha256.h"
#include "base64.h"
#include "ieee802_11_defs.h"
#include "eloop.h"
#include "wps_i.h"
#include "wps_dev_attr.h"
#include "wps_upnp.h"
#define WPS_WORKAROUNDS
struct wps_uuid_pin {
struct wps_uuid_pin *next;
u8 uuid[WPS_UUID_LEN];
int wildcard_uuid;
u8 *pin;
size_t pin_len;
#define PIN_LOCKED BIT(0)
#define PIN_EXPIRES BIT(1)
int flags;
struct os_time expiration;
};
static void wps_free_pin(struct wps_uuid_pin *pin)
{
os_free(pin->pin);
os_free(pin);
}
static void wps_free_pins(struct wps_uuid_pin *pins)
{
struct wps_uuid_pin *pin, *prev;
pin = pins;
while (pin) {
prev = pin;
pin = pin->next;
wps_free_pin(prev);
}
}
struct wps_pbc_session {
struct wps_pbc_session *next;
u8 addr[ETH_ALEN];
u8 uuid_e[WPS_UUID_LEN];
struct os_time timestamp;
};
static void wps_free_pbc_sessions(struct wps_pbc_session *pbc)
{
struct wps_pbc_session *prev;
while (pbc) {
prev = pbc;
pbc = pbc->next;
os_free(prev);
}
}
struct wps_registrar {
struct wps_context *wps;
int pbc;
int selected_registrar;
int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk,
size_t psk_len);
int (*set_ie_cb)(void *ctx, const u8 *beacon_ie, size_t beacon_ie_len,
const u8 *probe_resp_ie, size_t probe_resp_ie_len);
void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
const struct wps_device_data *dev);
void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
const u8 *uuid_e);
void *cb_ctx;
struct wps_uuid_pin *pins;
struct wps_pbc_session *pbc_sessions;
int skip_cred_build;
struct wpabuf *extra_cred;
int disable_auto_conf;
int sel_reg_dev_password_id_override;
int sel_reg_config_methods_override;
int static_wep_only;
int force_pbc_overlap;
};
static int wps_set_ie(struct wps_registrar *reg);
static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx);
static void wps_registrar_set_selected_timeout(void *eloop_ctx,
void *timeout_ctx);
static void wps_registrar_add_pbc_session(struct wps_registrar *reg,
const u8 *addr, const u8 *uuid_e)
{
struct wps_pbc_session *pbc, *prev = NULL;
struct os_time now;
os_get_time(&now);
pbc = reg->pbc_sessions;
while (pbc) {
if (os_memcmp(pbc->addr, addr, ETH_ALEN) == 0 &&
os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) {
if (prev)
prev->next = pbc->next;
else
reg->pbc_sessions = pbc->next;
break;
}
prev = pbc;
pbc = pbc->next;
}
if (!pbc) {
pbc = os_zalloc(sizeof(*pbc));
if (pbc == NULL)
return;
os_memcpy(pbc->addr, addr, ETH_ALEN);
if (uuid_e)
os_memcpy(pbc->uuid_e, uuid_e, WPS_UUID_LEN);
}
pbc->next = reg->pbc_sessions;
reg->pbc_sessions = pbc;
pbc->timestamp = now;
/* remove entries that have timed out */
prev = pbc;
pbc = pbc->next;
while (pbc) {
if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) {
prev->next = NULL;
wps_free_pbc_sessions(pbc);
break;
}
prev = pbc;
pbc = pbc->next;
}
}
static void wps_registrar_remove_pbc_session(struct wps_registrar *reg,
const u8 *addr, const u8 *uuid_e)
{
struct wps_pbc_session *pbc, *prev = NULL;
pbc = reg->pbc_sessions;
while (pbc) {
if (os_memcmp(pbc->addr, addr, ETH_ALEN) == 0 &&
os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) {
if (prev)
prev->next = pbc->next;
else
reg->pbc_sessions = pbc->next;
os_free(pbc);
break;
}
prev = pbc;
pbc = pbc->next;
}
}
static int wps_registrar_pbc_overlap(struct wps_registrar *reg,
const u8 *addr, const u8 *uuid_e)
{
int count = 0;
struct wps_pbc_session *pbc;
struct os_time now;
os_get_time(&now);
for (pbc = reg->pbc_sessions; pbc; pbc = pbc->next) {
if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME)
break;
if (addr == NULL || os_memcmp(addr, pbc->addr, ETH_ALEN) ||
uuid_e == NULL ||
os_memcmp(uuid_e, pbc->uuid_e, WPS_UUID_LEN))
count++;
}
if (addr || uuid_e)
count++;
return count > 1 ? 1 : 0;
}
static int wps_build_wps_state(struct wps_context *wps, struct wpabuf *msg)
{
wpa_printf(MSG_DEBUG, "WPS: * Wi-Fi Protected Setup State (%d)",
wps->wps_state);
wpabuf_put_be16(msg, ATTR_WPS_STATE);
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, wps->wps_state);
return 0;
}
#ifdef CONFIG_WPS_UPNP
static void wps_registrar_free_pending_m2(struct wps_context *wps)
{
struct upnp_pending_message *p, *p2, *prev = NULL;
p = wps->upnp_msgs;
while (p) {
if (p->type == WPS_M2 || p->type == WPS_M2D) {
if (prev == NULL)
wps->upnp_msgs = p->next;
else
prev->next = p->next;
wpa_printf(MSG_DEBUG, "WPS UPnP: Drop pending M2/M2D");
p2 = p;
p = p->next;
wpabuf_free(p2->msg);
os_free(p2);
continue;
}
prev = p;
p = p->next;
}
}
#endif /* CONFIG_WPS_UPNP */
static int wps_build_ap_setup_locked(struct wps_context *wps,
struct wpabuf *msg)
{
if (wps->ap_setup_locked) {
wpa_printf(MSG_DEBUG, "WPS: * AP Setup Locked");
wpabuf_put_be16(msg, ATTR_AP_SETUP_LOCKED);
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, 1);
}
return 0;
}
static int wps_build_selected_registrar(struct wps_registrar *reg,
struct wpabuf *msg)
{
if (!reg->selected_registrar)
return 0;
wpa_printf(MSG_DEBUG, "WPS: * Selected Registrar");
wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR);
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, 1);
return 0;
}
static int wps_build_sel_reg_dev_password_id(struct wps_registrar *reg,
struct wpabuf *msg)
{
u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT;
if (!reg->selected_registrar)
return 0;
if (reg->sel_reg_dev_password_id_override >= 0)
id = reg->sel_reg_dev_password_id_override;
wpa_printf(MSG_DEBUG, "WPS: * Device Password ID (%d)", id);
wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
wpabuf_put_be16(msg, 2);
wpabuf_put_be16(msg, id);
return 0;
}
static int wps_build_sel_reg_config_methods(struct wps_registrar *reg,
struct wpabuf *msg)
{
u16 methods;
if (!reg->selected_registrar)
return 0;
methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
if (reg->pbc)
methods |= WPS_CONFIG_PUSHBUTTON;
if (reg->sel_reg_config_methods_override >= 0)
methods = reg->sel_reg_config_methods_override;
wpa_printf(MSG_DEBUG, "WPS: * Selected Registrar Config Methods (%x)",
methods);
wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS);
wpabuf_put_be16(msg, 2);
wpabuf_put_be16(msg, methods);
return 0;
}
static int wps_build_probe_config_methods(struct wps_registrar *reg,
struct wpabuf *msg)
{
u16 methods;
methods = 0;
wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods);
wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
wpabuf_put_be16(msg, 2);
wpabuf_put_be16(msg, methods);
return 0;
}
static int wps_build_config_methods_r(struct wps_registrar *reg,
struct wpabuf *msg)
{
u16 methods;
methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
if (reg->pbc)
methods |= WPS_CONFIG_PUSHBUTTON;
return wps_build_config_methods(msg, methods);
}
static int wps_build_resp_type(struct wps_registrar *reg, struct wpabuf *msg)
{
u8 resp = reg->wps->ap ? WPS_RESP_AP : WPS_RESP_REGISTRAR;
wpa_printf(MSG_DEBUG, "WPS: * Response Type (%d)", resp);
wpabuf_put_be16(msg, ATTR_RESPONSE_TYPE);
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, resp);
return 0;
}
/**
* wps_registrar_init - Initialize WPS Registrar data
* @wps: Pointer to longterm WPS context
* @cfg: Registrar configuration
* Returns: Pointer to allocated Registrar data or %NULL on failure
*
* This function is used to initialize W