本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn:
[email protected]
来源: http://yfydz.cublog.cn
13.6 发起方收到响应方第2回应包, 发送第3初始包
/* STATE_MAIN_I2:
* SMF_PSK_AUTH: HDR, KE, Nr --> HDR*, IDi1, HASH_I
* SMF_DS_AUTH: HDR, KE, Nr --> HDR*, IDi1, [ CERT, ] SIG_I
*
* The following are not yet implemented.
* SMF_PKE_AUTH: HDR, KE, <IDr1_b>PubKey_i, <Nr_b>PubKey_i
* --> HDR*, HASH_I
* SMF_RPKE_AUTH: HDR, <Nr_b>PubKey_i, <KE_b>Ke_r, <IDr1_b>Ke_r
* --> HDR*, HASH_I
*/
// md中包含了所收到数据包的所有信息
// 此处的数据内容全部都是加密的了
stf_status
main_inR2_outI3(struct msg_digest *md)
{
// 状态结构
struct state *const st = md->st;
// 密钥交换载荷链表
pb_stream *const keyex_pbs = &md->chain[ISAKMP_NEXT_KE]->pbs;
// 认证载荷类型, 是通过预共享密钥还是RSA签名
// 根据认证类型确定下一载荷是哈希载荷还是RSA签名载荷
int auth_payload = st->st_oakley.auth == OAKLEY_PRESHARED_KEY
? ISAKMP_NEXT_HASH : ISAKMP_NEXT_SIG;
pb_stream id_pbs; /* ID Payload; also used for hash calculation */
bool send_cert = FALSE;
bool send_cr = FALSE;
generalName_t *requested_ca = NULL;
// 本地证书
cert_t mycert = st->st_connection->spd.this.cert;
/* KE in */
// 读取密钥交换载荷
RETURN_STF_FAILURE(accept_KE(&st->st_gr, "Gr"
, st->st_oakley.group, keyex_pbs));
/* Nr in */
// 读取NONCE载荷
RETURN_STF_FAILURE(accept_nonce(md, &st->st_nr, "Nr"));
/* decode certificate requests */
// 解码证书请求
decode_cr(md, &requested_ca);
if(requested_ca != NULL)
{
// 解码成功
st->hidden_variables.st_got_certrequest = TRUE;
}
/*
* send certificate if we have one and auth is RSA, and we were
* told we can send one if asked, and we were asked, or we were told
* to always send one.
*/
// 判断是否需要发送本地证书给对方
// 条件是使用RSA签名认证
send_cert = st->st_oakley.auth == OAKLEY_RSA_SIG
// 本地证书非空
&& mycert.type != CERT_NONE
// 连接中配置需要发送证书
&& ((st->st_connection->spd.this.sendcert == cert_sendifasked
&& st->hidden_variables.st_got_certrequest)
|| st->st_connection->spd.this.sendcert==cert_alwayssend
|| st->st_connection->spd.this.sendcert==cert_forcedtype);
// 记录判断证书发送的各种信息供调试
doi_log_cert_thinking(md
, st->st_oakley.auth
, mycert.type
, st->st_connection->spd.this.sendcert
, st->hidden_variables.st_got_certrequest
, send_cert);
/* send certificate request, if we don't have a preloaded RSA public key */
// 判断是否发送证书请求, 即要求对方也发送自己的证书过来
send_cr = !no_cr_send && send_cert && !has_preloaded_public_key(st);
DBG(DBG_CONTROL
, DBG_log(" I am %ssending a certificate request"
, send_cr ? "" : "not "));
/*
* free collected certificate requests since as initiator
* we don't heed them anyway
*/
free_generalNames(requested_ca, TRUE);
/* done parsing; initialize crypto */
// 进行DH交换的加密初始化操作
(void)perform_dh_secretiv(st, INITIATOR, st->st_oakley.group->group);
#ifdef NAT_TRAVERSAL
// 如果发现是NAT穿越, 查找内部地址信息
if (st->hidden_variables.st_nat_traversal & NAT_T_WITH_NATD) {
nat_traversal_natd_lookup(md);
}
// 如果有NAT穿越,输出相关信息
if (st->hidden_variables.st_nat_traversal) {
nat_traversal_show_result(st->hidden_variables.st_nat_traversal
, md->sender_port);
}
// 检查是否是NAT穿越保活(keep_alive)信息
if (st->hidden_variables.st_nat_traversal & NAT_T_WITH_KA) {
nat_traversal_new_ka_event();
}
#endif
// 以下开始构造要发送的数据包
/*************** build output packet HDR*;IDii;HASH/SIG_I ***************/
/* ??? NOTE: this is almost the same as main_inI3_outR3's code */
/* HDR* out done */
/* IDii out */
{
struct isakmp_ipsec_id id_hd;
chunk_t id_b;
// 创建ID载荷
build_id_payload(&id_hd, &id_b, &st->st_connection->spd.this);
id_hd.isaiid_np = (send_cert)? ISAKMP_NEXT_CERT : auth_payload;
if (!out_struct(&id_hd
, &isakmp_ipsec_identification_desc
, &md->rbody
, &id_pbs)
|| !out_chunk(id_b, &id_pbs, "my identity"))
return STF_INTERNAL_ERROR;
close_output_pbs(&id_pbs);
}
/* CERT out */
// 如果需要, 创建证书载荷
if (send_cert)
{
pb_stream cert_pbs;
struct isakmp_cert cert_hd;
cert_hd.isacert_np = (send_cr)? ISAKMP_NEXT_CR : ISAKMP_NEXT_SIG;
cert_hd.isacert_type = mycert.type;
openswan_log("I am sending my cert");
if (!out_struct(&cert_hd
, &isakmp_ipsec_certificate_desc
, &md->rbody
, &cert_pbs))
return STF_INTERNAL_ERROR;
if(mycert.forced) {
if (!out_chunk(mycert.u.blob, &cert_pbs, "forced CERT"))
return STF_INTERNAL_ERROR;
} else {
if (!out_chunk(get_mycert(mycert), &cert_pbs, "CERT"))
return STF_INTERNAL_ERROR;
}
close_output_pbs(&cert_pbs);
}
/* CR out */
// 如果需要,创建证书请求载荷
if (send_cr)
{
openswan_log("I am sending a certificate request");
if (!build_and_ship_CR(mycert.type
, st->st_connection->spd.that.ca
, &md->rbody, ISAKMP_NEXT_SIG))
return STF_INTERNAL_ERROR;
}
/* HASH_I or SIG_I out */
{
// 发送哈希载荷(预共享密钥认证)或签名载荷(RSA签名认证)
u_char hash_val[MAX_DIGEST_LEN];
size_t hash_len = main_mode_hash(st, hash_val, TRUE, &id_pbs);
if (auth_payload == ISAKMP_NEXT_HASH)
{
// 构造哈希载荷
/* HASH_I out */
if (!out_generic_raw(ISAKMP_NEXT_NONE
, &isakmp_hash_desc
, &md->rbody
, hash_val, hash_len, "HASH_I"))
return STF_INTERNAL_ERROR;
}
else
{
// 构造签名载荷
/* SIG_I out */
u_char sig_val[RSA_MAX_OCTETS];
size_t sig_len = RSA_sign_hash(st->st_connection
, sig_val, hash_val, hash_len);
if (sig_len == 0)
{
loglog(RC_LOG_SERIOUS, "unable to locate my private key for RSA Signature");
return STF_FAIL + AUTHENTICATION_FAILED;
}
if (!out_generic_raw(ISAKMP_NEXT_NONE
, &isakmp_signature_desc
, &md->rbody
, sig_val
, sig_len
, "SIG_I"))
return STF_INTERNAL_ERROR;
}
}
/* encrypt message, except for fixed part of header */
/* st_new_iv was computed by generate_skeyids_iv */
// 加密数据
if (!encrypt_message(&md->rbody, st))
return STF_INTERNAL_ERROR; /* ??? we may be partly committed */
return STF_OK;
}
13.7 响应方收到发送方第3初始化包, 发送第3响应包
如果使用OE方式, 响应方没有发起方的认证信息, 将进行DNS查找RSA记录信息来认证对方, 获取了对方认证信息后再重新进行该阶段处理, 所以有continue和tail函数处理. 但一般情况下不需要
/* STATE_MAIN_R2:
* PSK_AUTH: HDR*, IDi1, HASH_I --> HDR*, IDr1, HASH_R
* DS_AUTH: HDR*, IDi1, [ CERT, ] SIG_I --> HDR*, IDr1, [ CERT, ] SIG_R
* PKE_AUTH, RPKE_AUTH: HDR*, HASH_I --> HDR*, HASH_R
*
* Broken into parts to allow asynchronous DNS lookup.
*
* - main_inI3_outR3 to start
* - main_inI3_outR3_tail to finish or suspend for DNS lookup
* - main_inI3_outR3_continue to start main_inI3_outR3_tail again
*/
static key_tail_fn main_inI3_outR3_tail; /* forward */
// 实际只是main_inI3_outR3_tail的包裹函数
stf_status
main_inI3_outR3(struct msg_digest *md)
{
return main_inI3_outR3_tail(md, NULL);
}
// 继续函数,
static void
main_inI3_outR3_continue(struct adns_continuation *cr, err_t ugh)
{
key_continue(cr, ugh, main_inI3_outR3_tail);
}
static stf_status
main_inI3_outR3_tail(struct msg_digest *md
, struct key_continuation *kc)
{
struct state *const st = md->st;
u_int8_t auth_payload;
pb_stream r_id_pbs; /* ID Payload; also used for hash calculation */
cert_t mycert;
bool send_cert;
/* ID and HASH_I or SIG_I in
* Note: this may switch the connection being used!
*/
{
// 读取数据包中的ID和哈希或签名信息, 认证对方, FALSE表示非发起方, 是响应方
// 当需要启动OE机制进行DNS查找对方公钥信息时会使用继续函数是main_inI3_outR3_continue,
// 而该继续函数中调用结束函数main_inI3_outR3_tail, 形成递归
// 一般情况下不会调用继续函数的, 直接往下走了
stf_status r = main_id_and_auth(md, FALSE
, main_inI3_outR3_continue
, kc);
// 认证失败的话返回
if (r != STF_OK)
return r;
}
// 以下处理和main_inR2_outI3类似
/* send certificate if we have one and auth is RSA */
// 本地证书
mycert = st->st_connection->spd.this.cert;
// 检查是否需要发送本地证书
// 条件是使用RSA签名认证
send_cert = st->st_oakley.auth == OAKLEY_RSA_SIG
// 本地证书非空
&& mycert.type != CERT_NONE
// 连接配置中要求证书
&& ((st->st_connection->spd.this.sendcert == cert_sendifasked
&& st->hidden_variables.st_got_certrequest)
|| st->st_connection->spd.this.sendcert==cert_alwayssend);
// 记录判断证书发送的各种信息供调试
doi_log_cert_thinking(md
, st->st_oakley.auth
, mycert.type
, st->st_connection->spd.this.sendcert
, st->hidden_variables.st_got_certrequest
, send_cert);
// 以下开始构造发送的数据包
/*************** build output packet HDR*;IDir;HASH/SIG_R ***************/
/* proccess_packet() would automatically generate the HDR*
* payload if smc->first_out_payload is not ISAKMP_NEXT_NONE.
* We don't do this because we wish there to be no partially
* built output packet if we need to suspend for asynch DNS.
*/
/* ??? NOTE: this is almost the same as main_inR2_outI3's code */
/* HDR* out
* If auth were PKE_AUTH or RPKE_AUTH, ISAKMP_NEXT_HASH would
* be first payload.
*/
// 回转构造数据头
echo_hdr(md, TRUE, ISAKMP_NEXT_ID);
// 根据认证类型确定下一载荷是哈希载荷还是RSA签名载荷
auth_payload = st->st_oakley.auth == OAKLEY_PRESHARED_KEY
? ISAKMP_NEXT_HASH : ISAKMP_NEXT_SIG;
/* IDir out */
{
// 构造ID载荷
/* id_hd should be struct isakmp_id, but struct isakmp_ipsec_id
* allows build_id_payload() to work for both phases.
*/
struct isakmp_ipsec_id id_hd;
chunk_t id_b;
build_id_payload(&id_hd, &id_b, &st->st_connection->spd.this);
id_hd.isaiid_np = (send_cert)? ISAKMP_NEXT_CERT : auth_payload;
if (!out_struct(&id_hd, &isakmp_ipsec_identification_desc, &md->rbody, &r_id_pbs)
|| !out_chunk(id_b, &r_id_pbs, "my identity"))
return STF_INTERNAL_ERROR;
close_output_pbs(&r_id_pbs);
}
/* CERT out, if we have one */
// 如果需要构造证书载荷
if (send_cert)
{
pb_stream cert_pbs;
struct isakmp_cert cert_hd;
cert_hd.isacert_np = ISAKMP_NEXT_SIG;
cert_hd.isacert_type = mycert.type;
openswan_log("I am sending my cert");
if (!out_struct(&cert_hd, &isakmp_ipsec_certificate_desc, &md->rbody, &cert_pbs))
return STF_INTERNAL_ERROR;
if (!out_chunk(get_mycert(mycert), &cert_pbs, "CERT"))
return STF_INTERNAL_ERROR;
close_output_pbs(&cert_pbs);
}
/* HASH_R or SIG_R out */
// 构造哈希载荷或签名证书
{
u_char hash_val[MAX_DIGEST_LEN];
size_t hash_len = main_mode_hash(st, hash_val, FALSE, &r_id_pbs);
if (auth_payload == ISAKMP_NEXT_HASH)
{
// 预共享密钥, 构造哈希载荷
/* HASH_R out */
if (!out_generic_raw(ISAKMP_NEXT_NONE, &isakmp_hash_desc, &md->rbody
, hash_val, hash_len, "HASH_R"))
return STF_INTERNAL_ERROR;
}
else
{
/* SIG_R out */
u_char sig_val[RSA_MAX_OCTETS];
size_t sig_len = RSA_sign_hash(st->st_connection
, sig_val, hash_val, hash_len);
if (sig_len == 0)
{
loglog(RC_LOG_SERIOUS, "unable to locate my private key for RSA Signature");
return STF_FAIL + AUTHENTICATION_FAILED;
}
// RSA签名认证方式,否则签名载荷
if (!out_generic_raw(ISAKMP_NEXT_NONE, &isakmp_signature_desc
, &md->rbody, sig_val, sig_len, "SIG_R"))
return STF_INTERNAL_ERROR;
}
}
/* encrypt message, sans fixed part of header */
// 对数据进行加密
if (!encrypt_message(&md->rbody, st))
return STF_INTERNAL_ERROR; /* ??? we may be partly committed */
/* Last block of Phase 1 (R3), kept for Phase 2 IV generation */
// 输出数据信息, 阶段1的最后一个包
DBG_cond_dump(DBG_CRYPT, "last encrypted block of Phase 1:"
, st->st_new_iv, st->st_new_iv_len);
// 生成新的初始化向量
st->st_ph1_iv_len = st->st_new_iv_len;
set_ph1_iv(st, st->st_new_iv);
// ISAKMP SA建立, 主模式协商完成
ISAKMP_SA_established(st->st_connection, st->st_serialno);
/* ??? If st->st_connectionc->gw_info != NULL,
* we should keep the public key -- it tested out.
*/
// 返回成功
return STF_OK;
}
// 实际调用oakley_id_and_auth()函数来识别对方ID和认证信息
static inline stf_status
main_id_and_auth(struct msg_digest *md
, bool initiator /* are we the Initiator? */
, cont_fn_t cont_fn /* continuation function */
, struct key_continuation *kc) /* argument */
{
// FALSE表示非野蛮模式,是主模式
return oakley_id_and_auth(md, initiator, FALSE, cont_fn, kc);
}
/* Processs the Main Mode ID Payload and the Authenticator
* (Hash or Signature Payload).
* If a DNS query is still needed to get the other host's public key,
* the query is initiated and STF_SUSPEND is returned.
* Note: parameter kc is a continuation containing the results from
* the previous DNS query, or NULL indicating no query has been issued.
*/
static stf_status
oakley_id_and_auth(struct msg_digest *md
, bool initiator /* are we the Initiator? */
, bool aggrmode /* aggressive mode? */
, cont_fn_t cont_fn /* continuation function */
, const struct key_continuation *kc /* current state, can be NULL */
)
{
struct state *st = md->st;
u_char hash_val[MAX_DIGEST_LEN];
size_t hash_len;
// 返回值初始化为成功
stf_status r = STF_OK;
/* ID Payload in.
* Note: this may switch the connection being used!
*/
// 解码对方ID, 失败返回
if (!decode_peer_id(md, initiator, aggrmode))
return STF_FAIL + INVALID_ID_INFORMATION;
/* Hash the ID Payload.
* main_mode_hash requires idpl->cur to be at end of payload
* so we temporarily set if so.
*/
{
pb_stream *idpl = &md->chain[ISAKMP_NEXT_ID]->pbs;
u_int8_t *old_cur = idpl->cur;
idpl->cur = idpl->roof;
// 计算ID载荷的哈希值, 返回哈希值长度
hash_len = main_mode_hash(st, hash_val, !initiator, idpl);
idpl->cur = old_cur;
}
// 认证处理
switch (st->st_oakley.auth)
{
// 域共享密钥认证处理
case OAKLEY_PRESHARED_KEY:
{
// 数据包中的哈希载荷链表
pb_stream *const hash_pbs = &md->chain[ISAKMP_NEXT_HASH]->pbs;
// 比较哈希值和哈希长度是否匹配, 不匹配的话将返回错误
if (pbs_left(hash_pbs) != hash_len
|| memcmp(hash_pbs->cur, hash_val, hash_len) != 0)
{
DBG_cond_dump(DBG_CRYPT, "received HASH:"
, hash_pbs->cur, pbs_left(hash_pbs));
loglog(RC_LOG_SERIOUS, "received Hash Payload does not match computed value");
/* XXX Could send notification back */
r = STF_FAIL + INVALID_HASH_INFORMATION;
}
}
break;
case OAKLEY_RSA_SIG:
// RSA签名认证检查,从当前使用的所有RSA签名中查找匹配的项
r = RSA_check_signature(st, hash_val, hash_len
, &md->chain[ISAKMP_NEXT_SIG]->pbs
#ifdef USE_KEYRR
, kc == NULL? NULL : kc->ac.keys_from_dns
#endif /* USE_KEYRR */
, kc == NULL? NULL : kc->ac.gateways_from_dns
);
if (r == STF_SUSPEND)
{
// 如果认证结果是挂起, 表示需要通过OE来获取对方的RSA公钥信息, 将进入DNS查询处理
// 处理过程忽略, 注意继续函数cont_fn是在此情况下才用的
/* initiate/resume asynchronous DNS lookup for key */
struct key_continuation *nkc
= alloc_thing(struct key_continuation, "key continuation");
enum key_oppo_step step_done = kc == NULL? kos_null : kc->step;
err_t ugh;
/* Record that state is used by a suspended md */
passert(st->st_suspended_md == NULL);
st->st_suspended_md = md;
nkc->failure_ok = FALSE;
nkc->md = md;
switch (step_done)
{
case kos_null:
/* first try: look for the TXT records */
nkc->step = kos_his_txt;
#ifdef USE_KEYRR
nkc->failure_ok = TRUE;
#endif
ugh = start_adns_query(&st->st_connection->spd.that.id
, &st->st_connection->spd.that.id /* SG itself */
, T_TXT
, cont_fn
, &nkc->ac);
break;
#ifdef USE_KEYRR
case kos_his_txt:
/* second try: look for the KEY records */
nkc->step = kos_his_key;
ugh = start_adns_query(&st->st_connection->spd.that.id
, NULL /* no sgw for KEY */
, T_KEY
, cont_fn
, &nkc->ac);
break;
#endif /* USE_KEYRR */
default:
bad_case(step_done);
}
if (ugh != NULL)
{
report_key_dns_failure(&st->st_connection->spd.that.id, ugh);
st->st_suspended_md = NULL;
r = STF_FAIL + INVALID_KEY_INFORMATION;
} else {
/*
* since this state is waiting for a DNS query, delete
* any events that might kill it.
*/
delete_event(st);
}
}
break;
default:
bad_case(st->st_oakley.auth);
}
// 返回结果
if (r == STF_OK)
DBG(DBG_CRYPT, DBG_log("authentication succeeded"));
return r;
}
13.8 发起方收到响应方的第3响应包
和main_inI3_outR3类似, 如果使用OE方式, 发起方没有响应方的认证信息, 将进行DNS查找RSA记录信息来认证对方, 获取了对方认证信息后再重新进行该阶段处理, 所以有continue和tail函数处理. 但一般情况下不需要
/* STATE_MAIN_I3:
* Handle HDR*;IDir;HASH/SIG_R from responder.
*
* Broken into parts to allow asynchronous DNS for KEY records.
*
* - main_inR3 to start
* - main_inR3_tail to finish or suspend for DNS lookup
* - main_inR3_continue to start main_inR3_tail again
*/
static key_tail_fn main_inR3_tail; /* forward */
// 只是main_inR3_tail()的包裹函数
stf_status
main_inR3(struct msg_digest *md)
{
return main_inR3_tail(md, NULL);
}
// main_inR3的继续函数
static void
main_inR3_continue(struct adns_continuation *cr, err_t ugh)
{
key_continue(cr, ugh, main_inR3_tail);
}
static stf_status
main_inR3_tail(struct msg_digest *md
, struct key_continuation *kc)
{
struct state *const st = md->st;
/* ID and HASH_R or SIG_R in
* Note: this may switch the connection being used!
*/
{
// 主模式的ID和认证处理, 可能会进入OE过程, 所以要用继续函数重新处理数据包,
// 一般情况下不需要进OE处理
stf_status r = main_id_and_auth(md, TRUE, main_inR3_continue, kc);
if (r != STF_OK)
return r;
}
/**************** done input ****************/
/* save last IV from phase 1 so it can be restored later so anything
* between the end of phase 1 and the start of phase 2 ie mode config
* payloads etc will not loose our IV
*/
// 获取初始化向量
memcpy(st->st_ph1_iv, st->st_new_iv, st->st_new_iv_len);
st->st_ph1_iv_len = st->st_new_iv_len;
// 建立ISAKMP SA
ISAKMP_SA_established(st->st_connection, st->st_serialno);
passert((st->st_policy & POLICY_PFS)==0 || st->st_pfs_group != NULL );
/* ??? If c->gw_info != NULL,
* we should keep the public key -- it tested out.
*/
// 设置初始化向量
st->st_ph1_iv_len = st->st_new_iv_len;
set_ph1_iv(st, st->st_new_iv);
/* save last IV from phase 1 so it can be restored later so anything
* between the end of phase 1 and the start of phase 2 ie mode config
* payloads etc will not loose our IV
*/
memcpy(st->st_ph1_iv, st->st_new_iv, st->st_new_iv_len);
st->st_ph1_iv_len = st->st_new_iv_len;
// 完成阶段1的主模式协商
update_iv(st); /* finalize our Phase 1 IV */
return STF_OK;
}
...... 待续 ......