Commit cb32cd5064 for strongswan.org

commit cb32cd5064df961f95c2bb42670b24250cddedd5
Author: Tobias Brunner <tobias@strongswan.org>
Date:   Thu Nov 5 14:55:35 2026 +0100

    pem: Move certificate selection to enumerator and add getter

    This allows to easily get specific types of certificates from a bundle.

diff --git a/src/charon-nm/nm/nm_service.c b/src/charon-nm/nm/nm_service.c
index 81b5d7983b..ad7b09c345 100644
--- a/src/charon-nm/nm/nm_service.c
+++ b/src/charon-nm/nm/nm_service.c
@@ -891,7 +891,7 @@ static bool load_trusted_certificates(NMStrongswanPluginPrivate *priv,
 								BUILD_BLOB, file, BUILD_END);
 	if (bundle)
 	{
-		enumerator = bundle->create_cert_enumerator(bundle);
+		enumerator = bundle->create_cert_enumerator(bundle, PEM_SELECTION_ALL);
 		while (enumerator->enumerate(enumerator, &cert))
 		{
 			if (cert->get_type(cert) == CERT_X509)
diff --git a/src/libstrongswan/credentials/containers/pem.h b/src/libstrongswan/credentials/containers/pem.h
index cf822ee260..e120223d7d 100644
--- a/src/libstrongswan/credentials/containers/pem.h
+++ b/src/libstrongswan/credentials/containers/pem.h
@@ -23,8 +23,28 @@
 #define PEM_H_

 #include <credentials/containers/container.h>
+#include <credentials/certificates/certificate.h>

 typedef struct pem_t pem_t;
+typedef enum pem_selection_t pem_selection_t;
+
+/**
+ * Selection mode for enumerating certificates in PEM certificate bundles
+ */
+enum pem_selection_t {
+	/** Select all X.509 certificates contained in the bundle */
+	PEM_SELECTION_ALL,
+	/** Select all certificates except self-signed CA certificates */
+	PEM_SELECTION_NO_ROOT,
+	/** Select only self-signed CA certificates */
+	PEM_SELECTION_ROOT,
+	/** Select all CA certificates */
+	PEM_SELECTION_CA,
+	/** Select all non-self-signed (intermediate) CA certificates */
+	PEM_SELECTION_IM,
+	/** Select all end entity certificates */
+	PEM_SELECTION_EE,
+};

 /**
  * PEM certificate bundle container type.
@@ -37,11 +57,21 @@ struct pem_t {
 	container_t container;

 	/**
-	 * Create an enumerator over contained certificates.
+	 * Create an enumerator over selected certificates.
 	 *
+	 * @param selector	certificate selection mode
 	 * @return			enumerator over certificate_t
 	 */
-	enumerator_t *(*create_cert_enumerator)(pem_t *this);
+	enumerator_t *(*create_cert_enumerator)(pem_t *this,
+											pem_selection_t selector);
+
+	/**
+	 * Return the first certificate that matches the given selector.
+	 *
+	 * @param selector	certificate selection mode
+	 * @return			certificate reference or NULL if no match was found
+	 */
+	certificate_t *(*get_first)(pem_t *this, pem_selection_t selector);
 };

 #endif /** PEM_H_ @}*/
diff --git a/src/libstrongswan/plugins/pem/pem_bundle.c b/src/libstrongswan/plugins/pem/pem_bundle.c
index 92ecfbc02a..33e1d137e7 100644
--- a/src/libstrongswan/plugins/pem/pem_bundle.c
+++ b/src/libstrongswan/plugins/pem/pem_bundle.c
@@ -17,6 +17,7 @@
 #include "pem_bundle.h"

 #include <collections/array.h>
+#include <credentials/certificates/x509.h>

 typedef struct private_pem_bundle_t private_pem_bundle_t;

@@ -61,10 +62,82 @@ METHOD(container_t, get_encoding, bool,
 	return FALSE;
 }

+/**
+ * Check if a certificate is selected
+ */
+static bool cert_selected(pem_selection_t selector, certificate_t *cert)
+{
+	x509_t *x509 = (x509_t*)cert;
+	bool is_self, is_ca;
+
+	if (cert->get_type(cert) != CERT_X509)
+	{
+		return FALSE;
+	}
+
+	is_self = x509->get_flags(x509) & X509_SELF_SIGNED;
+	is_ca = x509->get_flags(x509) & X509_CA;
+
+	switch (selector)
+	{
+		case PEM_SELECTION_NO_ROOT:
+			return !(is_ca && is_self);
+		case PEM_SELECTION_ROOT:
+			return is_ca && is_self;
+		case PEM_SELECTION_CA:
+			return is_ca;
+		case PEM_SELECTION_IM:
+			return is_ca && !is_self;
+		case PEM_SELECTION_EE:
+			return !is_ca;
+		default:
+			return TRUE;
+	}
+}
+
+CALLBACK(filter_certs, bool,
+	pem_selection_t *selector, enumerator_t *orig, va_list args)
+{
+	certificate_t *cert, **out;
+
+	VA_ARGS_VGET(args, out);
+
+	while (orig->enumerate(orig, &cert))
+	{
+		if (cert_selected(*selector, cert))
+		{
+			*out = cert;
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
 METHOD(pem_t, create_cert_enumerator, enumerator_t*,
-	private_pem_bundle_t *this)
+	private_pem_bundle_t *this, pem_selection_t selector)
+{
+	pem_selection_t *sel = malloc_thing(pem_selection_t);
+
+	*sel = selector;
+	return enumerator_create_filter(array_create_enumerator(this->certs),
+									filter_certs, sel, free);
+}
+
+METHOD(pem_t, get_first, certificate_t*,
+	private_pem_bundle_t *this, pem_selection_t selector)
 {
-	return array_create_enumerator(this->certs);
+	certificate_t *cert;
+	int i;
+
+	for (i = 0; i < array_count(this->certs); i++)
+	{
+		array_get(this->certs, i, &cert);
+		if (cert_selected(selector, cert))
+		{
+			return cert->get_ref(cert);
+		}
+	}
+	return NULL;
 }

 METHOD(pem_bundle_t, add_cert, void,
@@ -98,6 +171,7 @@ pem_bundle_t *pem_bundle_create()
 					.destroy = _destroy,
 				},
 				.create_cert_enumerator = _create_cert_enumerator,
+				.get_first = _get_first,
 			},
 			.add_cert = _add_cert,
 		},
diff --git a/src/pki/commands/bundle.c b/src/pki/commands/bundle.c
index c159880e37..6d26a2549f 100644
--- a/src/pki/commands/bundle.c
+++ b/src/pki/commands/bundle.c
@@ -34,7 +34,7 @@ static int show(pem_t *bundle)
 	certificate_t *cert;
 	int index = 1;

-	enumerator = bundle->create_cert_enumerator(bundle);
+	enumerator = bundle->create_cert_enumerator(bundle, PEM_SELECTION_ALL);
 	while (enumerator->enumerate(enumerator, &cert))
 	{
 		x509_t *x509 = (x509_t*)cert;
@@ -89,19 +89,7 @@ static bool build_pathname(int index, int total, char *template,
 	return written > 0;
 }

-/**
- * Export selection modes if not using a specific index
- */
-typedef enum {
-	EXPORT_ALL,
-	EXPORT_NO_ROOT,
-	EXPORT_ROOT,
-	EXPORT_CA,
-	EXPORT_IM,
-	EXPORT_EE,
-} export_mode_t;
-
-ENUM(export_mode_names, EXPORT_ALL, EXPORT_EE,
+ENUM(selection_mode_names, PEM_SELECTION_ALL, PEM_SELECTION_EE,
 	"all",
 	"no-root",
 	"root",
@@ -110,31 +98,6 @@ ENUM(export_mode_names, EXPORT_ALL, EXPORT_EE,
 	"ee",
 );

-/**
- * Check if a certificate is selected for export
- */
-static bool cert_selected(export_mode_t mode, x509_t *x509)
-{
-	bool is_self = x509->get_flags(x509) & X509_SELF_SIGNED;
-	bool is_ca = x509->get_flags(x509) & X509_CA;
-
-	switch (mode)
-	{
-		case EXPORT_NO_ROOT:
-			return !(is_ca && is_self);
-		case EXPORT_ROOT:
-			return is_ca && is_self;
-		case EXPORT_CA:
-			return is_ca;
-		case EXPORT_IM:
-			return is_ca && !is_self;
-		case EXPORT_EE:
-			return !is_ca;
-		default:
-			return TRUE;
-	}
-}
-
 /**
  * Export a certificate from a PEM certificate bundle
  */
@@ -144,13 +107,13 @@ static int export(pem_t *bundle, char *selector, char *template,
 	enumerator_t *enumerator;
 	certificate_t *cert;
 	array_t *selected = NULL;
-	export_mode_t mode = EXPORT_ALL;
+	pem_selection_t mode = PEM_SELECTION_ALL;
 	chunk_t encoding;
 	char *end, *path;
 	uint32_t index = 0;
 	int i = 1, count = 0, ret = 1;

-	if (!enum_from_name(export_mode_names, selector, &mode) &&
+	if (!enum_from_name(selection_mode_names, selector, &mode) &&
 		(!uint32_from_string(selector, &end, 10, &index) || *end != '\0' ||
 		 !index))
 	{
@@ -158,17 +121,10 @@ static int export(pem_t *bundle, char *selector, char *template,
 		return 1;
 	}

-	enumerator = bundle->create_cert_enumerator(bundle);
+	enumerator = bundle->create_cert_enumerator(bundle, mode);
 	while (enumerator->enumerate(enumerator, &cert))
 	{
-		if (!index)
-		{
-			if (!cert_selected(mode, (x509_t*)cert))
-			{
-				continue;
-			}
-		}
-		else if (i++ != index)
+		if (index && i++ != index)
 		{
 			continue;
 		}
@@ -187,7 +143,7 @@ static int export(pem_t *bundle, char *selector, char *template,
 		else
 		{
 			fprintf(stderr, "no certificates match '%N' selector\n",
-					export_mode_names, mode);
+					selection_mode_names, mode);
 		}
 		return 1;
 	}
@@ -328,7 +284,7 @@ static bool add_certs(array_t **certs, char *path)
 	}
 	if (pem)
 	{
-		enumerator = pem->create_cert_enumerator(pem);
+		enumerator = pem->create_cert_enumerator(pem, PEM_SELECTION_ALL);
 		while (enumerator->enumerate(enumerator, &cert))
 		{
 			array_insert_create(certs, ARRAY_TAIL, cert->get_ref(cert));
diff --git a/src/swanctl/commands/load_creds.c b/src/swanctl/commands/load_creds.c
index 1c0d5288be..1feeaa44e9 100644
--- a/src/swanctl/commands/load_creds.c
+++ b/src/swanctl/commands/load_creds.c
@@ -114,7 +114,7 @@ static void load_bundle(load_ctx_t *ctx, char *dir, certificate_type_t type,
 	x509_t *x509;
 	chunk_t encoding;

-	enumerator = bundle->create_cert_enumerator(bundle);
+	enumerator = bundle->create_cert_enumerator(bundle, PEM_SELECTION_ALL);
 	while (enumerator->enumerate(enumerator, &cert))
 	{
 		x509_flag_t cert_flag = dir_flag;