* [PATCH 1/8] wscutil: Use a utility for building authorized_macs
@ 2020-08-28 12:46 Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 2/8] wscutil: Add wsc_build_beacon Andrew Zaborowski
` (7 more replies)
0 siblings, 8 replies; 9+ messages in thread
From: Andrew Zaborowski @ 2020-08-28 12:46 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 2026 bytes --]
Add the wfa_build_authorized_macs function (wfa_ prefix following the
wfa_extract_ naming) and use it in wsc_build_probe_response. The logic
is changed slightly to treat the first 6-zeros address in the array as
the end of the array.
---
src/wscutil.c | 32 +++++++++++++++++---------------
1 file changed, 17 insertions(+), 15 deletions(-)
diff --git a/src/wscutil.c b/src/wscutil.c
index d78a0354..f15cd215 100644
--- a/src/wscutil.c
+++ b/src/wscutil.c
@@ -2094,6 +2094,20 @@ static void build_wsc_state(struct wsc_attr_builder *builder,
wsc_attr_builder_put_u8(builder, 1); \
wsc_attr_builder_put_u8(builder, 0x20)
+static void wfa_build_authorized_macs(struct wsc_attr_builder *builder,
+ const uint8_t authorized_macs[static 30])
+{
+ int count;
+
+ for (count = 1; count < 5; count++)
+ if (util_mem_is_zero(authorized_macs + count * 6, 6))
+ break;
+
+ wsc_attr_builder_put_u8(builder, WSC_WFA_EXTENSION_AUTHORIZED_MACS);
+ wsc_attr_builder_put_u8(builder, count * 6);
+ wsc_attr_builder_put_bytes(builder, authorized_macs, count * 6);
+}
+
uint8_t *wsc_build_credential(const struct wsc_credential *in, size_t *out_len)
{
struct wsc_attr_builder *builder;
@@ -2204,21 +2218,9 @@ uint8_t *wsc_build_probe_response(
START_WFA_VENDOR_EXTENSION();
- if (!util_mem_is_zero(probe_response->authorized_macs, 30)) {
- int count;
-
- for (count = 1; count < 5; count++)
- if (util_mem_is_zero(probe_response->authorized_macs +
- count * 6, 30 - count * 6))
- break;
-
- wsc_attr_builder_put_u8(builder,
- WSC_WFA_EXTENSION_AUTHORIZED_MACS);
- wsc_attr_builder_put_u8(builder, count * 6);
- wsc_attr_builder_put_bytes(builder,
- probe_response->authorized_macs,
- count * 6);
- }
+ if (!util_mem_is_zero(probe_response->authorized_macs, 6))
+ wfa_build_authorized_macs(builder,
+ probe_response->authorized_macs);
if (probe_response->reg_config_methods) {
wsc_attr_builder_put_u8(builder,
--
2.25.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 2/8] wscutil: Add wsc_build_beacon
2020-08-28 12:46 [PATCH 1/8] wscutil: Use a utility for building authorized_macs Andrew Zaborowski
@ 2020-08-28 12:46 ` Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 3/8] eapol: Handle the use_eapol_start flag on authenticator Andrew Zaborowski
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Andrew Zaborowski @ 2020-08-28 12:46 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 2934 bytes --]
For consistency also update wsc_build_probe_response to use the same
__builtin_popcount based rf_bands check.
---
src/wscutil.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++-
src/wscutil.h | 1 +
2 files changed, 49 insertions(+), 1 deletion(-)
diff --git a/src/wscutil.c b/src/wscutil.c
index f15cd215..f5ff5d75 100644
--- a/src/wscutil.c
+++ b/src/wscutil.c
@@ -2138,6 +2138,53 @@ static void build_credential(struct wsc_attr_builder *builder,
l_free(data);
}
+uint8_t *wsc_build_beacon(const struct wsc_beacon *beacon, size_t *out_len)
+{
+ struct wsc_attr_builder *builder;
+ uint8_t *ret;
+
+ builder = wsc_attr_builder_new(512);
+ build_version(builder, 0x10);
+ build_wsc_state(builder, beacon->state);
+
+ if (beacon->ap_setup_locked)
+ build_ap_setup_locked(builder, true);
+
+ if (beacon->selected_registrar) {
+ build_selected_registrar(builder, true);
+ build_device_password_id(builder, beacon->device_password_id);
+ build_selected_registrar_configuration_methods(builder,
+ beacon->selected_reg_config_methods);
+ }
+
+ /* These two "should be provided" if dual-band */
+ if (__builtin_popcount(beacon->rf_bands) > 1) {
+ if (beacon->selected_registrar)
+ build_uuid_e(builder, beacon->uuid_e);
+
+ build_rf_bands(builder, beacon->rf_bands);
+ }
+
+ if (!beacon->version2)
+ goto done;
+
+ START_WFA_VENDOR_EXTENSION();
+
+ if (!util_mem_is_zero(beacon->authorized_macs, 6))
+ wfa_build_authorized_macs(builder, beacon->authorized_macs);
+
+ if (beacon->reg_config_methods) {
+ wsc_attr_builder_put_u8(builder,
+ WSC_WFA_EXTENSION_REGISTRAR_CONFIGRATION_METHODS);
+ wsc_attr_builder_put_u8(builder, 2);
+ wsc_attr_builder_put_u16(builder, beacon->reg_config_methods);
+ }
+
+done:
+ ret = wsc_attr_builder_free(builder, false, out_len);
+ return ret;
+}
+
uint8_t *wsc_build_probe_request(const struct wsc_probe_request *probe_request,
size_t *out_len)
{
@@ -2210,7 +2257,7 @@ uint8_t *wsc_build_probe_response(
build_device_name(builder, probe_response->device_name);
build_configuration_methods(builder, probe_response->config_methods);
- if (probe_response->rf_bands & (probe_response->rf_bands - 1))
+ if (__builtin_popcount(probe_response->rf_bands) > 1)
build_rf_bands(builder, probe_response->rf_bands);
if (!probe_response->version2)
diff --git a/src/wscutil.h b/src/wscutil.h
index 45d72fbb..5bb26d84 100644
--- a/src/wscutil.h
+++ b/src/wscutil.h
@@ -604,6 +604,7 @@ int wsc_parse_wsc_done(const uint8_t *pdu, uint32_t len, struct wsc_done *out);
uint8_t *wsc_build_credential(const struct wsc_credential *in, size_t *out_len);
+uint8_t *wsc_build_beacon(const struct wsc_beacon *beacon, size_t *out_len);
uint8_t *wsc_build_probe_request(const struct wsc_probe_request *probe_request,
size_t *out_len);
uint8_t *wsc_build_probe_response(
--
2.25.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 3/8] eapol: Handle the use_eapol_start flag on authenticator
2020-08-28 12:46 [PATCH 1/8] wscutil: Use a utility for building authorized_macs Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 2/8] wscutil: Add wsc_build_beacon Andrew Zaborowski
@ 2020-08-28 12:46 ` Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 4/8] ap: Stop ongoing handshake on reassociation Andrew Zaborowski
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Andrew Zaborowski @ 2020-08-28 12:46 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 1423 bytes --]
Reuse this flag on the authenticator side with a slightly different
meaning: when it's true we're forced to wait for the EAPoL-Start before
sending the first EAPoL-EAP frame to the supplicant, such as is required
in a WSC enrollee registration when the Association Request didn't have
a v2.0 WSC IE.
---
src/eapol.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/src/eapol.c b/src/eapol.c
index 2d339163..977f720b 100644
--- a/src/eapol.c
+++ b/src/eapol.c
@@ -2417,7 +2417,7 @@ bool eapol_start(struct eapol_sm *sm)
sm->timeout = l_timeout_create(eapol_4way_handshake_time,
eapol_timeout, sm, NULL);
- if (sm->use_eapol_start) {
+ if (!sm->handshake->authenticator && sm->use_eapol_start) {
/*
* We start a short timeout, if EAP packets are not received
* from AP, then we send the EAPoL-Start
@@ -2442,9 +2442,14 @@ bool eapol_start(struct eapol_sm *sm)
if (!sm->protocol_version)
sm->protocol_version = EAPOL_PROTOCOL_VERSION_2004;
- if (sm->handshake->settings_8021x)
- eap_start(sm->eap);
- else {
+ if (sm->handshake->settings_8021x) {
+ /*
+ * If we're allowed to, send EAP Identity request
+ * immediately, otherwise wait for an EAPoL-Start.
+ */
+ if (!sm->use_eapol_start)
+ eap_start(sm->eap);
+ } else {
if (L_WARN_ON(!sm->handshake->have_pmk))
return false;
--
2.25.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 4/8] ap: Stop ongoing handshake on reassociation
2020-08-28 12:46 [PATCH 1/8] wscutil: Use a utility for building authorized_macs Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 2/8] wscutil: Add wsc_build_beacon Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 3/8] eapol: Handle the use_eapol_start flag on authenticator Andrew Zaborowski
@ 2020-08-28 12:46 ` Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 5/8] ap: Push Button mode API and beacon changes Andrew Zaborowski
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Andrew Zaborowski @ 2020-08-28 12:46 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 2967 bytes --]
On a new association or re-association, in addition to forgetting a
complete RSN Association, also stop the EAPoL SM to stop any ongoing
handshake.
Do this in a new function ap_stop_handshake that is now used in a few
places that had copies of the same few lines. I'll be adding some more
lines to this function for WSC support.
---
src/ap.c | 51 +++++++++++++++++++++++++--------------------------
1 file changed, 25 insertions(+), 26 deletions(-)
diff --git a/src/ap.c b/src/ap.c
index bc8b11e8..fa7d030a 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -106,6 +106,19 @@ void ap_config_free(struct ap_config *config)
l_free(config);
}
+static void ap_stop_handshake(struct sta_state *sta)
+{
+ if (sta->sm) {
+ eapol_sm_free(sta->sm);
+ sta->sm = NULL;
+ }
+
+ if (sta->hs) {
+ handshake_state_free(sta->hs);
+ sta->hs = NULL;
+ }
+}
+
static void ap_sta_free(void *data)
{
struct sta_state *sta = data;
@@ -120,11 +133,7 @@ static void ap_sta_free(void *data)
if (sta->gtk_query_cmd_id)
l_genl_family_cancel(ap->nl80211, sta->gtk_query_cmd_id);
- if (sta->sm)
- eapol_sm_free(sta->sm);
-
- if (sta->hs)
- handshake_state_free(sta->hs);
+ ap_stop_handshake(sta);
l_free(sta);
}
@@ -180,14 +189,7 @@ static void ap_del_station(struct sta_state *sta, uint16_t reason,
sta->gtk_query_cmd_id = 0;
}
- if (sta->sm)
- eapol_sm_free(sta->sm);
-
- if (sta->hs)
- handshake_state_free(sta->hs);
-
- sta->hs = NULL;
- sta->sm = NULL;
+ ap_stop_handshake(sta);
if (send_event)
ap->event_func(AP_EVENT_STATION_REMOVED, &event_data,
@@ -268,14 +270,7 @@ static void ap_drop_rsna(struct sta_state *sta)
l_error("Issuing DEL_KEY failed");
}
- if (sta->sm)
- eapol_sm_free(sta->sm);
-
- if (sta->hs)
- handshake_state_free(sta->hs);
-
- sta->hs = NULL;
- sta->sm = NULL;
+ ap_stop_handshake(sta);
if (ap->event_func) {
struct ap_event_station_removed_data event_data = {};
@@ -938,6 +933,12 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
goto unsupported;
}
+ /* 802.11-2016 11.3.5.3 j) */
+ if (sta->rsna)
+ ap_drop_rsna(sta);
+ else if (sta->associated)
+ ap_stop_handshake(sta);
+
if (!sta->associated) {
/*
* Everything fine so far, assign an AID, send response.
@@ -961,10 +962,6 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
sta->assoc_rsne = l_memdup(rsn, rsn[1] + 2);
- /* 802.11-2016 11.3.5.3 j) */
- if (sta->rsna)
- ap_drop_rsna(sta);
-
sta->assoc_resp_cmd_id = ap_assoc_resp(ap, sta, sta->addr, sta->aid, 0,
reassoc,
ap_success_assoc_resp_cb);
@@ -987,8 +984,10 @@ bad_frame:
*
* For now, we need to drop the RSNA.
*/
- if (sta->associated && sta->rsna)
+ if (sta->rsna)
ap_drop_rsna(sta);
+ else if (sta->associated)
+ ap_stop_handshake(sta);
if (rates)
l_uintset_free(rates);
--
2.25.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 5/8] ap: Push Button mode API and beacon changes
2020-08-28 12:46 [PATCH 1/8] wscutil: Use a utility for building authorized_macs Andrew Zaborowski
` (2 preceding siblings ...)
2020-08-28 12:46 ` [PATCH 4/8] ap: Stop ongoing handshake on reassociation Andrew Zaborowski
@ 2020-08-28 12:46 ` Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 6/8] ap: WSC Probe Request processing logic Andrew Zaborowski
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Andrew Zaborowski @ 2020-08-28 12:46 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 10630 bytes --]
This adds the API for putting the AP in Push Button mode, which we'll
need to P2P GO side but may be useful on its own too. A WSC IE is added
to our beacons and probe responses indicating whether the PBC mode is
active.
---
src/ap.c | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
src/ap.h | 15 ++++
2 files changed, 217 insertions(+), 3 deletions(-)
diff --git a/src/ap.c b/src/ap.c
index fa7d030a..ce776553 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -45,6 +45,7 @@
#include "src/dbus.h"
#include "src/nl80211util.h"
#include "src/frame-xchg.h"
+#include "src/wscutil.h"
#include "src/ap.h"
struct ap_state {
@@ -64,6 +65,9 @@ struct ap_state {
uint32_t mlme_watch;
uint8_t gtk[CRYPTO_MAX_GTK_LEN];
uint8_t gtk_index;
+ struct l_timeout *wsc_pbc_timeout;
+ uint16_t wsc_dpid;
+ uint8_t wsc_uuid_r[16];
uint16_t last_aid;
struct l_queue *sta_states;
@@ -103,6 +107,7 @@ void ap_config_free(struct ap_config *config)
}
l_free(config->authorized_macs);
+ l_free(config->wsc_name);
l_free(config);
}
@@ -358,10 +363,15 @@ static size_t ap_build_beacon_pr_head(struct ap_state *ap,
}
/* Beacon / Probe Response frame portion after the TIM IE */
-static size_t ap_build_beacon_pr_tail(struct ap_state *ap, uint8_t *out_buf)
+static size_t ap_build_beacon_pr_tail(struct ap_state *ap, bool pr,
+ uint8_t *out_buf)
{
size_t len;
struct ie_rsn_info rsn;
+ uint8_t *wsc_data;
+ size_t wsc_data_size;
+ uint8_t *wsc_ie;
+ size_t wsc_ie_size;
/* TODO: Country IE between TIM IE and RSNE */
@@ -371,9 +381,140 @@ static size_t ap_build_beacon_pr_tail(struct ap_state *ap, uint8_t *out_buf)
return 0;
len = 2 + out_buf[1];
+ /* WSC IE */
+ if (pr) {
+ struct wsc_probe_response wsc_pr = {};
+
+ wsc_pr.version2 = true;
+ wsc_pr.state = WSC_STATE_CONFIGURED;
+
+ if (ap->wsc_pbc_timeout) {
+ wsc_pr.selected_registrar = true;
+ wsc_pr.device_password_id = ap->wsc_dpid;
+ wsc_pr.selected_reg_config_methods =
+ WSC_CONFIGURATION_METHOD_PUSH_BUTTON;
+ }
+
+ wsc_pr.response_type = WSC_RESPONSE_TYPE_AP;
+ memcpy(wsc_pr.uuid_e, ap->wsc_uuid_r, sizeof(wsc_pr.uuid_e));
+ wsc_pr.primary_device_type =
+ ap->config->wsc_primary_device_type;
+
+ if (ap->config->wsc_name)
+ l_strlcpy(wsc_pr.device_name, ap->config->wsc_name,
+ sizeof(wsc_pr.device_name));
+
+ wsc_pr.config_methods =
+ WSC_CONFIGURATION_METHOD_PUSH_BUTTON;
+
+ if (ap->config->authorized_macs_num) {
+ size_t len;
+
+ len = ap->config->authorized_macs_num * 6;
+ if (len > sizeof(wsc_pr.authorized_macs))
+ len = sizeof(wsc_pr.authorized_macs);
+
+ memcpy(wsc_pr.authorized_macs,
+ ap->config->authorized_macs, len);
+ }
+
+ wsc_data = wsc_build_probe_response(&wsc_pr, &wsc_data_size);
+ } else {
+ struct wsc_beacon wsc_beacon = {};
+
+ wsc_beacon.version2 = true;
+ wsc_beacon.state = WSC_STATE_CONFIGURED;
+
+ if (ap->wsc_pbc_timeout) {
+ wsc_beacon.selected_registrar = true;
+ wsc_beacon.device_password_id = ap->wsc_dpid;
+ wsc_beacon.selected_reg_config_methods =
+ WSC_CONFIGURATION_METHOD_PUSH_BUTTON;
+ }
+
+ if (ap->config->authorized_macs_num) {
+ size_t len;
+
+ len = ap->config->authorized_macs_num * 6;
+ if (len > sizeof(wsc_beacon.authorized_macs))
+ len = sizeof(wsc_beacon.authorized_macs);
+
+ memcpy(wsc_beacon.authorized_macs,
+ ap->config->authorized_macs, len);
+ }
+
+ wsc_data = wsc_build_beacon(&wsc_beacon, &wsc_data_size);
+ }
+
+ if (!wsc_data)
+ return 0;
+
+ wsc_ie = ie_tlv_encapsulate_wsc_payload(wsc_data, wsc_data_size,
+ &wsc_ie_size);
+ l_free(wsc_data);
+
+ if (!wsc_ie)
+ return 0;
+
+ memcpy(out_buf + len, wsc_ie, wsc_ie_size);
+ len += wsc_ie_size;
+ l_free(wsc_ie);
+
return len;
}
+static void ap_set_beacon_cb(struct l_genl_msg *msg, void *user_data)
+{
+ int error = l_genl_msg_get_error(msg);
+
+ if (error < 0)
+ l_error("SET_BEACON failed: %s (%i)", strerror(-error), -error);
+}
+
+static void ap_update_beacon(struct ap_state *ap)
+{
+ struct l_genl_msg *cmd;
+ uint8_t head[256], tail[256];
+ size_t head_len, tail_len;
+ uint64_t wdev_id = netdev_get_wdev_id(ap->netdev);
+ static const uint8_t bcast_addr[6] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+
+ head_len = ap_build_beacon_pr_head(ap, MPDU_MANAGEMENT_SUBTYPE_BEACON,
+ bcast_addr, head, sizeof(head));
+ tail_len = ap_build_beacon_pr_tail(ap, false, tail);
+ if (L_WARN_ON(!head_len || !tail_len))
+ return;
+
+ cmd = l_genl_msg_new_sized(NL80211_CMD_SET_BEACON,
+ 32 + head_len + tail_len);
+ l_genl_msg_append_attr(cmd, NL80211_ATTR_WDEV, 8, &wdev_id);
+ l_genl_msg_append_attr(cmd, NL80211_ATTR_BEACON_HEAD, head_len, head);
+ l_genl_msg_append_attr(cmd, NL80211_ATTR_BEACON_TAIL, tail_len, tail);
+ l_genl_msg_append_attr(cmd, NL80211_ATTR_IE, 0, "");
+ l_genl_msg_append_attr(cmd, NL80211_ATTR_IE_PROBE_RESP, 0, "");
+ l_genl_msg_append_attr(cmd, NL80211_ATTR_IE_ASSOC_RESP, 0, "");
+
+ if (l_genl_family_send(ap->nl80211, cmd, ap_set_beacon_cb, NULL, NULL))
+ return;
+
+ l_genl_msg_unref(cmd);
+ l_error("Issuing SET_BEACON failed");
+}
+
+static void ap_wsc_exit_pbc(struct ap_state *ap)
+{
+ if (!ap->wsc_pbc_timeout)
+ return;
+
+ l_timeout_remove(ap->wsc_pbc_timeout);
+ ap->wsc_dpid = 0;
+ ap_update_beacon(ap);
+
+ ap->event_func(AP_EVENT_PBC_MODE_EXIT, NULL, ap->user_data);
+}
+
static uint32_t ap_send_mgmt_frame(struct ap_state *ap,
const struct mmpdu_header *frame,
size_t frame_len, bool wait_ack,
@@ -1069,6 +1210,9 @@ bad_frame:
l_error("Sending error Reassociation Response failed");
}
+#define AP_WSC_PBC_MONITOR_TIME 120
+#define AP_WSC_PBC_WALK_TIME 120
+
static void ap_probe_resp_cb(struct l_genl_msg *msg, void *user_data)
{
if (l_genl_msg_get_error(msg) < 0)
@@ -1167,7 +1311,7 @@ static void ap_probe_req_cb(const struct mmpdu_header *hdr, const void *body,
len = ap_build_beacon_pr_head(ap,
MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE,
hdr->address_2, resp, sizeof(resp));
- len += ap_build_beacon_pr_tail(ap, resp + len);
+ len += ap_build_beacon_pr_tail(ap, true, resp + len);
ap_send_mgmt_frame(ap, (struct mmpdu_header *) resp, len, false,
ap_probe_resp_cb, NULL);
@@ -1397,7 +1541,7 @@ static struct l_genl_msg *ap_build_cmd_start_ap(struct ap_state *ap)
head_len = ap_build_beacon_pr_head(ap, MPDU_MANAGEMENT_SUBTYPE_BEACON,
bcast_addr, head, sizeof(head));
- tail_len = ap_build_beacon_pr_tail(ap, tail);
+ tail_len = ap_build_beacon_pr_tail(ap, false, tail);
if (!head_len || !tail_len)
return NULL;
@@ -1501,6 +1645,17 @@ struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
/* TODO: Start a Get Survey to decide the channel */
config->channel = 6;
+ if (!config->wsc_name)
+ config->wsc_name = l_strdup(config->ssid);
+
+ if (!config->wsc_primary_device_type.category) {
+ /* Make ourselves a WFA standard PC by default */
+ config->wsc_primary_device_type.category = 1;
+ memcpy(config->wsc_primary_device_type.oui, wsc_wfa_oui, 3);
+ config->wsc_primary_device_type.oui_type = 0x04;
+ config->wsc_primary_device_type.subcategory = 1;
+ }
+
/* TODO: Add all ciphers supported by wiphy */
ap->ciphers = wiphy_select_cipher(wiphy, 0xffff);
ap->group_cipher = wiphy_select_cipher(wiphy, 0xffff);
@@ -1523,6 +1678,8 @@ struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
l_uintset_put(ap->rates, 22); /* 11 Mbps*/
}
+ wsc_uuid_from_addr(netdev_get_address(netdev), ap->wsc_uuid_r);
+
if (crypto_psk_from_passphrase(config->psk, (uint8_t *) config->ssid,
strlen(config->ssid), ap->pmk) < 0)
goto error;
@@ -1702,6 +1859,45 @@ bool ap_station_disconnect(struct ap_state *ap, const uint8_t *mac,
return true;
}
+static void ap_wsc_pbc_timeout_cb(struct l_timeout *timeout, void *user_data)
+{
+ struct ap_state *ap = user_data;
+
+ l_debug("PBC mode timeout");
+ ap_wsc_exit_pbc(ap);
+}
+
+static void ap_wsc_pbc_timeout_destroy(void *user_data)
+{
+ struct ap_state *ap = user_data;
+
+ ap->wsc_pbc_timeout = NULL;
+}
+
+bool ap_push_button(struct ap_state *ap)
+{
+ if (!ap->started)
+ return false;
+
+ /*
+ * WSC v2.0.5 Section 11.3: "Multiple presses of the button are
+ * permitted. If a PBC button on an Enrollee or Registrar is
+ * pressed again during Walk Time, the timers for that device are
+ * restarted at that time [...]"
+ */
+ if (ap->wsc_pbc_timeout) {
+ l_timeout_modify(ap->wsc_pbc_timeout, AP_WSC_PBC_WALK_TIME);
+ return true;
+ }
+
+ ap->wsc_pbc_timeout = l_timeout_create(AP_WSC_PBC_WALK_TIME,
+ ap_wsc_pbc_timeout_cb, ap,
+ ap_wsc_pbc_timeout_destroy);
+ ap->wsc_dpid = WSC_DEVICE_PASSWORD_ID_PUSH_BUTTON;
+ ap_update_beacon(ap);
+ return true;
+}
+
struct ap_if_data {
struct netdev *netdev;
struct ap_state *ap;
@@ -1747,6 +1943,9 @@ static void ap_if_event_func(enum ap_event_type type, const void *event_data,
case AP_EVENT_STATION_ADDED:
case AP_EVENT_STATION_REMOVED:
+ case AP_EVENT_REGISTRATION_START:
+ case AP_EVENT_REGISTRATION_SUCCESS:
+ case AP_EVENT_PBC_MODE_EXIT:
/* Ignored */
break;
}
diff --git a/src/ap.h b/src/ap.h
index 330ae011..cb5238d1 100644
--- a/src/ap.h
+++ b/src/ap.h
@@ -29,6 +29,9 @@ enum ap_event_type {
AP_EVENT_STOPPING,
AP_EVENT_STATION_ADDED,
AP_EVENT_STATION_REMOVED,
+ AP_EVENT_REGISTRATION_START,
+ AP_EVENT_REGISTRATION_SUCCESS,
+ AP_EVENT_PBC_MODE_EXIT,
};
struct ap_event_station_added_data {
@@ -41,6 +44,14 @@ struct ap_event_station_removed_data {
enum mmpdu_reason_code reason;
};
+struct ap_event_registration_start_data {
+ const uint8_t *mac;
+};
+
+struct ap_event_registration_success_data {
+ const uint8_t *mac;
+};
+
typedef void (*ap_event_func_t)(enum ap_event_type type, const void *event_data,
void *user_data);
typedef void (*ap_stopped_func_t)(void *user_data);
@@ -51,6 +62,8 @@ struct ap_config {
uint8_t channel;
uint8_t *authorized_macs;
unsigned int authorized_macs_num;
+ char *wsc_name;
+ struct wsc_primary_device_type wsc_primary_device_type;
bool no_cck_rates : 1;
};
@@ -64,3 +77,5 @@ void ap_free(struct ap_state *ap);
bool ap_station_disconnect(struct ap_state *ap, const uint8_t *mac,
enum mmpdu_reason_code reason);
+
+bool ap_push_button(struct ap_state *ap);
--
2.25.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 6/8] ap: WSC Probe Request processing logic
2020-08-28 12:46 [PATCH 1/8] wscutil: Use a utility for building authorized_macs Andrew Zaborowski
` (3 preceding siblings ...)
2020-08-28 12:46 ` [PATCH 5/8] ap: Push Button mode API and beacon changes Andrew Zaborowski
@ 2020-08-28 12:46 ` Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 7/8] ap: Parse WSC PBC association request and build response Andrew Zaborowski
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Andrew Zaborowski @ 2020-08-28 12:46 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 6946 bytes --]
Implement the caching of WSC probe requests -- when an Enrollee later
associates to start registration we need to have its Probe Request on
file. Also use this cache for PBC "Session Overlap" detection.
---
src/ap.c | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 155 insertions(+)
diff --git a/src/ap.c b/src/ap.c
index ce776553..df0a9942 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -65,6 +65,7 @@ struct ap_state {
uint32_t mlme_watch;
uint8_t gtk[CRYPTO_MAX_GTK_LEN];
uint8_t gtk_index;
+ struct l_queue *wsc_pbc_probes;
struct l_timeout *wsc_pbc_timeout;
uint16_t wsc_dpid;
uint8_t wsc_uuid_r[16];
@@ -92,6 +93,12 @@ struct sta_state {
uint32_t gtk_query_cmd_id;
};
+struct ap_wsc_pbc_probe_record {
+ uint8_t mac[6];
+ uint8_t uuid_e[16];
+ uint64_t timestamp;
+};
+
static uint32_t netdev_watch;
void ap_config_free(struct ap_config *config)
@@ -165,6 +172,8 @@ static void ap_reset(struct ap_state *ap)
ap_config_free(ap->config);
ap->config = NULL;
+ l_queue_destroy(ap->wsc_pbc_probes, l_free);
+
ap->started = false;
}
@@ -643,6 +652,24 @@ error:
ap_del_station(sta, MMPDU_REASON_CODE_UNSPECIFIED, true);
}
+struct ap_pbc_record_expiry_data {
+ uint64_t min_time;
+ const uint8_t *mac;
+};
+
+static bool ap_wsc_pbc_record_expire(void *data, void *user_data)
+{
+ struct ap_wsc_pbc_probe_record *record = data;
+ const struct ap_pbc_record_expiry_data *expiry_data = user_data;
+
+ if (record->timestamp > expiry_data->min_time &&
+ memcmp(record->mac, expiry_data->mac, 6))
+ return false;
+
+ l_free(record);
+ return true;
+}
+
static struct l_genl_msg *ap_build_cmd_del_key(struct ap_state *ap)
{
uint32_t ifindex = netdev_get_ifindex(ap->netdev);
@@ -1213,6 +1240,115 @@ bad_frame:
#define AP_WSC_PBC_MONITOR_TIME 120
#define AP_WSC_PBC_WALK_TIME 120
+static void ap_process_wsc_probe_req(struct ap_state *ap, const uint8_t *from,
+ const uint8_t *wsc_data,
+ size_t wsc_data_len)
+{
+ struct wsc_probe_request req;
+ struct ap_pbc_record_expiry_data expiry_data;
+ struct ap_wsc_pbc_probe_record *record;
+ uint64_t now;
+ bool empty;
+ uint8_t first_sta_addr[6] = {};
+ const struct l_queue_entry *entry;
+
+ if (wsc_parse_probe_request(wsc_data, wsc_data_len, &req) < 0)
+ return;
+
+ if (!(req.config_methods & WSC_CONFIGURATION_METHOD_PUSH_BUTTON))
+ return;
+
+ if (req.device_password_id != WSC_DEVICE_PASSWORD_ID_PUSH_BUTTON)
+ return;
+
+ /* Save the address of the first enrollee record */
+ record = l_queue_peek_head(ap->wsc_pbc_probes);
+ if (record)
+ memcpy(first_sta_addr, record->mac, 6);
+
+ now = l_time_now();
+
+ /*
+ * Expire entries older than PBC Monitor Time. While there also drop
+ * older entries from the same Enrollee that sent us this new Probe
+ * Request. It's unclear whether we should also match by the UUID-E.
+ */
+ expiry_data.min_time = now - AP_WSC_PBC_MONITOR_TIME * 1000000;
+ expiry_data.mac = from;
+ l_queue_foreach_remove(ap->wsc_pbc_probes, ap_wsc_pbc_record_expire,
+ &expiry_data);
+
+ empty = l_queue_isempty(ap->wsc_pbc_probes);
+
+ if (!ap->wsc_pbc_probes)
+ ap->wsc_pbc_probes = l_queue_new();
+
+ /* Add new record */
+ record = l_new(struct ap_wsc_pbc_probe_record, 1);
+ memcpy(record->mac, from, 6);
+ memcpy(record->uuid_e, req.uuid_e, sizeof(record->uuid_e));
+ record->timestamp = now;
+ l_queue_push_tail(ap->wsc_pbc_probes, record);
+
+ /*
+ * If queue was non-empty and we've added one more record then we
+ * now have seen more than one PBC enrollee during the PBC Monitor
+ * Time and must exit "active PBC mode" due to "session overlap".
+ * WSC v2.0.5 Section 11.3:
+ * "Within the PBC Monitor Time, if the Registrar receives PBC
+ * probe requests from more than one Enrollee [...] then the
+ * Registrar SHALL signal a "session overlap" error. As a result,
+ * the Registrar shall refuse to enter active PBC mode and shall
+ * also refuse to perform a PBC-based Registration Protocol
+ * exchange [...]"
+ */
+ if (empty)
+ return;
+
+ if (ap->wsc_pbc_timeout) {
+ l_debug("Exiting PBC mode due to Session Overlap");
+ ap_wsc_exit_pbc(ap);
+ }
+
+ /*
+ * "If the Registrar is engaged in PBC Registration Protocol
+ * exchange with an Enrollee and receives a Probe Request or M1
+ * Message from another Enrollee, then the Registrar should
+ * signal a "session overlap" error".
+ *
+ * For simplicity just interrupt the handshake with that enrollee.
+ */
+ for (entry = l_queue_get_entries(ap->sta_states); entry;
+ entry = entry->next) {
+ struct sta_state *sta = entry->data;
+
+ if (!sta->associated || sta->assoc_rsne)
+ continue;
+
+ /*
+ * Check whether this enrollee is in PBC Registration
+ * Protocol by comparing its mac with the first (and only)
+ * record we had in ap->wsc_pbc_probes. If we had more
+ * than one record we wouldn't have been in
+ * "active PBC mode".
+ */
+ if (memcmp(sta->addr, first_sta_addr, 6) ||
+ !memcmp(sta->addr, from, 6))
+ continue;
+
+ l_debug("Interrupting handshake with %s due to Session Overlap",
+ util_address_to_string(sta->addr));
+
+ if (sta->hs) {
+ netdev_handshake_failed(sta->hs,
+ MMPDU_REASON_CODE_DISASSOC_AP_BUSY);
+ sta->sm = NULL;
+ }
+
+ ap_remove_sta(sta);
+ }
+}
+
static void ap_probe_resp_cb(struct l_genl_msg *msg, void *user_data)
{
if (l_genl_msg_get_error(msg) < 0)
@@ -1239,6 +1375,8 @@ static void ap_probe_req_cb(const struct mmpdu_header *hdr, const void *body,
const uint8_t *bssid = netdev_get_address(ap->netdev);
bool match = false;
uint8_t resp[512];
+ uint8_t *wsc_data;
+ ssize_t wsc_data_len;
l_info("AP Probe Request from %s",
util_address_to_string(hdr->address_2));
@@ -1308,6 +1446,18 @@ static void ap_probe_req_cb(const struct mmpdu_header *hdr, const void *body,
if (!match)
return;
+ /*
+ * Process the WSC IE first as it may cause us to exit "active PBC
+ * mode" and that can be immediately reflected in our Probe Response.
+ */
+ wsc_data = ie_tlv_extract_wsc_payload(req->ies, body_len - sizeof(*req),
+ &wsc_data_len);
+ if (wsc_data) {
+ ap_process_wsc_probe_req(ap, hdr->address_2,
+ wsc_data, wsc_data_len);
+ l_free(wsc_data);
+ }
+
len = ap_build_beacon_pr_head(ap,
MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE,
hdr->address_2, resp, sizeof(resp));
@@ -1879,6 +2029,11 @@ bool ap_push_button(struct ap_state *ap)
if (!ap->started)
return false;
+ if (l_queue_length(ap->wsc_pbc_probes) > 1) {
+ l_debug("Can't start PBC mode due to Session Overlap");
+ return false;
+ }
+
/*
* WSC v2.0.5 Section 11.3: "Multiple presses of the button are
* permitted. If a PBC button on an Enrollee or Registrar is
--
2.25.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 7/8] ap: Parse WSC PBC association request and build response
2020-08-28 12:46 [PATCH 1/8] wscutil: Use a utility for building authorized_macs Andrew Zaborowski
` (4 preceding siblings ...)
2020-08-28 12:46 ` [PATCH 6/8] ap: WSC Probe Request processing logic Andrew Zaborowski
@ 2020-08-28 12:46 ` Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 8/8] ap: Start EAP-WSC authentication with WSC enrollees Andrew Zaborowski
2020-08-28 15:55 ` [PATCH 1/8] wscutil: Use a utility for building authorized_macs Denis Kenzior
7 siblings, 0 replies; 9+ messages in thread
From: Andrew Zaborowski @ 2020-08-28 12:46 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 10999 bytes --]
Check the conditions for PBC enrollee registration when we receive the
Association Request with WSC IE and indicate to the enrollee whether we
accept the association using a WSC IE in the Association Response.
After this, a NULL sta->assoc_rsne indicates that the station is not
establishing the RSNA and is a WSC enrollee.
---
src/ap.c | 186 ++++++++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 151 insertions(+), 35 deletions(-)
diff --git a/src/ap.c b/src/ap.c
index df0a9942..d7d0bbad 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -91,6 +91,8 @@ struct sta_state {
struct eapol_sm *sm;
struct handshake_state *hs;
uint32_t gtk_query_cmd_id;
+ uint8_t wsc_uuid_e[16];
+ bool wsc_v2;
};
struct ap_wsc_pbc_probe_record {
@@ -906,7 +908,7 @@ static void ap_fail_assoc_resp_cb(struct l_genl_msg *msg, void *user_data)
}
static uint32_t ap_assoc_resp(struct ap_state *ap, struct sta_state *sta,
- const uint8_t *dest, uint16_t aid,
+ const uint8_t *dest,
enum mmpdu_reason_code status_code,
bool reassoc, l_genl_msg_func_t callback)
{
@@ -934,7 +936,7 @@ static uint32_t ap_assoc_resp(struct ap_state *ap, struct sta_state *sta,
resp = (void *) mmpdu_body(mpdu);
l_put_le16(capability, &resp->capability);
resp->status_code = L_CPU_TO_LE16(status_code);
- resp->aid = L_CPU_TO_LE16(aid | 0xc000);
+ resp->aid = sta ? L_CPU_TO_LE16(sta->aid | 0xc000) : 0;
/* Supported Rates IE */
resp->ies[ies_len++] = IE_TYPE_SUPPORTED_RATES;
@@ -956,6 +958,38 @@ static uint32_t ap_assoc_resp(struct ap_state *ap, struct sta_state *sta,
resp->ies[ies_len++] = count;
ies_len += count;
+ if (sta && !sta->assoc_rsne) {
+ struct wsc_association_response wsc_resp = {};
+ uint8_t *wsc_data;
+ size_t wsc_data_len;
+ uint8_t *wsc_ie;
+ size_t wsc_ie_len;
+
+ wsc_resp.response_type = WSC_RESPONSE_TYPE_AP;
+ wsc_resp.version2 = sta->wsc_v2;
+
+ wsc_data = wsc_build_association_response(&wsc_resp,
+ &wsc_data_len);
+ if (!wsc_data) {
+ l_error("wsc_build_beacon error");
+ goto send_frame;
+ }
+
+ wsc_ie = ie_tlv_encapsulate_wsc_payload(wsc_data, wsc_data_len,
+ &wsc_ie_len);
+ l_free(wsc_data);
+
+ if (!wsc_ie) {
+ l_error("ie_tlv_encapsulate_wsc_payload error");
+ goto send_frame;
+ }
+
+ memcpy(resp->ies + ies_len, wsc_ie, wsc_ie_len);
+ ies_len += wsc_ie_len;
+ l_free(wsc_ie);
+ }
+
+send_frame:
return ap_send_mgmt_frame(ap, mpdu, resp->ies + ies_len - mpdu_buf,
true, callback, sta);
}
@@ -1030,8 +1064,8 @@ static int ap_parse_supported_rates(struct ie_tlv_iter *iter,
*/
static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
const struct mmpdu_field_capability *capability,
- uint16_t listen_interval,
- struct ie_tlv_iter *ies)
+ uint16_t listen_interval, const uint8_t *ies,
+ size_t ies_len)
{
struct ap_state *ap = sta->ap;
const char *ssid = NULL;
@@ -1040,6 +1074,9 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
struct l_uintset *rates = NULL;
struct ie_rsn_info rsn_info;
int err;
+ struct ie_tlv_iter iter;
+ uint8_t *wsc_data = NULL;
+ ssize_t wsc_data_len;
if (sta->assoc_resp_cmd_id)
return;
@@ -1049,16 +1086,20 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
goto unsupported;
}
- while (ie_tlv_iter_next(ies))
- switch (ie_tlv_iter_get_tag(ies)) {
+ wsc_data = ie_tlv_extract_wsc_payload(ies, ies_len, &wsc_data_len);
+
+ ie_tlv_iter_init(&iter, ies, ies_len);
+
+ while (ie_tlv_iter_next(&iter))
+ switch (ie_tlv_iter_get_tag(&iter)) {
case IE_TYPE_SSID:
- ssid = (const char *) ie_tlv_iter_get_data(ies);
- ssid_len = ie_tlv_iter_get_length(ies);
+ ssid = (const char *) ie_tlv_iter_get_data(&iter);
+ ssid_len = ie_tlv_iter_get_length(&iter);
break;
case IE_TYPE_SUPPORTED_RATES:
case IE_TYPE_EXTENDED_SUPPORTED_RATES:
- if (ap_parse_supported_rates(ies, &rates) < 0) {
+ if (ap_parse_supported_rates(&iter, &rates) < 0) {
err = MMPDU_REASON_CODE_INVALID_IE;
goto bad_frame;
}
@@ -1066,16 +1107,26 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
break;
case IE_TYPE_RSN:
- if (ie_parse_rsne(ies, &rsn_info) < 0) {
+ /*
+ * WSC v2.0.5 Section 8.2:
+ * "Note that during the WSC association [...] the
+ * RSN IE and the WPA IE are irrelevant and shall be
+ * ignored by both the station and AP."
+ */
+ if (wsc_data)
+ break;
+
+ if (ie_parse_rsne(&iter, &rsn_info) < 0) {
err = MMPDU_REASON_CODE_INVALID_IE;
goto bad_frame;
}
- rsn = (const uint8_t *) ie_tlv_iter_get_data(ies) - 2;
+ rsn = (const uint8_t *) ie_tlv_iter_get_data(&iter) - 2;
break;
}
- if (!rates || !ssid || !rsn || ssid_len != strlen(ap->config->ssid) ||
+ if (!rates || !ssid || (!wsc_data && !rsn) ||
+ ssid_len != strlen(ap->config->ssid) ||
memcmp(ssid, ap->config->ssid, ssid_len)) {
err = MMPDU_REASON_CODE_INVALID_IE;
goto bad_frame;
@@ -1086,19 +1137,82 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
goto unsupported;
}
- if (rsn_info.mfpr && rsn_info.spp_a_msdu_required) {
- err = MMPDU_REASON_CODE_UNSPECIFIED;
- goto unsupported;
- }
+ /* Is the client requesting RSNA establishment or WSC registration */
+ if (!rsn) {
+ struct wsc_association_request wsc_req;
+ struct ap_event_registration_start_data event_data;
+ struct ap_wsc_pbc_probe_record *record;
- if (!(rsn_info.pairwise_ciphers & ap->ciphers)) {
- err = MMPDU_REASON_CODE_INVALID_PAIRWISE_CIPHER;
- goto unsupported;
- }
+ if (wsc_parse_association_request(wsc_data, wsc_data_len,
+ &wsc_req) < 0) {
+ err = MMPDU_REASON_CODE_INVALID_IE;
+ goto bad_frame;
+ }
- if (rsn_info.akm_suites != IE_RSN_AKM_SUITE_PSK) {
- err = MMPDU_REASON_CODE_INVALID_AKMP;
- goto unsupported;
+ l_free(wsc_data);
+ wsc_data = NULL;
+
+ if (wsc_req.request_type !=
+ WSC_REQUEST_TYPE_ENROLLEE_OPEN_8021X) {
+ err = MMPDU_REASON_CODE_INVALID_IE;
+ goto bad_frame;
+ }
+
+ if (!ap->wsc_pbc_timeout) {
+ l_debug("WSC association from %s but we're not in "
+ "PBC mode", util_address_to_string(sta->addr));
+ err = MMPDU_REASON_CODE_UNSPECIFIED;
+ goto bad_frame;
+ }
+
+ if (l_queue_isempty(ap->wsc_pbc_probes)) {
+ l_debug("%s tried to register as enrollee but we "
+ "don't have their Probe Request record",
+ util_address_to_string(sta->addr));
+ err = MMPDU_REASON_CODE_UNSPECIFIED;
+ goto bad_frame;
+ }
+
+ /*
+ * For PBC, the Enrollee must have sent the only PBC Probe
+ * Request within the monitor time and walk time.
+ */
+ record = l_queue_peek_head(ap->wsc_pbc_probes);
+ if (memcmp(sta->addr, record->mac, 6)) {
+ l_debug("Session overlap during %s's attempt to "
+ "register as WSC enrollee",
+ util_address_to_string(sta->addr));
+ err = MMPDU_REASON_CODE_UNSPECIFIED;
+ goto bad_frame;
+ }
+
+ memcpy(sta->wsc_uuid_e, record->uuid_e, 16);
+ sta->wsc_v2 = wsc_req.version2;
+
+ event_data.mac = sta->addr;
+ ap->event_func(AP_EVENT_REGISTRATION_START, &event_data,
+ ap->user_data);
+
+ /*
+ * Since we're starting the PBC Registration Protocol
+ * we can now exit the "active PBC mode".
+ */
+ ap_wsc_exit_pbc(ap);
+ } else {
+ if (rsn_info.mfpr && rsn_info.spp_a_msdu_required) {
+ err = MMPDU_REASON_CODE_UNSPECIFIED;
+ goto unsupported;
+ }
+
+ if (!(rsn_info.pairwise_ciphers & ap->ciphers)) {
+ err = MMPDU_REASON_CODE_INVALID_PAIRWISE_CIPHER;
+ goto unsupported;
+ }
+
+ if (rsn_info.akm_suites != IE_RSN_AKM_SUITE_PSK) {
+ err = MMPDU_REASON_CODE_INVALID_AKMP;
+ goto unsupported;
+ }
}
/* 802.11-2016 11.3.5.3 j) */
@@ -1128,10 +1242,12 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
if (sta->assoc_rsne)
l_free(sta->assoc_rsne);
- sta->assoc_rsne = l_memdup(rsn, rsn[1] + 2);
+ if (rsn)
+ sta->assoc_rsne = l_memdup(rsn, rsn[1] + 2);
+ else
+ sta->assoc_rsne = NULL;
- sta->assoc_resp_cmd_id = ap_assoc_resp(ap, sta, sta->addr, sta->aid, 0,
- reassoc,
+ sta->assoc_resp_cmd_id = ap_assoc_resp(ap, sta, sta->addr, 0, reassoc,
ap_success_assoc_resp_cb);
if (!sta->assoc_resp_cmd_id)
l_error("Sending success (Re)Association Response failed");
@@ -1160,7 +1276,9 @@ bad_frame:
if (rates)
l_uintset_free(rates);
- if (!ap_assoc_resp(ap, NULL, sta->addr, 0, err, reassoc,
+ l_free(wsc_data);
+
+ if (!ap_assoc_resp(ap, sta, sta->addr, err, reassoc,
ap_fail_assoc_resp_cb))
l_error("Sending error (Re)Association Response failed");
}
@@ -1174,7 +1292,6 @@ static void ap_assoc_req_cb(const struct mmpdu_header *hdr, const void *body,
const uint8_t *from = hdr->address_2;
const struct mmpdu_association_request *req = body;
const uint8_t *bssid = netdev_get_address(ap->netdev);
- struct ie_tlv_iter iter;
l_info("AP Association Request from %s", util_address_to_string(from));
@@ -1184,7 +1301,7 @@ static void ap_assoc_req_cb(const struct mmpdu_header *hdr, const void *body,
sta = l_queue_find(ap->sta_states, ap_sta_match_addr, from);
if (!sta) {
- if (!ap_assoc_resp(ap, NULL, from, 0,
+ if (!ap_assoc_resp(ap, NULL, from,
MMPDU_REASON_CODE_STA_REQ_ASSOC_WITHOUT_AUTH,
false, ap_fail_assoc_resp_cb))
l_error("Sending error Association Response failed");
@@ -1192,9 +1309,9 @@ static void ap_assoc_req_cb(const struct mmpdu_header *hdr, const void *body,
return;
}
- ie_tlv_iter_init(&iter, req->ies, body_len - sizeof(*req));
ap_assoc_reassoc(sta, false, &req->capability,
- L_LE16_TO_CPU(req->listen_interval), &iter);
+ L_LE16_TO_CPU(req->listen_interval),
+ req->ies, body_len - sizeof(*req));
}
/* 802.11-2016 9.3.3.8 */
@@ -1206,7 +1323,6 @@ static void ap_reassoc_req_cb(const struct mmpdu_header *hdr, const void *body,
const uint8_t *from = hdr->address_2;
const struct mmpdu_reassociation_request *req = body;
const uint8_t *bssid = netdev_get_address(ap->netdev);
- struct ie_tlv_iter iter;
int err;
l_info("AP Reassociation Request from %s",
@@ -1227,13 +1343,13 @@ static void ap_reassoc_req_cb(const struct mmpdu_header *hdr, const void *body,
goto bad_frame;
}
- ie_tlv_iter_init(&iter, req->ies, body_len - sizeof(*req));
ap_assoc_reassoc(sta, true, &req->capability,
- L_LE16_TO_CPU(req->listen_interval), &iter);
+ L_LE16_TO_CPU(req->listen_interval),
+ req->ies, body_len - sizeof(*req));
return;
bad_frame:
- if (!ap_assoc_resp(ap, NULL, from, 0, err, true, ap_fail_assoc_resp_cb))
+ if (!ap_assoc_resp(ap, NULL, from, err, true, ap_fail_assoc_resp_cb))
l_error("Sending error Reassociation Response failed");
}
--
2.25.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 8/8] ap: Start EAP-WSC authentication with WSC enrollees
2020-08-28 12:46 [PATCH 1/8] wscutil: Use a utility for building authorized_macs Andrew Zaborowski
` (5 preceding siblings ...)
2020-08-28 12:46 ` [PATCH 7/8] ap: Parse WSC PBC association request and build response Andrew Zaborowski
@ 2020-08-28 12:46 ` Andrew Zaborowski
2020-08-28 15:55 ` [PATCH 1/8] wscutil: Use a utility for building authorized_macs Denis Kenzior
7 siblings, 0 replies; 9+ messages in thread
From: Andrew Zaborowski @ 2020-08-28 12:46 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 9382 bytes --]
After association and sending the SET_STATION commands, set up the
handshake_state and eapol_sm for EAP-WSC and start the handshake.
---
src/ap.c | 227 +++++++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 186 insertions(+), 41 deletions(-)
diff --git a/src/ap.c b/src/ap.c
index d7d0bbad..1cee185c 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -46,6 +46,7 @@
#include "src/nl80211util.h"
#include "src/frame-xchg.h"
#include "src/wscutil.h"
+#include "src/eap-wsc.h"
#include "src/ap.h"
struct ap_state {
@@ -91,6 +92,8 @@ struct sta_state {
struct eapol_sm *sm;
struct handshake_state *hs;
uint32_t gtk_query_cmd_id;
+ struct l_idle *stop_handshake_work;
+ struct l_settings *wsc_settings;
uint8_t wsc_uuid_e[16];
bool wsc_v2;
};
@@ -131,6 +134,23 @@ static void ap_stop_handshake(struct sta_state *sta)
handshake_state_free(sta->hs);
sta->hs = NULL;
}
+
+ if (sta->wsc_settings) {
+ l_settings_free(sta->wsc_settings);
+ sta->wsc_settings = NULL;
+ }
+
+ if (sta->stop_handshake_work) {
+ l_idle_remove(sta->stop_handshake_work);
+ sta->stop_handshake_work = NULL;
+ }
+}
+
+static void ap_stop_handshake_work(struct l_idle *idle, void *user_data)
+{
+ struct sta_state *sta = user_data;
+
+ ap_stop_handshake(sta);
}
static void ap_sta_free(void *data)
@@ -560,6 +580,45 @@ static uint32_t ap_send_mgmt_frame(struct ap_state *ap,
return id;
}
+static void ap_start_handshake(struct sta_state *sta, bool use_eapol_start)
+{
+ struct ap_state *ap = sta->ap;
+ const uint8_t *own_addr = netdev_get_address(ap->netdev);
+ struct ie_rsn_info rsn;
+ uint8_t bss_rsne[24];
+
+ handshake_state_set_ssid(sta->hs, (void *) ap->config->ssid,
+ strlen(ap->config->ssid));
+ handshake_state_set_authenticator_address(sta->hs, own_addr);
+ handshake_state_set_supplicant_address(sta->hs, sta->addr);
+
+ ap_set_rsn_info(ap, &rsn);
+ /*
+ * Note: This assumes the length that ap_set_rsn_info() requires. If
+ * ap_set_rsn_info() changes then this will need to be updated.
+ */
+ ie_build_rsne(&rsn, bss_rsne);
+ handshake_state_set_authenticator_ie(sta->hs, bss_rsne);
+
+ sta->sm = eapol_sm_new(sta->hs);
+ if (!sta->sm) {
+ ap_stop_handshake(sta);
+ l_error("could not create sm object");
+ goto error;
+ }
+
+ eapol_sm_set_listen_interval(sta->sm, sta->listen_interval);
+ eapol_sm_set_use_eapol_start(sta->sm, use_eapol_start);
+
+ eapol_register(sta->sm);
+ eapol_start(sta->sm);
+
+ return;
+
+error:
+ ap_del_station(sta, MMPDU_REASON_CODE_UNSPECIFIED, true);
+}
+
static void ap_handshake_event(struct handshake_state *hs,
enum handshake_event event, void *user_data, ...)
{
@@ -587,53 +646,18 @@ static void ap_handshake_event(struct handshake_state *hs,
static void ap_start_rsna(struct sta_state *sta, const uint8_t *gtk_rsc)
{
- struct ap_state *ap = sta->ap;
- struct netdev *netdev = sta->ap->netdev;
- const uint8_t *own_addr = netdev_get_address(netdev);
- struct ie_rsn_info rsn;
- uint8_t bss_rsne[24];
-
- ap_set_rsn_info(ap, &rsn);
- /*
- * TODO: This assumes the length that ap_set_rsn_info() requires. If
- * ap_set_rsn_info() changes then this will need to be updated.
- */
- ie_build_rsne(&rsn, bss_rsne);
-
/* this handshake setup assumes PSK network */
- sta->hs = netdev_handshake_state_new(netdev);
-
- handshake_state_set_event_func(sta->hs, ap_handshake_event, sta);
- handshake_state_set_ssid(sta->hs, (void *) ap->config->ssid,
- strlen(ap->config->ssid));
+ sta->hs = netdev_handshake_state_new(sta->ap->netdev);
handshake_state_set_authenticator(sta->hs, true);
- handshake_state_set_authenticator_ie(sta->hs, bss_rsne);
+ handshake_state_set_event_func(sta->hs, ap_handshake_event, sta);
handshake_state_set_supplicant_ie(sta->hs, sta->assoc_rsne);
- handshake_state_set_pmk(sta->hs, ap->pmk, 32);
- handshake_state_set_authenticator_address(sta->hs, own_addr);
- handshake_state_set_supplicant_address(sta->hs, sta->addr);
+ handshake_state_set_pmk(sta->hs, sta->ap->pmk, 32);
if (gtk_rsc)
- handshake_state_set_gtk(sta->hs, ap->gtk, ap->gtk_index,
- gtk_rsc);
-
- sta->sm = eapol_sm_new(sta->hs);
- if (!sta->sm) {
- handshake_state_free(sta->hs);
- sta->hs = NULL;
- l_error("could not create sm object");
- goto error;
- }
-
- eapol_sm_set_listen_interval(sta->sm, sta->listen_interval);
-
- eapol_register(sta->sm);
- eapol_start(sta->sm);
+ handshake_state_set_gtk(sta->hs, sta->ap->gtk,
+ sta->ap->gtk_index, gtk_rsc);
- return;
-
-error:
- ap_del_station(sta, MMPDU_REASON_CODE_UNSPECIFIED, true);
+ ap_start_handshake(sta, false);
}
static void ap_gtk_query_cb(struct l_genl_msg *msg, void *user_data)
@@ -672,6 +696,114 @@ static bool ap_wsc_pbc_record_expire(void *data, void *user_data)
return true;
}
+static void ap_stop_handshake_schedule(struct sta_state *sta)
+{
+ if (sta->stop_handshake_work)
+ return;
+
+ sta->stop_handshake_work = l_idle_create(ap_stop_handshake_work,
+ sta, NULL);
+}
+
+static void ap_wsc_handshake_event(struct handshake_state *hs,
+ enum handshake_event event, void *user_data, ...)
+{
+ struct sta_state *sta = user_data;
+ va_list args;
+ struct ap_event_registration_success_data event_data;
+ struct ap_pbc_record_expiry_data expiry_data;
+
+ va_start(args, user_data);
+
+ switch (event) {
+ case HANDSHAKE_EVENT_FAILED:
+ sta->sm = NULL;
+ ap_stop_handshake_schedule(sta);
+ /*
+ * Some diagrams in WSC v2.0.5 indicate we should
+ * automatically deauthenticate the Enrollee. The text
+ * generally indicates the Enrollee may disassociate
+ * meaning that we should neither deauthenticate nor
+ * disassociate it automatically. Some places indicate
+ * that the enrollee can send a new EAPoL-Start right away
+ * on an unsuccessful registration, we don't implement
+ * this for now. STA remains associated but not authorized
+ * and basically has no other option than to re-associate
+ * or disassociate/deauthenticate.
+ */
+ break;
+ case HANDSHAKE_EVENT_EAP_NOTIFY:
+ if (va_arg(args, unsigned int) != EAP_WSC_EVENT_CREDENTIAL_SENT)
+ break;
+
+ /*
+ * WSC v2.0.5 Section 11.3:
+ * "If the Registrar successfully runs the PBC method to
+ * completion with an Enrollee, that Enrollee's probe requests
+ * are removed from the Monitor Time check the next time the
+ * Registrar's PBC button is pressed."
+ */
+ expiry_data.min_time = 0;
+ expiry_data.mac = sta->addr;
+ l_queue_foreach_remove(sta->ap->wsc_pbc_probes,
+ ap_wsc_pbc_record_expire,
+ &expiry_data);
+
+ event_data.mac = sta->addr;
+ sta->ap->event_func(AP_EVENT_REGISTRATION_SUCCESS, &event_data,
+ sta->ap->user_data);
+ break;
+ default:
+ break;
+ }
+
+ va_end(args);
+}
+
+static void ap_start_eap_wsc(struct sta_state *sta)
+{
+ struct ap_state *ap = sta->ap;
+
+ /*
+ * WSC v2.0.5 Section 8.2: "The AP is allowed to send
+ * EAP-Request/Identity to the station before EAPOL-Start is received
+ * if a WSC IE is included in the (re)association request and the
+ * WSC IE is version 2.0 or higher.
+ */
+ bool wait_for_eapol_start = !sta->wsc_v2;
+
+ L_AUTO_FREE_VAR(char *, uuid_r_str) = NULL;
+ L_AUTO_FREE_VAR(char *, uuid_e_str) = NULL;
+
+ uuid_r_str = l_util_hexstring(ap->wsc_uuid_r, 16);
+ uuid_e_str = l_util_hexstring(sta->wsc_uuid_e, 16);
+
+ sta->wsc_settings = l_settings_new();
+ l_settings_set_string(sta->wsc_settings, "Security", "EAP-Method",
+ "WSC-R");
+ l_settings_set_string(sta->wsc_settings, "WSC", "EnrolleeMAC",
+ util_address_to_string(sta->addr));
+ l_settings_set_string(sta->wsc_settings, "WSC", "UUID-R",
+ uuid_r_str);
+ l_settings_set_string(sta->wsc_settings, "WSC", "UUID-E",
+ uuid_e_str);
+ l_settings_set_uint(sta->wsc_settings, "WSC", "RFBand",
+ WSC_RF_BAND_2_4_GHZ);
+ l_settings_set_uint(sta->wsc_settings, "WSC", "ConfigurationMethods",
+ WSC_CONFIGURATION_METHOD_PUSH_BUTTON);
+ l_settings_set_string(sta->wsc_settings, "WSC", "WPA2-SSID",
+ ap->config->ssid);
+ l_settings_set_string(sta->wsc_settings, "WSC", "WPA2-Passphrase",
+ ap->config->psk);
+
+ sta->hs = netdev_handshake_state_new(ap->netdev);
+ handshake_state_set_authenticator(sta->hs, true);
+ handshake_state_set_event_func(sta->hs, ap_wsc_handshake_event, sta);
+ handshake_state_set_8021x_config(sta->hs, sta->wsc_settings);
+
+ ap_start_handshake(sta, wait_for_eapol_start);
+}
+
static struct l_genl_msg *ap_build_cmd_del_key(struct ap_state *ap)
{
uint32_t ifindex = netdev_get_ifindex(ap->netdev);
@@ -738,6 +870,19 @@ static void ap_associate_sta_cb(struct l_genl_msg *msg, void *user_data)
return;
}
+ /*
+ * WSC v2.0.5 Section 8.2:
+ * "Therefore if a WSC IE is present in the (re)association request,
+ * the AP shall engage in EAP-WSC with the station and shall not
+ * attempt any other security handshake."
+ *
+ * So no need for group traffic, skip the GTK setup below.
+ */
+ if (!sta->assoc_rsne) {
+ ap_start_eap_wsc(sta);
+ return;
+ }
+
/*
* Set up the group key. If this is our first STA then we have
* to add the new GTK to the kernel. In theory we should be
--
2.25.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 1/8] wscutil: Use a utility for building authorized_macs
2020-08-28 12:46 [PATCH 1/8] wscutil: Use a utility for building authorized_macs Andrew Zaborowski
` (6 preceding siblings ...)
2020-08-28 12:46 ` [PATCH 8/8] ap: Start EAP-WSC authentication with WSC enrollees Andrew Zaborowski
@ 2020-08-28 15:55 ` Denis Kenzior
7 siblings, 0 replies; 9+ messages in thread
From: Denis Kenzior @ 2020-08-28 15:55 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 474 bytes --]
Hi Andrew,
On 8/28/20 7:46 AM, Andrew Zaborowski wrote:
> Add the wfa_build_authorized_macs function (wfa_ prefix following the
> wfa_extract_ naming) and use it in wsc_build_probe_response. The logic
> is changed slightly to treat the first 6-zeros address in the array as
> the end of the array.
> ---
> src/wscutil.c | 32 +++++++++++++++++---------------
> 1 file changed, 17 insertions(+), 15 deletions(-)
>
All applied, thanks.
Regards,
-Denis
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2020-08-28 15:55 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-08-28 12:46 [PATCH 1/8] wscutil: Use a utility for building authorized_macs Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 2/8] wscutil: Add wsc_build_beacon Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 3/8] eapol: Handle the use_eapol_start flag on authenticator Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 4/8] ap: Stop ongoing handshake on reassociation Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 5/8] ap: Push Button mode API and beacon changes Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 6/8] ap: WSC Probe Request processing logic Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 7/8] ap: Parse WSC PBC association request and build response Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 8/8] ap: Start EAP-WSC authentication with WSC enrollees Andrew Zaborowski
2020-08-28 15:55 ` [PATCH 1/8] wscutil: Use a utility for building authorized_macs Denis Kenzior
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox