diff --git a/libreswan-5.3-outstanding-ike-auth-crossing.patch b/libreswan-5.3-outstanding-ike-auth-crossing.patch new file mode 100644 index 0000000..6d37dad --- /dev/null +++ b/libreswan-5.3-outstanding-ike-auth-crossing.patch @@ -0,0 +1,354 @@ +From a671b9ce5ff86ffc31142436e9c93031083d7221 Mon Sep 17 00:00:00 2001 +From: Ondrej Moris +Date: Fri, 21 Nov 2025 22:42:33 +0100 +Subject: [PATCH 1/2] Add option reject-simultaneous-ike-auth + +Newly added option (default values is yes) decides what to +do when IKE_AUTH request is received from the peer while having +an outstanding IKE_AUTH request for the same connection. + +Signed-off-by: Ondrej Moris +Signed-off-by: Andrew Cagney +--- + .../reject-simultaneous-ike-auth.xml | 21 +++++++++++++++++++ + configs/ipsec.conf.5.xml | 2 ++ + include/ipsecconf/keywords.h | 1 + + include/whack.h | 1 + + lib/libswan/ipsecconf/keywords.c | 1 + + lib/libswan/ipsecconf/starterwhack.c | 1 + + programs/pluto/connections.c | 6 ++++++ + programs/pluto/connections.h | 2 ++ + programs/whack/whack.c | 7 +++++++ + 9 files changed, 42 insertions(+) + create mode 100644 configs/d.ipsec.conf/reject-simultaneous-ike-auth.xml + +diff --git a/configs/d.ipsec.conf/reject-simultaneous-ike-auth.xml b/configs/d.ipsec.conf/reject-simultaneous-ike-auth.xml +new file mode 100644 +index 0000000000..7c3f68c42d +--- /dev/null ++++ b/configs/d.ipsec.conf/reject-simultaneous-ike-auth.xml +@@ -0,0 +1,21 @@ ++ ++ ++ ++ ++ ++ ++ ++ When libreswan receives IKE_AUTH request from the peer while having ++ an outstanding IKE_AUTH request for the same connection, reject with ++ AUTHENTICATION_FAILED to avoid potential SA mismatch issues. This only ++ applies to permanent IKEv2 connections so that the revival mechanism ++ ensures connection retry. ++ ++ ++ ++ The accepted values are (the default) or ++ . ++ ++ ++ ++ +diff --git a/configs/ipsec.conf.5.xml b/configs/ipsec.conf.5.xml +index 03f04106ae..eb0f69007d 100644 +--- a/configs/ipsec.conf.5.xml ++++ b/configs/ipsec.conf.5.xml +@@ -25,6 +25,7 @@ + + + ++ + + + +@@ -377,6 +378,7 @@ + &failureshunt; + &negotiationshunt; + &debug; ++ &reject-simultaneous-ike-auth; + + + +diff --git a/include/ipsecconf/keywords.h b/include/ipsecconf/keywords.h +index bdbc00ee98..21b5954c52 100644 +--- a/include/ipsecconf/keywords.h ++++ b/include/ipsecconf/keywords.h +@@ -201,6 +201,7 @@ enum config_conn_keyword { + KWS_CISCO_SPLIT, /* send cisco unity VID */ + + KWYN_SEND_ESP_TFC_PADDING_NOT_SUPPORTED, ++ KWYN_REJECT_SIMULTANEOUS_IKE_AUTH, + KWYN_FAKE_STRONGSWAN, /* send strongswan VID (required for twofish/serpent) */ + KWYN_SEND_VENDORID, /* per conn sending of our own libreswan vendorid */ + KNCF_IKEPAD, /* pad IKE packets to 4 bytes */ +diff --git a/include/whack.h b/include/whack.h +index 85f38da0aa..76d6eb2908 100644 +--- a/include/whack.h ++++ b/include/whack.h +@@ -409,6 +409,7 @@ struct whack_message { + const char *priority; + const char *tfc; + enum yn_options send_esp_tfc_padding_not_supported; ++ enum yn_options reject_simultaneous_ike_auth; + + enum yn_options iptfs; + enum yn_options iptfs_fragmentation; +diff --git a/lib/libswan/ipsecconf/keywords.c b/lib/libswan/ipsecconf/keywords.c +index 1e42bf2a4a..6c662a9688 100644 +--- a/lib/libswan/ipsecconf/keywords.c ++++ b/lib/libswan/ipsecconf/keywords.c +@@ -162,6 +162,7 @@ static const struct keyword_def config_conn_keyword[] = { + + K("initial-contact", LEMPTY, kt_sparse_name, KWYN_INITIAL_CONTACT, .sparse_names = &yn_option_names), + K("send-esp-tfc-padding-not-supported", LEMPTY, kt_sparse_name, KWYN_SEND_ESP_TFC_PADDING_NOT_SUPPORTED, .sparse_names = &yn_option_names), ++ K("reject-simultaneous-ike-auth", LEMPTY, kt_sparse_name, KWYN_REJECT_SIMULTANEOUS_IKE_AUTH, .sparse_names = &yn_option_names), + + K("iptfs", LEMPTY, kt_sparse_name, KWYN_IPTFS, .sparse_names = &yn_option_names), + K("iptfs-fragmentation", LEMPTY, kt_sparse_name, KWYN_IPTFS_FRAGMENTATION, .sparse_names = &yn_option_names), +diff --git a/lib/libswan/ipsecconf/starterwhack.c b/lib/libswan/ipsecconf/starterwhack.c +index 0e09a8e8d5..9a73769c15 100644 +--- a/lib/libswan/ipsecconf/starterwhack.c ++++ b/lib/libswan/ipsecconf/starterwhack.c +@@ -232,6 +232,7 @@ int starter_whack_add_conn(const char *ctlsocket, + conn->values[KWYN_SEND_ESP_TFC_PADDING_NOT_SUPPORTED].option; + msg.nflog_group = conn->values[KWS_NFLOG_GROUP].string; + msg.reqid = conn->values[KWS_REQID].string; ++ msg.reject_simultaneous_ike_auth = conn->values[KWYN_REJECT_SIMULTANEOUS_IKE_AUTH].option; + + if (conn->values[KNCF_TCP_REMOTEPORT].set) { + msg.tcp_remoteport = conn->values[KNCF_TCP_REMOTEPORT].option; +diff --git a/programs/pluto/connections.c b/programs/pluto/connections.c +index e949f56ebd..ca98dfb1e9 100644 +--- a/programs/pluto/connections.c ++++ b/programs/pluto/connections.c +@@ -4894,6 +4894,12 @@ static diag_t extract_connection(const struct whack_message *wm, + wm->send_esp_tfc_padding_not_supported, + YN_NO, wm, c->logger); + ++ if (wm->reject_simultaneous_ike_auth && ike_version < IKEv2) { ++ return diag("cannot specify reject-simultaneous-ike-auth for IKEv1"); ++ } ++ config->reject_simultaneous_ike_auth = extract_yn("", "reject-simultaneous-ike-auth", ++ wm->reject_simultaneous_ike_auth, /*value_when_unset*/YN_YES, wm, c->logger); ++ + /* + * Since security labels use the same REQID for everything, + * pre-assign it. +diff --git a/programs/pluto/connections.h b/programs/pluto/connections.h +index 0910fc9930..cd9cb331b6 100644 +--- a/programs/pluto/connections.h ++++ b/programs/pluto/connections.h +@@ -431,6 +431,8 @@ struct config { + uint32_t id; + } ipsec_interface; + ++ bool reject_simultaneous_ike_auth; ++ + struct end_config end[END_ROOF]; + }; + +diff --git a/programs/whack/whack.c b/programs/whack/whack.c +index a5a95045bf..5e47e009b8 100644 +--- a/programs/whack/whack.c ++++ b/programs/whack/whack.c +@@ -111,6 +111,7 @@ static void help(void) + " [--mtu ] \\\n" + " [--priority ] [--reqid ] \\\n" + " [--tfc ] [--send-esp-tfc-padding-not-supported] \\\n" ++ " [--reject-simultaneous-ike-auth] \\\n" + " [--iptfs[={yes,no}] \\\n" + " [--iptfs-fragmentation[={yes,no}]] \\\n" + " [--iptfs-packet-size ] \\\n" +@@ -516,6 +517,7 @@ enum opt { + CD_PRIORITY, + CD_TFC, + CD_SEND_ESP_TFC_PADDING_NOT_SUPPORTED, ++ CD_REJECT_SIMULTANEOUS_IKE_AUTH, + CD_PFS, + CD_REQID, + CD_NFLOG_GROUP, +@@ -898,6 +900,7 @@ const struct option optarg_options[] = { + { "tfc\0", required_argument, NULL, CD_TFC }, + { "send-esp-tfc-padding-not-supported\0yes|no", optional_argument, NULL, CD_SEND_ESP_TFC_PADDING_NOT_SUPPORTED }, + { "send-no-esp-tfc\0", no_argument, NULL, CD_SEND_ESP_TFC_PADDING_NOT_SUPPORTED }, ++ { "reject-simultaneous-ike-auth\0yes|no", optional_argument, NULL, CD_REJECT_SIMULTANEOUS_IKE_AUTH }, + { "pfs\0", optional_argument, NULL, CD_PFS }, + { "reqid\01-65535", required_argument, NULL, CD_REQID }, + #ifdef USE_NFLOG +@@ -2085,6 +2088,10 @@ int main(int argc, char **argv) + optarg_yn(logger, YN_YES); + continue; + ++ case CD_REJECT_SIMULTANEOUS_IKE_AUTH: /* --reject-simultaneous-ike-auth */ ++ msg.reject_simultaneous_ike_auth = optarg_yn(logger, YN_YES); ++ continue; ++ + case CD_PFS: /* --pfs */ + msg.pfs = optarg_yn(logger, YN_YES); + continue; +-- +2.52.0 + + +From 6d1d06ef085cbdfdfeb24f677cec9d4540db2903 Mon Sep 17 00:00:00 2001 +From: Ondrej Moris +Date: Fri, 21 Nov 2025 22:42:33 +0100 +Subject: [PATCH 2/2] ikev2: reject simultaneous IKE_AUTH requests + +If initiator has another IKE SA with IKE_AUTH request +outstanding for the same permanent connection then send +AUTHENTICATION_FAILED instead of IKE_AUTH response for this +IKE_AUTH request and terminate current IKE SA. This is to +prevent potential crossing streams scenario. + +This is now the default behavior and can be disabled by +a connection option reject-simultaneous-ike-auth. + +Signed-off-by: Ondrej Moris +--- + programs/pluto/ikev2_auth.c | 53 +++++++++++++++++++++++++++++++++ + programs/pluto/ikev2_auth.h | 3 ++ + programs/pluto/ikev2_child.c | 13 ++++++++ + programs/pluto/ikev2_ike_auth.c | 15 +++++++++- + 4 files changed, 83 insertions(+), 1 deletion(-) + +diff --git a/programs/pluto/ikev2_auth.c b/programs/pluto/ikev2_auth.c +index 6e89bce58b..2e1b6df655 100644 +--- a/programs/pluto/ikev2_auth.c ++++ b/programs/pluto/ikev2_auth.c +@@ -1044,3 +1044,56 @@ lset_t proposed_v2AUTH(struct ike_sa *ike, + } + } + } ++ ++/* ++ * Check if a given permanent connection has another IKE SA with ++ * IKE_AUTH request outstanding. This is useful to detect potential ++ * IKE_AUTH crossing streams scenarios. ++ */ ++bool has_outstanding_ike_auth_request(const struct connection *c, ++ const struct ike_sa *ike, ++ const struct msg_digest *md) ++{ ++ /* Check can be disabled in a connection config */ ++ if (!c->config->reject_simultaneous_ike_auth) { ++ return false; ++ } ++ ++ /* Connection must be permanent and request must be incoming */ ++ if (v2_msg_role(md) != MESSAGE_REQUEST || !is_permanent(c)) { ++ return false; ++ } ++ ++ struct state_filter sf = { ++ .connection_serialno = c->serialno, ++ .search = { ++ .order = NEW2OLD, ++ .verbose.logger = ike->sa.logger, ++ .where = HERE, ++ }, ++ }; ++ ++ while (next_state(&sf)) { ++ if (!IS_IKE_SA(sf.st)) { ++ continue; ++ } ++ ++ struct ike_sa *simultaneous_ike = pexpect_ike_sa(sf.st); ++ if (simultaneous_ike == NULL || simultaneous_ike == ike) { ++ continue; ++ } else if (simultaneous_ike->sa.st_sa_role != SA_INITIATOR) { ++ continue; ++ } else if (!v2_msgid_request_outstanding(simultaneous_ike)) { ++ continue; ++ } ++ ++ const struct v2_exchange *outstanding_request = ++ simultaneous_ike->sa.st_v2_msgid_windows.initiator.exchange; ++ if (outstanding_request != NULL && outstanding_request->type == ISAKMP_v2_IKE_AUTH) { ++ llog(RC_LOG, ike->sa.logger, "IKE SA "PRI_SO" has outstanding IKE_AUTH request", ++ pri_so(simultaneous_ike->sa.st_serialno)); ++ return true; ++ } ++ } ++ return false; ++} +diff --git a/programs/pluto/ikev2_auth.h b/programs/pluto/ikev2_auth.h +index 13ee71c36d..d616534507 100644 +--- a/programs/pluto/ikev2_auth.h ++++ b/programs/pluto/ikev2_auth.h +@@ -85,4 +85,7 @@ struct crypt_mac v2_remote_id_hash(const struct ike_sa *ike, const char *why, + + lset_t proposed_v2AUTH(struct ike_sa *ike, struct msg_digest *md); + ++bool has_outstanding_ike_auth_request(const struct connection *c, ++ const struct ike_sa *ike, ++ const struct msg_digest *md); + #endif +diff --git a/programs/pluto/ikev2_child.c b/programs/pluto/ikev2_child.c +index 49aaff98a2..5b2c0d7028 100644 +--- a/programs/pluto/ikev2_child.c ++++ b/programs/pluto/ikev2_child.c +@@ -66,6 +66,7 @@ + #include "ikev2_parent.h" + #include "ikev2_states.h" + #include "ikev2_notification.h" ++#include "ikev2_auth.h" + #include "iface.h" + #include "nat_traversal.h" + +@@ -1029,6 +1030,18 @@ static v2_notification_t process_v2_IKE_AUTH_request_child_sa_payloads(struct ik + ldbg_sa(child, "skipping TS processing, mainly to stop tests failing but rumored to cause connection flips?!?"); + } + ++ /* It is possible that Child SA switched to permanent connection ++ * where initiator has IKE SA with IKE_AUTH request outstanding, ++ * in that case send AUTHENTICATION_FAILED and terminate this IKE SA. ++ * This is to prevent potential crossing streams scenario for ++ * IKE AUTH exchange. ++ */ ++ if (has_outstanding_ike_auth_request(child->sa.st_connection, ike, md)) { ++ record_v2N_response(ike->sa.logger, ike, md, v2N_AUTHENTICATION_FAILED, ++ empty_shunk, ENCRYPTED_PAYLOAD); ++ return v2N_AUTHENTICATION_FAILED; ++ } ++ + n = process_childs_v2SA_payload("IKE_AUTH responder matching remote ESP/AH proposals", + ike, child, md, + child->sa.st_connection->config->child_sa.v2_ike_auth_proposals, +diff --git a/programs/pluto/ikev2_ike_auth.c b/programs/pluto/ikev2_ike_auth.c +index ba5aeace88..08f7c79739 100644 +--- a/programs/pluto/ikev2_ike_auth.c ++++ b/programs/pluto/ikev2_ike_auth.c +@@ -674,6 +674,20 @@ stf_status process_v2_IKE_AUTH_request_standard_payloads(struct ike_sa *ike, str + return STF_FATAL; + } + ++ const struct connection *c = ike->sa.st_connection; ++ ++ /* If initiator has another IKE SA with IKE_AUTH request ++ * outstanding for the same permanent connection then send ++ * AUTHENTICATION_FAILED instead of IKE_AUTH response for this ++ * IKE_AUTH request and terminate current IKE SA. This is to ++ * prevent potential crossing streams scenario. ++ */ ++ if (has_outstanding_ike_auth_request(c, ike, md)) { ++ record_v2N_response(ike->sa.logger, ike, md, v2N_AUTHENTICATION_FAILED, ++ empty_shunk, ENCRYPTED_PAYLOAD); ++ return STF_FATAL; ++ } ++ + /* + * This both decodes the initiator's ID and, when necessary, + * switches connection based on that ID. +@@ -682,7 +696,6 @@ stf_status process_v2_IKE_AUTH_request_standard_payloads(struct ike_sa *ike, str + * switch based on the contents of the CERTREQ. + */ + +- const struct connection *c = ike->sa.st_connection; + bool found_ppk = false; + + /* +-- +2.52.0 + diff --git a/libreswan.spec b/libreswan.spec index c2444ca..3be8a47 100644 --- a/libreswan.spec +++ b/libreswan.spec @@ -45,6 +45,7 @@ Source5: https://download.libreswan.org/cavs/ikev2.fax.bz2 %endif Patch1: libreswan-4.15-ipsec_import.patch +Patch2: libreswan-5.3-outstanding-ike-auth-crossing.patch BuildRequires: audit-libs-devel BuildRequires: bison