Merge branch 'childless'
authorTobias Brunner <tobias@strongswan.org>
Thu, 25 Apr 2019 13:32:02 +0000 (15:32 +0200)
committerTobias Brunner <tobias@strongswan.org>
Thu, 25 Apr 2019 13:32:02 +0000 (15:32 +0200)
Adds support for childless initiation of IKE_SAs (RFC 6023) e.g. to
force a separate DH exchange for all CHILD_SAs including the first one.

Also allows the initiation of only the IKE_SA via swanctl --initiate if
the peer supports this extension.

Closes strongswan/strongswan#99.

41 files changed:
src/charon-cmd/cmd/cmd_connection.c
src/charon-nm/nm/nm_service.c
src/conftest/config.c
src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_service.c
src/frontends/osx/charon-xpc/xpc_dispatch.c
src/libcharon/config/ike_cfg.c
src/libcharon/config/ike_cfg.h
src/libcharon/control/controller.c
src/libcharon/control/controller.h
src/libcharon/plugins/ha/ha_tunnel.c
src/libcharon/plugins/load_tester/load_tester_config.c
src/libcharon/plugins/medcli/medcli_config.c
src/libcharon/plugins/medsrv/medsrv_config.c
src/libcharon/plugins/sql/sql_config.c
src/libcharon/plugins/stroke/stroke_config.c
src/libcharon/plugins/uci/uci_config.c
src/libcharon/plugins/vici/README.md
src/libcharon/plugins/vici/vici_config.c
src/libcharon/plugins/vici/vici_control.c
src/libcharon/sa/ike_sa.h
src/libcharon/sa/ikev2/tasks/child_create.c
src/libcharon/sa/ikev2/tasks/ike_init.c
src/libcharon/tests/Makefile.am
src/libcharon/tests/exchange_tests.h
src/libcharon/tests/suites/test_childless.c [new file with mode: 0644]
src/libcharon/tests/suites/test_ike_cfg.c
src/libcharon/tests/suites/test_peer_cfg.c
src/libcharon/tests/utils/exchange_test_asserts.h
src/libcharon/tests/utils/exchange_test_helper.c
src/libcharon/tests/utils/exchange_test_helper.h
src/swanctl/commands/initiate.c
src/swanctl/swanctl.opt
testing/tests/swanctl/net2net-childless/description.txt [new file with mode: 0755]
testing/tests/swanctl/net2net-childless/evaltest.dat [new file with mode: 0755]
testing/tests/swanctl/net2net-childless/hosts/moon/etc/strongswan.conf [new file with mode: 0755]
testing/tests/swanctl/net2net-childless/hosts/moon/etc/swanctl/swanctl.conf [new file with mode: 0755]
testing/tests/swanctl/net2net-childless/hosts/sun/etc/strongswan.conf [new file with mode: 0755]
testing/tests/swanctl/net2net-childless/hosts/sun/etc/swanctl/swanctl.conf [new file with mode: 0755]
testing/tests/swanctl/net2net-childless/posttest.dat [new file with mode: 0755]
testing/tests/swanctl/net2net-childless/pretest.dat [new file with mode: 0755]
testing/tests/swanctl/net2net-childless/test.conf [new file with mode: 0755]

index 1cf431f..b91c898 100644 (file)
@@ -142,9 +142,13 @@ static peer_cfg_t* create_peer_cfg(private_cmd_connection_t *this)
 {
        ike_cfg_t *ike_cfg;
        peer_cfg_t *peer_cfg;
-       uint16_t local_port, remote_port = IKEV2_UDP_PORT;
-       ike_version_t version = IKE_ANY;
        proposal_t *proposal;
+       ike_cfg_create_t ike = {
+               .local = "0.0.0.0",
+               .remote = this->host,
+               .remote_port = IKEV2_UDP_PORT,
+               .fragmentation = FRAGMENTATION_YES,
+       };
        peer_cfg_create_t peer = {
                .cert_policy = CERT_SEND_IF_ASKED,
                .unique = UNIQUE_REPLACE,
@@ -161,7 +165,7 @@ static peer_cfg_t* create_peer_cfg(private_cmd_connection_t *this)
                case PROF_V2_PUB:
                case PROF_V2_EAP:
                case PROF_V2_PUB_EAP:
-                       version = IKEV2;
+                       ike.version = IKEV2;
                        break;
                case PROF_V1_PUB_AM:
                case PROF_V1_XAUTH_AM:
@@ -173,17 +177,16 @@ static peer_cfg_t* create_peer_cfg(private_cmd_connection_t *this)
                case PROF_V1_XAUTH:
                case PROF_V1_XAUTH_PSK:
                case PROF_V1_HYBRID:
-                       version = IKEV1;
+                       ike.version = IKEV1;
                        break;
        }
 
-       local_port = charon->socket->get_port(charon->socket, FALSE);
-       if (local_port != IKEV2_UDP_PORT)
+       ike.local_port = charon->socket->get_port(charon->socket, FALSE);
+       if (ike.local_port != IKEV2_UDP_PORT)
        {
-               remote_port = IKEV2_NATT_PORT;
+               ike.remote_port = IKEV2_NATT_PORT;
        }
-       ike_cfg = ike_cfg_create(version, TRUE, FALSE, "0.0.0.0", local_port,
-                                       this->host, remote_port, FRAGMENTATION_NO, 0);
+       ike_cfg = ike_cfg_create(&ike);
        if (this->ike_proposals->get_count(this->ike_proposals))
        {
                while (this->ike_proposals->remove_first(this->ike_proposals,
index e207ac8..1b07230 100644 (file)
@@ -381,8 +381,8 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
        NMSettingVpn *vpn;
        enumerator_t *enumerator;
        identification_t *user = NULL, *gateway = NULL;
-       const char *address, *str;
-       bool virtual, encap, proposal;
+       const char *str;
+       bool virtual, proposal;
        proposal_t *prop;
        ike_cfg_t *ike_cfg;
        peer_cfg_t *peer_cfg;
@@ -394,6 +394,13 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
        certificate_t *cert = NULL;
        x509_t *x509;
        bool agent = FALSE, smartcard = FALSE, loose_gateway_id = FALSE;
+       ike_cfg_create_t ike = {
+               .version = IKEV2,
+               .local = "%any",
+               .local_port = charon->socket->get_port(charon->socket, FALSE),
+               .remote_port = IKEV2_UDP_PORT,
+               .fragmentation = FRAGMENTATION_YES,
+       };
        peer_cfg_create_t peer = {
                .cert_policy = CERT_SEND_IF_ASKED,
                .unique = UNIQUE_REPLACE,
@@ -430,8 +437,8 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
                 priv->name);
        DBG4(DBG_CFG, "%s",
                 nm_setting_to_string(NM_SETTING(vpn)));
-       address = nm_setting_vpn_get_data_item(vpn, "address");
-       if (!address || !*address)
+       ike.remote = (char*)nm_setting_vpn_get_data_item(vpn, "address");
+       if (!ike.remote || !*ike.remote)
        {
                g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
                                        "Gateway address missing.");
@@ -440,7 +447,7 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
        str = nm_setting_vpn_get_data_item(vpn, "virtual");
        virtual = streq(str, "yes");
        str = nm_setting_vpn_get_data_item(vpn, "encap");
-       encap = streq(str, "yes");
+       ike.force_encap = streq(str, "yes");
        str = nm_setting_vpn_get_data_item(vpn, "ipcomp");
        child.options |= streq(str, "yes") ? OPT_IPCOMP : 0;
        str = nm_setting_vpn_get_data_item(vpn, "method");
@@ -503,7 +510,7 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
                 * of the gateway as its identity. This identity will be used for
                 * certificate lookup and requires the configured IP/DNS to be
                 * included in the gateway certificate. */
-               gateway = identification_create_from_string((char*)address);
+               gateway = identification_create_from_string(ike.remote);
                DBG1(DBG_CFG, "using CA certificate, gateway identity '%Y'", gateway);
                loose_gateway_id = TRUE;
        }
@@ -634,10 +641,7 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
        /**
         * Set up configurations
         */
-       ike_cfg = ike_cfg_create(IKEV2, TRUE, encap, "%any",
-                                                        charon->socket->get_port(charon->socket, FALSE),
-                                                       (char*)address, IKEV2_UDP_PORT,
-                                                        FRAGMENTATION_YES, 0);
+       ike_cfg = ike_cfg_create(&ike);
 
        str = nm_setting_vpn_get_data_item(vpn, "proposal");
        proposal = streq(str, "yes");
index d926dfc..ff47a77 100644 (file)
@@ -107,14 +107,21 @@ static ike_cfg_t *load_ike_config(private_config_t *this,
        ike_cfg_t *ike_cfg;
        proposal_t *proposal;
        char *token;
+       ike_cfg_create_t ike = {
+               .version = IKEV2,
+               .local = settings->get_str(settings, "configs.%s.lhost",
+                                                                  "%any", config),
+               .local_port = settings->get_int(settings, "configs.%s.lport",
+                                                                       500, config),
+               .remote = settings->get_str(settings, "configs.%s.rhost",
+                                                                       "%any", config),
+               .remote_port = settings->get_int(settings, "configs.%s.rport",
+                                                                       500, config),
+               .force_encap = settings->get_bool(settings, "configs.%s.fake_nat",
+                                                                       FALSE, config),
+       };
 
-       ike_cfg = ike_cfg_create(IKEV2, TRUE,
-               settings->get_bool(settings, "configs.%s.fake_nat", FALSE, config),
-               settings->get_str(settings, "configs.%s.lhost", "%any", config),
-               settings->get_int(settings, "configs.%s.lport", 500, config),
-               settings->get_str(settings, "configs.%s.rhost", "%any", config),
-               settings->get_int(settings, "configs.%s.rport", 500, config),
-               FRAGMENTATION_NO, 0);
+       ike_cfg = ike_cfg_create(&ike);
        token = settings->get_str(settings, "configs.%s.proposal", NULL, config);
        if (token)
        {
index a6b4505..de63489 100644 (file)
@@ -742,6 +742,13 @@ static job_requeue_t initiate(private_android_service_t *this)
        proposal_t *proposal;
        ike_sa_t *ike_sa;
        auth_cfg_t *auth;
+       ike_cfg_create_t ike = {
+               .version = IKEV2,
+               .local = "0.0.0.0",
+               .local_port = charon->socket->get_port(charon->socket, FALSE),
+               .foce_encap = TRUE,
+               .fragmentation = FRAGMENTATION_YES,
+       };
        peer_cfg_create_t peer = {
                .cert_policy = CERT_ALWAYS_SEND,
                .unique = UNIQUE_REPLACE,
@@ -761,23 +768,20 @@ static job_requeue_t initiate(private_android_service_t *this)
                .dpd_action = ACTION_RESTART,
                .close_action = ACTION_RESTART,
        };
-       char *type, *server, *remote_id;
-       int port;
-       bool certreq;
+       char *type, *remote_id;
 
        if (android_sdk_version >= ANDROID_LOLLIPOP)
        {   /* only try once and notify the GUI on Android 5+ where we have a blocking TUN device */
                peer.keyingtries = 1;
        }
 
-       server = this->settings->get_str(this->settings, "connection.server", NULL);
-       port = this->settings->get_int(this->settings, "connection.port",
-                                                                  IKEV2_UDP_PORT);
-       certreq = this->settings->get_bool(this->settings, "connection.certreq",
-                                                                          TRUE);
-       ike_cfg = ike_cfg_create(IKEV2, certreq, TRUE, "0.0.0.0",
-                                                        charon->socket->get_port(charon->socket, FALSE),
-                                                        server, port, FRAGMENTATION_YES, 0);
+       ike.remote = this->settings->get_str(this->settings, "connection.server",
+                                                                                NULL);
+       ike.remote_port = this->settings->get_int(this->settings, "connection.port",
+                                                                                         IKEV2_UDP_PORT);
+       ike.no_certreq = !this->settings->get_bool(this->settings,
+                                                                                          "connection.certreq", TRUE);
+       ike_cfg = ike_cfg_create(&ike);
        proposal = parse_proposal(this, PROTO_IKE, "connection.ike_proposal");
        if (proposal)
        {
index 0483199..51b2b50 100644 (file)
@@ -78,6 +78,14 @@ static peer_cfg_t* create_peer_cfg(char *name, char *host)
        ike_cfg_t *ike_cfg;
        peer_cfg_t *peer_cfg;
        uint16_t local_port, remote_port = IKEV2_UDP_PORT;
+       ike_cfg_create_t ike = {
+               .version = IKEV2,
+               .local = "0.0.0.0",
+               .remote = host,
+               .remote_port = IKEV2_UDP_PORT,
+               .no_certreq = TRUE,
+               .fragmentation = FRAGMENTATION_YES,
+       };
        peer_cfg_create_t peer = {
                .cert_policy = CERT_SEND_IF_ASKED,
                .unique = UNIQUE_REPLACE,
@@ -88,13 +96,12 @@ static peer_cfg_t* create_peer_cfg(char *name, char *host)
                .dpd = 30,
        };
 
-       local_port = charon->socket->get_port(charon->socket, FALSE);
-       if (local_port != IKEV2_UDP_PORT)
+       ike.local_port = charon->socket->get_port(charon->socket, FALSE);
+       if (ike.local_port != IKEV2_UDP_PORT)
        {
-               remote_port = IKEV2_NATT_PORT;
+               ike.remote_port = IKEV2_NATT_PORT;
        }
-       ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, "0.0.0.0", local_port,
-                                                        host, remote_port, FRAGMENTATION_NO, 0);
+       ike_cfg = ike_cfg_create(&ike);
        ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
        ike_cfg->add_proposal(ike_cfg, proposal_create_default_aead(PROTO_IKE));
        peer_cfg = peer_cfg_create(name, ike_cfg, &peer);
index 357c4a7..d99abbc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2018 Tobias Brunner
+ * Copyright (C) 2012-2019 Tobias Brunner
  * Copyright (C) 2005-2007 Martin Willi
  * Copyright (C) 2005 Jan Hutter
  * HSR Hochschule fuer Technik Rapperswil
@@ -101,11 +101,16 @@ struct private_ike_cfg_t {
        bool force_encap;
 
        /**
-        * use IKEv1 fragmentation
+        * use IKE fragmentation
         */
        fragmentation_t fragmentation;
 
        /**
+        * childless IKE_SAs
+        */
+       childless_t childless;
+
+       /**
         * DSCP value to use on sent IKE packets
         */
        uint8_t dscp;
@@ -140,6 +145,12 @@ METHOD(ike_cfg_t, fragmentation, fragmentation_t,
        return this->fragmentation;
 }
 
+METHOD(ike_cfg_t, childless, childless_t,
+       private_ike_cfg_t *this)
+{
+       return this->childless;
+}
+
 /**
  * Common function for resolve_me/other
  */
@@ -424,6 +435,7 @@ METHOD(ike_cfg_t, equals, bool,
                this->certreq == other->certreq &&
                this->force_encap == other->force_encap &&
                this->fragmentation == other->fragmentation &&
+               this->childless == other->childless &&
                streq(this->me, other->me) &&
                streq(this->other, other->other) &&
                this->my_port == other->my_port &&
@@ -609,13 +621,10 @@ bool ike_cfg_has_address(ike_cfg_t *cfg, host_t *addr, bool local)
        return found;
 }
 
-/**
- * Described in header.
+/*
+ * Described in header
  */
-ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap,
-                                                 char *me, uint16_t my_port,
-                                                 char *other, uint16_t other_port,
-                                                 fragmentation_t fragmentation, uint8_t dscp)
+ike_cfg_t *ike_cfg_create(ike_cfg_create_t *data)
 {
        private_ike_cfg_t *this;
 
@@ -625,6 +634,7 @@ ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap,
                        .send_certreq = _send_certreq,
                        .force_encap = _force_encap_,
                        .fragmentation = _fragmentation,
+                       .childless = _childless,
                        .resolve_me = _resolve_me,
                        .resolve_other = _resolve_other,
                        .match_me = _match_me,
@@ -644,24 +654,25 @@ ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap,
                        .destroy = _destroy,
                },
                .refcount = 1,
-               .version = version,
-               .certreq = certreq,
-               .force_encap = force_encap,
-               .fragmentation = fragmentation,
-               .me = strdup(me),
+               .version = data->version,
+               .certreq = !data->no_certreq,
+               .force_encap = data->force_encap,
+               .fragmentation = data->fragmentation,
+               .childless = data->childless,
+               .me = strdup(data->local),
                .my_ranges = linked_list_create(),
                .my_hosts = linked_list_create(),
-               .other = strdup(other),
+               .other = strdup(data->remote),
                .other_ranges = linked_list_create(),
                .other_hosts = linked_list_create(),
-               .my_port = my_port,
-               .other_port = other_port,
-               .dscp = dscp,
+               .my_port = data->local_port,
+               .other_port = data->remote_port,
+               .dscp = data->dscp,
                .proposals = linked_list_create(),
        );
 
-       parse_addresses(me, this->my_hosts, this->my_ranges);
-       parse_addresses(other, this->other_hosts, this->other_ranges);
+       parse_addresses(data->local, this->my_hosts, this->my_ranges);
+       parse_addresses(data->remote, this->other_hosts, this->other_ranges);
 
        return &this->public;
 }
index 49690c8..9c697da 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2018 Tobias Brunner
+ * Copyright (C) 2012-2019 Tobias Brunner
  * Copyright (C) 2005-2007 Martin Willi
  * Copyright (C) 2005 Jan Hutter
  * HSR Hochschule fuer Technik Rapperswil
@@ -25,7 +25,9 @@
 
 typedef enum ike_version_t ike_version_t;
 typedef enum fragmentation_t fragmentation_t;
+typedef enum childless_t childless_t;
 typedef struct ike_cfg_t ike_cfg_t;
+typedef struct ike_cfg_create_t ike_cfg_create_t;
 
 #include <library.h>
 #include <networking/host.h>
@@ -61,6 +63,18 @@ enum fragmentation_t {
 };
 
 /**
+ * Childless IKE_SAs (RFC 6023)
+ */
+enum childless_t {
+       /** Allow childless IKE_SAs as responder, but initiate regular IKE_SAs */
+       CHILDLESS_ALLOW,
+       /** Don't accept childless IKE_SAs as responder, don't initiate them */
+       CHILDLESS_NEVER,
+       /** Only accept the creation of childless IKE_SAs (also as responder) */
+       CHILDLESS_FORCE,
+};
+
+/**
  * enum strings for ike_version_t
  */
 extern enum_name_t *ike_version_names;
@@ -203,13 +217,20 @@ struct ike_cfg_t {
        bool (*force_encap) (ike_cfg_t *this);
 
        /**
-        * Use proprietary IKEv1 fragmentation
+        * Use IKE fragmentation
         *
         * @return                              TRUE to use fragmentation
         */
        fragmentation_t (*fragmentation) (ike_cfg_t *this);
 
        /**
+        * Whether to initiate/accept childless IKE_SAs
+        *
+        * @return                              initiate/accept childless IKE_SAs
+        */
+       childless_t (*childless)(ike_cfg_t *this);
+
+       /**
         * Get the DH group to use for IKE_SA setup.
         *
         * @return                              dh group to use for initialization
@@ -241,30 +262,43 @@ struct ike_cfg_t {
 };
 
 /**
- * Creates a ike_cfg_t object.
- *
- * Supplied hosts become owned by ike_cfg, strings get cloned.
+ * Data passed to the constructor of an ike_cfg_t object.
  *
- * me and other are comma separated lists of IP addresses, DNS names, IP ranges
- * or subnets. When initiating, the first non-range/subnet address is used
- * as address. When responding, a match is performed against all items in the
- * list.
+ * local and remote are comma separated lists of IP addresses, DNS names,
+ * IP ranges or subnets. When initiating, the first non-range/subnet address is
+ * used as address. When responding, a match is performed against all items in
+ * the list.
+ */
+struct ike_cfg_create_t {
+       /** IKE major version to use for this config */
+       ike_version_t version;
+       /** Address/DNS name of local peer (cloned) */
+       char *local;
+       /** IKE port to use as source, 500 uses IKEv2 port floating */
+       uint16_t local_port;
+       /** Address/DNS name of remote peer (cloned) */
+       char *remote;
+       /** IKE port to use as dest, 500 uses IKEv2 port floating */
+       uint16_t remote_port;
+       /** TRUE to not send any certificate requests */
+       bool no_certreq;
+       /** Enforce UDP encapsulation by faking NATD notify */
+       bool force_encap;
+       /** Use IKE fragmentation */
+       fragmentation_t fragmentation;
+       /** Childless IKE_SA configuration */
+       childless_t childless;
+       /** DSCP value to send IKE packets with */
+       uint8_t dscp;
+};
+
+/**
+ * Creates an ike_cfg_t object.
  *
- * @param version                      IKE major version to use for this config
- * @param certreq                      TRUE to send a certificate request
- * @param force_encap          enforce UDP encapsulation by faking NATD notify
- * @param me                           address/DNS name of local peer
- * @param my_port                      IKE port to use as source, 500 uses IKEv2 port floating
- * @param other                                address/DNS name of remote peer
- * @param other_port           IKE port to use as dest, 500 uses IKEv2 port floating
- * @param fragmentation                use IKEv1 fragmentation
- * @param dscp                         DSCP value to send IKE packets with
+ * @param data                         data for this ike_cfg
  * @return                                     ike_cfg_t object.
  */
-ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap,
-                                                 char *me, uint16_t my_port,
-                                                 char *other, uint16_t other_port,
-                                                 fragmentation_t fragmentation, uint8_t dscp);
+ike_cfg_t *ike_cfg_create(ike_cfg_create_t *data);
 
 /**
  * Determine the address family of the local or remote address(es).  If multiple
index 589c536..0c86275 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2015 Tobias Brunner
+ * Copyright (C) 2011-2019 Tobias Brunner
  * Copyright (C) 2007-2011 Martin Willi
  * Copyright (C) 2011 revosec AG
  * HSR Hochschule fuer Technik Rapperswil
@@ -265,19 +265,24 @@ METHOD(listener_t, ike_state_change, bool,
        {
                switch (state)
                {
-#ifdef ME
                        case IKE_ESTABLISHED:
-                       {       /* mediation connections are complete without CHILD_SA */
+                       {
+#ifdef ME
                                peer_cfg_t *peer_cfg = ike_sa->get_peer_cfg(ike_sa);
-
-                               if (peer_cfg->is_mediation(peer_cfg))
+#endif /* ME */
+                               /* we're done if we didn't initiate a CHILD_SA */
+                               if (!this->child_cfg
+#ifdef ME
+                                       /* the same is always true for mediation connections */
+                                       || peer_cfg->is_mediation(peer_cfg)
+#endif /* ME */
+                                       )
                                {
                                        this->status = SUCCESS;
                                        return listener_done(this);
                                }
                                break;
                        }
-#endif /* ME */
                        case IKE_DESTROYING:
                                return listener_done(this);
                        default:
@@ -414,7 +419,7 @@ METHOD(job_t, initiate_execute, job_requeue_t,
                                                                                                                peer_cfg);
        if (!ike_sa)
        {
-               listener->child_cfg->destroy(listener->child_cfg);
+               DESTROY_IF(listener->child_cfg);
                peer_cfg->destroy(peer_cfg);
                listener->status = FAILED;
                listener_done(listener);
@@ -446,7 +451,7 @@ METHOD(job_t, initiate_execute, job_requeue_t,
                                 "%d exceeds limit of %d", half_open, limit_half_open);
                        charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
                                                                                                                ike_sa);
-                       listener->child_cfg->destroy(listener->child_cfg);
+                       DESTROY_IF(listener->child_cfg);
                        listener->status = INVALID_STATE;
                        listener_done(listener);
                        return JOB_REQUEUE_NONE;
@@ -465,7 +470,7 @@ METHOD(job_t, initiate_execute, job_requeue_t,
                                         "limit of %d", jobs, limit_job_load);
                                charon->ike_sa_manager->checkin_and_destroy(
                                                                                                charon->ike_sa_manager, ike_sa);
-                               listener->child_cfg->destroy(listener->child_cfg);
+                               DESTROY_IF(listener->child_cfg);
                                listener->status = INVALID_STATE;
                                listener_done(listener);
                                return JOB_REQUEUE_NONE;
index af9baca..b4ccfce 100644 (file)
@@ -78,7 +78,7 @@ struct controller_t {
         * until the IKE_SA is established or failed.
         *
         * @param peer_cfg              peer_cfg to use for IKE_SA setup
-        * @param child_cfg             child_cfg to set up CHILD_SA from
+        * @param child_cfg             optional child_cfg to set up CHILD_SA from
         * @param cb                    logging callback
         * @param param                 parameter to include in each call of cb
         * @param timeout               timeout in ms to wait for callbacks, 0 to disable
index cfa896e..f942915 100644 (file)
@@ -190,6 +190,14 @@ static void setup_tunnel(private_ha_tunnel_t *this,
        auth_cfg_t *auth_cfg;
        child_cfg_t *child_cfg;
        traffic_selector_t *ts;
+       ike_cfg_create_t ike = {
+               .version = IKEV2,
+               .local = local,
+               .local_port = charon->socket->get_port(charon->socket, FALSE),
+               .remote = remote,
+               .remote_port = IKEV2_UDP_PORT,
+               .no_certreq = TRUE,
+       };
        peer_cfg_create_t peer = {
                .cert_policy = CERT_NEVER_SEND,
                .unique = UNIQUE_KEEP,
@@ -222,9 +230,7 @@ static void setup_tunnel(private_ha_tunnel_t *this,
        lib->credmgr->add_set(lib->credmgr, &this->creds.public);
 
        /* create config and backend */
-       ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, local,
-                                                        charon->socket->get_port(charon->socket, FALSE),
-                                                        remote, IKEV2_UDP_PORT, FRAGMENTATION_NO, 0);
+       ike_cfg = ike_cfg_create(&ike);
        ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
        ike_cfg->add_proposal(ike_cfg, proposal_create_default_aead(PROTO_IKE));
        peer_cfg = peer_cfg_create(HA_CFG_NAME, ike_cfg, &peer);
index 78be45f..6fb6673 100644 (file)
@@ -686,8 +686,12 @@ static peer_cfg_t* generate_config(private_load_tester_config_t *this, uint num)
        ike_cfg_t *ike_cfg;
        child_cfg_t *child_cfg;
        peer_cfg_t *peer_cfg;
-       char local[32], *remote;
+       char local[32];
        host_t *addr;
+       ike_cfg_create_t ike = {
+               .version = this->version,
+               .remote_port = IKEV2_UDP_PORT,
+       };
        peer_cfg_create_t peer = {
                .cert_policy = CERT_SEND_IF_ASKED,
                .unique = UNIQUE_NO,
@@ -726,28 +730,25 @@ static peer_cfg_t* generate_config(private_load_tester_config_t *this, uint num)
                {
                        snprintf(local, sizeof(local), "%s", this->initiator);
                }
-               remote = this->responder;
+               ike.remote = this->responder;
        }
        else
        {
                snprintf(local, sizeof(local), "%s", this->responder);
-               remote = this->initiator;
+               ike.remote = this->initiator;
        }
 
+       ike.local = local;
        if (this->port && num)
        {
-               ike_cfg = ike_cfg_create(this->version, TRUE, FALSE,
-                                                                local, this->port + num - 1,
-                                                                remote, IKEV2_NATT_PORT,
-                                                                FRAGMENTATION_NO, 0);
+               ike.local_port = this->port + num - 1;
+               ike.remote_port = IKEV2_NATT_PORT;
        }
        else
        {
-               ike_cfg = ike_cfg_create(this->version, TRUE, FALSE, local,
-                                                                charon->socket->get_port(charon->socket, FALSE),
-                                                                remote, IKEV2_UDP_PORT,
-                                                                FRAGMENTATION_NO, 0);
+               ike.local_port = charon->socket->get_port(charon->socket, FALSE);
        }
+       ike_cfg = ike_cfg_create(&ike);
        ike_cfg->add_proposal(ike_cfg, this->proposal->clone(this->proposal));
        peer_cfg = peer_cfg_create("load-test", ike_cfg, &peer);
 
index 789c01b..be42d7d 100644 (file)
@@ -87,9 +87,15 @@ static peer_cfg_t *build_mediation_config(private_medcli_config_t *this,
        auth_cfg_t *auth;
        ike_cfg_t *ike_cfg;
        peer_cfg_t *med_cfg;
+       ike_cfg_create_t ike = {
+               .version = IKEV2,
+               .local = "0.0.0.0",
+               .local_port = charon->socket->get_port(charon->socket, FALSE),
+               .remote_port = IKEV2_UDP_PORT,
+               .no_certreq = TRUE,
+       };
        peer_cfg_create_t peer = *defaults;
        chunk_t me, other;
-       char *address;
 
        /* query mediation server config:
         * - build ike_cfg/peer_cfg for mediation connection on-the-fly
@@ -98,14 +104,12 @@ static peer_cfg_t *build_mediation_config(private_medcli_config_t *this,
                        "SELECT Address, ClientConfig.KeyId, MediationServerConfig.KeyId "
                        "FROM MediationServerConfig JOIN ClientConfig",
                        DB_TEXT, DB_BLOB, DB_BLOB);
-       if (!e || !e->enumerate(e, &address, &me, &other))
+       if (!e || !e->enumerate(e, &ike.remote, &me, &other))
        {
                DESTROY_IF(e);
                return NULL;
        }
-       ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, "0.0.0.0",
-                                                        charon->socket->get_port(charon->socket, FALSE),
-                                                        address, IKEV2_UDP_PORT, FRAGMENTATION_NO, 0);
+       ike_cfg = ike_cfg_create(&ike);
        ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
        ike_cfg->add_proposal(ike_cfg, proposal_create_default_aead(PROTO_IKE));
 
@@ -397,6 +401,14 @@ METHOD(medcli_config_t, destroy, void,
 medcli_config_t *medcli_config_create(database_t *db)
 {
        private_medcli_config_t *this;
+       ike_cfg_create_t ike = {
+               .version = IKEV2,
+               .local = "0.0.0.0",
+               .local_port = charon->socket->get_port(charon->socket, FALSE),
+               .remote = "0.0.0.0",
+               .remote_port = IKEV2_UDP_PORT,
+               .no_certreq = TRUE,
+       };
 
        INIT(this,
                .public = {
@@ -410,10 +422,7 @@ medcli_config_t *medcli_config_create(database_t *db)
                .db = db,
                .rekey = lib->settings->get_time(lib->settings, "medcli.rekey", 1200),
                .dpd = lib->settings->get_time(lib->settings, "medcli.dpd", 300),
-               .ike = ike_cfg_create(IKEV2, FALSE, FALSE, "0.0.0.0",
-                                                         charon->socket->get_port(charon->socket, FALSE),
-                                                         "0.0.0.0", IKEV2_UDP_PORT,
-                                                         FRAGMENTATION_NO, 0),
+               .ike = ike_cfg_create(&ike),
        );
        this->ike->add_proposal(this->ike, proposal_create_default(PROTO_IKE));
        this->ike->add_proposal(this->ike, proposal_create_default_aead(PROTO_IKE));
index 6068022..b9dce8c 100644 (file)
@@ -130,6 +130,14 @@ METHOD(medsrv_config_t, destroy, void,
 medsrv_config_t *medsrv_config_create(database_t *db)
 {
        private_medsrv_config_t *this;
+       ike_cfg_create_t ike = {
+               .version = IKEV2,
+               .local = "0.0.0.0",
+               .local_port = charon->socket->get_port(charon->socket, FALSE),
+               .remote = "0.0.0.0",
+               .remote_port = IKEV2_UDP_PORT,
+               .no_certreq = TRUE,
+       };
 
        INIT(this,
                .public = {
@@ -143,10 +151,7 @@ medsrv_config_t *medsrv_config_create(database_t *db)
                .db = db,
                .rekey = lib->settings->get_time(lib->settings, "medsrv.rekey", 1200),
                .dpd = lib->settings->get_time(lib->settings, "medsrv.dpd", 300),
-               .ike = ike_cfg_create(IKEV2, FALSE, FALSE, "0.0.0.0",
-                                                         charon->socket->get_port(charon->socket, FALSE),
-                                                         "0.0.0.0", IKEV2_UDP_PORT,
-                                                         FRAGMENTATION_NO, 0),
+               .ike = ike_cfg_create(&ike),
        );
        this->ike->add_proposal(this->ike, proposal_create_default(PROTO_IKE));
        this->ike->add_proposal(this->ike, proposal_create_default_aead(PROTO_IKE));
index bb1ba71..fb8ea8c 100644 (file)
@@ -272,10 +272,18 @@ static ike_cfg_t *build_ike_cfg(private_sql_config_t *this, enumerator_t *e,
        while (e->enumerate(e, &id, &certreq, &force_encap, &local, &remote))
        {
                ike_cfg_t *ike_cfg;
+               ike_cfg_create_t ike = {
+                       .version = IKEV2,
+                       .local = local,
+                       .local_port = charon->socket->get_port(charon->socket, FALSE),
+                       .remote = remote,
+                       .remote_port = IKEV2_UDP_PORT,
+                       .no_certreq = !certreq,
+                       .force_encap = force_encap,
+                       .fragmentation = FRAGMENTATION_YES,
+               };
 
-               ike_cfg = ike_cfg_create(IKEV2, certreq, force_encap, local,
-                                                                charon->socket->get_port(charon->socket, FALSE),
-                                                                remote, IKEV2_UDP_PORT, FRAGMENTATION_NO, 0);
+               ike_cfg = ike_cfg_create(&ike);
                add_ike_proposals(this, ike_cfg, id);
                return ike_cfg;
        }
index 8cdb5ef..1514b74 100644 (file)
@@ -260,36 +260,40 @@ static void swap_ends(stroke_msg_t *msg)
  */
 static ike_cfg_t *build_ike_cfg(private_stroke_config_t *this, stroke_msg_t *msg)
 {
+       ike_cfg_create_t ike;
        ike_cfg_t *ike_cfg;
-       uint16_t ikeport;
        char me[256], other[256];
 
        swap_ends(msg);
 
+       ike = (ike_cfg_create_t){
+               .version = msg->add_conn.version,
+               .local = msg->add_conn.me.address,
+               .local_port = msg->add_conn.me.ikeport,
+               .remote = msg->add_conn.other.address,
+               .remote_port = msg->add_conn.other.ikeport,
+               .no_certreq = msg->add_conn.other.sendcert == CERT_NEVER_SEND,
+               .force_encap = msg->add_conn.force_encap,
+               .fragmentation = msg->add_conn.fragmentation,
+               .dscp = msg->add_conn.ikedscp,
+       };
        if (msg->add_conn.me.allow_any)
        {
                snprintf(me, sizeof(me), "%s,0.0.0.0/0,::/0",
                                 msg->add_conn.me.address);
+               ike.local = me;
        }
        if (msg->add_conn.other.allow_any)
        {
                snprintf(other, sizeof(other), "%s,0.0.0.0/0,::/0",
                                 msg->add_conn.other.address);
+               ike.remote = other;
+       }
+       if (ike.local_port == IKEV2_UDP_PORT)
+       {
+               ike.local_port = charon->socket->get_port(charon->socket, FALSE);
        }
-       ikeport = msg->add_conn.me.ikeport;
-       ikeport = (ikeport == IKEV2_UDP_PORT) ?
-                          charon->socket->get_port(charon->socket, FALSE) : ikeport;
-       ike_cfg = ike_cfg_create(msg->add_conn.version,
-                                                        msg->add_conn.other.sendcert != CERT_NEVER_SEND,
-                                                        msg->add_conn.force_encap,
-                                                        msg->add_conn.me.allow_any ?
-                                                               me : msg->add_conn.me.address,
-                                                        ikeport,
-                                                        msg->add_conn.other.allow_any ?
-                                                               other : msg->add_conn.other.address,
-                                                        msg->add_conn.other.ikeport,
-                                                        msg->add_conn.fragmentation,
-                                                        msg->add_conn.ikedscp);
+       ike_cfg = ike_cfg_create(&ike);
 
        if (!add_proposals(this, msg->add_conn.algorithms.ike, ike_cfg,
                                           NULL, PROTO_IKE))
index 5654fc5..a66bb58 100644 (file)
@@ -121,12 +121,19 @@ METHOD(enumerator_t, peer_enumerator_enumerate, bool,
        peer_enumerator_t *this, va_list args)
 {
        char *name, *ike_proposal, *esp_proposal, *ike_rekey, *esp_rekey;
-       char *local_id, *local_addr, *local_net;
-       char *remote_id, *remote_addr, *remote_net;
+       char *local_id, *local_net, *remote_id, *remote_net;
        peer_cfg_t **cfg;
        child_cfg_t *child_cfg;
        ike_cfg_t *ike_cfg;
        auth_cfg_t *auth;
+       ike_cfg_create_t ike = {
+               .version = IKEV2,
+               .local = "0.0.0.0",
+               .local_port = charon->socket->get_port(charon->socket, FALSE),
+               .remote = "0.0.0.0",
+               .remote_port = IKEV2_UDP_PORT,
+               .no_certreq = TRUE,
+       };
        peer_cfg_create_t peer = {
                .cert_policy = CERT_SEND_IF_ASKED,
                .unique = UNIQUE_NO,
@@ -152,8 +159,6 @@ METHOD(enumerator_t, peer_enumerator_enumerate, bool,
        name = "unnamed";
        local_id = NULL;
        remote_id = NULL;
-       local_addr = "0.0.0.0";
-       remote_addr = "0.0.0.0";
        local_net = NULL;
        remote_net = NULL;
        ike_proposal = NULL;
@@ -162,14 +167,12 @@ METHOD(enumerator_t, peer_enumerator_enumerate, bool,
        esp_rekey = NULL;
 
        if (this->inner->enumerate(this->inner, &name, &local_id, &remote_id,
-                       &local_addr, &remote_addr, &local_net, &remote_net,
+                       &ike.local, &ike.remote, &local_net, &remote_net,
                        &ike_proposal, &esp_proposal, &ike_rekey, &esp_rekey))
        {
+
                DESTROY_IF(this->peer_cfg);
-               ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, local_addr,
-                                                                charon->socket->get_port(charon->socket, FALSE),
-                                                                remote_addr, IKEV2_UDP_PORT,
-                                                                FRAGMENTATION_NO, 0);
+               ike_cfg = ike_cfg_create(&ike);
                ike_cfg->add_proposal(ike_cfg, create_proposal(ike_proposal, PROTO_IKE));
                peer.rekey_time = create_rekey(ike_rekey);
                this->peer_cfg = peer_cfg_create(name, ike_cfg, &peer);
@@ -248,23 +251,26 @@ METHOD(enumerator_t, ike_enumerator_enumerate, bool,
        ike_enumerator_t *this, va_list args)
 {
        ike_cfg_t **cfg;
-       char *local_addr, *remote_addr, *ike_proposal;
+       ike_cfg_create_t ike = {
+               .version = IKEV2,
+               .local = "0.0.0.0",
+               .local_port = charon->socket->get_port(charon->socket, FALSE),
+               .remote = "0.0.0.0",
+               .remote_port = IKEV2_UDP_PORT,
+               .no_certreq = TRUE,
+       };
+       char *ike_proposal;
 
        VA_ARGS_VGET(args, cfg);
 
        /* defaults */
-       local_addr = "0.0.0.0";
-       remote_addr = "0.0.0.0";
        ike_proposal = NULL;
 
        if (this->inner->enumerate(this->inner, NULL,
-                                                          &local_addr, &remote_addr, &ike_proposal))
+                                                          &ike.local, &ike.remote, &ike_proposal))
        {
                DESTROY_IF(this->ike_cfg);
-               this->ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, local_addr,
-                                                               charon->socket->get_port(charon->socket, FALSE),
-                                                               remote_addr, IKEV2_UDP_PORT,
-                                                               FRAGMENTATION_NO, 0);
+               this->ike_cfg = ike_cfg_create(&ike);
                this->ike_cfg->add_proposal(this->ike_cfg,
                                                                        create_proposal(ike_proposal, PROTO_IKE));
 
index 61427d2..f029d06 100644 (file)
@@ -258,7 +258,7 @@ Initiates an SA while streaming _control-log_ events.
 
        {
                child = <CHILD_SA configuration name to initiate>
-               ike = <optional IKE_SA configuration name to find child under>
+               ike = <IKE_SA configuration name to initiate or to find child under>
                timeout = <timeout in ms before returning>
                init-limits = <whether limits may prevent initiating the CHILD_SA>
                loglevel = <loglevel to issue "control-log" events for>
index f86d5c9..1ff0754 100644 (file)
@@ -310,6 +310,7 @@ typedef struct {
        uint64_t dpd_delay;
        uint64_t dpd_timeout;
        fragmentation_t fragmentation;
+       childless_t childless;
        unique_policy_t unique;
        uint32_t keyingtries;
        uint32_t local_port;
@@ -416,6 +417,7 @@ static void log_peer_data(peer_data_t *data)
        DBG2(DBG_CFG, "  dpd_delay = %llu", data->dpd_delay);
        DBG2(DBG_CFG, "  dpd_timeout = %llu", data->dpd_timeout);
        DBG2(DBG_CFG, "  fragmentation = %u",  data->fragmentation);
+       DBG2(DBG_CFG, "  childless = %u",  data->childless);
        DBG2(DBG_CFG, "  unique = %N", unique_policy_names, data->unique);
        DBG2(DBG_CFG, "  keyingtries = %u", data->keyingtries);
        DBG2(DBG_CFG, "  reauth_time = %llu", data->reauth_time);
@@ -1562,6 +1564,27 @@ CALLBACK(parse_frag, bool,
 }
 
 /**
+ * Parse a childless_t
+ */
+CALLBACK(parse_childless, bool,
+       childless_t *out, chunk_t v)
+{
+       enum_map_t map[] = {
+               { "allow",              CHILDLESS_ALLOW         },
+               { "never",              CHILDLESS_NEVER         },
+               { "force",              CHILDLESS_FORCE         },
+       };
+       int d;
+
+       if (parse_map(map, countof(map), &d, v))
+       {
+               *out = d;
+               return TRUE;
+       }
+       return FALSE;
+}
+
+/**
  * Parse a cert_policy_t
  */
 CALLBACK(parse_send_cert, bool,
@@ -1777,6 +1800,7 @@ CALLBACK(peer_kv, bool,
                { "dpd_delay",          parse_time,                     &peer->dpd_delay                        },
                { "dpd_timeout",        parse_time,                     &peer->dpd_timeout                      },
                { "fragmentation",      parse_frag,                     &peer->fragmentation            },
+               { "childless",          parse_childless,        &peer->childless                        },
                { "send_certreq",       parse_bool,                     &peer->send_certreq                     },
                { "send_cert",          parse_send_cert,        &peer->send_cert                        },
                { "keyingtries",        parse_uint32,           &peer->keyingtries                      },
@@ -2382,6 +2406,7 @@ CALLBACK(config_sn, bool,
        enumerator_t *enumerator;
        peer_cfg_create_t cfg;
        peer_cfg_t *peer_cfg;
+       ike_cfg_create_t ike;
        ike_cfg_t *ike_cfg;
        child_cfg_t *child_cfg;
        auth_data_t *auth;
@@ -2509,10 +2534,19 @@ CALLBACK(config_sn, bool,
 
        log_peer_data(&peer);
 
-       ike_cfg = ike_cfg_create(peer.version, peer.send_certreq, peer.encap,
-                                               peer.local_addrs, peer.local_port,
-                                               peer.remote_addrs, peer.remote_port,
-                                               peer.fragmentation, peer.dscp);
+       ike = (ike_cfg_create_t){
+               .version = peer.version,
+               .local = peer.local_addrs,
+               .local_port = peer.local_port,
+               .remote = peer.remote_addrs,
+               .remote_port = peer.remote_port,
+               .no_certreq = !peer.send_certreq,
+               .force_encap = peer.encap,
+               .fragmentation = peer.fragmentation,
+               .childless = peer.childless,
+               .dscp = peer.dscp,
+       };
+       ike_cfg = ike_cfg_create(&ike);
 
        cfg = (peer_cfg_create_t){
                .cert_policy = peer.send_cert,
index 16e49fd..4c09b57 100644 (file)
@@ -138,7 +138,7 @@ static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name)
 }
 
 /**
- * Find a peer/child config from a child config name
+ * Find a peer/child config from a config name
  */
 static child_cfg_t* find_child_cfg(char *name, char *pname, peer_cfg_t **out)
 {
@@ -154,6 +154,11 @@ static child_cfg_t* find_child_cfg(char *name, char *pname, peer_cfg_t **out)
                {
                        continue;
                }
+               if (!name)
+               {
+                       *out = peer_cfg->get_ref(peer_cfg);
+                       break;
+               }
                child_cfg = get_child_from_peer(peer_cfg, name);
                if (child_cfg)
                {
@@ -169,9 +174,9 @@ static child_cfg_t* find_child_cfg(char *name, char *pname, peer_cfg_t **out)
 CALLBACK(initiate, vici_message_t*,
        private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
 {
-       child_cfg_t *child_cfg = NULL;
-       peer_cfg_t *peer_cfg;
-       char *child, *ike;
+       peer_cfg_t *peer_cfg = NULL;
+       child_cfg_t *child_cfg;
+       char *child, *ike, *type, *sa;
        int timeout;
        bool limits;
        controller_cb_t log_cb = NULL;
@@ -186,7 +191,7 @@ CALLBACK(initiate, vici_message_t*,
        limits = request->get_bool(request, FALSE, "init-limits");
        log.level = request->get_int(request, 1, "loglevel");
 
-       if (!child)
+       if (!child && !ike)
        {
                return send_reply(this, "missing configuration name");
        }
@@ -195,12 +200,15 @@ CALLBACK(initiate, vici_message_t*,
                log_cb = (controller_cb_t)log_vici;
        }
 
-       DBG1(DBG_CFG, "vici initiate '%s'", child);
+       type = child ? "CHILD_SA" : "IKE_SA";
+       sa = child ?: ike;
 
        child_cfg = find_child_cfg(child, ike, &peer_cfg);
-       if (!child_cfg)
+
+       DBG1(DBG_CFG, "vici initiate %s '%s'", type, sa);
+       if (!peer_cfg)
        {
-               return send_reply(this, "CHILD_SA config '%s' not found", child);
+               return send_reply(this, "%s config '%s' not found", type, sa);
        }
        switch (charon->controller->initiate(charon->controller, peer_cfg,
                                                                        child_cfg, log_cb, &log, timeout, limits))
@@ -208,14 +216,14 @@ CALLBACK(initiate, vici_message_t*,
                case SUCCESS:
                        return send_reply(this, NULL);
                case OUT_OF_RES:
-                       return send_reply(this, "CHILD_SA '%s' not established after %dms",
-                                                         child, timeout);
+                       return send_reply(this, "%s '%s' not established after %dms", type,
+                                                         sa, timeout);
                case INVALID_STATE:
-                       return send_reply(this, "establishing CHILD_SA '%s' not possible "
-                                                         "at the moment due to limits", child);
+                       return send_reply(this, "establishing %s '%s' not possible at the "
+                                                         "moment due to limits", type, sa);
                case FAILED:
                default:
-                       return send_reply(this, "establishing CHILD_SA '%s' failed", child);
+                       return send_reply(this, "establishing %s '%s' failed", type, sa);
        }
 }
 
index c7ef1fe..d511081 100644 (file)
@@ -161,6 +161,11 @@ enum ike_extension_t {
         * Postquantum Preshared Keys, draft-ietf-ipsecme-qr-ikev2
         */
        EXT_PPK = (1<<15),
+
+       /**
+        * Responder accepts childless IKE_SAs, RFC 6023
+        */
+       EXT_IKE_CHILDLESS = (1<<16),
 };
 
 /**
index b80e71d..ac1f999 100644 (file)
@@ -1037,6 +1037,31 @@ static void process_payloads(private_child_create_t *this, message_t *message)
        enumerator->destroy(enumerator);
 }
 
+/**
+ * Check if we should defer the creation of this CHILD_SA until after the
+ * IKE_SA has been established childless.
+ */
+static status_t defer_child_sa(private_child_create_t *this)
+{
+       ike_cfg_t *ike_cfg;
+
+       ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
+
+       if (this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_CHILDLESS))
+       {
+               if (ike_cfg->childless(ike_cfg) == CHILDLESS_FORCE)
+               {
+                       return NEED_MORE;
+               }
+       }
+       else if (ike_cfg->childless(ike_cfg) == CHILDLESS_FORCE)
+       {
+               DBG1(DBG_IKE, "peer does not support childless IKE_SA initiation");
+               return DESTROY_ME;
+       }
+       return NOT_SUPPORTED;
+}
+
 METHOD(task_t, build_i, status_t,
        private_child_create_t *this, message_t *message)
 {
@@ -1067,6 +1092,19 @@ METHOD(task_t, build_i, status_t,
                                /* send only in the first request, not in subsequent rounds */
                                return NEED_MORE;
                        }
+                       switch (defer_child_sa(this))
+                       {
+                               case DESTROY_ME:
+                                       /* config mismatch */
+                                       return DESTROY_ME;
+                               case NEED_MORE:
+                                       /* defer until after IKE_SA has been established */
+                                       chunk_free(&this->my_nonce);
+                                       return NEED_MORE;
+                               default:
+                                       /* just continue to establish the CHILD_SA */
+                                       break;
+                       }
                        break;
                default:
                        break;
@@ -1312,6 +1350,37 @@ static child_cfg_t* select_child_cfg(private_child_create_t *this)
        return child_cfg;
 }
 
+/**
+ * Check how to handle a possibly childless IKE_SA
+ */
+static status_t handle_childless(private_child_create_t *this)
+{
+       ike_cfg_t *ike_cfg;
+
+       ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
+
+       if (!this->proposals && !this->tsi && !this->tsr)
+       {
+               /* looks like a childless IKE_SA, check if we allow it */
+               if (ike_cfg->childless(ike_cfg) == CHILDLESS_NEVER)
+               {
+                       /* we don't allow childless initiation */
+                       DBG1(DBG_IKE, "peer tried to initiate a childless IKE_SA");
+                       return INVALID_STATE;
+               }
+               return SUCCESS;
+       }
+
+       /* the peer apparently wants to create a regular IKE_SA */
+       if (ike_cfg->childless(ike_cfg) == CHILDLESS_FORCE)
+       {
+               /* reject it if we only allow childless initiation */
+               DBG1(DBG_IKE, "peer did not initiate a childless IKE_SA");
+               return INVALID_STATE;
+       }
+       return NOT_SUPPORTED;
+}
+
 METHOD(task_t, build_r, status_t,
        private_child_create_t *this, message_t *message)
 {
@@ -1348,6 +1417,19 @@ METHOD(task_t, build_r, status_t,
                        {       /* no CHILD_SA is created for redirected SAs */
                                return SUCCESS;
                        }
+                       switch (handle_childless(this))
+                       {
+                               case SUCCESS:
+                                       /* no CHILD_SA built */
+                                       return SUCCESS;
+                               case INVALID_STATE:
+                                       message->add_notify(message, FALSE, INVALID_SYNTAX,
+                                                                               chunk_empty);
+                                       return FAILED;
+                               default:
+                                       /* continue with regular initiation */
+                                       break;
+                       }
                        ike_auth = TRUE;
                default:
                        break;
@@ -1533,6 +1615,11 @@ METHOD(task_t, process_i, status_t,
                        {       /* wait until all authentication round completed */
                                return NEED_MORE;
                        }
+                       if (defer_child_sa(this) == NEED_MORE)
+                       {       /* defer until after IKE_SA has been established */
+                               chunk_free(&this->other_nonce);
+                               return NEED_MORE;
+                       }
                        ike_auth = TRUE;
                default:
                        break;
index b570904..04ce504 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2018 Tobias Brunner
+ * Copyright (C) 2008-2019 Tobias Brunner
  * Copyright (C) 2005-2008 Martin Willi
  * Copyright (C) 2005 Jan Hutter
  * HSR Hochschule fuer Technik Rapperswil
@@ -433,6 +433,13 @@ static bool build_payloads(private_ike_init_t *this, message_t *message)
        {
                message->add_notify(message, FALSE, USE_PPK, chunk_empty);
        }
+       /* notify the peer if we accept childless IKE_SAs */
+       if (!this->old_sa && !this->initiator &&
+                ike_cfg->childless(ike_cfg) != CHILDLESS_NEVER)
+       {
+               message->add_notify(message, FALSE, CHILDLESS_IKEV2_SUPPORTED,
+                                                       chunk_empty);
+       }
        return TRUE;
 }
 
@@ -578,6 +585,13 @@ static void process_payloads(private_ike_init_t *this, message_t *message)
                                                                                                                   EXT_IKE_REDIRECTION);
                                                }
                                                break;
+                                       case CHILDLESS_IKEV2_SUPPORTED:
+                                               if (this->initiator && !this->old_sa)
+                                               {
+                                                       this->ike_sa->enable_extension(this->ike_sa,
+                                                                                                                  EXT_IKE_CHILDLESS);
+                                               }
+                                               break;
                                        default:
                                                /* other notifies are handled elsewhere */
                                                break;
index 101b534..1d92501 100644 (file)
@@ -31,6 +31,7 @@ exchange_tests_SOURCES = \
   suites/test_ike_delete.c \
   suites/test_ike_mid_sync.c \
   suites/test_ike_rekey.c \
+  suites/test_childless.c \
   utils/exchange_test_asserts.h utils/exchange_test_asserts.c \
   utils/exchange_test_helper.h utils/exchange_test_helper.c \
   utils/job_asserts.h \
index 6b35ea5..491c25c 100644 (file)
@@ -19,3 +19,4 @@ TEST_SUITE(ike_rekey_suite_create)
 TEST_SUITE(child_create_suite_create)
 TEST_SUITE(child_delete_suite_create)
 TEST_SUITE(child_rekey_suite_create)
+TEST_SUITE(childless_suite_create)
diff --git a/src/libcharon/tests/suites/test_childless.c b/src/libcharon/tests/suites/test_childless.c
new file mode 100644 (file)
index 0000000..6ac02aa
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2019 Tobias Brunner
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "test_suite.h"
+
+#include <daemon.h>
+#include <tests/utils/exchange_test_helper.h>
+#include <tests/utils/exchange_test_asserts.h>
+#include <tests/utils/sa_asserts.h>
+
+/**
+ * Childless initiation of the IKE_SA. The first CHILD_SA is automatically
+ * initiated in a separate CREATE_CHILD_SA exchange including DH.
+ */
+START_TEST(test_regular)
+{
+       exchange_test_sa_conf_t conf = {
+               .initiator = {
+                       .childless = CHILDLESS_FORCE,
+                       .esp = "aes128-sha256-modp3072",
+               },
+               .responder = {
+                       .esp = "aes128-sha256-modp3072",
+               },
+       };
+       ike_sa_t *a, *b;
+       ike_sa_id_t *id_a, *id_b;
+       child_cfg_t *child_cfg;
+
+       child_cfg = exchange_test_helper->create_sa(exchange_test_helper, &a, &b,
+                                                                                               &conf);
+       id_a = a->get_id(a);
+       id_b = b->get_id(b);
+
+       call_ikesa(a, initiate, child_cfg, 0, NULL, NULL);
+
+       /* IKE_SA_INIT --> */
+       id_b->set_initiator_spi(id_b, id_a->get_initiator_spi(id_a));
+       exchange_test_helper->process_message(exchange_test_helper, b, NULL);
+       /* <-- IKE_SA_INIT */
+       assert_notify(IN, CHILDLESS_IKEV2_SUPPORTED);
+       id_a->set_responder_spi(id_a, id_b->get_responder_spi(id_b));
+       exchange_test_helper->process_message(exchange_test_helper, a, NULL);
+
+       /* IKE_AUTH --> */
+       assert_hook_not_called(child_updown);
+       assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+       assert_no_payload(IN, PLV2_TS_INITIATOR);
+       assert_no_payload(IN, PLV2_TS_RESPONDER);
+       exchange_test_helper->process_message(exchange_test_helper, b, NULL);
+
+       /* <-- IKE_AUTH */
+       assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+       assert_no_payload(IN, PLV2_TS_INITIATOR);
+       assert_no_payload(IN, PLV2_TS_RESPONDER);
+       exchange_test_helper->process_message(exchange_test_helper, a, NULL);
+       assert_child_sa_count(a, 0);
+       assert_child_sa_count(b, 0);
+       assert_hook();
+
+       /* CREATE_CHILD_SA { SA, Ni, KEi, TSi, TSr } --> */
+       assert_hook_called(child_updown);
+       assert_payload(IN, PLV2_KEY_EXCHANGE);
+       exchange_test_helper->process_message(exchange_test_helper, b, NULL);
+       assert_child_sa_count(b, 1);
+       assert_hook();
+
+       /* <-- CREATE_CHILD_SA { SA, Ni, KEi } */
+       assert_hook_called(child_updown);
+       assert_payload(IN, PLV2_KEY_EXCHANGE);
+       exchange_test_helper->process_message(exchange_test_helper, a, NULL);
+       assert_child_sa_count(a, 1);
+       assert_hook();
+
+       assert_sa_idle(a);
+       assert_sa_idle(b);
+
+       call_ikesa(a, destroy);
+       call_ikesa(b, destroy);
+}
+END_TEST
+
+/**
+ * Childless initiation of the IKE_SA, no CHILD_SA created automatically.
+ * It's created with a separate initiation and exchange afterwards.
+ */
+START_TEST(test_regular_manual)
+{
+       exchange_test_sa_conf_t conf = {
+               .initiator = {
+                       .esp = "aes128-sha256-modp3072",
+               },
+               .responder = {
+                       .esp = "aes128-sha256-modp3072",
+               },
+       };
+       ike_sa_t *a, *b;
+       ike_sa_id_t *id_a, *id_b;
+       child_cfg_t *child_cfg;
+
+       child_cfg = exchange_test_helper->create_sa(exchange_test_helper, &a, &b,
+                                                                                               &conf);
+       id_a = a->get_id(a);
+       id_b = b->get_id(b);
+
+       call_ikesa(a, initiate, NULL, 0, NULL, NULL);
+
+       /* IKE_SA_INIT --> */
+       id_b->set_initiator_spi(id_b, id_a->get_initiator_spi(id_a));
+       exchange_test_helper->process_message(exchange_test_helper, b, NULL);
+       /* <-- IKE_SA_INIT */
+       assert_notify(IN, CHILDLESS_IKEV2_SUPPORTED);
+       id_a->set_responder_spi(id_a, id_b->get_responder_spi(id_b));
+       exchange_test_helper->process_message(exchange_test_helper, a, NULL);
+
+       /* IKE_AUTH --> */
+       assert_hook_not_called(child_updown);
+       assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+       assert_no_payload(IN, PLV2_TS_INITIATOR);
+       assert_no_payload(IN, PLV2_TS_RESPONDER);
+       exchange_test_helper->process_message(exchange_test_helper, b, NULL);
+
+       /* <-- IKE_AUTH */
+       assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+       assert_no_payload(IN, PLV2_TS_INITIATOR);
+       assert_no_payload(IN, PLV2_TS_RESPONDER);
+       exchange_test_helper->process_message(exchange_test_helper, a, NULL);
+       assert_child_sa_count(a, 0);
+       assert_child_sa_count(b, 0);
+       assert_hook();
+
+       assert_sa_idle(a);
+       assert_sa_idle(b);
+
+       call_ikesa(a, initiate, child_cfg, 0, NULL, NULL);
+
+       /* CREATE_CHILD_SA { SA, Ni, KEi, TSi, TSr } --> */
+       assert_hook_called(child_updown);
+       assert_payload(IN, PLV2_KEY_EXCHANGE);
+       exchange_test_helper->process_message(exchange_test_helper, b, NULL);
+       assert_child_sa_count(b, 1);
+       assert_hook();
+
+       /* <-- CREATE_CHILD_SA { SA, Ni, KEi } */
+       assert_hook_called(child_updown);
+       assert_payload(IN, PLV2_KEY_EXCHANGE);
+       exchange_test_helper->process_message(exchange_test_helper, a, NULL);
+       assert_child_sa_count(a, 1);
+       assert_hook();
+
+       assert_sa_idle(a);
+       assert_sa_idle(b);
+
+       call_ikesa(a, destroy);
+       call_ikesa(b, destroy);
+}
+END_TEST
+
+/**
+ * The initiator aborts the initiation once it notices the responder does not
+ * support childless IKE_SAs.
+ */
+START_TEST(test_failure_init)
+{
+       exchange_test_sa_conf_t conf = {
+               .initiator = {
+                       .childless = CHILDLESS_FORCE,
+               },
+               .responder = {
+                       .childless = CHILDLESS_NEVER,
+               },
+       };
+       ike_sa_t *a, *b;
+       ike_sa_id_t *id_a, *id_b;
+       child_cfg_t *child_cfg;
+       status_t status;
+
+       child_cfg = exchange_test_helper->create_sa(exchange_test_helper, &a, &b,
+                                                                                               &conf);
+       id_a = a->get_id(a);
+       id_b = b->get_id(b);
+
+       call_ikesa(a, initiate, child_cfg, 0, NULL, NULL);
+
+       /* IKE_SA_INIT --> */
+       id_b->set_initiator_spi(id_b, id_a->get_initiator_spi(id_a));
+       exchange_test_helper->process_message(exchange_test_helper, b, NULL);
+       /* <-- IKE_SA_INIT */
+       assert_no_notify(IN, CHILDLESS_IKEV2_SUPPORTED);
+       id_a->set_responder_spi(id_a, id_b->get_responder_spi(id_b));
+       status = exchange_test_helper->process_message(exchange_test_helper, a,
+                                                                                                  NULL);
+       ck_assert_int_eq(DESTROY_ME, status);
+
+       call_ikesa(a, destroy);
+       call_ikesa(b, destroy);
+}
+END_TEST
+
+/**
+ * The responder aborts the initiation once it notices the initiator does not
+ * create a childless IKE_SA.
+ */
+START_TEST(test_failure_resp)
+{
+       exchange_test_sa_conf_t conf = {
+               .initiator = {
+                       .childless = CHILDLESS_NEVER,
+               },
+               .responder = {
+                       .childless = CHILDLESS_FORCE,
+               },
+       };
+       ike_sa_t *a, *b;
+       ike_sa_id_t *id_a, *id_b;
+       child_cfg_t *child_cfg;
+       status_t status;
+
+       child_cfg = exchange_test_helper->create_sa(exchange_test_helper, &a, &b,
+                                                                                               &conf);
+       id_a = a->get_id(a);
+       id_b = b->get_id(b);
+
+       call_ikesa(a, initiate, child_cfg, 0, NULL, NULL);
+
+       /* IKE_SA_INIT --> */
+       id_b->set_initiator_spi(id_b, id_a->get_initiator_spi(id_a));
+       exchange_test_helper->process_message(exchange_test_helper, b, NULL);
+       /* <-- IKE_SA_INIT */
+       assert_notify(IN, CHILDLESS_IKEV2_SUPPORTED);
+       id_a->set_responder_spi(id_a, id_b->get_responder_spi(id_b));
+       exchange_test_helper->process_message(exchange_test_helper, a, NULL);
+
+       /* IKE_AUTH --> */
+       assert_hook_not_called(child_updown);
+       assert_payload(IN, PLV2_SECURITY_ASSOCIATION);
+       assert_payload(IN, PLV2_TS_INITIATOR);
+       assert_payload(IN, PLV2_TS_RESPONDER);
+       status = exchange_test_helper->process_message(exchange_test_helper, b,
+                                                                                                  NULL);
+       ck_assert_int_eq(DESTROY_ME, status);
+       assert_hook();
+
+       /* <-- IKE_AUTH */
+       assert_hook_not_called(child_updown);
+       assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+       assert_no_payload(IN, PLV2_TS_INITIATOR);
+       assert_no_payload(IN, PLV2_TS_RESPONDER);
+       assert_notify(IN, INVALID_SYNTAX);
+       status = exchange_test_helper->process_message(exchange_test_helper, a,
+                                                                                                  NULL);
+       ck_assert_int_eq(DESTROY_ME, status);
+       assert_hook();
+
+       assert_sa_idle(a);
+       assert_sa_idle(b);
+
+       call_ikesa(a, destroy);
+       call_ikesa(b, destroy);
+}
+END_TEST
+
+Suite *childless_suite_create()
+{
+       Suite *s;
+       TCase *tc;
+
+       s = suite_create("childless");
+
+       tc = tcase_create("initiation");
+       tcase_add_test(tc, test_regular);
+       tcase_add_test(tc, test_regular_manual);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("failure");
+       tcase_add_test(tc, test_failure_init);
+       tcase_add_test(tc, test_failure_resp);
+       suite_add_tcase(s, tc);
+
+       return s;
+}
index 9bbc064..ded0462 100644 (file)
 
 static void assert_family(int expected, char *addr, bool local)
 {
+       ike_cfg_create_t ike = {
+               .version = IKEV2,
+               .local = local ? addr : "%any",
+               .local_port = 500,
+               .remote = local ? "%any" : addr,
+               .remote_port = 500,
+       };
        ike_cfg_t *cfg;
        int family;
 
-       cfg = ike_cfg_create(IKEV2, FALSE, FALSE, local ? addr : "%any", 500,
-                                                local ? "%any" : addr, 500, FRAGMENTATION_NO, 0);
+       cfg = ike_cfg_create(&ike);
        family = ike_cfg_get_family(cfg, local);
        ck_assert_msg(expected == family, "expected family %d != %d (addr: '%s')",
                                  expected, family, addr);
index 02e38a3..94337e2 100644 (file)
  */
 static ike_cfg_t *create_ike_cfg()
 {
-       return ike_cfg_create(IKEV2, TRUE, FALSE, "127.0.0.1", 500,
-                                                 "127.0.0.1", 500, FRAGMENTATION_NO, 0);
+       ike_cfg_create_t ike = {
+               .version = IKEV2,
+               .local = "127.0.0.1",
+               .local_port = 500,
+               .remote = "127.0.0.1",
+               .remote_port = 500,
+       };
+       return ike_cfg_create(&ike);
 }
 
 /**
index ae9ac5c..beb8cfd 100644 (file)
@@ -298,6 +298,26 @@ bool exchange_test_asserts_message(listener_t *this, ike_sa_t *ike_sa,
                                _assert_payload(#dir, 1, { TRUE, expected, 0 })
 
 /**
+ * Assert that the next in- or outbound plaintext message contains a payload
+ * of the given type.
+ *
+ * @param dir                  IN or OUT to check the next in- or outbound message
+ * @param expected             expected payload type
+ */
+#define assert_payload(dir, expected) \
+                               _assert_payload(#dir, -1, { TRUE, expected, 0 })
+
+/**
+ * Assert that the next in- or outbound plaintext message contains no payload
+ * of the given type.
+ *
+ * @param dir                  IN or OUT to check the next in- or outbound message
+ * @param unexpected   not expected payload type
+ */
+#define assert_no_payload(dir, unexpected) \
+                               _assert_payload(#dir, -1, { FALSE, unexpected, 0 })
+
+/**
  * Assert that the next in- or outbound plaintext message contains exactly
  * one notify of the given type.
  *
index bebf334..97fa6fe 100644 (file)
@@ -49,6 +49,11 @@ struct private_exchange_test_helper_t {
         * List of registered listeners
         */
        array_t *listeners;
+
+       /**
+        * Config backend
+        */
+       private_backend_t *backend;
 };
 
 /**
@@ -85,15 +90,24 @@ exchange_test_helper_t *exchange_test_helper;
 
 static ike_cfg_t *create_ike_cfg(bool initiator, exchange_test_sa_conf_t *conf)
 {
+       ike_cfg_create_t ike = {
+               .version = IKEV2,
+               .local = "127.0.0.1",
+               .local_port = IKEV2_UDP_PORT,
+               .remote = "127.0.0.1",
+               .remote_port = IKEV2_UDP_PORT,
+       };
        ike_cfg_t *ike_cfg;
        char *proposal = NULL;
 
-       ike_cfg = ike_cfg_create(IKEV2, TRUE, FALSE, "127.0.0.1", IKEV2_UDP_PORT,
-                                                        "127.0.0.1", IKEV2_UDP_PORT, FRAGMENTATION_NO, 0);
        if (conf)
        {
+               ike.childless = initiator ? conf->initiator.childless
+                                                                 : conf->responder.childless;
                proposal = initiator ? conf->initiator.ike : conf->responder.ike;
        }
+
+       ike_cfg = ike_cfg_create(&ike);
        if (proposal)
        {
                ike_cfg->add_proposal(ike_cfg,
@@ -180,6 +194,18 @@ METHOD(backend_t, create_peer_cfg_enumerator, enumerator_t*,
        return enumerator_create_single(this->peer_cfg, NULL);
 }
 
+/**
+ * Sets the config objects provided by the backend
+ */
+static void set_config(private_backend_t *this, ike_cfg_t *ike,
+                                          peer_cfg_t *peer)
+{
+       DESTROY_IF(this->ike_cfg);
+       this->ike_cfg = ike;
+       DESTROY_IF(this->peer_cfg);
+       this->peer_cfg = peer;
+}
+
 METHOD(exchange_test_helper_t, process_message, status_t,
        private_exchange_test_helper_t *this, ike_sa_t *ike_sa, message_t *message)
 {
@@ -204,43 +230,50 @@ METHOD(exchange_test_helper_t, process_message, status_t,
        return status;
 }
 
-METHOD(exchange_test_helper_t, establish_sa, void,
+METHOD(exchange_test_helper_t, create_sa, child_cfg_t*,
        private_exchange_test_helper_t *this, ike_sa_t **init, ike_sa_t **resp,
        exchange_test_sa_conf_t *conf)
 {
-       private_backend_t backend = {
-               .public = {
-                       .create_ike_cfg_enumerator = _create_ike_cfg_enumerator,
-                       .create_peer_cfg_enumerator = _create_peer_cfg_enumerator,
-                       .get_peer_cfg_by_name = (void*)return_null,
-               },
-       };
-       ike_sa_id_t *id_i, *id_r;
-       ike_sa_t *sa_i, *sa_r;
        peer_cfg_t *peer_cfg;
        child_cfg_t *child_cfg;
 
-       sa_i = *init = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
-                                                                                                               IKEV2, TRUE);
-       id_i = sa_i->get_id(sa_i);
+       *init = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
+                                                                                                IKEV2, TRUE);
 
-       sa_r = *resp = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
-                                                                                                               IKEV2, FALSE);
-       id_r = sa_r->get_id(sa_r);
+       *resp = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
+                                                                                                IKEV2, FALSE);
+
+       peer_cfg = create_peer_cfg(FALSE, conf);
+       child_cfg = create_child_cfg(FALSE, conf);
+       peer_cfg->add_child_cfg(peer_cfg, child_cfg->get_ref(child_cfg));
+       child_cfg->destroy(child_cfg);
+       set_config(this->backend,  create_ike_cfg(FALSE, conf), peer_cfg);
 
        peer_cfg = create_peer_cfg(TRUE, conf);
        child_cfg = create_child_cfg(TRUE, conf);
        peer_cfg->add_child_cfg(peer_cfg, child_cfg->get_ref(child_cfg));
-       sa_i->set_peer_cfg(sa_i, peer_cfg);
+       (*init)->set_peer_cfg(*init, peer_cfg);
        peer_cfg->destroy(peer_cfg);
-       call_ikesa(sa_i, initiate, child_cfg, 0, NULL, NULL);
+       return child_cfg;
+}
 
-       backend.ike_cfg = create_ike_cfg(FALSE, conf);
-       peer_cfg = backend.peer_cfg = create_peer_cfg(FALSE, conf);
-       child_cfg = create_child_cfg(FALSE, conf);
-       peer_cfg->add_child_cfg(peer_cfg, child_cfg->get_ref(child_cfg));
-       child_cfg->destroy(child_cfg);
-       charon->backends->add_backend(charon->backends, &backend.public);
+METHOD(exchange_test_helper_t, establish_sa, void,
+       private_exchange_test_helper_t *this, ike_sa_t **init, ike_sa_t **resp,
+       exchange_test_sa_conf_t *conf)
+{
+       ike_sa_id_t *id_i, *id_r;
+       ike_sa_t *sa_i, *sa_r;
+       child_cfg_t *child_i;
+
+       child_i = create_sa(this, init, resp, conf);
+
+       sa_i = *init;
+       sa_r = *resp;
+
+       id_i = sa_i->get_id(sa_i);
+       id_r = sa_r->get_id(sa_r);
+
+       call_ikesa(sa_i, initiate, child_i, 0, NULL, NULL);
 
        /* IKE_SA_INIT --> */
        id_r->set_initiator_spi(id_r, id_i->get_initiator_spi(id_i));
@@ -252,10 +285,6 @@ METHOD(exchange_test_helper_t, establish_sa, void,
        process_message(this, sa_r, NULL);
        /* <-- IKE_AUTH */
        process_message(this, sa_i, NULL);
-
-       charon->backends->remove_backend(charon->backends, &backend.public);
-       DESTROY_IF(backend.peer_cfg);
-       DESTROY_IF(backend.ike_cfg);
 }
 
 METHOD(exchange_test_helper_t, add_listener, void,
@@ -300,6 +329,7 @@ static nonce_gen_t *create_nonce_gen()
 void exchange_test_helper_init(char *plugins)
 {
        private_exchange_test_helper_t *this;
+       private_backend_t *backend;
        plugin_feature_t features[] = {
                PLUGIN_REGISTER(DH, mock_dh_create),
                        /* we only need to support a limited number of DH groups */
@@ -311,14 +341,24 @@ void exchange_test_helper_init(char *plugins)
                                PLUGIN_DEPENDS(RNG, RNG_WEAK),
        };
 
+       INIT(backend,
+               .public = {
+                       .create_ike_cfg_enumerator = _create_ike_cfg_enumerator,
+                       .create_peer_cfg_enumerator = _create_peer_cfg_enumerator,
+                       .get_peer_cfg_by_name = (void*)return_null,
+               },
+       );
+
        INIT(this,
                .public = {
                        .sender = mock_sender_create(),
                        .establish_sa = _establish_sa,
+                       .create_sa = _create_sa,
                        .process_message = _process_message,
                        .add_listener = _add_listener,
                },
                .creds = mem_cred_create(),
+               .backend = backend,
        );
 
        initialize_logging();
@@ -339,6 +379,8 @@ void exchange_test_helper_init(char *plugins)
        charon->ike_sa_manager->set_spi_cb(charon->ike_sa_manager, get_ike_spi,
                                                                           this);
 
+       charon->backends->add_backend(charon->backends, &backend->public);
+
        lib->credmgr->add_set(lib->credmgr, &this->creds->set);
 
        this->creds->add_shared(this->creds,
@@ -362,6 +404,9 @@ void exchange_test_helper_deinit()
        {
                charon->bus->remove_listener(charon->bus, listener);
        }
+       charon->backends->remove_backend(charon->backends, &this->backend->public);
+       set_config(this->backend, NULL, NULL);
+       free(this->backend);
        lib->credmgr->remove_set(lib->credmgr, &this->creds->set);
        this->creds->destroy(this->creds);
        /* flush SAs before destroying the sender (in case of test failures) */
index e1fdb01..1541e88 100644 (file)
@@ -58,6 +58,23 @@ struct exchange_test_helper_t {
                                                 ike_sa_t **resp, exchange_test_sa_conf_t *conf);
 
        /**
+        * Similar to establish_sa() but does only create the SA and config
+        * objects, no exchanges are initiated/handled.  The returned child_cfg
+        * object is that created for the initiator to be used for a call to
+        * initiate(). The config objects for the responder are managed and
+        * provided by an internal config backend.
+        *
+        * Note that the responder SPIs are not yet set.
+        *
+        * @param[out] init             IKE_SA of the initiator
+        * @param[out] resp             IKE_SA of the responder
+        * @param conf                  configuration for SAs
+        * @return                              child_cfg for the initiator
+        */
+       child_cfg_t *(*create_sa)(exchange_test_helper_t *this, ike_sa_t **init,
+                                                         ike_sa_t **resp, exchange_test_sa_conf_t *conf);
+
+       /**
         * Pass a message to the given IKE_SA for processing, setting the IKE_SA on
         * the bus while processing the message.
         *
@@ -92,6 +109,8 @@ struct exchange_test_sa_conf_t {
                char *ike;
                /** ESP proposal */
                char *esp;
+               /** Support for childless IKE_SAs */
+               childless_t childless;
        } initiator, responder;
 };
 
index bf8d2cd..8ade8bf 100644 (file)
@@ -128,11 +128,11 @@ static void __attribute__ ((constructor))reg()
 {
        command_register((command_t) {
                initiate, 'i', "initiate", "initiate a connection",
-               {"--child <name> [--ike <name>] [--timeout <s>] [--raw|--pretty]"},
+               {"[--child <name>] [--ike <name>] [--timeout <s>] [--raw|--pretty]"},
                {
                        {"help",                'h', 0, "show usage information"},
                        {"child",               'c', 1, "initiate a CHILD_SA configuration"},
-                       {"ike",                 'i', 1, "name of the connection to which the child belongs"},
+                       {"ike",                 'i', 1, "initiate an IKE_SA, or name of child's parent"},
                        {"timeout",             't', 1, "timeout in seconds before detaching"},
                        {"raw",                 'r', 0, "dump raw response message"},
                        {"pretty",              'P', 0, "dump raw response message in pretty print"},
index 460e17b..6765e9d 100644 (file)
@@ -154,7 +154,7 @@ connections.<conn>.dpd_timeout = 0s
        specified; this option has no effect on connections using IKE2.
 
 connections.<conn>.fragmentation = yes
-       Use IKE UDP datagram fragmentation (_yes_, _accept_, _no_ or _force_).
+       Use IKE UDP datagram fragmentation (_yes_, _accept_, _no_ or _force_).
 
        Use IKE fragmentation (proprietary IKEv1 extension or RFC 7383 IKEv2
        fragmentation).  Acceptable  values  are _yes_ (the     default), _accept_,
@@ -168,6 +168,21 @@ connections.<conn>.fragmentation = yes
        Note that fragmented IKE messages sent by a peer are always accepted
        irrespective of the value of this option (even when set to _no_).
 
+connections.<conn>.childless = allow
+       Use childless IKE_SA initiation (_allow_, _force_ or _never_).
+
+       Use childless IKE_SA initiation (RFC 6023) for IKEv2.  Acceptable values
+       are _allow_ (the default), _force_ and _never_. If set to _allow_,
+       responders will accept childless IKE_SAs (as indicated via notify in the
+       IKE_SA_INIT response) while initiators continue to create regular IKE_SAs
+       with the first CHILD_SA created during IKE_AUTH, unless the IKE_SA is
+       initiated explicitly without any children (which will fail if the responder
+       does not support or has disabled this extension).  If set to _force_, only
+       childless initiation is accepted and the first CHILD_SA is created with a
+       separate CREATE_CHILD_SA exchange (e.g. to use an independent DH exchange
+       for all CHILD_SAs).  Finally, setting the option to _never_ disables support
+       for childless IKE_SAs as responder.
+
 connections.<conn>.send_certreq = yes
        Send certificate requests payloads (_yes_ or _no_).
 
diff --git a/testing/tests/swanctl/net2net-childless/description.txt b/testing/tests/swanctl/net2net-childless/description.txt
new file mode 100755 (executable)
index 0000000..6da9081
--- /dev/null
@@ -0,0 +1,13 @@
+A connection between the subnets behind the gateways <b>moon</b> and <b>sun</b>
+is set up using childless initiation of IKEv2 SAs (RFC 6023).
+<p/>
+The IKE_SA is established without CHILD_SA during IKE_AUTH. Instead, the
+CHILD_SA is created right afterwards with a CREATE_CHILD_SA exchange, allowing
+the use of a separate DH exchange for the first CHILD_SA, which is not possible
+if it is created during IKE_AUTH.
+<p/>
+The authentication is based on <b>X.509 certificates</b>. Upon the successful
+establishment of the IPsec tunnel, the updown script automatically
+inserts iptables-based firewall rules that let pass the tunneled traffic.
+In order to test both tunnel and firewall, client <b>alice</b> behind gateway
+<b>moon</b> pings client <b>bob</b> located behind gateway <b>sun</b>.
diff --git a/testing/tests/swanctl/net2net-childless/evaltest.dat b/testing/tests/swanctl/net2net-childless/evaltest.dat
new file mode 100755 (executable)
index 0000000..9826cdd
--- /dev/null
@@ -0,0 +1,7 @@
+moon::swanctl --list-sas --raw 2> /dev/null::gw-gw.*version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=500 local-id=moon.strongswan.org remote-host=192.168.0.2 remote-port=500 remote-id=sun.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net-net.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128 dh-group=CURVE_25519.*local-ts=\[10.1.0.0/16] remote-ts=\[10.2.0.0/16]::YES
+sun:: swanctl --list-sas --raw 2> /dev/null::gw-gw.*version=2 state=ESTABLISHED local-host=192.168.0.2 local-port=500 local-id=sun.strongswan.org remote-host=192.168.0.1 remote-port=500 remote-id=moon.strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net-net.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128 dh-group=CURVE_25519.*local-ts=\[10.2.0.0/16] remote-ts=\[10.1.0.0/16]::YES
+moon::cat /var/log/daemon.log::generating CREATE_CHILD_SA request 2.*KE::YES
+moon::cat /var/log/daemon.log::parsed CREATE_CHILD_SA response 2.*KE::YES
+alice::ping -c 1 PH_IP_BOB::64 bytes from PH_IP_BOB: icmp_.eq=1::YES
+sun::tcpdump::IP moon.strongswan.org > sun.strongswan.org: ESP::YES
+sun::tcpdump::IP sun.strongswan.org > moon.strongswan.org: ESP::YES
diff --git a/testing/tests/swanctl/net2net-childless/hosts/moon/etc/strongswan.conf b/testing/tests/swanctl/net2net-childless/hosts/moon/etc/strongswan.conf
new file mode 100755 (executable)
index 0000000..ad4c18e
--- /dev/null
@@ -0,0 +1,9 @@
+# /etc/strongswan.conf - strongSwan configuration file
+
+swanctl {
+  load = pem pkcs1 x509 revocation constraints pubkey openssl random
+}
+
+charon-systemd {
+  load = random nonce aes sha1 sha2 hmac pem pkcs1 x509 revocation curve25519 gmp curl kernel-netlink socket-default updown vici
+}
diff --git a/testing/tests/swanctl/net2net-childless/hosts/moon/etc/swanctl/swanctl.conf b/testing/tests/swanctl/net2net-childless/hosts/moon/etc/swanctl/swanctl.conf
new file mode 100755 (executable)
index 0000000..ead3ce4
--- /dev/null
@@ -0,0 +1,35 @@
+connections {
+
+   gw-gw {
+      local_addrs  = 192.168.0.1
+      remote_addrs = 192.168.0.2
+
+      childless = force
+
+      local {
+         auth = pubkey
+         certs = moonCert.pem
+         id = moon.strongswan.org
+      }
+      remote {
+         auth = pubkey
+         id = sun.strongswan.org
+      }
+      children {
+         net-net {
+            local_ts  = 10.1.0.0/16
+            remote_ts = 10.2.0.0/16
+
+            updown = /usr/local/libexec/ipsec/_updown iptables
+            rekey_time = 5400
+            rekey_bytes = 500000000
+            rekey_packets = 1000000
+            esp_proposals = aes128gcm128-x25519
+         }
+      }
+      version = 2
+      mobike = no
+      reauth_time = 10800
+      proposals = aes128-sha256-x25519
+   }
+}
diff --git a/testing/tests/swanctl/net2net-childless/hosts/sun/etc/strongswan.conf b/testing/tests/swanctl/net2net-childless/hosts/sun/etc/strongswan.conf
new file mode 100755 (executable)
index 0000000..ad4c18e
--- /dev/null
@@ -0,0 +1,9 @@
+# /etc/strongswan.conf - strongSwan configuration file
+
+swanctl {
+  load = pem pkcs1 x509 revocation constraints pubkey openssl random
+}
+
+charon-systemd {
+  load = random nonce aes sha1 sha2 hmac pem pkcs1 x509 revocation curve25519 gmp curl kernel-netlink socket-default updown vici
+}
diff --git a/testing/tests/swanctl/net2net-childless/hosts/sun/etc/swanctl/swanctl.conf b/testing/tests/swanctl/net2net-childless/hosts/sun/etc/swanctl/swanctl.conf
new file mode 100755 (executable)
index 0000000..c488d1e
--- /dev/null
@@ -0,0 +1,33 @@
+connections {
+
+   gw-gw {
+      local_addrs  = 192.168.0.2
+      remote_addrs = 192.168.0.1
+
+      local {
+         auth = pubkey
+         certs = sunCert.pem
+         id = sun.strongswan.org
+      }
+      remote {
+         auth = pubkey
+         id = moon.strongswan.org
+      }
+      children {
+         net-net {
+            local_ts  = 10.2.0.0/16
+            remote_ts = 10.1.0.0/16
+
+            updown = /usr/local/libexec/ipsec/_updown iptables
+            rekey_time = 5400
+            rekey_bytes = 500000000
+            rekey_packets = 1000000
+            esp_proposals = aes128gcm128-x25519
+         }
+      }
+      version = 2
+      mobike = no
+      reauth_time = 10800
+      proposals = aes128-sha256-x25519
+   }
+}
diff --git a/testing/tests/swanctl/net2net-childless/posttest.dat b/testing/tests/swanctl/net2net-childless/posttest.dat
new file mode 100755 (executable)
index 0000000..755f0e5
--- /dev/null
@@ -0,0 +1,5 @@
+moon::swanctl --terminate --ike gw-gw 2> /dev/null
+moon::systemctl stop strongswan-swanctl
+sun::systemctl stop strongswan-swanctl
+moon::iptables-restore < /etc/iptables.flush
+sun::iptables-restore < /etc/iptables.flush
diff --git a/testing/tests/swanctl/net2net-childless/pretest.dat b/testing/tests/swanctl/net2net-childless/pretest.dat
new file mode 100755 (executable)
index 0000000..9440dda
--- /dev/null
@@ -0,0 +1,7 @@
+moon::iptables-restore < /etc/iptables.rules
+sun::iptables-restore < /etc/iptables.rules
+moon::systemctl start strongswan-swanctl
+sun::systemctl start strongswan-swanctl
+moon::expect-connection gw-gw
+sun::expect-connection gw-gw
+moon::swanctl --initiate --child net-net 2> /dev/null
diff --git a/testing/tests/swanctl/net2net-childless/test.conf b/testing/tests/swanctl/net2net-childless/test.conf
new file mode 100755 (executable)
index 0000000..87abc76
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# This configuration file provides information on the
+# guest instances used for this test
+
+# All guest instances that are required for this test
+#
+VIRTHOSTS="alice moon winnetou sun bob"
+
+# Corresponding block diagram
+#
+DIAGRAM="a-m-w-s-b.png"
+
+# Guest instances on which tcpdump is to be started
+#
+TCPDUMPHOSTS="sun"
+
+# Guest instances on which IPsec is started
+# Used for IPsec logging purposes
+#
+IPSECHOSTS="moon sun"
+
+# charon controlled by swanctl
+#
+SWANCTL=1