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.

-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      17 May 2017 05:21:24 -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     17 May 2017 05:21:24 -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,18 @@ ikev2_sa_responder_dh(struct iked_kex *k
                }
        }

+       /* Look for dhgroup mismatch */
+       if (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 +4319,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 17 May 2017 05:21:24 -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);

Reply via email to