wpa_supplicant2.9之sae握手流程

本文深入解析了Wi-Fi连接中WPA3 Personal采用的SAE加密方式,详细介绍了WPA3-SAE的四步认证流程,包括wpa_supplicant中的sme_authenticate、sme_send_authentication等关键步骤,以及驱动层面的交互。通过分析源码展示了从连接开始到SAE握手完成的过程。
摘要由CSDN通过智能技术生成


WPA3-Personal采用了新的加密方式,SAE算法。
WPA2的wifi连接流程:auth只有两帧

STA ------------>   Authentication Request  --------> AP //认证Auth类型,Open System , Shared Key等
STA <------------   Authentication Response <------ AP
STA ------------->  Association Request  ------------> AP  //请求与AP建立关联,从而可以进行数据交互
STA <-------------  Association Response <----------- AP
STA <-------------  EAPOL-KEY <----------- AP
STA ------------>  EAPOL-KEY  --------> AP 
STA <-------------  EAPOL-KEY <----------- AP
STA ------------>  EAPOL-KEY  --------> AP 

而WPA3-SAE的连接过程如下:auth有四帧(图盗的网上的)
在这里插入图片描述
由上可知SAE的Authentication流程有四帧。
本文及续篇主要是分析wpa_supplicant中STA设备连接时SAE Authentication流程相关的源码。

常见的sae auth方式有两种,一种是支持external auth的方式,另一种是支持SME的方式。
本篇讲SME的流程,下一篇讲external auth的流程。

SME介绍

先解释下SME(station management entity 站点管理实体)。为了方便对802.11MAC和PHY层的统一管理,定义了SME。该实体独立于MAC和PHY层,使用者可通过SME实体来操作MAC和PHY层的SAP。
在这里插入图片描述
关于SME的更详细的解释可以看这篇:https://blog.csdn.net/qq_33307581/article/details/110151985

wpa_supplicant_connect

在wpa_supplicant中,wpa_supplicant_connect开启连接流程。

wpa_supplicant_connect------>wpa_supplicant_associate

wpa_supplicant_associate函数里正式进入authentication流程(wpa_supplicant_associate这个函数名仅仅是个名称,不代表此函数中执行的是associate流程 )。

wpa_supplicant_associate函数中,会通过驱动提供的符号位参数来判断下是否使用SME,若使用SME,则进入由sme_authenticate的流程。本部分代码如下:

if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
	    ssid->mode == WPAS_MODE_INFRA) {
		sme_authenticate(wpa_s, bss, ssid);
		return;
}

其中,wpa_s->drv_flags是在函数wpa_supplicant_init_iface中初始化接口时注册的。
wpa_supplicant_init_iface中注册语句:

capa_res = wpa_drv_get_capa(wpa_s, &capa);//此函数获取驱动提供的参数
if (capa_res == 0) {
    ... ...
    wpa_s->drv_flags = capa.flags;//赋值
    ... ...
}

接下来开始正题了,开始分析从sme_authenticate为起点的支持SME的SAE AUTH流程。

sme_authenticate

代码如下:

void sme_authenticate(struct wpa_supplicant *wpa_s,
		      struct wpa_bss *bss, struct wpa_ssid *ssid)
{
	struct wpa_connect_work *cwork;
	if (bss == NULL || ssid == NULL)
		return;
	if (wpa_s->connect_work) {
		wpa_dbg(wpa_s, MSG_DEBUG, "SME: Reject sme_authenticate() call since connect_work exist");
		return;
	}//一些异常判断
	if (radio_work_pending(wpa_s, "sme-connect")) {//查找射频任务“sme-connect”是否已创建
		/*
		 * The previous sme-connect work might no longer be valid due to
		 * the fact that the BSS list was updated. In addition, it makes
		 * sense to adhere to the 'newer' decision.
		 */
		wpa_dbg(wpa_s, MSG_DEBUG,
			"SME: Remove previous pending sme-connect");
		radio_remove_works(wpa_s, "sme-connect", 0);//如果有,则删掉之前遗留的任务
	}
	wpas_abort_ongoing_scan(wpa_s);
	cwork = os_zalloc(sizeof(*cwork));//新建一个sme-connect射频任务
	if (cwork == NULL)
		return;
	cwork->bss = bss;
	cwork->ssid = ssid;
	cwork->sme = 1;
#ifdef CONFIG_SAE
	wpa_s->sme.sae.state = SAE_NOTHING;
	wpa_s->sme.sae.send_confirm = 0;
	wpa_s->sme.sae_group_index = 0;
#endif /* CONFIG_SAE */

	if (radio_add_work(wpa_s, bss->freq, "sme-connect", 1,
			   sme_auth_start_cb, cwork) < 0)
   //设置回调函数sme_auth_start_cb,当射频空闲并即将处理当前的事件时,调用该函数。
		wpas_connect_work_free(cwork);
}

关于radio_work:
在wpa_s中包含着一个重要的链表wpa_s->radio,该链表中包含着wpa_s指定驱动进行的一系列射频操作,例如scan,associate,authentication等。由于驱动的射频类操作能一项一项的执行,且耗时很长,所以只能先将待操作的事件存储到链表中,等待驱动的顺序执行。执行完当前的radio_work后,会将下一个radio work添加到timeout 链表中。

sme_auth_start_cb

static void sme_auth_start_cb(struct wpa_radio_work *work, int deinit)
{
	struct wpa_connect_work *cwork = work->ctx;
	struct wpa_supplicant *wpa_s = work->wpa_s;
	if (deinit) {
		if (work->started)
			wpa_s->connect_work = NULL;
		wpas_connect_work_free(cwork);
		return;
	}
	wpa_s->connect_work = work;
	if (cwork->bss_removed ||
	    !wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid) ||
	    wpas_network_disabled(wpa_s, cwork->ssid)) {//一些连接的异常情况判断
		wpa_dbg(wpa_s, MSG_DEBUG, "SME: BSS/SSID entry for authentication not valid anymore - drop connection attempt");
		wpas_connect_work_done(wpa_s);
		return;
	}
	/* Starting new connection, so clear the possibly used WPA IE from the
	 * previous association. */
	wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);//设置连接的IE参数
	sme_send_authentication(wpa_s, cwork->bss, cwork->ssid, 1);
}

sme_auth_start_cb最终会调sme_send_authentication。

sme_send_authentication

static void sme_send_authentication(struct wpa_supplicant *wpa_s,
				    struct wpa_bss *bss, struct wpa_ssid *ssid,
				    int start)
{
	struct wpa_driver_auth_params params;
	struct wpa_ssid *old_ssid;
    ... ... ... ...//创建下文要用到的变量
	if (bss == NULL) {
		wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for "
			"the network");
		wpas_connect_work_done(wpa_s);
		return;
	}
	skip_auth = wpa_s->conf->reassoc_same_bss_optim &&
		wpa_s->reassoc_same_bss;//判断是否可以跳过authentication环节
	wpa_s->current_bss = bss;
	os_memset(&params, 0, sizeof(params));
	wpa_s->reassociate = 0;
	params.freq = bss->freq;
    ... ... ... ...//配置各种之后需要用到的参数
#ifdef CONFIG_SAE
	if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE &&
	    pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0,
				    NULL,
				    wpa_s->key_mgmt == WPA_KEY_MGMT_FT_SAE ?
				    WPA_KEY_MGMT_FT_SAE :
				    WPA_KEY_MGMT_SAE) == 0) {//如果有当前连接的PMKSA缓存,则无需重新SAE Auth生成
		wpa_dbg(wpa_s, MSG_DEBUG,
			"PMKSA cache entry found - try to use PMKSA caching instead of new SAE authentication");
		wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
		params.auth_alg = WPA_AUTH_ALG_OPEN;
		wpa_s->sme.sae_pmksa_caching = 1;
	}
	if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE) {
		if (start)//如果是authentication的第一步,sta需要发sae commit
			resp = sme_auth_build_sae_commit(wpa_s, ssid,
							 bss->bssid, 0,
							 start == 2);
		else//若不是第一步,则是第三帧,sta需发sae confirm
			resp = sme_auth_build_sae_confirm(wpa_s, 0);
		if (resp == NULL) {
			wpas_connection_failed(wpa_s, bss->bssid);
			return;
		}
		params.auth_data = wpabuf_head(resp);
		params.auth_data_len = wpabuf_len(resp);
		wpa_s->sme.sae.state = start ? SAE_COMMITTED : SAE_CONFIRMED;//判断当前sae-auth的状态
	}
#endif /* CONFIG_SAE */
   ... ... ... ...//配置一些参数
	wpa_supplicant_cancel_sched_scan(wpa_s);
	wpa_supplicant_cancel_scan(wpa_s);
	wpa_msg(wpa_s, MSG_INFO, "SME: Trying to authenticate with " MACSTR
		" (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid),
		wpa_ssid_txt(params.ssid, params.ssid_len), params.freq);
	eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
	wpa_clear_keys(wpa_s, bss->bssid);
	wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);//状态变为auth
	if (old_ssid != wpa_s->current_ssid)
		wpas_notify_network_changed(wpa_s);
   ... ... ... ...
	if (skip_auth) {//如果无需auth了,则直接进入associate流程
		wpa_msg(wpa_s, MSG_DEBUG,
			"SME: Skip authentication step on reassoc-to-same-BSS");
		wpabuf_free(resp);
		sme_associate(wpa_s, ssid->mode, bss->bssid, WLAN_AUTH_OPEN);
		return;
	}
	wpa_s->sme.auth_alg = params.auth_alg;
	if (wpa_drv_authenticate(wpa_s, &params) < 0) {
		wpa_msg(wpa_s, MSG_INFO, "SME: Authentication request to the "
			"driver failed");
		wpas_connection_failed(wpa_s, bss->bssid);
		wpa_supplicant_mark_disassoc(wpa_s);
		wpabuf_free(resp);
		wpas_connect_work_done(wpa_s);
		return;
	}
	eloop_register_timeout(SME_AUTH_TIMEOUT, 0, sme_auth_timer, wpa_s,
			       NULL);//设个定时器,避免永远等待
	/*
	 * Association will be started based on the authentication event from
	 * the driver.
	 */
	wpabuf_free(resp);
}

wpa_drv_authenticate

static inline int wpa_drv_authenticate(struct wpa_supplicant *wpa_s,
				       struct wpa_driver_auth_params *params)
{
	if (wpa_s->driver->authenticate)
		return wpa_s->driver->authenticate(wpa_s->drv_priv, params);
	return -1;
}

authenticate函数的注册是在wpa_driver_nl80211_ops 里。

const struct wpa_driver_ops wpa_driver_nl80211_ops = {
	.name = "nl80211",
	.desc = "Linux nl80211/cfg80211", 
    ... ... ... ...
 	.authenticate = driver_nl80211_authenticate,
	.associate = wpa_driver_nl80211_associate,
     ... ... ... ...
};

故,实际调用的driver_nl80211_authenticate。
driver_nl80211_authenticate代码如下:

static int driver_nl80211_authenticate(void *priv,
				       struct wpa_driver_auth_params *params)
{
	struct i802_bss *bss = priv;
	return wpa_driver_nl80211_authenticate(bss, params);
}

wpa_driver_nl80211_authenticate

static int wpa_driver_nl80211_authenticate(
	struct i802_bss *bss, struct wpa_driver_auth_params *params)
{
	struct wpa_driver_nl80211_data *drv = bss->drv;
	int ret = -1, i;
	struct nl_msg *msg;
	enum nl80211_auth_type type;
    ... ... ... ...//配置各种参数
retry:
	wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)",
		   drv->ifindex);
	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_AUTHENTICATE);//发给wifi驱动NL80211_CMD_AUTHENTICATE这条指令
	if (!msg)
		goto fail;
	... ... ... ...//设置各种参数
	ret = send_and_recv_msgs(drv, msg, NULL, NULL);//将AUTH指令发下去
	msg = NULL;
	if (ret) {
		wpa_dbg(drv->ctx, MSG_DEBUG,
			"nl80211: MLME command failed (auth): count=%d ret=%d (%s)",
			count, ret, strerror(-ret));
		count++;
		... ...//发送失败的一些判断处理
	} else
		wpa_printf(MSG_DEBUG,
			   "nl80211: Authentication request send successfully");
fail:
	nlmsg_free(msg);
	return ret;
}

NL80211_CMD_XXX指令发给驱动,驱动会创建一个同名指令作回调。处理回调指令的函数就是do_process_drv_event。

do_process_drv_event

static void do_process_drv_event(struct i802_bss *bss, int cmd,
				 struct nlattr **tb)
{
	struct wpa_driver_nl80211_data *drv = bss->drv;
	int external_scan_event = 0;
	wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s",
		   cmd, nl80211_command_to_string(cmd), bss->ifname);
    ... ... ... ...//一些特殊指令的处理
	switch (cmd) {
   case XXX:
    	... ... ... ...
	case NL80211_CMD_AUTHENTICATE:
	case NL80211_CMD_ASSOCIATE:
	case NL80211_CMD_DEAUTHENTICATE:
	case NL80211_CMD_DISASSOCIATE:
	case NL80211_CMD_FRAME_TX_STATUS:
	case NL80211_CMD_UNPROT_DEAUTHENTICATE:
	case NL80211_CMD_UNPROT_DISASSOCIATE:
		mlme_event(bss, cmd, tb[NL80211_ATTR_FRAME],
			   tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
			   tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
			   tb[NL80211_ATTR_COOKIE],
			   tb[NL80211_ATTR_RX_SIGNAL_DBM],
			   tb[NL80211_ATTR_STA_WME],
			   tb[NL80211_ATTR_REQ_IE]);
		break;
	case NL80211_CMD_CONNECT:
	case NL80211_CMD_ROAM:
		mlme_event_connect(drv, cmd,
				   tb[NL80211_ATTR_STATUS_CODE],
				   tb[NL80211_ATTR_MAC],
				   tb[NL80211_ATTR_REQ_IE],
				   tb[NL80211_ATTR_RESP_IE],
				   tb[NL80211_ATTR_TIMED_OUT],
				   tb[NL80211_ATTR_TIMEOUT_REASON],
				   NULL, NULL, NULL,
				   tb[NL80211_ATTR_FILS_KEK],
				   NULL,
				   tb[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM],
				   tb[NL80211_ATTR_PMK],
				   tb[NL80211_ATTR_PMKID]);
		break;
   case XXX:
     	... ... ... ...
	default:
		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
			"(cmd=%d)", cmd);
		break;
	}
}

会接着调用mlme_event(bss, NL80211_CMD_AUTHENTICATION, … …)。
mlme_event中,case NL80211_CMD_AUTHENTICATE会调用mlme_event_auth处理。

mlme_event_auth

static void mlme_event_auth(struct wpa_driver_nl80211_data *drv,
			    const u8 *frame, size_t len)
{
	const struct ieee80211_mgmt *mgmt;
	union wpa_event_data event;
	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
	    drv->force_connect_cmd) {
		/*
		 * Avoid reporting two association events that would confuse
		 * the core code.
            * 如果当前wifi驱动未使用SME,则忽略掉auth event
		 */
		wpa_printf(MSG_DEBUG,
			   "nl80211: Ignore auth event when using driver SME");
		return;
	}
	wpa_printf(MSG_DEBUG, "nl80211: Authenticate event");
	mgmt = (const struct ieee80211_mgmt *) frame;
	if (len < 24 + sizeof(mgmt->u.auth)) {
		wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
			   "frame");
		return;
	}
	... ... ... ...
	wpa_supplicant_event(drv->ctx, EVENT_AUTH, &event);
}

wpa_supplicant_event中,对于EVENT_AUTH会调用sme_event_auth函数

sme_event_auth

void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
{
	struct wpa_ssid *ssid = wpa_s->current_ssid;
	... ... ... ...//一些异常判断
	wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication response: peer=" MACSTR
		" auth_type=%d auth_transaction=%d status_code=%d", MAC2STR(data->auth.peer), data->auth.auth_type, data->auth.auth_transaction, data->auth.status_code);
	wpa_hexdump(MSG_MSGDUMP, "SME: Authentication response IEs", data->auth.ies, data->auth.ies_len);
	eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
#ifdef CONFIG_SAE
	if (data->auth.auth_type == WLAN_AUTH_SAE) {
		int res;//这里会调sme_sae_auth
		res = sme_sae_auth(wpa_s, data->auth.auth_transaction, data->auth.status_code, data->auth.ies, data->auth.ies_len, 0, NULL);
		if (res < 0) {
			wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
			wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
		}
		if (res != 1)
			return;//commit阶段直接返回
		wpa_printf(MSG_DEBUG, "SME: SAE completed - setting PMK for "
			   "4-way handshake");//confirm阶段,开始set_pmk为下一步流程做准备
		wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN, wpa_s->sme.sae.pmkid, wpa_s->pending_bssid);
	}
#endif /* CONFIG_SAE */
	if (data->auth.status_code != WLAN_STATUS_SUCCESS) {
		... ... ... ...//异常处理
	}
	sme_associate(wpa_s, ssid->mode, data->auth.peer,
		      data->auth.auth_type);//auth成功了的话调用sme_associate函数,进入associate流程
}

sme_sae_auth

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)//如果当前并非使用external auth,则调用sme_send_authentication。本文走这条路径
			sme_send_authentication(wpa_s, wpa_s->current_bss,
						wpa_s->current_ssid, 0);
		else
			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;
}

confirm阶段又回到sme_send_authentication函数,这一次会在函数内调用sme_auth_build_sae_confirm生成sae confirm,再调用一次wpa_drv_authenticate重复整个流程。
当wpa_supplicant_event中再次收到EVENT_AUTH,仍调用sme_event_auth函数。
在sme_event_auth函数里,若confirm阶段成功,则调用sme_associate进入associate阶段。

至此,WPASv2.9的SAE之支持SME的auth四次握手阶段结束。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值