From 9486a2e5b0b5b1e133097c8c25491ca97a31d1db Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 29 Mar 2019 12:11:10 +0100 Subject: [PATCH 01/12] ike-cfg: Pass arguments as struct --- src/charon-cmd/cmd/cmd_connection.c | 21 ++++---- src/charon-nm/nm/nm_service.c | 24 +++++---- src/conftest/config.c | 21 +++++--- .../backend/android_service.c | 26 +++++---- src/frontends/osx/charon-xpc/xpc_dispatch.c | 17 ++++-- src/libcharon/config/ike_cfg.c | 33 ++++++------ src/libcharon/config/ike_cfg.h | 54 +++++++++++-------- src/libcharon/plugins/ha/ha_tunnel.c | 12 +++-- .../plugins/load_tester/load_tester_config.c | 23 ++++---- src/libcharon/plugins/medcli/medcli_config.c | 27 ++++++---- src/libcharon/plugins/medsrv/medsrv_config.c | 13 +++-- src/libcharon/plugins/sql/sql_config.c | 14 +++-- src/libcharon/plugins/stroke/stroke_config.c | 34 ++++++------ src/libcharon/plugins/uci/uci_config.c | 40 ++++++++------ src/libcharon/plugins/vici/vici_config.c | 17 ++++-- src/libcharon/tests/suites/test_ike_cfg.c | 10 +++- src/libcharon/tests/suites/test_peer_cfg.c | 10 +++- .../tests/utils/exchange_test_helper.c | 10 +++- 18 files changed, 253 insertions(+), 153 deletions(-) diff --git a/src/charon-cmd/cmd/cmd_connection.c b/src/charon-cmd/cmd/cmd_connection.c index 1cf431ff2..b91c89830 100644 --- a/src/charon-cmd/cmd/cmd_connection.c +++ b/src/charon-cmd/cmd/cmd_connection.c @@ -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, diff --git a/src/charon-nm/nm/nm_service.c b/src/charon-nm/nm/nm_service.c index e207ac851..1b07230fd 100644 --- a/src/charon-nm/nm/nm_service.c +++ b/src/charon-nm/nm/nm_service.c @@ -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"); diff --git a/src/conftest/config.c b/src/conftest/config.c index d926dfca2..ff47a77ee 100644 --- a/src/conftest/config.c +++ b/src/conftest/config.c @@ -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) { diff --git a/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_service.c index a6b45059e..de634891c 100644 --- a/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_service.c +++ b/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_service.c @@ -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) { diff --git a/src/frontends/osx/charon-xpc/xpc_dispatch.c b/src/frontends/osx/charon-xpc/xpc_dispatch.c index 0483199df..51b2b5008 100644 --- a/src/frontends/osx/charon-xpc/xpc_dispatch.c +++ b/src/frontends/osx/charon-xpc/xpc_dispatch.c @@ -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); diff --git a/src/libcharon/config/ike_cfg.c b/src/libcharon/config/ike_cfg.c index 357c4a73b..cb75b7054 100644 --- a/src/libcharon/config/ike_cfg.c +++ b/src/libcharon/config/ike_cfg.c @@ -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 @@ -609,13 +609,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; @@ -644,24 +641,24 @@ 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, + .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; } diff --git a/src/libcharon/config/ike_cfg.h b/src/libcharon/config/ike_cfg.h index 49690c892..92a9915e0 100644 --- a/src/libcharon/config/ike_cfg.h +++ b/src/libcharon/config/ike_cfg.h @@ -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 @@ -26,6 +26,7 @@ typedef enum ike_version_t ike_version_t; typedef enum fragmentation_t fragmentation_t; typedef struct ike_cfg_t ike_cfg_t; +typedef struct ike_cfg_create_t ike_cfg_create_t; #include #include @@ -241,30 +242,41 @@ struct ike_cfg_t { }; /** - * Creates a ike_cfg_t object. + * Data passed to the constructor of an ike_cfg_t object. * - * Supplied hosts become owned by ike_cfg, strings get cloned. + * 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; + /** DSCP value to send IKE packets with */ + uint8_t dscp; +}; + +/** + * Creates 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. - * - * @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 diff --git a/src/libcharon/plugins/ha/ha_tunnel.c b/src/libcharon/plugins/ha/ha_tunnel.c index cfa896e93..f94291505 100644 --- a/src/libcharon/plugins/ha/ha_tunnel.c +++ b/src/libcharon/plugins/ha/ha_tunnel.c @@ -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); diff --git a/src/libcharon/plugins/load_tester/load_tester_config.c b/src/libcharon/plugins/load_tester/load_tester_config.c index 78be45f68..6fb667375 100644 --- a/src/libcharon/plugins/load_tester/load_tester_config.c +++ b/src/libcharon/plugins/load_tester/load_tester_config.c @@ -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); diff --git a/src/libcharon/plugins/medcli/medcli_config.c b/src/libcharon/plugins/medcli/medcli_config.c index 789c01bae..be42d7d7d 100644 --- a/src/libcharon/plugins/medcli/medcli_config.c +++ b/src/libcharon/plugins/medcli/medcli_config.c @@ -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)); diff --git a/src/libcharon/plugins/medsrv/medsrv_config.c b/src/libcharon/plugins/medsrv/medsrv_config.c index 6068022b1..b9dce8c0d 100644 --- a/src/libcharon/plugins/medsrv/medsrv_config.c +++ b/src/libcharon/plugins/medsrv/medsrv_config.c @@ -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)); diff --git a/src/libcharon/plugins/sql/sql_config.c b/src/libcharon/plugins/sql/sql_config.c index bb1ba71db..fb8ea8c5e 100644 --- a/src/libcharon/plugins/sql/sql_config.c +++ b/src/libcharon/plugins/sql/sql_config.c @@ -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; } diff --git a/src/libcharon/plugins/stroke/stroke_config.c b/src/libcharon/plugins/stroke/stroke_config.c index 8cdb5ef48..1514b74dd 100644 --- a/src/libcharon/plugins/stroke/stroke_config.c +++ b/src/libcharon/plugins/stroke/stroke_config.c @@ -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; } - 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); + if (ike.local_port == IKEV2_UDP_PORT) + { + ike.local_port = charon->socket->get_port(charon->socket, FALSE); + } + ike_cfg = ike_cfg_create(&ike); if (!add_proposals(this, msg->add_conn.algorithms.ike, ike_cfg, NULL, PROTO_IKE)) diff --git a/src/libcharon/plugins/uci/uci_config.c b/src/libcharon/plugins/uci/uci_config.c index 5654fc51e..a66bb58cb 100644 --- a/src/libcharon/plugins/uci/uci_config.c +++ b/src/libcharon/plugins/uci/uci_config.c @@ -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)); diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index f86d5c9cd..59aadfd91 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -2382,6 +2382,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 +2510,18 @@ 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, + .dscp = peer.dscp, + }; + ike_cfg = ike_cfg_create(&ike); cfg = (peer_cfg_create_t){ .cert_policy = peer.send_cert, diff --git a/src/libcharon/tests/suites/test_ike_cfg.c b/src/libcharon/tests/suites/test_ike_cfg.c index 9bbc064f7..ded0462b3 100644 --- a/src/libcharon/tests/suites/test_ike_cfg.c +++ b/src/libcharon/tests/suites/test_ike_cfg.c @@ -19,11 +19,17 @@ 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); diff --git a/src/libcharon/tests/suites/test_peer_cfg.c b/src/libcharon/tests/suites/test_peer_cfg.c index 02e38a314..94337e258 100644 --- a/src/libcharon/tests/suites/test_peer_cfg.c +++ b/src/libcharon/tests/suites/test_peer_cfg.c @@ -23,8 +23,14 @@ */ 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); } /** diff --git a/src/libcharon/tests/utils/exchange_test_helper.c b/src/libcharon/tests/utils/exchange_test_helper.c index bebf33463..39f0d1647 100644 --- a/src/libcharon/tests/utils/exchange_test_helper.c +++ b/src/libcharon/tests/utils/exchange_test_helper.c @@ -85,11 +85,17 @@ 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); + ike_cfg = ike_cfg_create(&ike); if (conf) { proposal = initiator ? conf->initiator.ike : conf->responder.ike; From ddb083c164ffb150ab914238ffb82a55a16b9dab Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 29 Mar 2019 15:06:20 +0100 Subject: [PATCH 02/12] ike-cfg: Add setting for childless IKE_SAs --- src/libcharon/config/ike_cfg.c | 16 +++++++++++++++- src/libcharon/config/ike_cfg.h | 24 +++++++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/libcharon/config/ike_cfg.c b/src/libcharon/config/ike_cfg.c index cb75b7054..d99abbced 100644 --- a/src/libcharon/config/ike_cfg.c +++ b/src/libcharon/config/ike_cfg.c @@ -101,10 +101,15 @@ 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 */ @@ -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 && @@ -622,6 +634,7 @@ ike_cfg_t *ike_cfg_create(ike_cfg_create_t *data) .send_certreq = _send_certreq, .force_encap = _force_encap_, .fragmentation = _fragmentation, + .childless = _childless, .resolve_me = _resolve_me, .resolve_other = _resolve_other, .match_me = _match_me, @@ -645,6 +658,7 @@ ike_cfg_t *ike_cfg_create(ike_cfg_create_t *data) .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(), diff --git a/src/libcharon/config/ike_cfg.h b/src/libcharon/config/ike_cfg.h index 92a9915e0..9c697dadc 100644 --- a/src/libcharon/config/ike_cfg.h +++ b/src/libcharon/config/ike_cfg.h @@ -25,6 +25,7 @@ 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; @@ -61,6 +62,18 @@ enum fragmentation_t { FRAGMENTATION_FORCE, }; +/** + * 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 */ @@ -204,12 +217,19 @@ 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. * @@ -266,6 +286,8 @@ struct ike_cfg_create_t { 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; }; From 93104d0fe99214a6981f2efdd9bdbb6125822435 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 29 Mar 2019 15:18:08 +0100 Subject: [PATCH 03/12] ike-init: Notify initiator if childless IKE_SAs are accepted --- src/libcharon/sa/ike_sa.h | 5 +++++ src/libcharon/sa/ikev2/tasks/ike_init.c | 16 +++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index c7ef1fe3c..d51108140 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -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), }; /** diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index b570904e2..04ce5045e 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -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; From ed521a7470abd841a876716ee21b408670dbb382 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 29 Mar 2019 16:46:59 +0100 Subject: [PATCH 04/12] child-create: Initiate and handle childless IKE_SAs according to RFC 6023 --- src/libcharon/sa/ikev2/tasks/child_create.c | 87 +++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index b80e71d54..ac1f9994a 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -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; From 6b00d34b4233c1848b4d0f428bf1e81c9f23f5f0 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 29 Mar 2019 16:50:26 +0100 Subject: [PATCH 05/12] controller: Make child config optional for initiate() --- src/libcharon/control/controller.c | 23 ++++++++++++++--------- src/libcharon/control/controller.h | 2 +- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/libcharon/control/controller.c b/src/libcharon/control/controller.c index 589c536d2..0c86275e2 100644 --- a/src/libcharon/control/controller.c +++ b/src/libcharon/control/controller.c @@ -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; diff --git a/src/libcharon/control/controller.h b/src/libcharon/control/controller.h index af9baca01..b4ccfced2 100644 --- a/src/libcharon/control/controller.h +++ b/src/libcharon/control/controller.h @@ -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 From 2889b77da29c8c10fe9ab83aa636c21128a80a80 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 29 Mar 2019 17:13:49 +0100 Subject: [PATCH 06/12] vici: Make childless initiation of IKE_SAs configurable --- src/libcharon/plugins/vici/vici_config.c | 25 ++++++++++++++++++++++++ src/swanctl/swanctl.opt | 17 +++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index 59aadfd91..1ff0754f4 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -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); @@ -1561,6 +1563,27 @@ CALLBACK(parse_frag, bool, return FALSE; } +/** + * 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 */ @@ -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 }, @@ -2519,6 +2543,7 @@ CALLBACK(config_sn, bool, .no_certreq = !peer.send_certreq, .force_encap = peer.encap, .fragmentation = peer.fragmentation, + .childless = peer.childless, .dscp = peer.dscp, }; ike_cfg = ike_cfg_create(&ike); diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index 460e17b09..6765e9d41 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -154,7 +154,7 @@ connections..dpd_timeout = 0s specified; this option has no effect on connections using IKE2. connections..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..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..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..send_certreq = yes Send certificate requests payloads (_yes_ or _no_). From c863960eb148280b67f3656aa61c2a4a2e5be4e4 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 29 Mar 2019 17:38:39 +0100 Subject: [PATCH 07/12] vici: Support initiation of IKE_SAs The configuration must allow the initiation of a childless IKE_SA (which is already the case with the default of 'accept'). --- src/libcharon/plugins/vici/README.md | 2 +- src/libcharon/plugins/vici/vici_control.c | 34 ++++++++++++++--------- src/swanctl/commands/initiate.c | 4 +-- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/libcharon/plugins/vici/README.md b/src/libcharon/plugins/vici/README.md index 61427d2b1..f029d06d7 100644 --- a/src/libcharon/plugins/vici/README.md +++ b/src/libcharon/plugins/vici/README.md @@ -258,7 +258,7 @@ Initiates an SA while streaming _control-log_ events. { child = - ike = + ike = timeout = init-limits = loglevel = diff --git a/src/libcharon/plugins/vici/vici_control.c b/src/libcharon/plugins/vici/vici_control.c index 16e49fdbc..4c09b578d 100644 --- a/src/libcharon/plugins/vici/vici_control.c +++ b/src/libcharon/plugins/vici/vici_control.c @@ -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); } } diff --git a/src/swanctl/commands/initiate.c b/src/swanctl/commands/initiate.c index bf8d2cd79..8ade8bf41 100644 --- a/src/swanctl/commands/initiate.c +++ b/src/swanctl/commands/initiate.c @@ -128,11 +128,11 @@ static void __attribute__ ((constructor))reg() { command_register((command_t) { initiate, 'i', "initiate", "initiate a connection", - {"--child [--ike ] [--timeout ] [--raw|--pretty]"}, + {"[--child ] [--ike ] [--timeout ] [--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"}, From 202fb101b8aed4b8153f638580177163665b3faa Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 2 Apr 2019 16:22:21 +0200 Subject: [PATCH 08/12] unit-tests: Add macros to assert certain payloads are (not) in a message --- .../tests/utils/exchange_test_asserts.h | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/libcharon/tests/utils/exchange_test_asserts.h b/src/libcharon/tests/utils/exchange_test_asserts.h index ae9ac5c98..beb8cfd95 100644 --- a/src/libcharon/tests/utils/exchange_test_asserts.h +++ b/src/libcharon/tests/utils/exchange_test_asserts.h @@ -297,6 +297,26 @@ bool exchange_test_asserts_message(listener_t *this, ike_sa_t *ike_sa, #define assert_single_payload(dir, expected) \ _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. From e0678a8cc635cb0254f99333261c5bc6ab741c35 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 2 Apr 2019 16:23:34 +0200 Subject: [PATCH 09/12] unit-tests: Add helper to create but not yet establish two IKE_SAs --- .../tests/utils/exchange_test_helper.c | 92 +++++++++++++------ .../tests/utils/exchange_test_helper.h | 17 ++++ 2 files changed, 81 insertions(+), 28 deletions(-) diff --git a/src/libcharon/tests/utils/exchange_test_helper.c b/src/libcharon/tests/utils/exchange_test_helper.c index 39f0d1647..a267468fd 100644 --- a/src/libcharon/tests/utils/exchange_test_helper.c +++ b/src/libcharon/tests/utils/exchange_test_helper.c @@ -49,6 +49,11 @@ struct private_exchange_test_helper_t { * List of registered listeners */ array_t *listeners; + + /** + * Config backend + */ + private_backend_t *backend; }; /** @@ -186,6 +191,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) { @@ -210,43 +227,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)); @@ -258,10 +282,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, @@ -306,6 +326,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 */ @@ -317,14 +338,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(); @@ -345,6 +376,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, @@ -368,6 +401,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) */ diff --git a/src/libcharon/tests/utils/exchange_test_helper.h b/src/libcharon/tests/utils/exchange_test_helper.h index e1fdb012a..8f43c0927 100644 --- a/src/libcharon/tests/utils/exchange_test_helper.h +++ b/src/libcharon/tests/utils/exchange_test_helper.h @@ -57,6 +57,23 @@ struct exchange_test_helper_t { void (*establish_sa)(exchange_test_helper_t *this, ike_sa_t **init, 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. From 1b19469c6707a4cc9446a1c4dc4d2de3eeb44a2f Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 2 Apr 2019 16:24:01 +0200 Subject: [PATCH 10/12] unit-tests: Make childless initiation configurable --- src/libcharon/tests/utils/exchange_test_helper.c | 5 ++++- src/libcharon/tests/utils/exchange_test_helper.h | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libcharon/tests/utils/exchange_test_helper.c b/src/libcharon/tests/utils/exchange_test_helper.c index a267468fd..97fa6fecd 100644 --- a/src/libcharon/tests/utils/exchange_test_helper.c +++ b/src/libcharon/tests/utils/exchange_test_helper.c @@ -100,11 +100,14 @@ static ike_cfg_t *create_ike_cfg(bool initiator, exchange_test_sa_conf_t *conf) ike_cfg_t *ike_cfg; char *proposal = NULL; - ike_cfg = ike_cfg_create(&ike); 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, diff --git a/src/libcharon/tests/utils/exchange_test_helper.h b/src/libcharon/tests/utils/exchange_test_helper.h index 8f43c0927..1541e88da 100644 --- a/src/libcharon/tests/utils/exchange_test_helper.h +++ b/src/libcharon/tests/utils/exchange_test_helper.h @@ -109,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; }; From fbb0feeea95bf04e27867b60312ad49782390be5 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 2 Apr 2019 16:44:44 +0200 Subject: [PATCH 11/12] unit-tests: Add unit tests for childless IKE_SA initiation --- src/libcharon/tests/Makefile.am | 1 + src/libcharon/tests/exchange_tests.h | 1 + src/libcharon/tests/suites/test_childless.c | 293 ++++++++++++++++++++ 3 files changed, 295 insertions(+) create mode 100644 src/libcharon/tests/suites/test_childless.c diff --git a/src/libcharon/tests/Makefile.am b/src/libcharon/tests/Makefile.am index 101b534f0..1d925019c 100644 --- a/src/libcharon/tests/Makefile.am +++ b/src/libcharon/tests/Makefile.am @@ -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 \ diff --git a/src/libcharon/tests/exchange_tests.h b/src/libcharon/tests/exchange_tests.h index 6b35ea5e5..491c25cd9 100644 --- a/src/libcharon/tests/exchange_tests.h +++ b/src/libcharon/tests/exchange_tests.h @@ -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 index 000000000..6ac02aad8 --- /dev/null +++ b/src/libcharon/tests/suites/test_childless.c @@ -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 . + * + * 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 +#include +#include +#include + +/** + * 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; +} From 012221a8671877bd142737191f4ac33430f8f96e Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 3 Apr 2019 11:15:50 +0200 Subject: [PATCH 12/12] testing: Add swanctl/net2net-childless scenario --- .../swanctl/net2net-childless/description.txt | 13 +++++++ .../swanctl/net2net-childless/evaltest.dat | 7 ++++ .../hosts/moon/etc/strongswan.conf | 9 +++++ .../hosts/moon/etc/swanctl/swanctl.conf | 35 +++++++++++++++++++ .../hosts/sun/etc/strongswan.conf | 9 +++++ .../hosts/sun/etc/swanctl/swanctl.conf | 33 +++++++++++++++++ .../swanctl/net2net-childless/posttest.dat | 5 +++ .../swanctl/net2net-childless/pretest.dat | 7 ++++ .../tests/swanctl/net2net-childless/test.conf | 25 +++++++++++++ 9 files changed, 143 insertions(+) create mode 100755 testing/tests/swanctl/net2net-childless/description.txt create mode 100755 testing/tests/swanctl/net2net-childless/evaltest.dat create mode 100755 testing/tests/swanctl/net2net-childless/hosts/moon/etc/strongswan.conf create mode 100755 testing/tests/swanctl/net2net-childless/hosts/moon/etc/swanctl/swanctl.conf create mode 100755 testing/tests/swanctl/net2net-childless/hosts/sun/etc/strongswan.conf create mode 100755 testing/tests/swanctl/net2net-childless/hosts/sun/etc/swanctl/swanctl.conf create mode 100755 testing/tests/swanctl/net2net-childless/posttest.dat create mode 100755 testing/tests/swanctl/net2net-childless/pretest.dat create mode 100755 testing/tests/swanctl/net2net-childless/test.conf diff --git a/testing/tests/swanctl/net2net-childless/description.txt b/testing/tests/swanctl/net2net-childless/description.txt new file mode 100755 index 000000000..6da90813f --- /dev/null +++ b/testing/tests/swanctl/net2net-childless/description.txt @@ -0,0 +1,13 @@ +A connection between the subnets behind the gateways moon and sun +is set up using childless initiation of IKEv2 SAs (RFC 6023). +

+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. +

+The authentication is based on X.509 certificates. 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 alice behind gateway +moon pings client bob located behind gateway sun. diff --git a/testing/tests/swanctl/net2net-childless/evaltest.dat b/testing/tests/swanctl/net2net-childless/evaltest.dat new file mode 100755 index 000000000..9826cdd01 --- /dev/null +++ b/testing/tests/swanctl/net2net-childless/evaltest.dat @@ -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 index 000000000..ad4c18e43 --- /dev/null +++ b/testing/tests/swanctl/net2net-childless/hosts/moon/etc/strongswan.conf @@ -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 index 000000000..ead3ce472 --- /dev/null +++ b/testing/tests/swanctl/net2net-childless/hosts/moon/etc/swanctl/swanctl.conf @@ -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 index 000000000..ad4c18e43 --- /dev/null +++ b/testing/tests/swanctl/net2net-childless/hosts/sun/etc/strongswan.conf @@ -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 index 000000000..c488d1ed0 --- /dev/null +++ b/testing/tests/swanctl/net2net-childless/hosts/sun/etc/swanctl/swanctl.conf @@ -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 index 000000000..755f0e5f8 --- /dev/null +++ b/testing/tests/swanctl/net2net-childless/posttest.dat @@ -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 index 000000000..9440ddab0 --- /dev/null +++ b/testing/tests/swanctl/net2net-childless/pretest.dat @@ -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 index 000000000..87abc763b --- /dev/null +++ b/testing/tests/swanctl/net2net-childless/test.conf @@ -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