wpa_supplicant: Add WPS registrar support for softAP mode

This commit is contained in:
Kapil Gupta
2022-05-13 13:32:24 +05:30
parent df1a15e918
commit a6811adac7
37 changed files with 1812 additions and 1740 deletions

View File

@@ -12,7 +12,7 @@
#include "common/defs.h"
#include "common/wpa_common.h"
#define MAX_STA_COUNT 4
#define MAX_STA_COUNT 10
#define MAX_VLAN_ID 4094
typedef u8 macaddr[ETH_ALEN];
@@ -105,7 +105,6 @@ struct hostapd_wpa_psk {
u8 addr[ETH_ALEN];
};
#if 0
struct hostapd_eap_user {
struct hostapd_eap_user *next;
u8 *identity;
@@ -124,6 +123,7 @@ struct hostapd_eap_user {
int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
};
#if 0
struct hostapd_radius_attr {
u8 type;
struct wpabuf *val;
@@ -247,7 +247,11 @@ struct hostapd_bss_config {
*/
u16 max_listen_interval;
int wps_state;
#ifdef CONFIG_WPS
#define WPS_DEV_TYPE_LEN 8
/* maximum number of advertised WPS vendor extension attributes */
#define MAX_WPS_VENDOR_EXTENSIONS 10
int ap_setup_locked;
u8 uuid[16];
char *wps_pin_requests;

View File

@@ -233,10 +233,9 @@ hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
size_t identity_len, int phase2)
{
const struct hostapd_bss_config *conf = hapd->conf;
struct hostapd_eap_user *user = conf->eap_user;
#ifdef CONFIG_WPS
if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN &&
if (identity_len == WSC_ID_ENROLLEE_LEN &&
os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) {
static struct hostapd_eap_user wsc_enrollee;
os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee));
@@ -245,7 +244,7 @@ hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
return &wsc_enrollee;
}
if (conf->wps_state && identity_len == WSC_ID_REGISTRAR_LEN &&
if (identity_len == WSC_ID_REGISTRAR_LEN &&
os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) {
static struct hostapd_eap_user wsc_registrar;
os_memset(&wsc_registrar, 0, sizeof(wsc_registrar));
@@ -258,27 +257,6 @@ hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
}
#endif /* CONFIG_WPS */
while (user) {
if (!phase2 && user->identity == NULL) {
/* Wildcard match */
break;
}
if (user->phase2 == !!phase2 && user->wildcard_prefix &&
identity_len >= user->identity_len &&
os_memcmp(user->identity, identity, user->identity_len) ==
0) {
/* Wildcard prefix match */
break;
}
if (user->phase2 == !!phase2 &&
user->identity_len == identity_len &&
os_memcmp(user->identity, identity, identity_len) == 0)
break;
user = user->next;
}
#ifdef CONFIG_SQLITE
if (user == NULL && conf->eap_user_sqlite) {
return eap_user_sqlite_get(hapd, identity, identity_len,
@@ -286,5 +264,5 @@ hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
}
#endif /* CONFIG_SQLITE */
return user;
return NULL;
}

View File

@@ -63,6 +63,25 @@ struct hostapd_frame_info {
int ssi_signal; /* dBm */
};
#ifdef CONFIG_WPS
enum hapd_wps_status {
WPS_SUCCESS_STATUS = 1,
WPS_FAILURE_STATUS
};
enum pbc_status {
WPS_PBC_STATUS_DISABLE,
WPS_PBC_STATUS_ACTIVE,
WPS_PBC_STATUS_TIMEOUT,
WPS_PBC_STATUS_OVERLAP
};
struct wps_stat {
enum hapd_wps_status status;
enum pbc_status pbc_status;
u8 peer_addr[ETH_ALEN];
};
#endif
/**
* struct hostapd_data - hostapd per-BSS data structure
@@ -73,9 +92,13 @@ struct hostapd_data {
int interface_added; /* virtual interface added for this BSS */
u8 own_addr[ETH_ALEN];
struct sta_info *sta_list; /* STA info list head */
#define STA_HASH_SIZE 10
#define STA_HASH(sta) (sta[5] & 0xa)
struct sta_info *sta_hash[STA_HASH_SIZE];
int num_sta; /* number of entries in sta_list */
struct eapol_authenticator *eapol_auth;
struct wpa_authenticator *wpa_auth;
#ifdef CONFIG_FULL_DYNAMIC_VLAN
@@ -83,10 +106,15 @@ struct hostapd_data {
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
#ifdef CONFIG_WPS
struct wps_context *wps;
unsigned int ap_pin_failures;
unsigned int ap_pin_failures_consecutive;
struct upnp_wps_device_sm *wps_upnp;
unsigned int ap_pin_lockout_time;
struct wps_stat wps_stats;
void (*wps_event_cb)(void *ctx, enum wps_event event,
union wps_event_data *data);
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
@@ -112,4 +140,10 @@ struct hostapd_data {
#endif /* CONFIG_SQLITE */
};
struct hostapd_data *hostapd_get_hapd_data(void);
const struct hostapd_eap_user *
hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
size_t identity_len, int phase2);
#endif /* HOSTAPD_H */

View File

@@ -9,14 +9,138 @@
#include "utils/includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "crypto/crypto.h"
#include "common/ieee802_11_defs.h"
#include "hostapd.h"
#include "ap/sta_info.h"
#include "ap/wpa_auth.h"
#include "eap_server/eap.h"
#include "ap/ap_config.h"
#include "eap_common/eap_wsc_common.h"
#include "ap/ieee802_1x.h"
#include "utils/wpa_debug.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "eapol_auth/eapol_auth_sm_i.h"
#include "eap_server/eap.h"
#include "sta_info.h"
#include "ieee802_1x.h"
int hostapd_send_eapol(const u8 *source, const u8 *sta_addr,
const u8 *data, size_t data_len);
static void ieee802_1x_finished(struct hostapd_data *hapd,
struct sta_info *sta, int success,
int remediation);
static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta,
u8 type, const u8 *data, size_t datalen)
{
u8 *buf;
struct ieee802_1x_hdr *xhdr;
size_t len;
len = sizeof(*xhdr) + datalen;
buf = os_zalloc(len);
if (!buf) {
wpa_printf(MSG_ERROR, "malloc() failed for %s(len=%lu)",
__func__, (unsigned long) len);
return;
}
xhdr = (struct ieee802_1x_hdr *) buf;
xhdr->version = EAPOL_VERSION;
xhdr->type = type;
xhdr->length = host_to_be16(datalen);
if (datalen > 0 && data != NULL)
os_memcpy(xhdr + 1, data, datalen);
hostapd_send_eapol(hapd->own_addr, sta->addr, buf, len);
os_free(buf);
}
static void handle_eap_response(struct hostapd_data *hapd,
struct sta_info *sta, struct eap_hdr *eap,
size_t len)
{
u8 type, *data;
struct eapol_state_machine *sm = sta->eapol_sm;
if (!sm)
return;
data = (u8 *) (eap + 1);
if (len < sizeof(*eap) + 1) {
wpa_printf(MSG_INFO, "%s: too short response data", __func__);
return;
}
sm->eap_type_supp = type = data[0];
sm->dot1xAuthEapolRespFramesRx++;
wpabuf_free(sm->eap_if->eapRespData);
sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len);
sm->eapolEap = true;
}
/* Process incoming EAP packet from Supplicant */
static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta,
u8 *buf, size_t len)
{
struct eap_hdr *eap;
u16 eap_len;
if (len < sizeof(*eap)) {
wpa_printf(MSG_INFO, " too short EAP packet");
return;
}
eap = (struct eap_hdr *) buf;
eap_len = be_to_host16(eap->length);
wpa_printf(MSG_DEBUG, "EAP: code=%d identifier=%d length=%d",
eap->code, eap->identifier,
eap_len);
if (eap_len < sizeof(*eap)) {
wpa_printf(MSG_DEBUG, " Invalid EAP length");
return;
} else if (eap_len > len) {
wpa_printf(MSG_DEBUG,
" Too short frame to contain this EAP packet");
return;
} else if (eap_len < len) {
wpa_printf(MSG_DEBUG,
" Ignoring %lu extra bytes after EAP packet",
(unsigned long) len - eap_len);
}
switch (eap->code) {
case EAP_CODE_RESPONSE:
handle_eap_response(hapd, sta, eap, eap_len);
break;
case EAP_CODE_INITIATE:
break;
}
}
struct eapol_state_machine *
ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta)
{
int flags = 0;
if (sta->wpa_sm) {
flags |= EAPOL_SM_USES_WPA;
}
return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags,
sta->wps_ie, NULL, sta,
sta->identity, NULL);
}
/**
* ieee802_1x_receive - Process the EAPOL frames from the Supplicant
@@ -35,35 +159,44 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
struct ieee802_1x_eapol_key *key;
u16 datalen;
wpa_printf( MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR,
wpa_printf(MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR,
(unsigned long) len, MAC2STR(sa));
sta = ap_get_sta(hapd, sa);
if (!sta || !(sta->flags & (WLAN_STA_ASSOC | WLAN_STA_PREAUTH))) {
wpa_printf( MSG_DEBUG, "IEEE 802.1X data frame from not "
"associated/Pre-authenticating STA");
if (!sta) {
wpa_printf(MSG_DEBUG,
"IEEE 802.1X data frame from not associated/Pre-authenticating STA");
return;
}
if (len < sizeof(*hdr)) {
wpa_printf( MSG_DEBUG, " too short IEEE 802.1X packet\n");
wpa_printf(MSG_INFO, " too short IEEE 802.1X packet");
return;
}
hdr = (struct ieee802_1x_hdr *) buf;
datalen = be_to_host16(hdr->length);
wpa_printf( MSG_DEBUG, " IEEE 802.1X: version=%d type=%d length=%d",
wpa_printf(MSG_DEBUG, " IEEE 802.1X: version=%d type=%d length=%d",
hdr->version, hdr->type, datalen);
if (len - sizeof(*hdr) < datalen) {
wpa_printf( MSG_DEBUG, " frame too short for this IEEE 802.1X packet\n");
wpa_printf(MSG_INFO,
" frame too short for this IEEE 802.1X packet");
if (sta->eapol_sm)
sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++;
return;
}
if (len - sizeof(*hdr) > datalen) {
wpa_printf( MSG_DEBUG, " ignoring %lu extra octets after "
"IEEE 802.1X packet",
wpa_printf(MSG_DEBUG,
" ignoring %lu extra octets after IEEE 802.1X packet",
(unsigned long) len - sizeof(*hdr) - datalen);
}
if (sta->eapol_sm) {
sta->eapol_sm->dot1xAuthLastEapolFrameVersion = hdr->version;
sta->eapol_sm->dot1xAuthEapolFramesRx++;
}
key = (struct ieee802_1x_eapol_key *) (hdr + 1);
if (datalen >= sizeof(struct ieee802_1x_eapol_key) &&
hdr->type == IEEE802_1X_TYPE_EAPOL_KEY &&
@@ -73,4 +206,274 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
sizeof(*hdr) + datalen);
return;
}
if (!sta->eapol_sm) {
sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
if (!sta->eapol_sm)
return;
#ifdef CONFIG_WPS
if (!hapd->conf->ieee802_1x && hapd->conf->wps_state) {
u32 wflags = sta->flags & (WLAN_STA_WPS |
WLAN_STA_WPS2 |
WLAN_STA_MAYBE_WPS);
if (wflags == WLAN_STA_MAYBE_WPS ||
wflags == (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) {
/*
* Delay EAPOL frame transmission until a
* possible WPS STA initiates the handshake
* with EAPOL-Start. Only allow the wait to be
* skipped if the STA is known to support WPS
* 2.0.
*/
wpa_printf(MSG_DEBUG,
"WPS: Do not start EAPOL until EAPOL-Start is received");
sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
}
}
#endif /* CONFIG_WPS */
sta->eapol_sm->eap_if->portEnabled = true;
}
/* since we support version 1, we can ignore version field and proceed
* as specified in version 1 standard [IEEE Std 802.1X-2001, 7.5.5] */
/* TODO: actually, we are not version 1 anymore.. However, Version 2
* does not change frame contents, so should be ok to process frames
* more or less identically. Some changes might be needed for
* verification of fields. */
switch (hdr->type) {
case IEEE802_1X_TYPE_EAP_PACKET:
handle_eap(hapd, sta, (u8 *) (hdr + 1), datalen);
break;
case IEEE802_1X_TYPE_EAPOL_START:
sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
sta->eapol_sm->eapolStart = true;
sta->eapol_sm->dot1xAuthEapolStartFramesRx++;
eap_server_clear_identity(sta->eapol_sm->eap);
wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL);
break;
case IEEE802_1X_TYPE_EAPOL_LOGOFF:
break;
case IEEE802_1X_TYPE_EAPOL_KEY:
wpa_printf(MSG_DEBUG, " EAPOL-Key");
break;
case IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT:
wpa_printf(MSG_DEBUG, " EAPOL-Encapsulated-ASF-Alert");
/* TODO: implement support for this; show data */
break;
default:
wpa_printf(MSG_DEBUG, " unknown IEEE 802.1X packet type");
sta->eapol_sm->dot1xAuthInvalidEapolFramesRx++;
break;
}
eapol_auth_step(sta->eapol_sm);
}
void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta)
{
struct eapol_state_machine *sm = sta->eapol_sm;
if (!sm)
return;
sta->eapol_sm = NULL;
eapol_auth_free(sm);
}
static void ieee802_1x_eapol_send(void *ctx, void *sta_ctx, u8 type,
const u8 *data, size_t datalen)
{
#ifdef CONFIG_WPS
struct sta_info *sta = sta_ctx;
if ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) ==
WLAN_STA_MAYBE_WPS) {
const u8 *identity;
size_t identity_len;
struct eapol_state_machine *sm = sta->eapol_sm;
identity = eap_get_identity(sm->eap, &identity_len);
if (identity &&
((identity_len == WSC_ID_ENROLLEE_LEN &&
os_memcmp(identity, WSC_ID_ENROLLEE,
WSC_ID_ENROLLEE_LEN) == 0) ||
(identity_len == WSC_ID_REGISTRAR_LEN &&
os_memcmp(identity, WSC_ID_REGISTRAR,
WSC_ID_REGISTRAR_LEN) == 0))) {
wpa_printf(MSG_DEBUG,
"WPS: WLAN_STA_MAYBE_WPS -> WLAN_STA_WPS");
sta->flags |= WLAN_STA_WPS;
}
}
#endif /* CONFIG_WPS */
ieee802_1x_send(ctx, sta_ctx, type, data, datalen);
}
static void ieee802_1x_aaa_send(void *ctx, void *sta_ctx,
const u8 *data, size_t datalen)
{
#ifndef CONFIG_NO_RADIUS
struct hostapd_data *hapd = ctx;
struct sta_info *sta = sta_ctx;
ieee802_1x_encapsulate_radius(hapd, sta, data, datalen);
#endif /* CONFIG_NO_RADIUS */
}
static void _ieee802_1x_finished(void *ctx, void *sta_ctx, int success,
int preauth, int remediation)
{
struct hostapd_data *hapd = ctx;
struct sta_info *sta = sta_ctx;
ieee802_1x_finished(hapd, sta, success, remediation);
}
static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity,
size_t identity_len, int phase2,
struct eap_user *user)
{
struct hostapd_data *hapd = ctx;
const struct hostapd_eap_user *eap_user;
int i;
int rv = -1;
eap_user = hostapd_get_eap_user(hapd, identity, identity_len, phase2);
if (!eap_user)
goto out;
os_memset(user, 0, sizeof(*user));
user->phase2 = phase2;
for (i = 0; i < EAP_MAX_METHODS; i++) {
user->methods[i].vendor = eap_user->methods[i].vendor;
user->methods[i].method = eap_user->methods[i].method;
}
if (eap_user->password) {
user->password = os_memdup(eap_user->password,
eap_user->password_len);
if (!user->password)
goto out;
user->password_len = eap_user->password_len;
user->password_hash = eap_user->password_hash;
}
user->force_version = eap_user->force_version;
user->ttls_auth = eap_user->ttls_auth;
rv = 0;
out:
if (rv)
wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__);
return rv;
}
static int ieee802_1x_sta_entry_alive(void *ctx, const u8 *addr)
{
struct hostapd_data *hapd = ctx;
struct sta_info *sta;
sta = ap_get_sta(hapd, addr);
if (!sta || !sta->eapol_sm)
return 0;
return 1;
}
static void ieee802_1x_set_port_authorized(void *ctx, void *sta_ctx,
int authorized)
{
}
static void _ieee802_1x_abort_auth(void *ctx, void *sta_ctx)
{
}
static void ieee802_1x_eapol_event(void *ctx, void *sta_ctx,
enum eapol_event type)
{
#if 0
/* struct hostapd_data *hapd = ctx; */
struct sta_info *sta = sta_ctx;
switch (type) {
case EAPOL_AUTH_SM_CHANGE:
wpa_auth_sm_notify(sta->wpa_sm);
break;
case EAPOL_AUTH_REAUTHENTICATE:
wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL);
break;
}
#endif
}
int ieee802_1x_init(struct hostapd_data *hapd)
{
struct eapol_auth_config conf;
struct eapol_auth_cb cb;
struct eap_config *eap_cfg;
os_memset(&conf, 0, sizeof(conf));
eap_cfg = os_zalloc(sizeof(struct eap_config));
eap_cfg->max_auth_rounds = 100;
eap_cfg->max_auth_rounds_short = 50;
//eap_cfg->backend_auth = 1;
eap_cfg->eap_server = 1;
conf.eap_cfg = eap_cfg;
conf.ctx = hapd;
conf.wpa = hapd->conf->wpa;
os_memset(&cb, 0, sizeof(cb));
cb.eapol_send = ieee802_1x_eapol_send;
cb.aaa_send = ieee802_1x_aaa_send;
cb.finished = _ieee802_1x_finished;
cb.get_eap_user = ieee802_1x_get_eap_user;
cb.sta_entry_alive = ieee802_1x_sta_entry_alive;
cb.set_port_authorized = ieee802_1x_set_port_authorized;
cb.abort_auth = _ieee802_1x_abort_auth;
cb.eapol_event = ieee802_1x_eapol_event;
hapd->eapol_auth = eapol_auth_init(&conf, &cb);
if (!hapd->eapol_auth)
return -1;
return 0;
}
static void ieee802_1x_finished(struct hostapd_data *hapd,
struct sta_info *sta, int success,
int remediation)
{
if (!success) {
/*
* Many devices require deauthentication after WPS provisioning
* and some may not be be able to do that themselves, so
* disconnect the client here. In addition, this may also
* benefit IEEE 802.1X/EAPOL authentication cases, too since
* the EAPOL PAE state machine would remain in HELD state for
* considerable amount of time and some EAP methods, like
* EAP-FAST with anonymous provisioning, may require another
* EAPOL authentication to be started to complete connection.
*/
ap_sta_delayed_1x_auth_fail_disconnect(hapd, sta);
}
}

View File

@@ -17,8 +17,12 @@ struct hostapd_bss_config;
struct hostapd_radius_attr;
struct radius_msg;
void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
size_t len);
void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta);
int ieee802_1x_init(struct hostapd_data *hapd);
struct eapol_state_machine *
ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta);
#endif /* IEEE802_1X_H */

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/*
* hostapd / Station table
* Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
* Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -42,36 +42,20 @@ struct sta_info {
struct sta_info *next; /* next entry in sta list */
struct sta_info *hnext; /* next entry in hash table list */
u8 addr[6];
u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
u32 flags; /* Bitfield of WLAN_STA_* */
u16 capability;
u16 listen_interval; /* or beacon_int for APs */
u8 supported_rates[WLAN_SUPP_RATES_MAX];
int supported_rates_len;
u16 auth_alg;
enum {
STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE
} timeout_next;
/* IEEE 802.1X related data */
struct eapol_state_machine *eapol_sm;
struct wpa_state_machine *wpa_sm;
#ifdef CONFIG_IEEE80211W
int sa_query_count; /* number of pending SA Query requests;
* 0 = no SA Query in progress */
int sa_query_timed_out;
u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN *
* sa_query_count octets of pending SA Query
* transaction identifiers */
struct os_time sa_query_start;
#endif /* CONFIG_IEEE80211W */
char *identity; /* User-Name from RADIUS */
#ifdef CONFIG_INTERWORKING
#define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */
struct gas_dialog_info *gas_dialog;
u8 gas_dialog_next;
#endif /* CONFIG_INTERWORKING */
struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
#ifdef CONFIG_SAE
enum { SAE_INIT, SAE_COMMIT, SAE_CONFIRM } sae_state;
@@ -137,5 +121,16 @@ static inline int ap_sta_is_authorized(struct sta_info *sta)
void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta);
void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta);
void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd,
struct sta_info *sta);
int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen);
void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
struct sta_info *sta);
int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
struct sta_info *sta);
int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta);
void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta);
#endif /* STA_INFO_H */

View File

@@ -18,6 +18,7 @@
#include "hostapd.h"
#include "rsn_supp/wpa.h"
#include "ap/ap_config.h"
#include "ap/sta_info.h"
#include "common/wpa_common.h"
#include "crypto/aes_wrap.h"
@@ -29,6 +30,7 @@
#include "esp_wifi_driver.h"
#include "esp_wifi.h"
#include "esp_private/wifi.h"
#include "esp_wpas_glue.h"
#define STATE_MACHINE_DATA struct wpa_state_machine
#define STATE_MACHINE_DEBUG_PREFIX "WPA"
@@ -183,22 +185,7 @@ static inline int
wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *data, size_t data_len, int encrypt)
{
void *buffer = os_malloc(data_len + sizeof(struct l2_ethhdr));
struct l2_ethhdr *eth = buffer;
if (!buffer){
wpa_printf( MSG_DEBUG, "send_eapol, buffer=%p\n", buffer);
return -1;
}
memcpy(eth->h_dest, addr, ETH_ALEN);
memcpy(eth->h_source, wpa_auth->addr, ETH_ALEN);
eth->h_proto = host_to_be16(ETH_P_EAPOL);
memcpy((char *)buffer + sizeof(struct l2_ethhdr), data, data_len);
esp_wifi_internal_tx(1, buffer, sizeof(struct l2_ethhdr) + data_len);
os_free(buffer);
return 0;
return hostapd_send_eapol(wpa_auth->addr, addr, data, data_len);
}
int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
@@ -2406,11 +2393,12 @@ bool wpa_ap_join(void** sm, uint8_t *bssid, uint8_t *wpa_ie, uint8_t wpa_ie_len,
bool wpa_ap_remove(void* sm)
{
struct wpa_state_machine *wpa_sm;
if (!sm) return false;
struct hostapd_data *hapd = hostapd_get_hapd_data();
if (!sm || !hapd) {
return false;
}
wpa_sm = (struct wpa_state_machine*)sm;
wpa_auth_sta_deinit(wpa_sm);
ap_free_sta(hapd, sm);
return true;
}

View File

@@ -0,0 +1,586 @@
/*
* hostapd / WPS integration
* Copyright (c) 2008-2016, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "utils/uuid.h"
#include "common/wpa_ctrl.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "wps/wps.h"
#include "wps/wps_defs.h"
#include "wps/wps_dev_attr.h"
#include "wps/wps_attr_parse.h"
#include "hostapd.h"
#include "ap_config.h"
#include "wpa_auth.h"
#include "wpa_auth_i.h"
#include "sta_info.h"
#include "wps/wps_i.h"
#include "wps_hostapd.h"
#include "eap_server/eap_methods.h"
#include "ieee802_1x.h"
#include "esp_wps_i.h"
static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
static int hostapd_wps_set_ie_cb(void *ctx, struct wpabuf *beacon_ie,
struct wpabuf *probe_resp_ie)
{
int ret;
wpa_printf(MSG_DEBUG, "WPS: Updating beacon IEs(%d) to driver", beacon_ie->used);
ret = esp_wifi_set_appie_internal(WIFI_APPIE_RAM_BEACON,
(uint8_t *)wpabuf_head(beacon_ie), beacon_ie->used, 0);
if (ret != ESP_OK) {
wpa_printf(MSG_ERROR, "WPS: Failed to update beacon IEs");
goto cleanup;
}
wpa_printf(MSG_DEBUG, "WPS: Updating probe IEs(%d) to driver", probe_resp_ie->used);
ret = esp_wifi_set_appie_internal(WIFI_APPIE_RAM_PROBE_RSP,
(uint8_t *)wpabuf_head(probe_resp_ie), probe_resp_ie->used, 0);
cleanup:
wpabuf_free(beacon_ie);
wpabuf_free(probe_resp_ie);
return ret;
}
struct wps_stop_reg_data {
struct hostapd_data *current_hapd;
const u8 *uuid_e;
const u8 *dev_pw;
size_t dev_pw_len;
};
static int wps_stop_registrar(struct hostapd_data *hapd, void *ctx)
{
struct wps_stop_reg_data *data = ctx;
if (hapd != data->current_hapd && hapd->wps != NULL)
wps_registrar_complete(hapd->wps->registrar, data->uuid_e,
data->dev_pw, data->dev_pw_len);
return 0;
}
void hostapd_wps_eap_completed(struct hostapd_data *hapd)
{
}
static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
const u8 *uuid_e, const u8 *dev_pw,
size_t dev_pw_len)
{
struct hostapd_data *hapd = ctx;
char uuid[40];
struct wps_stop_reg_data data;
if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
return;
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s",
MAC2STR(mac_addr), uuid);
data.current_hapd = hapd;
data.uuid_e = uuid_e;
data.dev_pw = dev_pw;
data.dev_pw_len = dev_pw_len;
wps_stop_registrar(hapd, &data);
wps_set_status(WPS_STATUS_DISABLE);
/* TODO add callback event for freeRTOS */
}
static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx)
{
struct hostapd_data *hapd = eloop_data;
if (hapd->conf->ap_setup_locked)
return;
if (hapd->ap_pin_failures_consecutive >= 10)
return;
wpa_printf(MSG_DEBUG, "WPS: Re-enable AP PIN");
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
hapd->wps->ap_setup_locked = 0;
wps_registrar_update_ie(hapd->wps->registrar);
}
static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
{
struct wps_event_pwd_auth_fail *data = ctx;
if (!data->enrollee || hapd->conf->ap_pin == NULL || hapd->wps == NULL)
return 0;
/*
* Registrar failed to prove its knowledge of the AP PIN. Lock AP setup
* for some time if this happens multiple times to slow down brute
* force attacks.
*/
hapd->ap_pin_failures++;
hapd->ap_pin_failures_consecutive++;
wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u "
"(%u consecutive)",
hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive);
if (hapd->ap_pin_failures < 3)
return 0;
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_LOCKED);
hapd->wps->ap_setup_locked = 1;
wps_registrar_update_ie(hapd->wps->registrar);
if (!hapd->conf->ap_setup_locked &&
hapd->ap_pin_failures_consecutive >= 10) {
/*
* In indefinite lockdown - disable automatic AP PIN
* reenablement.
*/
eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely");
} else if (!hapd->conf->ap_setup_locked) {
if (hapd->ap_pin_lockout_time == 0)
hapd->ap_pin_lockout_time = 60;
else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
(hapd->ap_pin_failures % 3) == 0)
hapd->ap_pin_lockout_time *= 2;
wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN for %u seconds",
hapd->ap_pin_lockout_time);
eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
eloop_register_timeout(hapd->ap_pin_lockout_time, 0,
hostapd_wps_reenable_ap_pin, hapd,
NULL);
}
return 0;
}
static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
struct wps_event_pwd_auth_fail *data)
{
/* Update WPS Status - Authentication Failure */
wpa_printf(MSG_DEBUG, "WPS: Authentication failure update");
hapd->wps_stats.status = WPS_FAILURE_STATUS;
os_memcpy(hapd->wps_stats.peer_addr, data->peer_macaddr, ETH_ALEN);
wps_pwd_auth_fail(hapd, data);
}
static int wps_ap_pin_success(struct hostapd_data *hapd, void *ctx)
{
if (hapd->conf->ap_pin == NULL || hapd->wps == NULL)
return 0;
if (hapd->ap_pin_failures_consecutive == 0)
return 0;
wpa_printf(MSG_DEBUG, "WPS: Clear consecutive AP PIN failure counter "
"- total validation failures %u (%u consecutive)",
hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive);
hapd->ap_pin_failures_consecutive = 0;
return 0;
}
static void hostapd_wps_ap_pin_success(struct hostapd_data *hapd)
{
wps_ap_pin_success(hapd, NULL);
}
static void hostapd_wps_event_pbc_overlap(struct hostapd_data *hapd)
{
/* Update WPS Status - PBC Overlap */
hapd->wps_stats.pbc_status = WPS_PBC_STATUS_OVERLAP;
}
static void hostapd_wps_event_pbc_timeout(struct hostapd_data *hapd)
{
/* Update WPS PBC Status:PBC Timeout */
hapd->wps_stats.pbc_status = WPS_PBC_STATUS_TIMEOUT;
}
static void hostapd_wps_event_pbc_active(struct hostapd_data *hapd)
{
/* Update WPS PBC status - Active */
hapd->wps_stats.pbc_status = WPS_PBC_STATUS_ACTIVE;
}
static void hostapd_wps_event_pbc_disable(struct hostapd_data *hapd)
{
/* Update WPS PBC status - Active */
hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE;
}
static void hostapd_wps_event_success(struct hostapd_data *hapd,
struct wps_event_success *success)
{
/* Update WPS status - Success */
hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE;
hapd->wps_stats.status = WPS_SUCCESS_STATUS;
os_memcpy(hapd->wps_stats.peer_addr, success->peer_macaddr, ETH_ALEN);
}
static void hostapd_wps_event_fail(struct hostapd_data *hapd,
struct wps_event_fail *fail)
{
/* Update WPS status - Failure */
hapd->wps_stats.status = WPS_FAILURE_STATUS;
os_memcpy(hapd->wps_stats.peer_addr, fail->peer_macaddr, ETH_ALEN);
if (fail->error_indication > 0 &&
fail->error_indication < NUM_WPS_EI_VALUES) {
wpa_msg(hapd->msg_ctx, MSG_INFO,
WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)",
fail->msg, fail->config_error, fail->error_indication,
wps_ei_str(fail->error_indication));
} else {
wpa_msg(hapd->msg_ctx, MSG_INFO,
WPS_EVENT_FAIL "msg=%d config_error=%d",
fail->msg, fail->config_error);
}
}
static void hostapd_wps_event_cb(void *ctx, enum wps_event event,
union wps_event_data *data)
{
struct hostapd_data *hapd = ctx;
switch (event) {
case WPS_EV_M2D:
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_M2D);
break;
case WPS_EV_FAIL:
hostapd_wps_event_fail(hapd, &data->fail);
break;
case WPS_EV_SUCCESS:
hostapd_wps_event_success(hapd, &data->success);
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_SUCCESS);
break;
case WPS_EV_PWD_AUTH_FAIL:
hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail);
break;
case WPS_EV_PBC_OVERLAP:
hostapd_wps_event_pbc_overlap(hapd);
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_OVERLAP);
break;
case WPS_EV_PBC_TIMEOUT:
hostapd_wps_event_pbc_timeout(hapd);
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT);
break;
case WPS_EV_PBC_ACTIVE:
hostapd_wps_event_pbc_active(hapd);
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ACTIVE);
break;
case WPS_EV_PBC_DISABLE:
hostapd_wps_event_pbc_disable(hapd);
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_DISABLE);
break;
case WPS_EV_ER_AP_ADD:
break;
case WPS_EV_ER_AP_REMOVE:
break;
case WPS_EV_ER_ENROLLEE_ADD:
break;
case WPS_EV_ER_ENROLLEE_REMOVE:
break;
case WPS_EV_ER_AP_SETTINGS:
break;
case WPS_EV_ER_SET_SELECTED_REGISTRAR:
break;
case WPS_EV_AP_PIN_SUCCESS:
hostapd_wps_ap_pin_success(hapd);
break;
}
if (hapd->wps_event_cb)
hapd->wps_event_cb(hapd, event, data);
}
static int hostapd_wps_rf_band_cb(void *ctx)
{
return WPS_RF_24GHZ;
}
static void hostapd_wps_clear_ies(struct hostapd_data *hapd, int deinit_only)
{
esp_wifi_unset_appie_internal(WIFI_APPIE_RAM_BEACON);
esp_wifi_unset_appie_internal(WIFI_APPIE_RAM_PROBE_RSP);
}
static void hostapd_free_wps(struct wps_context *wps)
{
int i;
for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
wpabuf_free(wps->dev.vendor_ext[i]);
wps_device_data_free(&wps->dev);
os_free(wps->network_key);
wpabuf_free(wps->dh_pubkey);
wpabuf_free(wps->dh_privkey);
os_free(wps);
}
int hostapd_init_wps(struct hostapd_data *hapd, struct wps_data *wps_data, struct wps_context *wps)
{
struct wps_registrar_config cfg;
wps->event_cb = hostapd_wps_event_cb;
wps->rf_band_cb = hostapd_wps_rf_band_cb;
wps->cb_ctx = hapd;
wps->wps_state = WPS_STATE_CONFIGURED;
wps->ap_setup_locked = 0;
wps->ap = 1;
/* build credentials to be given */
hostapd_wps_config_ap(hapd, wps_data);
os_memset(&cfg, 0, sizeof(cfg));
cfg.set_ie_cb = hostapd_wps_set_ie_cb;
cfg.reg_success_cb = hostapd_wps_reg_success_cb;
cfg.cb_ctx = hapd;
cfg.disable_auto_conf = 1;
wps->registrar = wps_registrar_init(wps, &cfg);
if (wps->registrar == NULL) {
wpa_printf(MSG_ERROR, "Failed to initialize WPS Registrar");
goto fail;
}
hapd->wps = wps;
eap_server_identity_register();
if (eap_server_wsc_register() < 0) {
wpa_printf(MSG_ERROR, "Failed to register for wsc server");
}
if (ieee802_1x_init(hapd) < 0) {
return 0;
}
return 0;
fail:
hostapd_free_wps(wps);
return -1;
}
void hostapd_deinit_wps(struct hostapd_data *hapd)
{
eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
if (hapd->wps == NULL) {
hostapd_wps_clear_ies(hapd, 1);
return;
}
wps_registrar_deinit(hapd->wps->registrar);
hostapd_free_wps(hapd->wps);
hapd->wps = NULL;
hostapd_wps_clear_ies(hapd, 1);
}
void hostapd_update_wps(struct hostapd_data *hapd)
{
struct wps_context *wps = hapd->wps;
struct hostapd_bss_config *conf = hapd->conf;
if (!wps)
return;
os_memcpy(wps->ssid, conf->ssid.ssid, conf->ssid.ssid_len);
wps->ssid_len = conf->ssid.ssid_len;
/* Clear WPS settings, then fill them again */
hostapd_wps_config_ap(hapd, NULL);
wps_registrar_update_ie(wps->registrar);
}
int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *pin)
{
int ret;
if (hapd->wps == NULL)
return 0;
ret = wps_registrar_add_pin(hapd->wps->registrar, NULL,
NULL, pin, 8, 120);
return ret;
}
int hostapd_wps_button_pushed(struct hostapd_data *hapd,
const u8 *p2p_dev_addr)
{
if (hapd->wps) {
return wps_registrar_button_pushed(hapd->wps->registrar,
p2p_dev_addr);
}
return 0;
}
static int wps_cancel(struct hostapd_data *hapd)
{
if (hapd->wps) {
wps_registrar_wps_cancel(hapd->wps->registrar);
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_CANCEL);
}
return 0;
}
int hostapd_wps_cancel(struct hostapd_data *hapd)
{
int ret;
ret = wps_cancel(hapd);
return ret;
}
static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx)
{
struct hostapd_data *hapd = eloop_data;
wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out");
hostapd_wps_ap_pin_disable(hapd);
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_PIN_DISABLED);
}
static void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout)
{
wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout);
hapd->ap_pin_failures = 0;
hapd->ap_pin_failures_consecutive = 0;
hapd->conf->ap_setup_locked = 0;
if (hapd->wps->ap_setup_locked) {
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
hapd->wps->ap_setup_locked = 0;
wps_registrar_update_ie(hapd->wps->registrar);
}
eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
if (timeout > 0)
eloop_register_timeout(timeout, 0,
hostapd_wps_ap_pin_timeout, hapd, NULL);
}
static int wps_ap_pin_disable(struct hostapd_data *hapd)
{
os_free(hapd->conf->ap_pin);
hapd->conf->ap_pin = NULL;
eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
return 0;
}
void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd)
{
wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN");
wps_ap_pin_disable(hapd);
}
struct wps_ap_pin_data {
char pin_txt[9];
int timeout;
};
static int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx)
{
struct wps_ap_pin_data *data = ctx;
if (!hapd->wps)
return 0;
os_free(hapd->conf->ap_pin);
hapd->conf->ap_pin = os_strdup(data->pin_txt);
hostapd_wps_ap_pin_enable(hapd, data->timeout);
return 0;
}
const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout)
{
unsigned int pin;
struct wps_ap_pin_data data;
if (wps_generate_pin(&pin) < 0)
return NULL;
os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin);
data.timeout = timeout;
wps_ap_pin_set(hapd, &data);
return hapd->conf->ap_pin;
}
const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd)
{
return hapd->conf->ap_pin;
}
int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
int timeout)
{
struct wps_ap_pin_data data;
int ret;
ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin);
if (os_snprintf_error(sizeof(data.pin_txt), ret))
return -1;
data.timeout = timeout;
return wps_ap_pin_set(hapd, &data);
}
int hostapd_wps_config_ap(struct hostapd_data *hapd, struct wps_data *wps_data)
{
struct wps_credential cred = {0};
os_memcpy(cred.ssid, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len);
cred.ssid_len = hapd->conf->ssid.ssid_len;
cred.encr_type = WPS_ENCR_NONE;
cred.auth_type = WPS_AUTH_OPEN;
if (hapd->wpa_auth->conf.wpa == WPA_PROTO_WPA) {
cred.auth_type = WPS_AUTH_WPAPSK;
cred.encr_type = WPS_ENCR_TKIP;
} else if (hapd->wpa_auth->conf.wpa == WPA_PROTO_RSN) {
cred.auth_type = WPS_AUTH_WPA2PSK;
cred.encr_type = WPS_ENCR_AES;
} else if (hapd->wpa_auth->conf.wpa == (WPA_PROTO_RSN | WPA_PROTO_WPA)) {
cred.auth_type = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK;
cred.encr_type = WPS_ENCR_AES;
}
if (hapd->conf->ssid.wpa_passphrase) {
cred.key_len = os_strlen(hapd->conf->ssid.wpa_passphrase);
memcpy(cred.key, hapd->conf->ssid.wpa_passphrase, cred.key_len);
}
wps_data->use_cred = os_malloc(sizeof(struct wps_credential));
if (!wps_data->use_cred) {
wpa_printf(MSG_ERROR, "WPS: Disabling AP PIN");
return -1;
}
os_memcpy(wps_data->use_cred, &cred, sizeof(struct wps_credential));
return 0;
}

View File

@@ -11,14 +11,12 @@
#ifdef CONFIG_WPS
int hostapd_init_wps(struct hostapd_data *hapd,
struct hostapd_bss_config *conf);
int hostapd_init_wps(struct hostapd_data *hapd, struct wps_data *wps_data, struct wps_context *wps);
int hostapd_init_wps_complete(struct hostapd_data *hapd);
void hostapd_deinit_wps(struct hostapd_data *hapd);
void hostapd_update_wps(struct hostapd_data *hapd);
void hostapd_wps_eap_completed(struct hostapd_data *hapd);
int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr,
const char *uuid, const char *pin, int timeout);
int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *pin);
int hostapd_wps_button_pushed(struct hostapd_data *hapd,
const u8 *p2p_dev_addr);
int hostapd_wps_cancel(struct hostapd_data *hapd);
@@ -30,24 +28,12 @@ const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd);
int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
int timeout);
void hostapd_wps_update_ie(struct hostapd_data *hapd);
int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
const char *auth, const char *encr, const char *key);
int hostapd_wps_config_ap(struct hostapd_data *hapd, struct wps_data *wps_data);
int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd,
const struct wpabuf *data);
struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
int ndef);
struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef);
int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd,
const struct wpabuf *req,
const struct wpabuf *sel);
struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef);
int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd);
void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd);
#else /* CONFIG_WPS */
static inline int hostapd_init_wps(struct hostapd_data *hapd,
struct hostapd_bss_config *conf)
static inline int hostapd_init_wps(struct hostapd_data *hapd, struct wps_data *wps_data, struct wps_context *wps)
{
return 0;
}