~linuxhackerman/iwd

7a7c580ffc5ddb0f337aa7a040ab986fbd04c196 — Andrew Zaborowski 1 year, 3 months ago 99112c9
ap: Parse WSC PBC association request and build response

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.
1 files changed, 151 insertions(+), 35 deletions(-)

M src/ap.c
M src/ap.c => src/ap.c +151 -35
@@ 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");
}