hostapd的radius/eap server代码分析(3)-初始化及一次认证过程

本文详细分析了hostapd中radius/eapserver的初始化及认证过程,包括配置应用、radiusserver初始化、事件回调、消息收发、EAP状态机等关键环节,提供了一个全面的技术解读。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

hostapd的radius/eap server代码分析(3)-初始化及一次认证过程
这组代码分析只是不才准备发在csdn的blog中的,内容从hostapd到eapserver到具体eapType最后是openssl。
主要偏重于网络安全,而PEDIY似乎片中软件安全居多,发在这里不知是否合适。
注册PEDIY的目的是为了在密码学板块交流的。
发此文为了申请邀请码,希望斑竹赏脸

本文给出的代码中可能缺少某些结构及变量的定义说明,最好结合hostapd代码查看,所用版本为0.6.10
如有某些内容不通顺或叙述方法不好还望指出,以便后续修改。谢谢


前两篇是:
hostapd的radius/eap server代码分析(1)-main
http://blog.youkuaiyun.com/NJZhuJinhua/archive/2010/04/11/5473970.aspx
hostapd的radius/eap server代码分析(2)-hostapd配置
http://blog.youkuaiyun.com/NJZhuJinhua/archive/2010/04/13/5479573.aspx

njzjh@PEDIY May.6,2010
NJZhuJinhua@csdn May.6,2010
转载请注明出处

上会说道hostapd的配置部分,下面来看一下这些配置是怎么用到radius/eap server的实现中去的。
static int hostapd_setup_radius_srv(struct hostapd_data *hapd,
struct hostapd_bss_config *conf)
{
struct radius_server_conf srv;
os_memset(&srv, 0, sizeof(srv));
srv.client_file = conf->radius_server_clients;
srv.auth_port = conf->radius_server_auth_port;
......
hapd->radius_srv = radius_server_init(&srv);
if (hapd->radius_srv == NULL) {
wpa_printf(MSG_ERROR, "RADIUS server initialization failed.");
return -1;
}

return 0;
}
作为参数的hostapd_data *hapd以及hostapd_bss_config *conf前面已有说明,其实这里只用第一个参数即可,第二个参数实际是第一个参数的一个成员变量。
radius_server_conf srv:顾名思义,存储本radius server的配置信息。
用hapd以及conf进行设置,然后调用radius_server_init完成radius server的初始化。函数调用返回struct radius_server_data指针并将此指针赋值给hapd->radius_srv。
这些配置信息均是全局唯一的。
radius_server_init内对radius_server_data进行初始化,其中包括对客户端安全关联的初始化,打开套接字准备接收消息,注册套接字上的事件处理回调函数等。

其中注册事件处理回调函数代码如下:
if (eloop_register_read_sock(data->auth_sock,
radius_server_receive_auth,
data, NULL)) {
radius_server_deinit(data);
return NULL;
}
即在data->auth_sock上有消息到达时调用radius_server_receive_auth进行处理
[对radius消息的收发用ACE的EventHandler重新实现,效果也不错]

radius_server_receive_auth实现的内容主要如下:
1:recvfrom接收数据
2:取得客户端信息,包括安全关联等
3:msg = radius_msg_parse(buf, len);解析radius属性。其中msg为struct radius_msg *msg = NULL;
4:radius_msg_verify_msg_auth校验80属性
5:处理radius请求
if (radius_server_request(data, msg, (struct sockaddr *) &from,
fromlen, client, abuf, from_port, NULL) == -2)
return; /* msg was stored with the session */
-2的含义为PENDING。

下面直接给出radius_server_request的实现。
static int radius_server_request(struct radius_server_data *data,
struct radius_msg *msg,
struct sockaddr *from, socklen_t fromlen,
struct radius_client *client,
const char *from_addr, int from_port,
struct radius_session *force_sess)
{
u8 *eap = NULL;
size_t eap_len;
int res, state_included = 0;
u8 statebuf[4];
unsigned int state;
struct radius_session *sess;
struct radius_msg *reply;
int is_complete = 0;

if (force_sess) 直接指定已有session,譬如上一次pending了,现在complete_cb后继续执行时把刚才的session在这里指定一下即可,如果记下刚才生成session得到的state,这里也可以通过在msg中增加RADIUS_ATTR_STATE值来实现,太难看了,还是force_sess实现的好!
sess = force_sess;
else {
//根据请求中的state属性找session
res = radius_msg_get_attr(msg, RADIUS_ATTR_STATE, statebuf,
sizeof(statebuf));
state_included = res >= 0;
if (res == sizeof(statebuf)) {
state = WPA_GET_BE32(statebuf);
sess = radius_server_get_session(client, state);
} else {
sess = NULL;
}
}

if (sess) {
RADIUS_DEBUG("Request for session 0x%x", sess->sess_id);
} else if (state_included) {
//含有state但是没找到session,拒绝
RADIUS_DEBUG("State attribute included but no session found");
radius_server_reject(data, client, msg, from, fromlen,
from_addr, from_port);
return -1;
} else {
//没带state 认为第一次接入,生成新的session
sess = radius_server_get_new_session(data, client, msg);
if (sess == NULL) {
RADIUS_DEBUG("Could not create a new session");
radius_server_reject(data, client, msg, from, fromlen,
from_addr, from_port);
return -1;
}
}

//判断是否重发的消息,及遇上一次端口一致,radiusid一致,radius头中的鉴别码一致。
//对重发的请求消息,则重发上一次的challenge消息(如果sess->last_reply有的话)
if (sess->last_from_port == from_port &&
sess->last_identifier == msg->hdr->identifier &&
os_memcmp(sess->last_authenticator, msg->hdr->authenticator, 16) ==
0) {
RADIUS_DEBUG("Duplicate message from %s", from_addr);
data->counters.dup_access_requests++;
client->counters.dup_access_requests++;

if (sess->last_reply) {
res = sendto(data->auth_sock, sess->last_reply->buf,
sess->last_reply->buf_used, 0,
(struct sockaddr *) from, fromlen);
if (res < 0) {
perror("sendto[RADIUS SRV]");
}
return 0;
}

RADIUS_DEBUG("No previous reply available for duplicate "
"message");
return -1;
}

//取得radius请求消息中的eapmsg
eap = radius_msg_get_eap(msg, &eap_len);
if (eap == NULL) {
RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s",
from_addr);
data->counters.packets_dropped++;
client->counters.packets_dropped++;
return -1;
}

RADIUS_DUMP("Received EAP data", eap, eap_len);

/* FIX: if Code is Request, Success, or Failure, send Access-Reject;
* RFC3579 Sect. 2.6.2.
* Include EAP-Response/Nak with no preferred method if
* code == request.
* If code is not 1-4, discard the packet silently.
* Or is this already done by the EAP state machine? */
//上面注释看,eapserver跟eapstatemachine看来不是一个人写的。

wpabuf_free(sess->eap_if->eapRespData);
//用收到的eapmsg给sess->eap_if->eapRespData赋值
sess->eap_if->eapRespData = wpabuf_alloc_ext_data(eap, eap_len);
if (sess->eap_if->eapRespData == NULL)
os_free(eap);
eap = NULL;
sess->eap_if->eapResp = TRUE;

//运行 eap state machine,所处理数据即为sess->eap_if->eapRespData 。
eap_server_sm_step(sess->eap);

if ((sess->eap_if->eapReq || sess->eap_if->eapSuccess ||
sess->eap_if->eapFail) && sess->eap_if->eapReqData) {
//状态为三者之一置位且含有EAPReq数据的话,打印请求数据
RADIUS_DUMP("EAP data from the state machine",
wpabuf_head(sess->eap_if->eapReqData),
wpabuf_len(sess->eap_if->eapReqData));
} else if (sess->eap_if->eapFail) {
RADIUS_DEBUG("No EAP data from the state machine, but eapFail "
"set");
} else if (eap_sm_method_pending(sess->eap)) {
//eap处理过程中,有可能是具体eapmethod如eap-sim/aka等在处理过程中需要外发请求数据等待响应等情况。
if (sess->last_msg) {
radius_msg_free(sess->last_msg);
os_free(sess->last_msg);
}
sess->last_msg = msg;
sess->last_from_port = from_port;
os_free(sess->last_from_addr);
sess->last_from_addr = os_strdup(from_addr);
sess->last_fromlen = fromlen;
os_memcpy(&sess->last_from, from, fromlen);
return -2;
} else {
RADIUS_DEBUG("No EAP data from the state machine - ignore this"
" Access-Request silently (assuming it was a "
"duplicate)");
data->counters.packets_dropped++;
client->counters.packets_dropped++;
return -1;
}

//多轮交互后sess完成的标志,成功 or 失败?
if (sess->eap_if->eapSuccess || sess->eap_if->eapFail)
is_complete = 1;

//封装eap消息
reply = radius_server_encapsulate_eap(data, client, sess, msg);

if (reply) {
RADIUS_DEBUG("Reply to %s:%d", from_addr, from_port);
if (wpa_debug_level <= MSG_MSGDUMP) {
radius_msg_dump(reply);
}

//性能统计
switch (reply->hdr->code) {
case RADIUS_CODE_ACCESS_ACCEPT:
data->counters.access_accepts++;
client->counters.access_accepts++;
break;
case RADIUS_CODE_ACCESS_REJECT:
data->counters.access_rejects++;
client->counters.access_rejects++;
break;
case RADIUS_CODE_ACCESS_CHALLENGE:
data->counters.access_challenges++;
client->counters.access_challenges++;
break;
}
//发送AccessChallenge消息
res = sendto(data->auth_sock, reply->buf, reply->buf_used, 0,
(struct sockaddr *) from, fromlen);
if (res < 0) {
perror("sendto[RADIUS SRV]");
}
if (sess->last_reply) {
radius_msg_free(sess->last_reply);
os_free(sess->last_reply);
}
sess->last_reply = reply;
sess->last_from_port = from_port;
sess->last_identifier = msg->hdr->identifier;
//保存port id authenticator等,用于判断下一个请求是不是这一次aceessRequest的重发
os_memcpy(sess->last_authenticator, msg->hdr->authenticator,
16);
} else {
data->counters.packets_dropped++;
client->counters.packets_dropped++;
}

//session结束后,在下一次定时器到达后删除这个sess
if (is_complete) {
RADIUS_DEBUG("Removing completed session 0x%x after timeout",
sess->sess_id);
eloop_cancel_timeout(radius_server_session_remove_timeout,
data, sess);
eloop_register_timeout(10, 0,
radius_server_session_remove_timeout,
data, sess);
}

return 0;
}


刚才想发上去了,可惜注册24小时内不让发帖,继续补充如下:
hostapd的radius/eap server代码分析 标题太冗长,有点泛。后面小主题决定自成系列了,下一步计划是
eap状态机代码分析-(1)结构定义
eap状态机代码分析-(2)eap算法协商机制
eap状态机代码分析-(3)eap超时重传机制
eap状态机代码分析-(4)待定  

Send EAPOL-Key msg - hexdump(len=155): 02 03 00 97 02 13 ca 00 10 00 00 00 00 00 00 00 02 a0 3b 36 9d d0 c8 06 90 1f 5a 44 2f ec a7 db 24 83 c9 6f 9a 19 70 63 23 68 cd 57 3b 5c c9 3c 71 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... 行 72051: 07-01 15:11:24.627159 7246 7246 D hostapd : WPA: Use EAPOL-Key timeout of 1000 ms (retry counter 1) 行 72053: 07-01 15:11:24.644848 7246 7246 D hostapd : wlan2: Event EAPOL_RX (23) received 行 72054: 07-01 15:11:24.644873 7246 7246 D hostapd : Receive wpa msg : Event EAPOL_RX (23) received 行 72055: 07-01 15:11:24.644880 7246 7246 D hostapd : IEEE 802.1X: 99 bytes from 26:c5:5e:16:a9:37 (encrypted=-1) 行 72056: 07-01 15:11:24.644887 7246 7246 D hostapd : IEEE 802.1X: version=1 type=3 length=95 行 72057: 07-01 15:11:24.644891 7246 7246 D hostapd : WPA: Received EAPOL-Key from 26:c5:5e:16:a9:37 key_info=0x30a type=2 mic_len=16 key_data_length=0 行 72058: 07-01 15:11:24.644900 7246 7246 D hostapd : WPA: Received Key Nonce - hexdump(len=32): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 行 72059: 07-01 15:11:24.644904 7246 7246 D hostapd : WPA: Received Replay Counter - hexdump(len=8): 00 00 00 00 00 00 00 02 行 72060: 07-01 15:11:24.644910 7246 7246 D hostapd : WPA: EAPOL-Key MIC using HMAC-SHA1 行 72061: 07-01 15:11:24.644917 7246 7246 D hostapd : WPA: 26:c5:5e:16:a9:37 WPA_PTK entering state PTKINITDONE 行 72062: 07-01 15:11:24.644951 7246 7246 D hostapd : wpa_driver_nl80211_set_key: ifindex=30 (wlan2) alg=3 addr=0xb400007d96d53c10 key_idx=0 set_tx=1 seq_len=0 key_len=16 key_flag=0x2c link_id=-1 行 72063: 07-01 15:11:24.644957 7246 7246 D hostapd : nl80211: NEW_KEY 行 72064: 07-01 15:11:24.644962 7246 7246 D hostapd : nl80211: KEY_DATA - hexdump(len=16): [REMOVED] 行 72065: 07-01 15:11:24.644966 7246 7246 D hostapd : addr=26:c5:5e:16:a9:37 行 72066: 07-01 15:11:24.644970 7246 7246 D hostapd : pairwise key 行 72068: 07-01 15:11:24.650820 7246 7246 D hostapd : Added PTKSA cache entry addr=26:c5:5e:16:a9:37 cipher=16 行 72069: 07-01 15:11:24.650896 7246 7246 D hostapd : notify client 26:c5:5e:16:a9:37 Connected 行 72070: 07-01 15:11:24.651457 7246 7246 I hostapd : wlan2: AP-STA-CONNECTED 26:c5:5e:16:a9:37 行 72071: 07-01 15:11:24.651485 7246 7246 D hostapd : CTRL_IFACE monitor send /data/vendor/wifi/hostapd/sockets/wpa_ctrl_2591-7\x00 行 72073: 07-01 15:11:24.651739 7246 7246 D hostapd : Receive wpa msg : AP-STA-CONNECTED 26:c5:5e:16:a9:37 行 72074: 07-01 15:11:24.651763 7246 7246 D hostapd : nl80211: Set STA flags - ifname=wlan2 addr=26:c5:5e:16:a9:37 total_flags=0x61 flags_or=0x1 flags_and=0xffffffff authorized=1 行 72077: 07-01 15:11:24.651913 7246 7246 I hostapd : wlan2: STA 26:c5:5e:16:a9:37 WPA: pairwise key handshake completed (RSN) 行 72078: 07-01 15:11:24.651975 7246 7246 I hostapd : wlan2: STA 26:c5:5e:16:a9:37 WPA: pairwise key handshake completed (RSN) 行 72079: 07-01 15:11:24.652008 7246 7246 I hostapd : wlan2: EAPOL-4WAY-HS-COMPLETED 26:c5:5e:16:a9:37
07-02
elro -ffunction-sections -fdata-sections" /home/ubuntu/tina-v821-release/prebuilt/hostbuilt/make4.1/bin/make -C /home/ubuntu/tina-v821-release/out/v821/avaota_f1/openwrt/build_dir/target/hostapd-full-internal/hostapd-2020-06-08-5a8b3662/hostapd AR="riscv32-linux-musl-ar" AS="riscv32-linux-musl-gcc -c -Os -pipe -fno-caller-saves -fno-plt -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wformat -Werror=format-security -DPIC -fpic -D_FORTIFY_SOURCE=2 -Wl,-z,now -Wl,-z,relro -ffunction-sections -fdata-sections" LD=riscv32-linux-musl-ld NM="riscv32-linux-musl-nm" CC="riscv32-linux-musl-gcc" GCC="riscv32-linux-musl-gcc" CXX="riscv32-linux-musl-g++" RANLIB="riscv32-linux-musl-ranlib" STRIP=riscv32-linux-musl-strip OBJCOPY=riscv32-linux-musl-objcopy OBJDUMP=riscv32-linux-musl-objdump SIZE=riscv32-linux-musl-size CONFIG_ACS=y CONFIG_DRIVER_NL80211=y CONFIG_IEEE80211N= CONFIG_IEEE80211AC= CONFIG_IEEE80211AX= CONFIG_DRIVER_WEXT= CONFIG_WEP=y LIBS="-L/home/ubuntu/tina-v821-release/prebuilt/rootfsbuilt/riscv/nds32le-linux-musl-v5d//sysroot/usr/lib -DPIC -fpic -specs=/home/ubuntu/tina-v821-release/openwrt/openwrt/include/hardened-ld-pie.specs -znow -zrelro -Wl,--gc-sections -fuse-linker-plugin -lubox -lubus -lm -lnl-tiny" LIBS_c="" AR="riscv32-linux-musl-ar" BCHECK= hostapd hostapd_cli make[4]: Entering directory &#39;/home/ubuntu/tina-v821-release/out/v821/avaota_f1/openwrt/build_dir/target/hostapd-full-internal/hostapd-2020-06-08-5a8b3662/hostapd&#39; In file included from main.c:26: /home/ubuntu/tina-v821-release/out/v821/avaota_f1/openwrt/build_dir/target/hostapd-full-internal/hostapd-2020-06-08-5a8b3662/src/ap/hostapd.h:84:21: error: field &#39;ubus&#39; has incomplete type 84 | struct ubus_object ubus; | ^~~~ Makefile:1302: recipe for target &#39;main.o&#39; failed make[4]: *** [main.o] Error 1 make[4]: Leaving directory &#39;/home/ubuntu/tina-v821-release/out/v821/avaota_f1/openwrt/build_dir/target/hostapd-full-internal/hostapd-2020-06-08-5a8b3662/hostapd&#39; Makefile:708: recipe for target &#39;/home/ubuntu/tina-v821-release/out/v821/avaota_f1/openwrt/build_dir/target/hostapd-full-internal/hostapd-2020-06-08-5a8b3662/.built&#39; failed make[3]: *** [/home/ubuntu/tina-v821-release/out/v821/avaota_f1/openwrt/build_dir/target/hostapd-full-internal/hostapd-2020-06-08-5a8b3662/.built] Error 2 make[3]: Leaving directory &#39;/home/ubuntu/tina-v821-release/openwrt/openwrt/package/network/services/hostapd&#39; time: package/network/services/hostapd/full-internal/compile#0.71#0.22#0.79#1751822626.32#1751822627.10 ERROR: package/network/services/hostapd failed to build (build variant: full-internal). More error defails please refer to: /home/ubuntu/tina-v821-release/openwrt/openwrt/build_log/package/network/services/hostapd/full-internal/compile.txt package/Makefile:114: recipe for target &#39;package/network/services/hostapd/compile&#39; failed make[2]: *** [package/network/services/hostapd/compile] Error 1 make[2]: Leaving directory &#39;/home/ubuntu/tina-v821-release/openwrt/openwrt&#39; package/Makefile:110: recipe for target &#39;/home/ubuntu/tina-v821-release/openwrt/openwrt/staging_dir/target/stamp/.package_compile&#39; failed make[1]: *** [/home/ubuntu/tina-v821-release/openwrt/openwrt/staging_dir/target/stamp/.package_compile] Error 2 make[1]: Leaving directory &#39;/home/ubuntu/tina-v821-release/openwrt/openwrt&#39; /home/ubuntu/tina-v821-release/openwrt/openwrt/include/toplevel.mk:238: recipe for target &#39;world&#39; failed make: *** [world] Error 2 make: Leaving directory &#39;/home/ubuntu/tina-v821-release/openwrt/openwrt&#39; INFO: build_openwrt_rootfs failed 编译tianlinux的时候报错如上,帮我看看怎么办?
最新发布
07-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值