ikev1_quick.c
来自「ipsec vpn」· C语言 代码 · 共 2,176 行 · 第 1/4 页
C
2,176 行
, const char *which){ ip_subnet net_temp; if (!decode_net_id(id, id_pbs, &net_temp, which)) return FALSE; if (!samesubnet(net, &net_temp) || *protoid != id->isaiid_protoid || *port != id->isaiid_port) { loglog(RC_LOG_SERIOUS, "%s ID returned doesn't match my proposal", which); return FALSE; } return TRUE;}/* Compute HASH(1), HASH(2) of Quick Mode. * HASH(1) is part of Quick I1 message. * HASH(2) is part of Quick R1 message. * Used by: quick_outI1, quick_inI1_outR1 (twice), quick_inR1_outI2 * (see RFC 2409 "IKE" 5.5, pg. 18 or draft-ietf-ipsec-ike-01.txt 6.2 pg 25) */static size_tquick_mode_hash12(u_char *dest, const u_char *start, const u_char *roof, const struct state *st, const msgid_t *msgid, bool hash2){ struct hmac_ctx ctx;#if 0 /* if desperate to debug hashing */# define hmac_update(ctx, ptr, len) { \ DBG_dump("hash input", (ptr), (len)); \ (hmac_update)((ctx), (ptr), (len)); \ } DBG_dump("hash key", st->st_skeyid_a.ptr, st->st_skeyid_a.len);#endif hmac_init_chunk(&ctx, st->st_oakley.hasher, st->st_skeyid_a); hmac_update(&ctx, (const void *) msgid, sizeof(msgid_t)); if (hash2) hmac_update_chunk(&ctx, st->st_ni); /* include Ni_b in the hash */ hmac_update(&ctx, start, roof-start); hmac_final(dest, &ctx); DBG(DBG_CRYPT, DBG_log("HASH(%d) computed:", hash2 + 1); DBG_dump("", dest, ctx.hmac_digest_len)); return ctx.hmac_digest_len;# undef hmac_update}/* Compute HASH(3) in Quick Mode (part of Quick I2 message). * Used by: quick_inR1_outI2, quick_inI2 * See RFC2409 "The Internet Key Exchange (IKE)" 5.5. * NOTE: this hash (unlike HASH(1) and HASH(2)) ONLY covers the * Message ID and Nonces. This is a mistake. */static size_tquick_mode_hash3(u_char *dest, struct state *st){ struct hmac_ctx ctx; hmac_init_chunk(&ctx, st->st_oakley.hasher, st->st_skeyid_a); hmac_update(&ctx, "\0", 1); hmac_update(&ctx, (u_char *) &st->st_msgid, sizeof(st->st_msgid)); hmac_update_chunk(&ctx, st->st_ni); hmac_update_chunk(&ctx, st->st_nr); hmac_final(dest, &ctx); DBG_cond_dump(DBG_CRYPT, "HASH(3) computed:", dest, ctx.hmac_digest_len); return ctx.hmac_digest_len;}/* Compute Phase 2 IV. * Uses Phase 1 IV from st_iv; puts result in st_new_iv. */voidinit_phase2_iv(struct state *st, const msgid_t *msgid){ const struct hash_desc *h = st->st_oakley.hasher; union hash_ctx ctx; DBG_cond_dump(DBG_CRYPT, "last Phase 1 IV:" , st->st_ph1_iv, st->st_ph1_iv_len); st->st_new_iv_len = h->hash_digest_len; passert(st->st_new_iv_len <= sizeof(st->st_new_iv)); DBG_cond_dump(DBG_CRYPT, "current Phase 1 IV:" , st->st_iv, st->st_iv_len); h->hash_init(&ctx); h->hash_update(&ctx, st->st_ph1_iv, st->st_ph1_iv_len); passert(*msgid != 0); h->hash_update(&ctx, (const u_char *)msgid, sizeof(*msgid)); h->hash_final(st->st_new_iv, &ctx); DBG_cond_dump(DBG_CRYPT, "computed Phase 2 IV:" , st->st_new_iv, st->st_new_iv_len);}static stf_statusquick_outI1_tail(struct pluto_crypto_req_cont *pcrc , struct pluto_crypto_req *r);static voidquick_outI1_continue(struct pluto_crypto_req_cont *pcrc , struct pluto_crypto_req *r , err_t ugh){ struct qke_continuation *qke = (struct qke_continuation *)pcrc; struct state *const st = qke->st; stf_status e; DBG(DBG_CONTROLMORE , DBG_log("quick outI1: calculated ke+nonce, sending I1")); st->st_calculating = FALSE; /* XXX should check out ugh */ passert(ugh == NULL); passert(cur_state == NULL); passert(st != NULL); set_cur_state(st); /* we must reset before exit */ e = quick_outI1_tail(pcrc, r); reset_globals();}stf_statusquick_outI1(int whack_sock , struct state *isakmp_sa , struct connection *c , lset_t policy , unsigned long try , so_serial_t replacing){ struct state *st = duplicate_state(isakmp_sa); struct qke_continuation *qke = alloc_thing(struct qke_continuation , "quick_outI1 KE"); stf_status e; st->st_whack_sock = whack_sock; st->st_connection = c; passert(c != NULL); if(st->st_calculating) { return STF_IGNORE; } set_cur_state(st); /* we must reset before exit */ st->st_policy = policy; st->st_try = try; st->st_myuserprotoid = c->spd.this.protocol; st->st_peeruserprotoid = c->spd.that.protocol; st->st_myuserport = c->spd.this.port; st->st_peeruserport = c->spd.that.port; st->st_msgid = generate_msgid(isakmp_sa); st->st_state = STATE_QUICK_I1; insert_state(st); /* needs cookies, connection, and msgid */ if (replacing == SOS_NOBODY) openswan_log("initiating Quick Mode %s {using isakmp#%lu}" , prettypolicy(policy) , isakmp_sa->st_serialno); else openswan_log("initiating Quick Mode %s to replace #%lu {using isakmp#%lu}" , prettypolicy(policy) , replacing , isakmp_sa->st_serialno); /* * See if pfs_group has been specified for this conn, * if not, fallback to old use-same-as-P1 behaviour */#ifdef IKE_ALG if (st->st_connection) st->st_pfs_group = ike_alg_pfsgroup(st->st_connection , st->st_policy); if (!st->st_pfs_group)#endif /* If PFS specified, use the same group as during Phase 1: * since no negotiation is possible, we pick one that is * very likely supported. */ st->st_pfs_group = policy & POLICY_PFS? isakmp_sa->st_oakley.group : NULL; qke->st = st; qke->isakmp_sa = isakmp_sa; qke->replacing = replacing; qke->qke_pcrc.pcrc_func = quick_outI1_continue; if(policy & POLICY_PFS) { e=build_ke(&qke->qke_pcrc, st, st->st_pfs_group, st->st_import); } else { e=build_nonce(&qke->qke_pcrc, st, st->st_import); } reset_globals(); return e;} static stf_statusquick_outI1_tail(struct pluto_crypto_req_cont *pcrc , struct pluto_crypto_req *r){ struct qke_continuation *qke = (struct qke_continuation *)pcrc; struct state *st = qke->st; struct connection *c = st->st_connection; struct state *isakmp_sa = qke->isakmp_sa; pb_stream reply; /* not really a reply */ pb_stream rbody; u_char /* set by START_HASH_PAYLOAD: */ *r_hashval, /* where in reply to jam hash value */ *r_hash_start; /* start of what is to be hashed */ bool has_client = c->spd.this.has_client || c->spd.that.has_client || c->spd.this.protocol || c->spd.that.protocol || c->spd.this.port || c->spd.that.port; st->st_connection = c;#ifdef NAT_TRAVERSAL if (isakmp_sa->hidden_variables.st_nat_traversal & NAT_T_DETECTED) { /* Duplicate nat_traversal status in new state */ st->hidden_variables.st_nat_traversal = isakmp_sa->hidden_variables.st_nat_traversal; if (isakmp_sa->hidden_variables.st_nat_traversal & LELEM(NAT_TRAVERSAL_NAT_BHND_ME)) { has_client = TRUE; } nat_traversal_change_port_lookup(NULL, st); } else { st->hidden_variables.st_nat_traversal = 0; }#endif /* set up reply */ init_pbs(&reply, reply_buffer, sizeof(reply_buffer), "reply packet"); /* HDR* out */ { struct isakmp_hdr hdr; hdr.isa_version = ISAKMP_MAJOR_VERSION << ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION; hdr.isa_np = ISAKMP_NEXT_HASH; hdr.isa_xchg = ISAKMP_XCHG_QUICK; hdr.isa_msgid = st->st_msgid; hdr.isa_flags = ISAKMP_FLAG_ENCRYPTION; memcpy(hdr.isa_icookie, st->st_icookie, COOKIE_SIZE); memcpy(hdr.isa_rcookie, st->st_rcookie, COOKIE_SIZE); if (!out_struct(&hdr, &isakmp_hdr_desc, &reply, &rbody)) { reset_cur_state(); return STF_INTERNAL_ERROR; } } /* HASH(1) -- create and note space to be filled later */ START_HASH_PAYLOAD(rbody, ISAKMP_NEXT_SA); /* SA out */ /* Emit SA payload based on a subset of the policy bits. * POLICY_COMPRESS is considered iff we can do IPcomp. */ { lset_t pm = POLICY_ENCRYPT | POLICY_AUTHENTICATE; if (can_do_IPcomp) pm |= POLICY_COMPRESS; if (!out_sa(&rbody , &ipsec_sadb[(st->st_policy & pm) >> POLICY_IPSEC_SHIFT] , st, FALSE, FALSE, ISAKMP_NEXT_NONCE)) { reset_cur_state(); return STF_INTERNAL_ERROR; } } { int np; if(st->st_policy & POLICY_PFS) { np = ISAKMP_NEXT_KE; } else { if(has_client) { np = ISAKMP_NEXT_ID; } else { np = ISAKMP_NEXT_NONE; } } /* Ni out */ if (!ship_nonce(&st->st_ni, r, &rbody , np , "Ni")) { reset_cur_state(); return STF_INTERNAL_ERROR; } } /* [ KE ] out (for PFS) */ if (st->st_pfs_group != NULL) { if (!ship_KE(st, r, &st->st_gi , &rbody , has_client? ISAKMP_NEXT_ID : ISAKMP_NEXT_NONE)) { reset_cur_state(); return STF_INTERNAL_ERROR; } } /* [ IDci, IDcr ] out */ if (has_client) { /* IDci (we are initiator), then IDcr (peer is responder) */ if (!emit_subnet_id(&c->spd.this.client , ISAKMP_NEXT_ID , st->st_myuserprotoid , st->st_myuserport, &rbody) || !emit_subnet_id(&c->spd.that.client , ISAKMP_NEXT_NONE , st->st_peeruserprotoid , st->st_peeruserport, &rbody)) { reset_cur_state(); return STF_INTERNAL_ERROR; } }#ifdef NAT_TRAVERSAL if ((st->hidden_variables.st_nat_traversal & NAT_T_WITH_NATOA) && (!(st->st_policy & POLICY_TUNNEL)) && (st->hidden_variables.st_nat_traversal & LELEM(NAT_TRAVERSAL_NAT_BHND_ME))) { /** Send NAT-OA if our address is NATed */ if (!nat_traversal_add_natoa(ISAKMP_NEXT_NONE, &rbody, st)) { reset_cur_state(); return STF_INTERNAL_ERROR; } }#endif /* finish computing HASH(1), inserting it in output */ (void) quick_mode_hash12(r_hashval, r_hash_start, rbody.cur , st, &st->st_msgid, FALSE); /* encrypt message, except for fixed part of header */ init_phase2_iv(isakmp_sa, &st->st_msgid); st->st_new_iv_len = isakmp_sa->st_new_iv_len; set_new_iv(st, isakmp_sa->st_new_iv); if (!encrypt_message(&rbody, st)) { reset_cur_state(); return STF_INTERNAL_ERROR; } /* save packet, now that we know its size */ clonetochunk(st->st_tpacket, reply.start, pbs_offset(&reply) , "reply packet from quick_outI1"); /* send the packet */ send_packet(st, "quick_outI1", TRUE); delete_event(st); event_schedule(EVENT_RETRANSMIT, EVENT_RETRANSMIT_DELAY_0, st); if (qke->replacing == SOS_NOBODY) whack_log(RC_NEW_STATE + STATE_QUICK_I1 , "%s: initiate" , enum_name(&state_names, st->st_state)); else whack_log(RC_NEW_STATE + STATE_QUICK_I1 , "%s: initiate to replace #%lu" , enum_name(&state_names, st->st_state) , qke->replacing); return STF_OK;}/* Handle first message of Phase 2 -- Quick Mode. * HDR*, HASH(1), SA, Ni [, KE ] [, IDci, IDcr ] --> * HDR*, HASH(2), SA, Nr [, KE ] [, IDci, IDcr ] * (see RFC 2409 "IKE" 5.5) * Installs inbound IPsec SAs. * Although this seems early, we know enough to do so, and * this way we know that it is soon enough to catch all * packets that other side could send using this IPsec SA. * * Broken into parts to allow asynchronous DNS for TXT records: * * - quick_inI1_outR1 starts the ball rolling. * It checks and parses enough to learn the Phase 2 IDs * * - quick_inI1_outR1_authtail does the rest of the job * unless DNS must be consulted. In that case, * it starts a DNS query, salts away what is needed * to continue, and suspends. Calls * + quick_inI1_outR1_start_query * + quick_inI1_outR1_process_answer * * - quick_inI1_outR1_continue will restart quick_inI1_outR1_authtail * when DNS comes back with an answer. * * A big chunk of quick_inI1_outR1_authtail is executed twice. * This is necessary because the set of connections * might change while we are awaiting DNS. * When first called, gateways_from_dns == NULL. If DNS is * consulted asynchronously, gateways_from_dns != NULL the second time. * Remember that our state object might disappear too! * * * If the connection is opportunistic, we must verify delegation. * * 1. Check that we are authorized to be SG for * our client. We look for the TXT record that * delegates us. We also check that the public * key (if present) matches the private key we used. * Eventually, we should probably require DNSsec * authentication for our side. * * 2. If our client TXT record did not include a * public key, check the KEY record indicated * by the identity in the TXT record. * * 3. If the peer's client is the peer itself, we * consider it authenticated. Otherwise, we check * the TXT record for the client to see that * the identity of the SG matches the peer and * that some public key (if present in the TXT) * matches. We need not check the public key if * it isn't in the TXT record. * * Since p isn't yet instantiated, we need to look * in c for description of peer. * * We cannot afford to block waiting for a DNS query. * The code here is structured as two halves: * - process the result of just completed * DNS query (if any) * - if another query is needed, initiate the next * DNS query and suspend */enum verify_oppo_step { vos_fail, vos_start, vos_our_client, vos_our_txt,#ifdef USE_KEYRR vos_our_key,#endif /* USE_KEYRR */ vos_his_client, vos_done};static const char *const verify_step_name[] = { "vos_fail", "vos_start", "vos_our_client", "vos_our_txt",#ifdef USE_KEYRR "vos_our_key",#endif /* USE_KEYRR */ "vos_his_client", "vos_done"};/* hold anything we can handle of a Phase 2 ID */struct p2id { ip_subnet net; u_int8_t proto; u_int16_t port;};struct verify_oppo_bundle { enum verify_oppo_step step; bool failure_ok; /* if true, quick_inI1_outR1_continue will try * other things on DNS failure */ struct msg_digest *md; struct p2id my, his; unsigned int new_iv_len; /* p1st's might change */ u_char new_iv[MAX_DIGEST_LEN]; /* int whackfd; */ /* not needed because we are Responder */};struct verify_oppo_continuation { struct adns_continuation ac; /* common prefix */ struct verify_oppo_bundle b;};static stf_status quick_inI1_outR1_authtail(struct verify_oppo_bundle *b , struct adns_continuation *ac);stf_statusquick_inI1_outR1(struct msg_digest *md){ const struct state *const p1st = md->st; struct connection *c = p1st->st_connection; struct payload_digest *const id_pd = md->chain[ISAKMP_NEXT_ID]; struct verify_oppo_bundle b; /* HASH(1) in */ CHECK_QUICK_HASH(md , quick_mode_hash12(hash_val, hash_pbs->roof, md->message_pbs.roof , p1st, &md->hdr.isa_msgid, FALSE) , "HASH(1)", "Quick I1"); /* [ IDci, IDcr ] in * We do this now (probably out of physical order) because * we wish to select the correct connection before we consult * it for policy. */ if (id_pd != NULL) { /* ??? we are assuming IPSEC_DOI */ /* IDci (initiator is peer) */ if (!decode_net_id(&id_pd->payload.ipsec_id, &id_pd->pbs , &b.his.net, "peer client")) return STF_FAIL + INVALID_ID_INFORMATION; /* Hack for MS 818043 NAT-T Update */ if (id_pd->payload.ipsec_id.isaiid_idtype == ID_FQDN) memset(&b.his.net, 0, sizeof(ip_subnet)); if (id_pd->payload.ipsec_id.isaiid_idtype == ID_FQDN) happy(addrtosubnet(&c->spd.that.host_addr, &b.his.net)); /* End Hack for MS 818043 NAT-T Update */ b.his.proto = id_pd->payload.ipsec_id.isaiid_protoid; b.his.port = id_pd->payload.ipsec_id.isaiid_port; b.his.net.addr.u.v4.sin_port = htons(b.his.port);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?