版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
一、预备知识(eap_sm、eap_method结构体)
-
struct eap_sm {
//状态机,存储eap的状态
-
enum {
//枚举eap的各种状态
-
EAP_DISABLED, EAP_INITIALIZE, EAP_IDLE, EAP_RECEIVED,
-
EAP_INTEGRITY_CHECK, EAP_METHOD_RESPONSE, EAP_METHOD_REQUEST,
-
EAP_PROPOSE_METHOD, EAP_SELECT_ACTION, EAP_SEND_REQUEST,
-
EAP_DISCARD, EAP_NAK, EAP_RETRANSMIT, EAP_SUCCESS, EAP_FAILURE,
-
EAP_TIMEOUT_FAILURE, EAP_PICK_UP_METHOD,
-
EAP_INITIALIZE_PASSTHROUGH, EAP_IDLE2, EAP_RETRANSMIT2,
-
EAP_RECEIVED2, EAP_DISCARD2, EAP_SEND_REQUEST2,
-
EAP_AAA_REQUEST, EAP_AAA_RESPONSE, EAP_AAA_IDLE,
-
EAP_TIMEOUT_FAILURE2, EAP_FAILURE2, EAP_SUCCESS2
-
} EAP_state;
-
-
/* Constants */
-
int MaxRetrans;
//最大重传次数,eap支持超时重发机制.eap_sm在初始化时赋值为5
-
-
struct eap_eapol_interface eap_if;
//主要放些直接与消息相关的,如req及resp的数据,当前是req还是resp,是否到了重传的时机(retransWhile)等
-
-
/* Full authenticator state machine local variables */
-
-
/* Long-term (maintained between packets) */
-
EapType currentMethod;
//当前采用的Method,初始为EAP_TYPE_NONE,其后根据响应中的type定或自选
-
int currentId;
//当前eap id,开始设为-1,作为backend_AAA时被设为响应消息eapid,需要发送eapreq的时候设为nextId
-
enum {
-
METHOD_PROPOSED, METHOD_CONTINUE, METHOD_END
-
} methodState;
-
int retransCount;
//传送次数
-
struct wpabuf *lastReqData;
//记下已经发出的请求数据,如需要重传时需要发此数据
-
int methodTimeout;
-
-
/* Short-term (not maintained between packets) */
-
Boolean rxResp;
//收到消息的id为resp时设置rxResp为TRUE
-
int respId;
//收到的resp消息的id
-
EapType respMethod;
-
int respVendor;
-
u32 respVendorMethod;
-
Boolean ignore;
-
enum {
-
DECISION_SUCCESS, DECISION_FAILURE, DECISION_CONTINUE,
-
DECISION_PASSTHROUGH
-
} decision;
-
-
/* Miscellaneous variables */
-
const
struct eap_method *m;
/* selected EAP method,当前选定的eap method */
-
/* not defined in RFC 4137 */
-
Boolean changed;
//状态机是否改变,在不变时则退出状态机运行,后续可能要发送eapreq,eapsuccess或eapfailure,或在pending时不做事情
-
void *eapol_ctx, *msg_ctx;
//eapol_ctx:上下文信息,在状态机初始化时指向session,之后不动;msg_ctx:尚未使用
-
struct eapol_callbacks *eapol_cb;
//状态机初始化时设置eapol_cb。struct eapol_callbacks为多个需要用到的回调函数如get_eap_user等组成的结构体
-
void *eap_method_priv;
//由各个eap method定义的数据,在EAP_INITIALIZE是sm->eap_method_priv = sm->m->initPickUp(sm) 指向eap_identity_data
-
//在具体EAP method阶段则是具体eap_xxx_data
-
u8 *identity;
//在eap_identity_process内赋值,取自eap-resp/identity
-
size_t identity_len;
-
/* Whether Phase 2 method should validate identity match */
-
int require_identity_match;
//EAP-GTC用到
-
int lastId;
/* Identifier used in the last EAP-Packet */
-
struct eap_user *user;
-
int user_eap_method_index;
-
int init_phase2;
//eap_ttls_phase2_eap_init eap_peap_phase2_init两个函数内设置为1
-
void *ssl_ctx;
//在状态机初始化eap_server_sm_init内设置为一个全局的g_ssl_context上下文。后者通过g_ssl_context = tls_init(NULL)实现初始化
-
struct eap_sim_db_data *eap_sim_db_priv;
//指向系统配置的eap_sim/aka的配置信息,为eap_sim_db_data结构。主要含有与hlr的通信套接字信息,假名表,
-
//重鉴权用户信息,pending的用户查询等
-
Boolean backend_auth;
//是否作为backend authentication server
-
Boolean update_user;
//sm->identity是否更新了的标志,如为true时可能需要重新获取用户信息
-
int eap_server;
//是作为eapserver还是passthrough
-
-
int num_rounds;
//eap交互次数,最大允许EAP_MAX_AUTH_ROUNDS=50次
-
enum {
-
METHOD_PENDING_NONE, METHOD_PENDING_WAIT, METHOD_PENDING_CONT
-
} method_pending;
-
/*状态机初始化时method_pending为METHOD_PENDING_NONE,因业务需要,可以将method_pending设置为METHOD_PENDING_WAIT。
-
eap状态机在处理EAP_PROPOSE_METHOD或EAP_METHOD_RESPONSE时,如果为WAIT则什么不做,退出状态机。
-
如果为CONT则设置method_pending = METHOD_PENDING_NONE并继续执行EAP_METHOD_RESPONSE状态。
-
eap具体method业务在收到响应等需要的时候调用eap_sm_pending_cb,他会设置method_pending为CONT,这样再激活状态机他会继续执行*/
-
-
u8 *auth_challenge;
-
u8 *peer_challenge;
//均是eap-mschapv2鉴权过程中的参数,分别由server和peer生成的随机数
-
-
u8 *pac_opaque_encr_key;
-
u8 *eap_fast_a_id;
-
size_t eap_fast_a_id_len;
-
char *eap_fast_a_id_info;
-
enum {
-
NO_PROV, ANON_PROV, AUTH_PROV, BOTH_PROV
-
} eap_fast_prov;
-
int pac_key_lifetime;
-
int pac_key_refresh_time;
-
int eap_sim_aka_result_ind;
-
int tnc;
//以上均取自配置文件,eap server用不着这些,可以到配置文件中查看配置的这些变量的值
-
u16 pwd_group;
-
struct wps_context *wps;
-
struct wpabuf *assoc_wps_ie;
-
struct wpabuf *assoc_p2p_ie;
-
-
Boolean start_reauth;
-
-
u8 peer_addr[ETH_ALEN];
-
-
/* Fragmentation size for EAP method init() handler */
-
int fragment_size;
-
-
int pbc_in_m1;
-
-
const u8 *server_id;
-
size_t server_id_len;
-
-
#ifdef CONFIG_TESTING_OPTIONS
-
u32 tls_test_flags;
-
#endif /* CONFIG_TESTING_OPTIONS */
-
};
-
-
-
struct eap_method {
//这个结构体用来存放每种加密方法的各种操作函数和变量
-
int vendor;
//存放eap vender ID
-
EapType method;
//EapType是一个枚举类型,里面的值定义了method type
-
const
char *name;
//存放method的名字,比如PSK
-
-
void * (*init)(
struct eap_sm *sm);
//初始化eap method
-
void * (*initPickUp)(
struct eap_sm *sm);
-
void (*reset)(
struct eap_sm *sm,
void *priv);
-
-
struct wpabuf * (*buildReq)(
struct eap_sm *sm,
void *priv, u8
id);
//处理一个eap request 请求包
-
int (*getTimeout)(
struct eap_sm *sm,
void *priv);
-
Boolean (*check)(
struct eap_sm *sm,
void *priv,
struct wpabuf *respData);
-
void (*process)(
struct eap_sm *sm,
void *priv,
struct wpabuf *respData);
-
Boolean (*isDone)(
struct eap_sm *sm,
void *priv);
-
u8 * (*getKey)(
struct eap_sm *sm,
void *priv, size_t *len);
//从eap method中获取秘钥内容
-
-
Boolean (*isSuccess)(
struct eap_sm *sm,
void *priv);
-
-
-
void (*free)(
struct eap_method *method);
//释放eap method 数据
-
-
#define EAP_SERVER_METHOD_INTERFACE_VERSION 1
-
-
int version;
//peer端EAP interface版本
-
-
-
struct eap_method *next;
// 用于建立链表,指向下一个节点
-
-
u8 * (*get_emsk)(
struct eap_sm *sm,
void *priv, size_t *len);
//获取扩展的秘钥内容
-
};
上面两个结构体封装了很多参数和方法,显得尤其重要,接下来,我们进入hostapd_global_init()函数。
-
<span style=
"color:#000000;">
static int hostapd_global_init(struct hapd_interfaces *interfaces,const char *entropy_file)
-
{
-
os_memset(&
global,
0,
sizeof(
global));
//重置global变量
-
hostapd_logger_register_cb(hostapd_logger_cb);
-
-
if (eap_server_register_methods()) {
//注册eap server的加密方法
-
wpa_printf(MSG_ERROR,
"Failed to register EAP methods");
-
return
-1;
-
}
-
-
if (eloop_init()) {
//
-
wpa_printf(MSG_ERROR,
"Failed to initialize event loop");
-
return
-1;
-
}
-
-
random_init(entropy_file);
-
-
#ifndef CONFIG_NATIVE_WINDOWS
-
eloop_register_signal(SIGHUP, handle_reload, interfaces);
-
eloop_register_signal(SIGUSR1, handle_dump_state, interfaces);
-
#endif /* CONFIG_NATIVE_WINDOWS */
-
eloop_register_signal_terminate(handle_term, interfaces);
-
-
for (i =
0; wpa_drivers[i]; i++)
-
global.drv_count++;
-
if (
global.drv_count ==
0) {
-
wpa_printf(MSG_ERROR,
"No drivers enabled");
-
return
-1;
-
}
-
global.drv_priv = os_calloc(
global.drv_count,
sizeof(
void *));
-
if (
global.drv_priv == NULL)
-
return
-1;
-
-
return
0;
-
}</span>
1. 使用eap_server_register_methods函数注册eap server支持的安全模式,并存放在一个链表里面,下图是支持的安全模式。
-
int eap_server_register_methods(void)
-
{
-
int ret =
0;
-
-
#ifdef EAP_SERVER_IDENTITY
-
if (ret ==
0)
-
ret = eap_server_identity_register();
-
#endif /* EAP_SERVER_IDENTITY */
-
-
#ifdef EAP_SERVER_MD5
-
if (ret ==
0)
-
ret = eap_server_md5_register();
-
#endif /* EAP_SERVER_MD5 */
-
-
#ifdef EAP_SERVER_TLS
-
if (ret ==
0)
-
ret = eap_server_tls_register();
-
.......
根据宏开关来确认哪些安全模式是支持的,并调用相应协议的注册函数,注册一个加密安全模式放在struct eap_method链表中,因为这些安全模式注册函数都差不多,所以只介绍其中一种模式eap_server_psk_register()。
-
int eap_server_psk_register(void)
-
{
-
struct eap_method *eap;
-
int ret;
-
-
eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
-
EAP_VENDOR_IETF, EAP_TYPE_PSK,
"PSK");
-
if (eap ==
NULL)
-
return
-1;
-
-
eap->init = eap_psk_init;
-
eap->reset = eap_psk_reset;
-
eap->buildReq = eap_psk_buildReq;
-
eap->check = eap_psk_check;
-
eap->process = eap_psk_process;
-
eap->isDone = eap_psk_isDone;
-
eap->getKey = eap_psk_getKey;
-
eap->isSuccess = eap_psk_isSuccess;
-
eap->get_emsk = eap_psk_get_emsk;
-
-
ret = eap_server_method_register(eap);
-
if (ret)
-
eap_server_method_free(eap);
-
return ret;
-
}
首先定义一个struct eap_method 对象,用eap_server_method_alloc给这个对象申请一块空间,然后给这个对象根据不同的安全模式指向不同的操作函数,最后将这个eap对象通过eap_server_method_register函数添加到struct eap_method 结构体对象的链表里面。
2. 使用eloop_init()对struct eloop_data eloop对象进行初始化,至于struct eloop_data的作用将在后面介绍。
-
int eloop_init(void)
-
{
-
os_memset(&eloop,
0,
sizeof(eloop));
-
dl_list_init(&eloop.timeout);
-
#ifdef CONFIG_ELOOP_EPOLL
-
eloop.epollfd = epoll_create1(
0);
-
if (eloop.epollfd <
0) {
-
wpa_printf(MSG_ERROR,
"%s: epoll_create1 failed. %s\n",
-
__func__, strerror(errno));
-
return
-1;
-
}
-
eloop.readers.type = EVENT_TYPE_READ;
-
eloop.writers.type = EVENT_TYPE_WRITE;
-
eloop.exceptions.type = EVENT_TYPE_EXCEPTION;
-
#endif /* CONFIG_ELOOP_EPOLL */
-
#ifdef WPA_TRACE
-
signal(SIGSEGV, eloop_sigsegv_handler);
-
#endif /* WPA_TRACE */
-
return
0;
这个函数主要是重置eloop对象和初始化链表,然后对eloop成员的一些赋值等
3. random_init()
-
void random_init(const char *entropy_file)
-
{
-
os_free(random_entropy_file);
-
if (entropy_file)
-
random_entropy_file = os_strdup(entropy_file);
-
else
-
random_entropy_file =
NULL;
-
random_read_entropy();
-
-
#ifdef __linux__
-
if (random_fd >=
0)
-
return;
-
-
random_fd = open(
"/dev/random", O_RDONLY | O_NONBLOCK);
-
if (random_fd <
0) {
-
#ifndef CONFIG_NO_STDOUT_DEBUG
-
int error = errno;
-
perror(
"open(/dev/random)");
-
wpa_printf(MSG_ERROR,
"random: Cannot open /dev/random: %s",
-
strerror(error));
-
#endif /* CONFIG_NO_STDOUT_DEBUG */
-
return;
-
}
-
wpa_printf(MSG_DEBUG,
"random: Trying to read entropy from "
-
"/dev/random");
-
-
eloop_register_read_sock(random_fd, random_read_fd,
NULL,
NULL);
-
#endif /* __linux__ */
-
-
random_write_entropy();
-
}
这里面eloop_register_read_sock很重要,具体的需要用源代码去深入跟踪。
4. 最后是中断的注册和global对像的赋值等操作。
这篇主要对初始化过程进行了介绍,功能的具体实现将在后面篇幅中讲述。