initiate ike sa rekeying (ikesalifetime keyword), re-queue pfkey
authormarkus <markus@openbsd.org>
Tue, 6 May 2014 10:24:22 +0000 (10:24 +0000)
committermarkus <markus@openbsd.org>
Tue, 6 May 2014 10:24:22 +0000 (10:24 +0000)
events while we are busy initiating child-SAs; ok mikeb@

sbin/iked/config.c
sbin/iked/iked.conf.5
sbin/iked/iked.h
sbin/iked/ikev2.c
sbin/iked/ikev2.h
sbin/iked/ikev2_msg.c
sbin/iked/ikev2_pld.c
sbin/iked/parse.y
sbin/iked/pfkey.c
sbin/iked/policy.c

index 1c6ee7c..00f0b9d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: config.c,v 1.29 2014/05/06 09:48:40 markus Exp $      */
+/*     $OpenBSD: config.c,v 1.30 2014/05/06 10:24:22 markus Exp $      */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -79,6 +79,7 @@ void
 config_free_sa(struct iked *env, struct iked_sa *sa)
 {
        timer_del(env, &sa->sa_timer);
+       timer_del(env, &sa->sa_rekey);
 
        config_free_proposals(&sa->sa_proposals, 0);
        config_free_childsas(env, &sa->sa_childsas, NULL, NULL);
index 0428f89..efaa518 100644 (file)
@@ -1,4 +1,4 @@
-.\" $OpenBSD: iked.conf.5,v 1.30 2014/04/28 16:23:19 jmc Exp $
+.\" $OpenBSD: iked.conf.5,v 1.31 2014/05/06 10:24:22 markus Exp $
 .\"
 .\" Copyright (c) 2010 - 2014 Reyk Floeter <reyk@openbsd.org>
 .\" Copyright (c) 2004 Mathieu Sauve-Frankel  All rights reserved.
@@ -15,7 +15,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: April 28 2014 $
+.Dd $Mdocdate: May 6 2014 $
 .Dt IKED.CONF 5
 .Os
 .Sh NAME
@@ -435,6 +435,14 @@ is similar to
 .Ic srcid ,
 but instead specifies the ID to be used
 by the remote peer.
+.It Ic ikelifetime Ar time
+The optional
+.Ic ikelifetime
+parameter defines the IKE SA expiration timeout by the
+.Ar time
+SA was in created.
+A zero value disables active IKE SA rekeying.
+This is the default.
 .It Ic lifetime Ar time Op Ic bytes Ar bytes
 The optional
 .Ic lifetime
index 59321ae..a386ded 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: iked.h,v 1.75 2014/05/06 07:24:37 markus Exp $        */
+/*     $OpenBSD: iked.h,v 1.76 2014/05/06 10:24:22 markus Exp $        */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -274,7 +274,8 @@ struct iked_policy {
        struct iked_cfg                  pol_cfg[IKED_CFG_MAX];
        u_int                            pol_ncfg;
 
-       struct iked_lifetime             pol_lifetime;
+       u_int32_t                        pol_rekey;     /* ike SA lifetime */
+       struct iked_lifetime             pol_lifetime;  /* child SA lifetime */
 
        struct iked_sapeers              pol_sapeers;
 
@@ -411,6 +412,7 @@ struct iked_sa {
        struct iked_childsas             sa_childsas;   /* IPSec Child SAs */
        struct iked_saflows              sa_flows;      /* IPSec flows */
 
+       struct iked_sa                  *sa_next;       /* IKE SA rekeying */
        u_int64_t                        sa_rekeyspi;   /* peerspi for rekey*/
 
        u_int8_t                         sa_ipcomp;     /* IPcomp transform */
@@ -418,9 +420,11 @@ struct iked_sa {
        u_int16_t                        sa_cpi_in;     /* IPcomp incoming*/
 
        struct iked_timer                sa_timer;      /* SA timeouts */
-#define IKED_IKE_SA_REKEY_TIMEOUT       300            /* 5 minutes */
+#define IKED_IKE_SA_DELETE_TIMEOUT      300            /* 5 minutes */
 #define IKED_IKE_SA_ALIVE_TIMEOUT       60             /* 1 minute */
 
+       struct iked_timer                sa_rekey;      /* rekey timeout */
+
        struct iked_msgqueue             sa_requests;   /* request queue */
 #define IKED_RETRANSMIT_TIMEOUT                 2              /* 2 seconds */
 
@@ -751,10 +755,10 @@ struct ikev2_payload *
         ikev2_add_payload(struct ibuf *);
 int     ikev2_next_payload(struct ikev2_payload *, size_t,
            u_int8_t);
-void    ikev2_acquire_sa(struct iked *, struct iked_flow *);
+int     ikev2_acquire_sa(struct iked *, struct iked_flow *);
 void    ikev2_disable_rekeying(struct iked *, struct iked_sa *);
-void    ikev2_rekey_sa(struct iked *, struct iked_spi *);
-void    ikev2_drop_sa(struct iked *, struct iked_spi *);
+int     ikev2_rekey_sa(struct iked *, struct iked_spi *);
+int     ikev2_drop_sa(struct iked *, struct iked_spi *);
 int     ikev2_print_id(struct iked_id *, char *, size_t);
 
 /* ikev2_msg.c */
index 02fdf02..c4fce4d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ikev2.c,v 1.106 2014/05/06 09:48:40 markus Exp $      */
+/*     $OpenBSD: ikev2.c,v 1.107 2014/05/06 10:24:22 markus Exp $      */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -73,13 +73,16 @@ int  ikev2_resp_ike_eap(struct iked *, struct iked_sa *, struct ibuf *);
 
 int     ikev2_send_create_child_sa(struct iked *, struct iked_sa *,
            struct iked_spi *, u_int8_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_init_create_child_sa(struct iked *, struct iked_message *);
 int     ikev2_resp_create_child_sa(struct iked *, struct iked_message *);
+void    ikev2_ike_sa_rekey(struct iked *, void *);
 void    ikev2_ike_sa_timeout(struct iked *env, void *);
 void    ikev2_ike_sa_alive(struct iked *, void *);
 
 int     ikev2_sa_initiator(struct iked *, struct iked_sa *,
-           struct iked_message *);
+           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);
@@ -787,7 +790,7 @@ ikev2_init_ike_sa_peer(struct iked *env, struct iked_policy *pol,
        /* XXX free old sa_dhgroup ? */
        sa->sa_dhgroup = pol->pol_peerdh;
 
-       if (ikev2_sa_initiator(env, sa, NULL) == -1)
+       if (ikev2_sa_initiator(env, sa, NULL, NULL) == -1)
                goto done;
 
        if (pol->pol_local.addr.ss_family == AF_UNSPEC) {
@@ -932,7 +935,7 @@ ikev2_init_auth(struct iked *env, struct iked_message *msg)
        if (sa == NULL)
                return (-1);
 
-       if (ikev2_sa_initiator(env, sa, msg) == -1) {
+       if (ikev2_sa_initiator(env, sa, NULL, msg) == -1) {
                log_debug("%s: failed to get IKE keys", __func__);
                return (-1);
        }
@@ -1082,6 +1085,9 @@ ikev2_init_done(struct iked *env, struct iked_sa *sa)
                sa_state(env, sa, IKEV2_STATE_ESTABLISHED);
                timer_set(env, &sa->sa_timer, ikev2_ike_sa_alive, sa);
                timer_add(env, &sa->sa_timer, IKED_IKE_SA_ALIVE_TIMEOUT);
+               timer_set(env, &sa->sa_rekey, ikev2_ike_sa_rekey, sa);
+               if (sa->sa_policy->pol_rekey)
+                       timer_add(env, &sa->sa_rekey, sa->sa_policy->pol_rekey);
        }
 
        if (ret)
@@ -2212,6 +2218,9 @@ ikev2_resp_ike_auth(struct iked *env, struct iked_sa *sa)
                sa_state(env, sa, IKEV2_STATE_ESTABLISHED);
                timer_set(env, &sa->sa_timer, ikev2_ike_sa_alive, sa);
                timer_add(env, &sa->sa_timer, IKED_IKE_SA_ALIVE_TIMEOUT);
+               timer_set(env, &sa->sa_rekey, ikev2_ike_sa_rekey, sa);
+               if (sa->sa_policy->pol_rekey)
+                       timer_add(env, &sa->sa_rekey, sa->sa_policy->pol_rekey);
        }
 
  done:
@@ -2389,6 +2398,13 @@ ikev2_send_create_child_sa(struct iked *env, struct iked_sa *sa,
        else
                log_debug("%s: creating new CHILD SAs", __func__);
 
+       /* XXX cannot initiate multiple concurrent CREATE_CHILD_SA exchanges */
+       if (sa->sa_stateflags & IKED_REQ_CHILDSA) {
+               log_debug("%s: another CREATE_CHILD_SA exchange already active",
+                   __func__);
+               return (-1);
+       }
+
        sa->sa_rekeyspi = 0;    /* clear rekey spi */
        initiator = sa->sa_hdr.sh_initiator ? 1 : 0;
 
@@ -2524,12 +2540,110 @@ done:
        return (ret);
 }
 
+void
+ikev2_ike_sa_rekey(struct iked *env, void *arg)
+{
+       struct iked_sa                  *sa = arg;
+       struct iked_sa                  *nsa = NULL;
+       struct ikev2_payload            *pld = NULL;
+       struct ikev2_keyexchange        *ke;
+       struct group                    *group;
+       struct ibuf                     *e = NULL, *nonce = NULL;
+       ssize_t                          len = 0;
+       int                              ret = -1;
+
+       log_debug("%s: called for IKE SA %p", __func__, sa);
+
+       if (sa->sa_stateflags & IKED_REQ_CHILDSA) {
+               /*
+                * We cannot initiate multiple concurrent CREATE_CHILD_SA
+                * exchanges, so retry in one minute.
+                */
+               timer_add(env, &sa->sa_rekey, 60);
+               return;
+       }
+
+       if ((nsa = sa_new(env, 0, 0, 1, sa->sa_policy)) == NULL) {
+               log_debug("%s: failed to get new SA", __func__);
+               goto done;
+       }
+
+       if (ikev2_sa_initiator(env, nsa, sa, NULL)) {
+               log_debug("%s: failed to setup DH", __func__);
+               goto done;
+       }
+       sa_state(env, nsa, IKEV2_STATE_AUTH_SUCCESS);
+       nonce = nsa->sa_inonce;
+
+       if ((e = ibuf_static()) == NULL)
+               goto done;
+
+       /* SA payload */
+       if ((pld = ikev2_add_payload(e)) == NULL)
+               goto done;
+
+       /* just reuse the old IKE SA proposals */
+       if ((len = ikev2_add_proposals(env, nsa, e, &sa->sa_proposals,
+           IKEV2_SAPROTO_IKE, 1, 1)) == -1)
+               goto done;
+
+       if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONCE) == -1)
+               goto done;
+
+       /* NONCE payload */
+       if ((pld = ikev2_add_payload(e)) == NULL)
+               goto done;
+       if (ikev2_add_buf(e, nonce) == -1)
+               goto done;
+       len = ibuf_size(nonce);
+
+       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 = nsa->sa_dhgroup) == NULL) {
+               log_debug("%s: invalid dh", __func__);
+               goto done;
+       }
+       ke->kex_dhgroup = htobe16(group->id);
+       if (ikev2_add_buf(e, nsa->sa_dhiexchange) == -1)
+               goto done;
+       len = sizeof(*ke) + dh_getlen(group);
+
+       if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1)
+               goto done;
+
+       ret = ikev2_msg_send_encrypt(env, sa, &e,
+           IKEV2_EXCHANGE_CREATE_CHILD_SA, IKEV2_PAYLOAD_SA, 0);
+       if (ret == 0) {
+               sa->sa_stateflags |= IKED_REQ_CHILDSA;
+               sa->sa_next = nsa;
+               nsa = NULL;
+       }
+done:
+       if (nsa)
+               sa_free(env, nsa);
+       ibuf_release(e);
+
+       if (ret == 0)
+               log_debug("%s: create child SA sent", __func__);
+       else
+               log_debug("%s: could not send create child SA", __func__);
+       /* XXX should we try again in case of ret != 0 ? */
+}
+
 int
 ikev2_init_create_child_sa(struct iked *env, struct iked_message *msg)
 {
        struct iked_childsa             *csa = NULL;
        struct iked_proposal            *prop;
        struct iked_sa                  *sa = msg->msg_sa;
+       struct iked_sa                  *nsa;
+       struct iked_spi                 *spi;
        struct ikev2_delete             *del;
        struct ibuf                     *buf = NULL;
        u_int32_t                        spi32;
@@ -2561,6 +2675,33 @@ ikev2_init_create_child_sa(struct iked *env, struct iked_message *msg)
                return (-1);
        }
 
+       /* IKE SA rekeying */
+       if (prop->prop_protoid == IKEV2_SAPROTO_IKE) {
+               if (sa->sa_next == NULL) {
+                       log_debug("%s: missing IKE SA for rekeying", __func__);
+                       return (-1);
+               }
+               /* Update the responder SPI */
+               spi = &msg->msg_prop->prop_peerspi;
+               if ((nsa = sa_new(env, sa->sa_next->sa_hdr.sh_ispi,
+                   spi->spi, 1, NULL)) == NULL || nsa != sa->sa_next) {
+                       log_debug("%s: invalid rekey SA", __func__);
+                       if (nsa)
+                               sa_free(env, nsa);
+                       sa_free(env, sa->sa_next);
+                       sa->sa_next = NULL;
+                       return (-1);
+               }
+               if (ikev2_sa_initiator(env, nsa, sa, msg) == -1) {
+                       log_debug("%s: failed to get IKE keys", __func__);
+                       return (-1);
+               }
+               sa->sa_stateflags &= ~IKED_REQ_CHILDSA;
+               sa->sa_next = NULL;
+               return (ikev2_ikesa_enable(env, sa, nsa));
+       }
+
+       /* Child SA rekeying */
        if (sa->sa_rekeyspi &&
            (csa = childsa_lookup(sa, sa->sa_rekeyspi, prop->prop_protoid))
            != NULL) {
@@ -2635,11 +2776,163 @@ done:
 }
 
 int
-ikev2_resp_create_child_sa(struct iked *env, struct iked_message *msg)
+ikev2_ikesa_enable(struct iked *env, struct iked_sa *sa, struct iked_sa *nsa)
 {
        struct iked_childsa             *csa, *nextcsa;
        struct iked_flow                *flow, *nextflow;
        struct iked_proposal            *prop, *nextprop;
+       int                              initiator;
+
+       log_debug("%s: IKE SA %p ispi %s rspi %s replaced"
+           " by SA %p ispi %s rspi %s ",
+           __func__, sa,
+           print_spi(sa->sa_hdr.sh_ispi, 8),
+           print_spi(sa->sa_hdr.sh_rspi, 8),
+           nsa,
+           print_spi(nsa->sa_hdr.sh_ispi, 8),
+           print_spi(nsa->sa_hdr.sh_rspi, 8));
+
+       /*
+        * Transfer policy and address:
+        * - Remember if we initiated the original IKE-SA because of our policy.
+        * - Note that sa_address() will insert the new SA when we set sa_peer.
+        */
+       initiator = !memcmp(&sa->sa_polpeer, &sa->sa_policy->pol_peer,
+           sizeof(sa->sa_polpeer));
+       nsa->sa_policy = sa->sa_policy;
+       RB_REMOVE(iked_sapeers, &sa->sa_policy->pol_sapeers, sa);
+       sa->sa_policy = NULL;
+       if (sa_address(nsa, &nsa->sa_peer, &sa->sa_peer.addr,
+           initiator) == -1 ||
+           sa_address(nsa, &nsa->sa_local, &sa->sa_local.addr,
+           initiator) == -1) {
+               /* reinsert old SA :/ */
+               sa->sa_policy = nsa->sa_policy;
+               if (RB_FIND(iked_sapeers, &nsa->sa_policy->pol_sapeers, nsa))
+                       RB_REMOVE(iked_sapeers, &nsa->sa_policy->pol_sapeers, nsa);
+               RB_INSERT(iked_sapeers, &sa->sa_policy->pol_sapeers, sa);
+               nsa->sa_policy = NULL;
+               return (-1);
+       }
+
+       /* Transfer socket and NAT information */
+       nsa->sa_fd = sa->sa_fd;
+       nsa->sa_natt = sa->sa_natt;
+       nsa->sa_udpencap = sa->sa_udpencap;
+
+       /* Transfer all Child SAs and flows from the old IKE SA */
+       for (flow = TAILQ_FIRST(&sa->sa_flows); flow != NULL;
+            flow = nextflow) {
+               nextflow = TAILQ_NEXT(flow, flow_entry);
+               TAILQ_REMOVE(&sa->sa_flows, flow, flow_entry);
+               TAILQ_INSERT_TAIL(&nsa->sa_flows, flow,
+                   flow_entry);
+               flow->flow_ikesa = nsa;
+               flow->flow_local = &nsa->sa_local;
+               flow->flow_peer = &nsa->sa_peer;
+       }
+       for (csa = TAILQ_FIRST(&sa->sa_childsas); csa != NULL;
+            csa = nextcsa) {
+               nextcsa = TAILQ_NEXT(csa, csa_entry);
+               TAILQ_REMOVE(&sa->sa_childsas, csa, csa_entry);
+               TAILQ_INSERT_TAIL(&nsa->sa_childsas, csa,
+                   csa_entry);
+               csa->csa_ikesa = nsa;
+               if (csa->csa_dir == IPSP_DIRECTION_IN) {
+                       csa->csa_local = &nsa->sa_peer;
+                       csa->csa_peer = &nsa->sa_local;
+               } else {
+                       csa->csa_local = &nsa->sa_local;
+                       csa->csa_peer = &nsa->sa_peer;
+               }
+       }
+       /* Transfer all non-IKE proposals */
+       for (prop = TAILQ_FIRST(&sa->sa_proposals); prop != NULL;
+            prop = nextprop) {
+               nextprop = TAILQ_NEXT(prop, prop_entry);
+               if (prop->prop_protoid == IKEV2_SAPROTO_IKE)
+                       continue;
+               TAILQ_REMOVE(&sa->sa_proposals, prop, prop_entry);
+               TAILQ_INSERT_TAIL(&nsa->sa_proposals, prop,
+                   prop_entry);
+       }
+
+       /* Preserve ID information */
+       if (sa->sa_hdr.sh_initiator == nsa->sa_hdr.sh_initiator) {
+               nsa->sa_iid = sa->sa_iid;
+               nsa->sa_rid = sa->sa_rid;
+       } else {
+               /* initiator and responder role swapped */
+               nsa->sa_iid = sa->sa_rid;
+               nsa->sa_rid = sa->sa_iid;
+       }
+       /* duplicate the actual buffer */
+       nsa->sa_iid.id_buf = ibuf_dup(nsa->sa_iid.id_buf);
+       nsa->sa_rid.id_buf = ibuf_dup(nsa->sa_rid.id_buf);
+
+       /* Transfer sa_addrpool address */
+       if (sa->sa_addrpool) {
+               RB_REMOVE(iked_addrpool, &env->sc_addrpool, sa);
+               nsa->sa_addrpool = sa->sa_addrpool;
+               sa->sa_addrpool = NULL;
+               RB_INSERT(iked_addrpool, &env->sc_addrpool, nsa);
+       }
+
+       log_debug("%s: activating new IKE SA", __func__);
+       sa_state(env, nsa, IKEV2_STATE_ESTABLISHED);
+       timer_set(env, &nsa->sa_timer, ikev2_ike_sa_alive, nsa);
+       timer_add(env, &nsa->sa_timer, IKED_IKE_SA_ALIVE_TIMEOUT);
+       timer_set(env, &nsa->sa_rekey, ikev2_ike_sa_rekey, nsa);
+       if (nsa->sa_policy->pol_rekey)
+               timer_add(env, &nsa->sa_rekey, nsa->sa_policy->pol_rekey);
+       nsa->sa_stateflags = nsa->sa_statevalid; /* XXX */
+
+       /* unregister DPD keep alive timer & rekey first */
+       if (sa->sa_state == IKEV2_STATE_ESTABLISHED) {
+               timer_del(env, &sa->sa_rekey);
+               timer_del(env, &sa->sa_timer);
+       }
+
+       ikev2_ikesa_delete(env, sa, nsa->sa_hdr.sh_initiator);
+       return (0);
+}
+
+void
+ikev2_ikesa_delete(struct iked *env, struct iked_sa *sa, int initiator)
+{
+       struct ibuf                     *buf = NULL;
+       struct ikev2_delete             *del;
+
+       if (initiator) {
+               /* Send PAYLOAD_DELETE */
+               if ((buf = ibuf_static()) == NULL)
+                       goto done;
+               if ((del = ibuf_advance(buf, sizeof(*del))) == NULL)
+                       goto done;
+               del->del_protoid = IKEV2_SAPROTO_IKE;
+               del->del_spisize = 0;
+               del->del_nspi = 0;
+               if (ikev2_send_ike_e(env, sa, buf, IKEV2_PAYLOAD_DELETE,
+                   IKEV2_EXCHANGE_INFORMATIONAL, 0) == -1)
+                       goto done;
+               log_debug("%s: sent delete, closing SA", __func__);
+done:
+               ibuf_release(buf);
+               sa_state(env, sa, IKEV2_STATE_CLOSED);
+       } else {
+               sa_state(env, sa, IKEV2_STATE_CLOSING);
+       }
+
+       /* Remove IKE-SA after timeout, e.g. if we don't get a delete */
+       timer_set(env, &sa->sa_timer, ikev2_ike_sa_timeout, sa);
+       timer_add(env, &sa->sa_timer, IKED_IKE_SA_DELETE_TIMEOUT);
+}
+
+int
+ikev2_resp_create_child_sa(struct iked *env, struct iked_message *msg)
+{
+       struct iked_childsa             *csa;
+       struct iked_proposal            *prop;
        struct iked_sa                  *nsa = NULL, *sa = msg->msg_sa;
        struct iked_spi                 *spi, *rekey = &msg->msg_rekey;
        struct ikev2_keyexchange        *ke;
@@ -2838,60 +3131,9 @@ ikev2_resp_create_child_sa(struct iked *env, struct iked_message *msg)
            IKEV2_EXCHANGE_CREATE_CHILD_SA, firstpayload, 1)) == -1)
                goto done;
 
-       if (protoid == IKEV2_SAPROTO_IKE) {
-               /* Transfer all Child SAs and flows from the old IKE SA */
-               for (flow = TAILQ_FIRST(&sa->sa_flows); flow != NULL;
-                    flow = nextflow) {
-                       nextflow = TAILQ_NEXT(flow, flow_entry);
-                       TAILQ_REMOVE(&sa->sa_flows, flow, flow_entry);
-                       TAILQ_INSERT_TAIL(&nsa->sa_flows, flow,
-                           flow_entry);
-                       flow->flow_ikesa = nsa;
-               }
-               for (csa = TAILQ_FIRST(&sa->sa_childsas); csa != NULL;
-                    csa = nextcsa) {
-                       nextcsa = TAILQ_NEXT(csa, csa_entry);
-                       TAILQ_REMOVE(&sa->sa_childsas, csa, csa_entry);
-                       TAILQ_INSERT_TAIL(&nsa->sa_childsas, csa,
-                           csa_entry);
-                       csa->csa_ikesa = nsa;
-               }
-               /* Transfer all non-IKE proposals */
-               for (prop = TAILQ_FIRST(&sa->sa_proposals); prop != NULL;
-                    prop = nextprop) {
-                       nextprop = TAILQ_NEXT(prop, prop_entry);
-                       if (prop->prop_protoid == IKEV2_SAPROTO_IKE)
-                               continue;
-                       TAILQ_REMOVE(&sa->sa_proposals, prop, prop_entry);
-                       TAILQ_INSERT_TAIL(&nsa->sa_proposals, prop,
-                           prop_entry);
-               }
-               /* Preserve ID information */
-               nsa->sa_iid = sa->sa_iid;
-               nsa->sa_iid.id_buf = ibuf_dup(sa->sa_iid.id_buf);
-               nsa->sa_rid = sa->sa_rid;
-               nsa->sa_rid.id_buf = ibuf_dup(sa->sa_rid.id_buf);
-
-               log_debug("%s: activating new IKE SA", __func__);
-               sa_state(env, nsa, IKEV2_STATE_ESTABLISHED);
-               timer_set(env, &nsa->sa_timer, ikev2_ike_sa_alive, nsa);
-               timer_add(env, &nsa->sa_timer, IKED_IKE_SA_ALIVE_TIMEOUT);
-               nsa->sa_stateflags = sa->sa_statevalid; /* XXX */
-
-               /* unregister DPD keep alive timer first */
-               if (sa->sa_state == IKEV2_STATE_ESTABLISHED)
-                       timer_del(env, &sa->sa_timer);
-               timer_set(env, &sa->sa_timer, ikev2_ike_sa_timeout, sa);
-               timer_add(env, &sa->sa_timer, IKED_IKE_SA_REKEY_TIMEOUT);
-
-               if (sa->sa_addrpool) {
-                       /* transfer sa_addrpool address */
-                       RB_REMOVE(iked_addrpool, &env->sc_addrpool, sa);
-                       nsa->sa_addrpool = sa->sa_addrpool;
-                       sa->sa_addrpool = NULL;
-                       RB_INSERT(iked_addrpool, &env->sc_addrpool, nsa);
-               }
-       } else
+       if (protoid == IKEV2_SAPROTO_IKE)
+               ret = ikev2_ikesa_enable(env, sa, nsa);
+       else
                ret = ikev2_childsa_enable(env, sa);
 
  done:
@@ -3290,7 +3532,7 @@ ikev2_sa_initiator_dh(struct iked_sa *sa, struct iked_message *msg, u_int proto)
 
 int
 ikev2_sa_initiator(struct iked *env, struct iked_sa *sa,
-    struct iked_message *msg)
+    struct iked_sa *osa, struct iked_message *msg)
 {
        struct iked_transform   *xform;
 
@@ -3374,7 +3616,7 @@ ikev2_sa_initiator(struct iked *env, struct iked_sa *sa,
                return (-1);
        }
 
-       return (ikev2_sa_keys(env, sa, NULL));
+       return (ikev2_sa_keys(env, sa, osa ? osa->sa_key_d : NULL));
 }
 
 int
@@ -4457,7 +4699,8 @@ ikev2_valid_proposal(struct iked_proposal *prop,
        return (0);
 }
 
-void
+/* return 0 if processed, -1 if busy */
+int
 ikev2_acquire_sa(struct iked *env, struct iked_flow *acquire)
 {
        struct iked_flow        *flow;
@@ -4465,7 +4708,7 @@ ikev2_acquire_sa(struct iked *env, struct iked_flow *acquire)
        struct iked_policy       pol, *p = NULL;
 
        if (env->sc_passive)
-               return;
+               return (0);
 
        /* First try to find an active flow with IKE SA */
        flow = RB_FIND(iked_flows, &env->sc_activeflows, acquire);
@@ -4482,7 +4725,7 @@ ikev2_acquire_sa(struct iked *env, struct iked_flow *acquire)
 
                if ((p = policy_test(env, &pol)) == NULL) {
                        log_warnx("%s: flow wasn't found", __func__);
-                       return;
+                       return (0);
                }
 
                log_debug("%s: found matching policy '%s'", __func__,
@@ -4496,14 +4739,16 @@ ikev2_acquire_sa(struct iked *env, struct iked_flow *acquire)
 
                if ((sa = flow->flow_ikesa) == NULL) {
                        log_warnx("%s: flow without SA", __func__);
-                       return;
+                       return (0);
                }
-
+               if (sa->sa_stateflags & IKED_REQ_CHILDSA)
+                       return (-1);    /* busy, retry later */
                if (ikev2_send_create_child_sa(env, sa, NULL,
                    flow->flow_saproto) != 0)
                        log_warnx("%s: failed to initiate a "
                            "CREATE_CHILD_SA exchange", __func__);
        }
+       return (0);
 }
 
 void
@@ -4519,7 +4764,8 @@ ikev2_disable_rekeying(struct iked *env, struct iked_sa *sa)
        (void)ikev2_childsa_delete(env, sa, 0, 0, NULL, 1);
 }
 
-void
+/* return 0 if processed, -1 if busy */
+int
 ikev2_rekey_sa(struct iked *env, struct iked_spi *rekey)
 {
        struct iked_childsa             *csa, key;
@@ -4528,28 +4774,32 @@ ikev2_rekey_sa(struct iked *env, struct iked_spi *rekey)
        key.csa_spi = *rekey;
        csa = RB_FIND(iked_activesas, &env->sc_activesas, &key);
        if (!csa)
-               return;
+               return (0);
 
        if (csa->csa_rekey)     /* See if it's already taken care of */
-               return;
+               return (0);
        if ((sa = csa->csa_ikesa) == NULL) {
                log_warnx("%s: SA %s doesn't have a parent SA", __func__,
                    print_spi(rekey->spi, rekey->spi_size));
-               return;
+               return (0);
        }
        if (!sa_stateok(sa, IKEV2_STATE_ESTABLISHED)) {
                log_warnx("%s: SA %s is not established", __func__,
                    print_spi(rekey->spi, rekey->spi_size));
-               return;
+               return (0);
        }
+       if (sa->sa_stateflags & IKED_REQ_CHILDSA)
+               return (-1);    /* 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))
                log_warnx("%s: failed to initiate a CREATE_CHILD_SA exchange",
                    __func__);
+       return (0);
 }
 
-void
+/* return 0 if processed, -1 if busy */
+int
 ikev2_drop_sa(struct iked *env, struct iked_spi *drop)
 {
        struct ibuf                     *buf = NULL;
@@ -4561,12 +4811,18 @@ ikev2_drop_sa(struct iked *env, struct iked_spi *drop)
        key.csa_spi = *drop;
        csa = RB_FIND(iked_activesas, &env->sc_activesas, &key);
        if (!csa || csa->csa_rekey)
-               return;
+               return (0);
+
+       sa = csa->csa_ikesa;
+       if (sa && (sa->sa_stateflags & IKED_REQ_CHILDSA))
+               return (-1);    /* busy, retry later */
+
        RB_REMOVE(iked_activesas, &env->sc_activesas, csa);
        csa->csa_loaded = 0;
-       if ((sa = csa->csa_ikesa) == NULL) {
+       csa->csa_rekey = 1;     /* prevent re-loading */
+       if (sa == NULL) {
                log_debug("%s: failed to find a parent SA", __func__);
-               return;
+               return (0);
        }
 
        if (csa->csa_allocated)
@@ -4582,7 +4838,7 @@ ikev2_drop_sa(struct iked *env, struct iked_spi *drop)
        /* Send PAYLOAD_DELETE */
 
        if ((buf = ibuf_static()) == NULL)
-               return;
+               return (0);
        if ((del = ibuf_advance(buf, sizeof(*del))) == NULL)
                goto done;
        del->del_protoid = drop->spi_protoid;
@@ -4604,7 +4860,7 @@ ikev2_drop_sa(struct iked *env, struct iked_spi *drop)
 
 done:
        ibuf_release(buf);
-       return;
+       return (0);
 }
 
 int
index a1daf47..652cf87 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ikev2.h,v 1.14 2014/04/29 11:51:13 markus Exp $       */
+/*     $OpenBSD: ikev2.h,v 1.15 2014/05/06 10:24:22 markus Exp $       */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -37,7 +37,8 @@
 #define IKEV2_STATE_VALID              6       /* authenticated AND validated certs */
 #define IKEV2_STATE_EAP_VALID          7       /* EAP validated */
 #define IKEV2_STATE_ESTABLISHED                8       /* active IKE SA */
-#define IKEV2_STATE_CLOSED             9       /* delete this SA */
+#define IKEV2_STATE_CLOSING            9       /* expect delete for this SA */
+#define IKEV2_STATE_CLOSED             10      /* delete this SA */
 
 extern struct iked_constmap ikev2_state_map[];
 
index 29f609d..85a91d8 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ikev2_msg.c,v 1.33 2014/05/05 16:14:37 markus Exp $   */
+/*     $OpenBSD: ikev2_msg.c,v 1.34 2014/05/06 10:24:22 markus Exp $   */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -190,8 +190,22 @@ ikev2_msg_valid_ike_sa(struct iked *env, struct ike_header *oldhdr,
        struct iked_sa                   sa;
 #endif
 
-       if (msg->msg_sa != NULL && msg->msg_policy != NULL)
+       if (msg->msg_sa != NULL && msg->msg_policy != NULL) {
+               /*
+                * Only permit informational requests from initiator
+                * on closing SAs (for DELETE).
+                */
+               if (msg->msg_sa->sa_state == IKEV2_STATE_CLOSING) {
+                       if (((oldhdr->ike_flags &
+                           (IKEV2_FLAG_INITIATOR|IKEV2_FLAG_RESPONSE)) ==
+                           IKEV2_FLAG_INITIATOR) &&
+                           (oldhdr->ike_exchange ==
+                           IKEV2_EXCHANGE_INFORMATIONAL))
+                               return (0);
+                       return (-1);
+               }
                return (0);
+       }
 
 #if 0
        /*
index e46da1b..951085f 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ikev2_pld.c,v 1.44 2014/05/06 09:21:50 markus Exp $   */
+/*     $OpenBSD: ikev2_pld.c,v 1.45 2014/05/06 10:24:22 markus Exp $   */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -1340,9 +1340,13 @@ ikev2_pld_delete(struct iked *env, struct ikev2_payload *pld,
                        msg->msg_parent->msg_responded = 1;
                        ibuf_release(resp);
                        sa_state(env, sa, IKEV2_STATE_CLOSED);
-                       return (ret);
+               } else {
+                       /*
+                        * We're sending a delete message. Upper layer
+                        * must deal with deletion of the IKE SA.
+                        */
+                       ret = 0;
                }
-               log_debug("%s: invalid SPI size", __func__);
                return (ret);
        }
 
index 1d5ab4f..24cfe6b 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parse.y,v 1.37 2014/02/17 15:07:23 markus Exp $       */
+/*     $OpenBSD: parse.y,v 1.38 2014/05/06 10:24:22 markus Exp $       */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -321,7 +321,8 @@ void                         copy_transforms(u_int, const struct ipsec_xf *,
 int                     create_ike(char *, int, u_int8_t, struct ipsec_hosts *,
                             struct ipsec_hosts *, struct ipsec_mode *,
                             struct ipsec_mode *, u_int8_t,
-                            u_int8_t, char *, char *, struct iked_lifetime *,
+                            u_int8_t, char *, char *,
+                            u_int32_t, struct iked_lifetime *,
                             struct iked_auth *, struct ipsec_filters *,
                             struct ipsec_addr_wrap *);
 int                     create_user(const char *, const char *);
@@ -370,7 +371,7 @@ typedef struct {
 %token PASSIVE ACTIVE ANY TAG TAP PROTO LOCAL GROUP NAME CONFIG EAP USER
 %token IKEV1 FLOW SA TCPMD5 TUNNEL TRANSPORT COUPLE DECOUPLE SET
 %token INCLUDE LIFETIME BYTES INET INET6 QUICK SKIP DEFAULT
-%token IPCOMP OCSP
+%token IPCOMP OCSP IKELIFETIME
 %token <v.string>              STRING
 %token <v.number>              NUMBER
 %type  <v.string>              string
@@ -392,7 +393,7 @@ typedef struct {
 %type  <v.ikekey>              keyspec
 %type  <v.mode>                ike_sa child_sa
 %type  <v.lifetime>            lifetime
-%type  <v.number>              byte_spec time_spec
+%type  <v.number>              byte_spec time_spec ikelifetime
 %type  <v.string>              name
 %type  <v.cfg>                 cfg ikecfg ikecfgvals
 %%
@@ -446,9 +447,9 @@ user                : USER STRING STRING            {
                ;
 
 ikev2rule      : IKEV2 name ikeflags satype af proto hosts_list peers
-                   ike_sa child_sa ids lifetime ikeauth ikecfg filters {
+                   ike_sa child_sa ids ikelifetime lifetime ikeauth ikecfg filters {
                        if (create_ike($2, $5, $6, $7, &$8, $9, $10, $4, $3,
-                           $11.srcid, $11.dstid, &$12, &$13, $15, $14) == -1)
+                           $11.srcid, $11.dstid, $12, &$13, &$14, $16, $15) == -1)
                                YYERROR;
                }
                ;
@@ -895,6 +896,13 @@ lifetime   : /* empty */                           {
                }
                ;
 
+ikelifetime    : /* empty */                           {
+                       $$ = 0;
+               }
+               | IKELIFETIME time_spec                 {
+                       $$ = $2;
+               }
+
 keyspec                : STRING                        {
                        u_int8_t        *hex;
 
@@ -1084,6 +1092,7 @@ lookup(char *s)
                { "from",               FROM },
                { "group",              GROUP },
                { "ike",                IKEV1 },
+               { "ikelifetime",        IKELIFETIME },
                { "ikesa",              IKESA },
                { "ikev2",              IKEV2 },
                { "include",            INCLUDE },
@@ -2312,6 +2321,9 @@ print_policy(struct iked_policy *pol)
        if (pol->pol_peerid.id_length != 0)
                print_verbose(" dstid %s", pol->pol_peerid.id_data);
 
+       if (pol->pol_rekey)
+               print_verbose(" ikelifetime %u", pol->pol_rekey);
+
        print_verbose(" lifetime %llu bytes %llu",
            pol->pol_lifetime.lt_seconds, pol->pol_lifetime.lt_bytes);
 
@@ -2382,7 +2394,8 @@ int
 create_ike(char *name, int af, u_int8_t ipproto, struct ipsec_hosts *hosts,
     struct ipsec_hosts *peers, struct ipsec_mode *ike_sa,
     struct ipsec_mode *ipsec_sa, u_int8_t saproto,
-    u_int8_t flags, char *srcid, char *dstid, struct iked_lifetime *lt,
+    u_int8_t flags, char *srcid, char *dstid,
+    u_int32_t ikelifetime, struct iked_lifetime *lt,
     struct iked_auth *authtype, struct ipsec_filters *filter,
     struct ipsec_addr_wrap *ikecfg)
 {
@@ -2508,6 +2521,9 @@ create_ike(char *name, int af, u_int8_t ipproto, struct ipsec_hosts *hosts,
                        pol.pol_af = ipb->af;
        }
 
+       if (ikelifetime)
+               pol.pol_rekey = ikelifetime;
+
        if (lt)
                pol.pol_lifetime = *lt;
        else
index 892d3d0..af360ca 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: pfkey.c,v 1.33 2014/05/05 18:50:36 markus Exp $       */
+/*     $OpenBSD: pfkey.c,v 1.34 2014/05/06 10:24:22 markus Exp $       */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -59,7 +59,7 @@ struct pfkey_message {
        u_int8_t        *pm_data;
        ssize_t          pm_length;
 };
-SIMPLEQ_HEAD(, pfkey_message) pfkey_postponed =
+SIMPLEQ_HEAD(, pfkey_message) pfkey_retry, pfkey_postponed =
     SIMPLEQ_HEAD_INITIALIZER(pfkey_postponed);
 
 struct pfkey_constmap {
@@ -120,7 +120,7 @@ struct sadb_ident *
 void   *pfkey_find_ext(u_int8_t *, ssize_t, int);
 
 void   pfkey_timer_cb(int, short, void *);
-void   pfkey_process(struct iked *, struct pfkey_message *);
+int    pfkey_process(struct iked *, struct pfkey_message *);
 
 int
 pfkey_couple(int sd, struct iked_sas *sas, int couple)
@@ -1493,7 +1493,7 @@ void
 pfkey_dispatch(int sd, short event, void *arg)
 {
        struct iked             *env = (struct iked *)arg;
-       struct pfkey_message     pm;
+       struct pfkey_message     pm, *pmp;
        struct sadb_msg          hdr;
        ssize_t                  len;
        u_int8_t                *data;
@@ -1521,9 +1521,17 @@ pfkey_dispatch(int sd, short event, void *arg)
 
        pm.pm_data = data;
        pm.pm_length = len;
-       pfkey_process(env, &pm);
 
-       free(data);
+       if (pfkey_process(env, &pm) == -1 &&
+           (pmp = calloc(1, sizeof(*pmp))) != NULL) {
+               pmp->pm_data = data;
+               pmp->pm_length = len;
+               log_debug("%s: pfkey_process is busy, retry later", __func__);
+               SIMPLEQ_INSERT_TAIL(&pfkey_postponed, pmp, pm_entry);
+               evtimer_add(&pfkey_timer_ev, &pfkey_timer_tv);
+       } else {
+               free(data);
+       }
 }
 
 void
@@ -1532,16 +1540,32 @@ pfkey_timer_cb(int unused, short event, void *arg)
        struct iked             *env = arg;
        struct pfkey_message    *pm;
 
+       SIMPLEQ_INIT(&pfkey_retry);
        while (!SIMPLEQ_EMPTY(&pfkey_postponed)) {
                pm = SIMPLEQ_FIRST(&pfkey_postponed);
                SIMPLEQ_REMOVE_HEAD(&pfkey_postponed, pm_entry);
-               pfkey_process(env, pm);
-               free(pm->pm_data);
-               free(pm);
+               if (pfkey_process(env, pm) == -1) {
+                       log_debug("%s: pfkey_process is busy, retry later",
+                           __func__);
+                       SIMPLEQ_INSERT_TAIL(&pfkey_retry, pm, pm_entry);
+               } else {
+                       free(pm->pm_data);
+                       free(pm);
+               }
+       }
+       while ((pm = SIMPLEQ_FIRST(&pfkey_retry)) != NULL) {
+               SIMPLEQ_REMOVE_HEAD(&pfkey_retry, pm_entry);
+               SIMPLEQ_INSERT_TAIL(&pfkey_postponed, pm, pm_entry);
        }
+       if (!SIMPLEQ_EMPTY(&pfkey_postponed))
+               evtimer_add(&pfkey_timer_ev, &pfkey_timer_tv);
 }
 
-void
+/*
+ * pfkey_process returns 0 if the message has been processed and -1 if
+ * the system is busy and the the message should be passed again, later.
+ */
+int
 pfkey_process(struct iked *env, struct pfkey_message *pm)
 {
        struct iked_spi          spi;
@@ -1556,7 +1580,7 @@ pfkey_process(struct iked *env, struct pfkey_message *pm)
        struct sadb_x_policy     sa_pol;
        struct sockaddr         *ssrc, *sdst, *smask, *dmask, *speer;
        struct iovec             iov[IOV_CNT];
-       int                      iov_cnt, sd;
+       int                      ret = 0, iov_cnt, sd;
        u_int8_t                *reply;
        ssize_t                  rlen;
        const char              *errmsg = NULL;
@@ -1565,7 +1589,7 @@ pfkey_process(struct iked *env, struct pfkey_message *pm)
        size_t                   slen;
 
        if (!env || !data || !len)
-               return;
+               return (0);
 
        sd = env->sc_pfkey;
        hdr = (struct sadb_msg *)data;
@@ -1578,20 +1602,20 @@ pfkey_process(struct iked *env, struct pfkey_message *pm)
                if ((sa_addr = pfkey_find_ext(data, len,
                    SADB_EXT_ADDRESS_DST)) == NULL) {
                        log_debug("%s: no peer address", __func__);
-                       return;
+                       return (0);
                }
                speer = (struct sockaddr *)(sa_addr + 1);
                peer.addr_af = speer->sa_family;
                peer.addr_port = htons(socket_getport(speer));
                if ((slen = speer->sa_len) > sizeof(peer.addr)) {
                        log_debug("%s: invalid peer address len", __func__);
-                       return;
+                       return (0);
                }
                memcpy(&peer.addr, speer, slen);
                if (socket_af((struct sockaddr *)&peer.addr,
                    peer.addr_port) == -1) {
                        log_debug("%s: invalid address", __func__);
-                       return;
+                       return (0);
                }
                flow.flow_peer = &peer;
 
@@ -1624,7 +1648,7 @@ pfkey_process(struct iked *env, struct pfkey_message *pm)
 
                if (pfkey_write(sd, &smsg, iov, iov_cnt, &reply, &rlen)) {
                        log_warnx("%s: failed to get a policy", __func__);
-                       return;
+                       return (0);
                }
 
                if ((sa_addr = pfkey_find_ext(reply, rlen,
@@ -1637,13 +1661,13 @@ pfkey_process(struct iked *env, struct pfkey_message *pm)
                flow.flow_src.addr_port = htons(socket_getport(ssrc));
                if ((slen = ssrc->sa_len) > sizeof(flow.flow_src.addr)) {
                        log_debug("%s: invalid src address len", __func__);
-                       return;
+                       return (0);
                }
                memcpy(&flow.flow_src.addr, ssrc, slen);
                if (socket_af((struct sockaddr *)&flow.flow_src.addr,
                    flow.flow_src.addr_port) == -1) {
                        log_debug("%s: invalid address", __func__);
-                       return;
+                       return (0);
                }
 
                if ((sa_addr = pfkey_find_ext(reply, rlen,
@@ -1656,13 +1680,13 @@ pfkey_process(struct iked *env, struct pfkey_message *pm)
                flow.flow_dst.addr_port = htons(socket_getport(sdst));
                if ((slen = sdst->sa_len) > sizeof(flow.flow_dst.addr)) {
                        log_debug("%s: invalid dst address len", __func__);
-                       return;
+                       return (0);
                }
                memcpy(&flow.flow_dst.addr, sdst, slen);
                if (socket_af((struct sockaddr *)&flow.flow_dst.addr,
                    flow.flow_dst.addr_port) == -1) {
                        log_debug("%s: invalid address", __func__);
-                       return;
+                       return (0);
                }
 
                if ((sa_addr = pfkey_find_ext(reply, rlen,
@@ -1687,7 +1711,7 @@ pfkey_process(struct iked *env, struct pfkey_message *pm)
                default:
                        log_debug("%s: bad address family", __func__);
                        free(reply);
-                       return;
+                       return (0);
                }
 
                if ((sa_addr = pfkey_find_ext(reply, rlen,
@@ -1712,7 +1736,7 @@ pfkey_process(struct iked *env, struct pfkey_message *pm)
                default:
                        log_debug("%s: bad address family", __func__);
                        free(reply);
-                       return;
+                       return (0);
                }
 
                if ((sa_proto = pfkey_find_ext(reply, rlen,
@@ -1728,7 +1752,7 @@ pfkey_process(struct iked *env, struct pfkey_message *pm)
                    print_host(sdst, NULL, 0), print_host(dmask, NULL, 0),
                    print_host(speer, NULL, 0));
 
-               ikev2_acquire_sa(env, &flow);
+               ret = ikev2_acquire_sa(env, &flow);
 
 out:
                if (errmsg)
@@ -1739,7 +1763,7 @@ out:
        case SADB_EXPIRE:
                if ((sa = pfkey_find_ext(data, len, SADB_EXT_SA)) == NULL) {
                        log_warnx("%s: SA extension wasn't found", __func__);
-                       return;
+                       return (0);
                }
                if ((sa_ltime = pfkey_find_ext(data, len,
                        SADB_EXT_LIFETIME_SOFT)) == NULL &&
@@ -1747,7 +1771,7 @@ out:
                        SADB_EXT_LIFETIME_HARD)) == NULL) {
                        log_warnx("%s: lifetime extension wasn't found",
                            __func__);
-                       return;
+                       return (0);
                }
                spi.spi = ntohl(sa->sadb_sa_spi);
                spi.spi_size = 4;
@@ -1762,7 +1786,7 @@ out:
                        log_warnx("%s: usupported SA type %d spi %s",
                            __func__, hdr->sadb_msg_satype,
                            print_spi(spi.spi, spi.spi_size));
-                       return;
+                       return (0);
                }
 
                log_debug("%s: SA %s is expired, pending %s", __func__,
@@ -1771,9 +1795,10 @@ out:
                    "rekeying" : "deletion");
 
                if (sa_ltime->sadb_lifetime_exttype == SADB_EXT_LIFETIME_SOFT)
-                       ikev2_rekey_sa(env, &spi);
+                       ret = ikev2_rekey_sa(env, &spi);
                else
-                       ikev2_drop_sa(env, &spi);
+                       ret = ikev2_drop_sa(env, &spi);
                break;
        }
+       return (ret);
 }
index e5c553e..635e005 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: policy.c,v 1.33 2014/05/06 09:48:40 markus Exp $      */
+/*     $OpenBSD: policy.c,v 1.34 2014/05/06 10:24:22 markus Exp $      */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -242,7 +242,8 @@ sa_state(struct iked *env, struct iked_sa *sa, int state)
                            NULL, 0),
                            print_host((struct sockaddr *)&sa->sa_local.addr,
                            NULL, 0),
-                           sa->sa_policy->pol_name);
+                           sa->sa_policy ? sa->sa_policy->pol_name :
+                           "<unknown>");
                        break;
                default:
                        log_debug("%s: %s -> %s", __func__, a, b);
@@ -372,6 +373,11 @@ sa_free(struct iked *env, struct iked_sa *sa)
            print_spi(sa->sa_hdr.sh_ispi, 8),
            print_spi(sa->sa_hdr.sh_rspi, 8));
 
+       /* IKE rekeying running? */
+       if (sa->sa_next) {
+               RB_REMOVE(iked_sas, &env->sc_sas, sa->sa_next);
+               config_free_sa(env, sa->sa_next);
+       }
        RB_REMOVE(iked_sas, &env->sc_sas, sa);
        config_free_sa(env, sa);
 }
@@ -401,8 +407,8 @@ sa_address(struct iked_sa *sa, struct iked_addr *addr,
 {
        struct iked_policy      *pol = sa->sa_policy;
 
-       if (pol == NULL) {
-               log_debug("%s: invalid policy", __func__);
+       if (sa->sa_state != IKEV2_STATE_CLOSING && pol == NULL) {
+               log_debug("%s: missing policy", __func__);
                return (-1);
        }
 
@@ -415,7 +421,7 @@ sa_address(struct iked_sa *sa, struct iked_addr *addr,
                return (-1);
        }
 
-       if (addr == &sa->sa_peer) {
+       if (addr == &sa->sa_peer && pol) {
                /* XXX Re-insert node into the tree */
                RB_REMOVE(iked_sapeers, &pol->pol_sapeers, sa);
                memcpy(&sa->sa_polpeer, initiator ? &pol->pol_peer :
@@ -471,7 +477,7 @@ sa_lookup(struct iked *env, u_int64_t ispi, u_int64_t rspi,
        struct iked_sa  *sa, key;
 
        key.sa_hdr.sh_ispi = ispi;
-       key.sa_hdr.sh_rspi = rspi;
+       /* key.sa_hdr.sh_rspi = rspi; */
        key.sa_hdr.sh_initiator = initiator;
 
        if ((sa = RB_FIND(iked_sas, &env->sc_sas, &key)) != NULL) {