1214501Srpaulo/* 2214501Srpaulo * hostapd / WPS integration 3337817Scy * Copyright (c) 2008-2016, Jouni Malinen <j@w1.fi> 4214501Srpaulo * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7214501Srpaulo */ 8214501Srpaulo 9214501Srpaulo#include "utils/includes.h" 10214501Srpaulo 11214501Srpaulo#include "utils/common.h" 12214501Srpaulo#include "utils/eloop.h" 13214501Srpaulo#include "utils/uuid.h" 14214501Srpaulo#include "common/wpa_ctrl.h" 15214501Srpaulo#include "common/ieee802_11_defs.h" 16214501Srpaulo#include "common/ieee802_11_common.h" 17214501Srpaulo#include "eapol_auth/eapol_auth_sm.h" 18214501Srpaulo#include "eapol_auth/eapol_auth_sm_i.h" 19214501Srpaulo#include "wps/wps.h" 20214501Srpaulo#include "wps/wps_defs.h" 21214501Srpaulo#include "wps/wps_dev_attr.h" 22252726Srpaulo#include "wps/wps_attr_parse.h" 23214501Srpaulo#include "hostapd.h" 24214501Srpaulo#include "ap_config.h" 25252726Srpaulo#include "ap_drv_ops.h" 26214501Srpaulo#include "beacon.h" 27214501Srpaulo#include "sta_info.h" 28214501Srpaulo#include "wps_hostapd.h" 29214501Srpaulo 30214501Srpaulo 31214501Srpaulo#ifdef CONFIG_WPS_UPNP 32214501Srpaulo#include "wps/wps_upnp.h" 33214501Srpaulostatic int hostapd_wps_upnp_init(struct hostapd_data *hapd, 34214501Srpaulo struct wps_context *wps); 35214501Srpaulostatic void hostapd_wps_upnp_deinit(struct hostapd_data *hapd); 36214501Srpaulo#endif /* CONFIG_WPS_UPNP */ 37214501Srpaulo 38252726Srpaulostatic int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da, 39252726Srpaulo const u8 *bssid, 40252726Srpaulo const u8 *ie, size_t ie_len, 41252726Srpaulo int ssi_signal); 42214501Srpaulostatic void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx); 43281806Srpaulostatic void hostapd_wps_nfc_clear(struct wps_context *wps); 44214501Srpaulo 45214501Srpaulo 46252726Srpaulostruct wps_for_each_data { 47252726Srpaulo int (*func)(struct hostapd_data *h, void *ctx); 48252726Srpaulo void *ctx; 49281806Srpaulo struct hostapd_data *calling_hapd; 50252726Srpaulo}; 51252726Srpaulo 52252726Srpaulo 53252726Srpaulostatic int wps_for_each(struct hostapd_iface *iface, void *ctx) 54252726Srpaulo{ 55252726Srpaulo struct wps_for_each_data *data = ctx; 56252726Srpaulo size_t j; 57252726Srpaulo 58252726Srpaulo if (iface == NULL) 59252726Srpaulo return 0; 60252726Srpaulo for (j = 0; j < iface->num_bss; j++) { 61252726Srpaulo struct hostapd_data *hapd = iface->bss[j]; 62281806Srpaulo int ret; 63281806Srpaulo 64281806Srpaulo if (hapd != data->calling_hapd && 65281806Srpaulo (hapd->conf->wps_independent || 66281806Srpaulo data->calling_hapd->conf->wps_independent)) 67281806Srpaulo continue; 68281806Srpaulo 69281806Srpaulo ret = data->func(hapd, data->ctx); 70252726Srpaulo if (ret) 71252726Srpaulo return ret; 72252726Srpaulo } 73252726Srpaulo 74252726Srpaulo return 0; 75252726Srpaulo} 76252726Srpaulo 77252726Srpaulo 78252726Srpaulostatic int hostapd_wps_for_each(struct hostapd_data *hapd, 79252726Srpaulo int (*func)(struct hostapd_data *h, void *ctx), 80252726Srpaulo void *ctx) 81252726Srpaulo{ 82252726Srpaulo struct hostapd_iface *iface = hapd->iface; 83252726Srpaulo struct wps_for_each_data data; 84252726Srpaulo data.func = func; 85252726Srpaulo data.ctx = ctx; 86281806Srpaulo data.calling_hapd = hapd; 87252726Srpaulo if (iface->interfaces == NULL || 88252726Srpaulo iface->interfaces->for_each_interface == NULL) 89252726Srpaulo return wps_for_each(iface, &data); 90252726Srpaulo return iface->interfaces->for_each_interface(iface->interfaces, 91252726Srpaulo wps_for_each, &data); 92252726Srpaulo} 93252726Srpaulo 94252726Srpaulo 95281806Srpaulostatic int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, 96281806Srpaulo const u8 *p2p_dev_addr, const u8 *psk, 97214501Srpaulo size_t psk_len) 98214501Srpaulo{ 99214501Srpaulo struct hostapd_data *hapd = ctx; 100214501Srpaulo struct hostapd_wpa_psk *p; 101214501Srpaulo struct hostapd_ssid *ssid = &hapd->conf->ssid; 102214501Srpaulo 103281806Srpaulo if (is_zero_ether_addr(p2p_dev_addr)) { 104281806Srpaulo wpa_printf(MSG_DEBUG, 105281806Srpaulo "Received new WPA/WPA2-PSK from WPS for STA " MACSTR, 106281806Srpaulo MAC2STR(mac_addr)); 107281806Srpaulo } else { 108281806Srpaulo wpa_printf(MSG_DEBUG, 109281806Srpaulo "Received new WPA/WPA2-PSK from WPS for STA " MACSTR 110281806Srpaulo " P2P Device Addr " MACSTR, 111281806Srpaulo MAC2STR(mac_addr), MAC2STR(p2p_dev_addr)); 112281806Srpaulo } 113214501Srpaulo wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len); 114214501Srpaulo 115214501Srpaulo if (psk_len != PMK_LEN) { 116214501Srpaulo wpa_printf(MSG_DEBUG, "Unexpected PSK length %lu", 117214501Srpaulo (unsigned long) psk_len); 118214501Srpaulo return -1; 119214501Srpaulo } 120214501Srpaulo 121214501Srpaulo /* Add the new PSK to runtime PSK list */ 122214501Srpaulo p = os_zalloc(sizeof(*p)); 123214501Srpaulo if (p == NULL) 124214501Srpaulo return -1; 125214501Srpaulo os_memcpy(p->addr, mac_addr, ETH_ALEN); 126281806Srpaulo os_memcpy(p->p2p_dev_addr, p2p_dev_addr, ETH_ALEN); 127214501Srpaulo os_memcpy(p->psk, psk, PMK_LEN); 128214501Srpaulo 129281806Srpaulo if (hapd->new_psk_cb) { 130281806Srpaulo hapd->new_psk_cb(hapd->new_psk_cb_ctx, mac_addr, p2p_dev_addr, 131281806Srpaulo psk, psk_len); 132281806Srpaulo } 133281806Srpaulo 134214501Srpaulo p->next = ssid->wpa_psk; 135214501Srpaulo ssid->wpa_psk = p; 136214501Srpaulo 137214501Srpaulo if (ssid->wpa_psk_file) { 138214501Srpaulo FILE *f; 139214501Srpaulo char hex[PMK_LEN * 2 + 1]; 140214501Srpaulo /* Add the new PSK to PSK list file */ 141214501Srpaulo f = fopen(ssid->wpa_psk_file, "a"); 142214501Srpaulo if (f == NULL) { 143214501Srpaulo wpa_printf(MSG_DEBUG, "Failed to add the PSK to " 144214501Srpaulo "'%s'", ssid->wpa_psk_file); 145214501Srpaulo return -1; 146214501Srpaulo } 147214501Srpaulo 148214501Srpaulo wpa_snprintf_hex(hex, sizeof(hex), psk, psk_len); 149214501Srpaulo fprintf(f, MACSTR " %s\n", MAC2STR(mac_addr), hex); 150214501Srpaulo fclose(f); 151214501Srpaulo } 152214501Srpaulo 153214501Srpaulo return 0; 154214501Srpaulo} 155214501Srpaulo 156214501Srpaulo 157214501Srpaulostatic int hostapd_wps_set_ie_cb(void *ctx, struct wpabuf *beacon_ie, 158214501Srpaulo struct wpabuf *probe_resp_ie) 159214501Srpaulo{ 160214501Srpaulo struct hostapd_data *hapd = ctx; 161214501Srpaulo wpabuf_free(hapd->wps_beacon_ie); 162214501Srpaulo hapd->wps_beacon_ie = beacon_ie; 163214501Srpaulo wpabuf_free(hapd->wps_probe_resp_ie); 164214501Srpaulo hapd->wps_probe_resp_ie = probe_resp_ie; 165252726Srpaulo if (hapd->beacon_set_done) 166252726Srpaulo ieee802_11_set_beacon(hapd); 167252726Srpaulo return hostapd_set_ap_wps_ie(hapd); 168214501Srpaulo} 169214501Srpaulo 170214501Srpaulo 171214501Srpaulostatic void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e, 172214501Srpaulo const struct wps_device_data *dev) 173214501Srpaulo{ 174214501Srpaulo struct hostapd_data *hapd = ctx; 175214501Srpaulo char uuid[40], txt[400]; 176214501Srpaulo int len; 177214501Srpaulo char devtype[WPS_DEV_TYPE_BUFSIZE]; 178214501Srpaulo if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) 179214501Srpaulo return; 180214501Srpaulo wpa_printf(MSG_DEBUG, "WPS: PIN needed for E-UUID %s", uuid); 181214501Srpaulo len = os_snprintf(txt, sizeof(txt), WPS_EVENT_PIN_NEEDED 182214501Srpaulo "%s " MACSTR " [%s|%s|%s|%s|%s|%s]", 183214501Srpaulo uuid, MAC2STR(dev->mac_addr), dev->device_name, 184214501Srpaulo dev->manufacturer, dev->model_name, 185214501Srpaulo dev->model_number, dev->serial_number, 186214501Srpaulo wps_dev_type_bin2str(dev->pri_dev_type, devtype, 187214501Srpaulo sizeof(devtype))); 188281806Srpaulo if (!os_snprintf_error(sizeof(txt), len)) 189214501Srpaulo wpa_msg(hapd->msg_ctx, MSG_INFO, "%s", txt); 190214501Srpaulo 191214501Srpaulo if (hapd->conf->wps_pin_requests) { 192214501Srpaulo FILE *f; 193214501Srpaulo struct os_time t; 194214501Srpaulo f = fopen(hapd->conf->wps_pin_requests, "a"); 195214501Srpaulo if (f == NULL) 196214501Srpaulo return; 197214501Srpaulo os_get_time(&t); 198214501Srpaulo fprintf(f, "%ld\t%s\t" MACSTR "\t%s\t%s\t%s\t%s\t%s" 199214501Srpaulo "\t%s\n", 200214501Srpaulo t.sec, uuid, MAC2STR(dev->mac_addr), dev->device_name, 201214501Srpaulo dev->manufacturer, dev->model_name, dev->model_number, 202214501Srpaulo dev->serial_number, 203214501Srpaulo wps_dev_type_bin2str(dev->pri_dev_type, devtype, 204214501Srpaulo sizeof(devtype))); 205214501Srpaulo fclose(f); 206214501Srpaulo } 207214501Srpaulo} 208214501Srpaulo 209214501Srpaulo 210252726Srpaulostruct wps_stop_reg_data { 211252726Srpaulo struct hostapd_data *current_hapd; 212252726Srpaulo const u8 *uuid_e; 213252726Srpaulo const u8 *dev_pw; 214252726Srpaulo size_t dev_pw_len; 215252726Srpaulo}; 216252726Srpaulo 217252726Srpaulostatic int wps_stop_registrar(struct hostapd_data *hapd, void *ctx) 218252726Srpaulo{ 219252726Srpaulo struct wps_stop_reg_data *data = ctx; 220252726Srpaulo if (hapd != data->current_hapd && hapd->wps != NULL) 221252726Srpaulo wps_registrar_complete(hapd->wps->registrar, data->uuid_e, 222252726Srpaulo data->dev_pw, data->dev_pw_len); 223252726Srpaulo return 0; 224252726Srpaulo} 225252726Srpaulo 226252726Srpaulo 227214501Srpaulostatic void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr, 228252726Srpaulo const u8 *uuid_e, const u8 *dev_pw, 229252726Srpaulo size_t dev_pw_len) 230214501Srpaulo{ 231214501Srpaulo struct hostapd_data *hapd = ctx; 232214501Srpaulo char uuid[40]; 233252726Srpaulo struct wps_stop_reg_data data; 234214501Srpaulo if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) 235214501Srpaulo return; 236214501Srpaulo wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s", 237214501Srpaulo MAC2STR(mac_addr), uuid); 238214501Srpaulo if (hapd->wps_reg_success_cb) 239214501Srpaulo hapd->wps_reg_success_cb(hapd->wps_reg_success_cb_ctx, 240214501Srpaulo mac_addr, uuid_e); 241252726Srpaulo data.current_hapd = hapd; 242252726Srpaulo data.uuid_e = uuid_e; 243252726Srpaulo data.dev_pw = dev_pw; 244252726Srpaulo data.dev_pw_len = dev_pw_len; 245252726Srpaulo hostapd_wps_for_each(hapd, wps_stop_registrar, &data); 246214501Srpaulo} 247214501Srpaulo 248214501Srpaulo 249214501Srpaulostatic void hostapd_wps_enrollee_seen_cb(void *ctx, const u8 *addr, 250214501Srpaulo const u8 *uuid_e, 251214501Srpaulo const u8 *pri_dev_type, 252214501Srpaulo u16 config_methods, 253214501Srpaulo u16 dev_password_id, u8 request_type, 254214501Srpaulo const char *dev_name) 255214501Srpaulo{ 256214501Srpaulo struct hostapd_data *hapd = ctx; 257214501Srpaulo char uuid[40]; 258214501Srpaulo char devtype[WPS_DEV_TYPE_BUFSIZE]; 259214501Srpaulo if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) 260214501Srpaulo return; 261214501Srpaulo if (dev_name == NULL) 262214501Srpaulo dev_name = ""; 263214501Srpaulo wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ENROLLEE_SEEN MACSTR 264214501Srpaulo " %s %s 0x%x %u %u [%s]", 265214501Srpaulo MAC2STR(addr), uuid, 266214501Srpaulo wps_dev_type_bin2str(pri_dev_type, devtype, 267214501Srpaulo sizeof(devtype)), 268214501Srpaulo config_methods, dev_password_id, request_type, dev_name); 269214501Srpaulo} 270214501Srpaulo 271214501Srpaulo 272214501Srpaulostatic void wps_reload_config(void *eloop_data, void *user_ctx) 273214501Srpaulo{ 274214501Srpaulo struct hostapd_iface *iface = eloop_data; 275214501Srpaulo 276214501Srpaulo wpa_printf(MSG_DEBUG, "WPS: Reload configuration data"); 277252726Srpaulo if (iface->interfaces == NULL || 278252726Srpaulo iface->interfaces->reload_config(iface) < 0) { 279214501Srpaulo wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated " 280214501Srpaulo "configuration"); 281214501Srpaulo } 282214501Srpaulo} 283214501Srpaulo 284214501Srpaulo 285281806Srpaulovoid hostapd_wps_eap_completed(struct hostapd_data *hapd) 286281806Srpaulo{ 287281806Srpaulo /* 288281806Srpaulo * Reduce race condition of the station trying to reconnect immediately 289281806Srpaulo * after AP reconfiguration through WPS by rescheduling the reload 290281806Srpaulo * timeout to happen after EAP completion rather than the originally 291281806Srpaulo * scheduled 100 ms after new configuration became known. 292281806Srpaulo */ 293281806Srpaulo if (eloop_deplete_timeout(0, 0, wps_reload_config, hapd->iface, NULL) == 294281806Srpaulo 1) 295281806Srpaulo wpa_printf(MSG_DEBUG, "WPS: Reschedule immediate configuration reload"); 296281806Srpaulo} 297281806Srpaulo 298281806Srpaulo 299252726Srpaulostatic void hapd_new_ap_event(struct hostapd_data *hapd, const u8 *attr, 300252726Srpaulo size_t attr_len) 301214501Srpaulo{ 302252726Srpaulo size_t blen = attr_len * 2 + 1; 303252726Srpaulo char *buf = os_malloc(blen); 304252726Srpaulo if (buf) { 305252726Srpaulo wpa_snprintf_hex(buf, blen, attr, attr_len); 306252726Srpaulo wpa_msg(hapd->msg_ctx, MSG_INFO, 307252726Srpaulo WPS_EVENT_NEW_AP_SETTINGS "%s", buf); 308252726Srpaulo os_free(buf); 309252726Srpaulo } 310252726Srpaulo} 311252726Srpaulo 312252726Srpaulo 313281806Srpaulostatic int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd, 314281806Srpaulo const struct wps_credential *cred) 315281806Srpaulo{ 316281806Srpaulo struct hostapd_bss_config *bss = hapd->conf; 317281806Srpaulo 318281806Srpaulo wpa_printf(MSG_DEBUG, "WPS: Updating in-memory configuration"); 319281806Srpaulo 320281806Srpaulo bss->wps_state = 2; 321289549Srpaulo if (cred->ssid_len <= SSID_MAX_LEN) { 322281806Srpaulo os_memcpy(bss->ssid.ssid, cred->ssid, cred->ssid_len); 323281806Srpaulo bss->ssid.ssid_len = cred->ssid_len; 324281806Srpaulo bss->ssid.ssid_set = 1; 325281806Srpaulo } 326281806Srpaulo 327281806Srpaulo if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) && 328281806Srpaulo (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))) 329281806Srpaulo bss->wpa = 3; 330281806Srpaulo else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) 331281806Srpaulo bss->wpa = 2; 332281806Srpaulo else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)) 333281806Srpaulo bss->wpa = 1; 334281806Srpaulo else 335281806Srpaulo bss->wpa = 0; 336281806Srpaulo 337281806Srpaulo if (bss->wpa) { 338281806Srpaulo if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA)) 339281806Srpaulo bss->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; 340281806Srpaulo if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) 341281806Srpaulo bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; 342281806Srpaulo 343281806Srpaulo bss->wpa_pairwise = 0; 344289549Srpaulo if (cred->encr_type & WPS_ENCR_AES) { 345289549Srpaulo if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD) 346289549Srpaulo bss->wpa_pairwise |= WPA_CIPHER_GCMP; 347289549Srpaulo else 348289549Srpaulo bss->wpa_pairwise |= WPA_CIPHER_CCMP; 349289549Srpaulo } 350281806Srpaulo if (cred->encr_type & WPS_ENCR_TKIP) 351281806Srpaulo bss->wpa_pairwise |= WPA_CIPHER_TKIP; 352281806Srpaulo bss->rsn_pairwise = bss->wpa_pairwise; 353281806Srpaulo bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, 354281806Srpaulo bss->wpa_pairwise, 355281806Srpaulo bss->rsn_pairwise); 356281806Srpaulo 357346981Scy if (hapd->conf->wps_cred_add_sae && 358346981Scy (cred->auth_type & WPS_AUTH_WPA2PSK) && 359346981Scy cred->key_len != 2 * PMK_LEN) { 360346981Scy bss->wpa_key_mgmt |= WPA_KEY_MGMT_SAE; 361346981Scy#ifdef CONFIG_IEEE80211W 362346981Scy if (bss->ieee80211w == NO_MGMT_FRAME_PROTECTION) 363346981Scy bss->ieee80211w = 364346981Scy MGMT_FRAME_PROTECTION_OPTIONAL; 365346981Scy bss->sae_require_mfp = 1; 366346981Scy#endif /* CONFIG_IEEE80211W */ 367346981Scy } 368346981Scy 369281806Srpaulo if (cred->key_len >= 8 && cred->key_len < 64) { 370281806Srpaulo os_free(bss->ssid.wpa_passphrase); 371281806Srpaulo bss->ssid.wpa_passphrase = os_zalloc(cred->key_len + 1); 372281806Srpaulo if (bss->ssid.wpa_passphrase) 373281806Srpaulo os_memcpy(bss->ssid.wpa_passphrase, cred->key, 374281806Srpaulo cred->key_len); 375281806Srpaulo hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk); 376281806Srpaulo } else if (cred->key_len == 64) { 377281806Srpaulo hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk); 378281806Srpaulo bss->ssid.wpa_psk = 379281806Srpaulo os_zalloc(sizeof(struct hostapd_wpa_psk)); 380281806Srpaulo if (bss->ssid.wpa_psk && 381281806Srpaulo hexstr2bin((const char *) cred->key, 382281806Srpaulo bss->ssid.wpa_psk->psk, PMK_LEN) == 0) { 383281806Srpaulo bss->ssid.wpa_psk->group = 1; 384281806Srpaulo os_free(bss->ssid.wpa_passphrase); 385281806Srpaulo bss->ssid.wpa_passphrase = NULL; 386281806Srpaulo } 387281806Srpaulo } 388281806Srpaulo bss->auth_algs = 1; 389281806Srpaulo } else { 390281806Srpaulo /* 391281806Srpaulo * WPS 2.0 does not allow WEP to be configured, so no need to 392281806Srpaulo * process that option here either. 393281806Srpaulo */ 394281806Srpaulo bss->auth_algs = 1; 395281806Srpaulo } 396281806Srpaulo 397281806Srpaulo /* Schedule configuration reload after short period of time to allow 398281806Srpaulo * EAP-WSC to be finished. 399281806Srpaulo */ 400281806Srpaulo eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface, 401281806Srpaulo NULL); 402281806Srpaulo 403281806Srpaulo return 0; 404281806Srpaulo} 405281806Srpaulo 406281806Srpaulo 407252726Srpaulostatic int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) 408252726Srpaulo{ 409252726Srpaulo const struct wps_credential *cred = ctx; 410214501Srpaulo FILE *oconf, *nconf; 411214501Srpaulo size_t len, i; 412214501Srpaulo char *tmp_fname; 413214501Srpaulo char buf[1024]; 414214501Srpaulo int multi_bss; 415214501Srpaulo int wpa; 416346981Scy int pmf_changed = 0; 417214501Srpaulo 418252726Srpaulo if (hapd->wps == NULL) 419252726Srpaulo return 0; 420252726Srpaulo 421214501Srpaulo wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute", 422214501Srpaulo cred->cred_attr, cred->cred_attr_len); 423214501Srpaulo 424214501Srpaulo wpa_printf(MSG_DEBUG, "WPS: Received new AP Settings"); 425214501Srpaulo wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len); 426214501Srpaulo wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x", 427214501Srpaulo cred->auth_type); 428214501Srpaulo wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type); 429214501Srpaulo wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx); 430214501Srpaulo wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", 431214501Srpaulo cred->key, cred->key_len); 432214501Srpaulo wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR, 433214501Srpaulo MAC2STR(cred->mac_addr)); 434214501Srpaulo 435214501Srpaulo if ((hapd->conf->wps_cred_processing == 1 || 436214501Srpaulo hapd->conf->wps_cred_processing == 2) && cred->cred_attr) { 437252726Srpaulo hapd_new_ap_event(hapd, cred->cred_attr, cred->cred_attr_len); 438252726Srpaulo } else if (hapd->conf->wps_cred_processing == 1 || 439252726Srpaulo hapd->conf->wps_cred_processing == 2) { 440252726Srpaulo struct wpabuf *attr; 441252726Srpaulo attr = wpabuf_alloc(200); 442252726Srpaulo if (attr && wps_build_credential_wrap(attr, cred) == 0) 443252726Srpaulo hapd_new_ap_event(hapd, wpabuf_head_u8(attr), 444252726Srpaulo wpabuf_len(attr)); 445252726Srpaulo wpabuf_free(attr); 446214501Srpaulo } else 447214501Srpaulo wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS); 448214501Srpaulo 449214501Srpaulo if (hapd->conf->wps_cred_processing == 1) 450214501Srpaulo return 0; 451214501Srpaulo 452214501Srpaulo os_memcpy(hapd->wps->ssid, cred->ssid, cred->ssid_len); 453214501Srpaulo hapd->wps->ssid_len = cred->ssid_len; 454214501Srpaulo hapd->wps->encr_types = cred->encr_type; 455337817Scy hapd->wps->encr_types_rsn = cred->encr_type; 456337817Scy hapd->wps->encr_types_wpa = cred->encr_type; 457214501Srpaulo hapd->wps->auth_types = cred->auth_type; 458281806Srpaulo hapd->wps->ap_encr_type = cred->encr_type; 459281806Srpaulo hapd->wps->ap_auth_type = cred->auth_type; 460214501Srpaulo if (cred->key_len == 0) { 461214501Srpaulo os_free(hapd->wps->network_key); 462214501Srpaulo hapd->wps->network_key = NULL; 463214501Srpaulo hapd->wps->network_key_len = 0; 464289549Srpaulo } else if ((cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) && 465289549Srpaulo (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN)) { 466289549Srpaulo wpa_printf(MSG_INFO, "WPS: Invalid key length %lu for WPA/WPA2", 467289549Srpaulo (unsigned long) cred->key_len); 468289549Srpaulo return -1; 469214501Srpaulo } else { 470214501Srpaulo if (hapd->wps->network_key == NULL || 471214501Srpaulo hapd->wps->network_key_len < cred->key_len) { 472214501Srpaulo hapd->wps->network_key_len = 0; 473214501Srpaulo os_free(hapd->wps->network_key); 474214501Srpaulo hapd->wps->network_key = os_malloc(cred->key_len); 475214501Srpaulo if (hapd->wps->network_key == NULL) 476214501Srpaulo return -1; 477214501Srpaulo } 478214501Srpaulo hapd->wps->network_key_len = cred->key_len; 479214501Srpaulo os_memcpy(hapd->wps->network_key, cred->key, cred->key_len); 480214501Srpaulo } 481214501Srpaulo hapd->wps->wps_state = WPS_STATE_CONFIGURED; 482214501Srpaulo 483252726Srpaulo if (hapd->iface->config_fname == NULL) 484281806Srpaulo return hapd_wps_reconfig_in_memory(hapd, cred); 485214501Srpaulo len = os_strlen(hapd->iface->config_fname) + 5; 486214501Srpaulo tmp_fname = os_malloc(len); 487214501Srpaulo if (tmp_fname == NULL) 488214501Srpaulo return -1; 489214501Srpaulo os_snprintf(tmp_fname, len, "%s-new", hapd->iface->config_fname); 490214501Srpaulo 491214501Srpaulo oconf = fopen(hapd->iface->config_fname, "r"); 492214501Srpaulo if (oconf == NULL) { 493214501Srpaulo wpa_printf(MSG_WARNING, "WPS: Could not open current " 494214501Srpaulo "configuration file"); 495214501Srpaulo os_free(tmp_fname); 496214501Srpaulo return -1; 497214501Srpaulo } 498214501Srpaulo 499214501Srpaulo nconf = fopen(tmp_fname, "w"); 500214501Srpaulo if (nconf == NULL) { 501214501Srpaulo wpa_printf(MSG_WARNING, "WPS: Could not write updated " 502214501Srpaulo "configuration file"); 503214501Srpaulo os_free(tmp_fname); 504214501Srpaulo fclose(oconf); 505214501Srpaulo return -1; 506214501Srpaulo } 507214501Srpaulo 508214501Srpaulo fprintf(nconf, "# WPS configuration - START\n"); 509214501Srpaulo 510214501Srpaulo fprintf(nconf, "wps_state=2\n"); 511214501Srpaulo 512252726Srpaulo if (is_hex(cred->ssid, cred->ssid_len)) { 513252726Srpaulo fprintf(nconf, "ssid2="); 514252726Srpaulo for (i = 0; i < cred->ssid_len; i++) 515252726Srpaulo fprintf(nconf, "%02x", cred->ssid[i]); 516252726Srpaulo fprintf(nconf, "\n"); 517252726Srpaulo } else { 518252726Srpaulo fprintf(nconf, "ssid="); 519252726Srpaulo for (i = 0; i < cred->ssid_len; i++) 520252726Srpaulo fputc(cred->ssid[i], nconf); 521252726Srpaulo fprintf(nconf, "\n"); 522252726Srpaulo } 523214501Srpaulo 524214501Srpaulo if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) && 525214501Srpaulo (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))) 526214501Srpaulo wpa = 3; 527214501Srpaulo else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) 528214501Srpaulo wpa = 2; 529214501Srpaulo else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)) 530214501Srpaulo wpa = 1; 531214501Srpaulo else 532214501Srpaulo wpa = 0; 533214501Srpaulo 534214501Srpaulo if (wpa) { 535214501Srpaulo char *prefix; 536346981Scy#ifdef CONFIG_IEEE80211W 537346981Scy int sae = 0; 538346981Scy#endif /* CONFIG_IEEE80211W */ 539346981Scy 540214501Srpaulo fprintf(nconf, "wpa=%d\n", wpa); 541214501Srpaulo 542214501Srpaulo fprintf(nconf, "wpa_key_mgmt="); 543214501Srpaulo prefix = ""; 544214501Srpaulo if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA)) { 545214501Srpaulo fprintf(nconf, "WPA-EAP"); 546214501Srpaulo prefix = " "; 547214501Srpaulo } 548346981Scy if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) { 549214501Srpaulo fprintf(nconf, "%sWPA-PSK", prefix); 550346981Scy prefix = " "; 551346981Scy } 552346981Scy if (hapd->conf->wps_cred_add_sae && 553346981Scy (cred->auth_type & WPS_AUTH_WPA2PSK) && 554346981Scy cred->key_len != 2 * PMK_LEN) { 555346981Scy fprintf(nconf, "%sSAE", prefix); 556346981Scy#ifdef CONFIG_IEEE80211W 557346981Scy sae = 1; 558346981Scy#endif /* CONFIG_IEEE80211W */ 559346981Scy } 560214501Srpaulo fprintf(nconf, "\n"); 561214501Srpaulo 562346981Scy#ifdef CONFIG_IEEE80211W 563346981Scy if (sae && hapd->conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) { 564346981Scy fprintf(nconf, "ieee80211w=%d\n", 565346981Scy MGMT_FRAME_PROTECTION_OPTIONAL); 566346981Scy pmf_changed = 1; 567346981Scy } 568346981Scy if (sae) 569346981Scy fprintf(nconf, "sae_require_mfp=1\n"); 570346981Scy#endif /* CONFIG_IEEE80211W */ 571346981Scy 572214501Srpaulo fprintf(nconf, "wpa_pairwise="); 573214501Srpaulo prefix = ""; 574214501Srpaulo if (cred->encr_type & WPS_ENCR_AES) { 575289549Srpaulo if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD) 576289549Srpaulo fprintf(nconf, "GCMP"); 577289549Srpaulo else 578289549Srpaulo fprintf(nconf, "CCMP"); 579289549Srpaulo 580214501Srpaulo prefix = " "; 581214501Srpaulo } 582214501Srpaulo if (cred->encr_type & WPS_ENCR_TKIP) { 583214501Srpaulo fprintf(nconf, "%sTKIP", prefix); 584214501Srpaulo } 585214501Srpaulo fprintf(nconf, "\n"); 586214501Srpaulo 587214501Srpaulo if (cred->key_len >= 8 && cred->key_len < 64) { 588214501Srpaulo fprintf(nconf, "wpa_passphrase="); 589214501Srpaulo for (i = 0; i < cred->key_len; i++) 590214501Srpaulo fputc(cred->key[i], nconf); 591214501Srpaulo fprintf(nconf, "\n"); 592214501Srpaulo } else if (cred->key_len == 64) { 593214501Srpaulo fprintf(nconf, "wpa_psk="); 594214501Srpaulo for (i = 0; i < cred->key_len; i++) 595214501Srpaulo fputc(cred->key[i], nconf); 596214501Srpaulo fprintf(nconf, "\n"); 597214501Srpaulo } else { 598214501Srpaulo wpa_printf(MSG_WARNING, "WPS: Invalid key length %lu " 599214501Srpaulo "for WPA/WPA2", 600214501Srpaulo (unsigned long) cred->key_len); 601214501Srpaulo } 602214501Srpaulo 603214501Srpaulo fprintf(nconf, "auth_algs=1\n"); 604214501Srpaulo } else { 605281806Srpaulo /* 606281806Srpaulo * WPS 2.0 does not allow WEP to be configured, so no need to 607281806Srpaulo * process that option here either. 608281806Srpaulo */ 609281806Srpaulo fprintf(nconf, "auth_algs=1\n"); 610214501Srpaulo } 611214501Srpaulo 612214501Srpaulo fprintf(nconf, "# WPS configuration - END\n"); 613214501Srpaulo 614214501Srpaulo multi_bss = 0; 615214501Srpaulo while (fgets(buf, sizeof(buf), oconf)) { 616214501Srpaulo if (os_strncmp(buf, "bss=", 4) == 0) 617214501Srpaulo multi_bss = 1; 618214501Srpaulo if (!multi_bss && 619214501Srpaulo (str_starts(buf, "ssid=") || 620252726Srpaulo str_starts(buf, "ssid2=") || 621214501Srpaulo str_starts(buf, "auth_algs=") || 622252726Srpaulo str_starts(buf, "wep_default_key=") || 623252726Srpaulo str_starts(buf, "wep_key") || 624214501Srpaulo str_starts(buf, "wps_state=") || 625346981Scy (pmf_changed && str_starts(buf, "ieee80211w=")) || 626214501Srpaulo str_starts(buf, "wpa=") || 627214501Srpaulo str_starts(buf, "wpa_psk=") || 628214501Srpaulo str_starts(buf, "wpa_pairwise=") || 629214501Srpaulo str_starts(buf, "rsn_pairwise=") || 630214501Srpaulo str_starts(buf, "wpa_key_mgmt=") || 631214501Srpaulo str_starts(buf, "wpa_passphrase="))) { 632214501Srpaulo fprintf(nconf, "#WPS# %s", buf); 633214501Srpaulo } else 634214501Srpaulo fprintf(nconf, "%s", buf); 635214501Srpaulo } 636214501Srpaulo 637214501Srpaulo fclose(nconf); 638214501Srpaulo fclose(oconf); 639214501Srpaulo 640214501Srpaulo if (rename(tmp_fname, hapd->iface->config_fname) < 0) { 641214501Srpaulo wpa_printf(MSG_WARNING, "WPS: Failed to rename the updated " 642214501Srpaulo "configuration file: %s", strerror(errno)); 643214501Srpaulo os_free(tmp_fname); 644214501Srpaulo return -1; 645214501Srpaulo } 646214501Srpaulo 647214501Srpaulo os_free(tmp_fname); 648214501Srpaulo 649214501Srpaulo /* Schedule configuration reload after short period of time to allow 650214501Srpaulo * EAP-WSC to be finished. 651214501Srpaulo */ 652214501Srpaulo eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface, 653214501Srpaulo NULL); 654214501Srpaulo 655214501Srpaulo wpa_printf(MSG_DEBUG, "WPS: AP configuration updated"); 656214501Srpaulo 657214501Srpaulo return 0; 658214501Srpaulo} 659214501Srpaulo 660214501Srpaulo 661252726Srpaulostatic int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) 662252726Srpaulo{ 663252726Srpaulo struct hostapd_data *hapd = ctx; 664252726Srpaulo return hostapd_wps_for_each(hapd, hapd_wps_cred_cb, (void *) cred); 665252726Srpaulo} 666252726Srpaulo 667252726Srpaulo 668214501Srpaulostatic void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx) 669214501Srpaulo{ 670214501Srpaulo struct hostapd_data *hapd = eloop_data; 671214501Srpaulo 672214501Srpaulo if (hapd->conf->ap_setup_locked) 673214501Srpaulo return; 674252726Srpaulo if (hapd->ap_pin_failures_consecutive >= 10) 675252726Srpaulo return; 676214501Srpaulo 677214501Srpaulo wpa_printf(MSG_DEBUG, "WPS: Re-enable AP PIN"); 678214501Srpaulo wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED); 679214501Srpaulo hapd->wps->ap_setup_locked = 0; 680214501Srpaulo wps_registrar_update_ie(hapd->wps->registrar); 681214501Srpaulo} 682214501Srpaulo 683214501Srpaulo 684252726Srpaulostatic int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx) 685214501Srpaulo{ 686252726Srpaulo struct wps_event_pwd_auth_fail *data = ctx; 687214501Srpaulo 688252726Srpaulo if (!data->enrollee || hapd->conf->ap_pin == NULL || hapd->wps == NULL) 689252726Srpaulo return 0; 690252726Srpaulo 691214501Srpaulo /* 692214501Srpaulo * Registrar failed to prove its knowledge of the AP PIN. Lock AP setup 693214501Srpaulo * for some time if this happens multiple times to slow down brute 694214501Srpaulo * force attacks. 695214501Srpaulo */ 696214501Srpaulo hapd->ap_pin_failures++; 697252726Srpaulo hapd->ap_pin_failures_consecutive++; 698252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u " 699252726Srpaulo "(%u consecutive)", 700252726Srpaulo hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive); 701214501Srpaulo if (hapd->ap_pin_failures < 3) 702252726Srpaulo return 0; 703214501Srpaulo 704214501Srpaulo wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_LOCKED); 705214501Srpaulo hapd->wps->ap_setup_locked = 1; 706214501Srpaulo 707214501Srpaulo wps_registrar_update_ie(hapd->wps->registrar); 708214501Srpaulo 709252726Srpaulo if (!hapd->conf->ap_setup_locked && 710252726Srpaulo hapd->ap_pin_failures_consecutive >= 10) { 711252726Srpaulo /* 712252726Srpaulo * In indefinite lockdown - disable automatic AP PIN 713252726Srpaulo * reenablement. 714252726Srpaulo */ 715252726Srpaulo eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL); 716252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely"); 717252726Srpaulo } else if (!hapd->conf->ap_setup_locked) { 718214501Srpaulo if (hapd->ap_pin_lockout_time == 0) 719214501Srpaulo hapd->ap_pin_lockout_time = 60; 720214501Srpaulo else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 && 721214501Srpaulo (hapd->ap_pin_failures % 3) == 0) 722214501Srpaulo hapd->ap_pin_lockout_time *= 2; 723214501Srpaulo 724214501Srpaulo wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN for %u seconds", 725214501Srpaulo hapd->ap_pin_lockout_time); 726214501Srpaulo eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL); 727214501Srpaulo eloop_register_timeout(hapd->ap_pin_lockout_time, 0, 728214501Srpaulo hostapd_wps_reenable_ap_pin, hapd, 729214501Srpaulo NULL); 730214501Srpaulo } 731214501Srpaulo 732252726Srpaulo return 0; 733214501Srpaulo} 734214501Srpaulo 735214501Srpaulo 736252726Srpaulostatic void hostapd_pwd_auth_fail(struct hostapd_data *hapd, 737252726Srpaulo struct wps_event_pwd_auth_fail *data) 738252726Srpaulo{ 739281806Srpaulo /* Update WPS Status - Authentication Failure */ 740281806Srpaulo wpa_printf(MSG_DEBUG, "WPS: Authentication failure update"); 741281806Srpaulo hapd->wps_stats.status = WPS_STATUS_FAILURE; 742281806Srpaulo hapd->wps_stats.failure_reason = WPS_EI_AUTH_FAILURE; 743281806Srpaulo os_memcpy(hapd->wps_stats.peer_addr, data->peer_macaddr, ETH_ALEN); 744281806Srpaulo 745252726Srpaulo hostapd_wps_for_each(hapd, wps_pwd_auth_fail, data); 746252726Srpaulo} 747252726Srpaulo 748252726Srpaulo 749252726Srpaulostatic int wps_ap_pin_success(struct hostapd_data *hapd, void *ctx) 750252726Srpaulo{ 751252726Srpaulo if (hapd->conf->ap_pin == NULL || hapd->wps == NULL) 752252726Srpaulo return 0; 753252726Srpaulo 754252726Srpaulo if (hapd->ap_pin_failures_consecutive == 0) 755252726Srpaulo return 0; 756252726Srpaulo 757252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: Clear consecutive AP PIN failure counter " 758252726Srpaulo "- total validation failures %u (%u consecutive)", 759252726Srpaulo hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive); 760252726Srpaulo hapd->ap_pin_failures_consecutive = 0; 761252726Srpaulo 762252726Srpaulo return 0; 763252726Srpaulo} 764252726Srpaulo 765252726Srpaulo 766252726Srpaulostatic void hostapd_wps_ap_pin_success(struct hostapd_data *hapd) 767252726Srpaulo{ 768252726Srpaulo hostapd_wps_for_each(hapd, wps_ap_pin_success, NULL); 769252726Srpaulo} 770252726Srpaulo 771252726Srpaulo 772281806Srpaulostatic void hostapd_wps_event_pbc_overlap(struct hostapd_data *hapd) 773281806Srpaulo{ 774281806Srpaulo /* Update WPS Status - PBC Overlap */ 775281806Srpaulo hapd->wps_stats.pbc_status = WPS_PBC_STATUS_OVERLAP; 776281806Srpaulo} 777252726Srpaulo 778281806Srpaulo 779281806Srpaulostatic void hostapd_wps_event_pbc_timeout(struct hostapd_data *hapd) 780281806Srpaulo{ 781281806Srpaulo /* Update WPS PBC Status:PBC Timeout */ 782281806Srpaulo hapd->wps_stats.pbc_status = WPS_PBC_STATUS_TIMEOUT; 783281806Srpaulo} 784281806Srpaulo 785281806Srpaulo 786281806Srpaulostatic void hostapd_wps_event_pbc_active(struct hostapd_data *hapd) 787281806Srpaulo{ 788281806Srpaulo /* Update WPS PBC status - Active */ 789281806Srpaulo hapd->wps_stats.pbc_status = WPS_PBC_STATUS_ACTIVE; 790281806Srpaulo} 791281806Srpaulo 792281806Srpaulo 793281806Srpaulostatic void hostapd_wps_event_pbc_disable(struct hostapd_data *hapd) 794281806Srpaulo{ 795281806Srpaulo /* Update WPS PBC status - Active */ 796281806Srpaulo hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE; 797281806Srpaulo} 798281806Srpaulo 799281806Srpaulo 800281806Srpaulostatic void hostapd_wps_event_success(struct hostapd_data *hapd, 801281806Srpaulo struct wps_event_success *success) 802281806Srpaulo{ 803281806Srpaulo /* Update WPS status - Success */ 804281806Srpaulo hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE; 805281806Srpaulo hapd->wps_stats.status = WPS_STATUS_SUCCESS; 806281806Srpaulo os_memcpy(hapd->wps_stats.peer_addr, success->peer_macaddr, ETH_ALEN); 807281806Srpaulo} 808281806Srpaulo 809281806Srpaulo 810252726Srpaulostatic void hostapd_wps_event_fail(struct hostapd_data *hapd, 811252726Srpaulo struct wps_event_fail *fail) 812252726Srpaulo{ 813281806Srpaulo /* Update WPS status - Failure */ 814281806Srpaulo hapd->wps_stats.status = WPS_STATUS_FAILURE; 815281806Srpaulo os_memcpy(hapd->wps_stats.peer_addr, fail->peer_macaddr, ETH_ALEN); 816281806Srpaulo 817281806Srpaulo hapd->wps_stats.failure_reason = fail->error_indication; 818281806Srpaulo 819252726Srpaulo if (fail->error_indication > 0 && 820252726Srpaulo fail->error_indication < NUM_WPS_EI_VALUES) { 821252726Srpaulo wpa_msg(hapd->msg_ctx, MSG_INFO, 822252726Srpaulo WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)", 823252726Srpaulo fail->msg, fail->config_error, fail->error_indication, 824281806Srpaulo wps_ei_str(fail->error_indication)); 825252726Srpaulo } else { 826252726Srpaulo wpa_msg(hapd->msg_ctx, MSG_INFO, 827252726Srpaulo WPS_EVENT_FAIL "msg=%d config_error=%d", 828252726Srpaulo fail->msg, fail->config_error); 829252726Srpaulo } 830252726Srpaulo} 831252726Srpaulo 832252726Srpaulo 833214501Srpaulostatic void hostapd_wps_event_cb(void *ctx, enum wps_event event, 834214501Srpaulo union wps_event_data *data) 835214501Srpaulo{ 836214501Srpaulo struct hostapd_data *hapd = ctx; 837214501Srpaulo 838252726Srpaulo switch (event) { 839252726Srpaulo case WPS_EV_M2D: 840252726Srpaulo wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_M2D); 841252726Srpaulo break; 842252726Srpaulo case WPS_EV_FAIL: 843252726Srpaulo hostapd_wps_event_fail(hapd, &data->fail); 844252726Srpaulo break; 845252726Srpaulo case WPS_EV_SUCCESS: 846281806Srpaulo hostapd_wps_event_success(hapd, &data->success); 847252726Srpaulo wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_SUCCESS); 848252726Srpaulo break; 849252726Srpaulo case WPS_EV_PWD_AUTH_FAIL: 850214501Srpaulo hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail); 851252726Srpaulo break; 852252726Srpaulo case WPS_EV_PBC_OVERLAP: 853281806Srpaulo hostapd_wps_event_pbc_overlap(hapd); 854252726Srpaulo wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_OVERLAP); 855252726Srpaulo break; 856252726Srpaulo case WPS_EV_PBC_TIMEOUT: 857281806Srpaulo hostapd_wps_event_pbc_timeout(hapd); 858252726Srpaulo wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT); 859252726Srpaulo break; 860281806Srpaulo case WPS_EV_PBC_ACTIVE: 861281806Srpaulo hostapd_wps_event_pbc_active(hapd); 862281806Srpaulo wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ACTIVE); 863281806Srpaulo break; 864281806Srpaulo case WPS_EV_PBC_DISABLE: 865281806Srpaulo hostapd_wps_event_pbc_disable(hapd); 866281806Srpaulo wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_DISABLE); 867281806Srpaulo break; 868252726Srpaulo case WPS_EV_ER_AP_ADD: 869252726Srpaulo break; 870252726Srpaulo case WPS_EV_ER_AP_REMOVE: 871252726Srpaulo break; 872252726Srpaulo case WPS_EV_ER_ENROLLEE_ADD: 873252726Srpaulo break; 874252726Srpaulo case WPS_EV_ER_ENROLLEE_REMOVE: 875252726Srpaulo break; 876252726Srpaulo case WPS_EV_ER_AP_SETTINGS: 877252726Srpaulo break; 878252726Srpaulo case WPS_EV_ER_SET_SELECTED_REGISTRAR: 879252726Srpaulo break; 880252726Srpaulo case WPS_EV_AP_PIN_SUCCESS: 881252726Srpaulo hostapd_wps_ap_pin_success(hapd); 882252726Srpaulo break; 883252726Srpaulo } 884252726Srpaulo if (hapd->wps_event_cb) 885252726Srpaulo hapd->wps_event_cb(hapd->wps_event_cb_ctx, event, data); 886214501Srpaulo} 887214501Srpaulo 888214501Srpaulo 889281806Srpaulostatic int hostapd_wps_rf_band_cb(void *ctx) 890214501Srpaulo{ 891281806Srpaulo struct hostapd_data *hapd = ctx; 892281806Srpaulo 893281806Srpaulo return hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? 894289549Srpaulo WPS_RF_50GHZ : 895289549Srpaulo hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ? 896289549Srpaulo WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ 897281806Srpaulo} 898281806Srpaulo 899281806Srpaulo 900281806Srpaulostatic void hostapd_wps_clear_ies(struct hostapd_data *hapd, int deinit_only) 901281806Srpaulo{ 902214501Srpaulo wpabuf_free(hapd->wps_beacon_ie); 903214501Srpaulo hapd->wps_beacon_ie = NULL; 904214501Srpaulo 905214501Srpaulo wpabuf_free(hapd->wps_probe_resp_ie); 906214501Srpaulo hapd->wps_probe_resp_ie = NULL; 907214501Srpaulo 908289549Srpaulo if (deinit_only) { 909337817Scy if (hapd->drv_priv) 910337817Scy hostapd_reset_ap_wps_ie(hapd); 911281806Srpaulo return; 912289549Srpaulo } 913281806Srpaulo 914252726Srpaulo hostapd_set_ap_wps_ie(hapd); 915214501Srpaulo} 916214501Srpaulo 917214501Srpaulo 918252726Srpaulostatic int get_uuid_cb(struct hostapd_iface *iface, void *ctx) 919252726Srpaulo{ 920252726Srpaulo const u8 **uuid = ctx; 921252726Srpaulo size_t j; 922252726Srpaulo 923252726Srpaulo if (iface == NULL) 924252726Srpaulo return 0; 925252726Srpaulo for (j = 0; j < iface->num_bss; j++) { 926252726Srpaulo struct hostapd_data *hapd = iface->bss[j]; 927281806Srpaulo if (hapd->wps && !hapd->conf->wps_independent && 928281806Srpaulo !is_nil_uuid(hapd->wps->uuid)) { 929252726Srpaulo *uuid = hapd->wps->uuid; 930252726Srpaulo return 1; 931252726Srpaulo } 932252726Srpaulo } 933252726Srpaulo 934252726Srpaulo return 0; 935252726Srpaulo} 936252726Srpaulo 937252726Srpaulo 938252726Srpaulostatic const u8 * get_own_uuid(struct hostapd_iface *iface) 939252726Srpaulo{ 940252726Srpaulo const u8 *uuid; 941252726Srpaulo if (iface->interfaces == NULL || 942252726Srpaulo iface->interfaces->for_each_interface == NULL) 943252726Srpaulo return NULL; 944252726Srpaulo uuid = NULL; 945252726Srpaulo iface->interfaces->for_each_interface(iface->interfaces, get_uuid_cb, 946252726Srpaulo &uuid); 947252726Srpaulo return uuid; 948252726Srpaulo} 949252726Srpaulo 950252726Srpaulo 951252726Srpaulostatic int count_interface_cb(struct hostapd_iface *iface, void *ctx) 952252726Srpaulo{ 953252726Srpaulo int *count= ctx; 954252726Srpaulo (*count)++; 955252726Srpaulo return 0; 956252726Srpaulo} 957252726Srpaulo 958252726Srpaulo 959252726Srpaulostatic int interface_count(struct hostapd_iface *iface) 960252726Srpaulo{ 961252726Srpaulo int count = 0; 962252726Srpaulo if (iface->interfaces == NULL || 963252726Srpaulo iface->interfaces->for_each_interface == NULL) 964252726Srpaulo return 0; 965252726Srpaulo iface->interfaces->for_each_interface(iface->interfaces, 966252726Srpaulo count_interface_cb, &count); 967252726Srpaulo return count; 968252726Srpaulo} 969252726Srpaulo 970252726Srpaulo 971252726Srpaulostatic int hostapd_wps_set_vendor_ext(struct hostapd_data *hapd, 972252726Srpaulo struct wps_context *wps) 973252726Srpaulo{ 974252726Srpaulo int i; 975252726Srpaulo 976252726Srpaulo for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) { 977252726Srpaulo wpabuf_free(wps->dev.vendor_ext[i]); 978252726Srpaulo wps->dev.vendor_ext[i] = NULL; 979252726Srpaulo 980252726Srpaulo if (hapd->conf->wps_vendor_ext[i] == NULL) 981252726Srpaulo continue; 982252726Srpaulo 983252726Srpaulo wps->dev.vendor_ext[i] = 984252726Srpaulo wpabuf_dup(hapd->conf->wps_vendor_ext[i]); 985252726Srpaulo if (wps->dev.vendor_ext[i] == NULL) { 986252726Srpaulo while (--i >= 0) 987252726Srpaulo wpabuf_free(wps->dev.vendor_ext[i]); 988252726Srpaulo return -1; 989252726Srpaulo } 990252726Srpaulo } 991252726Srpaulo 992252726Srpaulo return 0; 993252726Srpaulo} 994252726Srpaulo 995252726Srpaulo 996281806Srpaulostatic void hostapd_free_wps(struct wps_context *wps) 997281806Srpaulo{ 998281806Srpaulo int i; 999281806Srpaulo 1000281806Srpaulo for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) 1001281806Srpaulo wpabuf_free(wps->dev.vendor_ext[i]); 1002281806Srpaulo wps_device_data_free(&wps->dev); 1003281806Srpaulo os_free(wps->network_key); 1004281806Srpaulo hostapd_wps_nfc_clear(wps); 1005281806Srpaulo wpabuf_free(wps->dh_pubkey); 1006281806Srpaulo wpabuf_free(wps->dh_privkey); 1007281806Srpaulo os_free(wps); 1008281806Srpaulo} 1009281806Srpaulo 1010281806Srpaulo 1011214501Srpauloint hostapd_init_wps(struct hostapd_data *hapd, 1012214501Srpaulo struct hostapd_bss_config *conf) 1013214501Srpaulo{ 1014214501Srpaulo struct wps_context *wps; 1015214501Srpaulo struct wps_registrar_config cfg; 1016346981Scy u8 *multi_ap_netw_key = NULL; 1017214501Srpaulo 1018214501Srpaulo if (conf->wps_state == 0) { 1019281806Srpaulo hostapd_wps_clear_ies(hapd, 0); 1020214501Srpaulo return 0; 1021214501Srpaulo } 1022214501Srpaulo 1023214501Srpaulo wps = os_zalloc(sizeof(*wps)); 1024214501Srpaulo if (wps == NULL) 1025214501Srpaulo return -1; 1026214501Srpaulo 1027214501Srpaulo wps->cred_cb = hostapd_wps_cred_cb; 1028214501Srpaulo wps->event_cb = hostapd_wps_event_cb; 1029281806Srpaulo wps->rf_band_cb = hostapd_wps_rf_band_cb; 1030214501Srpaulo wps->cb_ctx = hapd; 1031214501Srpaulo 1032214501Srpaulo os_memset(&cfg, 0, sizeof(cfg)); 1033214501Srpaulo wps->wps_state = hapd->conf->wps_state; 1034214501Srpaulo wps->ap_setup_locked = hapd->conf->ap_setup_locked; 1035214501Srpaulo if (is_nil_uuid(hapd->conf->uuid)) { 1036252726Srpaulo const u8 *uuid; 1037252726Srpaulo uuid = get_own_uuid(hapd->iface); 1038281806Srpaulo if (uuid && !conf->wps_independent) { 1039252726Srpaulo os_memcpy(wps->uuid, uuid, UUID_LEN); 1040252726Srpaulo wpa_hexdump(MSG_DEBUG, "WPS: Clone UUID from another " 1041252726Srpaulo "interface", wps->uuid, UUID_LEN); 1042252726Srpaulo } else { 1043252726Srpaulo uuid_gen_mac_addr(hapd->own_addr, wps->uuid); 1044252726Srpaulo wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC " 1045252726Srpaulo "address", wps->uuid, UUID_LEN); 1046252726Srpaulo } 1047252726Srpaulo } else { 1048252726Srpaulo os_memcpy(wps->uuid, hapd->conf->uuid, UUID_LEN); 1049252726Srpaulo wpa_hexdump(MSG_DEBUG, "WPS: Use configured UUID", 1050214501Srpaulo wps->uuid, UUID_LEN); 1051252726Srpaulo } 1052214501Srpaulo wps->ssid_len = hapd->conf->ssid.ssid_len; 1053214501Srpaulo os_memcpy(wps->ssid, hapd->conf->ssid.ssid, wps->ssid_len); 1054214501Srpaulo wps->ap = 1; 1055214501Srpaulo os_memcpy(wps->dev.mac_addr, hapd->own_addr, ETH_ALEN); 1056214501Srpaulo wps->dev.device_name = hapd->conf->device_name ? 1057214501Srpaulo os_strdup(hapd->conf->device_name) : NULL; 1058214501Srpaulo wps->dev.manufacturer = hapd->conf->manufacturer ? 1059214501Srpaulo os_strdup(hapd->conf->manufacturer) : NULL; 1060214501Srpaulo wps->dev.model_name = hapd->conf->model_name ? 1061214501Srpaulo os_strdup(hapd->conf->model_name) : NULL; 1062214501Srpaulo wps->dev.model_number = hapd->conf->model_number ? 1063214501Srpaulo os_strdup(hapd->conf->model_number) : NULL; 1064214501Srpaulo wps->dev.serial_number = hapd->conf->serial_number ? 1065214501Srpaulo os_strdup(hapd->conf->serial_number) : NULL; 1066214501Srpaulo wps->config_methods = 1067214501Srpaulo wps_config_methods_str2bin(hapd->conf->config_methods); 1068252726Srpaulo if ((wps->config_methods & 1069252726Srpaulo (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY | 1070252726Srpaulo WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) { 1071252726Srpaulo wpa_printf(MSG_INFO, "WPS: Converting display to " 1072252726Srpaulo "virtual_display for WPS 2.0 compliance"); 1073252726Srpaulo wps->config_methods |= WPS_CONFIG_VIRT_DISPLAY; 1074252726Srpaulo } 1075252726Srpaulo if ((wps->config_methods & 1076252726Srpaulo (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON | 1077252726Srpaulo WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) { 1078252726Srpaulo wpa_printf(MSG_INFO, "WPS: Converting push_button to " 1079252726Srpaulo "virtual_push_button for WPS 2.0 compliance"); 1080252726Srpaulo wps->config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON; 1081252726Srpaulo } 1082252726Srpaulo os_memcpy(wps->dev.pri_dev_type, hapd->conf->device_type, 1083252726Srpaulo WPS_DEV_TYPE_LEN); 1084252726Srpaulo 1085281806Srpaulo if (hostapd_wps_set_vendor_ext(hapd, wps) < 0) 1086281806Srpaulo goto fail; 1087252726Srpaulo 1088214501Srpaulo wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version); 1089214501Srpaulo 1090252726Srpaulo if (conf->wps_rf_bands) { 1091252726Srpaulo wps->dev.rf_bands = conf->wps_rf_bands; 1092252726Srpaulo } else { 1093252726Srpaulo wps->dev.rf_bands = 1094252726Srpaulo hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? 1095289549Srpaulo WPS_RF_50GHZ : 1096289549Srpaulo hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ? 1097289549Srpaulo WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ 1098252726Srpaulo } 1099252726Srpaulo 1100214501Srpaulo if (conf->wpa & WPA_PROTO_RSN) { 1101214501Srpaulo if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) 1102214501Srpaulo wps->auth_types |= WPS_AUTH_WPA2PSK; 1103214501Srpaulo if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) 1104214501Srpaulo wps->auth_types |= WPS_AUTH_WPA2; 1105214501Srpaulo 1106346981Scy if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | 1107346981Scy WPA_CIPHER_CCMP_256 | 1108346981Scy WPA_CIPHER_GCMP_256)) { 1109214501Srpaulo wps->encr_types |= WPS_ENCR_AES; 1110337817Scy wps->encr_types_rsn |= WPS_ENCR_AES; 1111337817Scy } 1112337817Scy if (conf->rsn_pairwise & WPA_CIPHER_TKIP) { 1113214501Srpaulo wps->encr_types |= WPS_ENCR_TKIP; 1114337817Scy wps->encr_types_rsn |= WPS_ENCR_TKIP; 1115337817Scy } 1116214501Srpaulo } 1117214501Srpaulo 1118214501Srpaulo if (conf->wpa & WPA_PROTO_WPA) { 1119214501Srpaulo if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) 1120214501Srpaulo wps->auth_types |= WPS_AUTH_WPAPSK; 1121214501Srpaulo if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) 1122214501Srpaulo wps->auth_types |= WPS_AUTH_WPA; 1123214501Srpaulo 1124337817Scy if (conf->wpa_pairwise & WPA_CIPHER_CCMP) { 1125214501Srpaulo wps->encr_types |= WPS_ENCR_AES; 1126337817Scy wps->encr_types_wpa |= WPS_ENCR_AES; 1127337817Scy } 1128337817Scy if (conf->wpa_pairwise & WPA_CIPHER_TKIP) { 1129214501Srpaulo wps->encr_types |= WPS_ENCR_TKIP; 1130337817Scy wps->encr_types_wpa |= WPS_ENCR_TKIP; 1131337817Scy } 1132214501Srpaulo } 1133214501Srpaulo 1134214501Srpaulo if (conf->ssid.security_policy == SECURITY_PLAINTEXT) { 1135214501Srpaulo wps->encr_types |= WPS_ENCR_NONE; 1136214501Srpaulo wps->auth_types |= WPS_AUTH_OPEN; 1137214501Srpaulo } 1138214501Srpaulo 1139214501Srpaulo if (conf->ssid.wpa_psk_file) { 1140214501Srpaulo /* Use per-device PSKs */ 1141214501Srpaulo } else if (conf->ssid.wpa_passphrase) { 1142214501Srpaulo wps->network_key = (u8 *) os_strdup(conf->ssid.wpa_passphrase); 1143214501Srpaulo wps->network_key_len = os_strlen(conf->ssid.wpa_passphrase); 1144214501Srpaulo } else if (conf->ssid.wpa_psk) { 1145214501Srpaulo wps->network_key = os_malloc(2 * PMK_LEN + 1); 1146281806Srpaulo if (wps->network_key == NULL) 1147281806Srpaulo goto fail; 1148214501Srpaulo wpa_snprintf_hex((char *) wps->network_key, 2 * PMK_LEN + 1, 1149214501Srpaulo conf->ssid.wpa_psk->psk, PMK_LEN); 1150214501Srpaulo wps->network_key_len = 2 * PMK_LEN; 1151214501Srpaulo } else if (conf->ssid.wep.keys_set && conf->ssid.wep.key[0]) { 1152214501Srpaulo wps->network_key = os_malloc(conf->ssid.wep.len[0]); 1153281806Srpaulo if (wps->network_key == NULL) 1154281806Srpaulo goto fail; 1155214501Srpaulo os_memcpy(wps->network_key, conf->ssid.wep.key[0], 1156214501Srpaulo conf->ssid.wep.len[0]); 1157214501Srpaulo wps->network_key_len = conf->ssid.wep.len[0]; 1158214501Srpaulo } 1159214501Srpaulo 1160214501Srpaulo if (conf->ssid.wpa_psk) { 1161214501Srpaulo os_memcpy(wps->psk, conf->ssid.wpa_psk->psk, PMK_LEN); 1162214501Srpaulo wps->psk_set = 1; 1163214501Srpaulo } 1164214501Srpaulo 1165281806Srpaulo wps->ap_auth_type = wps->auth_types; 1166281806Srpaulo wps->ap_encr_type = wps->encr_types; 1167214501Srpaulo if (conf->wps_state == WPS_STATE_NOT_CONFIGURED) { 1168214501Srpaulo /* Override parameters to enable security by default */ 1169214501Srpaulo wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK; 1170214501Srpaulo wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP; 1171337817Scy wps->encr_types_rsn = WPS_ENCR_AES | WPS_ENCR_TKIP; 1172337817Scy wps->encr_types_wpa = WPS_ENCR_AES | WPS_ENCR_TKIP; 1173214501Srpaulo } 1174214501Srpaulo 1175346981Scy if ((hapd->conf->multi_ap & FRONTHAUL_BSS) && 1176346981Scy hapd->conf->multi_ap_backhaul_ssid.ssid_len) { 1177346981Scy cfg.multi_ap_backhaul_ssid_len = 1178346981Scy hapd->conf->multi_ap_backhaul_ssid.ssid_len; 1179346981Scy cfg.multi_ap_backhaul_ssid = 1180346981Scy hapd->conf->multi_ap_backhaul_ssid.ssid; 1181346981Scy 1182346981Scy if (conf->multi_ap_backhaul_ssid.wpa_passphrase) { 1183346981Scy cfg.multi_ap_backhaul_network_key = (const u8 *) 1184346981Scy conf->multi_ap_backhaul_ssid.wpa_passphrase; 1185346981Scy cfg.multi_ap_backhaul_network_key_len = 1186346981Scy os_strlen(conf->multi_ap_backhaul_ssid.wpa_passphrase); 1187346981Scy } else if (conf->multi_ap_backhaul_ssid.wpa_psk) { 1188346981Scy multi_ap_netw_key = os_malloc(2 * PMK_LEN + 1); 1189346981Scy if (!multi_ap_netw_key) 1190346981Scy goto fail; 1191346981Scy wpa_snprintf_hex((char *) multi_ap_netw_key, 1192346981Scy 2 * PMK_LEN + 1, 1193346981Scy conf->multi_ap_backhaul_ssid.wpa_psk->psk, 1194346981Scy PMK_LEN); 1195346981Scy cfg.multi_ap_backhaul_network_key = multi_ap_netw_key; 1196346981Scy cfg.multi_ap_backhaul_network_key_len = 2 * PMK_LEN; 1197346981Scy } 1198346981Scy } 1199346981Scy 1200214501Srpaulo wps->ap_settings = conf->ap_settings; 1201214501Srpaulo wps->ap_settings_len = conf->ap_settings_len; 1202214501Srpaulo 1203214501Srpaulo cfg.new_psk_cb = hostapd_wps_new_psk_cb; 1204214501Srpaulo cfg.set_ie_cb = hostapd_wps_set_ie_cb; 1205214501Srpaulo cfg.pin_needed_cb = hostapd_wps_pin_needed_cb; 1206214501Srpaulo cfg.reg_success_cb = hostapd_wps_reg_success_cb; 1207214501Srpaulo cfg.enrollee_seen_cb = hostapd_wps_enrollee_seen_cb; 1208214501Srpaulo cfg.cb_ctx = hapd; 1209214501Srpaulo cfg.skip_cred_build = conf->skip_cred_build; 1210214501Srpaulo cfg.extra_cred = conf->extra_cred; 1211214501Srpaulo cfg.extra_cred_len = conf->extra_cred_len; 1212214501Srpaulo cfg.disable_auto_conf = (hapd->conf->wps_cred_processing == 1) && 1213214501Srpaulo conf->skip_cred_build; 1214214501Srpaulo if (conf->ssid.security_policy == SECURITY_STATIC_WEP) 1215214501Srpaulo cfg.static_wep_only = 1; 1216252726Srpaulo cfg.dualband = interface_count(hapd->iface) > 1; 1217252726Srpaulo if ((wps->dev.rf_bands & (WPS_RF_50GHZ | WPS_RF_24GHZ)) == 1218252726Srpaulo (WPS_RF_50GHZ | WPS_RF_24GHZ)) 1219252726Srpaulo cfg.dualband = 1; 1220252726Srpaulo if (cfg.dualband) 1221252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: Dualband AP"); 1222281806Srpaulo cfg.force_per_enrollee_psk = conf->force_per_enrollee_psk; 1223214501Srpaulo 1224214501Srpaulo wps->registrar = wps_registrar_init(wps, &cfg); 1225214501Srpaulo if (wps->registrar == NULL) { 1226252726Srpaulo wpa_printf(MSG_ERROR, "Failed to initialize WPS Registrar"); 1227281806Srpaulo goto fail; 1228214501Srpaulo } 1229214501Srpaulo 1230214501Srpaulo#ifdef CONFIG_WPS_UPNP 1231214501Srpaulo wps->friendly_name = hapd->conf->friendly_name; 1232214501Srpaulo wps->manufacturer_url = hapd->conf->manufacturer_url; 1233214501Srpaulo wps->model_description = hapd->conf->model_description; 1234214501Srpaulo wps->model_url = hapd->conf->model_url; 1235214501Srpaulo wps->upc = hapd->conf->upc; 1236252726Srpaulo#endif /* CONFIG_WPS_UPNP */ 1237214501Srpaulo 1238252726Srpaulo hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd); 1239252726Srpaulo 1240252726Srpaulo hapd->wps = wps; 1241346981Scy bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN); 1242252726Srpaulo 1243252726Srpaulo return 0; 1244281806Srpaulo 1245281806Srpaulofail: 1246346981Scy bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN); 1247281806Srpaulo hostapd_free_wps(wps); 1248281806Srpaulo return -1; 1249252726Srpaulo} 1250252726Srpaulo 1251252726Srpaulo 1252252726Srpauloint hostapd_init_wps_complete(struct hostapd_data *hapd) 1253252726Srpaulo{ 1254252726Srpaulo struct wps_context *wps = hapd->wps; 1255252726Srpaulo 1256252726Srpaulo if (wps == NULL) 1257252726Srpaulo return 0; 1258252726Srpaulo 1259252726Srpaulo#ifdef CONFIG_WPS_UPNP 1260214501Srpaulo if (hostapd_wps_upnp_init(hapd, wps) < 0) { 1261214501Srpaulo wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP"); 1262214501Srpaulo wps_registrar_deinit(wps->registrar); 1263281806Srpaulo hostapd_free_wps(wps); 1264252726Srpaulo hapd->wps = NULL; 1265214501Srpaulo return -1; 1266214501Srpaulo } 1267214501Srpaulo#endif /* CONFIG_WPS_UPNP */ 1268214501Srpaulo 1269252726Srpaulo return 0; 1270252726Srpaulo} 1271214501Srpaulo 1272214501Srpaulo 1273252726Srpaulostatic void hostapd_wps_nfc_clear(struct wps_context *wps) 1274252726Srpaulo{ 1275252726Srpaulo#ifdef CONFIG_WPS_NFC 1276281806Srpaulo wpa_printf(MSG_DEBUG, "WPS: Clear NFC Tag context %p", wps); 1277252726Srpaulo wps->ap_nfc_dev_pw_id = 0; 1278252726Srpaulo wpabuf_free(wps->ap_nfc_dh_pubkey); 1279252726Srpaulo wps->ap_nfc_dh_pubkey = NULL; 1280252726Srpaulo wpabuf_free(wps->ap_nfc_dh_privkey); 1281252726Srpaulo wps->ap_nfc_dh_privkey = NULL; 1282252726Srpaulo wpabuf_free(wps->ap_nfc_dev_pw); 1283252726Srpaulo wps->ap_nfc_dev_pw = NULL; 1284252726Srpaulo#endif /* CONFIG_WPS_NFC */ 1285214501Srpaulo} 1286214501Srpaulo 1287214501Srpaulo 1288214501Srpaulovoid hostapd_deinit_wps(struct hostapd_data *hapd) 1289214501Srpaulo{ 1290214501Srpaulo eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL); 1291214501Srpaulo eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL); 1292281806Srpaulo eloop_cancel_timeout(wps_reload_config, hapd->iface, NULL); 1293281806Srpaulo if (hapd->wps == NULL) { 1294281806Srpaulo hostapd_wps_clear_ies(hapd, 1); 1295214501Srpaulo return; 1296281806Srpaulo } 1297214501Srpaulo#ifdef CONFIG_WPS_UPNP 1298214501Srpaulo hostapd_wps_upnp_deinit(hapd); 1299214501Srpaulo#endif /* CONFIG_WPS_UPNP */ 1300214501Srpaulo wps_registrar_deinit(hapd->wps->registrar); 1301214501Srpaulo wps_free_pending_msgs(hapd->wps->upnp_msgs); 1302281806Srpaulo hostapd_free_wps(hapd->wps); 1303214501Srpaulo hapd->wps = NULL; 1304281806Srpaulo hostapd_wps_clear_ies(hapd, 1); 1305214501Srpaulo} 1306214501Srpaulo 1307214501Srpaulo 1308214501Srpaulovoid hostapd_update_wps(struct hostapd_data *hapd) 1309214501Srpaulo{ 1310214501Srpaulo if (hapd->wps == NULL) 1311214501Srpaulo return; 1312252726Srpaulo 1313252726Srpaulo#ifdef CONFIG_WPS_UPNP 1314252726Srpaulo hapd->wps->friendly_name = hapd->conf->friendly_name; 1315252726Srpaulo hapd->wps->manufacturer_url = hapd->conf->manufacturer_url; 1316252726Srpaulo hapd->wps->model_description = hapd->conf->model_description; 1317252726Srpaulo hapd->wps->model_url = hapd->conf->model_url; 1318252726Srpaulo hapd->wps->upc = hapd->conf->upc; 1319252726Srpaulo#endif /* CONFIG_WPS_UPNP */ 1320252726Srpaulo 1321252726Srpaulo hostapd_wps_set_vendor_ext(hapd, hapd->wps); 1322252726Srpaulo 1323214501Srpaulo if (hapd->conf->wps_state) 1324214501Srpaulo wps_registrar_update_ie(hapd->wps->registrar); 1325214501Srpaulo else 1326214501Srpaulo hostapd_deinit_wps(hapd); 1327214501Srpaulo} 1328214501Srpaulo 1329214501Srpaulo 1330252726Srpaulostruct wps_add_pin_data { 1331252726Srpaulo const u8 *addr; 1332252726Srpaulo const u8 *uuid; 1333252726Srpaulo const u8 *pin; 1334252726Srpaulo size_t pin_len; 1335252726Srpaulo int timeout; 1336252726Srpaulo int added; 1337252726Srpaulo}; 1338252726Srpaulo 1339252726Srpaulo 1340252726Srpaulostatic int wps_add_pin(struct hostapd_data *hapd, void *ctx) 1341214501Srpaulo{ 1342252726Srpaulo struct wps_add_pin_data *data = ctx; 1343252726Srpaulo int ret; 1344252726Srpaulo 1345252726Srpaulo if (hapd->wps == NULL) 1346252726Srpaulo return 0; 1347252726Srpaulo ret = wps_registrar_add_pin(hapd->wps->registrar, data->addr, 1348252726Srpaulo data->uuid, data->pin, data->pin_len, 1349252726Srpaulo data->timeout); 1350252726Srpaulo if (ret == 0) 1351252726Srpaulo data->added++; 1352252726Srpaulo return ret; 1353252726Srpaulo} 1354252726Srpaulo 1355252726Srpaulo 1356252726Srpauloint hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr, 1357252726Srpaulo const char *uuid, const char *pin, int timeout) 1358252726Srpaulo{ 1359214501Srpaulo u8 u[UUID_LEN]; 1360252726Srpaulo struct wps_add_pin_data data; 1361214501Srpaulo 1362252726Srpaulo data.addr = addr; 1363252726Srpaulo data.uuid = u; 1364252726Srpaulo data.pin = (const u8 *) pin; 1365252726Srpaulo data.pin_len = os_strlen(pin); 1366252726Srpaulo data.timeout = timeout; 1367252726Srpaulo data.added = 0; 1368252726Srpaulo 1369214501Srpaulo if (os_strcmp(uuid, "any") == 0) 1370252726Srpaulo data.uuid = NULL; 1371252726Srpaulo else { 1372252726Srpaulo if (uuid_str2bin(uuid, u)) 1373252726Srpaulo return -1; 1374252726Srpaulo data.uuid = u; 1375252726Srpaulo } 1376252726Srpaulo if (hostapd_wps_for_each(hapd, wps_add_pin, &data) < 0) 1377214501Srpaulo return -1; 1378252726Srpaulo return data.added ? 0 : -1; 1379214501Srpaulo} 1380214501Srpaulo 1381214501Srpaulo 1382289549Srpaulostruct wps_button_pushed_ctx { 1383289549Srpaulo const u8 *p2p_dev_addr; 1384289549Srpaulo unsigned int count; 1385289549Srpaulo}; 1386289549Srpaulo 1387252726Srpaulostatic int wps_button_pushed(struct hostapd_data *hapd, void *ctx) 1388214501Srpaulo{ 1389289549Srpaulo struct wps_button_pushed_ctx *data = ctx; 1390289549Srpaulo 1391289549Srpaulo if (hapd->wps) { 1392289549Srpaulo data->count++; 1393289549Srpaulo return wps_registrar_button_pushed(hapd->wps->registrar, 1394289549Srpaulo data->p2p_dev_addr); 1395289549Srpaulo } 1396289549Srpaulo 1397289549Srpaulo return 0; 1398214501Srpaulo} 1399214501Srpaulo 1400214501Srpaulo 1401252726Srpauloint hostapd_wps_button_pushed(struct hostapd_data *hapd, 1402252726Srpaulo const u8 *p2p_dev_addr) 1403214501Srpaulo{ 1404289549Srpaulo struct wps_button_pushed_ctx ctx; 1405289549Srpaulo int ret; 1406289549Srpaulo 1407289549Srpaulo os_memset(&ctx, 0, sizeof(ctx)); 1408289549Srpaulo ctx.p2p_dev_addr = p2p_dev_addr; 1409289549Srpaulo ret = hostapd_wps_for_each(hapd, wps_button_pushed, &ctx); 1410289549Srpaulo if (ret == 0 && !ctx.count) 1411289549Srpaulo ret = -1; 1412289549Srpaulo return ret; 1413252726Srpaulo} 1414214501Srpaulo 1415214501Srpaulo 1416289549Srpaulostruct wps_cancel_ctx { 1417289549Srpaulo unsigned int count; 1418289549Srpaulo}; 1419289549Srpaulo 1420252726Srpaulostatic int wps_cancel(struct hostapd_data *hapd, void *ctx) 1421252726Srpaulo{ 1422289549Srpaulo struct wps_cancel_ctx *data = ctx; 1423214501Srpaulo 1424289549Srpaulo if (hapd->wps) { 1425289549Srpaulo data->count++; 1426289549Srpaulo wps_registrar_wps_cancel(hapd->wps->registrar); 1427289549Srpaulo ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL); 1428289549Srpaulo } 1429214501Srpaulo 1430214501Srpaulo return 0; 1431252726Srpaulo} 1432214501Srpaulo 1433252726Srpaulo 1434252726Srpauloint hostapd_wps_cancel(struct hostapd_data *hapd) 1435252726Srpaulo{ 1436289549Srpaulo struct wps_cancel_ctx ctx; 1437289549Srpaulo int ret; 1438289549Srpaulo 1439289549Srpaulo os_memset(&ctx, 0, sizeof(ctx)); 1440289549Srpaulo ret = hostapd_wps_for_each(hapd, wps_cancel, &ctx); 1441289549Srpaulo if (ret == 0 && !ctx.count) 1442289549Srpaulo ret = -1; 1443289549Srpaulo return ret; 1444214501Srpaulo} 1445214501Srpaulo 1446214501Srpaulo 1447252726Srpaulostatic int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da, 1448252726Srpaulo const u8 *bssid, 1449252726Srpaulo const u8 *ie, size_t ie_len, 1450252726Srpaulo int ssi_signal) 1451214501Srpaulo{ 1452214501Srpaulo struct hostapd_data *hapd = ctx; 1453214501Srpaulo struct wpabuf *wps_ie; 1454214501Srpaulo struct ieee802_11_elems elems; 1455214501Srpaulo 1456214501Srpaulo if (hapd->wps == NULL) 1457214501Srpaulo return 0; 1458214501Srpaulo 1459214501Srpaulo if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) { 1460214501Srpaulo wpa_printf(MSG_DEBUG, "WPS: Could not parse ProbeReq from " 1461214501Srpaulo MACSTR, MAC2STR(addr)); 1462214501Srpaulo return 0; 1463214501Srpaulo } 1464214501Srpaulo 1465214501Srpaulo if (elems.ssid && elems.ssid_len > 0 && 1466214501Srpaulo (elems.ssid_len != hapd->conf->ssid.ssid_len || 1467214501Srpaulo os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) != 1468214501Srpaulo 0)) 1469214501Srpaulo return 0; /* Not for us */ 1470214501Srpaulo 1471214501Srpaulo wps_ie = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA); 1472214501Srpaulo if (wps_ie == NULL) 1473214501Srpaulo return 0; 1474252726Srpaulo if (wps_validate_probe_req(wps_ie, addr) < 0) { 1475252726Srpaulo wpabuf_free(wps_ie); 1476252726Srpaulo return 0; 1477252726Srpaulo } 1478214501Srpaulo 1479214501Srpaulo if (wpabuf_len(wps_ie) > 0) { 1480252726Srpaulo int p2p_wildcard = 0; 1481252726Srpaulo#ifdef CONFIG_P2P 1482252726Srpaulo if (elems.ssid && elems.ssid_len == P2P_WILDCARD_SSID_LEN && 1483252726Srpaulo os_memcmp(elems.ssid, P2P_WILDCARD_SSID, 1484252726Srpaulo P2P_WILDCARD_SSID_LEN) == 0) 1485252726Srpaulo p2p_wildcard = 1; 1486252726Srpaulo#endif /* CONFIG_P2P */ 1487252726Srpaulo wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie, 1488252726Srpaulo p2p_wildcard); 1489214501Srpaulo#ifdef CONFIG_WPS_UPNP 1490214501Srpaulo /* FIX: what exactly should be included in the WLANEvent? 1491214501Srpaulo * WPS attributes? Full ProbeReq frame? */ 1492252726Srpaulo if (!p2p_wildcard) 1493252726Srpaulo upnp_wps_device_send_wlan_event( 1494252726Srpaulo hapd->wps_upnp, addr, 1495252726Srpaulo UPNP_WPS_WLANEVENT_TYPE_PROBE, wps_ie); 1496214501Srpaulo#endif /* CONFIG_WPS_UPNP */ 1497214501Srpaulo } 1498214501Srpaulo 1499214501Srpaulo wpabuf_free(wps_ie); 1500214501Srpaulo 1501214501Srpaulo return 0; 1502214501Srpaulo} 1503214501Srpaulo 1504214501Srpaulo 1505214501Srpaulo#ifdef CONFIG_WPS_UPNP 1506214501Srpaulo 1507214501Srpaulostatic int hostapd_rx_req_put_wlan_response( 1508214501Srpaulo void *priv, enum upnp_wps_wlanevent_type ev_type, 1509214501Srpaulo const u8 *mac_addr, const struct wpabuf *msg, 1510214501Srpaulo enum wps_msg_type msg_type) 1511214501Srpaulo{ 1512214501Srpaulo struct hostapd_data *hapd = priv; 1513214501Srpaulo struct sta_info *sta; 1514214501Srpaulo struct upnp_pending_message *p; 1515214501Srpaulo 1516214501Srpaulo wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse ev_type=%d mac_addr=" 1517214501Srpaulo MACSTR, ev_type, MAC2STR(mac_addr)); 1518214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "WPS UPnP: PutWLANResponse NewMessage", 1519214501Srpaulo wpabuf_head(msg), wpabuf_len(msg)); 1520214501Srpaulo if (ev_type != UPNP_WPS_WLANEVENT_TYPE_EAP) { 1521214501Srpaulo wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored unexpected " 1522214501Srpaulo "PutWLANResponse WLANEventType %d", ev_type); 1523214501Srpaulo return -1; 1524214501Srpaulo } 1525214501Srpaulo 1526214501Srpaulo /* 1527214501Srpaulo * EAP response to ongoing to WPS Registration. Send it to EAP-WSC 1528214501Srpaulo * server implementation for delivery to the peer. 1529214501Srpaulo */ 1530214501Srpaulo 1531214501Srpaulo sta = ap_get_sta(hapd, mac_addr); 1532252726Srpaulo#ifndef CONFIG_WPS_STRICT 1533214501Srpaulo if (!sta) { 1534214501Srpaulo /* 1535214501Srpaulo * Workaround - Intel wsccmd uses bogus NewWLANEventMAC: 1536214501Srpaulo * Pick STA that is in an ongoing WPS registration without 1537214501Srpaulo * checking the MAC address. 1538214501Srpaulo */ 1539214501Srpaulo wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found based " 1540214501Srpaulo "on NewWLANEventMAC; try wildcard match"); 1541214501Srpaulo for (sta = hapd->sta_list; sta; sta = sta->next) { 1542214501Srpaulo if (sta->eapol_sm && (sta->flags & WLAN_STA_WPS)) 1543214501Srpaulo break; 1544214501Srpaulo } 1545214501Srpaulo } 1546252726Srpaulo#endif /* CONFIG_WPS_STRICT */ 1547214501Srpaulo 1548252726Srpaulo if (!sta || !(sta->flags & WLAN_STA_WPS)) { 1549214501Srpaulo wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found"); 1550214501Srpaulo return 0; 1551214501Srpaulo } 1552214501Srpaulo 1553281806Srpaulo if (!sta->eapol_sm) { 1554281806Srpaulo /* 1555281806Srpaulo * This can happen, e.g., if an ER sends an extra message after 1556281806Srpaulo * the station has disassociated (but not fully 1557281806Srpaulo * deauthenticated). 1558281806Srpaulo */ 1559281806Srpaulo wpa_printf(MSG_DEBUG, "WPS UPnP: Matching STA did not have EAPOL state machine initialized"); 1560281806Srpaulo return 0; 1561281806Srpaulo } 1562281806Srpaulo 1563214501Srpaulo p = os_zalloc(sizeof(*p)); 1564214501Srpaulo if (p == NULL) 1565214501Srpaulo return -1; 1566214501Srpaulo os_memcpy(p->addr, sta->addr, ETH_ALEN); 1567214501Srpaulo p->msg = wpabuf_dup(msg); 1568214501Srpaulo p->type = msg_type; 1569214501Srpaulo p->next = hapd->wps->upnp_msgs; 1570214501Srpaulo hapd->wps->upnp_msgs = p; 1571214501Srpaulo 1572214501Srpaulo return eapol_auth_eap_pending_cb(sta->eapol_sm, sta->eapol_sm->eap); 1573214501Srpaulo} 1574214501Srpaulo 1575214501Srpaulo 1576214501Srpaulostatic int hostapd_wps_upnp_init(struct hostapd_data *hapd, 1577214501Srpaulo struct wps_context *wps) 1578214501Srpaulo{ 1579214501Srpaulo struct upnp_wps_device_ctx *ctx; 1580214501Srpaulo 1581214501Srpaulo if (!hapd->conf->upnp_iface) 1582214501Srpaulo return 0; 1583214501Srpaulo ctx = os_zalloc(sizeof(*ctx)); 1584214501Srpaulo if (ctx == NULL) 1585214501Srpaulo return -1; 1586214501Srpaulo 1587214501Srpaulo ctx->rx_req_put_wlan_response = hostapd_rx_req_put_wlan_response; 1588214501Srpaulo if (hapd->conf->ap_pin) 1589214501Srpaulo ctx->ap_pin = os_strdup(hapd->conf->ap_pin); 1590214501Srpaulo 1591252726Srpaulo hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd, 1592252726Srpaulo hapd->conf->upnp_iface); 1593252726Srpaulo if (hapd->wps_upnp == NULL) 1594214501Srpaulo return -1; 1595214501Srpaulo wps->wps_upnp = hapd->wps_upnp; 1596214501Srpaulo 1597214501Srpaulo return 0; 1598214501Srpaulo} 1599214501Srpaulo 1600214501Srpaulo 1601214501Srpaulostatic void hostapd_wps_upnp_deinit(struct hostapd_data *hapd) 1602214501Srpaulo{ 1603252726Srpaulo upnp_wps_device_deinit(hapd->wps_upnp, hapd); 1604214501Srpaulo} 1605214501Srpaulo 1606214501Srpaulo#endif /* CONFIG_WPS_UPNP */ 1607214501Srpaulo 1608214501Srpaulo 1609214501Srpauloint hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr, 1610214501Srpaulo char *buf, size_t buflen) 1611214501Srpaulo{ 1612214501Srpaulo if (hapd->wps == NULL) 1613214501Srpaulo return 0; 1614214501Srpaulo return wps_registrar_get_info(hapd->wps->registrar, addr, buf, buflen); 1615214501Srpaulo} 1616214501Srpaulo 1617214501Srpaulo 1618214501Srpaulostatic void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx) 1619214501Srpaulo{ 1620214501Srpaulo struct hostapd_data *hapd = eloop_data; 1621214501Srpaulo wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out"); 1622214501Srpaulo hostapd_wps_ap_pin_disable(hapd); 1623252726Srpaulo wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_PIN_DISABLED); 1624214501Srpaulo} 1625214501Srpaulo 1626214501Srpaulo 1627214501Srpaulostatic void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout) 1628214501Srpaulo{ 1629214501Srpaulo wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout); 1630214501Srpaulo hapd->ap_pin_failures = 0; 1631252726Srpaulo hapd->ap_pin_failures_consecutive = 0; 1632214501Srpaulo hapd->conf->ap_setup_locked = 0; 1633214501Srpaulo if (hapd->wps->ap_setup_locked) { 1634214501Srpaulo wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED); 1635214501Srpaulo hapd->wps->ap_setup_locked = 0; 1636214501Srpaulo wps_registrar_update_ie(hapd->wps->registrar); 1637214501Srpaulo } 1638214501Srpaulo eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL); 1639214501Srpaulo if (timeout > 0) 1640214501Srpaulo eloop_register_timeout(timeout, 0, 1641214501Srpaulo hostapd_wps_ap_pin_timeout, hapd, NULL); 1642214501Srpaulo} 1643214501Srpaulo 1644214501Srpaulo 1645252726Srpaulostatic int wps_ap_pin_disable(struct hostapd_data *hapd, void *ctx) 1646214501Srpaulo{ 1647214501Srpaulo os_free(hapd->conf->ap_pin); 1648214501Srpaulo hapd->conf->ap_pin = NULL; 1649214501Srpaulo#ifdef CONFIG_WPS_UPNP 1650214501Srpaulo upnp_wps_set_ap_pin(hapd->wps_upnp, NULL); 1651214501Srpaulo#endif /* CONFIG_WPS_UPNP */ 1652214501Srpaulo eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL); 1653252726Srpaulo return 0; 1654214501Srpaulo} 1655214501Srpaulo 1656214501Srpaulo 1657252726Srpaulovoid hostapd_wps_ap_pin_disable(struct hostapd_data *hapd) 1658214501Srpaulo{ 1659252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN"); 1660252726Srpaulo hostapd_wps_for_each(hapd, wps_ap_pin_disable, NULL); 1661252726Srpaulo} 1662252726Srpaulo 1663252726Srpaulo 1664252726Srpaulostruct wps_ap_pin_data { 1665214501Srpaulo char pin_txt[9]; 1666252726Srpaulo int timeout; 1667252726Srpaulo}; 1668214501Srpaulo 1669252726Srpaulo 1670252726Srpaulostatic int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx) 1671252726Srpaulo{ 1672252726Srpaulo struct wps_ap_pin_data *data = ctx; 1673289549Srpaulo 1674289549Srpaulo if (!hapd->wps) 1675289549Srpaulo return 0; 1676289549Srpaulo 1677214501Srpaulo os_free(hapd->conf->ap_pin); 1678252726Srpaulo hapd->conf->ap_pin = os_strdup(data->pin_txt); 1679214501Srpaulo#ifdef CONFIG_WPS_UPNP 1680252726Srpaulo upnp_wps_set_ap_pin(hapd->wps_upnp, data->pin_txt); 1681214501Srpaulo#endif /* CONFIG_WPS_UPNP */ 1682252726Srpaulo hostapd_wps_ap_pin_enable(hapd, data->timeout); 1683252726Srpaulo return 0; 1684252726Srpaulo} 1685252726Srpaulo 1686252726Srpaulo 1687252726Srpauloconst char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout) 1688252726Srpaulo{ 1689252726Srpaulo unsigned int pin; 1690252726Srpaulo struct wps_ap_pin_data data; 1691252726Srpaulo 1692337817Scy if (wps_generate_pin(&pin) < 0) 1693337817Scy return NULL; 1694252726Srpaulo os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin); 1695252726Srpaulo data.timeout = timeout; 1696252726Srpaulo hostapd_wps_for_each(hapd, wps_ap_pin_set, &data); 1697214501Srpaulo return hapd->conf->ap_pin; 1698214501Srpaulo} 1699214501Srpaulo 1700214501Srpaulo 1701214501Srpauloconst char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd) 1702214501Srpaulo{ 1703214501Srpaulo return hapd->conf->ap_pin; 1704214501Srpaulo} 1705214501Srpaulo 1706214501Srpaulo 1707214501Srpauloint hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin, 1708214501Srpaulo int timeout) 1709214501Srpaulo{ 1710252726Srpaulo struct wps_ap_pin_data data; 1711252726Srpaulo int ret; 1712252726Srpaulo 1713252726Srpaulo ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin); 1714281806Srpaulo if (os_snprintf_error(sizeof(data.pin_txt), ret)) 1715214501Srpaulo return -1; 1716252726Srpaulo data.timeout = timeout; 1717252726Srpaulo return hostapd_wps_for_each(hapd, wps_ap_pin_set, &data); 1718252726Srpaulo} 1719252726Srpaulo 1720252726Srpaulo 1721252726Srpaulostatic int wps_update_ie(struct hostapd_data *hapd, void *ctx) 1722252726Srpaulo{ 1723252726Srpaulo if (hapd->wps) 1724252726Srpaulo wps_registrar_update_ie(hapd->wps->registrar); 1725214501Srpaulo return 0; 1726214501Srpaulo} 1727252726Srpaulo 1728252726Srpaulo 1729252726Srpaulovoid hostapd_wps_update_ie(struct hostapd_data *hapd) 1730252726Srpaulo{ 1731252726Srpaulo hostapd_wps_for_each(hapd, wps_update_ie, NULL); 1732252726Srpaulo} 1733252726Srpaulo 1734252726Srpaulo 1735252726Srpauloint hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid, 1736252726Srpaulo const char *auth, const char *encr, const char *key) 1737252726Srpaulo{ 1738252726Srpaulo struct wps_credential cred; 1739252726Srpaulo size_t len; 1740252726Srpaulo 1741252726Srpaulo os_memset(&cred, 0, sizeof(cred)); 1742252726Srpaulo 1743252726Srpaulo len = os_strlen(ssid); 1744252726Srpaulo if ((len & 1) || len > 2 * sizeof(cred.ssid) || 1745252726Srpaulo hexstr2bin(ssid, cred.ssid, len / 2)) 1746252726Srpaulo return -1; 1747252726Srpaulo cred.ssid_len = len / 2; 1748252726Srpaulo 1749252726Srpaulo if (os_strncmp(auth, "OPEN", 4) == 0) 1750252726Srpaulo cred.auth_type = WPS_AUTH_OPEN; 1751252726Srpaulo else if (os_strncmp(auth, "WPAPSK", 6) == 0) 1752252726Srpaulo cred.auth_type = WPS_AUTH_WPAPSK; 1753252726Srpaulo else if (os_strncmp(auth, "WPA2PSK", 7) == 0) 1754252726Srpaulo cred.auth_type = WPS_AUTH_WPA2PSK; 1755252726Srpaulo else 1756252726Srpaulo return -1; 1757252726Srpaulo 1758252726Srpaulo if (encr) { 1759252726Srpaulo if (os_strncmp(encr, "NONE", 4) == 0) 1760252726Srpaulo cred.encr_type = WPS_ENCR_NONE; 1761252726Srpaulo else if (os_strncmp(encr, "TKIP", 4) == 0) 1762252726Srpaulo cred.encr_type = WPS_ENCR_TKIP; 1763252726Srpaulo else if (os_strncmp(encr, "CCMP", 4) == 0) 1764252726Srpaulo cred.encr_type = WPS_ENCR_AES; 1765252726Srpaulo else 1766252726Srpaulo return -1; 1767252726Srpaulo } else 1768252726Srpaulo cred.encr_type = WPS_ENCR_NONE; 1769252726Srpaulo 1770252726Srpaulo if (key) { 1771252726Srpaulo len = os_strlen(key); 1772252726Srpaulo if ((len & 1) || len > 2 * sizeof(cred.key) || 1773252726Srpaulo hexstr2bin(key, cred.key, len / 2)) 1774252726Srpaulo return -1; 1775252726Srpaulo cred.key_len = len / 2; 1776252726Srpaulo } 1777252726Srpaulo 1778252726Srpaulo return wps_registrar_config_ap(hapd->wps->registrar, &cred); 1779252726Srpaulo} 1780252726Srpaulo 1781252726Srpaulo 1782252726Srpaulo#ifdef CONFIG_WPS_NFC 1783252726Srpaulo 1784252726Srpaulostruct wps_nfc_password_token_data { 1785252726Srpaulo const u8 *oob_dev_pw; 1786252726Srpaulo size_t oob_dev_pw_len; 1787252726Srpaulo int added; 1788252726Srpaulo}; 1789252726Srpaulo 1790252726Srpaulo 1791252726Srpaulostatic int wps_add_nfc_password_token(struct hostapd_data *hapd, void *ctx) 1792252726Srpaulo{ 1793252726Srpaulo struct wps_nfc_password_token_data *data = ctx; 1794252726Srpaulo int ret; 1795252726Srpaulo 1796252726Srpaulo if (hapd->wps == NULL) 1797252726Srpaulo return 0; 1798252726Srpaulo ret = wps_registrar_add_nfc_password_token(hapd->wps->registrar, 1799252726Srpaulo data->oob_dev_pw, 1800252726Srpaulo data->oob_dev_pw_len); 1801252726Srpaulo if (ret == 0) 1802252726Srpaulo data->added++; 1803252726Srpaulo return ret; 1804252726Srpaulo} 1805252726Srpaulo 1806252726Srpaulo 1807252726Srpaulostatic int hostapd_wps_add_nfc_password_token(struct hostapd_data *hapd, 1808252726Srpaulo struct wps_parse_attr *attr) 1809252726Srpaulo{ 1810252726Srpaulo struct wps_nfc_password_token_data data; 1811252726Srpaulo 1812252726Srpaulo data.oob_dev_pw = attr->oob_dev_password; 1813252726Srpaulo data.oob_dev_pw_len = attr->oob_dev_password_len; 1814252726Srpaulo data.added = 0; 1815252726Srpaulo if (hostapd_wps_for_each(hapd, wps_add_nfc_password_token, &data) < 0) 1816252726Srpaulo return -1; 1817252726Srpaulo return data.added ? 0 : -1; 1818252726Srpaulo} 1819252726Srpaulo 1820252726Srpaulo 1821252726Srpaulostatic int hostapd_wps_nfc_tag_process(struct hostapd_data *hapd, 1822252726Srpaulo const struct wpabuf *wps) 1823252726Srpaulo{ 1824252726Srpaulo struct wps_parse_attr attr; 1825252726Srpaulo 1826252726Srpaulo wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps); 1827252726Srpaulo 1828252726Srpaulo if (wps_parse_msg(wps, &attr)) { 1829252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag"); 1830252726Srpaulo return -1; 1831252726Srpaulo } 1832252726Srpaulo 1833252726Srpaulo if (attr.oob_dev_password) 1834252726Srpaulo return hostapd_wps_add_nfc_password_token(hapd, &attr); 1835252726Srpaulo 1836252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag"); 1837252726Srpaulo return -1; 1838252726Srpaulo} 1839252726Srpaulo 1840252726Srpaulo 1841252726Srpauloint hostapd_wps_nfc_tag_read(struct hostapd_data *hapd, 1842252726Srpaulo const struct wpabuf *data) 1843252726Srpaulo{ 1844252726Srpaulo const struct wpabuf *wps = data; 1845252726Srpaulo struct wpabuf *tmp = NULL; 1846252726Srpaulo int ret; 1847252726Srpaulo 1848252726Srpaulo if (wpabuf_len(data) < 4) 1849252726Srpaulo return -1; 1850252726Srpaulo 1851252726Srpaulo if (*wpabuf_head_u8(data) != 0x10) { 1852252726Srpaulo /* Assume this contains full NDEF record */ 1853252726Srpaulo tmp = ndef_parse_wifi(data); 1854252726Srpaulo if (tmp == NULL) { 1855252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF"); 1856252726Srpaulo return -1; 1857252726Srpaulo } 1858252726Srpaulo wps = tmp; 1859252726Srpaulo } 1860252726Srpaulo 1861252726Srpaulo ret = hostapd_wps_nfc_tag_process(hapd, wps); 1862252726Srpaulo wpabuf_free(tmp); 1863252726Srpaulo return ret; 1864252726Srpaulo} 1865252726Srpaulo 1866252726Srpaulo 1867252726Srpaulostruct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd, 1868252726Srpaulo int ndef) 1869252726Srpaulo{ 1870252726Srpaulo struct wpabuf *ret; 1871252726Srpaulo 1872252726Srpaulo if (hapd->wps == NULL) 1873252726Srpaulo return NULL; 1874252726Srpaulo 1875281806Srpaulo ret = wps_get_oob_cred(hapd->wps, hostapd_wps_rf_band_cb(hapd), 1876281806Srpaulo hapd->iconf->channel); 1877252726Srpaulo if (ndef && ret) { 1878252726Srpaulo struct wpabuf *tmp; 1879252726Srpaulo tmp = ndef_build_wifi(ret); 1880252726Srpaulo wpabuf_free(ret); 1881252726Srpaulo if (tmp == NULL) 1882252726Srpaulo return NULL; 1883252726Srpaulo ret = tmp; 1884252726Srpaulo } 1885252726Srpaulo 1886252726Srpaulo return ret; 1887252726Srpaulo} 1888252726Srpaulo 1889252726Srpaulo 1890281806Srpaulostruct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef) 1891281806Srpaulo{ 1892281806Srpaulo struct wpabuf *ret; 1893281806Srpaulo 1894281806Srpaulo if (hapd->wps == NULL) 1895281806Srpaulo return NULL; 1896281806Srpaulo 1897281806Srpaulo if (hapd->conf->wps_nfc_dh_pubkey == NULL) { 1898281806Srpaulo struct wps_context *wps = hapd->wps; 1899281806Srpaulo if (wps_nfc_gen_dh(&hapd->conf->wps_nfc_dh_pubkey, 1900281806Srpaulo &hapd->conf->wps_nfc_dh_privkey) < 0) 1901281806Srpaulo return NULL; 1902281806Srpaulo hostapd_wps_nfc_clear(wps); 1903281806Srpaulo wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER; 1904281806Srpaulo wps->ap_nfc_dh_pubkey = 1905281806Srpaulo wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey); 1906281806Srpaulo wps->ap_nfc_dh_privkey = 1907281806Srpaulo wpabuf_dup(hapd->conf->wps_nfc_dh_privkey); 1908281806Srpaulo if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) { 1909281806Srpaulo hostapd_wps_nfc_clear(wps); 1910281806Srpaulo return NULL; 1911281806Srpaulo } 1912281806Srpaulo } 1913281806Srpaulo 1914281806Srpaulo ret = wps_build_nfc_handover_sel(hapd->wps, 1915281806Srpaulo hapd->conf->wps_nfc_dh_pubkey, 1916281806Srpaulo hapd->own_addr, hapd->iface->freq); 1917281806Srpaulo 1918281806Srpaulo if (ndef && ret) { 1919281806Srpaulo struct wpabuf *tmp; 1920281806Srpaulo tmp = ndef_build_wifi(ret); 1921281806Srpaulo wpabuf_free(ret); 1922281806Srpaulo if (tmp == NULL) 1923281806Srpaulo return NULL; 1924281806Srpaulo ret = tmp; 1925281806Srpaulo } 1926281806Srpaulo 1927281806Srpaulo return ret; 1928281806Srpaulo} 1929281806Srpaulo 1930281806Srpaulo 1931281806Srpauloint hostapd_wps_nfc_report_handover(struct hostapd_data *hapd, 1932281806Srpaulo const struct wpabuf *req, 1933281806Srpaulo const struct wpabuf *sel) 1934281806Srpaulo{ 1935281806Srpaulo struct wpabuf *wps; 1936281806Srpaulo int ret = -1; 1937281806Srpaulo u16 wsc_len; 1938281806Srpaulo const u8 *pos; 1939281806Srpaulo struct wpabuf msg; 1940281806Srpaulo struct wps_parse_attr attr; 1941281806Srpaulo u16 dev_pw_id; 1942281806Srpaulo 1943281806Srpaulo /* 1944281806Srpaulo * Enrollee/station is always initiator of the NFC connection handover, 1945281806Srpaulo * so use the request message here to find Enrollee public key hash. 1946281806Srpaulo */ 1947281806Srpaulo wps = ndef_parse_wifi(req); 1948281806Srpaulo if (wps == NULL) 1949281806Srpaulo return -1; 1950281806Srpaulo wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc " 1951281806Srpaulo "payload from NFC connection handover"); 1952281806Srpaulo wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps); 1953281806Srpaulo if (wpabuf_len(wps) < 2) { 1954281806Srpaulo wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request " 1955281806Srpaulo "Message"); 1956281806Srpaulo goto out; 1957281806Srpaulo } 1958281806Srpaulo pos = wpabuf_head(wps); 1959281806Srpaulo wsc_len = WPA_GET_BE16(pos); 1960281806Srpaulo if (wsc_len > wpabuf_len(wps) - 2) { 1961281806Srpaulo wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) " 1962281806Srpaulo "in rt Wi-Fi Handover Request Message", wsc_len); 1963281806Srpaulo goto out; 1964281806Srpaulo } 1965281806Srpaulo pos += 2; 1966281806Srpaulo 1967281806Srpaulo wpa_hexdump(MSG_DEBUG, 1968281806Srpaulo "WPS: WSC attributes in Wi-Fi Handover Request Message", 1969281806Srpaulo pos, wsc_len); 1970281806Srpaulo if (wsc_len < wpabuf_len(wps) - 2) { 1971281806Srpaulo wpa_hexdump(MSG_DEBUG, 1972281806Srpaulo "WPS: Ignore extra data after WSC attributes", 1973281806Srpaulo pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len); 1974281806Srpaulo } 1975281806Srpaulo 1976281806Srpaulo wpabuf_set(&msg, pos, wsc_len); 1977281806Srpaulo ret = wps_parse_msg(&msg, &attr); 1978281806Srpaulo if (ret < 0) { 1979281806Srpaulo wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in " 1980281806Srpaulo "Wi-Fi Handover Request Message"); 1981281806Srpaulo goto out; 1982281806Srpaulo } 1983281806Srpaulo 1984281806Srpaulo if (attr.oob_dev_password == NULL || 1985281806Srpaulo attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) { 1986281806Srpaulo wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password " 1987281806Srpaulo "included in Wi-Fi Handover Request Message"); 1988281806Srpaulo ret = -1; 1989281806Srpaulo goto out; 1990281806Srpaulo } 1991281806Srpaulo 1992281806Srpaulo if (attr.uuid_e == NULL) { 1993281806Srpaulo wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi " 1994281806Srpaulo "Handover Request Message"); 1995281806Srpaulo ret = -1; 1996281806Srpaulo goto out; 1997281806Srpaulo } 1998281806Srpaulo 1999281806Srpaulo wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN); 2000281806Srpaulo 2001281806Srpaulo wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password", 2002281806Srpaulo attr.oob_dev_password, attr.oob_dev_password_len); 2003281806Srpaulo dev_pw_id = WPA_GET_BE16(attr.oob_dev_password + 2004281806Srpaulo WPS_OOB_PUBKEY_HASH_LEN); 2005281806Srpaulo if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) { 2006281806Srpaulo wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID " 2007281806Srpaulo "%u in Wi-Fi Handover Request Message", dev_pw_id); 2008281806Srpaulo ret = -1; 2009281806Srpaulo goto out; 2010281806Srpaulo } 2011281806Srpaulo wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash", 2012281806Srpaulo attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN); 2013281806Srpaulo 2014281806Srpaulo ret = wps_registrar_add_nfc_pw_token(hapd->wps->registrar, 2015281806Srpaulo attr.oob_dev_password, 2016281806Srpaulo DEV_PW_NFC_CONNECTION_HANDOVER, 2017281806Srpaulo NULL, 0, 1); 2018281806Srpaulo 2019281806Srpauloout: 2020281806Srpaulo wpabuf_free(wps); 2021281806Srpaulo return ret; 2022281806Srpaulo} 2023281806Srpaulo 2024281806Srpaulo 2025252726Srpaulostruct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef) 2026252726Srpaulo{ 2027281806Srpaulo if (hapd->conf->wps_nfc_pw_from_config) { 2028281806Srpaulo return wps_nfc_token_build(ndef, 2029281806Srpaulo hapd->conf->wps_nfc_dev_pw_id, 2030281806Srpaulo hapd->conf->wps_nfc_dh_pubkey, 2031281806Srpaulo hapd->conf->wps_nfc_dev_pw); 2032281806Srpaulo } 2033281806Srpaulo 2034252726Srpaulo return wps_nfc_token_gen(ndef, &hapd->conf->wps_nfc_dev_pw_id, 2035252726Srpaulo &hapd->conf->wps_nfc_dh_pubkey, 2036252726Srpaulo &hapd->conf->wps_nfc_dh_privkey, 2037252726Srpaulo &hapd->conf->wps_nfc_dev_pw); 2038252726Srpaulo} 2039252726Srpaulo 2040252726Srpaulo 2041252726Srpauloint hostapd_wps_nfc_token_enable(struct hostapd_data *hapd) 2042252726Srpaulo{ 2043252726Srpaulo struct wps_context *wps = hapd->wps; 2044281806Srpaulo struct wpabuf *pw; 2045252726Srpaulo 2046252726Srpaulo if (wps == NULL) 2047252726Srpaulo return -1; 2048252726Srpaulo 2049252726Srpaulo if (!hapd->conf->wps_nfc_dh_pubkey || 2050252726Srpaulo !hapd->conf->wps_nfc_dh_privkey || 2051252726Srpaulo !hapd->conf->wps_nfc_dev_pw || 2052252726Srpaulo !hapd->conf->wps_nfc_dev_pw_id) 2053252726Srpaulo return -1; 2054252726Srpaulo 2055252726Srpaulo hostapd_wps_nfc_clear(wps); 2056281806Srpaulo wpa_printf(MSG_DEBUG, 2057281806Srpaulo "WPS: Enable NFC Tag (Dev Pw Id %u) for AP interface %s (context %p)", 2058281806Srpaulo hapd->conf->wps_nfc_dev_pw_id, hapd->conf->iface, wps); 2059252726Srpaulo wps->ap_nfc_dev_pw_id = hapd->conf->wps_nfc_dev_pw_id; 2060252726Srpaulo wps->ap_nfc_dh_pubkey = wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey); 2061252726Srpaulo wps->ap_nfc_dh_privkey = wpabuf_dup(hapd->conf->wps_nfc_dh_privkey); 2062281806Srpaulo pw = hapd->conf->wps_nfc_dev_pw; 2063281806Srpaulo wps->ap_nfc_dev_pw = wpabuf_alloc( 2064281806Srpaulo wpabuf_len(pw) * 2 + 1); 2065281806Srpaulo if (wps->ap_nfc_dev_pw) { 2066281806Srpaulo wpa_snprintf_hex_uppercase( 2067281806Srpaulo (char *) wpabuf_put(wps->ap_nfc_dev_pw, 2068281806Srpaulo wpabuf_len(pw) * 2), 2069281806Srpaulo wpabuf_len(pw) * 2 + 1, 2070281806Srpaulo wpabuf_head(pw), wpabuf_len(pw)); 2071281806Srpaulo } 2072252726Srpaulo 2073252726Srpaulo if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey || 2074252726Srpaulo !wps->ap_nfc_dev_pw) { 2075252726Srpaulo hostapd_wps_nfc_clear(wps); 2076252726Srpaulo return -1; 2077252726Srpaulo } 2078252726Srpaulo 2079252726Srpaulo return 0; 2080252726Srpaulo} 2081252726Srpaulo 2082252726Srpaulo 2083252726Srpaulovoid hostapd_wps_nfc_token_disable(struct hostapd_data *hapd) 2084252726Srpaulo{ 2085281806Srpaulo wpa_printf(MSG_DEBUG, "WPS: Disable NFC token for AP interface %s", 2086281806Srpaulo hapd->conf->iface); 2087252726Srpaulo hostapd_wps_nfc_clear(hapd->wps); 2088252726Srpaulo} 2089252726Srpaulo 2090252726Srpaulo#endif /* CONFIG_WPS_NFC */ 2091