wpa_supplicant2.9之sae握手流程(续)

葛书
2023-12-01


本文延续上一篇 wpa_supplicant2.9之sae握手流程
,继续分析wpa_supplicant代码中sae auth的另一种方式:external auth
(文中若涉及到与上篇重复的函数流程不会再次细说,可看上篇)

wpa_supplicant_associate

判定当前wifi驱动是否走external_auth流程,主要是看wpa_s->drv_flags 里WPA_DRIVER_FLAGS_SME比特位是否置1。置1,会在wpa_supplicant_associate内中间一步调用sme_authenticate后直接return。未置1则在wpa_supplicant_associate函数里继续走下去。

external auth的流程始于wpa_supplicant_associate里最后一段:

if (radio_add_work(wpa_s, bss ? bss->freq : 0, "connect", 1,
			   wpas_start_assoc_cb, cwork) < 0) {
		os_free(cwork);
}

这段在wpa_s的链表wpa_s->radio上添加了一个radio_work,这个作业的处理函数是wpas_start_assoc_cb。当执行到这个radio_work的时候,wpas_start_assoc_cb会被调到。

wpas_start_assoc_cb

wpas_start_assoc_cb内部设置各类参数,例如key_mgmt,he_caps,vht_caps等等。

static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
{
	struct wpa_connect_work *cwork = work->ctx;
	struct wpa_bss *bss = cwork->bss;
	... ... ... ...//创建n多下文要用的变量
	... ... ... ...//一些异常判断
	os_memcpy(prev_bssid, wpa_s->bssid, ETH_ALEN);
	os_memset(&params, 0, sizeof(params));
	wpa_s->reassociate = 0;//标识是否为重连
	wpa_s->eap_expected_failure = 0;
	if (bss &&
	    (!wpas_driver_bss_selection(wpa_s) || wpas_wps_searching(wpa_s))) {//判断当前的bssid是否可连接,一般走这个if条件
#ifdef CONFIG_IEEE80211R
		const u8 *ie, *md = NULL;
#endif /* CONFIG_IEEE80211R */
		wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR " (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len), bss->freq);
		bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
		os_memset(wpa_s->bssid, 0, ETH_ALEN);
		os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
		if (bssid_changed)
			wpas_notify_bssid_changed(wpa_s);
#ifdef CONFIG_IEEE80211R
		ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
		if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN)
			md = ie + 2;
		wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0);
		if (md) {
			/* Prepare for the next transition */
			wpa_ft_prepare_auth_request(wpa_s->wpa, ie);
		}
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_WPS
	} else if ((ssid->ssid == NULL || ssid->ssid_len == 0) &&
		   wpa_s->conf->ap_scan == 2 &&
		   (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {//异常情况
		/* Use ap_scan==1 style network selection to find the network*/
		wpas_connect_work_done(wpa_s);
		wpa_s->scan_req = MANUAL_SCAN_REQ;
		wpa_s->reassociate = 1;
		wpa_supplicant_req_scan(wpa_s, 0, 0);
		return;
#endif /* CONFIG_WPS */
	} else {//其他情况
		wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'",
			wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
		... ... ... ...//一些处理
	}
	if (!wpa_s->pno)
		wpa_supplicant_cancel_sched_scan(wpa_s);
	wpa_supplicant_cancel_scan(wpa_s);//确认可以开始连接了就不需要再扫描了
	/* Starting new association, so clear the possibly used WPA IE from the
	 * previous association. */
	wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
	wpa_ie = wpas_populate_assoc_ies(wpa_s, bss, ssid, &params, NULL);//调的这个函数里会组成关联过程要用到的ie
	if (!wpa_ie) {
		wpas_connect_work_done(wpa_s);
		return;
	}
	... ... ... ...//配置参数,如key_mgmt
	wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);//将wpa_supplicant状态改为关联中
	... ... ... ...//配置参数,如bss相关,key_mgmt_suite等等
	ret = wpa_drv_associate(wpa_s, &params);//真正开始进行关联过程
	os_free(wpa_ie);
	if (ret < 0) {//异常处理
		wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
			"failed");
		if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SANE_ERROR_CODES) {
			/*
			 * The driver is known to mean what is saying, so we
			 * can stop right here; the association will not
			 * succeed.
			 */
			wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
			wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
			os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
			return;
		}
		/* try to continue anyway; new association will be tried again
		 * after timeout */
		assoc_failed = 1;
	}
    ... ... ... ...//多种情况判断处理
	wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
	wpa_supplicant_initiate_eapol(wpa_s);
	if (old_ssid != wpa_s->current_ssid)
		wpas_notify_network_changed(wpa_s);
}

wpas_start_assoc_cb里会调用wpa_drv_associate。

wpa_drv_associate -> wpa_driver_nl80211_associate -> wpa_driver_nl80211_connect -> wpa_driver_nl80211_try_connect

wpa_driver_nl80211_try_connect

wpa_driver_nl80211_try_connect里,部分代码如下:

wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_CONNECT);//先创建NL80211_CMD_CONNECT的msg
if (!msg)
	return -1;
ret = nl80211_connect_common(drv, params, msg);//根据params,向msg里填参数
if (ret)
    goto fail;
if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&
	    nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))
	goto fail;//利用nla_put系列函数往msg中填参
... ... ... ...//继续向msg中填参
if (nl_connect)//最终将msg发给net层
	ret = send_and_recv(drv->global, nl_connect, msg, NULL, (void *) -1);
else
	ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);

NL80211_CMD_CONNECT最终会调用驱动注册的cfg80211_connect处理函数。在驱动接下来的内部处理流程里,需要通过cfg80211_external_auth_request通知上层开启external auth流程。cfg80211_external_auth_request里创建事件NL80211_CMD_EXTERNAL_AUTH向上发送,process_bss_event会收到并调用nl80211_external_auth处理本事件。

nl80211_external_auth

static void nl80211_external_auth(struct wpa_driver_nl80211_data *drv,
				  struct nlattr **tb)
{
	union wpa_event_data event;
	enum nl80211_external_auth_action act;
	if (!tb[NL80211_ATTR_AKM_SUITES] || !tb[NL80211_ATTR_EXTERNAL_AUTH_ACTION] ||
	    !tb[NL80211_ATTR_BSSID] || !tb[NL80211_ATTR_SSID])
		return;
	os_memset(&event, 0, sizeof(event));
	act = nla_get_u32(tb[NL80211_ATTR_EXTERNAL_AUTH_ACTION]);
	switch (act) {//判断是刚开始的external auth事件还是异常结束的external auth事件
	case NL80211_EXTERNAL_AUTH_START: //正常进入external auth流程
		event.external_auth.action = EXT_AUTH_START;
		break;
	case NL80211_EXTERNAL_AUTH_ABORT:
		event.external_auth.action = EXT_AUTH_ABORT;
		break;
	default:
		return;
	}
	event.external_auth.key_mgmt_suite = nla_get_u32(tb[NL80211_ATTR_AKM_SUITES]);//通过nla_get等函数从tb中获取信息存进event里
	event.external_auth.ssid_len = nla_len(tb[NL80211_ATTR_SSID]);
	if (event.external_auth.ssid_len > SSID_MAX_LEN)
		return;
	event.external_auth.ssid = nla_data(tb[NL80211_ATTR_SSID]);
	event.external_auth.bssid = nla_data(tb[NL80211_ATTR_BSSID]);
	wpa_printf(MSG_DEBUG, "nl80211: External auth action: %u, AKM: 0x%x", event.external_auth.action, event.external_auth.key_mgmt_suite);
	wpa_supplicant_event(drv->ctx, EVENT_EXTERNAL_AUTH, &event);//将event传给wpa_supplicant的状态机里
}

wpa_supplicant_event收到EVENT_EXTERNAL_AUTH事件,会调用sme_external_auth_trigger处理。

sme_external_auth_trigger

void sme_external_auth_trigger(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
{
	if (RSN_SELECTOR_GET(&data->external_auth.key_mgmt_suite) != RSN_AUTH_KEY_MGMT_SAE)
		return;
	if (data->external_auth.action == EXT_AUTH_START) {
		if (!data->external_auth.bssid || !data->external_auth.ssid)
			return;
		os_memcpy(wpa_s->sme.ext_auth_bssid, data->external_auth.bssid, ETH_ALEN);
		os_memcpy(wpa_s->sme.ext_auth_ssid, data->external_auth.ssid, data->external_auth.ssid_len);
		wpa_s->sme.ext_auth_ssid_len = data->external_auth.ssid_len;
		wpa_s->sme.seq_num = 0;
		wpa_s->sme.sae.state = SAE_NOTHING;
		wpa_s->sme.sae.send_confirm = 0;
		wpa_s->sme.sae_group_index = 0;//初始化好wpa_s的sme结构
		if (sme_handle_external_auth_start(wpa_s, data) < 0)//调用sme_handle_external_auth_start开始external_auth
			sme_send_external_auth_status(wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE);
	} else if (data->external_auth.action == EXT_AUTH_ABORT) {//异常情况需改变状态通知其他模块
		/* Report failure to driver for the wrong trigger */
		sme_send_external_auth_status(wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE);
	}
}

sme_handle_external_auth_start里检查待连接wifi状态是否正常,若正常则调用sme_external_auth_send_sae_commit。

sme_external_auth_send_sae_commit

static int sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s,
					     const u8 *bssid,
					     struct wpa_ssid *ssid)
{
	struct wpabuf *resp, *buf;
	resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1, 0);//创建sae-auth的第一帧--sta向ap发的commit
	if (!resp) {
		wpa_printf(MSG_DEBUG, "SAE: Failed to build SAE commit");
		return -1;
	}
	wpa_s->sme.sae.state = SAE_COMMITTED;
	buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + wpabuf_len(resp));
	if (!buf) {
		wpabuf_free(resp);
		return -1;
	}
	wpa_s->sme.seq_num++;
	sme_external_auth_build_buf(buf, resp, wpa_s->own_addr, bssid, 1, wpa_s->sme.seq_num);
	wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0);//发给下层这个组好的auth包
	wpabuf_free(resp);
	wpabuf_free(buf);
	return 0;
}

驱动层处理

这之后,驱动注册的cfg80211_mgmt_tx处理函数会被调用,发出这第一个auth包。驱动收到AP回复的sae-auth 第二帧commit包后,需调接口函数上传给上层。

我了解的是可以通过函数cfg80211_rx_mgmt上传。cfg80211_rx_mgmt里会调用nl80211_send_mgmt,nl80211_send_mgmt中hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);,以NL80211_CMD_FRAME事件类型上传。

NL80211_CMD_FRAME事件

process_bss_event会收到NL80211_CMD_FRAME事件,并调用mlme_event处理本事件。
mlme_event中:

case NL80211_CMD_FRAME://对于NL80211_CMD_FRAME会调mlme_event_mgmt
		mlme_event_mgmt(bss, freq, sig, nla_data(frame), nla_len(frame));
		break;

mlme_event_mgmt代码如下:

static void mlme_event_mgmt(struct i802_bss *bss,
			    struct nlattr *freq, struct nlattr *sig,
			    const u8 *frame, size_t len)
{
	struct wpa_driver_nl80211_data *drv = bss->drv;
	const struct ieee80211_mgmt *mgmt;
	union wpa_event_data event;
	u16 fc, stype;
	int ssi_signal = 0;
	int rx_freq = 0;
	wpa_printf(MSG_MSGDUMP, "nl80211: Frame event");
	mgmt = (const struct ieee80211_mgmt *) frame;
	if (len < 24) {
		wpa_printf(MSG_DEBUG, "nl80211: Too short management frame");
		return;
	}
	fc = le_to_host16(mgmt->frame_control);
	stype = WLAN_FC_GET_STYPE(fc);
	if (sig)
		ssi_signal = (s32) nla_get_u32(sig);
	os_memset(&event, 0, sizeof(event));//创建wpa_supplicant中的event
	if (freq) {//填充event结构中的值
		event.rx_mgmt.freq = nla_get_u32(freq);
		rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq;
	}
	wpa_printf(MSG_DEBUG, "nl80211: RX frame da=" MACSTR " sa=" MACSTR " bssid=" MACSTR
		   " freq=%d ssi_signal=%d fc=0x%x seq_ctrl=0x%x stype=%u (%s) len=%u",
		   MAC2STR(mgmt->da), MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid), rx_freq, ssi_signal, fc,
		   le_to_host16(mgmt->seq_ctrl), stype, fc2str(fc), (unsigned int) len);
	event.rx_mgmt.frame = frame;//给event变量赋值
	event.rx_mgmt.frame_len = len;
	event.rx_mgmt.ssi_signal = ssi_signal;
	event.rx_mgmt.drv_priv = bss;
	wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);//传给wpa_s的事件状态机
}

事件EVENT_RX_MGMT

wpa_supplicant_event收到事件EVENT_RX_MGMT后,会调用sme_external_auth_mgmt_rx函数处理。

case EVENT_RX_MGMT: {
		u16 fc, stype;
		const struct ieee80211_mgmt *mgmt;
#ifdef CONFIG_TESTING_OPTIONS
    ... ... ... ...//CONFIG_TESTING_OPTIONS的一些操作
#endif /* CONFIG_TESTING_OPTIONS */
		mgmt = (const struct ieee80211_mgmt *) data->rx_mgmt.frame;
		fc = le_to_host16(mgmt->frame_control);
		stype = WLAN_FC_GET_STYPE(fc);
#ifdef CONFIG_AP
		if (wpa_s->ap_iface == NULL) {
#endif /* CONFIG_AP */
#ifdef CONFIG_P2P
    ... ... ... ...//CONFIG_P2P的一些操作
#endif /* CONFIG_P2P */
#ifdef CONFIG_IBSS_RSN
    ... ... ... ...//CONFIG_IBSS_RSN的一些操作
#endif /* CONFIG_IBSS_RSN */
			if (stype == WLAN_FC_STYPE_ACTION) {
				wpas_event_rx_mgmt_action( wpa_s, data->rx_mgmt.frame, data->rx_mgmt.frame_len, data->rx_mgmt.freq, data->rx_mgmt.ssi_signal);
				break;
			}
			if (wpa_s->ifmsh) {
				mesh_mpm_mgmt_rx(wpa_s, &data->rx_mgmt);
				break;
			}
#ifdef CONFIG_SAE
			if (stype == WLAN_FC_STYPE_AUTH && !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) {//如果subtype是AUTH并且支持SAE不支持SME
				sme_external_auth_mgmt_rx( wpa_s, data->rx_mgmt.frame, data->rx_mgmt.frame_len);//调用sme_external_auth_mgmt_rx处理这个auth包
				break;
			}
#endif /* CONFIG_SAE */
			wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received management frame in non-AP mode");
			break;
#ifdef CONFIG_AP
		}
            ... ... ... ...//CONFIG_AP的一些处理
		ap_mgmt_rx(wpa_s, &data->rx_mgmt);
#endif /* CONFIG_AP */
		break;
		}

sme_external_auth_mgmt_rx

void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s,
			       const u8 *auth_frame, size_t len)
{
	const struct ieee80211_mgmt *header;
	size_t auth_length;
	header = (const struct ieee80211_mgmt *) auth_frame;
	auth_length = IEEE80211_HDRLEN + sizeof(header->u.auth);
	if (len < auth_length) {
		/* Notify failure to the driver */
		sme_send_external_auth_status(wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE);
		return;
	}
	if (le_to_host16(header->u.auth.auth_alg) == WLAN_AUTH_SAE) {
		int res;//调sme_sae_auth
		res = sme_sae_auth( wpa_s, le_to_host16(header->u.auth.auth_transaction), le_to_host16(header->u.auth.status_code), header->u.auth.variable, len - auth_length, 1, header->sa);
		if (res < 0) {
			/* Notify failure to the driver */
			sme_send_external_auth_status( wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE);
			return;
		}
		if (res != 1)//如果是confirm阶段成功,返回的是1;commit阶段成功,返回的是0
			return;//若commit阶段成功,直接return
		wpa_printf(MSG_DEBUG, "SME: SAE completed - setting PMK for 4-way handshake");//confirm阶段成功设置pmk
		wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN, wpa_s->sme.sae.pmkid, wpa_s->pending_bssid);
	}
}

sme_sae_auth

这是external auth流程里第一次进入sme_sae_auth里,auth_transaction传进来的值为1,external传进来的值也为1。

static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
			u16 status_code, const u8 *data, size_t len, int external, const u8 *sa)
{
	int *groups;
	wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE authentication transaction %u "
		"status code %u", auth_transaction, status_code);
	... ... ... ...
	if (auth_transaction == 1) {//auth_transaction值为1或2,代表sae的auth握手阶段
		u16 res;//commit阶段
		groups = wpa_s->conf->sae_groups;
		wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit");
		... ... ... ...//处理一些参数
		wpabuf_free(wpa_s->sme.sae_token);
		wpa_s->sme.sae_token = NULL;
		if (!external)
			sme_send_authentication(wpa_s, wpa_s->current_bss, wpa_s->current_ssid, 0);
		else//使用external auth,本文走这条路径
			sme_external_auth_send_sae_confirm(wpa_s, sa);
		return 0;//commit阶段返回0
	} else if (auth_transaction == 2) {//confirm阶段
		wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm");
		if (wpa_s->sme.sae.state != SAE_CONFIRMED)
			return -1;
		if (sae_check_confirm(&wpa_s->sme.sae, data, len) < 0)
			return -1;
		wpa_s->sme.sae.state = SAE_ACCEPTED;
		sae_clear_temp_data(&wpa_s->sme.sae);
		if (external) {//如果当前使用external auth
			/* Report success to driver */
			sme_send_external_auth_status(wpa_s, WLAN_STATUS_SUCCESS);
		}
		return 1;//confirm阶段返回1
	}
	return -1;
}

sme_external_auth_send_sae_confirm

static void sme_external_auth_send_sae_confirm(struct wpa_supplicant *wpa_s,
					       const u8 *da)
{
	struct wpabuf *resp, *buf;
	resp = sme_auth_build_sae_confirm(wpa_s, 1);//创建sae-auth的第三帧--sta向ap发的confirm
	if (!resp) {
		wpa_printf(MSG_DEBUG, "SAE: Confirm message buf alloc failure");
		return;
	}
	wpa_s->sme.sae.state = SAE_CONFIRMED;
	buf = wpabuf_alloc(4 + SAE_CONFIRM_MAX_LEN + wpabuf_len(resp));
	if (!buf) {
		wpa_printf(MSG_DEBUG, "SAE: Auth Confirm buf alloc failure");
		wpabuf_free(resp);
		return;
	}
	wpa_s->sme.seq_num++;
	sme_external_auth_build_buf(buf, resp, wpa_s->own_addr, da, 2, wpa_s->sme.seq_num);
	wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0);//向下层发这个组好的sae-auth confirm包
	wpabuf_free(resp);
	wpabuf_free(buf);
}

接下来是相同的流程,驱动通过注册的cfg80211_mgmt_tx处理函数,发出这第三步confirm-auth包。驱动收到AP回复的第四个confirm-auth包后,调cfg80211_rx_mgmt函数上传给上层。之后同样的,这个ap发来的auth会以NL80211_CMD_FRAME事件类型上传。

到wpa_supplicant里的函数过程都一样,直到在sme_sae_auth里,这次是external auth流程中第二次进入sme_sae_auth,auth_transaction传进来的值为2,external传进来的值为1。这一次,sme_sae_auth里将进入另一个if条件,检查完状态后,返回1到sme_external_auth_mgmt_rx里,正式结束auth阶段,开始设置PMK,准备进入associate阶段。

至此,WPAv2.9的SAE之支持external auth的auth四次握手阶段结束,进入associate流程。

 类似资料: