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;