WPS: Allow NFC password token from AP to be used with wps_reg
[mech_eap.git] / wpa_supplicant / wps_supplicant.c
index 1bdfea8..393c395 100644 (file)
@@ -1,15 +1,9 @@
 /*
  * wpa_supplicant / 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 "includes.h"
@@ -17,6 +11,7 @@
 #include "common.h"
 #include "eloop.h"
 #include "uuid.h"
+#include "crypto/random.h"
 #include "crypto/dh_group5.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
@@ -24,7 +19,9 @@
 #include "common/wpa_ctrl.h"
 #include "eap_common/eap_wsc_common.h"
 #include "eap_peer/eap.h"
+#include "eapol_supp/eapol_supp_sm.h"
 #include "rsn_supp/wpa.h"
+#include "wps/wps_attr_parse.h"
 #include "config.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
@@ -76,8 +73,10 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
        if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid &&
            !(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
                int disabled = wpa_s->current_ssid->disabled;
+               unsigned int freq = wpa_s->assoc_freq;
                wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - "
-                          "try to associate with the received credential");
+                          "try to associate with the received credential "
+                          "(freq=%u)", freq);
                wpa_supplicant_deauthenticate(wpa_s,
                                              WLAN_REASON_DEAUTH_LEAVING);
                if (disabled) {
@@ -86,7 +85,8 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
                        return 1;
                }
                wpa_s->after_wps = 5;
-               wpa_s->wps_freq = wpa_s->assoc_freq;
+               wpa_s->wps_freq = freq;
+               wpa_s->normal_scans = 0;
                wpa_s->reassociate = 1;
                wpa_supplicant_req_scan(wpa_s, 0, 0);
                return 1;
@@ -126,6 +126,8 @@ static void wpas_wps_security_workaround(struct wpa_supplicant *wpa_s,
        if (wpa_drv_get_capa(wpa_s, &capa))
                return; /* Unknown what driver supports */
 
+       if (ssid->ssid == NULL)
+               return;
        bss = wpa_bss_get(wpa_s, cred->mac_addr, ssid->ssid, ssid->ssid_len);
        if (bss == NULL) {
                wpa_printf(MSG_DEBUG, "WPS: The AP was not found from BSS "
@@ -191,7 +193,9 @@ static int wpa_supplicant_wps_cred(void *ctx,
        struct wpa_ssid *ssid = wpa_s->current_ssid;
        u8 key_idx = 0;
        u16 auth_type;
+#ifdef CONFIG_WPS_REG_DISABLE_OPEN
        int registrar = 0;
+#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
 
        if ((wpa_s->conf->wps_cred_processing == 1 ||
             wpa_s->conf->wps_cred_processing == 2) && cred->cred_attr) {
@@ -245,11 +249,13 @@ static int wpa_supplicant_wps_cred(void *ctx,
        if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
                wpa_printf(MSG_DEBUG, "WPS: Replace WPS network block based "
                           "on the received credential");
+#ifdef CONFIG_WPS_REG_DISABLE_OPEN
                if (ssid->eap.identity &&
                    ssid->eap.identity_len == WSC_ID_REGISTRAR_LEN &&
                    os_memcmp(ssid->eap.identity, WSC_ID_REGISTRAR,
                              WSC_ID_REGISTRAR_LEN) == 0)
                        registrar = 1;
+#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
                os_free(ssid->eap.identity);
                ssid->eap.identity = NULL;
                ssid->eap.identity_len = 0;
@@ -259,6 +265,7 @@ static int wpa_supplicant_wps_cred(void *ctx,
                ssid->eap.eap_methods = NULL;
                if (!ssid->p2p_group)
                        ssid->temporary = 0;
+               ssid->bssid_set = 0;
        } else {
                wpa_printf(MSG_DEBUG, "WPS: Create a new network based on the "
                           "received credential");
@@ -470,6 +477,9 @@ static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s,
        }
        wpas_clear_wps(wpa_s);
        wpas_notify_wps_event_fail(wpa_s, fail);
+#ifdef CONFIG_P2P
+       wpas_p2p_wps_failed(wpa_s, fail);
+#endif /* CONFIG_P2P */
 }
 
 
@@ -653,6 +663,8 @@ static void wpa_supplicant_wps_event(void *ctx, enum wps_event event,
                wpa_supplicant_wps_event_er_set_sel_reg(wpa_s,
                                                        &data->set_sel_reg);
                break;
+       case WPS_EV_AP_PIN_SUCCESS:
+               break;
        }
 }
 
@@ -670,7 +682,9 @@ enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid)
 static void wpas_clear_wps(struct wpa_supplicant *wpa_s)
 {
        int id;
-       struct wpa_ssid *ssid, *remove_ssid = NULL;
+       struct wpa_ssid *ssid, *remove_ssid = NULL, *prev_current;
+
+       prev_current = wpa_s->current_ssid;
 
        eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
 
@@ -689,6 +703,11 @@ static void wpas_clear_wps(struct wpa_supplicant *wpa_s)
                        id = -1;
                ssid = ssid->next;
                if (id >= 0) {
+                       if (prev_current == remove_ssid) {
+                               wpa_sm_set_config(wpa_s->wpa, NULL);
+                               eapol_sm_notify_config(wpa_s->eapol, NULL,
+                                                      NULL);
+                       }
                        wpas_notify_network_removed(wpa_s, remove_ssid);
                        wpa_config_remove_network(wpa_s->conf, id);
                }
@@ -772,22 +791,46 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
 
 
 static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
-                            struct wpa_ssid *selected)
+                            struct wpa_ssid *selected, const u8 *bssid)
 {
        struct wpa_ssid *ssid;
+       struct wpa_bss *bss;
+
+       wpa_s->known_wps_freq = 0;
+       if (bssid) {
+               bss = wpa_bss_get_bssid(wpa_s, bssid);
+               if (bss && bss->freq > 0) {
+                       wpa_s->known_wps_freq = 1;
+                       wpa_s->wps_freq = bss->freq;
+               }
+       }
+
+       if (wpa_s->current_ssid)
+               wpa_supplicant_deauthenticate(
+                       wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 
        /* Mark all other networks disabled and trigger reassociation */
        ssid = wpa_s->conf->ssid;
        while (ssid) {
                int was_disabled = ssid->disabled;
-               ssid->disabled = ssid != selected;
-               if (was_disabled != ssid->disabled)
-                       wpas_notify_network_enabled_changed(wpa_s, ssid);
+               /*
+                * In case the network object corresponds to a persistent group
+                * then do not send out network disabled signal. In addition,
+                * do not change disabled status of persistent network objects
+                * from 2 to 1 should we connect to another network.
+                */
+               if (was_disabled != 2) {
+                       ssid->disabled = ssid != selected;
+                       if (was_disabled != ssid->disabled)
+                               wpas_notify_network_enabled_changed(wpa_s,
+                                                                   ssid);
+               }
                ssid = ssid->next;
        }
        wpa_s->disconnected = 0;
        wpa_s->reassociate = 1;
        wpa_s->scan_runs = 0;
+       wpa_s->normal_scans = 0;
        wpa_s->wps_success = 0;
        wpa_s->blacklist_cleared = 0;
        wpa_supplicant_req_scan(wpa_s, 0, 0);
@@ -821,7 +864,7 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
                ssid->eap.fragment_size = wpa_s->wps_fragment_size;
        eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
                               wpa_s, NULL);
-       wpas_wps_reassoc(wpa_s, ssid);
+       wpas_wps_reassoc(wpa_s, ssid, bssid);
        return 0;
 }
 
@@ -864,7 +907,7 @@ int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
                ssid->eap.fragment_size = wpa_s->wps_fragment_size;
        eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
                               wpa_s, NULL);
-       wpas_wps_reassoc(wpa_s, ssid);
+       wpas_wps_reassoc(wpa_s, ssid, bssid);
        return rpin;
 }
 
@@ -879,7 +922,8 @@ int wpas_wps_cancel(struct wpa_supplicant *wpa_s)
        }
 #endif /* CONFIG_AP */
 
-       if (wpa_s->wpa_state == WPA_SCANNING) {
+       if (wpa_s->wpa_state == WPA_SCANNING ||
+           wpa_s->wpa_state == WPA_DISCONNECTED) {
                wpa_printf(MSG_DEBUG, "WPS: Cancel operation - cancel scan");
                wpa_supplicant_cancel_scan(wpa_s);
                wpas_clear_wps(wpa_s);
@@ -984,7 +1028,7 @@ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
                ssid->eap.fragment_size = wpa_s->wps_fragment_size;
        eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
                               wpa_s, NULL);
-       wpas_wps_reassoc(wpa_s, ssid);
+       wpas_wps_reassoc(wpa_s, ssid, bssid);
        return 0;
 }
 
@@ -1073,8 +1117,10 @@ static void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s,
                while (first && first->next)
                        first = first->next;
                if (first && first != wpa_s) {
-                       os_memcpy(wps->uuid, wpa_s->global->ifaces->wps->uuid,
-                                 WPS_UUID_LEN);
+                       if (wps != wpa_s->global->ifaces->wps)
+                               os_memcpy(wps->uuid,
+                                         wpa_s->global->ifaces->wps->uuid,
+                                         WPS_UUID_LEN);
                        wpa_hexdump(MSG_DEBUG, "WPS: UUID from the first "
                                    "interface", wps->uuid, WPS_UUID_LEN);
                } else {
@@ -1090,11 +1136,29 @@ static void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s,
 }
 
 
+static void wpas_wps_set_vendor_ext_m1(struct wpa_supplicant *wpa_s,
+                                      struct wps_context *wps)
+{
+       wpabuf_free(wps->dev.vendor_ext_m1);
+       wps->dev.vendor_ext_m1 = NULL;
+
+       if (wpa_s->conf->wps_vendor_ext_m1) {
+               wps->dev.vendor_ext_m1 =
+                       wpabuf_dup(wpa_s->conf->wps_vendor_ext_m1);
+               if (!wps->dev.vendor_ext_m1) {
+                       wpa_printf(MSG_ERROR, "WPS: Cannot "
+                                  "allocate memory for vendor_ext_m1");
+               }
+       }
+}
+
+
 int wpas_wps_init(struct wpa_supplicant *wpa_s)
 {
        struct wps_context *wps;
        struct wps_registrar_config rcfg;
-       int i;
+       struct hostapd_hw_modes *modes;
+       u16 m;
 
        wps = os_zalloc(sizeof(*wps));
        if (wps == NULL)
@@ -1119,31 +1183,34 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s)
                return -1;
        }
        wps->config_methods = wps_fix_config_methods(wps->config_methods);
-       if (wpa_s->conf->device_type &&
-           wps_dev_type_str2bin(wpa_s->conf->device_type,
-                                wps->dev.pri_dev_type) < 0) {
-               wpa_printf(MSG_ERROR, "WPS: Invalid device_type");
-               os_free(wps);
-               return -1;
-       }
+       wps->dev.config_methods = wps->config_methods;
+       os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type,
+                 WPS_DEV_TYPE_LEN);
 
-       for (i = 0; i < MAX_SEC_DEVICE_TYPES; i++) {
-               if (wpa_s->conf->sec_device_type[i] == NULL)
-                       continue;
-               if (wps_dev_type_str2bin(
-                           wpa_s->conf->sec_device_type[i],
-                           wps->dev.sec_dev_type[wps->dev.num_sec_dev_types])
-                   < 0) {
-                       wpa_printf(MSG_ERROR, "WPS: Invalid sec_device_type");
-                       return -1;
-               }
-               wps->dev.num_sec_dev_types++;
-               if (wps->dev.num_sec_dev_types == WPS_SEC_DEVICE_TYPES)
-                       break;
-       }
+       wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
+       os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type,
+                 WPS_DEV_TYPE_LEN * wps->dev.num_sec_dev_types);
+
+       wpas_wps_set_vendor_ext_m1(wpa_s, wps);
 
        wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
-       wps->dev.rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ; /* TODO: config */
+       modes = wpa_s->hw.modes;
+       if (modes) {
+               for (m = 0; m < wpa_s->hw.num_modes; m++) {
+                       if (modes[m].mode == HOSTAPD_MODE_IEEE80211B ||
+                           modes[m].mode == HOSTAPD_MODE_IEEE80211G)
+                               wps->dev.rf_bands |= WPS_RF_24GHZ;
+                       else if (modes[m].mode == HOSTAPD_MODE_IEEE80211A)
+                               wps->dev.rf_bands |= WPS_RF_50GHZ;
+               }
+       }
+       if (wps->dev.rf_bands == 0) {
+               /*
+                * Default to claiming support for both bands if the driver
+                * does not provide support for fetching supported bands.
+                */
+               wps->dev.rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ;
+       }
        os_memcpy(wps->dev.mac_addr, wpa_s->own_addr, ETH_ALEN);
        wpas_wps_set_uuid(wpa_s, wps);
 
@@ -1186,6 +1253,7 @@ void wpas_wps_deinit(struct wpa_supplicant *wpa_s)
        wpabuf_free(wpa_s->wps->dh_privkey);
        wpabuf_free(wpa_s->wps->oob_conf.pubkey_hash);
        wpabuf_free(wpa_s->wps->oob_conf.dev_password);
+       wpabuf_free(wpa_s->wps->dev.vendor_ext_m1);
        os_free(wpa_s->wps->network_key);
        os_free(wpa_s->wps);
        wpa_s->wps = NULL;
@@ -1606,6 +1674,34 @@ int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid,
 }
 
 
+#ifdef CONFIG_WPS_NFC
+struct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s,
+                                            int ndef, const char *uuid)
+{
+       struct wpabuf *ret;
+       u8 u[UUID_LEN];
+
+       if (!wpa_s->wps_er)
+               return NULL;
+
+       if (uuid_str2bin(uuid, u))
+               return NULL;
+
+       ret = wps_er_nfc_config_token(wpa_s->wps_er, u);
+       if (ndef && ret) {
+               struct wpabuf *tmp;
+               tmp = ndef_build_wifi(ret);
+               wpabuf_free(ret);
+               if (tmp == NULL)
+                       return NULL;
+               ret = tmp;
+       }
+
+       return ret;
+}
+#endif /* CONFIG_WPS_NFC */
+
+
 static int callbacks_pending = 0;
 
 static void wpas_wps_terminate_cb(void *ctx)
@@ -1665,35 +1761,19 @@ void wpas_wps_update_config(struct wpa_supplicant *wpa_s)
        }
        wps->config_methods = wps_fix_config_methods(wps->config_methods);
 
-       if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE) {
-               if (wpa_s->conf->device_type &&
-                   wps_dev_type_str2bin(wpa_s->conf->device_type,
-                                        wps->dev.pri_dev_type) < 0)
-                       wpa_printf(MSG_ERROR, "WPS: Invalid device_type");
-       }
+       if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE)
+               os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type,
+                         WPS_DEV_TYPE_LEN);
 
        if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE) {
-               int i;
-
-               wps->dev.num_sec_dev_types = 0;
-
-               for (i = 0; i < MAX_SEC_DEVICE_TYPES; i++) {
-                       if (wpa_s->conf->sec_device_type[i] == NULL)
-                               continue;
-                       if (wps_dev_type_str2bin(
-                                   wpa_s->conf->sec_device_type[i],
-                                   wps->dev.sec_dev_type[
-                                           wps->dev.num_sec_dev_types]) < 0) {
-                               wpa_printf(MSG_ERROR,
-                                          "WPS: Invalid sec_device_type");
-                               continue;
-                       }
-                       wps->dev.num_sec_dev_types++;
-                       if (wps->dev.num_sec_dev_types == WPS_SEC_DEVICE_TYPES)
-                               break;
-               }
+               wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
+               os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type,
+                         wps->dev.num_sec_dev_types * WPS_DEV_TYPE_LEN);
        }
 
+       if (wpa_s->conf->changed_parameters & CFG_CHANGED_VENDOR_EXTENSION)
+               wpas_wps_set_vendor_ext_m1(wpa_s, wps);
+
        if (wpa_s->conf->changed_parameters & CFG_CHANGED_OS_VERSION)
                wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
 
@@ -1710,3 +1790,170 @@ void wpas_wps_update_config(struct wpa_supplicant *wpa_s)
                wps->dev.serial_number = wpa_s->conf->serial_number;
        }
 }
+
+
+#ifdef CONFIG_WPS_NFC
+
+struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef)
+{
+       struct wpabuf *priv = NULL, *pub = NULL, *pw;
+       void *dh_ctx;
+       struct wpabuf *ret;
+
+       pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN);
+       if (pw == NULL)
+               return NULL;
+
+       if (random_get_bytes(wpabuf_put(pw, WPS_OOB_DEVICE_PASSWORD_LEN),
+                            WPS_OOB_DEVICE_PASSWORD_LEN)) {
+               wpabuf_free(pw);
+               return NULL;
+       }
+
+       dh_ctx = dh5_init(&priv, &pub);
+       if (dh_ctx == NULL) {
+               wpabuf_free(pw);
+               return NULL;
+       }
+       dh5_free(dh_ctx);
+
+       wpa_s->conf->wps_nfc_dev_pw_id = 0x10 + os_random() % 0xfff0;
+       wpabuf_free(wpa_s->conf->wps_nfc_dh_pubkey);
+       wpa_s->conf->wps_nfc_dh_pubkey = pub;
+       wpabuf_free(wpa_s->conf->wps_nfc_dh_privkey);
+       wpa_s->conf->wps_nfc_dh_privkey = priv;
+       wpabuf_free(wpa_s->conf->wps_nfc_dev_pw);
+       wpa_s->conf->wps_nfc_dev_pw = pw;
+
+       ret = wps_build_nfc_pw_token(wpa_s->conf->wps_nfc_dev_pw_id,
+                                    wpa_s->conf->wps_nfc_dh_pubkey,
+                                    wpa_s->conf->wps_nfc_dev_pw);
+       if (ndef && ret) {
+               struct wpabuf *tmp;
+               tmp = ndef_build_wifi(ret);
+               wpabuf_free(ret);
+               if (tmp == NULL)
+                       return NULL;
+               ret = tmp;
+       }
+
+       return ret;
+}
+
+
+int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+       struct wps_context *wps = wpa_s->wps;
+       char pw[32 * 2 + 1];
+
+       if (wpa_s->conf->wps_nfc_dh_pubkey == NULL ||
+           wpa_s->conf->wps_nfc_dh_privkey == NULL ||
+           wpa_s->conf->wps_nfc_dev_pw == NULL)
+               return -1;
+
+       dh5_free(wps->dh_ctx);
+       wpabuf_free(wps->dh_pubkey);
+       wpabuf_free(wps->dh_privkey);
+       wps->dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
+       wps->dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey);
+       if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) {
+               wps->dh_ctx = NULL;
+               wpabuf_free(wps->dh_pubkey);
+               wps->dh_pubkey = NULL;
+               wpabuf_free(wps->dh_privkey);
+               wps->dh_privkey = NULL;
+               return -1;
+       }
+       wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey);
+       if (wps->dh_ctx == NULL)
+               return -1;
+
+       wpa_snprintf_hex_uppercase(pw, sizeof(pw),
+                                  wpabuf_head(wpa_s->conf->wps_nfc_dev_pw),
+                                  wpabuf_len(wpa_s->conf->wps_nfc_dev_pw));
+       return wpas_wps_start_pin(wpa_s, bssid, pw, 0,
+                                 wpa_s->conf->wps_nfc_dev_pw_id);
+}
+
+
+static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s,
+                            struct wps_parse_attr *attr)
+{
+       if (wps_oob_use_cred(wpa_s->wps, attr) < 0)
+               return -1;
+
+       if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network "
+                  "based on the received credential added");
+       wpa_s->normal_scans = 0;
+       wpa_supplicant_reinit_autoscan(wpa_s);
+       wpa_s->disconnected = 0;
+       wpa_s->reassociate = 1;
+       wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+       return 0;
+}
+
+
+static int wpas_wps_add_nfc_password_token(struct wpa_supplicant *wpa_s,
+                                          struct wps_parse_attr *attr)
+{
+       return wps_registrar_add_nfc_password_token(
+               wpa_s->wps->registrar, attr->oob_dev_password,
+               attr->oob_dev_password_len);
+}
+
+
+static int wpas_wps_nfc_tag_process(struct wpa_supplicant *wpa_s,
+                                   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.num_cred)
+               return wpas_wps_use_cred(wpa_s, &attr);
+
+#ifdef CONFIG_WPS_ER
+       if (attr.oob_dev_password)
+               return wpas_wps_add_nfc_password_token(wpa_s, &attr);
+#endif /* CONFIG_WPS_ER */
+
+       wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag");
+       return -1;
+}
+
+
+int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
+                         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 = wpas_wps_nfc_tag_process(wpa_s, wps);
+       wpabuf_free(tmp);
+       return ret;
+}
+
+#endif /* CONFIG_WPS_NFC */