pluto: reject crossing IKE_AUTH request only for the one side
Related: RHEL-89969 Signed-off-by: Daiki Ueno <dueno@redhat.com>
This commit is contained in:
parent
fa94735b84
commit
53f869e6fc
@ -1,7 +1,7 @@
|
||||
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
|
||||
Subject: [PATCH 1/5] 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
|
||||
@ -193,7 +193,7 @@ index a5a95045bf..5e47e009b8 100644
|
||||
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
|
||||
Subject: [PATCH 2/5] ikev2: reject simultaneous IKE_AUTH requests
|
||||
|
||||
If initiator has another IKE SA with IKE_AUTH request
|
||||
outstanding for the same permanent connection then send
|
||||
@ -352,3 +352,546 @@ index ba5aeace88..08f7c79739 100644
|
||||
--
|
||||
2.52.0
|
||||
|
||||
|
||||
From 437d0ff3a4b32b22b2272347881f49a333e9ffbf Mon Sep 17 00:00:00 2001
|
||||
From: Daiki Ueno <dueno@redhat.com>
|
||||
Date: Sat, 17 Jan 2026 13:28:39 +0900
|
||||
Subject: [PATCH 3/5] pluto: reject crossing IKE_AUTH request only for the one
|
||||
side
|
||||
|
||||
When an IKE_AUTH request is crossed, both sides previously rejected it
|
||||
by sending AUTHENTICATION_FAILED, resulting in no IKE_SA being
|
||||
established. This patch relaxes the condition and make the one outlive
|
||||
the other.
|
||||
|
||||
Suggested-by: Ondrej Moris <omoris@redhat.com>
|
||||
Signed-off-by: Daiki Ueno <dueno@redhat.com>
|
||||
---
|
||||
include/ike_spi.h | 1 +
|
||||
programs/pluto/ike_spi.c | 11 +++++++++++
|
||||
programs/pluto/ikev2_auth.c | 10 +++++-----
|
||||
programs/pluto/ikev2_auth.h | 2 +-
|
||||
programs/pluto/ikev2_child.c | 35 +++++++++++++++++++++++++++++----
|
||||
programs/pluto/ikev2_ike_auth.c | 35 +++++++++++++++++++++++++++++----
|
||||
6 files changed, 80 insertions(+), 14 deletions(-)
|
||||
|
||||
diff --git a/include/ike_spi.h b/include/ike_spi.h
|
||||
index 0c05542bae..376c120c0e 100644
|
||||
--- a/include/ike_spi.h
|
||||
+++ b/include/ike_spi.h
|
||||
@@ -39,6 +39,7 @@ typedef struct {
|
||||
} ike_spis_t;
|
||||
|
||||
bool ike_spis_eq(const ike_spis_t *lhs, const ike_spis_t *rhs);
|
||||
+bool ike_spis_gt(const ike_spis_t *lhs, const ike_spis_t *rhs);
|
||||
|
||||
/*
|
||||
* Need to handle two cases:
|
||||
diff --git a/programs/pluto/ike_spi.c b/programs/pluto/ike_spi.c
|
||||
index 312c835bed..d7bb73a6e8 100644
|
||||
--- a/programs/pluto/ike_spi.c
|
||||
+++ b/programs/pluto/ike_spi.c
|
||||
@@ -42,6 +42,17 @@ bool ike_spis_eq(const ike_spis_t *lhs, const ike_spis_t *rhs)
|
||||
ike_spi_eq(&lhs->responder, &rhs->responder));
|
||||
}
|
||||
|
||||
+bool ike_spis_gt(const ike_spis_t *lhs, const ike_spis_t *rhs)
|
||||
+{
|
||||
+ int d = memcmp(lhs->initiator.bytes, rhs->initiator.bytes,
|
||||
+ sizeof(lhs->initiator.bytes));
|
||||
+ if (d == 0) {
|
||||
+ d = memcmp(lhs->responder.bytes, rhs->responder.bytes,
|
||||
+ sizeof(lhs->responder.bytes));
|
||||
+ }
|
||||
+ return d > 0;
|
||||
+}
|
||||
+
|
||||
static struct {
|
||||
uint8_t bytes[SHA2_256_DIGEST_SIZE];
|
||||
} ike_spi_secret;
|
||||
diff --git a/programs/pluto/ikev2_auth.c b/programs/pluto/ikev2_auth.c
|
||||
index 2e1b6df655..ee36ef8b03 100644
|
||||
--- a/programs/pluto/ikev2_auth.c
|
||||
+++ b/programs/pluto/ikev2_auth.c
|
||||
@@ -1050,18 +1050,18 @@ lset_t proposed_v2AUTH(struct ike_sa *ike,
|
||||
* 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,
|
||||
+struct ike_sa *get_sa_with_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;
|
||||
+ return NULL;
|
||||
}
|
||||
|
||||
/* Connection must be permanent and request must be incoming */
|
||||
if (v2_msg_role(md) != MESSAGE_REQUEST || !is_permanent(c)) {
|
||||
- return false;
|
||||
+ return NULL;
|
||||
}
|
||||
|
||||
struct state_filter sf = {
|
||||
@@ -1092,8 +1092,8 @@ bool has_outstanding_ike_auth_request(const struct connection *c,
|
||||
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 simultaneous_ike;
|
||||
}
|
||||
}
|
||||
- return false;
|
||||
+ return NULL;
|
||||
}
|
||||
diff --git a/programs/pluto/ikev2_auth.h b/programs/pluto/ikev2_auth.h
|
||||
index d616534507..bb659ce88f 100644
|
||||
--- a/programs/pluto/ikev2_auth.h
|
||||
+++ b/programs/pluto/ikev2_auth.h
|
||||
@@ -85,7 +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,
|
||||
+struct ike_sa *get_sa_with_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 5b2c0d7028..63df00956f 100644
|
||||
--- a/programs/pluto/ikev2_child.c
|
||||
+++ b/programs/pluto/ikev2_child.c
|
||||
@@ -69,6 +69,7 @@
|
||||
#include "ikev2_auth.h"
|
||||
#include "iface.h"
|
||||
#include "nat_traversal.h"
|
||||
+#include "terminate.h"
|
||||
|
||||
static bool emit_v2_child_response_payloads(struct ike_sa *ike,
|
||||
const struct child_sa *child,
|
||||
@@ -1036,10 +1037,36 @@ static v2_notification_t process_v2_IKE_AUTH_request_child_sa_payloads(struct ik
|
||||
* 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;
|
||||
+ struct ike_sa *simultaneous_ike =
|
||||
+ get_sa_with_outstanding_ike_auth_request(child->sa.st_connection, ike, md);
|
||||
+ if (simultaneous_ike != NULL) {
|
||||
+ /* Compare our initiated SPI vs their initiated SPI
|
||||
+ * from the message. Note that the ordering doesn't
|
||||
+ * matter, but it ensures that both sides have the
|
||||
+ * same consensus on which IKE SA should be dropped.
|
||||
+ */
|
||||
+ if (ike_spis_gt(&simultaneous_ike->sa.st_ike_spis,
|
||||
+ &ike->sa.st_ike_spis)) {
|
||||
+ /* Our IKE SA with oustanding IKE AUTH request
|
||||
+ * has SPI higher, delete this IKE SA, keep
|
||||
+ * the simultaneous_ike.
|
||||
+ */
|
||||
+ ldbg(ike->sa.logger, "preferring the outstanding "PRI_SO" over the current "PRI_SO,
|
||||
+ pri_so(simultaneous_ike->sa.st_serialno),
|
||||
+ pri_so(ike->sa.st_serialno));
|
||||
+ record_v2N_response(ike->sa.logger, ike, md, v2N_AUTHENTICATION_FAILED,
|
||||
+ empty_shunk, ENCRYPTED_PAYLOAD);
|
||||
+ return v2N_AUTHENTICATION_FAILED;
|
||||
+ } else {
|
||||
+ /* IKE SA for the current IKE AUTH request has
|
||||
+ * SPI higher, continue with IKE AUTH reply
|
||||
+ * and drop the other one.
|
||||
+ */
|
||||
+ ldbg(ike->sa.logger, "preferring the current "PRI_SO" over the outstanding "PRI_SO"",
|
||||
+ pri_so(ike->sa.st_serialno),
|
||||
+ pri_so(simultaneous_ike->sa.st_serialno));
|
||||
+ terminate_ike_family(&simultaneous_ike, REASON_SUPERSEDED_BY_NEW_SA, HERE);
|
||||
+ }
|
||||
}
|
||||
|
||||
n = process_childs_v2SA_payload("IKE_AUTH responder matching remote ESP/AH proposals",
|
||||
diff --git a/programs/pluto/ikev2_ike_auth.c b/programs/pluto/ikev2_ike_auth.c
|
||||
index 08f7c79739..acd268e89a 100644
|
||||
--- a/programs/pluto/ikev2_ike_auth.c
|
||||
+++ b/programs/pluto/ikev2_ike_auth.c
|
||||
@@ -76,6 +76,7 @@
|
||||
#include "ikev2_notification.h"
|
||||
#include "peer_id.h"
|
||||
#include "ddos.h"
|
||||
+#include "terminate.h"
|
||||
|
||||
static ikev2_state_transition_fn process_v2_IKE_AUTH_request;
|
||||
|
||||
@@ -682,10 +683,36 @@ stf_status process_v2_IKE_AUTH_request_standard_payloads(struct ike_sa *ike, str
|
||||
* 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;
|
||||
+ struct ike_sa *simultaneous_ike =
|
||||
+ get_sa_with_outstanding_ike_auth_request(c, ike, md);
|
||||
+ if (simultaneous_ike != NULL) {
|
||||
+ /* Compare our initiated SPIs vs their initiated SPIs
|
||||
+ * from the message. Note that the ordering doesn't
|
||||
+ * matter, but it ensures that both sides have the
|
||||
+ * same consensus on which IKE SA should be dropped.
|
||||
+ */
|
||||
+ if (ike_spis_gt(&simultaneous_ike->sa.st_ike_spis,
|
||||
+ &ike->sa.st_ike_spis)) {
|
||||
+ /* Our IKE SA with oustanding IKE AUTH request
|
||||
+ * has SPI higher, delete this IKE SA, keep
|
||||
+ * the simultaneous_ike.
|
||||
+ */
|
||||
+ ldbg(ike->sa.logger, "preferring the outstanding "PRI_SO" over the current "PRI_SO,
|
||||
+ pri_so(simultaneous_ike->sa.st_serialno),
|
||||
+ pri_so(ike->sa.st_serialno));
|
||||
+ record_v2N_response(ike->sa.logger, ike, md, v2N_AUTHENTICATION_FAILED,
|
||||
+ empty_shunk, ENCRYPTED_PAYLOAD);
|
||||
+ return STF_FATAL;
|
||||
+ } else {
|
||||
+ /* IKE SA for the current IKE AUTH request has
|
||||
+ * SPI higher, continue with IKE AUTH reply
|
||||
+ * and drop the other one.
|
||||
+ */
|
||||
+ ldbg(ike->sa.logger, "preferring the current "PRI_SO" over the outstanding "PRI_SO"",
|
||||
+ pri_so(ike->sa.st_serialno),
|
||||
+ pri_so(simultaneous_ike->sa.st_serialno));
|
||||
+ terminate_ike_family(&simultaneous_ike, REASON_SUPERSEDED_BY_NEW_SA, HERE);
|
||||
+ }
|
||||
}
|
||||
|
||||
/*
|
||||
--
|
||||
2.52.0
|
||||
|
||||
|
||||
From e812e32c2d58d0a96ebfb38cd5a03ea4bf99161e Mon Sep 17 00:00:00 2001
|
||||
From: Ondrej Moris <omoris@redhat.com>
|
||||
Date: Sun, 25 Jan 2026 19:41:25 +0100
|
||||
Subject: [PATCH 4/5] ikev2: refactor simultaneous IKE_AUTH logic
|
||||
|
||||
1. Moved decision which IKE to reject into the function.
|
||||
2. Renamed function to better capture its new structure.
|
||||
|
||||
Signed-off-by: Ondrej Moris <omoris@redhat.com>
|
||||
---
|
||||
programs/pluto/ikev2_auth.c | 80 +++++++++++++++++++++++----------
|
||||
programs/pluto/ikev2_auth.h | 6 +--
|
||||
programs/pluto/ikev2_child.c | 44 +++++-------------
|
||||
programs/pluto/ikev2_ike_auth.c | 43 +++++-------------
|
||||
4 files changed, 80 insertions(+), 93 deletions(-)
|
||||
|
||||
diff --git a/programs/pluto/ikev2_auth.c b/programs/pluto/ikev2_auth.c
|
||||
index ee36ef8b03..f0c08fb27f 100644
|
||||
--- a/programs/pluto/ikev2_auth.c
|
||||
+++ b/programs/pluto/ikev2_auth.c
|
||||
@@ -1046,13 +1046,23 @@ 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.
|
||||
+ * Check relevant simultaneous IKE_AUTH requests and decide
|
||||
+ * which IKE SA is going to be rejected (if any). This is goint
|
||||
+ * to be called when processing IKE_AUTH request from the responder
|
||||
+ *
|
||||
+ * Relevant:
|
||||
+ * - same connection
|
||||
+ * - initiating IKE
|
||||
+ * - non-established with IKE_AUTH request waiting for reply
|
||||
+ *
|
||||
+ * Returns:
|
||||
+ * - ike: reject current IKE_AUTH request
|
||||
+ * - other IKE SA than ike: terminate it, keep ike
|
||||
+ * - NULL: no simultaneous IKE SA
|
||||
*/
|
||||
-struct ike_sa *get_sa_with_outstanding_ike_auth_request(const struct connection *c,
|
||||
- const struct ike_sa *ike,
|
||||
- const struct msg_digest *md)
|
||||
+struct ike_sa *check_simultaneous_ike_auth(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) {
|
||||
@@ -1065,35 +1075,57 @@ struct ike_sa *get_sa_with_outstanding_ike_auth_request(const struct connection
|
||||
}
|
||||
|
||||
struct state_filter sf = {
|
||||
- .connection_serialno = c->serialno,
|
||||
- .search = {
|
||||
- .order = NEW2OLD,
|
||||
- .verbose.logger = ike->sa.logger,
|
||||
- .where = HERE,
|
||||
- },
|
||||
+ .connection_serialno = c->serialno,
|
||||
+ .search = {
|
||||
+ .order = NEW2OLD,
|
||||
+ .verbose.logger = ike->sa.logger,
|
||||
+ .where = HERE,
|
||||
+ },
|
||||
};
|
||||
|
||||
+ struct ike_sa *simultaneous_ike = NULL;
|
||||
+
|
||||
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) {
|
||||
+ struct ike_sa *candidate = pexpect_ike_sa(sf.st);
|
||||
+
|
||||
+ if (candidate == NULL || candidate == ike) {
|
||||
continue;
|
||||
- } else if (!v2_msgid_request_outstanding(simultaneous_ike)) {
|
||||
+ } else if (candidate->sa.st_sa_role != SA_INITIATOR) {
|
||||
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 simultaneous_ike;
|
||||
+ /* Does candidate has IKE_AUTH request outstanding? */
|
||||
+ if (v2_msgid_request_outstanding(candidate)) {
|
||||
+ const struct v2_exchange *outstanding_request = candidate->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(candidate->sa.st_serialno));
|
||||
+ simultaneous_ike = candidate;
|
||||
+ break;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
- return NULL;
|
||||
+
|
||||
+ /* No simultaneous IKE found */
|
||||
+ if (simultaneous_ike == NULL) {
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ /* Simultaneous IKE found, we need to decide if we keep that one or current IKE.
|
||||
+ *
|
||||
+ * We keep one with higher SPI.
|
||||
+ */
|
||||
+ if (ike_spis_gt(&simultaneous_ike->sa.st_ike_spis, &ike->sa.st_ike_spis)) {
|
||||
+ ldbg(ike->sa.logger, "preferring the simultaneous IKE SA "PRI_SO" over the current IKE SA "PRI_SO,
|
||||
+ pri_so(simultaneous_ike->sa.st_serialno), pri_so(ike->sa.st_serialno));
|
||||
+ return (struct ike_sa *) ike;
|
||||
+ } else {
|
||||
+ ldbg(ike->sa.logger, "preferring the current IKE SA "PRI_SO" over the simultanoues IKE SA "PRI_SO"",
|
||||
+ pri_so(ike->sa.st_serialno), pri_so(simultaneous_ike->sa.st_serialno));
|
||||
+ return simultaneous_ike;
|
||||
+ }
|
||||
}
|
||||
diff --git a/programs/pluto/ikev2_auth.h b/programs/pluto/ikev2_auth.h
|
||||
index bb659ce88f..1c25479ed7 100644
|
||||
--- a/programs/pluto/ikev2_auth.h
|
||||
+++ b/programs/pluto/ikev2_auth.h
|
||||
@@ -85,7 +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);
|
||||
|
||||
-struct ike_sa *get_sa_with_outstanding_ike_auth_request(const struct connection *c,
|
||||
- const struct ike_sa *ike,
|
||||
- const struct msg_digest *md);
|
||||
+struct ike_sa *check_simultaneous_ike_auth(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 63df00956f..1205622b27 100644
|
||||
--- a/programs/pluto/ikev2_child.c
|
||||
+++ b/programs/pluto/ikev2_child.c
|
||||
@@ -1032,41 +1032,19 @@ static v2_notification_t process_v2_IKE_AUTH_request_child_sa_payloads(struct ik
|
||||
}
|
||||
|
||||
/* 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
|
||||
+ * where initiator has IKE SA with IKE_AUTH request outstanding
|
||||
+ * (or recently established IKE SA) in that case keep only one of
|
||||
+ * them. This is to prevent potential crossing streams scenario for
|
||||
* IKE AUTH exchange.
|
||||
*/
|
||||
- struct ike_sa *simultaneous_ike =
|
||||
- get_sa_with_outstanding_ike_auth_request(child->sa.st_connection, ike, md);
|
||||
- if (simultaneous_ike != NULL) {
|
||||
- /* Compare our initiated SPI vs their initiated SPI
|
||||
- * from the message. Note that the ordering doesn't
|
||||
- * matter, but it ensures that both sides have the
|
||||
- * same consensus on which IKE SA should be dropped.
|
||||
- */
|
||||
- if (ike_spis_gt(&simultaneous_ike->sa.st_ike_spis,
|
||||
- &ike->sa.st_ike_spis)) {
|
||||
- /* Our IKE SA with oustanding IKE AUTH request
|
||||
- * has SPI higher, delete this IKE SA, keep
|
||||
- * the simultaneous_ike.
|
||||
- */
|
||||
- ldbg(ike->sa.logger, "preferring the outstanding "PRI_SO" over the current "PRI_SO,
|
||||
- pri_so(simultaneous_ike->sa.st_serialno),
|
||||
- pri_so(ike->sa.st_serialno));
|
||||
- record_v2N_response(ike->sa.logger, ike, md, v2N_AUTHENTICATION_FAILED,
|
||||
- empty_shunk, ENCRYPTED_PAYLOAD);
|
||||
- return v2N_AUTHENTICATION_FAILED;
|
||||
- } else {
|
||||
- /* IKE SA for the current IKE AUTH request has
|
||||
- * SPI higher, continue with IKE AUTH reply
|
||||
- * and drop the other one.
|
||||
- */
|
||||
- ldbg(ike->sa.logger, "preferring the current "PRI_SO" over the outstanding "PRI_SO"",
|
||||
- pri_so(ike->sa.st_serialno),
|
||||
- pri_so(simultaneous_ike->sa.st_serialno));
|
||||
- terminate_ike_family(&simultaneous_ike, REASON_SUPERSEDED_BY_NEW_SA, HERE);
|
||||
- }
|
||||
+ struct ike_sa *ike_to_reject = check_simultaneous_ike_auth(child->sa.st_connection, ike, md);
|
||||
+ if (ike_to_reject == ike) {
|
||||
+ /* Reject current IKE_AUTH request */
|
||||
+ record_v2N_response(ike->sa.logger, ike, md, v2N_AUTHENTICATION_FAILED, empty_shunk, ENCRYPTED_PAYLOAD);
|
||||
+ return v2N_AUTHENTICATION_FAILED;
|
||||
+ } else if (ike_to_reject != NULL) {
|
||||
+ /* Terminate the other IKE SA, continue with current */
|
||||
+ terminate_ike_family(&ike_to_reject, REASON_SUPERSEDED_BY_NEW_SA, HERE);
|
||||
}
|
||||
|
||||
n = process_childs_v2SA_payload("IKE_AUTH responder matching remote ESP/AH proposals",
|
||||
diff --git a/programs/pluto/ikev2_ike_auth.c b/programs/pluto/ikev2_ike_auth.c
|
||||
index acd268e89a..fc35ecc200 100644
|
||||
--- a/programs/pluto/ikev2_ike_auth.c
|
||||
+++ b/programs/pluto/ikev2_ike_auth.c
|
||||
@@ -678,41 +678,18 @@ stf_status process_v2_IKE_AUTH_request_standard_payloads(struct ike_sa *ike, str
|
||||
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
|
||||
+ * outstanding for the same permanent connection (or recently
|
||||
+ * established one) then we keep only one of them. This is to
|
||||
* prevent potential crossing streams scenario.
|
||||
*/
|
||||
- struct ike_sa *simultaneous_ike =
|
||||
- get_sa_with_outstanding_ike_auth_request(c, ike, md);
|
||||
- if (simultaneous_ike != NULL) {
|
||||
- /* Compare our initiated SPIs vs their initiated SPIs
|
||||
- * from the message. Note that the ordering doesn't
|
||||
- * matter, but it ensures that both sides have the
|
||||
- * same consensus on which IKE SA should be dropped.
|
||||
- */
|
||||
- if (ike_spis_gt(&simultaneous_ike->sa.st_ike_spis,
|
||||
- &ike->sa.st_ike_spis)) {
|
||||
- /* Our IKE SA with oustanding IKE AUTH request
|
||||
- * has SPI higher, delete this IKE SA, keep
|
||||
- * the simultaneous_ike.
|
||||
- */
|
||||
- ldbg(ike->sa.logger, "preferring the outstanding "PRI_SO" over the current "PRI_SO,
|
||||
- pri_so(simultaneous_ike->sa.st_serialno),
|
||||
- pri_so(ike->sa.st_serialno));
|
||||
- record_v2N_response(ike->sa.logger, ike, md, v2N_AUTHENTICATION_FAILED,
|
||||
- empty_shunk, ENCRYPTED_PAYLOAD);
|
||||
- return STF_FATAL;
|
||||
- } else {
|
||||
- /* IKE SA for the current IKE AUTH request has
|
||||
- * SPI higher, continue with IKE AUTH reply
|
||||
- * and drop the other one.
|
||||
- */
|
||||
- ldbg(ike->sa.logger, "preferring the current "PRI_SO" over the outstanding "PRI_SO"",
|
||||
- pri_so(ike->sa.st_serialno),
|
||||
- pri_so(simultaneous_ike->sa.st_serialno));
|
||||
- terminate_ike_family(&simultaneous_ike, REASON_SUPERSEDED_BY_NEW_SA, HERE);
|
||||
- }
|
||||
+ struct ike_sa *ike_to_reject = check_simultaneous_ike_auth(c, ike, md);
|
||||
+ if (ike_to_reject == ike) {
|
||||
+ /* Reject current IKE_AUTH request */
|
||||
+ record_v2N_response(ike->sa.logger, ike, md, v2N_AUTHENTICATION_FAILED, empty_shunk, ENCRYPTED_PAYLOAD);
|
||||
+ return STF_FATAL;
|
||||
+ } else if (ike_to_reject != NULL) {
|
||||
+ /* Terminate the other IKE SA, continue with current */
|
||||
+ terminate_ike_family(&ike_to_reject, REASON_SUPERSEDED_BY_NEW_SA, HERE);
|
||||
}
|
||||
|
||||
/*
|
||||
--
|
||||
2.52.0
|
||||
|
||||
|
||||
From 47fc2d3bb650d4ecfd066ab04ebc454bcefb91d1 Mon Sep 17 00:00:00 2001
|
||||
From: Ondrej Moris <omoris@redhat.com>
|
||||
Date: Sun, 25 Jan 2026 19:46:45 +0100
|
||||
Subject: [PATCH 5/5] ikev2: extend check for simultaneous IKE_AUTH
|
||||
|
||||
On initiator, if simultaneous IKE receives IKE_AUTH response
|
||||
shortly before the current IKE_AUTH request is received,
|
||||
then this simultaneous IKE gets established and hence it
|
||||
incorrectly missed by the current check. This commit extends
|
||||
the check for simultaneous IKE to capture such cases and if
|
||||
there is such simultaneous IKE it will be keps and the current
|
||||
one will be dropped.
|
||||
|
||||
Signed-off-by: Ondrej Moris <omoris@redhat.com>
|
||||
---
|
||||
programs/pluto/ikev2_auth.c | 21 +++++++++++++++++++--
|
||||
1 file changed, 19 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/programs/pluto/ikev2_auth.c b/programs/pluto/ikev2_auth.c
|
||||
index f0c08fb27f..c1fed3bcdd 100644
|
||||
--- a/programs/pluto/ikev2_auth.c
|
||||
+++ b/programs/pluto/ikev2_auth.c
|
||||
@@ -1054,6 +1054,7 @@ lset_t proposed_v2AUTH(struct ike_sa *ike,
|
||||
* - same connection
|
||||
* - initiating IKE
|
||||
* - non-established with IKE_AUTH request waiting for reply
|
||||
+ * - established within 1 second of processing this IKE_AUTH request
|
||||
*
|
||||
* Returns:
|
||||
* - ike: reject current IKE_AUTH request
|
||||
@@ -1108,6 +1109,17 @@ struct ike_sa *check_simultaneous_ike_auth(const struct connection *c,
|
||||
break;
|
||||
}
|
||||
}
|
||||
+
|
||||
+ /* Was candidate established within the last second of the current IKE_AUTH request arrival? */
|
||||
+ if (candidate->sa.st_state->kind == STATE_V2_ESTABLISHED_IKE_SA) {
|
||||
+ deltatime_t age = monotime_diff(mononow(), candidate->sa.st_v2_msgid_windows.initiator.last_recv);
|
||||
+ if (deltatime_cmp(age, <, deltatime(1))) {
|
||||
+ llog(RC_LOG, ike->sa.logger, "IKE SA "PRI_SO" recently established",
|
||||
+ pri_so(candidate->sa.st_serialno));
|
||||
+ simultaneous_ike = candidate;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
/* No simultaneous IKE found */
|
||||
@@ -1117,9 +1129,14 @@ struct ike_sa *check_simultaneous_ike_auth(const struct connection *c,
|
||||
|
||||
/* Simultaneous IKE found, we need to decide if we keep that one or current IKE.
|
||||
*
|
||||
- * We keep one with higher SPI.
|
||||
+ * If simultaneous IKE is already established, we keep it. Otherwise we keep one
|
||||
+ * with higher SPI.
|
||||
*/
|
||||
- if (ike_spis_gt(&simultaneous_ike->sa.st_ike_spis, &ike->sa.st_ike_spis)) {
|
||||
+ if (simultaneous_ike->sa.st_state->kind == STATE_V2_ESTABLISHED_IKE_SA) {
|
||||
+ llog(RC_LOG, ike->sa.logger, "rejecting IKE_AUTH request; IKE SA "PRI_SO" already established",
|
||||
+ pri_so(simultaneous_ike->sa.st_serialno));
|
||||
+ return (struct ike_sa *) ike;
|
||||
+ } else if (ike_spis_gt(&simultaneous_ike->sa.st_ike_spis, &ike->sa.st_ike_spis)) {
|
||||
ldbg(ike->sa.logger, "preferring the simultaneous IKE SA "PRI_SO" over the current IKE SA "PRI_SO,
|
||||
pri_so(simultaneous_ike->sa.st_serialno), pri_so(ike->sa.st_serialno));
|
||||
return (struct ike_sa *) ike;
|
||||
--
|
||||
2.52.0
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user