-/* $OpenBSD: ikev2.c,v 1.107 2014/05/06 10:24:22 markus Exp $ */
+/* $OpenBSD: ikev2.c,v 1.108 2014/05/06 14:10:53 markus Exp $ */
/*
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
int ikev2_sa_responder(struct iked *, struct iked_sa *, struct iked_sa *,
struct iked_message *);
int ikev2_sa_initiator_dh(struct iked_sa *, struct iked_message *, u_int);
-int ikev2_sa_responder_dh(struct iked_sa *, struct iked_message *, u_int);
+int ikev2_sa_responder_dh(struct iked_kex *, struct iked_proposals *,
+ struct iked_message *, u_int);
void ikev2_sa_cleanup_dh(struct iked_sa *);
int ikev2_sa_keys(struct iked *, struct iked_sa *, struct ibuf *);
int ikev2_sa_tag(struct iked_sa *, struct iked_id *);
int ikev2_set_sa_proposal(struct iked_sa *, struct iked_policy *, u_int);
-int ikev2_childsa_negotiate(struct iked *, struct iked_sa *, int, int);
+int ikev2_childsa_negotiate(struct iked *, struct iked_sa *,
+ struct iked_kex *, struct iked_proposals *, int, int);
int ikev2_match_proposals(struct iked_proposal *, struct iked_proposal *,
struct iked_transform **);
int ikev2_valid_proposal(struct iked_proposal *,
}
if (!TAILQ_EMPTY(&msg->msg_proposals)) {
- if (ikev2_sa_negotiate(sa, &sa->sa_policy->pol_proposals,
- &msg->msg_proposals) != 0) {
+ if (ikev2_sa_negotiate(&sa->sa_proposals,
+ &sa->sa_policy->pol_proposals, &msg->msg_proposals) != 0) {
log_debug("%s: no proposal chosen", __func__);
msg->msg_error = IKEV2_N_NO_PROPOSAL_CHOSEN;
return (-1);
if (!sa_stateok(sa, IKEV2_STATE_VALID))
return (0); /* ignored */
- ret = ikev2_childsa_negotiate(env, sa, sa->sa_hdr.sh_initiator, 0);
+ ret = ikev2_childsa_negotiate(env, sa, &sa->sa_kex, &sa->sa_proposals,
+ sa->sa_hdr.sh_initiator, 0);
if (ret == 0)
ret = ikev2_childsa_enable(env, sa);
if (ret == 0) {
if (ikev2_cp_setaddr(env, sa) < 0)
return (-1);
- if (ikev2_childsa_negotiate(env, sa, sa->sa_hdr.sh_initiator, 0) == -1)
+ if (ikev2_childsa_negotiate(env, sa, &sa->sa_kex, &sa->sa_proposals,
+ sa->sa_hdr.sh_initiator, 0) < 0)
return (-1);
/* New encrypted message buffer */
return (-1);
}
- if (ikev2_sa_negotiate(sa, &sa->sa_proposals,
+ if (ikev2_sa_negotiate(&sa->sa_proposals, &sa->sa_proposals,
&msg->msg_proposals) != 0) {
log_debug("%s: no proposal chosen", __func__);
return (-1);
ibuf_release(sa->sa_rnonce);
sa->sa_rnonce = ibuf_dup(msg->msg_nonce);
- if (ikev2_childsa_negotiate(env, sa, 1, pfs)) {
+ if (ikev2_childsa_negotiate(env, sa, &sa->sa_kex, &sa->sa_proposals, 1,
+ pfs)) {
log_debug("%s: failed to get CHILD SAs", __func__);
return (-1);
}
{
struct iked_childsa *csa;
struct iked_proposal *prop;
+ struct iked_proposals proposals;
+ struct iked_kex *kex, *kextmp = NULL;
struct iked_sa *nsa = NULL, *sa = msg->msg_sa;
struct iked_spi *spi, *rekey = &msg->msg_rekey;
struct ikev2_keyexchange *ke;
struct ikev2_payload *pld = NULL;
struct ibuf *e = NULL, *nonce = NULL;
- struct group *group;
u_int8_t firstpayload;
ssize_t len = 0;
int initiator, protoid, rekeying = 1;
if (!ikev2_msg_frompeer(msg) || msg->msg_prop == NULL)
return (0);
+ TAILQ_INIT(&proposals);
+
if ((protoid = rekey->spi_protoid) == 0) {
/*
* If REKEY_SA notification is not present, then it's either
sa_state(env, nsa, IKEV2_STATE_AUTH_SUCCESS);
nonce = nsa->sa_rnonce;
+ kex = &nsa->sa_kex;
} else {
/* Child SA creating/rekeying */
+ if ((kex = kextmp = calloc(1, sizeof(*kextmp))) == NULL) {
+ log_debug("%s: calloc kex", __func__);
+ goto fail;
+ }
+
+ if (ikev2_sa_negotiate(&proposals,
+ &sa->sa_policy->pol_proposals, &msg->msg_proposals) != 0) {
+ log_debug("%s: no proposal chosen", __func__);
+ goto fail;
+ }
+
/* check KE payload for PFS */
if (ibuf_length(msg->msg_parent->msg_ke)) {
log_debug("%s: using PFS", __func__);
- ikev2_sa_cleanup_dh(sa);
- if (ikev2_sa_responder_dh(sa, msg->msg_parent,
- protoid) < 0) {
+ if (ikev2_sa_responder_dh(kex, &proposals,
+ msg->msg_parent, protoid) < 0) {
log_debug("%s: failed to setup DH", __func__);
- return (ret);
+ goto fail;
}
pfs = 1;
/* XXX check group against policy ? */
}
/* Update peer SPI */
- TAILQ_FOREACH(prop, &sa->sa_proposals, prop_entry) {
+ TAILQ_FOREACH(prop, &proposals, prop_entry) {
if (prop->prop_protoid == protoid)
break;
}
if (prop == NULL) {
log_debug("%s: failed to find %s proposals", __func__,
print_map(protoid, ikev2_saproto_map));
- return (-1);
+ goto fail;
} else
prop->prop_peerspi = msg->msg_prop->prop_peerspi;
log_debug("%s: CHILD SA %s wasn't found",
__func__, print_spi(rekey->spi,
rekey->spi_size));
- return (-1);
+ goto fail;
}
if (!csa->csa_loaded || !csa->csa_peersa ||
!csa->csa_peersa->csa_loaded) {
log_debug("%s: SA is not loaded or no peer SA",
__func__);
- return (-1);
+ goto fail;
}
csa->csa_rekey = 1;
csa->csa_peersa->csa_rekey = 1;
/* Update initiator's nonce */
if (!ibuf_length(msg->msg_nonce)) {
log_debug("%s: initiator didn't send nonce", __func__);
- return (-1);
+ goto fail;
}
- ibuf_release(sa->sa_inonce);
- sa->sa_inonce = ibuf_dup(msg->msg_nonce);
+ ibuf_release(kex->kex_inonce);
+ kex->kex_inonce = ibuf_dup(msg->msg_nonce);
/* Generate new responder's nonce */
if ((nonce = ibuf_random(IKED_NONCE_SIZE)) == NULL)
- return (-1);
+ goto fail;
/* Update responder's nonce */
- ibuf_release(sa->sa_rnonce);
- sa->sa_rnonce = nonce;
+ ibuf_release(kex->kex_rnonce);
+ kex->kex_rnonce = nonce;
- if (ikev2_childsa_negotiate(env, sa, 0, pfs)) {
+ if (ikev2_childsa_negotiate(env, sa, kex, &proposals, 0, pfs)) {
log_debug("%s: failed to get CHILD SAs", __func__);
- return (-1);
+ goto fail;
}
}
goto done;
if ((len = ikev2_add_proposals(env, nsa ? nsa : sa, e,
- nsa ? &nsa->sa_proposals : &sa->sa_proposals,
+ nsa ? &nsa->sa_proposals : &proposals,
protoid, 0, nsa ? 1 : 0)) == -1)
goto done;
goto done;
if ((ke = ibuf_advance(e, sizeof(*ke))) == NULL)
goto done;
- if ((group = nsa ?
- nsa->sa_dhgroup : sa->sa_dhgroup) == NULL) {
+ if (kex->kex_dhgroup == NULL) {
log_debug("%s: invalid dh", __func__);
goto done;
}
- ke->kex_dhgroup = htobe16(group->id);
- if (ikev2_add_buf(e, nsa ?
- nsa->sa_dhrexchange : sa->sa_dhrexchange) == -1)
+ ke->kex_dhgroup = htobe16(kex->kex_dhgroup->id);
+ if (ikev2_add_buf(e, kex->kex_dhrexchange) == -1)
goto done;
- len = sizeof(*ke) + dh_getlen(group);
+ len = sizeof(*ke) + dh_getlen(kex->kex_dhgroup);
}
if (protoid != IKEV2_SAPROTO_IKE)
if (ret && protoid != IKEV2_SAPROTO_IKE)
ikev2_childsa_delete(env, sa, 0, 0, NULL, 1);
ibuf_release(e);
+ config_free_proposals(&proposals, 0);
+ config_free_kex(kextmp);
return (ret);
+
+ fail:
+ config_free_proposals(&proposals, 0);
+ config_free_kex(kextmp);
+ return (-1);
}
void
}
int
-ikev2_sa_negotiate(struct iked_sa *sa, struct iked_proposals *local,
+ikev2_sa_negotiate(struct iked_proposals *result, struct iked_proposals *local,
struct iked_proposals *peer)
{
struct iked_proposal *ppeer = NULL, *plocal, *prop, vpeer, vlocal;
if (chosen_score == 0)
return (-1);
- else if (sa == NULL)
+ else if (result == NULL)
return (0);
- (void)config_free_proposals(&sa->sa_proposals, vpeer.prop_protoid);
- prop = config_add_proposal(&sa->sa_proposals, vpeer.prop_id,
- vpeer.prop_protoid);
+ (void)config_free_proposals(result, vpeer.prop_protoid);
+ prop = config_add_proposal(result, vpeer.prop_id, vpeer.prop_protoid);
if (vpeer.prop_localspi.spi_size) {
prop->prop_localspi.spi_size = vpeer.prop_localspi.spi_size;
}
/* XXX we need a better way to get this */
- if (ikev2_sa_negotiate(sa, &msg->msg_policy->pol_proposals,
- &msg->msg_proposals) != 0) {
+ if (ikev2_sa_negotiate(&sa->sa_proposals,
+ &msg->msg_policy->pol_proposals, &msg->msg_proposals) != 0) {
log_debug("%s: no proposal chosen", __func__);
msg->msg_error = IKEV2_N_NO_PROPOSAL_CHOSEN;
return (-1);
}
int
-ikev2_sa_responder_dh(struct iked_sa *sa, struct iked_message *msg, u_int proto)
+ikev2_sa_responder_dh(struct iked_kex *kex, struct iked_proposals *proposals,
+ struct iked_message *msg, u_int proto)
{
struct iked_transform *xform;
- if (sa->sa_dhgroup == NULL) {
- if ((xform = config_findtransform(&sa->sa_proposals,
+ if (kex->kex_dhgroup == NULL) {
+ if ((xform = config_findtransform(proposals,
IKEV2_XFORMTYPE_DH, proto)) == NULL) {
log_debug("%s: did not find dh transform", __func__);
return (-1);
}
- if ((sa->sa_dhgroup =
+ if ((kex->kex_dhgroup =
group_get(xform->xform_id)) == NULL) {
log_debug("%s: invalid dh %d", __func__,
xform->xform_id);
}
}
- if (!ibuf_length(sa->sa_dhrexchange)) {
- if ((sa->sa_dhrexchange = ibuf_new(NULL,
- dh_getlen(sa->sa_dhgroup))) == NULL) {
+ if (!ibuf_length(kex->kex_dhrexchange)) {
+ if ((kex->kex_dhrexchange = ibuf_new(NULL,
+ dh_getlen(kex->kex_dhgroup))) == NULL) {
log_debug("%s: failed to alloc dh exchange", __func__);
return (-1);
}
- if (dh_create_exchange(sa->sa_dhgroup,
- sa->sa_dhrexchange->buf) == -1) {
+ if (dh_create_exchange(kex->kex_dhgroup,
+ kex->kex_dhrexchange->buf) == -1) {
log_debug("%s: failed to get dh exchange", __func__);
return (-1);
}
}
- if (!ibuf_length(sa->sa_dhiexchange)) {
- if ((sa->sa_dhiexchange = ibuf_dup(msg->msg_ke)) == NULL ||
- ((ssize_t)ibuf_length(sa->sa_dhiexchange) !=
- dh_getlen(sa->sa_dhgroup))) {
+ if (!ibuf_length(kex->kex_dhiexchange)) {
+ if ((kex->kex_dhiexchange = ibuf_dup(msg->msg_ke)) == NULL ||
+ ((ssize_t)ibuf_length(kex->kex_dhiexchange) !=
+ dh_getlen(kex->kex_dhgroup))) {
/* XXX send notification to peer */
log_debug("%s: invalid dh, size %d", __func__,
- dh_getlen(sa->sa_dhgroup) * 8);
+ dh_getlen(kex->kex_dhgroup) * 8);
return (-1);
}
}
/* Set a pointer to the peer exchange */
- sa->sa_dhpeer = sa->sa_dhiexchange;
+ kex->kex_dhpeer = kex->kex_dhiexchange;
return (0);
}
int
}
/* XXX we need a better way to get this */
- if (ikev2_sa_negotiate(sa, &msg->msg_policy->pol_proposals,
- &msg->msg_proposals) != 0) {
+ if (ikev2_sa_negotiate(&sa->sa_proposals,
+ &msg->msg_policy->pol_proposals, &msg->msg_proposals) != 0) {
log_debug("%s: no proposal chosen", __func__);
msg->msg_error = IKEV2_N_NO_PROPOSAL_CHOSEN;
return (-1);
}
}
- if (ikev2_sa_responder_dh(sa, msg, 0) < 0)
+ if (ikev2_sa_responder_dh(&sa->sa_kex, &sa->sa_proposals, msg, 0) < 0)
return (-1);
return (ikev2_sa_keys(env, sa, osa ? osa->sa_key_d : NULL));
}
int
-ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa, int initiator,
+ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa,
+ struct iked_kex *kex, struct iked_proposals *proposals, int initiator,
int pfs)
{
struct iked_proposal *prop;
return (-1);
/* We need to determine the key material length first */
- TAILQ_FOREACH(prop, &sa->sa_proposals, prop_entry) {
+ TAILQ_FOREACH(prop, proposals, prop_entry) {
if (prop->prop_protoid == IKEV2_SAPROTO_IKE)
continue;
log_debug("%s: proposal %d", __func__, prop->prop_id);
}
if (pfs) {
log_debug("%s: using PFS", __func__);
- if (sa->sa_dhpeer == NULL || ibuf_length(sa->sa_dhpeer) == 0 ||
- (group = sa->sa_dhgroup) == NULL) {
+ if (kex->kex_dhpeer == NULL || ibuf_length(kex->kex_dhpeer) == 0 ||
+ (group = kex->kex_dhgroup) == NULL) {
log_debug("%s: no dh group for pfs", __func__);
goto done;
}
goto done;
}
if (dh_create_shared(group, dhsecret->buf,
- sa->sa_dhpeer->buf) == -1) {
+ kex->kex_dhpeer->buf) == -1) {
log_debug("%s: failed to get dh secret"
" group %d len %d secret %zu exchange %zu", __func__,
group->id, dh_getlen(group), ibuf_length(dhsecret),
- ibuf_length(sa->sa_dhpeer));
+ ibuf_length(kex->kex_dhpeer));
goto done;
}
if (ibuf_cat(seed, dhsecret) != 0) {
goto done;
}
}
- if (ibuf_cat(seed, sa->sa_inonce) != 0 ||
- ibuf_cat(seed, sa->sa_rnonce) != 0 ||
+ if (ibuf_cat(seed, kex->kex_inonce) != 0 ||
+ ibuf_cat(seed, kex->kex_rnonce) != 0 ||
(keymat = ikev2_prfplus(sa->sa_prf,
sa->sa_key_d, seed, ilen)) == NULL) {
log_debug("%s: failed to get IKE SA key material", __func__);
}
/* Create the new flows */
- TAILQ_FOREACH(prop, &sa->sa_proposals, prop_entry) {
+ TAILQ_FOREACH(prop, proposals, prop_entry) {
if (ikev2_valid_proposal(prop, NULL, NULL, NULL) != 0)
continue;
}
/* create the CHILD SAs using the key material */
- TAILQ_FOREACH(prop, &sa->sa_proposals, prop_entry) {
+ TAILQ_FOREACH(prop, proposals, prop_entry) {
if (ikev2_valid_proposal(prop, &encrxf, &integrxf, &esn) != 0)
continue;