WPS: Share a single function for generating NFS password tokens
[mech_eap.git] / src / ap / wps_hostapd.c
index 1266559..07ce06c 100644 (file)
@@ -1,15 +1,9 @@
 /*
  * hostapd / WPS integration
- * Copyright (c) 2008-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "utils/includes.h"
@@ -18,6 +12,8 @@
 #include "utils/eloop.h"
 #include "utils/uuid.h"
 #include "crypto/dh_groups.h"
+#include "crypto/dh_group5.h"
+#include "crypto/random.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 "ap_drv_ops.h"
 #include "beacon.h"
 #include "sta_info.h"
 #include "wps_hostapd.h"
@@ -40,8 +38,10 @@ static int hostapd_wps_upnp_init(struct hostapd_data *hapd,
 static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd);
 #endif /* CONFIG_WPS_UPNP */
 
-static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr,
-                                   const u8 *ie, size_t ie_len);
+static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
+                                   const u8 *bssid,
+                                   const u8 *ie, size_t ie_len,
+                                   int ssi_signal);
 static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
 
 
@@ -139,8 +139,9 @@ static int hostapd_wps_set_ie_cb(void *ctx, struct wpabuf *beacon_ie,
        hapd->wps_beacon_ie = beacon_ie;
        wpabuf_free(hapd->wps_probe_resp_ie);
        hapd->wps_probe_resp_ie = probe_resp_ie;
-       ieee802_11_set_beacon(hapd);
-       return hapd->drv.set_ap_wps_ie(hapd);
+       if (hapd->beacon_set_done)
+               ieee802_11_set_beacon(hapd);
+       return hostapd_set_ap_wps_ie(hapd);
 }
 
 
@@ -183,11 +184,30 @@ static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
 }
 
 
+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;
+}
+
+
 static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
-                                      const u8 *uuid_e)
+                                      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",
@@ -195,6 +215,11 @@ static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
        if (hapd->wps_reg_success_cb)
                hapd->wps_reg_success_cb(hapd->wps_reg_success_cb_ctx,
                                         mac_addr, uuid_e);
+       data.current_hapd = hapd;
+       data.uuid_e = uuid_e;
+       data.dev_pw = dev_pw;
+       data.dev_pw_len = dev_pw_len;
+       hostapd_wps_for_each(hapd, wps_stop_registrar, &data);
 }
 
 
@@ -239,6 +264,20 @@ static void wps_reload_config(void *eloop_data, void *user_ctx)
 }
 
 
+static void hapd_new_ap_event(struct hostapd_data *hapd, const u8 *attr,
+                             size_t attr_len)
+{
+       size_t blen = attr_len * 2 + 1;
+       char *buf = os_malloc(blen);
+       if (buf) {
+               wpa_snprintf_hex(buf, blen, attr, attr_len);
+               wpa_msg(hapd->msg_ctx, MSG_INFO,
+                       WPS_EVENT_NEW_AP_SETTINGS "%s", buf);
+               os_free(buf);
+       }
+}
+
+
 static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx)
 {
        const struct wps_credential *cred = ctx;
@@ -268,15 +307,15 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx)
 
        if ((hapd->conf->wps_cred_processing == 1 ||
             hapd->conf->wps_cred_processing == 2) && cred->cred_attr) {
-               size_t blen = cred->cred_attr_len * 2 + 1;
-               char *_buf = os_malloc(blen);
-               if (_buf) {
-                       wpa_snprintf_hex(_buf, blen,
-                                        cred->cred_attr, cred->cred_attr_len);
-                       wpa_msg(hapd->msg_ctx, MSG_INFO, "%s%s",
-                               WPS_EVENT_NEW_AP_SETTINGS, _buf);
-                       os_free(_buf);
-               }
+               hapd_new_ap_event(hapd, cred->cred_attr, cred->cred_attr_len);
+       } else if (hapd->conf->wps_cred_processing == 1 ||
+                  hapd->conf->wps_cred_processing == 2) {
+               struct wpabuf *attr;
+               attr = wpabuf_alloc(200);
+               if (attr && wps_build_credential_wrap(attr, cred) == 0)
+                       hapd_new_ap_event(hapd, wpabuf_head_u8(attr),
+                                         wpabuf_len(attr));
+               wpabuf_free(attr);
        } else
                wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS);
 
@@ -477,6 +516,8 @@ static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx)
 
        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);
@@ -498,8 +539,10 @@ static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
         * force attacks.
         */
        hapd->ap_pin_failures++;
-       wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u",
-                  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;
 
@@ -508,7 +551,15 @@ static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
 
        wps_registrar_update_ie(hapd->wps->registrar);
 
-       if (!hapd->conf->ap_setup_locked) {
+       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 &&
@@ -534,12 +585,49 @@ static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
 }
 
 
+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)
+{
+       hostapd_wps_for_each(hapd, wps_ap_pin_success, NULL);
+}
+
+
+static const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = {
+       "No Error", /* WPS_EI_NO_ERROR */
+       "TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */
+       "WEP Prohibited" /* WPS_EI_SECURITY_WEP_PROHIBITED */
+};
+
 static void hostapd_wps_event_fail(struct hostapd_data *hapd,
                                   struct wps_event_fail *fail)
 {
-       wpa_msg(hapd->msg_ctx, MSG_INFO,
-               WPS_EVENT_FAIL "msg=%d config_error=%d",
-               fail->msg, fail->config_error);
+       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_event_fail_reason[fail->error_indication]);
+       } else {
+               wpa_msg(hapd->msg_ctx, MSG_INFO,
+                       WPS_EVENT_FAIL "msg=%d config_error=%d",
+                       fail->msg, fail->config_error);
+       }
 }
 
 
@@ -550,18 +638,22 @@ static void hostapd_wps_event_cb(void *ctx, enum wps_event event,
 
        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:
+               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:
+               wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_OVERLAP);
                break;
        case WPS_EV_PBC_TIMEOUT:
+               wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT);
                break;
        case WPS_EV_ER_AP_ADD:
                break;
@@ -575,6 +667,9 @@ static void hostapd_wps_event_cb(void *ctx, enum wps_event event,
                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->wps_event_cb_ctx, event, data);
@@ -589,7 +684,7 @@ static void hostapd_wps_clear_ies(struct hostapd_data *hapd)
        wpabuf_free(hapd->wps_probe_resp_ie);
        hapd->wps_probe_resp_ie = NULL;
 
-       hapd->drv.set_ap_wps_ie(hapd);
+       hostapd_set_ap_wps_ie(hapd);
 }
 
 
@@ -642,6 +737,31 @@ static int interface_count(struct hostapd_iface *iface)
 }
 
 
+static int hostapd_wps_set_vendor_ext(struct hostapd_data *hapd,
+                                     struct wps_context *wps)
+{
+       int i;
+
+       for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+               wpabuf_free(wps->dev.vendor_ext[i]);
+               wps->dev.vendor_ext[i] = NULL;
+
+               if (hapd->conf->wps_vendor_ext[i] == NULL)
+                       continue;
+
+               wps->dev.vendor_ext[i] =
+                       wpabuf_dup(hapd->conf->wps_vendor_ext[i]);
+               if (wps->dev.vendor_ext[i] == NULL) {
+                       while (--i >= 0)
+                               wpabuf_free(wps->dev.vendor_ext[i]);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+
 int hostapd_init_wps(struct hostapd_data *hapd,
                     struct hostapd_bss_config *conf)
 {
@@ -713,16 +833,23 @@ int hostapd_init_wps(struct hostapd_data *hapd,
                wps->config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
        }
 #endif /* CONFIG_WPS2 */
-       if (hapd->conf->device_type &&
-           wps_dev_type_str2bin(hapd->conf->device_type,
-                                wps->dev.pri_dev_type) < 0) {
-               wpa_printf(MSG_ERROR, "WPS: Invalid device_type");
+       os_memcpy(wps->dev.pri_dev_type, hapd->conf->device_type,
+                 WPS_DEV_TYPE_LEN);
+
+       if (hostapd_wps_set_vendor_ext(hapd, wps) < 0) {
                os_free(wps);
                return -1;
        }
+
        wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version);
-       wps->dev.rf_bands = hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
-               WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
+
+       if (conf->wps_rf_bands) {
+               wps->dev.rf_bands = conf->wps_rf_bands;
+       } else {
+               wps->dev.rf_bands =
+                       hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
+                       WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
+       }
 
        if (conf->wpa & WPA_PROTO_RSN) {
                if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK)
@@ -835,21 +962,49 @@ int hostapd_init_wps(struct hostapd_data *hapd,
        wps->model_description = hapd->conf->model_description;
        wps->model_url = hapd->conf->model_url;
        wps->upc = hapd->conf->upc;
+#endif /* CONFIG_WPS_UPNP */
+
+       hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd);
+
+       hapd->wps = wps;
+
+       return 0;
+}
+
+
+int hostapd_init_wps_complete(struct hostapd_data *hapd)
+{
+       struct wps_context *wps = hapd->wps;
+
+       if (wps == NULL)
+               return 0;
 
+#ifdef CONFIG_WPS_UPNP
        if (hostapd_wps_upnp_init(hapd, wps) < 0) {
                wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP");
                wps_registrar_deinit(wps->registrar);
                os_free(wps->network_key);
                os_free(wps);
+               hapd->wps = NULL;
                return -1;
        }
 #endif /* CONFIG_WPS_UPNP */
 
-       hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd);
+       return 0;
+}
 
-       hapd->wps = wps;
 
-       return 0;
+static void hostapd_wps_nfc_clear(struct wps_context *wps)
+{
+#ifdef CONFIG_WPS_NFC
+       wps->ap_nfc_dev_pw_id = 0;
+       wpabuf_free(wps->ap_nfc_dh_pubkey);
+       wps->ap_nfc_dh_pubkey = NULL;
+       wpabuf_free(wps->ap_nfc_dh_privkey);
+       wps->ap_nfc_dh_privkey = NULL;
+       wpabuf_free(wps->ap_nfc_dev_pw);
+       wps->ap_nfc_dev_pw = NULL;
+#endif /* CONFIG_WPS_NFC */
 }
 
 
@@ -870,6 +1025,7 @@ void hostapd_deinit_wps(struct hostapd_data *hapd)
        wpabuf_free(hapd->wps->oob_conf.pubkey_hash);
        wpabuf_free(hapd->wps->oob_conf.dev_password);
        wps_free_pending_msgs(hapd->wps->upnp_msgs);
+       hostapd_wps_nfc_clear(hapd->wps);
        os_free(hapd->wps);
        hapd->wps = NULL;
        hostapd_wps_clear_ies(hapd);
@@ -889,6 +1045,8 @@ void hostapd_update_wps(struct hostapd_data *hapd)
        hapd->wps->upc = hapd->conf->upc;
 #endif /* CONFIG_WPS_UPNP */
 
+       hostapd_wps_set_vendor_ext(hapd, hapd->wps);
+
        if (hapd->conf->wps_state)
                wps_registrar_update_ie(hapd->wps->registrar);
        else
@@ -950,15 +1108,36 @@ int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr,
 
 static int wps_button_pushed(struct hostapd_data *hapd, void *ctx)
 {
+       const u8 *p2p_dev_addr = ctx;
+       if (hapd->wps == NULL)
+               return 0;
+       return wps_registrar_button_pushed(hapd->wps->registrar, p2p_dev_addr);
+}
+
+
+int hostapd_wps_button_pushed(struct hostapd_data *hapd,
+                             const u8 *p2p_dev_addr)
+{
+       return hostapd_wps_for_each(hapd, wps_button_pushed,
+                                   (void *) p2p_dev_addr);
+}
+
+
+static int wps_cancel(struct hostapd_data *hapd, void *ctx)
+{
        if (hapd->wps == NULL)
                return 0;
-       return wps_registrar_button_pushed(hapd->wps->registrar);
+
+       wps_registrar_wps_cancel(hapd->wps->registrar);
+       ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
+
+       return 0;
 }
 
 
-int hostapd_wps_button_pushed(struct hostapd_data *hapd)
+int hostapd_wps_cancel(struct hostapd_data *hapd)
 {
-       return hostapd_wps_for_each(hapd, wps_button_pushed, NULL);
+       return hostapd_wps_for_each(hapd, wps_cancel, NULL);
 }
 
 
@@ -1016,8 +1195,10 @@ error:
 #endif /* CONFIG_WPS_OOB */
 
 
-static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr,
-                                   const u8 *ie, size_t ie_len)
+static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
+                                   const u8 *bssid,
+                                   const u8 *ie, size_t ie_len,
+                                   int ssi_signal)
 {
        struct hostapd_data *hapd = ctx;
        struct wpabuf *wps_ie;
@@ -1115,7 +1296,7 @@ static int hostapd_rx_req_put_wlan_response(
        }
 #endif /* CONFIG_WPS_STRICT */
 
-       if (!sta) {
+       if (!sta || !(sta->flags & WLAN_STA_WPS)) {
                wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found");
                return 0;
        }
@@ -1180,6 +1361,7 @@ 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);
 }
 
 
@@ -1187,6 +1369,7 @@ 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);
@@ -1244,7 +1427,7 @@ const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout)
        struct wps_ap_pin_data data;
 
        pin = wps_generate_pin();
-       os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%u", pin);
+       os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin);
        data.timeout = timeout;
        hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
        return hapd->conf->ap_pin;
@@ -1332,3 +1515,156 @@ int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
 
        return wps_registrar_config_ap(hapd->wps->registrar, &cred);
 }
+
+
+#ifdef CONFIG_WPS_NFC
+
+struct wps_nfc_password_token_data {
+       const u8 *oob_dev_pw;
+       size_t oob_dev_pw_len;
+       int added;
+};
+
+
+static int wps_add_nfc_password_token(struct hostapd_data *hapd, void *ctx)
+{
+       struct wps_nfc_password_token_data *data = ctx;
+       int ret;
+
+       if (hapd->wps == NULL)
+               return 0;
+       ret = wps_registrar_add_nfc_password_token(hapd->wps->registrar,
+                                                  data->oob_dev_pw,
+                                                  data->oob_dev_pw_len);
+       if (ret == 0)
+               data->added++;
+       return ret;
+}
+
+
+static int hostapd_wps_add_nfc_password_token(struct hostapd_data *hapd,
+                                             struct wps_parse_attr *attr)
+{
+       struct wps_nfc_password_token_data data;
+
+       data.oob_dev_pw = attr->oob_dev_password;
+       data.oob_dev_pw_len = attr->oob_dev_password_len;
+       data.added = 0;
+       if (hostapd_wps_for_each(hapd, wps_add_nfc_password_token, &data) < 0)
+               return -1;
+       return data.added ? 0 : -1;
+}
+
+
+static int hostapd_wps_nfc_tag_process(struct hostapd_data *hapd,
+                                      const struct wpabuf *wps)
+{
+       struct wps_parse_attr attr;
+
+       wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps);
+
+       if (wps_parse_msg(wps, &attr)) {
+               wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag");
+               return -1;
+       }
+
+       if (attr.oob_dev_password)
+               return hostapd_wps_add_nfc_password_token(hapd, &attr);
+
+       wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag");
+       return -1;
+}
+
+
+int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd,
+                            const struct wpabuf *data)
+{
+       const struct wpabuf *wps = data;
+       struct wpabuf *tmp = NULL;
+       int ret;
+
+       if (wpabuf_len(data) < 4)
+               return -1;
+
+       if (*wpabuf_head_u8(data) != 0x10) {
+               /* Assume this contains full NDEF record */
+               tmp = ndef_parse_wifi(data);
+               if (tmp == NULL) {
+                       wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF");
+                       return -1;
+               }
+               wps = tmp;
+       }
+
+       ret = hostapd_wps_nfc_tag_process(hapd, wps);
+       wpabuf_free(tmp);
+       return ret;
+}
+
+
+struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
+                                            int ndef)
+{
+       struct wpabuf *ret;
+
+       if (hapd->wps == NULL)
+               return NULL;
+
+       ret = wps_get_oob_cred(hapd->wps);
+       if (ndef && ret) {
+               struct wpabuf *tmp;
+               tmp = ndef_build_wifi(ret);
+               wpabuf_free(ret);
+               if (tmp == NULL)
+                       return NULL;
+               ret = tmp;
+       }
+
+       return ret;
+}
+
+
+struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef)
+{
+       return wps_nfc_token_gen(ndef, &hapd->conf->wps_nfc_dev_pw_id,
+                                &hapd->conf->wps_nfc_dh_pubkey,
+                                &hapd->conf->wps_nfc_dh_privkey,
+                                &hapd->conf->wps_nfc_dev_pw);
+}
+
+
+int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd)
+{
+       struct wps_context *wps = hapd->wps;
+
+       if (wps == NULL)
+               return -1;
+
+       if (!hapd->conf->wps_nfc_dh_pubkey ||
+           !hapd->conf->wps_nfc_dh_privkey ||
+           !hapd->conf->wps_nfc_dev_pw ||
+           !hapd->conf->wps_nfc_dev_pw_id)
+               return -1;
+
+       hostapd_wps_nfc_clear(wps);
+       wps->ap_nfc_dev_pw_id = hapd->conf->wps_nfc_dev_pw_id;
+       wps->ap_nfc_dh_pubkey = wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey);
+       wps->ap_nfc_dh_privkey = wpabuf_dup(hapd->conf->wps_nfc_dh_privkey);
+       wps->ap_nfc_dev_pw = wpabuf_dup(hapd->conf->wps_nfc_dev_pw);
+
+       if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey ||
+           !wps->ap_nfc_dev_pw) {
+               hostapd_wps_nfc_clear(wps);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd)
+{
+       hostapd_wps_nfc_clear(hapd->wps);
+}
+
+#endif /* CONFIG_WPS_NFC */