Commit f60a55e36f for strongswan.org

commit f60a55e36f95e210ab067de295c9f6bacefcdd1a
Author: Tobias Brunner <tobias@strongswan.org>
Date:   Tue Jun 23 11:55:31 2026 +0200

    pkcs5: Validate parsed parameters to avoid DoS attacks

    With the unbounded iterations, an attacker can craft a PKCS#7 file and
    send it during IKEv1 to block the processing thread practically for an
    unlimited amount of time.

    As the key length is used for an allocation on the stack, not limiting
    it could cause a crash.  We validate it after parsing the params, but
    since `encryption_algorithm_from_oid()` only returns trusted key lengths
    that are lower than the limit, that's fine.

    The unlimited salt length had no direct impact (the maximum is bound by
    the accepted message size), but we now limit it as well before cloning.

    Fixes: 4076e3ee9121 ("Extract PKCS#5 handling from pkcs8 plugin to separate helper class")
    Fixes: fd1ff46f6143 ("Added support for PKCS#5 v2 schemes when decrypting PKCS#8 files.")
    Fixes: cab127cba66c ("Added support for encrypted PKCS#8 files (for some PKCS#5 v1.5 schemes).")
    Fixes: CVE-2026-78129

diff --git a/src/libstrongswan/crypto/pkcs5.c b/src/libstrongswan/crypto/pkcs5.c
index 822656f84b..e01010b90a 100644
--- a/src/libstrongswan/crypto/pkcs5.c
+++ b/src/libstrongswan/crypto/pkcs5.c
@@ -14,6 +14,8 @@
  * for more details.
  */

+#include <inttypes.h>
+
 #include "pkcs5.h"

 #include <utils/debug.h>
@@ -22,6 +24,15 @@
 #include <asn1/asn1_parser.h>
 #include <credentials/containers/pkcs12.h>

+/** maximum accepted length for salts in parsed parameters */
+#define PKCS5_SALT_LEN_MAX			128
+
+/** maximum accepted iteration count in parsed parameters */
+#define PKCS5_ITERATIONS_MAX		1000000
+
+/** maximum key length accepted in parsed parameters */
+#define PKCS5_KEY_LEN_MAX			64
+
 typedef struct private_pkcs5_t private_pkcs5_t;

 /**
@@ -379,6 +390,41 @@ METHOD(pkcs5_t, decrypt, bool,
 						   keymat, key, iv);
 }

+/**
+ * Make sure the salt has an appropriate length
+ */
+static bool validate_salt_length(chunk_t salt)
+{
+	if (salt.len > PKCS5_SALT_LEN_MAX)
+	{
+		DBG1(DBG_ASN, "  salt length %zu exceeds maximum of %zu bytes",
+			 salt.len, (size_t)PKCS5_SALT_LEN_MAX);
+		return FALSE;
+	}
+	return TRUE;
+}
+
+/**
+ * Validate that parsed parameters are in an allowed range
+ */
+static bool validate_params(private_pkcs5_t *this)
+{
+	if (!this->iterations || this->iterations > PKCS5_ITERATIONS_MAX)
+	{
+		DBG1(DBG_ASN, "  iteration count %" PRIu64 " is out of range "
+			 "(1-%" PRIu64 ")", this->iterations,
+			 (uint64_t)PKCS5_ITERATIONS_MAX);
+		return FALSE;
+	}
+	if (this->keylen > PKCS5_KEY_LEN_MAX)
+	{
+		DBG1(DBG_ASN, "  key length %zu exceeds maximum of %zu bytes",
+			 this->keylen, (size_t)PKCS5_KEY_LEN_MAX);
+		return FALSE;
+	}
+	return TRUE;
+}
+
 /**
  * ASN.1 definition of a PBEParameter structure
  */
@@ -399,7 +445,7 @@ static bool parse_pbes1_params(private_pkcs5_t *this, chunk_t blob, int level0)
 	asn1_parser_t *parser;
 	chunk_t object;
 	int objectID;
-	bool success;
+	bool success = FALSE;

 	parser = asn1_parser_create(pbeParameterObjects, blob);
 	parser->set_top_level(parser, level0);
@@ -410,6 +456,10 @@ static bool parse_pbes1_params(private_pkcs5_t *this, chunk_t blob, int level0)
 		{
 			case PBEPARAM_SALT:
 			{
+				if (!validate_salt_length(object))
+				{
+					goto end;
+				}
 				this->salt = chunk_clone(object);
 				break;
 			}
@@ -421,6 +471,11 @@ static bool parse_pbes1_params(private_pkcs5_t *this, chunk_t blob, int level0)
 		}
 	}
 	success = parser->success(parser);
+	if (success)
+	{
+		success = validate_params(this);
+	}
+end:
 	parser->destroy(parser);
 	return success;
 }
@@ -471,6 +526,10 @@ static bool parse_pbkdf2_params(private_pkcs5_t *this, chunk_t blob, int level0)
 		{
 			case PBKDF2_SALT:
 			{
+				if (!validate_salt_length(object))
+				{
+					goto end;
+				}
 				this->salt = chunk_clone(object);
 				break;
 			}
@@ -500,6 +559,10 @@ static bool parse_pbkdf2_params(private_pkcs5_t *this, chunk_t blob, int level0)
 		}
 	}
 	success = parser->success(parser);
+	if (success)
+	{
+		success = validate_params(this);
+	}
 end:
 	parser->destroy(parser);
 	return success;