mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-11-25 17:59:25 +01:00
ap: WSC Probe Request processing logic
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.
This commit is contained in:
parent
43c101ab14
commit
99112c9317
155
src/ap.c
155
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
|
||||
|
Loading…
Reference in New Issue
Block a user