主模式第四包:main_inI2_outR2

1. 序言

main_inI2_outR2()函数是ISAKMP协商过程中第四包的核心处理函数的入口,同时在此处理流程中已经获取到足够的隧道信息,可以生成需要的密钥信息。这里我们主要说明main_inI2_outR2的函数调用关系、处理流程以及对源码的注释分析,关于main_inI2_outR2的上下文环境暂不叙述,留给后面的文章进行更新。

ISAKMP协商报文的处理流程都比较复杂,此函数在协商的报文处理函数中比较复杂的,因此个人学习期间难免有遗漏和理解错误的地方,请大家多多批评指正。

对于源码的学习,我并没有把每一行进行备注,而是将自己认为的关键点做了注释或者标注。

2.函数调用关系

暂略。(此流程调用比较多,后面会在补充上)

3. 第四个报文流程图

第四个报文处理流程大致可以划分为四类功能:

  • 解析收到的第三个报文内容
  • 生成随机数(本段的KE和Nonce值)
  • 构造应答报文
  • 使用DH算法制作秘钥(三把钥匙)

整理的处理流程如下:

个人觉得这个流程做的还是很清晰和准确的(原谅我没有上色)

4. main_inI2_outR2源码分析

stf_status
main_inI2_outR2(struct msg_digest *md)
{
struct state *const st = md->st;
pb_stream *keyex_pbs = &md->chain[ISAKMP_NEXT_KE]->pbs; /* if we are already processing a packet on this st, we will be unable
* to start another crypto operation below */
if (is_suspended(st)) {/*为了方式该流程处理时间过长导致对端超时重发*/
openswan_log("%s: already processing a suspended cyrpto operation "
"on this SA, duplicate will be dropped.", __func__);
return STF_TOOMUCHCRYPTO;
} /* KE in *//*从报文中获取KE载荷,并填充到st->st_gi上*/
RETURN_STF_FAILURE(accept_KE(&st->st_gi, "Gi"
, st->st_oakley.group, keyex_pbs)); /* Ni in *//*从报文中获取Nonce载荷,并填充到st->st_ni上*/
RETURN_STF_FAILURE(accept_v1_nonce(md, &st->st_ni, "Ni")); /* decode certificate requests *//*解析证书载荷,以链表的方式存储在st->st_connection->requested_ca*/
ikev1_decode_cr(md, &st->st_connection->ikev1_requested_ca_names); if(st->st_connection->requested_ca != NULL)
{
st->hidden_variables.st_got_certrequest = TRUE;
} #ifdef NAT_TRAVERSAL
DBG(DBG_NATT
, DBG_log("inI2: checking NAT-T: %d and %d"
, nat_traversal_enabled
, st->hidden_variables.st_nat_traversal)); if (st->hidden_variables.st_nat_traversal & NAT_T_WITH_NATD) {
DBG(DBG_NATT, DBG_log(" NAT_T_WITH_NATD detected"));
nat_traversal_natd_lookup(md);/*根据哈希值确定是否经过NAT;状态上的NAT-T标志在此处做的修改*/
}
if (st->hidden_variables.st_nat_traversal) {/*打印NAT-T、端口浮动相关信息*/
nat_traversal_show_result(st->hidden_variables.st_nat_traversal
, md->sender_port);
}
if (st->hidden_variables.st_nat_traversal & NAT_T_WITH_KA) {
DBG(DBG_NATT, DBG_log(" NAT_T_WITH_KA detected"));
nat_traversal_new_ka_event();/*添加NAT-T的保活事件*/
}
#endif {
struct ke_continuation *ke = alloc_thing(struct ke_continuation
, "inI2_outR2 KE"); ke->md = md;
set_suspended(st, md); passert(st->st_sec_in_use == FALSE);
pcrc_init(&ke->ke_pcrc);
ke->ke_pcrc.pcrc_func = main_inI2_outR2_continue;
return build_ke(&ke->ke_pcrc, st
, st->st_oakley.group, st->st_import);
}
}

5. nat_traversal_natd_lookup源码分析

/*检查是否需要经过NAT-T*/
void nat_traversal_natd_lookup(struct msg_digest *md)
{
unsigned char hash_me[MAX_DIGEST_LEN];
unsigned char hash_him[MAX_DIGEST_LEN];
struct payload_digest *p;
struct state *st = md->st;
bool found_me = FALSE;
bool found_him= FALSE;
int i; passert(st);
passert(md->iface);
passert(st->st_oakley.prf_hasher); /** Count NAT-D **/
for (p = md->chain[ISAKMP_NEXT_NATD_RFC], i=0;
p != NULL;
p = p->next, i++);/*统计NAT-D的数量*/ /**
* We need at least 2 NAT-D (1 for us, many for peer)
*/
if (i < 2) {
loglog(RC_LOG_SERIOUS,
"NAT-Traversal: Only %d NAT-D - Aborting NAT-Traversal negotiation", i);
st->hidden_variables.st_nat_traversal = 0;
return;
} /**
* First one with my IP & port
*/
_natd_hash(st->st_oakley.prf_hasher, hash_me
, st->st_icookie, st->st_rcookie
, &(md->iface->ip_addr)
, ntohs(md->iface->port)); /**
* The others with sender IP & port
*/
_natd_hash(st->st_oakley.prf_hasher, hash_him
, st->st_icookie, st->st_rcookie
, &(md->sender), ntohs(md->sender_port)); for (p = md->chain[ISAKMP_NEXT_NATD_RFC], i=0;
p != NULL && (!found_me || !found_him);
p = p->next)
{
DBG(DBG_NATT,
DBG_log("NAT_TRAVERSAL hash=%d (me:%d) (him:%d)"
, i, found_me, found_him);
DBG_dump("expected NAT-D(me):", hash_me,
st->st_oakley.prf_hasher->hash_digest_len);
DBG_dump("expected NAT-D(him):", hash_him,
st->st_oakley.prf_hasher->hash_digest_len);
DBG_dump("received NAT-D:", p->pbs.cur, pbs_left(&p->pbs));
); if ( (pbs_left(&p->pbs) == st->st_oakley.prf_hasher->hash_digest_len)
&& (memcmp(p->pbs.cur, hash_me
, st->st_oakley.prf_hasher->hash_digest_len)==0))
{
found_me = TRUE;/*本端未经过NAT*/
} if ( (pbs_left(&p->pbs) == st->st_oakley.prf_hasher->hash_digest_len)
&& (memcmp(p->pbs.cur, hash_him
, st->st_oakley.prf_hasher->hash_digest_len)==0))
{
found_him = TRUE;/*对端未经过NAT*/
} i++;
} DBG(DBG_NATT,
DBG_log("NAT_TRAVERSAL hash=%d (me:%d) (him:%d)"
, i, found_me, found_him)); if(!found_me) {
st->hidden_variables.st_nat_traversal |= LELEM(NAT_TRAVERSAL_NAT_BHND_ME);/*本端经过NAT*/
st->hidden_variables.st_natd = md->sender;
} memset(&st->hidden_variables.st_natd,0,sizeof(st->hidden_variables.st_natd));
anyaddr(AF_INET, &st->hidden_variables.st_natd); if(!found_him) {
st->hidden_variables.st_nat_traversal |= LELEM(NAT_TRAVERSAL_NAT_BHND_PEER);/*对端经过NAT*/
st->hidden_variables.st_natd = md->sender;
} if(st->st_connection->forceencaps) {/*如果需要强制使用UDP封装双方都需要NAT-D*/
DBG(DBG_NATT,
DBG_log("NAT_TRAVERSAL forceencaps enabled")); st->hidden_variables.st_nat_traversal |= LELEM(NAT_TRAVERSAL_NAT_BHND_PEER);
st->hidden_variables.st_nat_traversal |= LELEM(NAT_TRAVERSAL_NAT_BHND_ME);
st->hidden_variables.st_natd = md->sender;
}
}

6. build_ke源码分析

stf_status build_ke(struct pluto_crypto_req_cont *cn
, struct state *st
, const struct oakley_group_desc *group
, enum crypto_importance importance)
{
struct pluto_crypto_req rd;
struct pluto_crypto_req *r = &rd;
err_t e;
bool toomuch = FALSE;
/*初始化加密请求*/
pcr_init(r, pcr_build_kenonce, importance);
r->pcr_d.kn.oakley_group = group->group; cn->pcrc_serialno = st->st_serialno;
/*申请制作加密材料请求*/
e= send_crypto_helper_request(r, cn, &toomuch); if(e != NULL) {/*加密失败*/
loglog(RC_LOG_SERIOUS, "can not start crypto helper: %s", e);
if(toomuch) {
return STF_TOOMUCHCRYPTO;
} else {
return STF_FAIL;
}
} else if(!toomuch) {/*加密任务繁忙,先挂起等待再次调度*/
st->st_calculating = TRUE;
delete_event(st);
event_schedule(EVENT_CRYPTO_FAILED, EVENT_CRYPTO_FAILED_DELAY, st);
return STF_SUSPEND;
} else {
/* we must have run the continuation directly, so
* complete_v1_state_transition already got called.
* 由于我们已经手动执行了main_inR1_outI2_continue(),该函数最终会调用到complete_v1_state_transition
* 因此在(process_v1_state_xxx流程中不必再此执行状态转换函数。因此返回STF_INLINE,当再次到complete_v1_state_transition
*判断返回值为它,则不再执行此函数。)
*/
return STF_INLINE;
}
}

7. main_inI2_outR2_continue源码分析

static void
main_inI2_outR2_continue(struct pluto_crypto_req_cont *pcrc
, struct pluto_crypto_req *r
, err_t ugh)
{
struct ke_continuation *ke = (struct ke_continuation *)pcrc;
struct msg_digest *md = ke->md;
struct state *const st = md->st;
stf_status e;
... ...
set_suspended(st, NULL); /* no longer connected or suspended */ set_cur_state(st); st->st_calculating = FALSE;
e = main_inI2_outR2_tail(pcrc, r);/*构造应答报文*/ if(ke->md != NULL) {
complete_v1_state_transition(&ke->md, e);/*发送报文并完后后续处理工作*/
if(ke->md) release_md(ke->md);
}
reset_cur_state();
}

8. main_inI2_outR2_tail源码分析

/*
* this routine gets called after any DH exponentiation that needs to be done
* has been done, and we are ready to send our g^y.
*/
stf_status
main_inI2_outR2_tail(struct pluto_crypto_req_cont *pcrc
, struct pluto_crypto_req *r)
{
struct ke_continuation *ke = (struct ke_continuation *)pcrc;
struct msg_digest *md = ke->md;
struct state *st = md->st; /* send CR if auth is RSA and no preloaded RSA public key exists*/
bool send_cr = FALSE; /**************** build output packet HDR;KE;Nr ****************/ /*如果以下四个条件同时满足,则通过需要发送证书。。*/
send_cr = !no_cr_send /*配置中允许发送证书*/
&& (st->st_oakley.auth == OAKLEY_RSA_SIG) /*使用RSA签名*/
&& !has_preloaded_public_key(st) /*未加载未共享秘钥*/
&& st->st_connection->spd.that.ca.ptr != NULL; /*对端证书非空*/ /* HDR out */
echo_hdr(md, FALSE, ISAKMP_NEXT_KE); /* KE out *//*添加KE载荷,并将其存储在st->st_gr*/
if (!ship_KE(st, r, &st->st_gr
, &md->rbody, ISAKMP_NEXT_NONCE))
{
osw_abort();
return STF_INTERNAL_ERROR;
} #ifdef DEBUG
{
/* Nr out */
int next_payload;
next_payload = ISAKMP_NEXT_NONE; if(cur_debugging & IMPAIR_BUST_MR2)
{
next_payload = ISAKMP_NEXT_VID;
}
if(send_cr)
{
next_payload = ISAKMP_NEXT_CR;
}
if (!ship_nonce(&st->st_nr, r
, &md->rbody
, next_payload
, "Nr"))
return STF_INTERNAL_ERROR; if (cur_debugging & IMPAIR_BUST_MR2)
{
/* generate a pointless large VID payload to push message over MTU */
pb_stream vid_pbs; if (!out_generic((send_cr)? ISAKMP_NEXT_CR : ISAKMP_NEXT_NONE,
&isakmp_vendor_id_desc, &md->rbody, &vid_pbs))
return STF_INTERNAL_ERROR;
if (!out_zero(1500 /*MTU?*/, &vid_pbs, "Filler VID"))
return STF_INTERNAL_ERROR;
close_output_pbs(&vid_pbs);
}
}
#else
/* Nr out *//*添加NONCE载荷,并将其存储在st->st_nr*/
if (!ship_nonce(&st->st_nr, r
, &md->rbody
, (send_cr)? ISAKMP_NEXT_CR : ISAKMP_NEXT_NONE
, "Nr"))
return STF_INTERNAL_ERROR;
#endif /* CR out *//*如果需要发送证书*/
if (send_cr)
{
if (st->st_connection->kind == CK_PERMANENT)/*双方连接固定,即两端的IP的确定的*/
{
if (!build_and_ship_CR(CERT_X509_SIGNATURE /*添加对端证书载荷*/
, st->st_connection->spd.that.ca
, &md->rbody, ISAKMP_NEXT_NONE))
return STF_INTERNAL_ERROR;
}
else
{
generalName_t *ca = NULL;
/*查询可用的证书 ???*/
if (collect_rw_ca_candidates(md, &ca))/*收集所有可用证书,并全部加载到ca链表上*/
{
generalName_t *gn; for (gn = ca; gn != NULL; gn = gn->next)
{
if (!build_and_ship_CR(CERT_X509_SIGNATURE, gn->name/*将所有的可用证书加载到链表上*/
, &md->rbody
, gn->next == NULL ? ISAKMP_NEXT_NONE : ISAKMP_NEXT_CR))
return STF_INTERNAL_ERROR;
}
free_generalNames(ca, FALSE);/*释放可用证书链表*/
}
else
{/*确实没有找到可用的证书,则填充一个空的证书载荷*/
if (!build_and_ship_CR(CERT_X509_SIGNATURE, empty_chunk
, &md->rbody, ISAKMP_NEXT_NONE))
return STF_INTERNAL_ERROR;
}
}
} #ifdef NAT_TRAVERSAL
if (st->hidden_variables.st_nat_traversal & NAT_T_WITH_NATD) {
if (!nat_traversal_add_natd(ISAKMP_NEXT_NONE, &md->rbody, md))/*添加NAT-D载荷*/
return STF_INTERNAL_ERROR;
}
#endif /* finish message */
close_message(&md->rbody);
/********************************************* 使 用 DH 算 法 开 始 制 作 密 钥 ***********************************************/
/*
* next message will be encrypted, so, we need to have
* the DH value calculated. We can do this in the background,
* sending the reply right away. We have to be careful on the next
* state, since the other end may reply faster than we can calculate
* things. If it is the case, then the packet is placed in the
* continuation, and we let the continuation process it. If there
* is a retransmit, we keep only the last packet.
*
* Also, note that this is not a suspended state, since we are
* actually just doing work in the background.
*
*/
{
/* Looks like we missed perform_dh() declared at
* programs/pluto/pluto_crypt.h as external and implemented nowhere.
* Following code regarding dh_continuation allocation seems useless
* as it's never used. At least, we should free it.
*/
struct dh_continuation *dh = alloc_thing(struct dh_continuation
, "main_inI2_outR2_tail");
stf_status e; dh->md = NULL;
dh->serialno = st->st_serialno;
pcrc_init(&dh->dh_pcrc);
dh->dh_pcrc.pcrc_func = main_inI2_outR2_calcdone;
passert(st->st_suspended_md == NULL); DBG(DBG_CONTROLMORE
, DBG_log("main inI2_outR2: starting async DH calculation (group=%d)", st->st_oakley.group->group)); e = start_dh_secretiv(&dh->dh_pcrc, st
, st->st_import
, RESPONDER
, st->st_oakley.group->group); DBG(DBG_CONTROLMORE,
DBG_log("started dh_secretiv, returned: stf=%s\n"
, stf_status_name(e))); if(e == STF_FAIL) {
loglog(RC_LOG_SERIOUS, "failed to start async DH calculation, stf=%s\n"
, stf_status_name(e));
return e;
} /* we are calculating in the background, so it doesn't count */
if(e == STF_SUSPEND) {
st->st_calculating = FALSE;
}
}
return STF_OK;
}

9. main_inI2_outR2_calcdone源码分析

static void
main_inI2_outR2_calcdone(struct pluto_crypto_req_cont *pcrc
, struct pluto_crypto_req *r
, err_t ugh)
{
struct dh_continuation *dh = (struct dh_continuation *)pcrc;
struct state *st; DBG(DBG_CONTROLMORE
, DBG_log("main inI2_outR2: calculated DH finished")); st = state_with_serialno(dh->serialno);
if(st == NULL) {
openswan_log("state %ld disappeared during crypto\n", dh->serialno);
return;
} set_cur_state(st);
if(ugh) {
loglog(RC_LOG_SERIOUS, "DH crypto failed: %s\n", ugh);
return;
}
/*将生成的三把秘钥、DH-IV等信息存储在状态上*/
finish_dh_secretiv(st, r);
if(!r->pcr_success) {
loglog(RC_LOG_SERIOUS, "DH crypto failed, invalid keys");
return;
} ikev2_validate_key_lengths(st); st->hidden_variables.st_skeyid_calculated = TRUE;
update_iv(st);/*更新IV值*/
/* XXX: Do we need to free dh here? If so, how about the other exits?
* pfree(dh); dh = NULL;
*/ /*
* if there was a packet received while we were calculating, then
* process it now.
*/
/*如果在计算秘钥的过程中收到新的报文则现在再处理该报文*/
if(st->st_suspended_md != NULL) {
struct msg_digest *md = st->st_suspended_md; set_suspended(st, NULL);
process_packet_tail(&md);
if(md != NULL) {
release_md(md);
}
}
reset_cur_state();
return;
}

openswan协商流程之(四):main_inI2_outR2()的更多相关文章

  1. openswan协商流程之(七):main_inR3

    主模式第六包(收包):main_inR3 1. 序言 main_inR3()函数是ISAKMP协商过程中第一阶段的最后一个报文的接收处理函数,它的作用同main_inI3_outR3()部分功能相同: ...

  2. openswan协商流程之(一):main_outI1()

    主模式第一包:main_outI1() 1. 序言 main_outI1()作为主模式主动发起连接请求的核心处理函数,我们可以通过学习该函数的处理流程来探究openswan中报文封装的基本思想.如果之 ...

  3. openswan协商流程之(五):main_inR2_outI3()

    主模式第五包:main_inR2_outI3 文章目录 主模式第五包:main_inR2_outI3 1. 序言 2.函数调用关系 3. 第五个报文流程图 4. main_inR2_outI3()源码 ...

  4. openswan协商流程之(三):main_inR1_outI2

    主模式第三包:main_inR1_outI2 1. 序言 main_inR1_outI2()函数是ISAKMP协商过程中第三包的核心处理函数的入口.这里我们主要说明main_inR1_outI2的函数 ...

  5. openswan协商流程之(六):main_inI3_outR3()

    主模式第六包:main_inI3_outR3 1. 序言 main_inI3_outR3()函数是ISAKMP协商过程中第六包的核心处理函数的入口,第五六包主要用来验证对方的身份信息,同时此报文也是加 ...

  6. openswan协商流程之(二):main_inI1_outR1()

    主模式第二包:main_inI1_outR1() 文章目录 主模式第二包:main_inI1_outR1() 1. 序言 2. `main_inI1_outR1()`处理流程图 3. `main_in ...

  7. IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包

    IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包 文章目录 IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包 1. IKEv2 协商总体框架 2. 第二包流程图 3. ...

  8. 一个简单的Eclipse调试Debug流程(四)

    本文链接:https://blog.csdn.net/u011781521/article/details/55000066    http://blog.csdn.net/u010075335/ar ...

  9. 以太网驱动的流程浅析(四)-以太网驱动probe流程【原创】

    以太网驱动的流程浅析(四)-以太网驱动probe流程 Author:张昺华 Email:920052390@qq.com Time:2019年3月23日星期六 此文也在我的个人公众号以及<Lin ...

随机推荐

  1. 基于SpringBoot的药店管理系统java药房管理系统(源码+数据库文件+文档)

    注意:该项目只展示部分功能,如需了解,评论区咨询即可. 1.开发环境 开发语言:Java 后台框架:SpringBoot 前端技术:HTML+CSS+JavaScript+Bootstrap+jQue ...

  2. 【笔记】多项式回归的思想以及在sklearn中使用多项式回归和pipeline

    多项式回归以及在sklearn中使用多项式回归和pipeline 多项式回归 线性回归法有一个很大的局限性,就是假设数据背后是存在线性关系的,但是实际上,具有线性关系的数据集是相对来说比较少的,更多时 ...

  3. 由”二进制里不能有3“引发的对parseInt的思考

    看到一道面试题,["1", "2", "3"].map(parseInt) 答案是多少? 心生好奇,做做看,发现卡住,没什么头绪.首先对pa ...

  4. Beescms V4.0_R_20160525代码审计笔记

    写在前面 什么是报错注入?正常用户访问服务器发送id信息返回正确的id数据.报错注入是想办法构造语句,让错误信息中可以显示数据库的内容:如果能让错误信息中返回数据库中的内容,即实现SQL注入. 复现过 ...

  5. DVWA-全等级XSS(反射型、存储型)

    DVWA简介 DVWA(Damn Vulnerable Web Application)是一个用来进行安全脆弱性鉴定的PHP/MySQL Web应用,旨在为安全专业人员测试自己的专业技能和工具提供合法 ...

  6. 题解 string

    传送门 考试的时候只来得及糊了个\(n^4\)的暴力,结果考完发现\(n^2\)比\(n^4\)还好写 题意就是就是要求把一堆字符串的前后缀拼起来之后在原串中出现了多少次 然而前后缀可以有很多,再枚举 ...

  7. noip模拟6(T2更新

    由于蒟弱目前还没调出T1和T2,所以先写T3和T4.(T1T2更完辣! update in 6.12 07:19 T3 大佬 题目描述: 他发现katarina大佬真是太强了,于是就学习了一下kata ...

  8. Ratel:一直站在Android逆向巅峰的平头哥

    本文来源:带动行业内卷,渣总义不容辞 字越少事儿越大,请关注github(可以点击阅读原文): https://github.com/virjarRatel 平头哥(ratel)是一个Android逆 ...

  9. wpf Button 动态改变效果

    <Button  x:Name="LearnMore"  Grid.Row="6"  HorizontalAlignment="Left&quo ...

  10. ASP.NET Core Web API接收文件传输

    ASP.NET解析API参数的方式有很多种,包括[FromBody],[FromForm],[FromServices],[FromHeader][FromQuery]. 文件传输方式也分很多种,包括 ...