Implement MOBIKE (RFC 4555) support in iked(8), with us acting as
authorpatrick <patrick@openbsd.org>
Mon, 27 Nov 2017 18:39:35 +0000 (18:39 +0000)
committerpatrick <patrick@openbsd.org>
Mon, 27 Nov 2017 18:39:35 +0000 (18:39 +0000)
responder.  In practice this support means that clients like iPhones
can roam in different networks (LTE, WiFi) and change their external
addresses without having to re-do the whole handshake.  It allows the
client to choose how and when to change the external tunnel endpoint
addresses on demand, depending on which network is better or even is
connected at all.

ok sthen@
tweaks from jmc@
tested by a handful

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

index 590e4d7..8214ac0 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: config.c,v 1.48 2017/04/13 07:04:09 patrick Exp $     */
+/*     $OpenBSD: config.c,v 1.49 2017/11/27 18:39:35 patrick Exp $     */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -814,6 +814,29 @@ config_getcompile(struct iked *env, struct imsg *imsg)
        return (0);
 }
 
+int
+config_setmobike(struct iked *env)
+{
+       unsigned int boolval;
+
+       boolval = env->sc_mobike;
+       proc_compose(&env->sc_ps, PROC_IKEV2, IMSG_CTL_MOBIKE,
+           &boolval, sizeof(boolval));
+       return (0);
+}
+
+int
+config_getmobike(struct iked *env, struct imsg *imsg)
+{
+       unsigned int boolval;
+
+       IMSG_SIZE_CHECK(imsg, &boolval);
+       memcpy(&boolval, imsg->data, sizeof(boolval));
+       env->sc_mobike = boolval;
+       log_debug("%s: %smobike", __func__, env->sc_mobike ? "" : "no ");
+       return (0);
+}
+
 int
 config_setocsp(struct iked *env)
 {
index 09fef3e..5669f5a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: iked.c,v 1.35 2017/11/08 16:57:41 patrick Exp $       */
+/*     $OpenBSD: iked.c,v 1.36 2017/11/27 18:39:35 patrick Exp $       */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -250,6 +250,7 @@ parent_configure(struct iked *env)
        if (pledge("stdio rpath proc dns inet route sendfd", NULL) == -1)
                fatal("pledge");
 
+       config_setmobike(env);
        config_setcoupled(env, env->sc_decoupled ? 0 : 1);
        config_setmode(env, env->sc_passive ? 1 : 0);
        config_setocsp(env);
@@ -280,6 +281,7 @@ parent_reload(struct iked *env, int reset, const char *filename)
                /* Re-compile policies and skip steps */
                config_setcompile(env, PROC_IKEV2);
 
+               config_setmobike(env);
                config_setcoupled(env, env->sc_decoupled ? 0 : 1);
                config_setmode(env, env->sc_passive ? 1 : 0);
                config_setocsp(env);
index 8c77f24..2cf7785 100644 (file)
@@ -1,4 +1,4 @@
-.\" $OpenBSD: iked.conf.5,v 1.50 2017/06/01 15:23:43 sthen Exp $
+.\" $OpenBSD: iked.conf.5,v 1.51 2017/11/27 18:39:35 patrick 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: June 1 2017 $
+.Dd $Mdocdate: November 27 2017 $
 .Dt IKED.CONF 5
 .Os
 .Sh NAME
@@ -136,6 +136,15 @@ This is the default.
 .It Ic set decouple
 Don't load the negotiated SAs and flows from the kernel.
 This mode is only useful for testing and debugging.
+.It Ic set mobike
+Enable MOBIKE (RFC 4555) support.
+This is the default.
+MOBIKE allows the peer IP address to be changed for IKE and IPsec SAs.
+Currently
+.Xr iked 8
+only supports MOBIKE when acting as a responder.
+.It Ic set nomobike
+Disables MOBIKE support.
 .It Ic set ocsp Ar URL
 Enable OCSP and set the URL of the OCSP responder.
 Please note that the matching responder and issuer certificates
index b536d58..a005968 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: iked.h,v 1.115 2017/04/26 10:42:38 henning Exp $      */
+/*     $OpenBSD: iked.h,v 1.116 2017/11/27 18:39:35 patrick Exp $      */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -157,6 +157,8 @@ struct iked_flow {
 
        RB_ENTRY(iked_flow)              flow_node;
        TAILQ_ENTRY(iked_flow)           flow_entry;
+
+       int                              flow_replacing; /* cf flow_replace() */
        int                              flow_ipcomp;
 };
 RB_HEAD(iked_flows, iked_flow);
@@ -371,6 +373,7 @@ struct iked_sa {
 #define IKED_SATYPE_LOCAL               1              /* Local SA */
 
        struct iked_addr                 sa_peer;
+       struct iked_addr                 sa_peer_loaded;/* MOBIKE */
        struct iked_addr                 sa_local;
        int                              sa_fd;
 
@@ -441,6 +444,8 @@ struct iked_sa {
        uint16_t                         sa_cpi_out;    /* IPcomp outgoing */
        uint16_t                         sa_cpi_in;     /* IPcomp incoming*/
 
+       int                              sa_mobike;     /* MOBIKE */
+
        struct iked_timer                sa_timer;      /* SA timeouts */
 #define IKED_IKE_SA_EXCHANGE_TIMEOUT    300            /* 5 minutes */
 #define IKED_IKE_SA_REKEY_TIMEOUT       120            /* 2 minutes */
@@ -487,6 +492,7 @@ struct iked_message {
        int                      msg_response;
        int                      msg_responded;
        int                      msg_natt;
+       int                      msg_natt_rcvd;
        int                      msg_error;
        int                      msg_e;
        struct iked_message     *msg_parent;
@@ -508,6 +514,10 @@ struct iked_message {
        struct iked_id           msg_cert;
        struct ibuf             *msg_cookie;
 
+       /* MOBIKE */
+       int                      msg_update_sa_addresses;
+       struct ibuf             *msg_cookie2;
+
        /* Parse stack */
        struct iked_proposal    *msg_prop;
        uint16_t                 msg_attrlength;
@@ -590,6 +600,8 @@ struct iked {
        uint8_t                          sc_passive;
        uint8_t                          sc_decoupled;
 
+       uint8_t                          sc_mobike;     /* MOBIKE */
+
        struct iked_policies             sc_policies;
        struct iked_policy              *sc_defaultcon;
 
@@ -687,6 +699,8 @@ int  config_setocsp(struct iked *);
 int     config_getocsp(struct iked *, struct imsg *);
 int     config_setkeys(struct iked *);
 int     config_getkey(struct iked *, struct imsg *);
+int     config_setmobike(struct iked *);
+int     config_getmobike(struct iked *, struct imsg *);
 
 /* policy.c */
 void    policy_init(struct iked *);
@@ -711,6 +725,7 @@ struct iked_childsa *
         childsa_lookup(struct iked_sa *, uint64_t, uint8_t);
 void    flow_free(struct iked_flow *);
 int     flow_equal(struct iked_flow *, struct iked_flow *);
+int     flow_replace(struct iked *, struct iked_flow *);
 struct iked_sa *
         sa_lookup(struct iked *, uint64_t, uint64_t, unsigned int);
 struct iked_user *
@@ -860,6 +875,7 @@ int  pfkey_flow_delete(int fd, struct iked_flow *);
 int     pfkey_block(int, int, unsigned int);
 int     pfkey_sa_init(int, struct iked_childsa *, uint32_t *);
 int     pfkey_sa_add(int, struct iked_childsa *, struct iked_childsa *);
+int     pfkey_sa_update_addresses(int, struct iked_childsa *);
 int     pfkey_sa_delete(int, struct iked_childsa *);
 int     pfkey_sa_last_used(int, struct iked_childsa *, uint64_t *);
 int     pfkey_flush(int);
index e908b62..b505f76 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ikev2.c,v 1.157 2017/11/08 09:35:19 patrick Exp $     */
+/*     $OpenBSD: ikev2.c,v 1.158 2017/11/27 18:39:35 patrick Exp $     */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -140,6 +140,13 @@ ssize_t    ikev2_add_sighashnotify(struct ibuf *, struct ikev2_payload **,
 ssize_t ikev2_add_nat_detection(struct iked *, struct ibuf *,
            struct ikev2_payload **, struct iked_message *, ssize_t);
 
+ssize_t         ikev2_add_mobike(struct iked *, struct ibuf *,
+           struct ikev2_payload **, ssize_t, struct iked_sa *);
+int     ikev2_update_sa_addresses(struct iked *, struct iked_sa *);
+int     ikev2_resp_informational(struct iked *, struct iked_sa *,
+           struct iked_message *);
+
+
 static struct privsep_proc procs[] = {
        { "parent",     PROC_PARENT,    ikev2_dispatch_parent },
        { "certstore",  PROC_CERT,      ikev2_dispatch_cert }
@@ -189,6 +196,8 @@ ikev2_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
                            IKED_INITIATOR_INITIAL);
                }
                return (0);
+       case IMSG_CTL_MOBIKE:
+               return (config_getmobike(env, imsg));
        case IMSG_UDP_SOCKET:
                return (config_getsocket(env, imsg, ikev2_msg_cb));
        case IMSG_PFKEY_SOCKET:
@@ -1584,6 +1593,30 @@ ikev2_add_ipcompnotify(struct iked *env, struct ibuf *e,
        return (len);
 }
 
+ssize_t
+ikev2_add_mobike(struct iked *env, struct ibuf *e,
+    struct ikev2_payload **pld, ssize_t len, struct iked_sa *sa)
+{
+       struct ikev2_notify             *n;
+       uint8_t                         *ptr;
+
+       if (*pld)
+               if (ikev2_next_payload(*pld, len, IKEV2_PAYLOAD_NOTIFY) == -1)
+                       return (-1);
+       if ((*pld = ikev2_add_payload(e)) == NULL)
+               return (-1);
+       len = sizeof(*n);
+       if ((ptr = ibuf_advance(e, len)) == NULL)
+               return (-1);
+       n = (struct ikev2_notify *)ptr;
+       n->n_protoid = 0;
+       n->n_spisize = 0;
+       n->n_type = htobe16(IKEV2_N_MOBIKE_SUPPORTED);
+       log_debug("%s: done", __func__);
+
+       return (len);
+}
+
 ssize_t
 ikev2_add_sighashnotify(struct ibuf *e, struct ikev2_payload **pld,
     ssize_t len)
@@ -2055,6 +2088,84 @@ ikev2_add_buf(struct ibuf *buf, struct ibuf *data)
        return (0);
 }
 
+int
+ikev2_resp_informational(struct iked *env, struct iked_sa *sa,
+    struct iked_message *msg)
+{
+       struct ikev2_notify             *n;
+       struct ikev2_payload            *pld = NULL;
+       struct ike_header               *hdr;
+       struct ibuf                     *buf = NULL;
+       ssize_t                          len = 0;
+       int                              ret = -1;
+       int                              oflags = 0;
+       uint8_t                          firstpayload = IKEV2_PAYLOAD_NONE;
+
+       if (!sa_stateok(sa, IKEV2_STATE_AUTH_REQUEST) ||
+           msg->msg_responded || msg->msg_error)
+               goto done;
+
+       if ((buf = ibuf_static()) == NULL)
+               goto done;
+       /*
+        * Include NAT_DETECTION notification on UPDATE_SA_ADDRESSES or if
+        * the peer did include them, too (RFC 455, 3.8).
+        */
+       if (sa->sa_mobike &&
+           (msg->msg_update_sa_addresses || msg->msg_natt_rcvd)) {
+               /*
+                * XXX workaround so ikev2_msg_frompeer() fails for
+                * XXX ikev2_nat_detection(), and the correct src/dst are
+                * XXX used for the nat detection payload.
+                */
+               if (msg->msg_parent == NULL)
+                       goto done;
+               if ((hdr = ibuf_seek(msg->msg_parent->msg_data, 0,
+                   sizeof(*hdr))) == NULL)
+                       goto done;
+               oflags = hdr->ike_flags;
+               if (sa->sa_hdr.sh_initiator)
+                       hdr->ike_flags |= IKEV2_FLAG_INITIATOR;
+               else
+                       hdr->ike_flags &= ~IKEV2_FLAG_INITIATOR;
+               /* NAT-T notify payloads */
+               len = ikev2_add_nat_detection(env, buf, &pld, msg, len);
+               hdr->ike_flags = oflags;        /* XXX undo workaround */
+               if (len == -1)
+                       goto done;
+               firstpayload = IKEV2_PAYLOAD_NOTIFY;
+       }
+       /* Reflect COOKIE2 */
+       if (msg->msg_cookie2) {
+               if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NOTIFY) == -1)
+                       goto done;
+               if ((pld = ikev2_add_payload(buf)) == NULL)
+                       goto done;
+               if ((n = ibuf_advance(buf, sizeof(*n))) == NULL)
+                       goto done;
+               n->n_protoid = IKEV2_SAPROTO_IKE;
+               n->n_spisize = 0;
+               n->n_type = htobe16(IKEV2_N_COOKIE2);
+               if (ikev2_add_buf(buf, msg->msg_cookie2) == -1)
+                       goto done;
+               len = sizeof(*n) + ibuf_size(msg->msg_cookie2);
+               log_debug("%s: added cookie2", __func__);
+               if (firstpayload == IKEV2_PAYLOAD_NONE)
+                       firstpayload = IKEV2_PAYLOAD_NOTIFY;
+       }
+       /* add terminator, if there is already a payload */
+       if (firstpayload != IKEV2_PAYLOAD_NONE)
+               if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1)
+                       goto done;
+       ret = ikev2_msg_send_encrypt(env, sa, &buf,
+           IKEV2_EXCHANGE_INFORMATIONAL, firstpayload, 1);
+       if (ret != -1)
+               msg->msg_responded = 1;
+ done:
+       ibuf_release(buf);
+       return (ret);
+}
+
 void
 ikev2_resp_recv(struct iked *env, struct iked_message *msg,
     struct ike_header *hdr)
@@ -2152,13 +2263,9 @@ ikev2_resp_recv(struct iked *env, struct iked_message *msg,
                        ikev2_send_error(env, sa, msg, hdr->ike_exchange);
                break;
        case IKEV2_EXCHANGE_INFORMATIONAL:
-               if (sa_stateok(sa, IKEV2_STATE_AUTH_REQUEST) &&
-                   !msg->msg_responded && !msg->msg_error) {
-                       (void)ikev2_send_ike_e(env, sa, NULL,
-                           IKEV2_PAYLOAD_NONE, IKEV2_EXCHANGE_INFORMATIONAL,
-                           1);
-                       msg->msg_responded = 1;
-               }
+               if (msg->msg_update_sa_addresses)
+                       ikev2_update_sa_addresses(env, sa);
+               (void)ikev2_resp_informational(env, sa, msg);
                break;
        default:
                break;
@@ -2451,6 +2558,11 @@ ikev2_resp_ike_auth(struct iked *env, struct iked_sa *sa)
            (len = ikev2_add_ipcompnotify(env, e, &pld, len, sa)) == -1)
                goto done;
 
+       /* MOBIKE */
+       if (sa->sa_mobike &&
+           (len = ikev2_add_mobike(env, e, &pld, len, sa)) == -1)
+               goto done;
+
        if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_SA) == -1)
                goto done;
 
@@ -3151,10 +3263,13 @@ ikev2_ikesa_enable(struct iked *env, struct iked_sa *sa, struct iked_sa *nsa)
        nsa->sa_natt = sa->sa_natt;
        nsa->sa_udpencap = sa->sa_udpencap;
        nsa->sa_usekeepalive = sa->sa_usekeepalive;
+       nsa->sa_mobike = sa->sa_mobike;
 
        /* Transfer old addresses */
        memcpy(&nsa->sa_local, &sa->sa_local, sizeof(nsa->sa_local));
        memcpy(&nsa->sa_peer, &sa->sa_peer, sizeof(nsa->sa_peer));
+       memcpy(&nsa->sa_peer_loaded, &sa->sa_peer_loaded,
+           sizeof(nsa->sa_peer_loaded));
 
        /* Transfer all Child SAs and flows from the old IKE SA */
        for (flow = TAILQ_FIRST(&sa->sa_flows); flow != NULL;
@@ -5104,6 +5219,7 @@ ikev2_childsa_enable(struct iked *env, struct iked_sa *sa)
 {
        struct iked_childsa     *csa, *ocsa;
        struct iked_flow        *flow, *oflow;
+       int                      peer_changed, reload;
 
        if (sa->sa_ipcomp && sa->sa_cpi_in && sa->sa_cpi_out &&
            ikev2_ipcomp_enable(env, sa) == -1)
@@ -5136,9 +5252,26 @@ ikev2_childsa_enable(struct iked *env, struct iked_sa *sa)
                    print_spi(csa->csa_spi.spi, csa->csa_spi.spi_size));
        }
 
+       peer_changed = (memcmp(&sa->sa_peer_loaded, &sa->sa_peer,
+           sizeof(sa->sa_peer_loaded)) != 0);
+
        TAILQ_FOREACH(flow, &sa->sa_flows, flow_entry) {
-               if (flow->flow_loaded)
-                       continue;
+               /* re-load the flow if the peer for the flow has changed */
+               reload = 0;
+               if (flow->flow_loaded) {
+                       if (!peer_changed) {
+                               log_debug("%s: flow already loaded %p",
+                                   __func__, flow);
+                               continue;
+                       }
+                       RB_REMOVE(iked_flows, &env->sc_activeflows, flow);
+                       /* flow might be shared w/other SA */
+                       if (!flow->flow_replacing ||
+                           flow_replace(env, flow) != 0)
+                               (void)pfkey_flow_delete(env->sc_pfkey, flow);
+                       flow->flow_loaded = 0; /* we did RB_REMOVE */
+                       reload = 1;
+               }
 
                if (pfkey_flow_add(env->sc_pfkey, flow) != 0) {
                        log_debug("%s: failed to load flow", __func__);
@@ -5150,12 +5283,24 @@ ikev2_childsa_enable(struct iked *env, struct iked_sa *sa)
                        log_debug("%s: replaced old flow %p with %p",
                            __func__, oflow, flow);
                        oflow->flow_loaded = 0;
+                       oflow->flow_replacing = 0;
                        RB_REMOVE(iked_flows, &env->sc_activeflows, oflow);
+                       flow->flow_replacing = 1;
                }
 
                RB_INSERT(iked_flows, &env->sc_activeflows, flow);
 
-               log_debug("%s: loaded flow %p", __func__, flow);
+               log_debug("%s: %sloaded flow %p", __func__,
+                   reload ? "re" : "", flow);
+       }
+
+       /* remember the current address for ikev2_update_sa_addresses()  */
+       if (peer_changed) {
+               memcpy(&sa->sa_peer_loaded, &sa->sa_peer,
+                   sizeof(sa->sa_peer_loaded));
+               log_debug("%s: remember SA peer %s", __func__,
+                   print_host((struct sockaddr *)&sa->sa_peer_loaded.addr,
+                   NULL, 0));
        }
 
        return (0);
@@ -5721,3 +5866,81 @@ ikev2_cp_fixaddr(struct iked_sa *sa, struct iked_addr *addr,
        }
        return (0);
 }
+
+int
+ikev2_update_sa_addresses(struct iked *env, struct iked_sa *sa)
+{
+       struct iked_childsa     *csa;
+       struct iked_flow        *flow, *oflow;
+       struct iked_message     *msg;
+
+       if (!sa_stateok(sa, IKEV2_STATE_ESTABLISHED))
+               return -1;
+
+       log_info("%s: old %s new %s", __func__,
+           print_host((struct sockaddr *)&sa->sa_peer_loaded.addr, NULL, 0),
+           print_host((struct sockaddr *)&sa->sa_peer.addr, NULL, 0));
+
+       TAILQ_FOREACH(csa, &sa->sa_childsas, csa_entry) {
+               if (!csa->csa_loaded)
+                       continue;
+               if (pfkey_sa_update_addresses(env->sc_pfkey, csa) != 0)
+                       log_debug("%s: failed to update sa", __func__);
+       }
+
+       /* delete and re-add flows */
+       TAILQ_FOREACH(flow, &sa->sa_flows, flow_entry) {
+               if (flow->flow_loaded) {
+                       RB_REMOVE(iked_flows, &env->sc_activeflows, flow);
+                       /* flow might be shared w/other SA */
+                       if (!flow->flow_replacing ||
+                           flow_replace(env, flow) != 0)
+                               (void)pfkey_flow_delete(env->sc_pfkey, flow);
+                       flow->flow_loaded = 0;
+               }
+               /* update IPcomp flows */
+               if (flow->flow_ipcomp) {
+                       struct iked_addr *addr =
+                           (flow->flow_dir == IPSP_DIRECTION_OUT) ?
+                           &flow->flow_dst :
+                           &flow->flow_src;
+                       memcpy(addr, &sa->sa_peer, sizeof(sa->sa_peer));
+                       socket_setport((struct sockaddr *)&addr->addr, 0);
+                       addr->addr_port = 0;
+                       addr->addr_mask = (addr->addr_af == AF_INET) ? 32 : 128;
+               }
+               if (pfkey_flow_add(env->sc_pfkey, flow) != 0)
+                       log_debug("%s: failed to add flow %p", __func__, flow);
+               if (!flow->flow_loaded)
+                       continue;
+               if ((oflow = RB_FIND(iked_flows, &env->sc_activeflows, flow))
+                   != NULL) {
+                       log_debug("%s: replaced old flow %p with %p",
+                           __func__, oflow, flow);
+                       oflow->flow_loaded = 0;
+                       oflow->flow_replacing = 0;
+                       RB_REMOVE(iked_flows, &env->sc_activeflows, oflow);
+                       flow->flow_replacing = 1;
+               }
+               RB_INSERT(iked_flows, &env->sc_activeflows, flow);
+       }
+
+       /* update pending requests and responses */
+       TAILQ_FOREACH(msg, &sa->sa_requests, msg_entry) {
+               msg->msg_local = sa->sa_local.addr;
+               msg->msg_locallen = sa->sa_local.addr.ss_len;
+               msg->msg_peer = sa->sa_peer.addr;
+               msg->msg_peerlen = sa->sa_peer.addr.ss_len;
+       }
+       TAILQ_FOREACH(msg, &sa->sa_responses, msg_entry) {
+               msg->msg_local = sa->sa_local.addr;
+               msg->msg_locallen = sa->sa_local.addr.ss_len;
+               msg->msg_peer = sa->sa_peer.addr;
+               msg->msg_peerlen = sa->sa_peer.addr.ss_len;
+       }
+
+       /* Update sa_peer_loaded, to match in-kernel information */
+       memcpy(&sa->sa_peer_loaded, &sa->sa_peer, sizeof(sa->sa_peer_loaded));
+
+       return 0;
+}
index 79c9631..e94d699 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ikev2_msg.c,v 1.52 2017/04/26 10:42:38 henning Exp $  */
+/*     $OpenBSD: ikev2_msg.c,v 1.53 2017/11/27 18:39:35 patrick Exp $  */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -184,6 +184,7 @@ ikev2_msg_cleanup(struct iked *env, struct iked_message *msg)
                ibuf_release(msg->msg_id.id_buf);
                ibuf_release(msg->msg_cert.id_buf);
                ibuf_release(msg->msg_cookie);
+               ibuf_release(msg->msg_cookie2);
 
                msg->msg_nonce = NULL;
                msg->msg_ke = NULL;
@@ -191,6 +192,7 @@ ikev2_msg_cleanup(struct iked *env, struct iked_message *msg)
                msg->msg_id.id_buf = NULL;
                msg->msg_cert.id_buf = NULL;
                msg->msg_cookie = NULL;
+               msg->msg_cookie2 = NULL;
 
                config_free_proposals(&msg->msg_proposals, 0);
        }
index 5724520..38a05e8 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ikev2_pld.c,v 1.62 2017/04/13 07:04:09 patrick Exp $  */
+/*     $OpenBSD: ikev2_pld.c,v 1.63 2017/11/27 18:39:35 patrick Exp $  */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -1153,6 +1153,8 @@ ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld,
                                msg->msg_sa->sa_usekeepalive = 1;
                }
                print_hex(md, 0, sizeof(md));
+               /* remember for MOBIKE */
+               msg->msg_parent->msg_natt_rcvd = 1;
                break;
        case IKEV2_N_AUTHENTICATION_FAILED:
                if (!msg->msg_e) {
@@ -1292,6 +1294,66 @@ ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld,
                        msg->msg_sa->sa_cpi_out = betoh16(cpi);
                }
                break;
+       case IKEV2_N_MOBIKE_SUPPORTED:
+               if (!msg->msg_e) {
+                       log_debug("%s: N_MOBIKE_SUPPORTED not encrypted",
+                           __func__);
+                       return (-1);
+               }
+               if (len != 0) {
+                       log_debug("%s: ignoring malformed mobike"
+                           " notification: %zu", __func__, len);
+                       return (0);
+               }
+               if (!env->sc_mobike) {
+                       log_debug("%s: mobike disabled", __func__);
+                       return (0);
+               }
+               msg->msg_sa->sa_mobike = 1;
+               /* enforce natt */
+               msg->msg_sa->sa_natt = 1;
+               break;
+       case IKEV2_N_UPDATE_SA_ADDRESSES:
+               if (!msg->msg_e) {
+                       log_debug("%s: N_UPDATE_SA_ADDRESSES not encrypted",
+                           __func__);
+                       return (-1);
+               }
+               if (!msg->msg_sa->sa_mobike) {
+                       log_debug("%s: ignoring update sa addresses"
+                           " notification w/o mobike: %zu", __func__, len);
+                       return (0);
+               }
+               if (len != 0) {
+                       log_debug("%s: ignoring malformed update sa addresses"
+                           " notification: %zu", __func__, len);
+                       return (0);
+               }
+               msg->msg_parent->msg_update_sa_addresses = 1;
+               break;
+       case IKEV2_N_COOKIE2:
+               if (!msg->msg_e) {
+                       log_debug("%s: N_COOKIE2 not encrypted",
+                           __func__);
+                       return (-1);
+               }
+               if (!msg->msg_sa->sa_mobike) {
+                       log_debug("%s: ignoring cookie2 notification"
+                           " w/o mobike: %zu", __func__, len);
+                       return (0);
+               }
+               if (len < IKED_COOKIE2_MIN || len > IKED_COOKIE2_MAX) {
+                       log_debug("%s: ignoring malformed cookie2"
+                           " notification: %zu", __func__, len);
+                       return (0);
+               }
+               ibuf_release(msg->msg_cookie2); /* should not happen */
+               if ((msg->msg_cookie2 = ibuf_new(buf, len)) == NULL) {
+                       log_debug("%s: failed to get peer cookie2", __func__);
+                       return (-1);
+               }
+               msg->msg_parent->msg_cookie2 = msg->msg_cookie2;
+               break;
        case IKEV2_N_COOKIE:
                if (msg->msg_e) {
                        log_debug("%s: N_COOKIE encrypted",
index d6c4806..5a38333 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parse.y,v 1.66 2017/11/15 15:45:02 patrick Exp $      */
+/*     $OpenBSD: parse.y,v 1.67 2017/11/27 18:39:35 patrick Exp $      */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -99,6 +99,7 @@ static int             debug = 0;
 static int              rules = 0;
 static int              passive = 0;
 static int              decouple = 0;
+static int              mobike = 1;
 static char            *ocsp_url = NULL;
 
 struct ipsec_xf {
@@ -384,7 +385,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 IKELIFETIME
+%token IPCOMP OCSP IKELIFETIME MOBIKE NOMOBIKE
 %token <v.string>              STRING
 %token <v.number>              NUMBER
 %type  <v.string>              string
@@ -445,6 +446,8 @@ set         : SET ACTIVE    { passive = 0; }
                | SET PASSIVE   { passive = 1; }
                | SET COUPLE    { decouple = 0; }
                | SET DECOUPLE  { decouple = 1; }
+               | SET MOBIKE    { mobike = 1; }
+               | SET NOMOBIKE  { mobike = 0; }
                | SET OCSP STRING               {
                        if ((ocsp_url = strdup($3)) == NULL) {
                                yyerror("cannot set ocsp_url");
@@ -1126,7 +1129,9 @@ lookup(char *s)
                { "ipcomp",             IPCOMP },
                { "lifetime",           LIFETIME },
                { "local",              LOCAL },
+               { "mobike",             MOBIKE },
                { "name",               NAME },
+               { "nomobike",           NOMOBIKE },
                { "ocsp",               OCSP },
                { "passive",            PASSIVE },
                { "peer",               PEER },
@@ -1496,6 +1501,7 @@ parse_config(const char *filename, struct iked *x_env)
 
        free(ocsp_url);
 
+       mobike = 1;
        decouple = passive = 0;
        ocsp_url = NULL;
 
@@ -1508,6 +1514,7 @@ parse_config(const char *filename, struct iked *x_env)
 
        env->sc_passive = passive ? 1 : 0;
        env->sc_decoupled = decouple ? 1 : 0;
+       env->sc_mobike = mobike;
        env->sc_ocsp_url = ocsp_url;
 
        if (!rules)
index bcee41e..d99ebcd 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: pfkey.c,v 1.58 2017/04/18 02:29:56 deraadt Exp $      */
+/*     $OpenBSD: pfkey.c,v 1.59 2017/11/27 18:39:35 patrick Exp $      */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -45,6 +45,9 @@
 #define PFKEYV2_CHUNK sizeof(uint64_t)
 #define PFKEY_REPLY_TIMEOUT 1000
 
+/* only used internally */
+#define IKED_SADB_UPDATE_SA_ADDRESSES 0xff
+
 static uint32_t sadb_msg_seq = 0;
 static unsigned int sadb_decoupled = 0;
 static unsigned int sadb_ipv6refcnt = 0;
@@ -435,17 +438,18 @@ pfkey_sa(int sd, uint8_t satype, uint8_t action, struct iked_childsa *sa)
 {
        struct sadb_msg          smsg;
        struct sadb_sa           sadb;
-       struct sadb_address      sa_src, sa_dst;
+       struct sadb_address      sa_src, sa_dst, sa_pxy;
        struct sadb_key          sa_authkey, sa_enckey;
        struct sadb_lifetime     sa_ltime_hard, sa_ltime_soft;
        struct sadb_x_udpencap   udpencap;
        struct sadb_x_tag        sa_tag;
        char                    *tag = NULL;
        struct sadb_x_tap        sa_tap;
-       struct sockaddr_storage  ssrc, sdst;
+       struct sockaddr_storage  ssrc, sdst, spxy;
        struct sadb_ident       *sa_srcid, *sa_dstid;
        struct iked_lifetime    *lt;
        struct iked_policy      *pol;
+       struct iked_addr        *dst;
        struct iovec             iov[IOV_CNT];
        uint32_t                 jitter;
        int                      iov_cnt;
@@ -467,13 +471,26 @@ pfkey_sa(int sd, uint8_t satype, uint8_t action, struct iked_childsa *sa)
                return (-1);
        }
 
+       dst = (action == IKED_SADB_UPDATE_SA_ADDRESSES &&
+           sa->csa_dir == IPSP_DIRECTION_OUT) ?
+           &sa->csa_ikesa->sa_peer_loaded :
+           sa->csa_peer;
        bzero(&sdst, sizeof(sdst));
-       memcpy(&sdst, &sa->csa_peer->addr, sizeof(sdst));
+       memcpy(&sdst, &dst->addr, sizeof(sdst));
        if (socket_af((struct sockaddr *)&sdst, 0) == -1) {
                log_warn("%s: invalid address", __func__);
                return (-1);
        }
 
+       bzero(&spxy, sizeof(spxy));
+       if (dst != sa->csa_peer) {
+               memcpy(&spxy, &sa->csa_peer->addr, sizeof(spxy));
+               if (socket_af((struct sockaddr *)&spxy, 0) == -1) {
+                       log_warn("%s: invalid address", __func__);
+                       return (-1);
+               }
+       }
+
        bzero(&smsg, sizeof(smsg));
        smsg.sadb_msg_version = PF_KEY_V2;
        smsg.sadb_msg_seq = ++sadb_msg_seq;
@@ -503,6 +520,10 @@ pfkey_sa(int sd, uint8_t satype, uint8_t action, struct iked_childsa *sa)
        sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
        sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
 
+       bzero(&sa_pxy, sizeof(sa_pxy));
+       sa_pxy.sadb_address_len = (sizeof(sa_pxy) + ROUNDUP(spxy.ss_len)) / 8;
+       sa_pxy.sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
+
        bzero(&sa_authkey, sizeof(sa_authkey));
        bzero(&sa_enckey, sizeof(sa_enckey));
        bzero(&udpencap, sizeof udpencap);
@@ -524,6 +545,11 @@ pfkey_sa(int sd, uint8_t satype, uint8_t action, struct iked_childsa *sa)
                    ntohs(udpencap.sadb_x_udpencap_port));
        }
 
+       if (action == IKED_SADB_UPDATE_SA_ADDRESSES) {
+               smsg.sadb_msg_type = SADB_UPDATE;
+               goto send;
+       }
+
        if ((action == SADB_ADD || action == SADB_UPDATE) &&
            !sa->csa_persistent && (lt->lt_bytes || lt->lt_seconds)) {
                sa_ltime_hard.sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
@@ -655,6 +681,17 @@ pfkey_sa(int sd, uint8_t satype, uint8_t action, struct iked_childsa *sa)
        smsg.sadb_msg_len += sa_dst.sadb_address_len;
        iov_cnt++;
 
+       if (spxy.ss_len) {
+               /* pxy addr */
+               iov[iov_cnt].iov_base = &sa_pxy;
+               iov[iov_cnt].iov_len = sizeof(sa_pxy);
+               iov_cnt++;
+               iov[iov_cnt].iov_base = &spxy;
+               iov[iov_cnt].iov_len = ROUNDUP(spxy.ss_len);
+               smsg.sadb_msg_len += sa_pxy.sadb_address_len;
+               iov_cnt++;
+       }
+
        if (sa_ltime_soft.sadb_lifetime_len) {
                /* soft lifetime */
                iov[iov_cnt].iov_base = &sa_ltime_soft;
@@ -1342,6 +1379,24 @@ pfkey_sa_add(int fd, struct iked_childsa *sa, struct iked_childsa *last)
        return (0);
 }
 
+int
+pfkey_sa_update_addresses(int fd, struct iked_childsa *sa)
+{
+       uint8_t          satype;
+
+       if (!sa->csa_ikesa)
+               return (-1);
+       /* check if peer has changed */
+       if (sa->csa_ikesa->sa_peer_loaded.addr.ss_family == AF_UNSPEC ||
+           memcmp(&sa->csa_ikesa->sa_peer_loaded, &sa->csa_ikesa->sa_peer,
+           sizeof(sa->csa_ikesa->sa_peer_loaded)) == 0)
+               return (0);
+       if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1)
+               return (-1);
+       log_debug("%s: spi %s", __func__, print_spi(sa->csa_spi.spi, 4));
+       return pfkey_sa(fd, satype, IKED_SADB_UPDATE_SA_ADDRESSES, sa);
+}
+
 int
 pfkey_sa_delete(int fd, struct iked_childsa *sa)
 {
index 0719323..a7c71de 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: policy.c,v 1.46 2017/03/13 18:48:16 mikeb Exp $       */
+/*     $OpenBSD: policy.c,v 1.47 2017/11/27 18:39:35 patrick Exp $     */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -404,6 +404,45 @@ sa_free(struct iked *env, struct iked_sa *sa)
        config_free_sa(env, sa);
 }
 
+/* oflow did replace active flow, so we need to re-activate a matching flow */
+int
+flow_replace(struct iked *env, struct iked_flow *oflow)
+{
+       struct iked_sa          *sa;
+       struct iked_flow        *flow, *other;
+
+       if (!oflow->flow_loaded)
+               return (-1);
+       RB_FOREACH(sa, iked_sas, &env->sc_sas) {
+               if (oflow->flow_ikesa == sa ||
+                   sa->sa_state != IKEV2_STATE_ESTABLISHED)
+                       continue;
+               TAILQ_FOREACH(flow, &sa->sa_flows, flow_entry) {
+                       if (flow == oflow ||
+                           flow->flow_loaded || !flow_equal(flow, oflow))
+                               continue;
+                       if ((other = RB_FIND(iked_flows, &env->sc_activeflows,
+                           flow)) != NULL) {
+                               /* XXX should not happen */
+                               log_debug("%s: found flow %p for %p/%p",
+                                   __func__, other, flow, other);
+                               return (-1);
+                       }
+                       if (pfkey_flow_add(env->sc_pfkey, flow) != 0) {
+                               log_debug("%s: failed to load flow", __func__);
+                               return (-1);
+                       }
+                       RB_INSERT(iked_flows, &env->sc_activeflows, flow);
+                       log_debug("%s: loaded flow %p replaces %p", __func__,
+                           flow, oflow);
+                       /* check for matching flow if we get deleted, too */
+                       flow->flow_replacing = 1;
+                       return (0);
+               }
+       }
+       return (-1);
+}
+
 void
 sa_free_flows(struct iked *env, struct iked_saflows *head)
 {
@@ -417,7 +456,9 @@ sa_free_flows(struct iked *env, struct iked_saflows *head)
                if (flow->flow_loaded)
                        RB_REMOVE(iked_flows, &env->sc_activeflows, flow);
                TAILQ_REMOVE(head, flow, flow_entry);
-               (void)pfkey_flow_delete(env->sc_pfkey, flow);
+               if (!flow->flow_replacing ||
+                   flow_replace(env, flow) != 0)
+                       (void)pfkey_flow_delete(env->sc_pfkey, flow);
                flow_free(flow);
        }
 }
index df358ee..d905323 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: types.h,v 1.28 2017/03/27 10:43:53 mikeb Exp $        */
+/*     $OpenBSD: types.h,v 1.29 2017/11/27 18:39:35 patrick Exp $      */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -59,6 +59,9 @@
 #define IKED_COOKIE_MIN                1       /* min 1 bytes */
 #define IKED_COOKIE_MAX                64      /* max 64 bytes */
 
+#define IKED_COOKIE2_MIN       8       /* min 8 bytes */
+#define IKED_COOKIE2_MAX       64      /* max 64 bytes */
+
 #define IKED_ID_SIZE           1024    /* XXX should be dynamic */
 #define IKED_PSK_SIZE          1024    /* XXX should be dynamic */
 #define IKED_MSGBUF_MAX                8192
@@ -99,6 +102,7 @@ enum imsg_type {
        IMSG_CTL_DECOUPLE,
        IMSG_CTL_ACTIVE,
        IMSG_CTL_PASSIVE,
+       IMSG_CTL_MOBIKE,
        IMSG_COMPILE,
        IMSG_UDP_SOCKET,
        IMSG_PFKEY_SOCKET,