首页
论坛
专栏
课程
1

[旧帖] [原创]hostapd的radius/eap server代码分析(3)-初始化及一次认证过程 0.00元

njzjh 2010-5-7 22:04 1113
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)待定

快讯:科锐逆向工程师培训(第32期,10月10日开学)!

最新回复 (2)
njzjh 2010-5-9 09:24
2
邀请码好难得啊
SENTEMUL 2010-5-9 10:29
3
很长细,支持一下
返回