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:
Daiki Ueno 2026-01-20 15:00:31 +09:00
parent fa94735b84
commit 53f869e6fc

View File

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