From 975f0a0ec631ff3b5d26123995132412838d6820 Mon Sep 17 00:00:00 2001 From: tobhe Date: Thu, 21 Jan 2021 16:46:47 +0000 Subject: [PATCH] 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@ --- sbin/iked/config.c | 20 +++++--- sbin/iked/iked.h | 6 ++- sbin/iked/ikev2.c | 115 +++++++++++++++++++++++++++++++++------------ 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 @@ -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 @@ -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 @@ -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); -- 2.20.1