initial support for PFS; ok reyk@
authormarkus <markus@openbsd.org>
Tue, 6 May 2014 07:24:37 +0000 (07:24 +0000)
committermarkus <markus@openbsd.org>
Tue, 6 May 2014 07:24:37 +0000 (07:24 +0000)
sbin/iked/config.c
sbin/iked/iked.h
sbin/iked/ikev2.c
sbin/iked/ikev2_pld.c

index 987f5f2..5c33685 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: config.c,v 1.27 2014/04/22 12:00:03 reyk Exp $        */
+/*     $OpenBSD: config.c,v 1.28 2014/05/06 07:24:37 markus Exp $      */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -339,7 +339,8 @@ config_add_transform(struct iked_proposal *prop, u_int type,
 }
 
 struct iked_transform *
-config_findtransform(struct iked_proposals *props, u_int8_t type)
+config_findtransform(struct iked_proposals *props, u_int8_t type,
+    u_int proto)
 {
        struct iked_proposal    *prop;
        struct iked_transform   *xform;
@@ -347,6 +348,9 @@ config_findtransform(struct iked_proposals *props, u_int8_t type)
 
        /* Search of the first transform with the desired type */
        TAILQ_FOREACH(prop, props, prop_entry) {
+               /* Find any proposal or only selected SA proto */
+               if (proto != 0 && prop->prop_protoid != proto)
+                       continue;
                for (i = 0; i < prop->prop_nxforms; i++) {
                        xform = prop->prop_xforms + i;
                        if (xform->xform_type == type)
index 0e77aea..59321ae 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: iked.h,v 1.74 2014/05/06 07:08:10 markus Exp $        */
+/*     $OpenBSD: iked.h,v 1.75 2014/05/06 07:24:37 markus Exp $        */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -411,6 +411,8 @@ struct iked_sa {
        struct iked_childsas             sa_childsas;   /* IPSec Child SAs */
        struct iked_saflows              sa_flows;      /* IPSec flows */
 
+       u_int64_t                        sa_rekeyspi;   /* peerspi for rekey*/
+
        u_int8_t                         sa_ipcomp;     /* IPcomp transform */
        u_int16_t                        sa_cpi_out;    /* IPcomp outgoing */
        u_int16_t                        sa_cpi_in;     /* IPcomp incoming*/
@@ -606,7 +608,7 @@ struct iked_user *
 u_int64_t
         config_getspi(void);
 struct iked_transform *
-        config_findtransform(struct iked_proposals *, u_int8_t);
+        config_findtransform(struct iked_proposals *, u_int8_t, u_int);
 void    config_free_policy(struct iked *, struct iked_policy *);
 struct iked_proposal *
         config_add_proposal(struct iked_proposals *, u_int, u_int);
index 4756957..fd8285a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ikev2.c,v 1.103 2014/05/06 07:08:10 markus Exp $      */
+/*     $OpenBSD: ikev2.c,v 1.104 2014/05/06 07:24:37 markus Exp $      */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -82,10 +82,14 @@ int  ikev2_sa_initiator(struct iked *, struct iked_sa *,
            struct iked_message *);
 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);
+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     ikev2_childsa_negotiate(struct iked *, struct iked_sa *, int, int);
 int     ikev2_match_proposals(struct iked_proposal *, struct iked_proposal *,
            struct iked_transform **);
 int     ikev2_valid_proposal(struct iked_proposal *,
@@ -779,6 +783,7 @@ ikev2_init_ike_sa_peer(struct iked *env, struct iked_policy *pol,
                return (-1);
 
        /* Pick peer's DH group if asked */
+       /* XXX free old sa_dhgroup ? */
        sa->sa_dhgroup = pol->pol_peerdh;
 
        if (ikev2_sa_initiator(env, sa, NULL) == -1)
@@ -1069,7 +1074,7 @@ ikev2_init_done(struct iked *env, struct iked_sa *sa)
        if (!sa_stateok(sa, IKEV2_STATE_VALID))
                return (0);     /* ignored */
 
-       ret = ikev2_childsa_negotiate(env, sa, sa->sa_hdr.sh_initiator);
+       ret = ikev2_childsa_negotiate(env, sa, sa->sa_hdr.sh_initiator, 0);
        if (ret == 0)
                ret = ikev2_childsa_enable(env, sa);
        if (ret == 0) {
@@ -2088,7 +2093,7 @@ ikev2_resp_ike_auth(struct iked *env, struct iked_sa *sa)
        if (ikev2_cp_setaddr(env, sa) < 0)
                return (-1);
 
-       if (ikev2_childsa_negotiate(env, sa, sa->sa_hdr.sh_initiator) == -1)
+       if (ikev2_childsa_negotiate(env, sa, sa->sa_hdr.sh_initiator, 0) == -1)
                return (-1);
 
        /* New encrypted message buffer */
@@ -2332,13 +2337,43 @@ ikev2_send_ike_e(struct iked *env, struct iked_sa *sa, struct ibuf *buf,
        return (ret);
 }
 
+int
+ikev2_set_sa_proposal(struct iked_sa *sa, struct iked_policy *pol,
+    u_int proto)
+{
+       struct iked_proposal            *prop, *copy;
+       struct iked_transform           *xform;
+       u_int                            i;
+
+       /* create copy of the policy proposals */
+       config_free_proposals(&sa->sa_proposals, proto);
+       TAILQ_FOREACH(prop, &pol->pol_proposals, prop_entry) {
+               if (proto != 0 && prop->prop_protoid != proto)
+                       continue;
+               if ((copy = config_add_proposal(&sa->sa_proposals,
+                   prop->prop_id, prop->prop_protoid)) == NULL)
+                       return (-1);
+               for (i = 0; i < prop->prop_nxforms; i++) {
+                       xform = &prop->prop_xforms[i];
+                       if (config_add_transform(copy, xform->xform_type,
+                           xform->xform_id, xform->xform_length,
+                           xform->xform_keylength) == NULL)
+                               return (-1);
+               }
+       }
+       return (0);
+}
+
 int
 ikev2_send_create_child_sa(struct iked *env, struct iked_sa *sa,
     struct iked_spi *rekey, u_int8_t protoid)
 {
+       struct iked_policy              *pol = sa->sa_policy;
        struct iked_childsa             *csa = NULL, *csb = NULL;
        struct ikev2_notify             *n;
        struct ikev2_payload            *pld = NULL;
+       struct ikev2_keyexchange        *ke;
+       struct group                    *group;
        struct ibuf                     *e = NULL, *nonce = NULL;
        u_int8_t                        *ptr;
        u_int8_t                         firstpayload;
@@ -2353,6 +2388,7 @@ ikev2_send_create_child_sa(struct iked *env, struct iked_sa *sa,
        else
                log_debug("%s: creating new CHILD SAs", __func__);
 
+       sa->sa_rekeyspi = 0;    /* clear rekey spi */
        initiator = sa->sa_hdr.sh_initiator ? 1 : 0;
 
        if (rekey &&
@@ -2361,6 +2397,7 @@ ikev2_send_create_child_sa(struct iked *env, struct iked_sa *sa,
            (csb = csa->csa_peersa) == NULL)) {
                log_debug("%s: CHILD SA %s wasn't found", __func__,
                    print_spi(rekey->spi, rekey->spi_size));
+               goto done;
        }
 
        /* Generate new nonce */
@@ -2375,7 +2412,7 @@ ikev2_send_create_child_sa(struct iked *env, struct iked_sa *sa,
                goto done;
 
        /* compression */
-       if ((sa->sa_policy->pol_flags & IKED_POLICY_IPCOMP) &&
+       if ((pol->pol_flags & IKED_POLICY_IPCOMP) &&
            (len = ikev2_add_ipcompnotify(env, e, &pld, 0, sa)) == -1)
                goto done;
 
@@ -2389,6 +2426,17 @@ ikev2_send_create_child_sa(struct iked *env, struct iked_sa *sa,
        /* SA payload */
        if ((pld = ikev2_add_payload(e)) == NULL)
                goto done;
+
+       /*
+        * We need to reset the sa_proposal. Otherwise it would be
+        * left over from the IKE_AUTH exchange and would not contain
+        * any DH groups (e.g. for ESP child SAs).
+        */
+       if (ikev2_set_sa_proposal(sa, pol, protoid) < 0) {
+               log_debug("%s: ikev2_set_sa_proposal failed", __func__);
+               goto done;
+       }
+
        if ((len = ikev2_add_proposals(env, sa, e, &sa->sa_proposals,
            protoid, 1, 0)) == -1)
                goto done;
@@ -2403,6 +2451,32 @@ ikev2_send_create_child_sa(struct iked *env, struct iked_sa *sa,
                goto done;
        len = ibuf_size(nonce);
 
+       if (config_findtransform(&pol->pol_proposals, IKEV2_XFORMTYPE_DH,
+           protoid)) {
+               log_debug("%s: enable PFS", __func__);
+               ikev2_sa_cleanup_dh(sa);
+               if (ikev2_sa_initiator_dh(sa, NULL, protoid) < 0) {
+                       log_debug("%s: failed to setup DH", __func__);
+                       goto done;
+               }
+               if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_KE) == -1)
+                       goto done;
+
+               /* KE payload */
+               if ((pld = ikev2_add_payload(e)) == NULL)
+                       goto done;
+               if ((ke = ibuf_advance(e, sizeof(*ke))) == NULL)
+                       goto done;
+               if ((group = sa->sa_dhgroup) == NULL) {
+                       log_debug("%s: invalid dh", __func__);
+                       goto done;
+               }
+               ke->kex_dhgroup = htobe16(group->id);
+               if (ikev2_add_buf(e, sa->sa_dhiexchange) == -1)
+                       goto done;
+               len = sizeof(*ke) + dh_getlen(group);
+       }
+
        if ((len = ikev2_add_ts(e, &pld, len, sa, !initiator)) == -1)
                goto done;
 
@@ -2435,6 +2509,11 @@ ikev2_send_create_child_sa(struct iked *env, struct iked_sa *sa,
                if (rekey) {
                        csa->csa_rekey = 1;
                        csb->csa_rekey = 1;
+                       /*
+                        * Remember the peer spi of the rekeyed
+                        * SA for ikev2_init_create_child_sa().
+                        */
+                       sa->sa_rekeyspi = csa->csa_peerspi;
                }
                sa->sa_stateflags |= IKED_REQ_CHILDSA;
        }
@@ -2447,25 +2526,30 @@ done:
 int
 ikev2_init_create_child_sa(struct iked *env, struct iked_message *msg)
 {
-       struct iked_childsa             *csa;
+       struct iked_childsa             *csa = NULL;
        struct iked_proposal            *prop;
        struct iked_sa                  *sa = msg->msg_sa;
        struct ikev2_delete             *del;
        struct ibuf                     *buf = NULL;
-       u_int64_t                        peerspi;
        u_int32_t                        spi32;
-       int                              rekeying = 0, ret = -1;
+       int                              pfs = 0, ret = -1;
 
        if (!ikev2_msg_frompeer(msg) ||
            (sa->sa_stateflags & IKED_REQ_CHILDSA) == 0)
                return (0);
 
-       if (msg->msg_prop == NULL) {
+       if (msg->msg_prop == NULL ||
+           TAILQ_EMPTY(&msg->msg_proposals)) {
                log_debug("%s: no proposal specified", __func__);
                return (-1);
        }
 
-       /* Update peer SPI */
+       if (ikev2_sa_negotiate(sa, &sa->sa_proposals,
+           &msg->msg_proposals) != 0) {
+               log_debug("%s: no proposal chosen", __func__);
+               return (-1);
+       }
+
        TAILQ_FOREACH(prop, &sa->sa_proposals, prop_entry) {
                if (prop->prop_protoid == msg->msg_prop->prop_protoid)
                        break;
@@ -2476,13 +2560,29 @@ ikev2_init_create_child_sa(struct iked *env, struct iked_message *msg)
                return (-1);
        }
 
-       peerspi = prop->prop_peerspi.spi;
-       prop->prop_peerspi.spi = msg->msg_prop->prop_peerspi.spi;
+       if (sa->sa_rekeyspi &&
+           (csa = childsa_lookup(sa, sa->sa_rekeyspi, prop->prop_protoid))
+           != NULL) {
+               log_debug("%s: rekeying CHILD SA old %s spi %s", __func__,
+                   print_spi(csa->csa_spi.spi, csa->csa_spi.spi_size),
+                   print_spi(prop->prop_peerspi.spi,
+                   prop->prop_peerspi.spi_size));
+       }
 
-       if ((csa = childsa_lookup(sa, peerspi, prop->prop_protoid)) != NULL) {
-               rekeying = 1;
-               log_debug("%s: rekeying CHILD SA %s", __func__,
-                   print_spi(peerspi, prop->prop_peerspi.spi_size));
+       /* check KE payload for PFS */
+       if (ibuf_length(msg->msg_ke)) {
+               log_debug("%s: using PFS", __func__);
+               if (ikev2_sa_initiator_dh(sa, msg, prop->prop_protoid) < 0) {
+                       log_debug("%s: failed to setup DH", __func__);
+                       return (ret);
+               }
+               if (sa->sa_dhpeer == NULL) {
+                       log_debug("%s: no peer DH", __func__);
+                       return (ret);
+               }
+               pfs = 1;
+               /* XXX check group against policy ? */
+               /* XXX should ikev2_sa_negotiate do this? */
        }
 
        /* Update responder's nonce */
@@ -2493,12 +2593,12 @@ ikev2_init_create_child_sa(struct iked *env, struct iked_message *msg)
        ibuf_release(sa->sa_rnonce);
        sa->sa_rnonce = ibuf_dup(msg->msg_nonce);
 
-       if (ikev2_childsa_negotiate(env, sa, 1)) {
+       if (ikev2_childsa_negotiate(env, sa, 1, pfs)) {
                log_debug("%s: failed to get CHILD SAs", __func__);
                return (-1);
        }
 
-       if (rekeying) {
+       if (csa) {
                /* Child SA rekeying */
 
                if ((buf = ibuf_static()) == NULL)
@@ -2542,16 +2642,14 @@ ikev2_resp_create_child_sa(struct iked *env, struct iked_message *msg)
        struct iked_sa                  *nsa = NULL, *sa = msg->msg_sa;
        struct iked_spi                 *spi, *rekey = &msg->msg_rekey;
        struct ikev2_keyexchange        *ke;
-       struct ikev2_notify             *n;
        struct ikev2_payload            *pld = NULL;
-       struct ibuf                     *buf = NULL, *e = NULL, *nonce = NULL;
+       struct ibuf                     *e = NULL, *nonce = NULL;
        struct group                    *group;
-       u_int64_t                        spi64;
-       u_int32_t                        spi32;
        u_int8_t                         firstpayload;
        ssize_t                          len = 0;
        int                              initiator, protoid, rekeying = 1;
        int                              ret = -1;
+       int                              pfs = 0;
 
        initiator = sa->sa_hdr.sh_initiator ? 1 : 0;
 
@@ -2606,31 +2704,17 @@ ikev2_resp_create_child_sa(struct iked *env, struct iked_message *msg)
        } else {
                /* Child SA creating/rekeying */
 
+               /* check KE payload for PFS */
                if (ibuf_length(msg->msg_parent->msg_ke)) {
-                       /* We do not support KE payload yet */
-                       if ((buf = ibuf_static()) == NULL)
-                               return (-1);
-                       if ((n = ibuf_advance(buf, sizeof(*n))) == NULL) {
-                               ibuf_release(buf);
-                               return (-1);
-                       }
-                       n->n_protoid = protoid;
-                       n->n_spisize = rekey->spi_size;
-                       n->n_type = htobe16(IKEV2_N_NO_ADDITIONAL_SAS);
-                       switch (rekey->spi_size) {
-                       case 4:
-                               spi32 = htobe32(rekey->spi);
-                               ibuf_add(buf, &spi32, rekey->spi_size);
-                               break;
-                       case 8:
-                               spi64 = htobe64(rekey->spi);
-                               ibuf_add(buf, &spi64, rekey->spi_size);
-                               break;
+                       log_debug("%s: using PFS", __func__);
+                       ikev2_sa_cleanup_dh(sa);
+                       if (ikev2_sa_responder_dh(sa, msg->msg_parent,
+                           protoid) < 0) {
+                               log_debug("%s: failed to setup DH", __func__);
+                               return (ret);
                        }
-                       ikev2_send_ike_e(env, sa, buf, IKEV2_PAYLOAD_NOTIFY,
-                           IKEV2_EXCHANGE_CREATE_CHILD_SA, 1);
-                       ibuf_release(buf);
-                       return (0);
+                       pfs = 1;
+                       /* XXX check group against policy ? */
                }
 
                /* Update peer SPI */
@@ -2680,7 +2764,7 @@ ikev2_resp_create_child_sa(struct iked *env, struct iked_message *msg)
                ibuf_release(sa->sa_rnonce);
                sa->sa_rnonce = nonce;
 
-               if (ikev2_childsa_negotiate(env, sa, 0)) {
+               if (ikev2_childsa_negotiate(env, sa, 0, pfs)) {
                        log_debug("%s: failed to get CHILD SAs", __func__);
                        return (-1);
                }
@@ -2720,7 +2804,8 @@ ikev2_resp_create_child_sa(struct iked *env, struct iked_message *msg)
                goto done;
        len = ibuf_size(nonce);
 
-       if (protoid == IKEV2_SAPROTO_IKE) {
+       if (protoid == IKEV2_SAPROTO_IKE || pfs) {
+
                if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_KE) == -1)
                        goto done;
 
@@ -2729,18 +2814,21 @@ ikev2_resp_create_child_sa(struct iked *env, struct iked_message *msg)
                        goto done;
                if ((ke = ibuf_advance(e, sizeof(*ke))) == NULL)
                        goto done;
-               if ((group = nsa->sa_dhgroup) == NULL) {
+               if ((group = nsa ?
+                   nsa->sa_dhgroup : sa->sa_dhgroup) == NULL) {
                        log_debug("%s: invalid dh", __func__);
                        goto done;
                }
                ke->kex_dhgroup = htobe16(group->id);
-               if (ikev2_add_buf(e, nsa->sa_dhrexchange) == -1)
+               if (ikev2_add_buf(e, nsa ?
+                   nsa->sa_dhrexchange : sa->sa_dhrexchange) == -1)
                        goto done;
                len = sizeof(*ke) + dh_getlen(group);
-       } else {
+       }
+
+       if (protoid != IKEV2_SAPROTO_IKE)
                if ((len = ikev2_add_ts(e, &pld, len, sa, initiator)) == -1)
                        goto done;
-       }
 
        if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1)
                goto done;
@@ -3065,12 +3153,13 @@ ikev2_sa_negotiate(struct iked_sa *sa, struct iked_proposals *local,
     struct iked_proposals *peer)
 {
        struct iked_proposal    *ppeer = NULL, *plocal, *prop, vpeer, vlocal;
-       struct iked_transform   *chosen[IKEV2_XFORMTYPE_MAX];
+       struct iked_transform    chosen[IKEV2_XFORMTYPE_MAX];
+       struct iked_transform   *valid[IKEV2_XFORMTYPE_MAX];
        struct iked_transform   *match[IKEV2_XFORMTYPE_MAX];
        u_int                    i, score, chosen_score = 0;
        u_int8_t                 protoid = 0;
 
-       bzero(chosen, sizeof(chosen));
+       bzero(valid, sizeof(valid));
        bzero(&vlocal, sizeof(vlocal));
        bzero(&vpeer, sizeof(vpeer));
 
@@ -3089,8 +3178,11 @@ ikev2_sa_negotiate(struct iked_sa *sa, struct iked_proposals *local,
                        log_debug("%s: score %d", __func__, score);
                        if (score && (!chosen_score || score < chosen_score)) {
                                chosen_score = score;
-                               for (i = 0; i < IKEV2_XFORMTYPE_MAX; i++)
-                                       chosen[i] = match[i];
+                               for (i = 0; i < IKEV2_XFORMTYPE_MAX; i++) {
+                                       if ((valid[i] = match[i]))
+                                               memcpy(&chosen[i], match[i],
+                                                   sizeof(chosen[0]));
+                               }
                                memcpy(&vpeer, ppeer, sizeof(vpeer));
                                memcpy(&vlocal, plocal, sizeof(vlocal));
                        }
@@ -3118,18 +3210,18 @@ ikev2_sa_negotiate(struct iked_sa *sa, struct iked_proposals *local,
        }
 
        for (i = 0; i < IKEV2_XFORMTYPE_MAX; i++) {
-               if (chosen[i] == NULL)
+               if (valid[i] == NULL)
                        continue;
                print_debug("%s: score %d: %s %s", __func__,
-                   chosen[i]->xform_score, print_map(i, ikev2_xformtype_map),
-                   print_map(chosen[i]->xform_id, chosen[i]->xform_map));
-               if (chosen[i]->xform_length)
-                       print_debug(" %d", chosen[i]->xform_length);
+                   chosen[i].xform_score, print_map(i, ikev2_xformtype_map),
+                   print_map(chosen[i].xform_id, chosen[i].xform_map));
+               if (chosen[i].xform_length)
+                       print_debug(" %d", chosen[i].xform_length);
                print_debug("\n");
 
-               if (config_add_transform(prop, chosen[i]->xform_type,
-                   chosen[i]->xform_id, chosen[i]->xform_length,
-                   chosen[i]->xform_keylength) == NULL)
+               if (config_add_transform(prop, chosen[i].xform_type,
+                   chosen[i].xform_id, chosen[i].xform_length,
+                   chosen[i].xform_keylength) == NULL)
                        break;
        }
 
@@ -3137,15 +3229,14 @@ ikev2_sa_negotiate(struct iked_sa *sa, struct iked_proposals *local,
 }
 
 int
-ikev2_sa_initiator(struct iked *env, struct iked_sa *sa,
-    struct iked_message *msg)
+ikev2_sa_initiator_dh(struct iked_sa *sa, struct iked_message *msg, u_int proto)
 {
        struct iked_policy      *pol = sa->sa_policy;
        struct iked_transform   *xform;
 
        if (sa->sa_dhgroup == NULL) {
                if ((xform = config_findtransform(&pol->pol_proposals,
-                   IKEV2_XFORMTYPE_DH)) == NULL) {
+                   IKEV2_XFORMTYPE_DH, proto)) == NULL) {
                        log_debug("%s: did not find dh transform", __func__);
                        return (-1);
                }
@@ -3170,28 +3261,10 @@ ikev2_sa_initiator(struct iked *env, struct iked_sa *sa,
                }
        }
 
-       if (!ibuf_length(sa->sa_inonce)) {
-               if ((sa->sa_inonce = ibuf_random(IKED_NONCE_SIZE)) == NULL) {
-                       log_debug("%s: failed to get local nonce", __func__);
-                       return (-1);
-               }
-       }
-
        /* Initial message */
        if (msg == NULL)
                return (0);
 
-       if (!ibuf_length(sa->sa_rnonce)) {
-               if (!ibuf_length(msg->msg_nonce)) {
-                       log_debug("%s: invalid peer nonce", __func__);
-                       return (-1);
-               }
-               if ((sa->sa_rnonce = ibuf_dup(msg->msg_nonce)) == NULL) {
-                       log_debug("%s: failed to get peer nonce", __func__);
-                       return (-1);
-               }
-       }
-
        if (!ibuf_length(sa->sa_dhrexchange)) {
                if (!ibuf_length(msg->msg_ke)) {
                        log_debug("%s: invalid peer dh exchange", __func__);
@@ -3211,6 +3284,39 @@ ikev2_sa_initiator(struct iked *env, struct iked_sa *sa,
 
        /* Set a pointer to the peer exchange */
        sa->sa_dhpeer = sa->sa_dhrexchange;
+       return (0);
+}
+
+int
+ikev2_sa_initiator(struct iked *env, struct iked_sa *sa,
+    struct iked_message *msg)
+{
+       struct iked_transform   *xform;
+
+       if (ikev2_sa_initiator_dh(sa, msg, 0) < 0)
+               return (-1);
+
+       if (!ibuf_length(sa->sa_inonce)) {
+               if ((sa->sa_inonce = ibuf_random(IKED_NONCE_SIZE)) == NULL) {
+                       log_debug("%s: failed to get local nonce", __func__);
+                       return (-1);
+               }
+       }
+
+       /* Initial message */
+       if (msg == NULL)
+               return (0);
+
+       if (!ibuf_length(sa->sa_rnonce)) {
+               if (!ibuf_length(msg->msg_nonce)) {
+                       log_debug("%s: invalid peer nonce", __func__);
+                       return (-1);
+               }
+               if ((sa->sa_rnonce = ibuf_dup(msg->msg_nonce)) == NULL) {
+                       log_debug("%s: failed to get peer nonce", __func__);
+                       return (-1);
+               }
+       }
 
        /* XXX we need a better way to get this */
        if (ikev2_sa_negotiate(sa, &msg->msg_policy->pol_proposals,
@@ -3223,7 +3329,7 @@ ikev2_sa_initiator(struct iked *env, struct iked_sa *sa,
 
        if (sa->sa_encr == NULL) {
                if ((xform = config_findtransform(&sa->sa_proposals,
-                   IKEV2_XFORMTYPE_ENCR)) == NULL) {
+                   IKEV2_XFORMTYPE_ENCR, 0)) == NULL) {
                        log_debug("%s: did not find encr transform", __func__);
                        return (-1);
                }
@@ -3236,7 +3342,7 @@ ikev2_sa_initiator(struct iked *env, struct iked_sa *sa,
 
        if (sa->sa_prf == NULL) {
                if ((xform = config_findtransform(&sa->sa_proposals,
-                   IKEV2_XFORMTYPE_PRF)) == NULL) {
+                   IKEV2_XFORMTYPE_PRF, 0)) == NULL) {
                        log_debug("%s: did not find prf transform", __func__);
                        return (-1);
                }
@@ -3249,7 +3355,7 @@ ikev2_sa_initiator(struct iked *env, struct iked_sa *sa,
 
        if (sa->sa_integr == NULL) {
                if ((xform = config_findtransform(&sa->sa_proposals,
-                   IKEV2_XFORMTYPE_INTEGR)) == NULL) {
+                   IKEV2_XFORMTYPE_INTEGR, 0)) == NULL) {
                        log_debug("%s: did not find integr transform",
                            __func__);
                        return (-1);
@@ -3270,6 +3376,53 @@ ikev2_sa_initiator(struct iked *env, struct iked_sa *sa,
        return (ikev2_sa_keys(env, sa, NULL));
 }
 
+int
+ikev2_sa_responder_dh(struct iked_sa *sa, struct iked_message *msg, u_int proto)
+{
+       struct iked_transform   *xform;
+
+       if (sa->sa_dhgroup == NULL) {
+               if ((xform = config_findtransform(&sa->sa_proposals,
+                   IKEV2_XFORMTYPE_DH, proto)) == NULL) {
+                       log_debug("%s: did not find dh transform", __func__);
+                       return (-1);
+               }
+               if ((sa->sa_dhgroup =
+                   group_get(xform->xform_id)) == NULL) {
+                       log_debug("%s: invalid dh %d", __func__,
+                           xform->xform_id);
+                       return (-1);
+               }
+       }
+
+       if (!ibuf_length(sa->sa_dhrexchange)) {
+               if ((sa->sa_dhrexchange = ibuf_new(NULL,
+                   dh_getlen(sa->sa_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) {
+                       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))) {
+                       /* XXX send notification to peer */
+                       log_debug("%s: invalid dh, size %d", __func__,
+                           dh_getlen(sa->sa_dhgroup) * 8);
+                       return (-1);
+               }
+       }
+
+       /* Set a pointer to the peer exchange */
+       sa->sa_dhpeer = sa->sa_dhiexchange;
+       return (0);
+}
 int
 ikev2_sa_responder(struct iked *env, struct iked_sa *sa, struct iked_sa *osa,
     struct iked_message *msg)
@@ -3308,7 +3461,7 @@ ikev2_sa_responder(struct iked *env, struct iked_sa *sa, struct iked_sa *osa,
 
        if (sa->sa_encr == NULL) {
                if ((xform = config_findtransform(&sa->sa_proposals,
-                   IKEV2_XFORMTYPE_ENCR)) == NULL) {
+                   IKEV2_XFORMTYPE_ENCR, 0)) == NULL) {
                        log_debug("%s: did not find encr transform", __func__);
                        return (-1);
                }
@@ -3321,7 +3474,7 @@ ikev2_sa_responder(struct iked *env, struct iked_sa *sa, struct iked_sa *osa,
 
        if (sa->sa_prf == NULL) {
                if ((xform = config_findtransform(&sa->sa_proposals,
-                   IKEV2_XFORMTYPE_PRF)) == NULL) {
+                   IKEV2_XFORMTYPE_PRF, 0)) == NULL) {
                        log_debug("%s: did not find prf transform", __func__);
                        return (-1);
                }
@@ -3334,7 +3487,7 @@ ikev2_sa_responder(struct iked *env, struct iked_sa *sa, struct iked_sa *osa,
 
        if (sa->sa_integr == NULL) {
                if ((xform = config_findtransform(&sa->sa_proposals,
-                   IKEV2_XFORMTYPE_INTEGR)) == NULL) {
+                   IKEV2_XFORMTYPE_INTEGR, 0)) == NULL) {
                        log_debug("%s: did not find integr transform",
                            __func__);
                        return (-1);
@@ -3346,45 +3499,8 @@ ikev2_sa_responder(struct iked *env, struct iked_sa *sa, struct iked_sa *osa,
                }
        }
 
-       if (sa->sa_dhgroup == NULL) {
-               if ((xform = config_findtransform(&sa->sa_proposals,
-                   IKEV2_XFORMTYPE_DH)) == NULL) {
-                       log_debug("%s: did not find dh transform", __func__);
-                       return (-1);
-               }
-               if ((sa->sa_dhgroup =
-                   group_get(xform->xform_id)) == NULL) {
-                       log_debug("%s: invalid dh", __func__);
-                       return (-1);
-               }
-       }
-
-       if (!ibuf_length(sa->sa_dhrexchange)) {
-               if ((sa->sa_dhrexchange = ibuf_new(NULL,
-                   dh_getlen(sa->sa_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) {
-                       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))) {
-                       /* XXX send notification to peer */
-                       log_debug("%s: invalid dh, size %d", __func__,
-                           dh_getlen(sa->sa_dhgroup) * 8);
-                       return (-1);
-               }
-       }
-
-       /* Set a pointer to the peer exchange */
-       sa->sa_dhpeer = sa->sa_dhiexchange;
+       if (ikev2_sa_responder_dh(sa, msg, 0) < 0)
+               return (-1);
 
        return (ikev2_sa_keys(env, sa, osa ? osa->sa_key_d : NULL));
 }
@@ -3592,6 +3708,17 @@ ikev2_sa_keys(struct iked *env, struct iked_sa *sa, struct ibuf *key)
        return (ret);
 }
 
+void
+ikev2_sa_cleanup_dh(struct iked_sa *sa)
+{
+       ibuf_release(sa->sa_dhiexchange);
+       ibuf_release(sa->sa_dhrexchange);
+       group_free(sa->sa_dhgroup);
+       sa->sa_dhiexchange = NULL;
+       sa->sa_dhrexchange = NULL;
+       sa->sa_dhgroup = NULL;
+}
+
 struct ibuf *
 ikev2_prfplus(struct iked_hash *prf, struct ibuf *key, struct ibuf *seed,
     size_t keymatlen)
@@ -3749,14 +3876,16 @@ ikev2_sa_tag(struct iked_sa *sa, struct iked_id *id)
 }
 
 int
-ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa, int initiator)
+ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa, int initiator,
+    int pfs)
 {
        struct iked_proposal    *prop;
        struct iked_transform   *xform, *encrxf = NULL, *integrxf = NULL;
        struct iked_childsa     *csa, *csb;
        struct iked_flow        *flow, *saflow, *flowa, *flowb;
        struct iked_id          *peerid, *localid;
-       struct ibuf             *keymat = NULL, *seed = NULL;
+       struct ibuf             *keymat = NULL, *seed = NULL, *dhsecret = NULL;
+       struct group            *group;
        u_int32_t                spi = 0;
        u_int                    i;
        size_t                   ilen = 0;
@@ -3807,7 +3936,35 @@ ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa, int initiator)
 
        log_debug("%s: key material length %zu", __func__, ilen);
 
-       if ((seed = ibuf_dup(sa->sa_inonce)) == NULL ||
+       if ((seed = ibuf_new(NULL, 0)) == NULL) {
+               log_debug("%s: failed to setup IKE SA key material", __func__);
+               goto done;
+       }
+       if (pfs) {
+               log_debug("%s: using PFS", __func__);
+               if (sa->sa_dhpeer == NULL || ibuf_length(sa->sa_dhpeer) == 0 ||
+                   (group = sa->sa_dhgroup) == NULL) {
+                       log_debug("%s: no dh group for pfs", __func__);
+                       goto done;
+               }
+               if ((dhsecret = ibuf_new(NULL, dh_getlen(group))) == NULL) {
+                       log_debug("%s: failed to alloc dh secret", __func__);
+                       goto done;
+               }
+               if (dh_create_shared(group, dhsecret->buf,
+                   sa->sa_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));
+                       goto done;
+               }
+               if (ibuf_cat(seed, dhsecret) != 0) {
+                       log_debug("%s: failed to set dh secret", __func__);
+                       goto done;
+               }
+       }
+       if (ibuf_cat(seed, sa->sa_inonce) != 0 ||
            ibuf_cat(seed, sa->sa_rnonce) != 0 ||
            (keymat = ikev2_prfplus(sa->sa_prf,
            sa->sa_key_d, seed, ilen)) == NULL) {
@@ -3973,6 +4130,7 @@ ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa, int initiator)
 
        ret = 0;
  done:
+       ibuf_release(dhsecret);
        ibuf_release(keymat);
        ibuf_release(seed);
 
index d725804..6f11e77 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ikev2_pld.c,v 1.41 2014/05/05 15:21:20 markus Exp $   */
+/*     $OpenBSD: ikev2_pld.c,v 1.42 2014/05/06 07:24:37 markus Exp $   */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -1157,6 +1157,7 @@ ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld,
                            " (%zu != %zu)", __func__, len, sizeof(group));
                        return (-1);
                }
+               /* XXX chould also happen for PFS */
                if (!msg->msg_sa->sa_hdr.sh_initiator) {
                        log_debug("%s: not an initiator", __func__);
                        sa_free(env, msg->msg_sa);
@@ -1175,6 +1176,7 @@ ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld,
                    group);
                sa_free(env, msg->msg_sa);
                msg->msg_sa = NULL;
+               /* XXX chould also happen for PFS so we have to check state XXX*/
                timer_set(env, &env->sc_inittmr, ikev2_init_ike_sa, NULL);
                timer_add(env, &env->sc_inittmr, IKED_INITIATOR_INITIAL);
                break;