本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源: 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;
}
 
...... 待续 ......