summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortobhe <tobhe@cvs.openbsd.org>2021-01-21 16:46:48 +0000
committertobhe <tobhe@cvs.openbsd.org>2021-01-21 16:46:48 +0000
commit0be656db44e6ee7cd1809a9c5b383be13398b20c (patch)
treef478b7329e4081d29bb2915871d78b41e4901e95
parent0a6e01ddb88c03c59a18f08956f3232c6f860475 (diff)
Add support for INVALID_KE_PAYLOAD in CREATE_CHILD_SA
exchange. In the case of an invalid KE error, retry CREATE_CHILD_SA exchange with different group instead of restarting the full IKE handshake. ok markus@
-rw-r--r--sbin/iked/config.c20
-rw-r--r--sbin/iked/iked.h6
-rw-r--r--sbin/iked/ikev2.c115
3 files changed, 104 insertions, 37 deletions
diff --git a/sbin/iked/config.c b/sbin/iked/config.c
index a77e5320381..7df23d84bba 100644
--- a/sbin/iked/config.c
+++ b/sbin/iked/config.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: config.c,v 1.74 2020/11/29 21:00:43 tobhe Exp $ */
+/* $OpenBSD: config.c,v 1.75 2021/01/21 16:46:47 tobhe Exp $ */
/*
* Copyright (c) 2019 Tobias Heider <tobias.heider@stusta.de>
@@ -84,8 +84,7 @@ config_free_kex(struct iked_kex *kex)
ibuf_release(kex->kex_inonce);
ibuf_release(kex->kex_rnonce);
- if (kex->kex_dhgroup != NULL)
- group_free(kex->kex_dhgroup);
+ group_free(kex->kex_dhgroup);
ibuf_release(kex->kex_dhiexchange);
ibuf_release(kex->kex_dhrexchange);
@@ -140,8 +139,7 @@ config_free_sa(struct iked *env, struct iked_sa *sa)
ibuf_release(sa->sa_inonce);
ibuf_release(sa->sa_rnonce);
- if (sa->sa_dhgroup != NULL)
- group_free(sa->sa_dhgroup);
+ group_free(sa->sa_dhgroup);
ibuf_release(sa->sa_dhiexchange);
ibuf_release(sa->sa_dhrexchange);
@@ -408,7 +406,7 @@ config_add_transform(struct iked_proposal *prop, unsigned int type,
}
struct iked_transform *
-config_findtransform(struct iked_proposals *props, uint8_t type,
+config_findtransform_ext(struct iked_proposals *props, uint8_t type, int id,
unsigned int proto)
{
struct iked_proposal *prop;
@@ -422,6 +420,9 @@ config_findtransform(struct iked_proposals *props, uint8_t type,
continue;
for (i = 0; i < prop->prop_nxforms; i++) {
xform = prop->prop_xforms + i;
+ /* optional lookup of specific transform */
+ if (id >= 0 && xform->xform_id != id)
+ continue;
if (xform->xform_type == type)
return (xform);
}
@@ -430,6 +431,13 @@ config_findtransform(struct iked_proposals *props, uint8_t type,
return (NULL);
}
+struct iked_transform *
+config_findtransform(struct iked_proposals *props, uint8_t type,
+ unsigned int proto)
+{
+ return config_findtransform_ext(props, type, -1, proto);
+}
+
struct iked_user *
config_new_user(struct iked *env, struct iked_user *new)
{
diff --git a/sbin/iked/iked.h b/sbin/iked/iked.h
index 242ceef109b..a8578d8ee51 100644
--- a/sbin/iked/iked.h
+++ b/sbin/iked/iked.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: iked.h,v 1.179 2020/12/21 22:49:36 tobhe Exp $ */
+/* $OpenBSD: iked.h,v 1.180 2021/01/21 16:46:47 tobhe Exp $ */
/*
* Copyright (c) 2019 Tobias Heider <tobias.heider@stusta.de>
@@ -479,7 +479,7 @@ struct iked_sa {
struct iked_sa *sa_previ; /* matching back pointer */
struct iked_sa *sa_nextr; /* simultaneous rekey */
struct iked_sa *sa_prevr; /* matching back pointer */
- uint64_t sa_rekeyspi; /* peerspi CSA rekey*/
+ uint64_t sa_rekeyspi; /* peerspi CSA rekey */
struct ibuf *sa_simult; /* simultaneous rekey */
struct iked_ipcomp sa_ipcompi; /* IPcomp initator */
@@ -800,6 +800,8 @@ uint64_t
config_getspi(void);
struct iked_transform *
config_findtransform(struct iked_proposals *, uint8_t, unsigned int);
+struct iked_transform *
+ config_findtransform_ext(struct iked_proposals *, uint8_t,int, unsigned int);
void config_free_policy(struct iked *, struct iked_policy *);
struct iked_proposal *
config_add_proposal(struct iked_proposals *, unsigned int,
diff --git a/sbin/iked/ikev2.c b/sbin/iked/ikev2.c
index 5a2a2c52dbe..2f7b781a195 100644
--- a/sbin/iked/ikev2.c
+++ b/sbin/iked/ikev2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ikev2.c,v 1.295 2021/01/20 18:44:28 tobhe Exp $ */
+/* $OpenBSD: ikev2.c,v 1.296 2021/01/21 16:46:47 tobhe Exp $ */
/*
* Copyright (c) 2019 Tobias Heider <tobias.heider@stusta.de>
@@ -103,7 +103,7 @@ ssize_t ikev2_handle_delete(struct iked *, struct iked_message *,
struct ibuf *, struct ikev2_payload **, uint8_t *);
int ikev2_send_create_child_sa(struct iked *, struct iked_sa *,
- struct iked_spi *, uint8_t);
+ struct iked_spi *, uint8_t, uint16_t);
int ikev2_ikesa_enable(struct iked *, struct iked_sa *, struct iked_sa *);
void ikev2_ikesa_delete(struct iked *, struct iked_sa *, int);
int ikev2_nonce_cmp(struct ibuf *, struct ibuf *);
@@ -593,6 +593,7 @@ ikev2_recv(struct iked *env, struct iked_message *msg)
initiator = (hdr->ike_flags & IKEV2_FLAG_INITIATOR) ? 0 : 1;
msg->msg_response = (hdr->ike_flags & IKEV2_FLAG_RESPONSE) ? 1 : 0;
+ msg->msg_exchange = hdr->ike_exchange;
msg->msg_sa = sa_lookup(env,
betoh64(hdr->ike_ispi), betoh64(hdr->ike_rspi),
initiator);
@@ -2952,7 +2953,10 @@ ikev2_handle_notifies(struct iked *env, struct iked_message *msg)
{
struct iked_ipcomp *ic;
struct iked_sa *sa;
- uint16_t group;
+ struct iked_spi rekey;
+ struct group *group;
+ uint16_t groupid;
+ unsigned int protoid;
if ((sa = msg->msg_sa) == NULL)
return (-1);
@@ -2980,39 +2984,85 @@ ikev2_handle_notifies(struct iked *env, struct iked_message *msg)
}
if (msg->msg_flags & IKED_MSG_FLAGS_INVALID_KE) {
- /* XXX chould also happen for PFS */
- group = betoh16(msg->msg_group);
- if (!sa->sa_hdr.sh_initiator) {
- log_debug("%s: not an initiator", __func__);
+ groupid = betoh16(msg->msg_group);
+ if (group_getid(groupid) == NULL) {
+ log_debug("%s: unable to select DH group %u",
+ __func__, groupid);
ikev2_ike_sa_setreason(sa,
- "received invalid KE as responder");
+ "unable to select DH group");
sa_state(env, sa, IKEV2_STATE_CLOSED);
msg->msg_sa = NULL;
return (-1);
}
- if (group_getid(group) == NULL) {
- log_debug("%s: unable to select DH group %u", __func__,
- group);
+ log_debug("%s: responder selected DH group %u", __func__,
+ groupid);
+ switch (msg->msg_exchange) {
+ case IKEV2_EXCHANGE_IKE_SA_INIT:
+ protoid = IKEV2_SAPROTO_ESP;
+ if (!sa->sa_hdr.sh_initiator) {
+ log_debug("%s: not an initiator", __func__);
+ ikev2_ike_sa_setreason(sa,
+ "received invalid KE as responder");
+ sa_state(env, sa, IKEV2_STATE_CLOSED);
+ msg->msg_sa = NULL;
+ return (-1);
+ }
+ if (config_findtransform_ext(&msg->msg_policy->pol_proposals,
+ IKEV2_XFORMTYPE_DH, groupid, protoid) == NULL) {
+ log_debug("%s: DH group %u denied by policy",
+ __func__, groupid);
+ ikev2_ike_sa_setreason(sa,
+ "unsupported group in INVALID_KE message");
+ sa_state(env, sa, IKEV2_STATE_CLOSED);
+ msg->msg_sa = NULL;
+ return (-1);
+ }
ikev2_ike_sa_setreason(sa,
- "unable to select DH group");
+ "reinitiating with new DH group");
sa_state(env, sa, IKEV2_STATE_CLOSED);
msg->msg_sa = NULL;
+ msg->msg_policy->pol_peerdh = groupid;
+ timer_set(env, &env->sc_inittmr, ikev2_init_ike_sa, NULL);
+ timer_add(env, &env->sc_inittmr, IKED_INITIATOR_INITIAL);
+ return (-1);
+ case IKEV2_EXCHANGE_CREATE_CHILD_SA:
+ if (!(sa->sa_stateflags & IKED_REQ_CHILDSA)) {
+ log_debug("%s: IKED_REQ_CHILDSA missing",
+ __func__);
+ return (-1);
+ }
+ sa->sa_stateflags &= ~IKED_REQ_CHILDSA;
+ protoid = sa->sa_rekeyspi ?
+ IKEV2_SAPROTO_ESP : IKEV2_SAPROTO_IKE;
+ if (config_findtransform_ext(&msg->msg_policy->pol_proposals,
+ IKEV2_XFORMTYPE_DH, groupid, protoid) == NULL) {
+ log_debug("%s: DH group %u denied by policy",
+ __func__, groupid);
+ ikev2_ike_sa_setreason(sa,
+ "unsupported group in INVALID_KE message");
+ sa_state(env, sa, IKEV2_STATE_CLOSED);
+ msg->msg_sa = NULL;
+ return (-1);
+ }
+ if (protoid == IKEV2_SAPROTO_ESP) {
+ /* CHILDSA */
+ rekey.spi = sa->sa_rekeyspi;
+ rekey.spi_size = 4;
+ rekey.spi_protoid = protoid;
+ (void)ikev2_send_create_child_sa(env, sa,
+ &rekey, rekey.spi_protoid, groupid);
+ } else {
+ /* IKESA */
+ if ((group = group_get(groupid)) == NULL)
+ return -1;
+ group_free(sa->sa_dhgroup);
+ sa->sa_dhgroup = group;
+ timer_set(env, &sa->sa_rekey,
+ ikev2_ike_sa_rekey, sa);
+ timer_add(env, &sa->sa_rekey, 0);
+ }
return (-1);
}
- msg->msg_policy->pol_peerdh = group;
- log_debug("%s: responder selected DH group %u", __func__,
- group);
- ikev2_ike_sa_setreason(sa,
- "reinitiating with new DH group");
- sa_state(env, sa, IKEV2_STATE_CLOSED);
- msg->msg_sa = NULL;
-
- /*
- * XXX should also happen for PFS so we have to check state.
- */
- timer_set(env, &env->sc_inittmr, ikev2_init_ike_sa, NULL);
- timer_add(env, &env->sc_inittmr, IKED_INITIATOR_INITIAL);
- return (-1);
}
if (msg->msg_flags & IKED_MSG_FLAGS_IPCOMP_SUPPORTED) {
@@ -3762,7 +3812,7 @@ ikev2_set_sa_proposal(struct iked_sa *sa, struct iked_policy *pol,
int
ikev2_send_create_child_sa(struct iked *env, struct iked_sa *sa,
- struct iked_spi *rekey, uint8_t protoid)
+ struct iked_spi *rekey, uint8_t protoid, uint16_t proposed_group)
{
struct iked_policy *pol = sa->sa_policy;
struct iked_childsa *csa = NULL, *csb = NULL;
@@ -3862,6 +3912,13 @@ ikev2_send_create_child_sa(struct iked *env, struct iked_sa *sa,
protoid)) {
log_debug("%s: enable PFS", __func__);
ikev2_sa_cleanup_dh(sa);
+ if (proposed_group) {
+ if ((sa->sa_dhgroup =
+ group_get(proposed_group)) == NULL) {
+ log_debug("%s: failed to get group", __func__);
+ goto done;
+ }
+ }
if (ikev2_sa_initiator_dh(sa, NULL, protoid, NULL) < 0) {
log_debug("%s: failed to setup DH", __func__);
goto done;
@@ -6357,7 +6414,7 @@ ikev2_child_sa_acquire(struct iked *env, struct iked_flow *acquire)
if (sa->sa_stateflags & (IKED_REQ_CHILDSA|IKED_REQ_INF))
return (-1); /* busy, retry later */
if (ikev2_send_create_child_sa(env, sa, NULL,
- flow->flow_saproto) != 0)
+ flow->flow_saproto, 0) != 0)
log_warnx("%s: failed to initiate a "
"CREATE_CHILD_SA exchange", SPI_SA(sa, __func__));
}
@@ -6407,7 +6464,7 @@ ikev2_child_sa_rekey(struct iked *env, struct iked_spi *rekey)
return (-1); /* peer is busy, retry later */
if (csa->csa_allocated) /* Peer SPI died first, get the local one */
rekey->spi = csa->csa_peerspi;
- if (ikev2_send_create_child_sa(env, sa, rekey, rekey->spi_protoid))
+ if (ikev2_send_create_child_sa(env, sa, rekey, rekey->spi_protoid, 0))
log_warnx("%s: failed to initiate a CREATE_CHILD_SA exchange",
SPI_SA(sa, __func__));
return (0);