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 + -
显示快捷键?