~linuxhackerman/iwd

4bc20a0979655a97b7776ab1a8778e82c0785860 — Andrew Zaborowski 1 year, 3 months ago 7a7c580
ap: Start EAP-WSC authentication with WSC enrollees

After association and sending the SET_STATION commands, set up the
handshake_state and eapol_sm for EAP-WSC and start the handshake.
1 files changed, 186 insertions(+), 41 deletions(-)

M src/ap.c
M src/ap.c => src/ap.c +186 -41
@@ 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);


@@ 739,6 871,19 @@ static void ap_associate_sta_cb(struct l_genl_msg *msg, void *user_data)
	}

	/*
	 * 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
	 * able to supply our own RSC (e.g. generated randomly) and use it