Cumulative patch from commit 2e988392436227c51002b573ee27a8cee37f70e9
2e98839 P2P: Disable DNS server from dnsmasq
c07f261 P2P NFC: Add script for connection handover with nfcpy
12288d8 WPS NFC: Protect nfcpy pretty print calls against exceptions
c209dd1 WPS NFC: nfcpy script to use new connection handover design
6202500 WPS NFC: Logging level configuration to wps-nfc.py and wps-ap-nfc.py
1f1b5b3 WPS NFC: Clean up nfcpy script no-wait operations
79ede5a WPS NFC: Validate ctrl_iface response before decoding it
ab1db08 WPS NFC: Use argparse in the nfcpy scripts
6f8fa6e WPS NFC: Update wps-nfc.py and wps-ap-nfc.py to use new nfcpy API
b56f6c8 P2P NFC: Add support for freq option in NFC ctrl_iface commands
91a6501 WPS NFC: Use BSSID and AP Channel from handover select
91226e0 WPS: Add testing option to corrupt public key hash
7312776 WPS NFC: add more debug prints for connection handover report
5cd4f66 WPS NFC: Use AP Channel information from credential container
d2f1837 WPS NFC: Add BSSID and AP channel info to Configuration Token
75dbf98 WPS-STRICT: Update valid Device Password ID and Config Error range
5cd4740 P2P NFC: WPA state machine config with driver-based BSS selection
8e9f53c P2P NFC: Static handover with NFC Tag on client
dd87677 P2P NFC: Enable own NFC Tag on GO Registrar
abe44e3 P2P NFC: Add GO info into handover message when in client role
23318be P2P NFC: Optimize join-a-group operation based on NFC information
86e3208 P2P NFC: Copy DH parameters to a separate group interface
d4b4d7f WPS NFC: Update DH keys for ER operations
ac08752 WPS NFC: Use pubkey mismatch config error from Enrollee
59b45d1 P2P NFC: Add processing of P2P client while NFC handover case
74df9ec P2P NFC: Do not try to join peer if both devices are already GO
201b0f5 P2P: Add test option to disable IP address assignment request
25ef852 P2P: Add support for IP address assignment in 4-way handshake
fdd48ff P2P NFC: Optimize GO Negotiation retries
c4f87a7 P2P NFC: Add NFC tag enabling for static handover
dd37a93 P2P NFC: Report handover select from tag for static handover
db6ae69 P2P NFC: Report connection handover as trigger for P2P
9358878 P2P NFC: Build connection handover messages
c00ab85 P2P NFC: Define WPS_NFC config method
0deab08 P2P NFC: Allow separate WPS/P2P IES to be parsed
fca9958 P2P NFC: Pass OOB Dev Password through P2P parser
ab9e344 P2P NFC: Pass OOB Device Password ID to P2P
5154689 P2P NFC: Add WPS attribute building for P2P NFC
01afd8d P2P NFC: Add NDEF helpers for P2P connection handover messages
9e323a2 P2P NFC: Add OOB GO Negotiation Channel attribute
14d8645 WPS NFC: Allow BSSID and channel to be included in handover select
50d1f89 NFC: Update WPS ER to use the new connection handover design
d950793 WPS NFC: Add support for wpa_supplicant AP/GO mode to use handover
fa4c298 WPS NFC: Process new style handover select
068cdb1 WPS NFC: New style connection handover select from AP/Registrar
3189ca0 WPS NFC: Add AP mode connection handover report
41f9ffb WPS NFC: Build new style carrier record for connection handover request
3f1639d WPS NFC: Split DH key generation to a separate function
9754917 WPS NFC: Update NFC connection handover design
34b6795 WPS NFC: Use abbreviated handshake if both PK hashes delivered OOB
57630e6 WPS: Preparations for allowing SSID filtering for provisioning step
5f45455 WPS NFC: Validate peer public key hash on Enrollee
ff40cd6 WPS NFC: Send M2D with config error 20 on pkhash mismatch
e435417 WPS: Remove Version attribute from NFC messages
72403ec WPS: Add builder functions for AP Channel and RF Bands attributes
ea43ad9 P2P: Make group operating channel available
9f7cd9a P2P: Split add-group-info into a helper function
253f2e3 P2P: Apply unsafe frequency rules to available channels
1682c62 Add a header file defining QCA OUI and vendor extensions
Change-Id: Ia7604d018e1ffb25e06bdc01ce258fc4a0569245
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 0bb937e..b4860a0 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -384,6 +384,12 @@
#define P2P_MANAGE BIT(3)
#define P2P_ALLOW_CROSS_CONNECTION BIT(4)
int p2p;
+#ifdef CONFIG_P2P
+ u8 ip_addr_go[4];
+ u8 ip_addr_mask[4];
+ u8 ip_addr_start[4];
+ u8 ip_addr_end[4];
+#endif /* CONFIG_P2P */
int disassoc_low_ack;
int skip_inactivity_poll;
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 670b834..24e764d 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -908,6 +908,7 @@
char buf[100];
#ifdef CONFIG_P2P
u8 addr[ETH_ALEN];
+ u8 ip_addr_buf[4];
#endif /* CONFIG_P2P */
if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED))
@@ -929,12 +930,25 @@
os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr));
if (authorized) {
- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s", buf);
+ char ip_addr[100];
+ ip_addr[0] = '\0';
+#ifdef CONFIG_P2P
+ if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) {
+ os_snprintf(ip_addr, sizeof(ip_addr),
+ " ip_addr=%u.%u.%u.%u",
+ ip_addr_buf[0], ip_addr_buf[1],
+ ip_addr_buf[2], ip_addr_buf[3]);
+ }
+#endif /* CONFIG_P2P */
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s",
+ buf, ip_addr);
if (hapd->msg_ctx_parent &&
hapd->msg_ctx_parent != hapd->msg_ctx)
wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
- AP_STA_CONNECTED "%s", buf);
+ AP_STA_CONNECTED "%s%s",
+ buf, ip_addr);
sta->flags |= WLAN_STA_AUTHORIZED;
} else {
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 5993edf..707a63f 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -11,6 +11,7 @@
#include "utils/common.h"
#include "utils/eloop.h"
#include "utils/state_machine.h"
+#include "utils/bitfield.h"
#include "common/ieee802_11_defs.h"
#include "crypto/aes_wrap.h"
#include "crypto/crypto.h"
@@ -424,6 +425,17 @@
wpa_rekey_gtk, wpa_auth, NULL);
}
+#ifdef CONFIG_P2P
+ if (WPA_GET_BE32(conf->ip_addr_start)) {
+ int count = WPA_GET_BE32(conf->ip_addr_end) -
+ WPA_GET_BE32(conf->ip_addr_start) + 1;
+ if (count > 1000)
+ count = 1000;
+ if (count > 0)
+ wpa_auth->ip_pool = bitfield_alloc(count);
+ }
+#endif /* CONFIG_P2P */
+
return wpa_auth;
}
@@ -466,6 +478,11 @@
wpa_auth->ft_pmk_cache = NULL;
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+ bitfield_free(wpa_auth->ip_pool);
+#endif /* CONFIG_P2P */
+
+
os_free(wpa_auth->wpa_ie);
group = wpa_auth->group;
@@ -583,6 +600,19 @@
static void wpa_free_sta_sm(struct wpa_state_machine *sm)
{
+#ifdef CONFIG_P2P
+ if (WPA_GET_BE32(sm->ip_addr)) {
+ u32 start;
+ wpa_printf(MSG_DEBUG, "P2P: Free assigned IP "
+ "address %u.%u.%u.%u from " MACSTR,
+ sm->ip_addr[0], sm->ip_addr[1],
+ sm->ip_addr[2], sm->ip_addr[3],
+ MAC2STR(sm->addr));
+ start = WPA_GET_BE32(sm->wpa_auth->conf.ip_addr_start);
+ bitfield_clear(sm->wpa_auth->ip_pool,
+ WPA_GET_BE32(sm->ip_addr) - start);
+ }
+#endif /* CONFIG_P2P */
if (sm->GUpdateStationKeys) {
sm->group->GKeyDoneStations--;
sm->GUpdateStationKeys = FALSE;
@@ -1000,6 +1030,26 @@
return;
}
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+ if (kde.ip_addr_req && kde.ip_addr_req[0] &&
+ wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
+ int idx;
+ wpa_printf(MSG_DEBUG, "P2P: IP address requested in "
+ "EAPOL-Key exchange");
+ idx = bitfield_get_first_zero(wpa_auth->ip_pool);
+ if (idx >= 0) {
+ u32 start = WPA_GET_BE32(wpa_auth->conf.
+ ip_addr_start);
+ bitfield_set(wpa_auth->ip_pool, idx);
+ WPA_PUT_BE32(sm->ip_addr, start + idx);
+ wpa_printf(MSG_DEBUG, "P2P: Assigned IP "
+ "address %u.%u.%u.%u to " MACSTR,
+ sm->ip_addr[0], sm->ip_addr[1],
+ sm->ip_addr[2], sm->ip_addr[3],
+ MAC2STR(sm->addr));
+ }
+ }
+#endif /* CONFIG_P2P */
break;
case PAIRWISE_4:
if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
@@ -1995,6 +2045,10 @@
kde_len += 300; /* FTIE + 2 * TIE */
}
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+ if (WPA_GET_BE32(sm->ip_addr) > 0)
+ kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4;
+#endif /* CONFIG_P2P */
kde = os_malloc(kde_len);
if (kde == NULL)
return;
@@ -2056,6 +2110,16 @@
pos += 4;
}
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+ if (WPA_GET_BE32(sm->ip_addr) > 0) {
+ u8 addr[3 * 4];
+ os_memcpy(addr, sm->ip_addr, 4);
+ os_memcpy(addr + 4, sm->wpa_auth->conf.ip_addr_mask, 4);
+ os_memcpy(addr + 8, sm->wpa_auth->conf.ip_addr_go, 4);
+ pos = wpa_add_kde(pos, WFA_KEY_DATA_IP_ADDR_ALLOC,
+ addr, sizeof(addr), NULL, 0);
+ }
+#endif /* CONFIG_P2P */
wpa_send_eapol(sm->wpa_auth, sm,
(secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC |
@@ -3103,3 +3167,14 @@
return 0;
return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE;
}
+
+
+#ifdef CONFIG_P2P
+int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr)
+{
+ if (sm == NULL || WPA_GET_BE32(sm->ip_addr) == 0)
+ return -1;
+ os_memcpy(addr, sm->ip_addr, 4);
+ return 0;
+}
+#endif /* CONFIG_P2P */
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index da45ae4..bc3dec4 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -163,6 +163,12 @@
#ifdef CONFIG_TESTING_OPTIONS
double corrupt_gtk_rekey_mic_probability;
#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_P2P
+ u8 ip_addr_go[4];
+ u8 ip_addr_mask[4];
+ u8 ip_addr_start[4];
+ u8 ip_addr_end[4];
+#endif /* CONFIG_P2P */
};
typedef enum {
@@ -297,4 +303,6 @@
int wpa_auth_uses_sae(struct wpa_state_machine *sm);
int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm);
+int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr);
+
#endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 4c1d625..5af1495 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -78,6 +78,12 @@
wconf->corrupt_gtk_rekey_mic_probability =
iconf->corrupt_gtk_rekey_mic_probability;
#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_P2P
+ os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4);
+ os_memcpy(wconf->ip_addr_mask, conf->ip_addr_mask, 4);
+ os_memcpy(wconf->ip_addr_start, conf->ip_addr_start, 4);
+ os_memcpy(wconf->ip_addr_end, conf->ip_addr_end, 4);
+#endif /* CONFIG_P2P */
}
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 9736874..fcd5878 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -121,6 +121,10 @@
#endif /* CONFIG_IEEE80211R */
int pending_1_of_4_timeout;
+
+#ifdef CONFIG_P2P
+ u8 ip_addr[4];
+#endif /* CONFIG_P2P */
};
@@ -185,6 +189,10 @@
struct rsn_pmksa_cache *pmksa;
struct wpa_ft_pmk_cache *ft_pmk_cache;
+
+#ifdef CONFIG_P2P
+ struct bitfield *ip_pool;
+#endif /* CONFIG_P2P */
};
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index cdfcca1..274f4d6 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -708,6 +708,25 @@
}
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_P2P
+ if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
+ RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) {
+ ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key",
+ ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN);
+ return 0;
+ }
+
+ if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 &&
+ RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) {
+ ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG,
+ "WPA: IP Address Allocation in EAPOL-Key",
+ ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN);
+ return 0;
+ }
+#endif /* CONFIG_P2P */
+
return 0;
}
diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h
index 4999139..f945882 100644
--- a/src/ap/wpa_auth_ie.h
+++ b/src/ap/wpa_auth_ie.h
@@ -39,6 +39,10 @@
const u8 *ftie;
size_t ftie_len;
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+ const u8 *ip_addr_req;
+ const u8 *ip_addr_alloc;
+#endif /* CONFIG_P2P */
};
int wpa_parse_kde_ies(const u8 *buf, size_t len,
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 4c94210..1b1dce4 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -1784,7 +1784,8 @@
if (hapd->wps == NULL)
return NULL;
- ret = wps_get_oob_cred(hapd->wps);
+ ret = wps_get_oob_cred(hapd->wps, hostapd_wps_rf_band_cb(hapd),
+ hapd->iconf->channel);
if (ndef && ret) {
struct wpabuf *tmp;
tmp = ndef_build_wifi(ret);
@@ -1800,11 +1801,136 @@
struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef)
{
+ struct wpabuf *ret;
+
+ if (hapd->wps == NULL)
+ return NULL;
+
+ if (hapd->conf->wps_nfc_dh_pubkey == NULL) {
+ struct wps_context *wps = hapd->wps;
+ if (wps_nfc_gen_dh(&hapd->conf->wps_nfc_dh_pubkey,
+ &hapd->conf->wps_nfc_dh_privkey) < 0)
+ return NULL;
+ hostapd_wps_nfc_clear(wps);
+ wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
+ 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);
+ if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) {
+ hostapd_wps_nfc_clear(wps);
+ return NULL;
+ }
+ }
+
+ ret = wps_build_nfc_handover_sel(hapd->wps,
+ hapd->conf->wps_nfc_dh_pubkey,
+ hapd->own_addr, hapd->iface->freq);
+
+ if (ndef && ret) {
+ struct wpabuf *tmp;
+ tmp = ndef_build_wifi(ret);
+ wpabuf_free(ret);
+ if (tmp == NULL)
+ return NULL;
+ ret = tmp;
+ }
+
+ return ret;
+}
+
+
+int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd,
+ const struct wpabuf *req,
+ const struct wpabuf *sel)
+{
+ struct wpabuf *wps;
+ int ret = -1;
+ u16 wsc_len;
+ const u8 *pos;
+ struct wpabuf msg;
+ struct wps_parse_attr attr;
+ u16 dev_pw_id;
+
/*
- * Handover Select carrier record for WPS uses the same format as
- * configuration token.
+ * Enrollee/station is always initiator of the NFC connection handover,
+ * so use the request message here to find Enrollee public key hash.
*/
- return hostapd_wps_nfc_config_token(hapd, ndef);
+ wps = ndef_parse_wifi(req);
+ if (wps == NULL)
+ return -1;
+ wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
+ "payload from NFC connection handover");
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
+ if (wpabuf_len(wps) < 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request "
+ "Message");
+ goto out;
+ }
+ pos = wpabuf_head(wps);
+ wsc_len = WPA_GET_BE16(pos);
+ if (wsc_len > wpabuf_len(wps) - 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) "
+ "in rt Wi-Fi Handover Request Message", wsc_len);
+ goto out;
+ }
+ pos += 2;
+
+ wpa_hexdump(MSG_DEBUG,
+ "WPS: WSC attributes in Wi-Fi Handover Request Message",
+ pos, wsc_len);
+ if (wsc_len < wpabuf_len(wps) - 2) {
+ wpa_hexdump(MSG_DEBUG,
+ "WPS: Ignore extra data after WSC attributes",
+ pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len);
+ }
+
+ wpabuf_set(&msg, pos, wsc_len);
+ ret = wps_parse_msg(&msg, &attr);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in "
+ "Wi-Fi Handover Request Message");
+ goto out;
+ }
+
+ if (attr.oob_dev_password == NULL ||
+ attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+ wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password "
+ "included in Wi-Fi Handover Request Message");
+ ret = -1;
+ goto out;
+ }
+
+ if (attr.uuid_e == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi "
+ "Handover Request Message");
+ ret = -1;
+ goto out;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN);
+
+ wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password",
+ attr.oob_dev_password, attr.oob_dev_password_len);
+ dev_pw_id = WPA_GET_BE16(attr.oob_dev_password +
+ WPS_OOB_PUBKEY_HASH_LEN);
+ if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID "
+ "%u in Wi-Fi Handover Request Message", dev_pw_id);
+ ret = -1;
+ goto out;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash",
+ attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
+
+ ret = wps_registrar_add_nfc_pw_token(hapd->wps->registrar,
+ attr.oob_dev_password,
+ DEV_PW_NFC_CONNECTION_HANDOVER,
+ NULL, 0, 1);
+
+out:
+ wpabuf_free(wps);
+ return ret;
}
diff --git a/src/ap/wps_hostapd.h b/src/ap/wps_hostapd.h
index a292598..204bd82 100644
--- a/src/ap/wps_hostapd.h
+++ b/src/ap/wps_hostapd.h
@@ -37,6 +37,9 @@
struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
int ndef);
struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef);
+int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd,
+ const struct wpabuf *req,
+ const struct wpabuf *sel);
struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef);
int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd);
void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd);
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 592ae54..6f7f777 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -926,6 +926,7 @@
P2P_ATTR_INTERFACE = 16,
P2P_ATTR_OPERATING_CHANNEL = 17,
P2P_ATTR_INVITATION_FLAGS = 18,
+ P2P_ATTR_OOB_GO_NEG_CHANNEL = 19,
P2P_ATTR_VENDOR_SPECIFIC = 221
};
@@ -947,6 +948,7 @@
#define P2P_GROUP_CAPAB_CROSS_CONN BIT(4)
#define P2P_GROUP_CAPAB_PERSISTENT_RECONN BIT(5)
#define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6)
+#define P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION BIT(7)
/* Invitation Flags */
#define P2P_INVITATION_FLAGS_TYPE BIT(0)
@@ -971,6 +973,12 @@
P2P_SC_FAIL_REJECTED_BY_USER = 11,
};
+enum p2p_role_indication {
+ P2P_DEVICE_NOT_IN_GROUP = 0x00,
+ P2P_CLIENT_IN_A_GROUP = 0x01,
+ P2P_GO_IN_A_GROUP = 0x02,
+};
+
#define P2P_WILDCARD_SSID "DIRECT-"
#define P2P_WILDCARD_SSID_LEN 7
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
new file mode 100644
index 0000000..0d83920
--- /dev/null
+++ b/src/common/qca-vendor.h
@@ -0,0 +1,51 @@
+/*
+ * Qualcomm Atheros OUI and vendor specific assignments
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef QCA_VENDOR_H
+#define QCA_VENDOR_H
+
+/*
+ * This file is a registry of identifier assignments from the Qualcomm Atheros
+ * OUI 00:13:74 for purposes other than MAC address assignment. New identifiers
+ * can be assigned through normal review process for changes to the upstream
+ * hostap.git repository.
+ */
+
+#define OUI_QCA 0x001374
+
+/**
+ * enum qca_nl80211_vendor_subcmds - QCA nl80211 vendor command identifiers
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_UNSPEC: Reserved value 0
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_TEST: Test command/event
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: Recommendation of frequency
+ * ranges to avoid to reduce issues due to interference or internal
+ * co-existence information in the driver. The event data structure is
+ * defined in struct qca_avoid_freq_list.
+ */
+enum qca_nl80211_vendor_subcmds {
+ QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
+ QCA_NL80211_VENDOR_SUBCMD_TEST = 1,
+ /* subcmds 2..9 not yet allocated */
+ QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY = 10,
+};
+
+
+struct qca_avoid_freq_range {
+ u32 start_freq;
+ u32 end_freq;
+} STRUCT_PACKED;
+
+struct qca_avoid_freq_list {
+ u32 count;
+ struct qca_avoid_freq_range range[0];
+} STRUCT_PACKED;
+
+#endif /* QCA_VENDOR_H */
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 62505f1..dcc035c 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -109,6 +109,9 @@
#define RSN_KEY_DATA_MULTIBAND_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
#define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
+#define WFA_KEY_DATA_IP_ADDR_REQ RSN_SELECTOR(0x50, 0x6f, 0x9a, 4)
+#define WFA_KEY_DATA_IP_ADDR_ALLOC RSN_SELECTOR(0x50, 0x6f, 0x9a, 5)
+
#define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
#define RSN_SELECTOR_PUT(a, val) WPA_PUT_BE32((u8 *) (a), (val))
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 9bd59a9..759cee4 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -74,6 +74,8 @@
* be used again.
*/
#define WPA_EVENT_FREQ_CONFLICT "CTRL-EVENT-FREQ-CONFLICT "
+/** Frequency ranges that the driver recommends to avoid */
+#define WPA_EVENT_AVOID_FREQ "CTRL-EVENT-AVOID-FREQ "
/** WPS overlap detected in PBC mode */
#define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED "
/** Available WPS AP with active PBC found in scan results */
@@ -148,9 +150,13 @@
#define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED "
#define P2P_EVENT_PERSISTENT_PSK_FAIL "P2P-PERSISTENT-PSK-FAIL id="
#define P2P_EVENT_PRESENCE_RESPONSE "P2P-PRESENCE-RESPONSE "
+#define P2P_EVENT_NFC_BOTH_GO "P2P-NFC-BOTH-GO "
+#define P2P_EVENT_NFC_PEER_CLIENT "P2P-NFC-PEER-CLIENT "
+#define P2P_EVENT_NFC_WHILE_CLIENT "P2P-NFC-WHILE-CLIENT "
/* parameters: <PMF enabled> <timeout in ms> <Session Information URL> */
#define ESS_DISASSOC_IMMINENT "ESS-DISASSOC-IMMINENT "
+#define P2P_EVENT_REMOVE_AND_REFORM_GROUP "P2P-REMOVE-AND-REFORM-GROUP "
#define INTERWORKING_AP "INTERWORKING-AP "
#define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH "
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 4fd16ed..2f4536e 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -3147,7 +3147,16 @@
* EVENT_SCAN_RESULTS is used to indicate when the scan has been
* completed (either successfully or by getting cancelled).
*/
- EVENT_SCAN_STARTED
+ EVENT_SCAN_STARTED,
+
+ /**
+ * EVENT_AVOID_FREQUENCIES - Received avoid frequency range
+ *
+ * This event indicates a set of frequency ranges that should be avoided
+ * to reduce issues due to interference or internal co-existence
+ * information in the driver.
+ */
+ EVENT_AVOID_FREQUENCIES
};
@@ -3766,6 +3775,13 @@
struct channel_list_changed {
enum reg_change_initiator initiator;
} channel_list_changed;
+
+ /**
+ * freq_range - List of frequency ranges
+ *
+ * This is used as the data with EVENT_AVOID_FREQUENCIES.
+ */
+ struct wpa_freq_range_list freq_range;
};
/**
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 64bdddb..3058cd5 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -78,6 +78,7 @@
E2S(DFS_NOP_FINISHED);
E2S(SURVEY);
E2S(SCAN_STARTED);
+ E2S(AVOID_FREQUENCIES);
}
return "UNKNOWN";
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index b5bf368..646d3f8 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -28,6 +28,7 @@
#include "common.h"
#include "eloop.h"
#include "utils/list.h"
+#include "common/qca-vendor.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "l2_packet/l2_packet.h"
@@ -2716,6 +2717,69 @@
}
+static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv,
+ const u8 *data, size_t len)
+{
+ u32 i, count;
+ union wpa_event_data event;
+ struct wpa_freq_range *range = NULL;
+ const struct qca_avoid_freq_list *freq_range;
+
+ freq_range = (const struct qca_avoid_freq_list *) data;
+ if (len < sizeof(freq_range->count))
+ return;
+
+ count = freq_range->count;
+ if (len < sizeof(freq_range->count) +
+ count * sizeof(struct qca_avoid_freq_range)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignored too short avoid frequency list (len=%u)",
+ (unsigned int) len);
+ return;
+ }
+
+ if (count > 0) {
+ range = os_calloc(count, sizeof(struct wpa_freq_range));
+ if (range == NULL)
+ return;
+ }
+
+ os_memset(&event, 0, sizeof(event));
+ for (i = 0; i < count; i++) {
+ unsigned int idx = event.freq_range.num;
+ range[idx].min = freq_range->range[i].start_freq;
+ range[idx].max = freq_range->range[i].end_freq;
+ wpa_printf(MSG_DEBUG, "nl80211: Avoid frequency range: %u-%u",
+ range[idx].min, range[idx].max);
+ if (range[idx].min > range[idx].max) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid frequency range");
+ continue;
+ }
+ event.freq_range.num++;
+ }
+ event.freq_range.range = range;
+
+ wpa_supplicant_event(drv->ctx, EVENT_AVOID_FREQUENCIES, &event);
+
+ os_free(range);
+}
+
+
+static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
+ u32 subcmd, u8 *data, size_t len)
+{
+ switch (subcmd) {
+ case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
+ qca_nl80211_avoid_freq(drv, data, len);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Ignore unsupported QCA vendor event %u",
+ subcmd);
+ break;
+ }
+}
+
+
static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv,
struct nlattr **tb)
{
@@ -2751,6 +2815,9 @@
}
switch (vendor_id) {
+ case OUI_QCA:
+ nl80211_vendor_event_qca(drv, subcmd, data, len);
+ break;
default:
wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event");
break;
diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c
index 8edb1ca..6bdd341 100644
--- a/src/eap_peer/eap_wsc.c
+++ b/src/eap_peer/eap_wsc.c
@@ -144,12 +144,13 @@
size_t identity_len;
int registrar;
struct wps_config cfg;
- const char *pos;
+ const char *pos, *end;
const char *phase1;
struct wps_context *wps;
struct wps_credential new_ap_settings;
int res;
int nfc = 0;
+ u8 pkhash[WPS_OOB_PUBKEY_HASH_LEN];
wps = sm->wps;
if (wps == NULL) {
@@ -209,6 +210,15 @@
cfg.pbc = 1;
}
+ pos = os_strstr(phase1, "dev_pw_id=");
+ if (pos) {
+ u16 id = atoi(pos + 10);
+ if (id == DEV_PW_NFC_CONNECTION_HANDOVER)
+ nfc = 1;
+ if (cfg.pin || id == DEV_PW_NFC_CONNECTION_HANDOVER)
+ cfg.dev_pw_id = id;
+ }
+
if (cfg.pin == NULL && !cfg.pbc && !nfc) {
wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 "
"configuration data");
@@ -216,9 +226,23 @@
return NULL;
}
- pos = os_strstr(phase1, "dev_pw_id=");
- if (pos && cfg.pin)
- cfg.dev_pw_id = atoi(pos + 10);
+ pos = os_strstr(phase1, " pkhash=");
+ if (pos) {
+ size_t len;
+ pos += 8;
+ end = os_strchr(pos, ' ');
+ if (end)
+ len = end - pos;
+ else
+ len = os_strlen(pos);
+ if (len != 2 * WPS_OOB_PUBKEY_HASH_LEN ||
+ hexstr2bin(pos, pkhash, WPS_OOB_PUBKEY_HASH_LEN)) {
+ wpa_printf(MSG_INFO, "EAP-WSC: Invalid pkhash");
+ os_free(data);
+ return NULL;
+ }
+ cfg.peer_pubkey_hash = pkhash;
+ }
res = eap_wsc_new_ap_settings(&new_ap_settings, phase1);
if (res < 0) {
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 8aabfc0..ca347e7 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -211,6 +211,7 @@
if (p2p->go_neg_peer) {
p2p->go_neg_peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE;
p2p->go_neg_peer->wps_method = WPS_NOT_READY;
+ p2p->go_neg_peer->oob_pw_id = 0;
}
p2p->go_neg_peer = NULL;
@@ -1292,15 +1293,16 @@
int go_intent, const u8 *own_interface_addr,
unsigned int force_freq, int persistent_group,
const u8 *force_ssid, size_t force_ssid_len,
- int pd_before_go_neg, unsigned int pref_freq)
+ int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id)
{
struct p2p_device *dev;
p2p_dbg(p2p, "Request to start group negotiation - peer=" MACSTR
" GO Intent=%d Intended Interface Address=" MACSTR
- " wps_method=%d persistent_group=%d pd_before_go_neg=%d",
+ " wps_method=%d persistent_group=%d pd_before_go_neg=%d "
+ "oob_pw_id=%u",
MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
- wps_method, persistent_group, pd_before_go_neg);
+ wps_method, persistent_group, pd_before_go_neg, oob_pw_id);
dev = p2p_get_device(p2p, peer_addr);
if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
@@ -1384,6 +1386,7 @@
}
dev->wps_method = wps_method;
+ dev->oob_pw_id = oob_pw_id;
dev->status = P2P_SC_SUCCESS;
if (p2p->p2p_scan_running) {
@@ -1403,15 +1406,15 @@
int go_intent, const u8 *own_interface_addr,
unsigned int force_freq, int persistent_group,
const u8 *force_ssid, size_t force_ssid_len,
- unsigned int pref_freq)
+ unsigned int pref_freq, u16 oob_pw_id)
{
struct p2p_device *dev;
p2p_dbg(p2p, "Request to authorize group negotiation - peer=" MACSTR
" GO Intent=%d Intended Interface Address=" MACSTR
- " wps_method=%d persistent_group=%d",
+ " wps_method=%d persistent_group=%d oob_pw_id=%u",
MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
- wps_method, persistent_group);
+ wps_method, persistent_group, oob_pw_id);
dev = p2p_get_device(p2p, peer_addr);
if (dev == NULL) {
@@ -1442,6 +1445,7 @@
os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
dev->wps_method = wps_method;
+ dev->oob_pw_id = oob_pw_id;
dev->status = P2P_SC_SUCCESS;
return 0;
@@ -1593,6 +1597,7 @@
p2p->ssid_set = 0;
peer->go_neg_req_sent = 0;
peer->wps_method = WPS_NOT_READY;
+ peer->oob_pw_id = 0;
p2p_set_state(p2p, P2P_PROVISIONING);
p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
@@ -1757,7 +1762,8 @@
if (p2p->invite_peer == NULL)
return;
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
- p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr);
+ p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr,
+ p2p->invite_dev_pw_id);
}
@@ -2459,6 +2465,7 @@
p2p->go_neg_peer = NULL;
dev->wps_method = WPS_NOT_READY;
+ dev->oob_pw_id = 0;
dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
@@ -3122,7 +3129,12 @@
p2p_connect_send(p2p, p2p->go_neg_peer);
return;
}
-
+ if (p2p->go_neg_peer && p2p->go_neg_peer->oob_go_neg_freq > 0) {
+ p2p_dbg(p2p, "Skip connect-listen since GO Neg channel known (OOB)");
+ p2p_set_state(p2p, P2P_CONNECT_LISTEN);
+ p2p_set_timeout(p2p, 0, 30000);
+ return;
+ }
p2p_set_state(p2p, P2P_CONNECT_LISTEN);
p2p_listen_in_find(p2p, 0);
}
@@ -3266,7 +3278,7 @@
if (p2p->invite_peer && p2p->invite_peer->invitation_reqs < 100) {
p2p_set_state(p2p, P2P_INVITE);
p2p_invite_send(p2p, p2p->invite_peer,
- p2p->invite_go_dev_addr);
+ p2p->invite_go_dev_addr, p2p->invite_dev_pw_id);
} else {
if (p2p->invite_peer) {
p2p_dbg(p2p, "Invitation Request retry limit reached");
@@ -3381,6 +3393,8 @@
return "Keypad";
case WPS_PBC:
return "PBC";
+ case WPS_NFC:
+ return "NFC";
}
return "??";
@@ -4362,3 +4376,219 @@
va_end(ap);
p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_ERROR, buf);
}
+
+
+#ifdef CONFIG_WPS_NFC
+
+static struct wpabuf * p2p_build_nfc_handover(struct p2p_data *p2p,
+ int client_freq,
+ const u8 *go_dev_addr,
+ const u8 *ssid, size_t ssid_len)
+{
+ struct wpabuf *buf;
+ u8 op_class, channel;
+ enum p2p_role_indication role = P2P_DEVICE_NOT_IN_GROUP;
+
+ buf = wpabuf_alloc(1000);
+ if (buf == NULL)
+ return NULL;
+
+ op_class = p2p->cfg->reg_class;
+ channel = p2p->cfg->channel;
+
+ p2p_buf_add_capability(buf, p2p->dev_capab &
+ ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
+ p2p_buf_add_device_info(buf, p2p, NULL);
+
+ if (p2p->num_groups > 0) {
+ role = P2P_GO_IN_A_GROUP;
+ p2p_freq_to_channel(p2p_group_get_freq(p2p->groups[0]),
+ &op_class, &channel);
+ } else if (client_freq > 0) {
+ role = P2P_CLIENT_IN_A_GROUP;
+ p2p_freq_to_channel(client_freq, &op_class, &channel);
+ }
+
+ p2p_buf_add_oob_go_neg_channel(buf, p2p->cfg->country, op_class,
+ channel, role);
+
+ if (p2p->num_groups > 0) {
+ /* Limit number of clients to avoid very long message */
+ p2p_buf_add_group_info(p2p->groups[0], buf, 5);
+ p2p_group_buf_add_id(p2p->groups[0], buf);
+ } else if (client_freq > 0 &&
+ go_dev_addr && !is_zero_ether_addr(go_dev_addr) &&
+ ssid && ssid_len > 0) {
+ /*
+ * Add the optional P2P Group ID to indicate in which group this
+ * device is a P2P Client.
+ */
+ p2p_buf_add_group_id(buf, go_dev_addr, ssid, ssid_len);
+ }
+
+ return buf;
+}
+
+
+struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p,
+ int client_freq,
+ const u8 *go_dev_addr,
+ const u8 *ssid, size_t ssid_len)
+{
+ return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid,
+ ssid_len);
+}
+
+
+struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p,
+ int client_freq,
+ const u8 *go_dev_addr,
+ const u8 *ssid, size_t ssid_len)
+{
+ return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid,
+ ssid_len);
+}
+
+
+int p2p_process_nfc_connection_handover(struct p2p_data *p2p,
+ struct p2p_nfc_params *params)
+{
+ struct p2p_message msg;
+ struct p2p_device *dev;
+ const u8 *p2p_dev_addr;
+ int freq;
+ enum p2p_role_indication role;
+
+ params->next_step = NO_ACTION;
+
+ if (p2p_parse_ies_separate(params->wsc_attr, params->wsc_len,
+ params->p2p_attr, params->p2p_len, &msg)) {
+ p2p_dbg(p2p, "Failed to parse WSC/P2P attributes from NFC");
+ p2p_parse_free(&msg);
+ return -1;
+ }
+
+ if (msg.p2p_device_addr)
+ p2p_dev_addr = msg.p2p_device_addr;
+ else if (msg.device_id)
+ p2p_dev_addr = msg.device_id;
+ else {
+ p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id");
+ p2p_parse_free(&msg);
+ return -1;
+ }
+
+ if (msg.oob_dev_password) {
+ os_memcpy(params->oob_dev_pw, msg.oob_dev_password,
+ msg.oob_dev_password_len);
+ params->oob_dev_pw_len = msg.oob_dev_password_len;
+ }
+
+ dev = p2p_create_device(p2p, p2p_dev_addr);
+ if (dev == NULL) {
+ p2p_parse_free(&msg);
+ return -1;
+ }
+
+ params->peer = &dev->info;
+
+ os_get_reltime(&dev->last_seen);
+ dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
+ p2p_copy_wps_info(p2p, dev, 0, &msg);
+
+ if (!msg.oob_go_neg_channel) {
+ p2p_dbg(p2p, "OOB GO Negotiation Channel attribute not included");
+ return -1;
+ }
+
+ if (msg.oob_go_neg_channel[3] == 0 &&
+ msg.oob_go_neg_channel[4] == 0)
+ freq = 0;
+ else
+ freq = p2p_channel_to_freq(msg.oob_go_neg_channel[3],
+ msg.oob_go_neg_channel[4]);
+ if (freq < 0) {
+ p2p_dbg(p2p, "Unknown peer OOB GO Neg channel");
+ return -1;
+ }
+ role = msg.oob_go_neg_channel[5];
+
+ if (role == P2P_GO_IN_A_GROUP) {
+ p2p_dbg(p2p, "Peer OOB GO operating channel: %u MHz", freq);
+ params->go_freq = freq;
+ } else if (role == P2P_CLIENT_IN_A_GROUP) {
+ p2p_dbg(p2p, "Peer (client) OOB GO operating channel: %u MHz",
+ freq);
+ params->go_freq = freq;
+ } else
+ p2p_dbg(p2p, "Peer OOB GO Neg channel: %u MHz", freq);
+ dev->oob_go_neg_freq = freq;
+
+ if (!params->sel && role != P2P_GO_IN_A_GROUP) {
+ freq = p2p_channel_to_freq(p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (freq < 0) {
+ p2p_dbg(p2p, "Own listen channel not known");
+ return -1;
+ }
+ p2p_dbg(p2p, "Use own Listen channel as OOB GO Neg channel: %u MHz", freq);
+ dev->oob_go_neg_freq = freq;
+ }
+
+ if (msg.group_id) {
+ os_memcpy(params->go_dev_addr, msg.group_id, ETH_ALEN);
+ params->go_ssid_len = msg.group_id_len - ETH_ALEN;
+ os_memcpy(params->go_ssid, msg.group_id + ETH_ALEN,
+ params->go_ssid_len);
+ }
+
+ p2p_parse_free(&msg);
+
+ if (dev->flags & P2P_DEV_USER_REJECTED) {
+ p2p_dbg(p2p, "Do not report rejected device");
+ return 0;
+ }
+
+ if (!(dev->flags & P2P_DEV_REPORTED)) {
+ p2p->cfg->dev_found(p2p->cfg->cb_ctx, p2p_dev_addr, &dev->info,
+ !(dev->flags & P2P_DEV_REPORTED_ONCE));
+ dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+ }
+
+ if (role == P2P_GO_IN_A_GROUP && p2p->num_groups > 0)
+ params->next_step = BOTH_GO;
+ else if (role == P2P_GO_IN_A_GROUP)
+ params->next_step = JOIN_GROUP;
+ else if (role == P2P_CLIENT_IN_A_GROUP) {
+ dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY;
+ params->next_step = PEER_CLIENT;
+ } else if (p2p->num_groups > 0)
+ params->next_step = AUTH_JOIN;
+ else if (params->sel)
+ params->next_step = INIT_GO_NEG;
+ else
+ params->next_step = RESP_GO_NEG;
+
+ return 0;
+}
+
+
+void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id,
+ int go_intent,
+ const u8 *own_interface_addr)
+{
+
+ p2p->authorized_oob_dev_pw_id = dev_pw_id;
+ if (dev_pw_id == 0) {
+ p2p_dbg(p2p, "NFC OOB Password unauthorized for static handover");
+ return;
+ }
+
+ p2p_dbg(p2p, "NFC OOB Password (id=%u) authorized for static handover",
+ dev_pw_id);
+
+ p2p->go_intent = go_intent;
+ os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
+}
+
+#endif /* CONFIG_WPS_NFC */
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 2ce6ea6..08e7176 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -9,6 +9,8 @@
#ifndef P2P_H
#define P2P_H
+#include "wps/wps_defs.h"
+
/**
* P2P_MAX_REG_CLASSES - Maximum number of regulatory classes
*/
@@ -50,7 +52,7 @@
};
enum p2p_wps_method {
- WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC
+ WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC
};
/**
@@ -706,6 +708,8 @@
* persistent group (instead of invitation to join an active
* group)
* @channels: Available operating channels for the group
+ * @dev_pw_id: Device Password ID for NFC static handover or -1 if not
+ * used
* Returns: Status code (P2P_SC_*)
*
* This optional callback can be used to implement persistent reconnect
@@ -727,7 +731,8 @@
const u8 *go_dev_addr, const u8 *ssid,
size_t ssid_len, int *go, u8 *group_bssid,
int *force_freq, int persistent_group,
- const struct p2p_channels *channels);
+ const struct p2p_channels *channels,
+ int dev_pw_id);
/**
* invitation_received - Callback on Invitation Request RX
@@ -972,7 +977,7 @@
int go_intent, const u8 *own_interface_addr,
unsigned int force_freq, int persistent_group,
const u8 *force_ssid, size_t force_ssid_len,
- int pd_before_go_neg, unsigned int pref_freq);
+ int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id);
/**
* p2p_authorize - Authorize P2P group formation (GO negotiation)
@@ -1000,7 +1005,7 @@
int go_intent, const u8 *own_interface_addr,
unsigned int force_freq, int persistent_group,
const u8 *force_ssid, size_t force_ssid_len,
- unsigned int pref_freq);
+ unsigned int pref_freq, u16 oob_pw_id);
/**
* p2p_reject - Reject peer device (explicitly block connection attempts)
@@ -1102,12 +1107,14 @@
* @persistent_group: Whether this is to reinvoke a persistent group
* @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if
* force_freq == 0)
+ * @dev_pw_id: Device Password ID from OOB Device Password (NFC) static handover
+ * case or -1 if not used
* Returns: 0 on success, -1 on failure
*/
int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
const u8 *bssid, const u8 *ssid, size_t ssid_len,
unsigned int force_freq, const u8 *go_dev_addr,
- int persistent_group, unsigned int pref_freq);
+ int persistent_group, unsigned int pref_freq, int dev_pw_id);
/**
* p2p_presence_req - Request GO presence
@@ -1372,6 +1379,11 @@
size_t ssid_len;
/**
+ * freq - Operating channel of the group
+ */
+ int freq;
+
+ /**
* cb_ctx - Context to use with callback functions
*/
void *cb_ctx;
@@ -1892,4 +1904,41 @@
*/
const char * p2p_get_state_txt(struct p2p_data *p2p);
+struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p,
+ int client_freq,
+ const u8 *go_dev_addr,
+ const u8 *ssid, size_t ssid_len);
+struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p,
+ int client_freq,
+ const u8 *go_dev_addr,
+ const u8 *ssid, size_t ssid_len);
+
+struct p2p_nfc_params {
+ int sel;
+ const u8 *wsc_attr;
+ size_t wsc_len;
+ const u8 *p2p_attr;
+ size_t p2p_len;
+
+ enum {
+ NO_ACTION, JOIN_GROUP, AUTH_JOIN, INIT_GO_NEG, RESP_GO_NEG,
+ BOTH_GO, PEER_CLIENT
+ } next_step;
+ struct p2p_peer_info *peer;
+ u8 oob_dev_pw[WPS_OOB_PUBKEY_HASH_LEN + 2 +
+ WPS_OOB_DEVICE_PASSWORD_LEN];
+ size_t oob_dev_pw_len;
+ int go_freq;
+ u8 go_dev_addr[ETH_ALEN];
+ u8 go_ssid[32];
+ size_t go_ssid_len;
+};
+
+int p2p_process_nfc_connection_handover(struct p2p_data *p2p,
+ struct p2p_nfc_params *params);
+
+void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id,
+ int go_intent,
+ const u8 *own_interface_addr);
+
#endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index 42c0232..664fade 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -328,6 +328,23 @@
}
+void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country,
+ u8 oper_class, u8 channel,
+ enum p2p_role_indication role)
+{
+ /* OOB Group Owner Negotiation Channel */
+ wpabuf_put_u8(buf, P2P_ATTR_OOB_GO_NEG_CHANNEL);
+ wpabuf_put_le16(buf, 6);
+ wpabuf_put_data(buf, country, 3);
+ wpabuf_put_u8(buf, oper_class); /* Operating Class */
+ wpabuf_put_u8(buf, channel); /* Channel Number */
+ wpabuf_put_u8(buf, (u8) role); /* Role indication */
+ wpa_printf(MSG_DEBUG, "P2P: * OOB GO Negotiation Channel: Operating "
+ "Class %u Channel %u Role %d",
+ oper_class, channel, role);
+}
+
+
static int p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
const char *val)
{
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index a887a5e..76436f5 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -103,6 +103,8 @@
return DEV_PW_USER_SPECIFIED;
case WPS_PBC:
return DEV_PW_PUSHBUTTON;
+ case WPS_NFC:
+ return DEV_PW_NFC_CONNECTION_HANDOVER;
default:
return DEV_PW_DEFAULT;
}
@@ -118,6 +120,8 @@
return "Keypad";
case WPS_PBC:
return "PBC";
+ case WPS_NFC:
+ return "NFC";
default:
return "??";
}
@@ -131,6 +135,7 @@
u8 *len;
u8 group_capab;
size_t extra = 0;
+ u16 pw_id;
#ifdef CONFIG_WIFI_DISPLAY
if (p2p->wfd_ie_go_neg)
@@ -172,8 +177,10 @@
p2p_buf_update_ie_hdr(buf, len);
/* WPS IE with Device Password ID attribute */
- if (p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method),
- 0) < 0) {
+ pw_id = p2p_wps_method_pw_id(peer->wps_method);
+ if (peer->oob_pw_id)
+ pw_id = peer->oob_pw_id;
+ if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) {
p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Request");
wpabuf_free(buf);
return NULL;
@@ -210,6 +217,8 @@
}
freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+ if (dev->oob_go_neg_freq > 0)
+ freq = dev->oob_go_neg_freq;
if (freq <= 0) {
p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
MACSTR " to send GO Negotiation Request",
@@ -250,6 +259,7 @@
u8 *len;
u8 group_capab;
size_t extra = 0;
+ u16 pw_id;
p2p_dbg(p2p, "Building GO Negotiation Response");
@@ -312,9 +322,10 @@
p2p_buf_update_ie_hdr(buf, len);
/* WPS IE with Device Password ID attribute */
- if (p2p_build_wps_ie(p2p, buf,
- p2p_wps_method_pw_id(peer ? peer->wps_method :
- WPS_NOT_READY), 0) < 0) {
+ pw_id = p2p_wps_method_pw_id(peer ? peer->wps_method : WPS_NOT_READY);
+ if (peer && peer->oob_pw_id)
+ pw_id = peer->oob_pw_id;
+ if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) {
p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Response");
wpabuf_free(buf);
return NULL;
@@ -605,7 +616,11 @@
if (dev && dev->flags & P2P_DEV_USER_REJECTED) {
p2p_dbg(p2p, "User has rejected this peer");
status = P2P_SC_FAIL_REJECTED_BY_USER;
- } else if (dev == NULL || dev->wps_method == WPS_NOT_READY) {
+ } else if (dev == NULL ||
+ (dev->wps_method == WPS_NOT_READY &&
+ (p2p->authorized_oob_dev_pw_id == 0 ||
+ p2p->authorized_oob_dev_pw_id !=
+ msg.dev_password_id))) {
p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR,
MAC2STR(sa));
status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
@@ -692,6 +707,28 @@
}
break;
default:
+ if (msg.dev_password_id &&
+ msg.dev_password_id == dev->oob_pw_id) {
+ p2p_dbg(p2p, "Peer using NFC");
+ if (dev->wps_method != WPS_NFC) {
+ p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
+ p2p_wps_method_str(
+ dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
+ }
+#ifdef CONFIG_WPS_NFC
+ if (p2p->authorized_oob_dev_pw_id &&
+ msg.dev_password_id ==
+ p2p->authorized_oob_dev_pw_id) {
+ p2p_dbg(p2p, "Using static handover with our device password from NFC Tag");
+ dev->wps_method = WPS_NFC;
+ dev->oob_pw_id = p2p->authorized_oob_dev_pw_id;
+ break;
+ }
+#endif /* CONFIG_WPS_NFC */
p2p_dbg(p2p, "Unsupported Device Password ID %d",
msg.dev_password_id);
status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
@@ -1017,6 +1054,17 @@
}
break;
default:
+ if (msg.dev_password_id &&
+ msg.dev_password_id == dev->oob_pw_id) {
+ p2p_dbg(p2p, "Peer using NFC");
+ if (dev->wps_method != WPS_NFC) {
+ p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
+ }
p2p_dbg(p2p, "Unsupported Device Password ID %d",
msg.dev_password_id);
status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index 92b5583..395ca08 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -154,6 +154,7 @@
group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
if (group->num_members >= group->cfg->max_clients)
group_capab |= P2P_GROUP_CAPAB_GROUP_LIMIT;
+ group_capab |= P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION;
p2p_buf_add_capability(ie, dev_capab, group_capab);
}
@@ -397,10 +398,38 @@
#endif /* CONFIG_WIFI_DISPLAY */
+void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf,
+ int max_clients)
+{
+ u8 *group_info;
+ int count = 0;
+ struct p2p_group_member *m;
+
+ p2p_dbg(group->p2p, "* P2P Group Info");
+ group_info = wpabuf_put(buf, 0);
+ wpabuf_put_u8(buf, P2P_ATTR_GROUP_INFO);
+ wpabuf_put_le16(buf, 0); /* Length to be filled */
+ for (m = group->members; m; m = m->next) {
+ p2p_client_info(buf, m);
+ count++;
+ if (max_clients >= 0 && count >= max_clients)
+ break;
+ }
+ WPA_PUT_LE16(group_info + 1,
+ (u8 *) wpabuf_put(buf, 0) - group_info - 3);
+}
+
+
+void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf)
+{
+ p2p_buf_add_group_id(buf, group->p2p->cfg->dev_addr, group->cfg->ssid,
+ group->cfg->ssid_len);
+}
+
+
static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
{
struct wpabuf *p2p_subelems, *ie;
- struct p2p_group_member *m;
p2p_subelems = wpabuf_alloc(500);
if (p2p_subelems == NULL)
@@ -413,17 +442,8 @@
p2p_buf_add_device_info(p2p_subelems, group->p2p, NULL);
/* P2P Group Info: Only when at least one P2P Client is connected */
- if (group->members) {
- u8 *group_info;
- group_info = wpabuf_put(p2p_subelems, 0);
- wpabuf_put_u8(p2p_subelems, P2P_ATTR_GROUP_INFO);
- wpabuf_put_le16(p2p_subelems, 0); /* Length to be filled */
- for (m = group->members; m; m = m->next)
- p2p_client_info(p2p_subelems, m);
- WPA_PUT_LE16(group_info + 1,
- (u8 *) wpabuf_put(p2p_subelems, 0) - group_info -
- 3);
- }
+ if (group->members)
+ p2p_buf_add_group_info(group, p2p_subelems, -1);
ie = p2p_group_encaps_probe_resp(p2p_subelems);
wpabuf_free(p2p_subelems);
@@ -987,3 +1007,9 @@
group->beacon_update = 1;
p2p_group_update_ies(group);
}
+
+
+int p2p_group_get_freq(struct p2p_group *group)
+{
+ return group->cfg->freq;
+}
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index 3e105eb..6ebaa84 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -12,6 +12,8 @@
#include "utils/list.h"
#include "p2p.h"
+enum p2p_role_indication;
+
enum p2p_go_state {
UNKNOWN_GO,
LOCAL_GO,
@@ -25,7 +27,9 @@
struct dl_list list;
struct os_reltime last_seen;
int listen_freq;
+ int oob_go_neg_freq;
enum p2p_wps_method wps_method;
+ u16 oob_pw_id;
struct p2p_peer_info info;
@@ -239,6 +243,7 @@
const u8 *invite_go_dev_addr;
u8 invite_go_dev_addr_buf[ETH_ALEN];
+ int invite_dev_pw_id;
/**
* sd_peer - Pointer to Service Discovery peer
@@ -462,6 +467,8 @@
struct wpabuf *wfd_assoc_bssid;
struct wpabuf *wfd_coupled_sink_info;
#endif /* CONFIG_WIFI_DISPLAY */
+
+ u16 authorized_oob_dev_pw_id;
};
/**
@@ -503,6 +510,8 @@
const u8 *minor_reason_code;
+ const u8 *oob_go_neg_channel;
+
/* P2P Device Info */
const u8 *p2p_device_info;
size_t p2p_device_info_len;
@@ -514,6 +523,7 @@
/* WPS IE */
u16 dev_password_id;
+ int dev_password_id_present;
u16 wps_config_methods;
const u8 *wps_pri_dev_type;
const u8 *wps_sec_dev_type_list;
@@ -528,6 +538,8 @@
size_t model_number_len;
const u8 *serial_number;
size_t serial_number_len;
+ const u8 *oob_dev_password;
+ size_t oob_dev_password_len;
/* DS Parameter Set IE */
const u8 *ds_params;
@@ -578,6 +590,8 @@
int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg);
int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg);
int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg);
+int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p,
+ size_t p2p_len, struct p2p_message *msg);
void p2p_parse_free(struct p2p_message *msg);
int p2p_attr_text(struct wpabuf *data, char *buf, char *end);
int p2p_group_info_parse(const u8 *gi, size_t gi_len,
@@ -602,6 +616,10 @@
void p2p_group_update_ies(struct p2p_group *group);
void p2p_group_force_beacon_update_ies(struct p2p_group *group);
struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g);
+void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf,
+ int max_clients);
+void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf);
+int p2p_group_get_freq(struct p2p_group *group);
void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token);
@@ -633,6 +651,9 @@
void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period,
u16 interval);
void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p);
+void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country,
+ u8 oper_class, u8 channel,
+ enum p2p_role_indication role);
int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
int all_attr);
@@ -680,7 +701,7 @@
void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len);
int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
- const u8 *go_dev_addr);
+ const u8 *go_dev_addr, int dev_pw_id);
void p2p_invitation_req_cb(struct p2p_data *p2p, int success);
void p2p_invitation_resp_cb(struct p2p_data *p2p, int success);
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index 2734386..98cfb33 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -16,7 +16,8 @@
static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
struct p2p_device *peer,
- const u8 *go_dev_addr)
+ const u8 *go_dev_addr,
+ int dev_pw_id)
{
struct wpabuf *buf;
u8 *len;
@@ -85,6 +86,11 @@
wpabuf_put_buf(buf, wfd_ie);
#endif /* CONFIG_WIFI_DISPLAY */
+ if (dev_pw_id >= 0) {
+ /* WSC IE in Invitation Request for NFC static handover */
+ p2p_build_wps_ie(p2p, buf, dev_pw_id, 0);
+ }
+
return buf;
}
@@ -228,7 +234,8 @@
status = p2p->cfg->invitation_process(
p2p->cfg->cb_ctx, sa, msg.group_bssid, msg.group_id,
msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN,
- &go, group_bssid, &op_freq, persistent, &intersection);
+ &go, group_bssid, &op_freq, persistent, &intersection,
+ msg.dev_password_id_present ? msg.dev_password_id : -1);
}
if (op_freq) {
@@ -450,12 +457,14 @@
int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
- const u8 *go_dev_addr)
+ const u8 *go_dev_addr, int dev_pw_id)
{
struct wpabuf *req;
int freq;
freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+ if (freq <= 0)
+ freq = dev->oob_go_neg_freq;
if (freq <= 0) {
p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
MACSTR " to send Invitation Request",
@@ -463,7 +472,7 @@
return -1;
}
- req = p2p_build_invitation_req(p2p, dev, go_dev_addr);
+ req = p2p_build_invitation_req(p2p, dev, go_dev_addr, dev_pw_id);
if (req == NULL)
return -1;
if (p2p->state != P2P_IDLE)
@@ -528,7 +537,7 @@
int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
const u8 *bssid, const u8 *ssid, size_t ssid_len,
unsigned int force_freq, const u8 *go_dev_addr,
- int persistent_group, unsigned int pref_freq)
+ int persistent_group, unsigned int pref_freq, int dev_pw_id)
{
struct p2p_device *dev;
@@ -546,9 +555,15 @@
p2p->invite_go_dev_addr = NULL;
wpa_hexdump_ascii(MSG_DEBUG, "Invitation for SSID",
ssid, ssid_len);
+ if (dev_pw_id >= 0) {
+ p2p_dbg(p2p, "Invitation to use Device Password ID %d",
+ dev_pw_id);
+ }
+ p2p->invite_dev_pw_id = dev_pw_id;
dev = p2p_get_device(p2p, peer);
- if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0)) {
+ if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0 &&
+ dev->oob_go_neg_freq <= 0)) {
p2p_dbg(p2p, "Cannot invite unknown P2P Device " MACSTR,
MAC2STR(peer));
return -1;
@@ -586,5 +601,5 @@
os_memcpy(p2p->inv_ssid, ssid, ssid_len);
p2p->inv_ssid_len = ssid_len;
p2p->inv_persistent = persistent_group;
- return p2p_invite_send(p2p, dev, go_dev_addr);
+ return p2p_invite_send(p2p, dev, go_dev_addr, dev_pw_id);
}
diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c
index 097a31d..d6144a0 100644
--- a/src/p2p/p2p_parse.c
+++ b/src/p2p/p2p_parse.c
@@ -268,6 +268,19 @@
wpa_printf(MSG_DEBUG, "P2P: * Minor Reason Code: %u",
*msg->minor_reason_code);
break;
+ case P2P_ATTR_OOB_GO_NEG_CHANNEL:
+ if (len < 6) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short OOB GO Neg "
+ "Channel attribute (length %d)", len);
+ return -1;
+ }
+ msg->oob_go_neg_channel = data;
+ wpa_printf(MSG_DEBUG, "P2P: * OOB GO Neg Channel: "
+ "Country %c%c(0x%02x) Operating Class %d "
+ "Channel Number %d Role %d",
+ data[0], data[1], data[2], data[3], data[4],
+ data[5]);
+ break;
default:
wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d "
"(length %d)", id, len);
@@ -340,6 +353,7 @@
msg->dev_password_id = WPA_GET_BE16(attr.dev_password_id);
wpa_printf(MSG_DEBUG, "P2P: Device Password ID: %d",
msg->dev_password_id);
+ msg->dev_password_id_present = 1;
}
if (attr.primary_dev_type) {
char devtype[WPS_DEV_TYPE_BUFSIZE];
@@ -367,6 +381,9 @@
msg->serial_number = attr.serial_number;
msg->serial_number_len = attr.serial_number_len;
+ msg->oob_dev_password = attr.oob_dev_password;
+ msg->oob_dev_password_len = attr.oob_dev_password_len;
+
return 0;
}
@@ -450,6 +467,33 @@
}
+int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p,
+ size_t p2p_len, struct p2p_message *msg)
+{
+ os_memset(msg, 0, sizeof(*msg));
+
+ msg->wps_attributes = wpabuf_alloc_copy(wsc, wsc_len);
+ if (msg->wps_attributes &&
+ p2p_parse_wps_ie(msg->wps_attributes, msg)) {
+ p2p_parse_free(msg);
+ return -1;
+ }
+
+ msg->p2p_attributes = wpabuf_alloc_copy(p2p, p2p_len);
+ if (msg->p2p_attributes &&
+ p2p_parse_p2p_ie(msg->p2p_attributes, msg)) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to parse P2P IE data");
+ if (msg->p2p_attributes)
+ wpa_hexdump_buf(MSG_MSGDUMP, "P2P: P2P IE data",
+ msg->p2p_attributes);
+ p2p_parse_free(msg);
+ return -1;
+ }
+
+ return 0;
+}
+
+
/**
* p2p_parse_free - Free temporary data from P2P parsing
* @msg: Parsed attributes
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index a294730..4474c3b 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -379,6 +379,8 @@
struct wpa_ptk *ptk;
u8 buf[8];
int res;
+ u8 *kde, *kde_buf = NULL;
+ size_t kde_len;
if (wpa_sm_get_network_ctx(sm) == NULL) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No SSID info "
@@ -435,15 +437,39 @@
os_memcpy(ptk->u.auth.rx_mic_key, buf, 8);
sm->tptk_set = 1;
+ kde = sm->assoc_wpa_ie;
+ kde_len = sm->assoc_wpa_ie_len;
+
+#ifdef CONFIG_P2P
+ if (sm->p2p) {
+ kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 1);
+ if (kde_buf) {
+ u8 *pos;
+ wpa_printf(MSG_DEBUG, "P2P: Add IP Address Request KDE "
+ "into EAPOL-Key 2/4");
+ os_memcpy(kde_buf, kde, kde_len);
+ kde = kde_buf;
+ pos = kde + kde_len;
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = RSN_SELECTOR_LEN + 1;
+ RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_IP_ADDR_REQ);
+ pos += RSN_SELECTOR_LEN;
+ *pos++ = 0x01;
+ kde_len = pos - kde;
+ }
+ }
+#endif /* CONFIG_P2P */
+
if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce,
- sm->assoc_wpa_ie, sm->assoc_wpa_ie_len,
- ptk))
+ kde, kde_len, ptk))
goto failed;
+ os_free(kde_buf);
os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN);
return;
failed:
+ os_free(kde_buf);
wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
}
@@ -1090,6 +1116,14 @@
goto failed;
}
+#ifdef CONFIG_P2P
+ if (ie.ip_addr_alloc) {
+ os_memcpy(sm->p2p_ip_addr, ie.ip_addr_alloc, 3 * 4);
+ wpa_hexdump(MSG_DEBUG, "P2P: IP address info",
+ sm->p2p_ip_addr, sizeof(sm->p2p_ip_addr));
+ }
+#endif /* CONFIG_P2P */
+
if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
NULL, 0, &sm->ptk)) {
goto failed;
@@ -2086,6 +2120,10 @@
#ifdef CONFIG_TDLS
wpa_tdls_assoc(sm);
#endif /* CONFIG_TDLS */
+
+#ifdef CONFIG_P2P
+ os_memset(sm->p2p_ip_addr, 0, sizeof(sm->p2p_ip_addr));
+#endif /* CONFIG_P2P */
}
@@ -2209,6 +2247,7 @@
} else
sm->ssid_len = 0;
sm->wpa_ptk_rekey = config->wpa_ptk_rekey;
+ sm->p2p = config->p2p;
} else {
sm->network_ctx = NULL;
sm->peerkey_enabled = 0;
@@ -2218,6 +2257,7 @@
sm->eap_conf_ctx = NULL;
sm->ssid_len = 0;
sm->wpa_ptk_rekey = 0;
+ sm->p2p = 0;
}
}
@@ -2731,3 +2771,16 @@
return 1;
}
#endif /* CONFIG_PEERKEY */
+
+
+#ifdef CONFIG_P2P
+
+int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf)
+{
+ if (sm == NULL || WPA_GET_BE32(sm->p2p_ip_addr) == 0)
+ return -1;
+ os_memcpy(buf, sm->p2p_ip_addr, 3 * 4);
+ return 0;
+}
+
+#endif /* CONFIG_P2P */
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 9884ce1..20b3f62 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -95,6 +95,7 @@
const u8 *ssid;
size_t ssid_len;
int wpa_ptk_rekey;
+ int p2p;
};
#ifndef CONFIG_NO_WPA
@@ -145,6 +146,8 @@
void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx);
+int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf);
+
#else /* CONFIG_NO_WPA */
static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index cad6c8d..75cfb47 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -58,6 +58,7 @@
u8 ssid[32];
size_t ssid_len;
int wpa_ptk_rekey;
+ int p2p;
u8 own_addr[ETH_ALEN];
const char *ifname;
@@ -122,6 +123,10 @@
u8 *assoc_resp_ies; /* MDIE and FTIE from (Re)Association Response */
size_t assoc_resp_ies_len;
#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_P2P
+ u8 p2p_ip_addr[3 * 4];
+#endif /* CONFIG_P2P */
};
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index ab8d104..e58bdc4 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -345,6 +345,25 @@
}
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_P2P
+ if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
+ RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) {
+ ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key",
+ ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN);
+ return 0;
+ }
+
+ if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 &&
+ RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) {
+ ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG,
+ "WPA: IP Address Allocation in EAPOL-Key",
+ ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN);
+ return 0;
+ }
+#endif /* CONFIG_P2P */
+
return 0;
}
diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h
index 0375767..82b6fa3 100644
--- a/src/rsn_supp/wpa_ie.h
+++ b/src/rsn_supp/wpa_ie.h
@@ -59,6 +59,10 @@
size_t supp_oper_classes_len;
u8 qosinfo;
u16 aid;
+#ifdef CONFIG_P2P
+ const u8 *ip_addr_req;
+ const u8 *ip_addr_alloc;
+#endif /* CONFIG_P2P */
};
int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
diff --git a/src/wps/ndef.c b/src/wps/ndef.c
index 96685d2..2b35064 100644
--- a/src/wps/ndef.c
+++ b/src/wps/ndef.c
@@ -30,6 +30,7 @@
};
static char wifi_handover_type[] = "application/vnd.wfa.wsc";
+static char p2p_handover_type[] = "application/vnd.wfa.p2p";
static int ndef_parse_record(const u8 *data, u32 size,
struct ndef_record *record)
@@ -170,85 +171,26 @@
}
-struct wpabuf * ndef_build_wifi_hc(int begin)
+static int p2p_filter(struct ndef_record *record)
{
- struct wpabuf *hc, *carrier;
-
- carrier = wpabuf_alloc(2 + os_strlen(wifi_handover_type));
- if (carrier == NULL)
- return NULL;
- wpabuf_put_u8(carrier, 0x02); /* Carrier Type Format */
- wpabuf_put_u8(carrier, os_strlen(wifi_handover_type));
- wpabuf_put_str(carrier, wifi_handover_type);
-
- hc = ndef_build_record((begin ? FLAG_MESSAGE_BEGIN : 0) |
- FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "Hc", 2,
- "0", 1, carrier);
- wpabuf_free(carrier);
-
- return hc;
+ if (record->type_length != os_strlen(p2p_handover_type))
+ return 0;
+ if (os_memcmp(record->type, p2p_handover_type,
+ os_strlen(p2p_handover_type)) != 0)
+ return 0;
+ return 1;
}
-struct wpabuf * ndef_build_wifi_hr(void)
+struct wpabuf * ndef_parse_p2p(const struct wpabuf *buf)
{
- struct wpabuf *rn, *cr, *ac_payload, *ac, *hr_payload, *hr;
- struct wpabuf *hc;
+ return ndef_parse_records(buf, p2p_filter);
+}
- rn = wpabuf_alloc(2);
- if (rn == NULL)
- return NULL;
- wpabuf_put_be16(rn, os_random() & 0xffff);
- cr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "cr", 2,
- NULL, 0, rn);
- wpabuf_free(rn);
-
- if (cr == NULL)
- return NULL;
-
- ac_payload = wpabuf_alloc(4);
- if (ac_payload == NULL) {
- wpabuf_free(cr);
- return NULL;
- }
- wpabuf_put_u8(ac_payload, 0x01); /* Carrier Flags: CRS=1 "active" */
- wpabuf_put_u8(ac_payload, 0x01); /* Carrier Data Reference Length */
- wpabuf_put_u8(ac_payload, '0'); /* Carrier Data Reference: "0" */
- wpabuf_put_u8(ac_payload, 0); /* Aux Data Reference Count */
-
- ac = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "ac", 2,
- NULL, 0, ac_payload);
- wpabuf_free(ac_payload);
- if (ac == NULL) {
- wpabuf_free(cr);
- return NULL;
- }
-
- hr_payload = wpabuf_alloc(1 + wpabuf_len(cr) + wpabuf_len(ac));
- if (hr_payload == NULL) {
- wpabuf_free(cr);
- wpabuf_free(ac);
- return NULL;
- }
-
- wpabuf_put_u8(hr_payload, 0x12); /* Connection Handover Version 1.2 */
- wpabuf_put_buf(hr_payload, cr);
- wpabuf_put_buf(hr_payload, ac);
- wpabuf_free(cr);
- wpabuf_free(ac);
-
- hr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "Hr", 2,
- NULL, 0, hr_payload);
- wpabuf_free(hr_payload);
- if (hr == NULL)
- return NULL;
-
- hc = ndef_build_wifi_hc(0);
- if (hc == NULL) {
- wpabuf_free(hr);
- return NULL;
- }
-
- return wpabuf_concat(hr, hc);
+struct wpabuf * ndef_build_p2p(const struct wpabuf *buf)
+{
+ return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END |
+ FLAG_TNF_RFC2046, p2p_handover_type,
+ os_strlen(p2p_handover_type), NULL, 0, buf);
}
diff --git a/src/wps/wps.c b/src/wps/wps.c
index 22d7eea..3d019f1 100644
--- a/src/wps/wps.c
+++ b/src/wps/wps.c
@@ -18,6 +18,7 @@
#ifdef CONFIG_WPS_TESTING
int wps_version_number = 0x20;
int wps_testing_dummy_cred = 0;
+int wps_corrupt_pkhash = 0;
#endif /* CONFIG_WPS_TESTING */
@@ -58,6 +59,10 @@
}
#ifdef CONFIG_WPS_NFC
+ if (cfg->pin == NULL &&
+ cfg->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)
+ data->dev_pw_id = cfg->dev_pw_id;
+
if (cfg->wps->ap && !cfg->registrar && cfg->wps->ap_nfc_dev_pw_id) {
/* Keep AP PIN as alternative Device Password */
data->alt_dev_pw_id = data->dev_pw_id;
@@ -133,6 +138,12 @@
data->use_psk_key = cfg->use_psk_key;
data->pbc_in_m1 = cfg->pbc_in_m1;
+ if (cfg->peer_pubkey_hash) {
+ os_memcpy(data->peer_pubkey_hash, cfg->peer_pubkey_hash,
+ WPS_OOB_PUBKEY_HASH_LEN);
+ data->peer_pubkey_hash_set = 1;
+ }
+
return data;
}
@@ -168,7 +179,6 @@
wps_device_data_free(&data->peer_dev);
os_free(data->new_ap_settings);
dh5_free(data->dh_ctx);
- os_free(data->nfc_pw_token);
os_free(data);
}
diff --git a/src/wps/wps.h b/src/wps/wps.h
index 15137a8..6ccce1a 100644
--- a/src/wps/wps.h
+++ b/src/wps/wps.h
@@ -183,6 +183,11 @@
* PBC with the AP.
*/
int pbc_in_m1;
+
+ /**
+ * peer_pubkey_hash - Peer public key hash or %NULL if not known
+ */
+ const u8 *peer_pubkey_hash;
};
struct wps_data * wps_init(const struct wps_config *cfg);
@@ -801,7 +806,8 @@
struct wps_credential *cred);
int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
const u8 *pubkey_hash, u16 pw_id,
- const u8 *dev_pw, size_t dev_pw_len);
+ const u8 *dev_pw, size_t dev_pw_len,
+ int pk_hash_provided_oob);
int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
const u8 *oob_dev_pw,
size_t oob_dev_pw_len);
@@ -815,7 +821,8 @@
int wps_pin_str_valid(const char *pin);
void wps_free_pending_msgs(struct upnp_pending_message *msgs);
-struct wpabuf * wps_get_oob_cred(struct wps_context *wps);
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band,
+ int channel);
int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr);
int wps_attr_text(struct wpabuf *data, char *buf, char *end);
const char * wps_ei_str(enum wps_error_indication ei);
@@ -839,6 +846,9 @@
struct wps_credential *cred);
struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid,
const u8 *addr);
+struct wpabuf * wps_er_nfc_handover_sel(struct wps_er *er,
+ struct wps_context *wps, const u8 *uuid,
+ const u8 *addr, struct wpabuf *pubkey);
int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN]);
char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
@@ -850,15 +860,27 @@
const struct wpabuf *dev_pw);
struct wpabuf * wps_nfc_token_build(int ndef, int id, struct wpabuf *pubkey,
struct wpabuf *dev_pw);
+int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey);
struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
struct wpabuf **privkey,
struct wpabuf **dev_pw);
+struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx,
+ struct wpabuf *nfc_dh_pubkey);
+struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx,
+ struct wpabuf *nfc_dh_pubkey,
+ const u8 *bssid, int freq);
+struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx,
+ struct wpabuf *nfc_dh_pubkey);
+struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx,
+ int nfc_dev_pw_id,
+ struct wpabuf *nfc_dh_pubkey,
+ struct wpabuf *nfc_dev_pw);
/* ndef.c */
struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf);
struct wpabuf * ndef_build_wifi(const struct wpabuf *buf);
-struct wpabuf * ndef_build_wifi_hc(int begin);
-struct wpabuf * ndef_build_wifi_hr(void);
+struct wpabuf * ndef_parse_p2p(const struct wpabuf *buf);
+struct wpabuf * ndef_build_p2p(const struct wpabuf *buf);
#ifdef CONFIG_WPS_STRICT
int wps_validate_beacon(const struct wpabuf *wps_ie);
diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c
index 336246e..62d0feb 100644
--- a/src/wps/wps_attr_build.c
+++ b/src/wps/wps_attr_build.c
@@ -38,8 +38,15 @@
wps->wps->dh_ctx = NULL;
pubkey = wpabuf_dup(wps->wps->dh_pubkey);
#ifdef CONFIG_WPS_NFC
- } else if (wps->dev_pw_id >= 0x10 && wps->wps->ap &&
- wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id) {
+ } else if ((wps->dev_pw_id >= 0x10 ||
+ wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) &&
+ (wps->wps->ap ||
+ (wps->wps->ap_nfc_dh_pubkey &&
+ wps->wps->ap_nfc_dev_pw_id ==
+ DEV_PW_NFC_CONNECTION_HANDOVER &&
+ wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)) &&
+ (wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id ||
+ wps->wps->ap_nfc_dh_pubkey)) {
wpa_printf(MSG_DEBUG, "WPS: Using NFC password token DH keys");
if (wps->wps->ap_nfc_dh_privkey == NULL) {
wpa_printf(MSG_DEBUG,
@@ -392,6 +399,14 @@
addr[0] = wpabuf_head(pubkey);
hash_len = wpabuf_len(pubkey);
sha256_vector(1, addr, &hash_len, pubkey_hash);
+#ifdef CONFIG_WPS_TESTING
+ if (wps_corrupt_pkhash) {
+ wpa_hexdump(MSG_DEBUG, "WPS: Real Public Key Hash",
+ pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+ wpa_printf(MSG_INFO, "WPS: Testing - corrupt public key hash");
+ pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN - 2]++;
+ }
+#endif /* CONFIG_WPS_TESTING */
wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD);
wpabuf_put_be16(msg, WPS_OOB_PUBKEY_HASH_LEN + 2 + dev_pw_len);
@@ -399,9 +414,11 @@
pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
wpabuf_put_be16(msg, dev_pw_id);
- wpa_hexdump_key(MSG_DEBUG, "WPS: OOB Device Password",
- dev_pw, dev_pw_len);
- wpabuf_put_data(msg, dev_pw, dev_pw_len);
+ if (dev_pw) {
+ wpa_hexdump_key(MSG_DEBUG, "WPS: OOB Device Password",
+ dev_pw, dev_pw_len);
+ wpabuf_put_data(msg, dev_pw, dev_pw_len);
+ }
return 0;
}
@@ -449,3 +466,23 @@
wpabuf_put_data(msg, addr, ETH_ALEN);
return 0;
}
+
+
+int wps_build_rf_bands_attr(struct wpabuf *msg, u8 rf_bands)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * RF Bands (%x)", rf_bands);
+ wpabuf_put_be16(msg, ATTR_RF_BANDS);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, rf_bands);
+ return 0;
+}
+
+
+int wps_build_ap_channel(struct wpabuf *msg, u16 ap_channel)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * AP Channel (%u)", ap_channel);
+ wpabuf_put_be16(msg, ATTR_AP_CHANNEL);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, ap_channel);
+ return 0;
+}
diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c
index 3999b1b..f4e2e38 100644
--- a/src/wps/wps_attr_parse.c
+++ b/src/wps/wps_attr_parse.c
@@ -263,10 +263,13 @@
attr->dev_password_id = pos;
break;
case ATTR_OOB_DEVICE_PASSWORD:
- if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
- WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
+ if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 ||
len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
- WPS_OOB_DEVICE_PASSWORD_LEN) {
+ WPS_OOB_DEVICE_PASSWORD_LEN ||
+ (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
+ WPS_OOB_DEVICE_PASSWORD_MIN_LEN &&
+ WPA_GET_BE16(pos + WPS_OOB_PUBKEY_HASH_LEN) !=
+ DEV_PW_NFC_CONNECTION_HANDOVER)) {
wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
"Password length %u", len);
return -1;
diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c
index 4b431ad..abf3a4f 100644
--- a/src/wps/wps_common.c
+++ b/src/wps/wps_common.c
@@ -9,6 +9,8 @@
#include "includes.h"
#include "common.h"
+#include "common/defs.h"
+#include "common/ieee802_11_common.h"
#include "crypto/aes_wrap.h"
#include "crypto/crypto.h"
#include "crypto/dh_group5.h"
@@ -16,6 +18,7 @@
#include "crypto/sha256.h"
#include "crypto/random.h"
#include "wps_i.h"
+#include "wps_dev_attr.h"
void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
@@ -349,7 +352,8 @@
#ifdef CONFIG_WPS_OOB
-struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band,
+ int channel)
{
struct wps_data data;
struct wpabuf *plain;
@@ -365,8 +369,10 @@
data.wps = wps;
data.auth_type = wps->auth_types;
data.encr_type = wps->encr_types;
- if (wps_build_version(plain) ||
- wps_build_cred(&data, plain) ||
+ if (wps_build_cred(&data, plain) ||
+ (rf_band && wps_build_rf_bands_attr(plain, rf_band)) ||
+ (channel && wps_build_ap_channel(plain, channel)) ||
+ wps_build_mac_addr(plain, wps->dev.mac_addr) ||
wps_build_wfa_ext(plain, 0, NULL, 0)) {
os_free(data.new_psk);
wpabuf_free(plain);
@@ -412,8 +418,7 @@
if (data == NULL)
return NULL;
- if (wps_build_version(data) ||
- wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
+ if (wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
wpabuf_head(dev_pw), wpabuf_len(dev_pw)) ||
wps_build_wfa_ext(data, 0, NULL, 0)) {
wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password "
@@ -636,12 +641,36 @@
}
+int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey)
+{
+ struct wpabuf *priv = NULL, *pub = NULL;
+ void *dh_ctx;
+
+ dh_ctx = dh5_init(&priv, &pub);
+ if (dh_ctx == NULL)
+ return -1;
+ pub = wpabuf_zeropad(pub, 192);
+ if (pub == NULL) {
+ wpabuf_free(priv);
+ return -1;
+ }
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: Generated new DH pubkey", pub);
+ dh5_free(dh_ctx);
+
+ wpabuf_free(*pubkey);
+ *pubkey = pub;
+ wpabuf_free(*privkey);
+ *privkey = priv;
+
+ return 0;
+}
+
+
struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
struct wpabuf **privkey,
struct wpabuf **dev_pw)
{
- struct wpabuf *priv = NULL, *pub = NULL, *pw;
- void *dh_ctx;
+ struct wpabuf *pw;
u16 val;
pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN);
@@ -655,22 +684,223 @@
return NULL;
}
- dh_ctx = dh5_init(&priv, &pub);
- if (dh_ctx == NULL) {
+ if (wps_nfc_gen_dh(pubkey, privkey) < 0) {
wpabuf_free(pw);
return NULL;
}
- dh5_free(dh_ctx);
*id = 0x10 + val % 0xfff0;
- wpabuf_free(*pubkey);
- *pubkey = pub;
- wpabuf_free(*privkey);
- *privkey = priv;
wpabuf_free(*dev_pw);
*dev_pw = pw;
return wps_nfc_token_build(ndef, *id, *pubkey, *dev_pw);
}
+
+struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx,
+ struct wpabuf *nfc_dh_pubkey)
+{
+ struct wpabuf *msg;
+ void *len;
+
+ if (ctx == NULL)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+ "handover request");
+
+ if (nfc_dh_pubkey == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password "
+ "configured");
+ return NULL;
+ }
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return msg;
+ len = wpabuf_put(msg, 2);
+
+ if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
+ nfc_dh_pubkey, NULL, 0) ||
+ wps_build_uuid_e(msg, ctx->uuid) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ WPA_PUT_BE16(len, wpabuf_len(msg) - 2);
+
+ return msg;
+}
+
+
+static int wps_build_ssid(struct wpabuf *msg, struct wps_context *wps)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * SSID");
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID in Connection Handover Select",
+ wps->ssid, wps->ssid_len);
+ wpabuf_put_be16(msg, ATTR_SSID);
+ wpabuf_put_be16(msg, wps->ssid_len);
+ wpabuf_put_data(msg, wps->ssid, wps->ssid_len);
+ return 0;
+}
+
+
+static int wps_build_ap_freq(struct wpabuf *msg, int freq)
+{
+ enum hostapd_hw_mode mode;
+ u8 channel, rf_band;
+ u16 ap_channel;
+
+ if (freq <= 0)
+ return 0;
+
+ mode = ieee80211_freq_to_chan(freq, &channel);
+ if (mode == NUM_HOSTAPD_MODES)
+ return 0; /* Unknown channel */
+
+ if (mode == HOSTAPD_MODE_IEEE80211G || mode == HOSTAPD_MODE_IEEE80211B)
+ rf_band = WPS_RF_24GHZ;
+ else if (mode == HOSTAPD_MODE_IEEE80211A)
+ rf_band = WPS_RF_50GHZ;
+ else
+ return 0; /* Unknown band */
+ ap_channel = channel;
+
+ if (wps_build_rf_bands_attr(msg, rf_band) ||
+ wps_build_ap_channel(msg, ap_channel))
+ return -1;
+
+ return 0;
+}
+
+
+struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx,
+ struct wpabuf *nfc_dh_pubkey,
+ const u8 *bssid, int freq)
+{
+ struct wpabuf *msg;
+ void *len;
+
+ if (ctx == NULL)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+ "handover select");
+
+ if (nfc_dh_pubkey == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password "
+ "configured");
+ return NULL;
+ }
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return msg;
+ len = wpabuf_put(msg, 2);
+
+ if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
+ nfc_dh_pubkey, NULL, 0) ||
+ wps_build_ssid(msg, ctx) ||
+ wps_build_ap_freq(msg, freq) ||
+ (bssid && wps_build_mac_addr(msg, bssid)) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ WPA_PUT_BE16(len, wpabuf_len(msg) - 2);
+
+ return msg;
+}
+
+
+struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx,
+ struct wpabuf *nfc_dh_pubkey)
+{
+ struct wpabuf *msg;
+
+ if (ctx == NULL)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+ "handover request (P2P)");
+
+ if (nfc_dh_pubkey == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No NFC DH Public Key configured");
+ return NULL;
+ }
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return msg;
+
+ if (wps_build_manufacturer(&ctx->dev, msg) ||
+ wps_build_model_name(&ctx->dev, msg) ||
+ wps_build_model_number(&ctx->dev, msg) ||
+ wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
+ nfc_dh_pubkey, NULL, 0) ||
+ wps_build_rf_bands(&ctx->dev, msg, 0) ||
+ wps_build_serial_number(&ctx->dev, msg) ||
+ wps_build_uuid_e(msg, ctx->uuid) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+
+struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx,
+ int nfc_dev_pw_id,
+ struct wpabuf *nfc_dh_pubkey,
+ struct wpabuf *nfc_dev_pw)
+{
+ struct wpabuf *msg;
+ const u8 *dev_pw;
+ size_t dev_pw_len;
+
+ if (ctx == NULL)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+ "handover select (P2P)");
+
+ if (nfc_dh_pubkey == NULL ||
+ (nfc_dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER &&
+ nfc_dev_pw == NULL)) {
+ wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password "
+ "configured");
+ return NULL;
+ }
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return msg;
+
+ if (nfc_dev_pw) {
+ dev_pw = wpabuf_head(nfc_dev_pw);
+ dev_pw_len = wpabuf_len(nfc_dev_pw);
+ } else {
+ dev_pw = NULL;
+ dev_pw_len = 0;
+ }
+
+ if (wps_build_manufacturer(&ctx->dev, msg) ||
+ wps_build_model_name(&ctx->dev, msg) ||
+ wps_build_model_number(&ctx->dev, msg) ||
+ wps_build_oob_dev_pw(msg, nfc_dev_pw_id, nfc_dh_pubkey,
+ dev_pw, dev_pw_len) ||
+ wps_build_rf_bands(&ctx->dev, msg, 0) ||
+ wps_build_serial_number(&ctx->dev, msg) ||
+ wps_build_uuid_e(msg, ctx->uuid) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
#endif /* CONFIG_WPS_NFC */
diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h
index 3421ac5..6f8a49f 100644
--- a/src/wps/wps_defs.h
+++ b/src/wps/wps_defs.h
@@ -13,6 +13,7 @@
extern int wps_version_number;
extern int wps_testing_dummy_cred;
+extern int wps_corrupt_pkhash;
#define WPS_VERSION wps_version_number
#else /* CONFIG_WPS_TESTING */
@@ -155,7 +156,8 @@
DEV_PW_MACHINE_SPECIFIED = 0x0002,
DEV_PW_REKEY = 0x0003,
DEV_PW_PUSHBUTTON = 0x0004,
- DEV_PW_REGISTRAR_SPECIFIED = 0x0005
+ DEV_PW_REGISTRAR_SPECIFIED = 0x0005,
+ DEV_PW_NFC_CONNECTION_HANDOVER = 0x0007
};
/* Message Type */
@@ -215,7 +217,9 @@
WPS_CFG_SETUP_LOCKED = 15,
WPS_CFG_MSG_TIMEOUT = 16,
WPS_CFG_REG_SESS_TIMEOUT = 17,
- WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18
+ WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18,
+ WPS_CFG_60G_CHAN_NOT_SUPPORTED = 19,
+ WPS_CFG_PUBLIC_KEY_HASH_MISMATCH = 20
};
/* Vendor specific Error Indication for WPS event messages */
diff --git a/src/wps/wps_dev_attr.c b/src/wps/wps_dev_attr.c
index fe736f3..0d01211 100644
--- a/src/wps/wps_dev_attr.c
+++ b/src/wps/wps_dev_attr.c
@@ -85,8 +85,7 @@
}
-static int wps_build_serial_number(struct wps_device_data *dev,
- struct wpabuf *msg)
+int wps_build_serial_number(struct wps_device_data *dev, struct wpabuf *msg)
{
size_t len;
wpa_printf(MSG_DEBUG, "WPS: * Serial Number");
@@ -220,11 +219,7 @@
int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg,
u8 rf_band)
{
- wpa_printf(MSG_DEBUG, "WPS: * RF Bands (%x)", dev->rf_bands);
- wpabuf_put_be16(msg, ATTR_RF_BANDS);
- wpabuf_put_be16(msg, 1);
- wpabuf_put_u8(msg, rf_band ? rf_band : dev->rf_bands);
- return 0;
+ return wps_build_rf_bands_attr(msg, rf_band ? rf_band : dev->rf_bands);
}
diff --git a/src/wps/wps_dev_attr.h b/src/wps/wps_dev_attr.h
index f0169a7..c9034ad 100644
--- a/src/wps/wps_dev_attr.h
+++ b/src/wps/wps_dev_attr.h
@@ -14,6 +14,7 @@
int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_serial_number(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg);
diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c
index 7b86ff7..9d48ca5 100644
--- a/src/wps/wps_enrollee.c
+++ b/src/wps/wps_enrollee.c
@@ -514,6 +514,24 @@
return -1;
}
+ if (wps->peer_pubkey_hash_set) {
+ u8 hash[WPS_HASH_LEN];
+ sha256_vector(1, &pk, &pk_len, hash);
+ if (os_memcmp(hash, wps->peer_pubkey_hash,
+ WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+ wpa_printf(MSG_ERROR, "WPS: Public Key hash mismatch");
+ wpa_hexdump(MSG_DEBUG, "WPS: Received public key",
+ pk, pk_len);
+ wpa_hexdump(MSG_DEBUG, "WPS: Calculated public key "
+ "hash", hash, WPS_OOB_PUBKEY_HASH_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Expected public key hash",
+ wps->peer_pubkey_hash,
+ WPS_OOB_PUBKEY_HASH_LEN);
+ wps->config_error = WPS_CFG_PUBLIC_KEY_HASH_MISMATCH;
+ return -1;
+ }
+ }
+
wpabuf_free(wps->dh_pubkey_r);
wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
if (wps->dh_pubkey_r == NULL)
@@ -929,6 +947,38 @@
return WPS_CONTINUE;
}
+#ifdef CONFIG_WPS_NFC
+ if (wps->peer_pubkey_hash_set) {
+ struct wpabuf *decrypted;
+ struct wps_parse_attr eattr;
+
+ decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+ attr->encr_settings_len);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to decrypt "
+ "Encrypted Settings attribute");
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted "
+ "Settings attribute");
+ if (wps_parse_msg(decrypted, &eattr) < 0 ||
+ wps_process_key_wrap_auth(wps, decrypted,
+ eattr.key_wrap_auth) ||
+ wps_process_creds(wps, eattr.cred, eattr.cred_len,
+ eattr.num_cred, attr->version2 != NULL)) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+ wpabuf_free(decrypted);
+
+ wps->state = WPS_MSG_DONE;
+ return WPS_CONTINUE;
+ }
+#endif /* CONFIG_WPS_NFC */
+
wps->state = SEND_M3;
return WPS_CONTINUE;
}
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
index e729617..8e9ee7a 100644
--- a/src/wps/wps_er.c
+++ b/src/wps/wps_er.c
@@ -2040,8 +2040,7 @@
os_memset(&data, 0, sizeof(data));
data.wps = wps;
data.use_cred = cred;
- if (wps_build_version(ret) ||
- wps_build_cred(&data, ret) ||
+ if (wps_build_cred(&data, ret) ||
wps_build_wfa_ext(ret, 0, NULL, 0)) {
wpabuf_free(ret);
return NULL;
@@ -2071,4 +2070,29 @@
return wps_er_config_token_from_cred(er->wps, ap->ap_settings);
}
+
+struct wpabuf * wps_er_nfc_handover_sel(struct wps_er *er,
+ struct wps_context *wps, const u8 *uuid,
+ const u8 *addr, struct wpabuf *pubkey)
+{
+ struct wps_er_ap *ap;
+
+ if (er == NULL)
+ return NULL;
+
+ ap = wps_er_ap_get(er, NULL, uuid, addr);
+ if (ap == NULL)
+ return NULL;
+ if (ap->ap_settings == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: No settings known for the "
+ "selected AP");
+ return NULL;
+ }
+
+ os_memcpy(wps->ssid, ap->ap_settings->ssid, ap->ap_settings->ssid_len);
+ wps->ssid_len = ap->ap_settings->ssid_len;
+
+ return wps_build_nfc_handover_sel(wps, pubkey, addr, 0);
+}
+
#endif /* CONFIG_WPS_NFC */
diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h
index dac1250..22070db 100644
--- a/src/wps/wps_i.h
+++ b/src/wps/wps_i.h
@@ -75,6 +75,9 @@
size_t alt_dev_password_len;
u16 alt_dev_pw_id;
+ u8 peer_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+ int peer_pubkey_hash_set;
+
/**
* request_type - Request Type attribute from (Re)AssocReq
*/
@@ -173,6 +176,8 @@
size_t dev_pw_len);
struct wpabuf * wps_ie_encapsulate(struct wpabuf *data);
int wps_build_mac_addr(struct wpabuf *msg, const u8 *addr);
+int wps_build_rf_bands_attr(struct wpabuf *msg, u8 rf_bands);
+int wps_build_ap_channel(struct wpabuf *msg, u16 ap_channel);
/* wps_attr_process.c */
int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index 19490a1..6d879be 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -31,9 +31,11 @@
struct wps_nfc_pw_token {
struct dl_list list;
u8 pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+ unsigned int peer_pk_hash_known:1;
u16 pw_id;
u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1];
size_t dev_pw_len;
+ int pk_hash_provided_oob; /* whether own PK hash was provided OOB */
};
@@ -1360,10 +1362,23 @@
pin_len = 8;
#ifdef CONFIG_WPS_NFC
} else if (wps->nfc_pw_token) {
+ if (wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)
+ {
+ wpa_printf(MSG_DEBUG, "WPS: Using NFC connection "
+ "handover and abbreviated WPS handshake "
+ "without Device Password");
+ return 0;
+ }
wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from NFC "
"Password Token");
pin = wps->nfc_pw_token->dev_pw;
pin_len = wps->nfc_pw_token->dev_pw_len;
+ } else if (wps->dev_pw_id >= 0x10 &&
+ wps->wps->ap_nfc_dev_pw_id == wps->dev_pw_id &&
+ wps->wps->ap_nfc_dev_pw) {
+ wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from own NFC Password Token");
+ pin = wpabuf_head(wps->wps->ap_nfc_dev_pw);
+ pin_len = wpabuf_len(wps->wps->ap_nfc_dev_pw);
#endif /* CONFIG_WPS_NFC */
} else {
pin = wps_registrar_get_pin(wps->wps->registrar, wps->uuid_e,
@@ -1777,6 +1792,7 @@
static struct wpabuf * wps_build_m2(struct wps_data *wps)
{
struct wpabuf *msg;
+ int config_in_m2 = 0;
if (random_get_bytes(wps->nonce_r, WPS_NONCE_LEN) < 0)
return NULL;
@@ -1807,14 +1823,41 @@
wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
wps_build_dev_password_id(msg, wps->dev_pw_id) ||
wps_build_os_version(&wps->wps->dev, msg) ||
- wps_build_wfa_ext(msg, 0, NULL, 0) ||
- wps_build_authenticator(wps, msg)) {
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+#ifdef CONFIG_WPS_NFC
+ if (wps->nfc_pw_token && wps->nfc_pw_token->pk_hash_provided_oob &&
+ wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
+ /*
+ * Use abbreviated handshake since public key hash allowed
+ * Enrollee to validate our public key similarly to how Enrollee
+ * public key was validated. There is no need to validate Device
+ * Password in this case.
+ */
+ struct wpabuf *plain = wpabuf_alloc(500);
+ if (plain == NULL ||
+ wps_build_cred(wps, plain) ||
+ wps_build_key_wrap_auth(wps, plain) ||
+ wps_build_encr_settings(wps, msg, plain)) {
+ wpabuf_free(msg);
+ wpabuf_free(plain);
+ return NULL;
+ }
+ wpabuf_free(plain);
+ config_in_m2 = 1;
+ }
+#endif /* CONFIG_WPS_NFC */
+
+ if (wps_build_authenticator(wps, msg)) {
wpabuf_free(msg);
return NULL;
}
wps->int_reg = 1;
- wps->state = RECV_M3;
+ wps->state = config_in_m2 ? RECV_DONE : RECV_M3;
return msg;
}
@@ -2528,6 +2571,9 @@
wps->dev_pw_id != DEV_PW_USER_SPECIFIED &&
wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED &&
wps->dev_pw_id != DEV_PW_REGISTRAR_SPECIFIED &&
+#ifdef CONFIG_WPS_NFC
+ wps->dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER &&
+#endif /* CONFIG_WPS_NFC */
(wps->dev_pw_id != DEV_PW_PUSHBUTTON ||
!wps->wps->registrar->pbc)) {
wpa_printf(MSG_DEBUG, "WPS: Unsupported Device Password ID %d",
@@ -2537,7 +2583,8 @@
}
#ifdef CONFIG_WPS_NFC
- if (wps->dev_pw_id >= 0x10) {
+ if (wps->dev_pw_id >= 0x10 ||
+ wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
struct wps_nfc_pw_token *token;
const u8 *addr[1];
u8 hash[WPS_HASH_LEN];
@@ -2546,7 +2593,7 @@
wps->dev_pw_id, wps->wps, wps->wps->registrar);
token = wps_get_nfc_pw_token(
&wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id);
- if (token) {
+ if (token && token->peer_pk_hash_known) {
wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
"Password Token");
dl_list_del(&token->list);
@@ -2558,8 +2605,19 @@
WPS_OOB_PUBKEY_HASH_LEN) != 0) {
wpa_printf(MSG_ERROR, "WPS: Public Key hash "
"mismatch");
- return WPS_FAILURE;
+ wps->state = SEND_M2D;
+ wps->config_error =
+ WPS_CFG_PUBLIC_KEY_HASH_MISMATCH;
+ return WPS_CONTINUE;
}
+ } else if (token) {
+ wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
+ "Password Token (no peer PK hash)");
+ wps->nfc_pw_token = token;
+ } else if (wps->dev_pw_id >= 0x10 &&
+ wps->wps->ap_nfc_dev_pw_id == wps->dev_pw_id &&
+ wps->wps->ap_nfc_dev_pw) {
+ wpa_printf(MSG_DEBUG, "WPS: Found match with own NFC Password Token");
}
}
#endif /* CONFIG_WPS_NFC */
@@ -3493,25 +3551,39 @@
int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
const u8 *pubkey_hash, u16 pw_id,
- const u8 *dev_pw, size_t dev_pw_len)
+ const u8 *dev_pw, size_t dev_pw_len,
+ int pk_hash_provided_oob)
{
struct wps_nfc_pw_token *token;
if (dev_pw_len > WPS_OOB_DEVICE_PASSWORD_LEN)
return -1;
+ if (pw_id == DEV_PW_NFC_CONNECTION_HANDOVER &&
+ (pubkey_hash == NULL || !pk_hash_provided_oob)) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected NFC Password Token "
+ "addition - missing public key hash");
+ return -1;
+ }
+
wps_free_nfc_pw_tokens(®->nfc_pw_tokens, pw_id);
token = os_zalloc(sizeof(*token));
if (token == NULL)
return -1;
- os_memcpy(token->pubkey_hash, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+ token->peer_pk_hash_known = pubkey_hash != NULL;
+ if (pubkey_hash)
+ os_memcpy(token->pubkey_hash, pubkey_hash,
+ WPS_OOB_PUBKEY_HASH_LEN);
token->pw_id = pw_id;
- wpa_snprintf_hex_uppercase((char *) token->dev_pw,
- sizeof(token->dev_pw),
- dev_pw, dev_pw_len);
- token->dev_pw_len = dev_pw_len * 2;
+ token->pk_hash_provided_oob = pk_hash_provided_oob;
+ if (dev_pw) {
+ wpa_snprintf_hex_uppercase((char *) token->dev_pw,
+ sizeof(token->dev_pw),
+ dev_pw, dev_pw_len);
+ token->dev_pw_len = dev_pw_len * 2;
+ }
dl_list_add(®->nfc_pw_tokens, &token->list);
@@ -3540,8 +3612,7 @@
u16 id;
size_t dev_pw_len;
- if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
- WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
+ if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 ||
oob_dev_pw_len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
WPS_OOB_DEVICE_PASSWORD_LEN)
return -1;
@@ -3560,7 +3631,7 @@
wpa_hexdump_key(MSG_DEBUG, "WPS: Device Password", dev_pw, dev_pw_len);
return wps_registrar_add_nfc_pw_token(reg, hash, id, dev_pw,
- dev_pw_len);
+ dev_pw_len, 0);
}
@@ -3570,6 +3641,14 @@
wps_registrar_remove_authorized_mac(reg,
(u8 *) "\xff\xff\xff\xff\xff\xff");
wps_registrar_selected_registrar_changed(reg, 0);
+
+ /*
+ * Free the NFC password token if it was used only for a single protocol
+ * run. The static handover case uses the same password token multiple
+ * times, so do not free that case here.
+ */
+ if (token->peer_pk_hash_known)
+ os_free(token);
}
#endif /* CONFIG_WPS_NFC */
diff --git a/src/wps/wps_validate.c b/src/wps/wps_validate.c
index e366256..1c6a14b 100644
--- a/src/wps/wps_validate.c
+++ b/src/wps/wps_validate.c
@@ -267,7 +267,7 @@
return 0;
}
val = WPA_GET_BE16(config_error);
- if (val > 18) {
+ if (val > 20) {
wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration Error "
"attribute value 0x%04x", val);
return -1;
@@ -290,7 +290,7 @@
return 0;
}
val = WPA_GET_BE16(dev_password_id);
- if (val >= 0x0006 && val <= 0x000f) {
+ if (val >= 0x0008 && val <= 0x000f) {
wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Device Password ID "
"attribute value 0x%04x", val);
return -1;