ikev2: reject simultaneous IKE_AUTH requests

Resolves: RHEL-89969
Signed-off-by: Ondrej Moris <omoris@redhat.com>
Signed-off-by: Daiki Ueno <dueno@redhat.com>
This commit is contained in:
Ondrej Moris 2026-01-09 14:46:51 +09:00 committed by Daiki Ueno
parent 0d7aa35ef3
commit 1a895c84df
2 changed files with 355 additions and 0 deletions

View File

@ -0,0 +1,354 @@
From a671b9ce5ff86ffc31142436e9c93031083d7221 Mon Sep 17 00:00:00 2001
From: Ondrej Moris <omoris@redhat.com>
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 <omoris@redhat.com>
Signed-off-by: Andrew Cagney <cagney@gnu.org>
---
.../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 @@
+<varlistentry>
+ <term>
+ <option>reject-simultaneous-ike-auth</option>
+ </term>
+ <listitem>
+
+ <para>
+ 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.
+ </para>
+
+ <para>
+ The accepted values are <option>yes</option> (the default) or
+ <option>no</option>.
+ </para>
+
+ </listitem>
+</varlistentry>
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 @@
<!ENTITY accept-redirect-to SYSTEM "d.ipsec.conf/accept-redirect-to.xml">
<!ENTITY aggressive SYSTEM "d.ipsec.conf/aggressive.xml">
<!ENTITY ah SYSTEM "d.ipsec.conf/ah.xml">
+<!ENTITY reject-simultaneous-ike-auth SYSTEM "d.ipsec.conf/reject-simultaneous-ike-auth.xml">
<!ENTITY audit-log SYSTEM "d.ipsec.conf/audit-log.xml">
<!ENTITY authby SYSTEM "d.ipsec.conf/authby.xml">
<!ENTITY author SYSTEM "d.ipsec.conf/author.xml">
@@ -377,6 +378,7 @@
&failureshunt;
&negotiationshunt;
&debug;
+ &reject-simultaneous-ike-auth;
</variablelist>
</refsect2>
</refsect1>
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 <mtu>] \\\n"
" [--priority <prio>] [--reqid <reqid>] \\\n"
" [--tfc <size>] [--send-esp-tfc-padding-not-supported] \\\n"
+ " [--reject-simultaneous-ike-auth] \\\n"
" [--iptfs[={yes,no}] \\\n"
" [--iptfs-fragmentation[={yes,no}]] \\\n"
" [--iptfs-packet-size <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 <omoris@redhat.com>
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 <omoris@redhat.com>
---
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

View File

@ -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