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

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.csdn.net/NJZhuJinhua/archive/2010/04/11/5473970.aspx
hostapd的radius/eap server代码分析(2)-hostapd配置
http://blog.csdn.net/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)待定  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值