ikev1_quick.c

来自「ipsec vpn」· C语言 代码 · 共 2,176 行 · 第 1/4 页

C
2,176
字号
	/* IDcr (we are responder) */	if (!decode_net_id(&id_pd->next->payload.ipsec_id, &id_pd->next->pbs	, &b.my.net, "our client"))	    return STF_FAIL + INVALID_ID_INFORMATION;	b.my.proto = id_pd->next->payload.ipsec_id.isaiid_protoid;	b.my.port = id_pd->next->payload.ipsec_id.isaiid_port;	b.my.net.addr.u.v4.sin_port = htons(b.my.port);    }    else    {	/* implicit IDci and IDcr: peer and self */	if (!sameaddrtype(&c->spd.this.host_addr, &c->spd.that.host_addr))	    return STF_FAIL;	happy(addrtosubnet(&c->spd.this.host_addr, &b.my.net));	happy(addrtosubnet(&c->spd.that.host_addr, &b.his.net));	b.his.proto = b.my.proto = 0;	b.his.port = b.my.port = 0;    }    b.step = vos_start;    b.md = md;    b.new_iv_len = p1st->st_new_iv_len;    save_new_iv(p1st, b.new_iv);    return quick_inI1_outR1_authtail(&b, NULL);}static voidreport_verify_failure(struct verify_oppo_bundle *b, err_t ugh){    struct state *st = b->md->st;    char fgwb[ADDRTOT_BUF]	, cb[ADDRTOT_BUF];    ip_address client;    err_t which;    switch (b->step)    {    case vos_our_client:    case vos_our_txt:#ifdef USE_KEYRR    case vos_our_key:#endif /* USE_KEYRR */	which = "our";	networkof(&b->my.net, &client);	break;    case vos_his_client:	which = "his";	networkof(&b->his.net, &client);	break;    case vos_start:    case vos_done:    case vos_fail:    default:	bad_case(b->step);    }    addrtot(&st->st_connection->spd.that.host_addr, 0, fgwb, sizeof(fgwb));    addrtot(&client, 0, cb, sizeof(cb));    loglog(RC_OPPOFAILURE	, "gateway %s wants connection with %s as %s client, but DNS fails to confirm delegation: %s {msgid:%08x}"	   , fgwb, cb, which, ugh, st->st_msgid);}static voidquick_inI1_outR1_continue(struct adns_continuation *cr, err_t ugh){    stf_status r;    struct verify_oppo_continuation *vc = (void *)cr;    struct verify_oppo_bundle *b = &vc->b;    struct state *st = b->md->st;    DBG(DBG_CONTROLMORE	, DBG_log("quick inI1_outR1: adns continuation: %s", ugh));    passert(cur_state == NULL);    /* if st == NULL, our state has been deleted -- just clean up */    if (st != NULL)    {	passert(st->st_suspended_md == b->md);	st->st_suspended_md = NULL;	/* no longer connected or suspended */	cur_state = st;	if (!b->failure_ok && ugh != NULL)	{	    report_verify_failure(b, ugh);	    r = STF_FAIL + INVALID_ID_INFORMATION;	}	else	{	    r = quick_inI1_outR1_authtail(b, cr);	}	complete_state_transition(&b->md, r);    }    if (b->md != NULL)	release_md(b->md);    cur_state = NULL;}static stf_statusquick_inI1_outR1_start_query(struct verify_oppo_bundle *b, enum verify_oppo_step next_step){    struct msg_digest *md = b->md;    struct state *p1st = md->st;    struct connection *c = p1st->st_connection;    struct verify_oppo_continuation *vc	= alloc_thing(struct verify_oppo_continuation, "verify continuation");    struct id id	/* subject of query */	, *our_id	/* needed for myid playing */	, our_id_space;	/* ephemeral: no need for unshare_id_content */    ip_address client;    err_t ugh;    /* Record that state is used by a suspended md */    b->step = next_step;    /* not just vc->b.step */    vc->b = *b;    passert(p1st->st_suspended_md == NULL);    p1st->st_suspended_md = b->md;    DBG(DBG_CONTROL,	{	    char ours[SUBNETTOT_BUF];	    char his[SUBNETTOT_BUF];	    subnettot(&c->spd.this.client, 0, ours, sizeof(ours));	    subnettot(&c->spd.that.client, 0, his, sizeof(his));	    DBG_log("responding with DNS query - from %s to %s new state: %s"		    , ours, his, verify_step_name[b->step]);	});    /* Resolve %myid in a cheesy way.     * We have to do the resolution because start_adns_query     * et al have insufficient information to do so.     * If %myid is already known, we'll use that value     * (XXX this may be a mistake: it could be stale).     * If %myid is unknown, we should check to see if     * there are credentials for the IP address or the FQDN.     * Instead, we'll just assume the IP address since we are     * acting as the responder and only the IP address would     * have gotten it to us.     * We don't even try to do this for the other side:     * %myid makes no sense for the other side (but it is syntactically     * legal).     */    our_id = resolve_myid(&c->spd.this.id);    if (our_id->kind == ID_NONE)    {	iptoid(&c->spd.this.host_addr, &our_id_space);	our_id = &our_id_space;    }    switch (next_step)    {    case vos_our_client:	networkof(&b->my.net, &client);	iptoid(&client, &id);	vc->b.failure_ok = b->failure_ok = FALSE;	ugh = start_adns_query(&id	    , our_id	    , T_TXT	    , quick_inI1_outR1_continue	    , &vc->ac);	break;    case vos_our_txt:	vc->b.failure_ok = b->failure_ok = TRUE;	ugh = start_adns_query(our_id	    , our_id	/* self as SG */	    , T_TXT	    , quick_inI1_outR1_continue	    , &vc->ac);	break;#ifdef USE_KEYRR    case vos_our_key:	vc->b.failure_ok = b->failure_ok = FALSE;	ugh = start_adns_query(our_id	    , NULL	    , T_KEY	    , quick_inI1_outR1_continue	    , &vc->ac);	break;#endif    case vos_his_client:	networkof(&b->his.net, &client);	iptoid(&client, &id);	vc->b.failure_ok = b->failure_ok = FALSE;	ugh = start_adns_query(&id	    , &c->spd.that.id	    , T_TXT	    , quick_inI1_outR1_continue	    , &vc->ac);	break;    default:	bad_case(next_step);    }    if (ugh != NULL)    {	/* note: we'd like to use vc->b but vc has been freed	 * so we have to use b.  This is why we plunked next_state	 * into b, not just vc->b.	 */	report_verify_failure(b, ugh);	p1st->st_suspended_md = NULL;	return STF_FAIL + INVALID_ID_INFORMATION;    }    else    {	return STF_SUSPEND;    }}static enum verify_oppo_stepquick_inI1_outR1_process_answer(struct verify_oppo_bundle *b, struct adns_continuation *ac, struct state *p1st){    struct connection *c = p1st->st_connection;    enum verify_oppo_step next_step;    err_t ugh = NULL;    DBG(DBG_CONTROL,	{	    char ours[SUBNETTOT_BUF];	    char his[SUBNETTOT_BUF];	    subnettot(&c->spd.this.client, 0, ours, sizeof(ours));	    subnettot(&c->spd.that.client, 0, his, sizeof(his));	    DBG_log("responding on demand from %s to %s state: %s"		    , ours, his, verify_step_name[b->step]);	});    /* process just completed DNS query (if any) */    switch (b->step)    {    case vos_start:	/* no query to digest */	next_step = vos_our_client;	break;    case vos_our_client:	next_step = vos_his_client;	{	    const struct RSA_private_key *pri = get_RSA_private_key(c);	    struct gw_info *gwp;	    if (pri == NULL)	    {		ugh = "we don't know our own key";		break;	    }	    ugh = "our client does not delegate us as its Security Gateway";	    for (gwp = ac->gateways_from_dns; gwp != NULL; gwp = gwp->next)	    {		ugh = "our client delegates us as its Security Gateway but with the wrong public key";		/* If there is no key in the TXT record,		 * we count it as a win, but we will have		 * to separately fetch and check the KEY record.		 * If there is a key from the TXT record,		 * we count it as a win if we match the key.		 */		if (!gwp->gw_key_present)		{		    next_step = vos_our_txt;		    ugh = NULL;	/* good! */		    break;		}		else if (same_RSA_public_key(&pri->pub, &gwp->key->u.rsa))		{		    ugh = NULL;	/* good! */		    break;		}	    }	}	break;    case vos_our_txt:	next_step = vos_his_client;	{	    const struct RSA_private_key *pri = get_RSA_private_key(c);	    if (pri == NULL)	    {		ugh = "we don't know our own key";		break;	    }	    {		struct gw_info *gwp;		for (gwp = ac->gateways_from_dns; gwp != NULL; gwp = gwp->next)		{#ifdef USE_KEYRR		    /* not an error yet, because we have to check KEY RR as well */		    ugh = NULL;#else		    ugh = "our client delegation depends on our " RRNAME " record, but it has the wrong public key";#endif		    if (gwp->gw_key_present		    && same_RSA_public_key(&pri->pub, &gwp->key->u.rsa))		    {			ugh = NULL;	/* good! */			break;		    }#ifdef USE_KEYRR		    next_step = vos_our_key;#endif		}	    }	}	break;#ifdef USE_KEYRR    case vos_our_key:	next_step = vos_his_client;	{	    const struct RSA_private_key *pri = get_RSA_private_key(c);	    if (pri == NULL)	    {		ugh = "we don't know our own key";		break;	    }	    {		struct pubkey_list *kp;		ugh = "our client delegation depends on our missing " RRNAME " record";		for (kp = ac->keys_from_dns; kp != NULL; kp = kp->next)		{		    ugh = "our client delegation depends on our " RRNAME " record, but it has the wrong public key";		    if (same_RSA_public_key(&pri->pub, &kp->key->u.rsa))		    {			/* do this only once a day */			if (!logged_txt_warning)			{			    loglog(RC_LOG_SERIOUS, "found KEY RR but not TXT RR. See http://www.freeswan.org/err/txt-change.html.");			    logged_txt_warning = TRUE;			}			ugh = NULL;	/* good! */			break;		    }		}	    }	}	break;#endif /* USE_KEYRR */    case vos_his_client:	next_step = vos_done;	{	    struct gw_info *gwp;	    /* check that the public key that authenticated	     * the ISAKMP SA (p1st) will do for this gateway.	     */	    ugh = "peer's client does not delegate to peer";	    for (gwp = ac->gateways_from_dns; gwp != NULL; gwp = gwp->next)	    {		ugh = "peer and its client disagree about public key";		/* If there is a key from the TXT record,		 * we count it as a win if we match the key.		 * If there was no key, we claim a match since		 * it implies fetching a KEY from the same		 * place we must have gotten it.		 */		if (!gwp->gw_key_present		|| same_RSA_public_key(&p1st->st_peer_pubkey->u.rsa		, &gwp->key->u.rsa))		{		    ugh = NULL;	/* good! */		    break;		}	    }	}	break;    default:	bad_case(b->step);    }    if (ugh != NULL)    {	report_verify_failure(b, ugh);	next_step = vos_fail;    }    return next_step;}static stf_statusquick_inI1_outR1_cryptotail(struct qke_continuation *qke			    , struct pluto_crypto_req *r);static voidquick_inI1_outR1_cryptocontinue(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 inI1_outR1: calculated ke+nonce, sending R1"));    /* XXX should check out ugh */    passert(ugh == NULL);    passert(cur_state == NULL);    passert(st != NULL);    passert(st->st_connection != NULL);    set_cur_state(st);	/* we must reset before exit */    st->st_calculating=FALSE;    e = quick_inI1_outR1_cryptotail(qke, r);    if(qke->md != NULL) {	complete_state_transition(&qke->md, e);	release_md(qke->md);    }    reset_cur_state();}static stf_statusquick_inI1_outR1_authtail(struct verify_oppo_bundle *b		      , struct adns_continuation *ac){    struct msg_digest *md = b->md;    struct state *const p1st = md->st;    struct connection *c = p1st->st_connection;    ip_subnet *our_net = &b->my.net	, *his_net = &b->his.net;    /* Now that we have identities of client subnets, we must look for     * a suitable connection (our current one only matches for hosts).     */    {	struct connection *p = find_client_connection(c	    , our_net, his_net, b->my.proto, b->my.port, b->his.proto, b->his.port);#if 0#ifdef NAT_TRAVERSAL#ifdef I_KNOW_TRANSPORT_MODE_HAS_SECURITY_CONCERN_BUT_I_WANT_IT    if( (p1st->hidden_variables.st_nat_traversal & NAT_T_DETECTED)       && !(p1st->st_policy & POLICY_TUNNEL)       && (p1st->hidden_variables.st_nat_traversal & LELEM(NAT_TRAVERSAL_NAT_BHND_ME))       && (p == NULL) )        {          p = c;          DBG(DBG_CONTROL, DBG_log("using (something) old for transport mode connection \"%s\"", p->name));        }#endif#endif#endif	if (p == NULL)	{	    /* This message occurs in very puzzling circumstances	     * so we must add as much information and beauty as we can.	     */	    struct end		me = c->spd.this,		he = c->spd.that;	    char buf[2*SUBNETTOT_BUF + 2*ADDRTOT_BUF + 2*IDTOA_BUF + 2*ADDRTOT_BUF + 12]; /* + 12 for separating */	    size_t l;	    me.client = *our_net;	    me.has_client = !subnetisaddr(our_net, &me.host_addr);	    me.protocol = b->my.proto;	    me.port = b->my.port;	    he.client = *his_net;	    he.has_client = !subnetisaddr(his_net, &he.host_addr);	    he.protocol = b->his.proto;	    he.port = b->his.port;	    l = format_end(buf, sizeof(buf), &me, NULL, TRUE, LEMPTY);	    l += snprintf(buf + l, sizeof(buf) - l, "...");	    (void)format_end(buf + l, sizeof(buf) - l, &he, NULL, FALSE, LEMPTY);	    openswan_log("cannot respond to IPsec SA request"		" because no connection is known for %s"		, buf);	    return STF_FAIL + INVALID_ID_INFORMATION;	}	else if (p != c)	{	    /* We've got a better connection: it can support the	     * specified clients.  But it may need instantiation.	     */	    if (p->kind == CK_TEMPLATE)	    {		/* Yup, it needs instantiation.  How much?		 * Is it a Road Warrior connection (simple)		 * or is it an Opportunistic connection (needing gw validation)?		 */		if (p->policy & POLICY_OPPO)		{		    /* Opportunistic case: delegation must be verified.		     * Here be dragons.		     */		    enum verify_oppo_step next_step;		    ip_address our_client, his_client;		    passert(subnetishost(our_net) && subnetishost(his_net));		    networkof(our_net, &our_client);		    networkof(his_net, &his_client);		    next_step = quick_inI1_outR1_process_answer(b, ac, p1st);		    if (next_step == vos_fail)			return STF_FAIL + INVALID_ID_INFORMATION;		    /* short circuit: if peer's client is self,		     * accept that we've verified delegation in Phase 1		     */		    if (next_step == vos_his_client		    && sameaddr(&c->spd.that.host_addr, &his_client))			next_step = vos_done;		    /* the second chunk: initiate the next DNS query (if any) */		    DBG(DBG_CONTROL,			{			    char ours[SUBNETTOT_BUF];			    char his[SUBNETTOT_BUF];			    subnettot(&c->spd.this.client, 0, ours, sizeof(ours));			    subnettot(&c->spd.that.client, 0, his, sizeof(his));			    DBG_log("responding on demand from %s to %s new state: %s"				    , ours, his, verify_step_name[next_step]);			});		    /* start next DNS query and suspend (if necessary) */		    if (next_step != vos_done)			return quick_inI1_outR1_start_query(b, next_step);		    /* Instantiate inbound Opportunistic connection,		     * carrying over authenticated peer ID

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?