hostapd代码分析-完全的802.1X认证过程(radius服务器)

时间:2024-03-15 15:20:57

第一章、802.1X认证过程简述

802.1x认证的函数调用如下所示:

hostapd代码分析-完全的802.1X认证过程(radius服务器)

                                                                            图 1-1

       由上图可以看出,802.1x认证主要是在函数eapol_sm_step_run中完成。过程可以分为,认证、连接、EAP处理、四次握手(在图中未体现)四个过程。

红色线段:

  表示在hostapd_notif_assoc函数结束之后,执行eapol_sm_step_run发送request/identity报文

橙色线段:

       表示接收EAP报文,发送radius报文过程

绿色线段:

       表示接收radius报文,发送EAP报文过程

1.1、 认证

 802.1X认证的认证步骤与PSK认证、open认证相同。

步骤如下:

1、wpa_supplicant_event(Drv_callback.c):根据不同报文获取不同的操作函数。

2、hostapd_notif_auth(Drv_callback.c):authentication request处理函数

3、hostapd_sta_auth(Ap_drv_ops.c):authentication response发送

4、atheros_sta_auth(driver_atheros.c):发送authentication response

       Note:该函数由函数指针调用,函数指针结构体赋值见附录

5、ioctl:发送报文给底层驱动。然后由驱动发送authentication response报文

小结:

       认证过程,即由认证处理函数进行简单的处理,然后创建authentication response报文交给底层驱动发送出去。

1.2、 连接

连接过程如下:

       1、wpa_supplicant_event(1)(Drv_callback.c):根据不同报文获取不同的操作函数。

       2、hostapd_notif_assoc(2)(Drv_callback.c):association request处理函数

       3、hostapd_sta_assoc(3)(Ap_drv_ops.c):association request发送

       4、atheros_sta_assoc(4)(driver_atheros.c):发送association request

 5、ioctl:发送报文给底层驱动。然后由驱动发送association response报文

       6、hostapd_new_assoc_sta(3)(hostapd.c):通知一个新的工作站连入AP

       7、ieee802_1x_new_station(4)(ieee802_1x.c):处理一个新的工作站

       8、ieee802_1x_alloc_eapol_sm调用eapol_auth_alloc

   调用eapol_auth_initializeàeapol_sm_step_run(状态机初始化):图1-1的红色线段指向即为该过程。

       9、wpa_auth_sta_associated(4)(wpa_auth.c):wpa认证连接

       10、wpa_sm_step(5)(wpa_auth.c):设置WPA_PTK状态。

Note:

(n):代表下钻的等级。

 

802.1X认证的连接和PSK以及OPEN认证之间有一些不同

主要不同之处:

       802.1X认证将WPA_PTK状态进行到authentication2过程即结束。然后等待802.1X认证结束之后再进行四次握手过程

       PSK认证将WPA_PTK状态进行到PSKSTART,发送四次握手的报文1

       Open认证无WAP_PTK过程。

      

1.3、 802.1X认证

1x认证过程模型如下(由radius服务器)

hostapd代码分析-完全的802.1X认证过程(radius服务器)

图1-2 802.1X认证模型

 在有认证服务器的情况下,认证者(AP)将主要的认证功能全部交给认证服务器,自身只进行EAP报文和radius报文之间的转化。但是第一个request/identity报文需要由认证者创建并发送。所以整个过程大致分为五步:

1、 认证者发送request/identity报文

2、 认证者处理response/identity报文,并将其转换为radius报文,并发送

3、 认证者接收radius报文,解封出EAP报文并发送

4、 认证者接收EAP报文,封装成radius报文并发送

5、 认证帧接收radius-accept报文,解封出EAP-success报文并发送

1.3.1、发送request/identity

 1、在hostapd_notif_assoc执行完毕后。调用struct eloop_timeout结构体的函数指针所指向的eapol_sm_step_cb函数。

       2、eapol_sm_step_cb调用eapol_sm_step_run函数发送request/identity

1.3.2、处理response/identity,发送radius

       1、hostapd_event_eapol_rx(1)(drv_callback.c)函数处理

       2、ieee802_1x_receive(2)

       3、handle_eap(3):根据EAP报文的类型,选择相应的函数

       4、handle_eap_response(4):EAP响应报文处理函数

       5、eapol_auth_step(3):配置eloop_timeout回调函数

       6、timeout回调函数eapol_sm_step_cb调用eapol_sm_step_run:发送radius报文

1.3.3、接收radius报文,发送EAP报文

       即图1-1中的绿色线段所表示过程

 1、ieee802_1x_receive_auth(1):经过radius_client_receive函数进行简单处理后回调

       2、ieee802_1x_decapsulate_radius(2):解封出EAP报文

       3、eapol_auth_step(2):配置eloop_timeout回调函数

       4、timeout回调函数eapol_sm_step_cbàeapol_sm_step_run:发送EAP报文

1.3.4、接收EAP报文,发送radius报文

       即图1-1中的橙色线段所表示过程

       1、hostapd_event_eapol_rx(1)(drv_callback.c)函数处理

       2、ieee802_1x_receive(2)

       3、handle_eap(3):根据EAP报文的类型,选择相应的函数

       4、handle_eap_response(4):EAP响应报文处理函数

       5、eapol_auth_step(3):配置eloop_timeout回调函数

       6、timeout回调函数eapol_sm_step_cbàeapol_sm_step_run:发送radius报文

注意:1.3.2与1.3.4在上述介绍中看似相同,但是实际在eapol_sm_step_run处理中不同。下章将详细介绍里边的具体执行

1.3.5、接收radius-accept报文,发送EAP-success报文

 1、ieee802_1x_receive_auth(1):经过radius_client_receive函数进行简单处理后回调

       2、ieee802_1x_decapsulate_radius(2):解封出EAP报文

       3、eapol_auth_step(2):配置eloop_timeout回调函数

       4、timeout回调函数eapol_sm_step_cb调用eapol_sm_step_run:发送EAP报文

注意:1.3.3与1.3.5在上述介绍中看似相同,但是实际在eapol_sm_step_run处理中不同。下章将详细介绍里边的具体执行

 

第二章、eapol_sm_step_run函数

2.1、 eapol_sm_step_runhan数介绍

功能:

 eapol状态机状态切换运行函数

参数:

       sm:eapol状态机数据结构体struct eapol_state_machine

过程:

       包含一个goto循环,包含两个阶段

       1、记录AUTH_PAE、BE_AUTH、REAUTH_TIMER、AUTH_KEY_TX、KEY_RX、CTRL_DIR

的状态,然后执行这六种状态机的SM_STEP函数,然后根据现有条件去切换状态。

       2、如果上述六种状态机,任何一个状态机状态发生改变,那么重新执行步骤1

       3、如果上述六种状态机未发生改变,执行eap_server_sm_step函数,进行EAP状态机状态切换。如果EAP状态机状态发生改变,那么重新执行步骤1

    并且在eapol_sm_step_run函数中定义了一个max_steps。每执行一次goto,该值减一。如果减到为0,则跳出goto循环。

如图所示:

 hostapd代码分析-完全的802.1X认证过程(radius服务器)

图2-1eapol_sm_step_run函数执行流程

2.2、 六种状态机介绍

2.2.1、AUTH_PAE  

 连接埠认证状态,主要是在802.1x认证前准备阶段和认证后的四次握手后进行状态切换

有以下10个状态

1、 AUTH_PAE_INITIALIZE 

 初始化:

 sm->portMode = Auto;

 sm->keyRun = FALSE;   

2、 AUTH_PAE_DISCONNECTED

 断开连接

 设置端口和下网标志位,并通知底层驱动

3、AUTH_PAE_CONNECTING

       连接

       sm->authEntersConnecting++;  

       sm->reAuthenticate= FALSE 

       sm->reAuthCount++

4、AUTH_PAE_AUTHENTICATING

       认证中

       sm->eapolStart= FALSE;

       sm->authSuccess= FALSE;

       sm->authFail= FALSE;

       sm->authTimeout= FALSE;

       sm->authStart= TRUE;

       sm->keyRun= FALSE;

       sm->keyDone= FALSE;

5、AUTH_PAE_AUTHENTICATED

       已认证(代表整个认证过程全部结束,可以DHCP获取IP了)

       设置端口标志位,并通知底层驱动

       调用_ieee802_1x_finished函数,认证成功  (该状态在四次握手之后表示成功)

6、AUTH_PAE_ABORTING

       中止

7、AUTH_PAE_HELD

       挂起

8、AUTH_PAE_FORCE_AUTH,

       强行认证

9、AUTH_PAE_FORCE_UNAUTH

       强行断开认证

10、AUTH_PAE_RESTART

       重新开始

       sm->eap_if->eapRestart= TRUE

注意: INITIALIZE  DISCONNECTED  RESTART CONNECTING AUTHENTICATING五个状态在802.1x中使用

AUTH_PAE_AUTHENTICATED状态在四次握手成功后调用

2.2.2、BE_AUTH      

 后端认证,伴随802.1x认证过程。处理接收到的EAP报文,发送EAP报文

 由以下8个状态

1、 BE_AUTH_REQUEST

 发送EAP报文给申请者

       txReq();  //发送报文

 sm->eap_if->eapReq = FALSE; //发送后发送申请标志位清零

 sm->backendOtherRequestsToSupplicant++;

2、 BE_AUTH_RESPONSE

 处理接收到的EAP报文

 sm->authTimeout= FALSE;

 sm->eapolEap= FALSE;

 sm->eap_if->eapNoReq= FALSE;  

 sm->aWhile =sm->serverTimeout;

 sm->eap_if->eapResp= TRUE;   //接收到EAP报文标志位置位

 sm->backendResponses++;    

3、 BE_AUTH_SUCCESS

 后端认证成功,即802.1X认证成功

 txReq();  //向申请者发送EAP/success报文

 sm->authSuccess= TRUE; 

 sm->keyRun = TRUE;

4、 BE_AUTH_FAIL

 认证失败

 txReq(); //发送EAP/failure报文

 sm->authFail = TRUE;

5、 BE_AUTH_TIMEOUT

 认证超时

 sm->authTimeout = TRUE;

6、 BE_AUTH_IDLE

 闲置等待

 sm->authStart = FALSE;

7、 BE_AUTH_INITIALIZE,

 初始化

 abortAuth();      //中止认证

 sm->eap_if->eapNoReq= FALSE;

 sm->authAbort = FALSE;

8、BE_AUTH_IGNORE

       忽略

       sm->eap_if->eapNoReq= FALSE

       (这个状态只有在response状态之后,eapNoReq仍旧为true)

认证开始前:BE_AUTH_INITIALIZE、BE_AUTH_IDLE

认证过程中:BE_AUTH_REQUEST 和 BE_AUTH_RESPONSE交替进行

认证成功:BE_AUTH_SUCCESS、BE_AUTH_IDLE

 

2.2.3、REAUTH_TIMER

重新认证计时器状态机

有以下两种状态

1、 REAUTH_TIMER_INITIALIZE

 重新认证计时器初始化

 sm->reAuthWhen = sm->reAuthPeriod;  //设置重新认证时间间隔为重新认证时间周期

 

2、 REAUTH_TIMER_REAUTHENTICATE

 重新认证

 sm->reAuthenticate = TRUE

 wpa_auth_sm_event

2.2.4、AUTH_KEY_TX  

 认证者**发送状态机

1、 AUTH_KEY_TX_NO_KEY_TRANSMIT

 没有**发送

 仅改变状态,无任何操作

2、 AUTH_KEY_TX_KEY_TRANSMIT

 发送**

       txKey();    //**发送

       sm->eap_if->eapKeyAvailable= FALSE;

       sm->keyDone= TRUE;

2.2.5、KEY_RX

       **接收状态机

1、KEY_RX_NO_KEY_RECEIVE

       无**接收

       仅仅改变状态,无任何操作

2、KEY_RX_KEY_RECEIVE

       有**接收

       processKey();

       sm->rxKey= FALSE;

 

2.2.6、CTRL_DIR

 控制方向状态机

1、 CTRL_DIR_FORCE_BOTH

 sm->operControlledDirections = Both

2、 CTRL_DIR_IN_OR_BOTH

 sm->operControlledDirections= sm->adminControlledDirections;

 

2.2.7、小结

AUTH_PAE是连接埠的认证状态机,可以分为三个阶段

1、 连接处理函数hostapd_notif_assoc函数中的初始化阶段  INITIALIZE 

2、 发送EAP-identity/request报文之前的重新开始阶段       DISCONNECTED  RESTART CONNECTING AUTHENTICATING

3、 四次握手之后的认证完成确认阶段     AUTH_PAE_AUTHENTICATED

 

所以AUTH_PAE并不负责,802.1x认证,以及认证后的四次握手。这些有BE_AUTH进行处理

1、认证开始前:BE_AUTH_INITIALIZE、BE_AUTH_IDLE(hostapd_notif_assoc中)

2、认证过程中:BE_AUTH_REQUEST和 BE_AUTH_RESPONSE交替进行(802.1x认证具体过程)

3、认证成功:BE_AUTH_SUCCESS、BE_AUTH_IDLE:802.1x认证结束,可以开始四次握手

 

 REAUTH_TIMER伴随着每一步操作,无论是发送还是接受,只要是成功的步骤,都要将重新认证计时器初始化

而AUTH_KEY_RX、KEY_RX、CTRL_DIR只在hostapd_notif_assoc函数进行初始化,之后的过程不参加

 

第三章、eap_server_sm_step函数

 如图2-1所示,eapol_sm_step_run函数中,六种状态执行完毕,之后,会调用

eap_server_sm_step函数,进行EAP状态机的处理

3.1、EAP状态列表

struct eap_sm {

       enum{

0、EAP_DISABLED,

1、EAP_INITIALIZE,

2、EAP_IDLE,

3、EAP_RECEIVED,

4、EAP_INTEGRITY_CHECK,

5、EAP_METHOD_RESPONSE,

6、EAP_METHOD_REQUEST,

7、EAP_PROPOSE_METHOD,

8、EAP_SELECT_ACTION,

9、EAP_SEND_REQUEST,

10、EAP_DISCARD

11、EAP_NAK,

12、EAP_RETRANSMIT,

13、EAP_SUCCESS,

14、EAP_FAILURE,

15、EAP_TIMEOUT_FAILURE,

16、EAP_PICK_UP_METHOD,

17、EAP_INITIALIZE_PASSTHROUGH,

18、EAP_IDLE2,

19、EAP_RETRANSMIT2,

20、EAP_RECEIVED2,

21、EAP_DISCARD2,

22、EAP_SEND_REQUEST2,

23、EAP_AAA_REQUEST,

24、EAP_AAA_RESPONSE,

25、EAP_AAA_IDLE,

26、EAP_TIMEOUT_FAILURE2,

27、EAP_FAILURE2,

28、EAP_SUCCESS2,

29、EAP_INITIATE_REAUTH_START,

30、EAP_INITIATE_RECEIVED

       }EAP_state;

总共三十一个状态

3.2、802.1X认证中的EAP状态切换

1、 开始

 INITIALIZE         //eapRestart=1;portEnabled=1,EAP_state=0

      将eapRestart=0

       SELECT_ACTION    //选择行为 continue

       PROPOSE_METHOD //获取建议method

       METHOD_REQUEST//创建请求数据报

       SEND_REQUEST    //将创建的数据报放置到lastReqData,并设置标志位

 IDLE                     //重新发送时间间隔

2、接收EAP发送radius

 RECEIVED         //分析EAP报文,为sm结构体赋值

 INTEGRITY_CHECK  //完整性检查

 METHOD_RESPONSE //处理获取身份,key,session id

 SELECT_ACTION     //选择行为 PASSTHROUGH

 INITIALIZE_PASSTHROUGH//释放aaaEapRespData

 AAA_REQUEST            //将接收到的EAP报文放到aaaEapRespData中

 AAA_IDLE                 //设置重发时间间隔

 

3、接收radius发送EAP

 AAA_RESPONSE   //接收radius后的处理,获取EAP报文数据,radius ID,timeout;

 SEND_REQUEST2  //从radius获取的EAP报文放到lastReqData,并设置标志位

 IDLE2            //重新发送时间间隔

4、 接收EAP发送radius

 RECEIVED2 //分析EAP报文,为sm结构体赋值

 AAA_REQUEST     //将接收到的EAP报文放到aaaEapRespData中

 AAA_IDLE            //设置重发时间间隔

3/4步循环……

5、 接收radius accept报文

 SUCCESS2        // eapSuccess和start_reauth为true

 过程如图3-1所示

hostapd代码分析-完全的802.1X认证过程(radius服务器)

图3-1EAP状态机状态切换(802.1x认证过程)

附录:

A: 驱动函数接口

const struct wpa_driver_opswpa_driver_atheros_ops = {

       .name                   = "atheros",

       .hapd_init              = atheros_init,

       .hapd_deinit          = atheros_deinit,

       .set_ieee8021x              = atheros_set_ieee8021x,

       .set_privacy           = atheros_set_privacy,

       .set_key         = atheros_set_key,

       .get_seqnum         =atheros_get_seqnum,

       .flush                    = atheros_flush,

       .set_generic_elem  = atheros_set_opt_ie,

       .sta_set_flags         = atheros_sta_set_flags,

       .read_sta_data              = atheros_read_sta_driver_data,

       .read_sta_info      = atheros_read_sta_driver_info,

       .get_sta_idle_time  = atheros_get_sta_idle_time,

       .hapd_send_eapol = atheros_send_eapol,

       .sta_disassoc         = atheros_sta_disassoc,

       .sta_deauth           = atheros_sta_deauth,

       .hapd_set_ssid              = atheros_set_ssid,

       .hapd_get_ssid             = atheros_get_ssid,

       .set_countermeasures   = atheros_set_countermeasures,

       .sta_clear_stats      = atheros_sta_clear_stats,

       .commit                = atheros_commit,

       .set_ap_wps_ie             = atheros_set_ap_wps_ie,

       .set_authmode             = atheros_set_authmode,

       .set_ap                  = atheros_set_ap,

#if defined(CONFIG_IEEE80211R) ||defined(CONFIG_IEEE80211W)

       .sta_assoc              = atheros_sta_assoc,

       .sta_auth               = atheros_sta_auth,

       .send_mlme            =atheros_send_mgmt,

#endif /* CONFIG_IEEE80211R ||CONFIG_IEEE80211W */

#ifdef CONFIG_IEEE80211R

       .add_tspec        =atheros_add_tspec,

       .add_sta_node           =atheros_add_sta_node,

#endif /* CONFIG_IEEE80211R */

       .send_action          = atheros_send_action,

#if defined(CONFIG_WNM) &&defined(IEEE80211_APPIE_FRAME_WNM)

       .wnm_oper           = atheros_wnm_oper,

#endif /* CONFIG_WNM &&IEEE80211_APPIE_FRAME_WNM */

       .set_qos_map        = atheros_set_qos_map,

};

Note:不同的设备可能不同。

B: eapol_auth_cb函数接口

(eapol认证控制接口)

 

cb.eapol_send = ieee802_1x_eapol_send;

       cb.aaa_send= ieee802_1x_aaa_send;

       cb.finished= _ieee802_1x_finished;

       cb.get_eap_user= ieee802_1x_get_eap_user;

       cb.sta_entry_alive= ieee802_1x_sta_entry_alive;

       cb.logger= ieee802_1x_logger;

       cb.set_port_authorized= ieee802_1x_set_port_authorized;

       cb.abort_auth= _ieee802_1x_abort_auth;

       cb.tx_key= _ieee802_1x_tx_key;

       cb.eapol_event= ieee802_1x_eapol_event;

#ifdef CONFIG_ERP

       cb.erp_get_key= ieee802_1x_erp_get_key;

       cb.erp_add_key= ieee802_1x_erp_add_key;

#endif /* CONFIG_ERP */

C: eap_method函数接口

       eap->init= eap_identity_init;

       eap->initPickUp= eap_identity_initPickUp;

       eap->reset= eap_identity_reset;

       eap->buildReq= eap_identity_buildReq;

       eap->check= eap_identity_check;

       eap->process= eap_identity_process;

       eap->isDone= eap_identity_isDone;

       eap->isSuccess= eap_identity_isSuccess;