Tim Stewart <[email protected]> writes:
> This patch teaches iked to reject a KE with a Notify payload of type
> INVALID_KE_PAYLOAD when the KE uses a different Diffie-Hellman group
> than is configured locally. The rejection indicates the desired
> group.
>
> In my environment, this patch allows stock strongSwan on Android from
> the Google Play store to interop with iked. strongSwan's logs show
> the following once iked is patched:
>
> [IKE] initiating IKE_SA android[7] to 192.0.2.1
> [ENC] generating IKE_SA_INIT request 0 [ SA KE No N(NATD_S_IP) N(NATD_D_IP)
> N(FRAG_SUP) N(HASH_ALG) N(REDIR_SUP) ]
> [ENC] parsed IKE_SA_INIT response 0 [ N(INVAL_KE) ]
> [IKE] peer didn't accept DH group ECP_256, it requested MODP_2048
> [IKE] initiating IKE_SA android[7] to 192.0.2.1
> [ENC] generating IKE_SA_INIT request 0 [ SA KE No N(NATD_S_IP) N(NATD_D_IP)
> N(FRAG_SUP) N(HASH_ALG) N(REDIR_SUP) ]
> [ENC] parsed IKE_SA_INIT response 0 [ SA KE No N(NATD_S_IP) N(NATD_D_IP)
> CERTREQ N(HASH_ALG) ]
>
> I'm happy to iterate on this patch to get it into proper shape for
> inclusion.
I discovered a bug in the previous patch that broke renegotiation of
CHILD SAs. I was ignoring "other than NONE" in the following sentence
from RFC 5996 section 3.4:
If the selected proposal uses a different Diffie-Hellman group (other
than NONE), the message MUST be rejected with a Notify payload of type
INVALID_KE_PAYLOAD.
The new patch below repairs the flaw.
-TimS
Index: iked.h
===================================================================
RCS file: /cvs/src/sbin/iked/iked.h,v
retrieving revision 1.115
diff -u -p -r1.115 iked.h
--- iked.h 26 Apr 2017 10:42:38 -0000 1.115
+++ iked.h 22 May 2017 05:29:17 -0000
@@ -502,6 +502,7 @@ struct iked_message {
struct iked_proposals msg_proposals;
struct iked_spi msg_rekey;
struct ibuf *msg_nonce; /* dh NONCE */
+ uint16_t msg_dhgroup; /* dh group */
struct ibuf *msg_ke; /* dh key exchange */
struct iked_id msg_auth; /* AUTH payload */
struct iked_id msg_id;
Index: ikev2.c
===================================================================
RCS file: /cvs/src/sbin/iked/ikev2.c,v
retrieving revision 1.154
diff -u -p -r1.154 ikev2.c
--- ikev2.c 26 Apr 2017 10:42:38 -0000 1.154
+++ ikev2.c 22 May 2017 05:29:18 -0000
@@ -71,6 +71,8 @@ int ikev2_init_done(struct iked *, stru
void ikev2_resp_recv(struct iked *, struct iked_message *,
struct ike_header *);
int ikev2_resp_ike_sa_init(struct iked *, struct iked_message *);
+int ikev2_resp_ike_invalid_ke(struct iked *, struct iked_message *,
+ struct iked_kex *);
int ikev2_resp_ike_auth(struct iked *, struct iked_sa *);
int ikev2_resp_ike_eap(struct iked *, struct iked_sa *, struct ibuf *);
int ikev2_send_auth_failed(struct iked *, struct iked_sa *);
@@ -96,8 +98,8 @@ int ikev2_sa_responder(struct iked *, s
struct iked_message *);
int ikev2_sa_initiator_dh(struct iked_sa *, struct iked_message *,
unsigned int);
-int ikev2_sa_responder_dh(struct iked_kex *, struct iked_proposals *,
- struct iked_message *, unsigned int);
+int ikev2_sa_responder_dh(struct iked *, struct iked_kex *,
+ struct iked_proposals *, struct iked_message *, unsigned 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 *);
@@ -2279,6 +2281,84 @@ ikev2_resp_ike_sa_init(struct iked *env,
}
int
+ikev2_resp_ike_invalid_ke(struct iked *env, struct iked_message *msg,
+ struct iked_kex *kex)
+{
+ struct iked_message resp;
+ struct ike_header *hdr;
+ struct ikev2_payload *pld;
+ struct ikev2_notify *n;
+ struct iked_sa *sa = msg->msg_sa;
+ struct ibuf *buf;
+ uint8_t *ptr;
+ ssize_t len;
+ uint16_t group;
+ int ret = -1;
+
+ if (sa->sa_hdr.sh_initiator) {
+ log_debug("%s: called by initiator", __func__);
+ return (-1);
+ }
+
+ log_debug("%s: rejecting with INVALID_KE_PAYLOAD", __func__);
+
+ if ((buf = ikev2_msg_init(env, &resp,
+ &msg->msg_peer, msg->msg_peerlen,
+ &msg->msg_local, msg->msg_locallen, 1)) == NULL)
+ goto done;
+
+ resp.msg_sa = sa;
+ resp.msg_fd = msg->msg_fd;
+ resp.msg_natt = msg->msg_natt;
+ resp.msg_msgid = 0;
+
+ /* IKE header */
+ if ((hdr = ikev2_add_header(buf, sa, resp.msg_msgid,
+ IKEV2_PAYLOAD_NOTIFY, IKEV2_EXCHANGE_IKE_SA_INIT,
+ IKEV2_FLAG_RESPONSE)) == NULL)
+ goto done;
+
+ /* NOTIFY payload */
+ if ((pld = ikev2_add_payload(buf)) == NULL)
+ goto done;
+ if ((n = ibuf_advance(buf, sizeof(*n))) == NULL)
+ goto done;
+ n->n_protoid = 0;
+ n->n_spisize = 0;
+ n->n_type = htobe16(IKEV2_N_INVALID_KE_PAYLOAD);
+
+ /* add desired dh group to NOTIFY */
+ group = htobe16(kex->kex_dhgroup->id);
+ len = sizeof(group);
+ if ((ptr = ibuf_advance(buf, len)) == NULL)
+ goto done;
+ memcpy(ptr, &group, sizeof(group));
+ len += sizeof(*n);
+
+ if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1)
+ goto done;
+
+ if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1)
+ goto done;
+
+ (void)ikev2_pld_parse(env, hdr, &resp, 0);
+
+ ibuf_release(sa->sa_2ndmsg);
+ if ((sa->sa_2ndmsg = ibuf_dup(buf)) == NULL) {
+ log_debug("%s: failed to copy 2nd message", __func__);
+ goto done;
+ }
+
+ resp.msg_sa = NULL; /* Don't save the response */
+ ret = ikev2_msg_send(env, &resp);
+
+done:
+ ikev2_msg_cleanup(env, &resp);
+
+ return (ret);
+}
+
+int
ikev2_send_auth_failed(struct iked *env, struct iked_sa *sa)
{
struct ikev2_notify *n;
@@ -3397,7 +3477,7 @@ ikev2_resp_create_child_sa(struct iked *
/* check KE payload for PFS */
if (ibuf_length(msg->msg_parent->msg_ke)) {
log_debug("%s: using PFS", __func__);
- if (ikev2_sa_responder_dh(kex, &proposals,
+ if (ikev2_sa_responder_dh(env, kex, &proposals,
msg->msg_parent, protoid) < 0) {
log_debug("%s: failed to setup DH", __func__);
goto fail;
@@ -4102,8 +4182,9 @@ ikev2_sa_initiator(struct iked *env, str
}
int
-ikev2_sa_responder_dh(struct iked_kex *kex, struct iked_proposals *proposals,
- struct iked_message *msg, unsigned int proto)
+ikev2_sa_responder_dh(struct iked *env, struct iked_kex *kex,
+ struct iked_proposals *proposals, struct iked_message *msg,
+ unsigned int proto)
{
struct iked_transform *xform;
@@ -4134,6 +4215,19 @@ ikev2_sa_responder_dh(struct iked_kex *k
}
}
+ /* Look for dhgroup mismatch */
+ if (msg->msg_dhgroup != IKEV2_XFORMDH_NONE &&
+ msg->msg_dhgroup != kex->kex_dhgroup->id) {
+ log_debug("%s: want dh %s, KE has %s", __func__,
+ print_map(kex->kex_dhgroup->id, ikev2_xformdh_map),
+ print_map(msg->msg_dhgroup, ikev2_xformdh_map));
+ if (ikev2_resp_ike_invalid_ke(env, msg,
+ kex) != 0)
+ log_debug("%s: failed to send init response",
+ __func__);
+ return (-1);
+ }
+
if (!ibuf_length(kex->kex_dhiexchange)) {
if ((kex->kex_dhiexchange = ibuf_dup(msg->msg_ke)) == NULL ||
((ssize_t)ibuf_length(kex->kex_dhiexchange) !=
@@ -4226,7 +4320,7 @@ ikev2_sa_responder(struct iked *env, str
}
}
- if (ikev2_sa_responder_dh(&sa->sa_kex, &sa->sa_proposals, msg, 0) < 0)
+ if (ikev2_sa_responder_dh(env, &sa->sa_kex, &sa->sa_proposals, msg, 0)
< 0)
return (-1);
return (ikev2_sa_keys(env, sa, osa ? osa->sa_key_d : NULL));
Index: ikev2_pld.c
===================================================================
RCS file: /cvs/src/sbin/iked/ikev2_pld.c,v
retrieving revision 1.62
diff -u -p -r1.62 ikev2_pld.c
--- ikev2_pld.c 13 Apr 2017 07:04:09 -0000 1.62
+++ ikev2_pld.c 22 May 2017 05:29:18 -0000
@@ -680,6 +680,7 @@ ikev2_pld_ke(struct iked *env, struct ik
log_debug("%s: dh group %s reserved %d", __func__,
print_map(betoh16(kex.kex_dhgroup), ikev2_xformdh_map),
betoh16(kex.kex_reserved));
+ msg->msg_dhgroup = betoh16(kex.kex_dhgroup);
buf = msgbuf + offset + sizeof(kex);
len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(kex);