主模式第五包:main_inR2_outI3
文章目錄
1. 序言
main_inR2_outI3()
函數是ISAKMP協商過程中第五包的核心處理函數的入口,發起端通過此包收到相應端的KE值和Nonce值收到,可以生成需要的密鑰信息。這裏我們主要說明main_inR2_outI3
的函數調用關係、處理流程以及對源碼的註釋分析,關於main_inR2_outI3
的上下文環境暫不敘述,留給後面的文章進行更新。
ISAKMP協商報文的處理流程都比較複雜,此函數在協商的報文處理函數中比較複雜的,因此個人學習期間難免有遺漏和理解錯誤的地方,請大家多多批評指正。
對於源碼的學習,我並沒有把每一行進行備註,而是將自己認爲的關鍵點做了註釋或者標註。
目前主要是整理源碼中的處理裏流程和實現邏輯,尚未深入比較細節的處理;後續在我整理完畢使用主模式協商的9個報文後,我再次結合代碼整理每一個報文的詳細流程,到時把每一個報文的注意事項、作用,處理方式做一個整體上的把握。同時結合書本上的描述來解釋代碼層的實現。
2.函數調用關係
這裏暫時給出xmind整理的思維導圖。
3. 第五個報文流程圖
第五個報文的處理流程主要分爲三個大功能:
- 解析收到的對端報文
- 密鑰交換載荷KE
- Nonce載荷
- 使用DH算法制作加密密鑰、認證密鑰、哈希密鑰等
- 構造應答報文(第五個報文)
- 簽名
- 報文加密
4. main_inR2_outI3()源碼學習
該函數主要的功能是:
- 解析對端的KE載荷和Nonce,因爲這是製作密鑰的基礎材料。
- 解析完畢後,開始製作密鑰。
stf_status
main_inR2_outI3(struct msg_digest *md)
{
struct dh_continuation *dh;
/*獲取受到的第四個報文中的KE載荷*/
pb_stream *const keyex_pbs = &md->chain[ISAKMP_NEXT_KE]->pbs;
struct state *const st = md->st;
/* 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_gr上*/
RETURN_STF_FAILURE(accept_KE(&st->st_gr, "Gr"
, st->st_oakley.group, keyex_pbs));
/* Nr in *//*解析Nonce載荷,並存儲在st->st_nr*/
RETURN_STF_FAILURE(accept_v1_nonce(md, &st->st_nr, "Nr"));
dh = alloc_thing(struct dh_continuation, "aggr outR1 DH");
if(!dh) { return STF_FATAL; }
dh->md = md;
set_suspended(st, md);/*掛起當前報文,防止由於耗時操作重新收到對方的重發報文*/
pcrc_init(&dh->dh_pcrc);
dh->dh_pcrc.pcrc_func = main_inR2_outI3_cryptotail;
return start_dh_secretiv(&dh->dh_pcrc, st
, st->st_import
, INITIATOR
, st->st_oakley.group->group);
}
5. start_dh_secretiv()源碼學習
該函數的主要功能:
- 獲取協商的認證算法、哈希算法、oakley羣信息
- 準備Nonce、KE、cookie、st_sec_chunk(密鑰信息?)
- 製作密鑰(該流程中會通過回調函數組裝報文併發送)
- 發送完畢報文的後續處理
/*
* invoke helper to do DH work.
*/
stf_status start_dh_secretiv(struct pluto_crypto_req_cont *cn
, struct state *st
, enum crypto_importance importance
, enum phase1_role init /* TRUE=g_init,FALSE=g_r */
, u_int16_t oakley_group2)
{
struct pluto_crypto_req r;
struct pcr_skeyid_q *dhq;
const chunk_t *pss = get_preshared_secret(st->st_connection);/*獲取預共享祕鑰*/
err_t e;
bool toomuch = FALSE;
pcr_init(&r, pcr_compute_dh_iv, importance);/*使用DH算法生成三把祕鑰*/
dhq = &r.pcr_d.dhq;
passert(st->st_sec_in_use);
/* convert appropriate data to dhq */
dhq->auth = st->st_oakley.auth;
dhq->prf_hash = st->st_oakley.prf_hash;
dhq->oakley_group = oakley_group2;
dhq->init = init;
dhq->keysize = st->st_oakley.enckeylen/BITS_PER_BYTE;
passert(r.pcr_d.dhq.oakley_group != 0);
DBG(DBG_CONTROL | DBG_CRYPT,
DBG_log("parent1 type: %d group: %d len: %d\n", r.pcr_type,
r.pcr_d.dhq.oakley_group, (int)r.pcr_len));
/*將*pss的內容拷貝到dhq->space中,起始位置是thespace;同時將dhq->pss指向spce對應的空間。然後更新thespace*/
if(pss) {
pluto_crypto_copychunk(&dhq->thespace, dhq->space, &dhq->pss, *pss);
}
pluto_crypto_copychunk(&dhq->thespace, dhq->space, &dhq->ni, st->st_ni);
pluto_crypto_copychunk(&dhq->thespace, dhq->space, &dhq->nr, st->st_nr);
pluto_crypto_copychunk(&dhq->thespace, dhq->space, &dhq->gi, st->st_gi);
pluto_crypto_copychunk(&dhq->thespace, dhq->space, &dhq->gr, st->st_gr);
pluto_crypto_copychunk(&dhq->thespace, dhq->space
, &dhq->secret, st->st_sec_chunk);
/*最後發起端和相應者Nonce、KE等信息全部拷貝到了space中*/
#ifdef HAVE_LIBNSS
/*copying required encryption algo*/
/*dhq->encrypt_algo = st->st_oakley.encrypt;*/
dhq->encrypter = st->st_oakley.encrypter;
DBG(DBG_CRYPT, DBG_log("Copying DH pub key pointer to be sent to a thread helper"));
pluto_crypto_copychunk(&dhq->thespace, dhq->space , &dhq->pubk, st->pubk);
#endif
/*將發起者、相應者cookie也拷貝到dhq->space中*/
pluto_crypto_allocchunk(&dhq->thespace, &dhq->icookie, COOKIE_SIZE);
memcpy(wire_chunk_ptr(dhq, &dhq->icookie), st->st_icookie, COOKIE_SIZE);
pluto_crypto_allocchunk(&dhq->thespace, &dhq->rcookie, COOKIE_SIZE);
memcpy(wire_chunk_ptr(dhq, &dhq->rcookie)
, st->st_rcookie, COOKIE_SIZE);
/*至此,發起端和相應者Nonce、KE、cookie等信息全部拷貝到了space中*/
passert(dhq->oakley_group != 0);
e = send_crypto_helper_request(&r, cn, &toomuch);/*r 中包含上述信息,用來生成三把祕鑰*/
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_state_transition already got called.
*/
return STF_INLINE;
}
}
6. main_inR2_outI3_cryptotail()源碼學習
static void
main_inR2_outI3_cryptotail(struct pluto_crypto_req_cont *pcrc
, struct pluto_crypto_req *r
, err_t ugh)
{
struct dh_continuation *dh = (struct dh_continuation *)pcrc;
struct msg_digest *md = dh->md;
struct state *const st = md->st;
stf_status e;
DBG(DBG_CONTROLMORE
, DBG_log("main inR2_outI3: calculated DH, sending R1"));
if (st == NULL) {
loglog(RC_LOG_SERIOUS, "%s: Request was disconnected from state",
__FUNCTION__);
if (dh->md)
release_md(dh->md);
return;
}
passert(cur_state == NULL);
passert(st != NULL);
passert(st->st_suspended_md == dh->md);
set_suspended(st, NULL); /* no longer connected or suspended */
set_cur_state(st);
st->st_calculating = FALSE;
if(ugh) {
loglog(RC_LOG_SERIOUS, "failed in DH exponentiation: %s", ugh);
e = STF_FATAL;
} else {
e = main_inR2_outI3_continue(md, r);
}
if(dh->md != NULL) {
complete_v1_state_transition(&dh->md, e);
if(dh->md) release_md(dh->md);
}
reset_cur_state();
}
7. main_inR2_outI3_cryptotail()源碼學習
該函數的主要功能是:
- 將製作的密鑰存儲在狀態上
- 解析對方報文中的證書,確定是否需要發送證書載荷
- 構造ID標識載荷、並計算哈希
- 如果使用哈希認證則填充哈希載荷;如果使用簽名認證則進行數字簽名
- 報文加密
/* 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
*/
static stf_status
main_inR2_outI3_continue(struct msg_digest *md
, struct pluto_crypto_req *r)
{
struct state *const st = md->st;
/*選擇認證的載荷類型:哈希\簽名*/
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;/*獲取自己的證書*/
finish_dh_secretiv(st, r);/*將DH產生的三把祕鑰存儲在結構體上*/
if(!r->pcr_success) {
return STF_FAIL + INVALID_KEY_INFORMATION;
}
/* decode certificate requests */
decode_cr(md, &requested_ca);/*解析報文中的證書載荷,並使用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.
* 同時滿足下面的條件再發送證書載荷:
* 1. 使用RSA簽名
* 2. 本端導入了數字證書
* 3. 配置要求發送證書或強制發送證書
* 4. 從對端收到了證書
*/
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 */
#ifdef NAT_TRAVERSAL
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);
}
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 */
/*HDR在哪裏完成填充的呢???*/
/* IDii out */
{
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
, &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;
}
#ifdef TPM
{
pb_stream *pbs = &md->rbody;
size_t enc_len = pbs_offset(pbs) - sizeof(struct isakmp_hdr);
TCLCALLOUT_crypt("preHash", st,pbs,sizeof(struct isakmp_hdr),enc_len);
/* find location of ID PBS */
tpm_findID(pbs, &id_pbs);
}
#endif
/* HASH_I or SIG_I out */
{
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;
}