Commit 6f67e7cbdd for strongswan.org
commit 6f67e7cbdd8ceac6673bf600eaeab8c04991ae57
Author: Tobias Brunner <tobias@strongswan.org>
Date: Mon Sep 14 12:20:29 2026 +0200
swanctl: Add support to load certificates from a PEM-encoded bundle
We try to parse a certificate file as bundle first, which covers single
PEM-encoded files as well. Only attempt to do this for directories
that contain CAs and regular certificates. The other dirs are generally
for single item files (in particular the ones that enforce a flag like
`x509ocsp` or `x509aa`). To allow loading full chain bundles that
include the intermediate CA certificates as well as the end entity
certificate from both the `x509` and the `x509ca` directory, the flag is
filtered when a parsed certificate does not contain a CA basic
constraint (it would get rejected by the vici plugin otherwise).
Note that the latter changes how PEM files with a single end entity
certificate in the `x509ca` directory are treated. Previously, the
passed flag caused the vici plugin to reject the certificate. Now it
gets accepted because the flag is changed. While technically in the
wrong location, it shouldn't pose an issue to load such a certificate.
Another change is the log message, for PEM-encoded certificates the
subject DN is printed, while for DER-encoded files that's not the
case as we don't parse them in swanctl. For PKCS#12 files, the DN
of extracted certificates was previously written to stderr, that is now
also changed.
diff --git a/src/swanctl/commands/load_creds.c b/src/swanctl/commands/load_creds.c
index 3fc38456b7..1c0d5288be 100644
--- a/src/swanctl/commands/load_creds.c
+++ b/src/swanctl/commands/load_creds.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016-2017 Tobias Brunner
+ * Copyright (C) 2016-2026 Tobias Brunner
* Copyright (C) 2015 Andreas Steffen
* Copyright (C) 2014 Martin Willi
*
@@ -26,9 +26,11 @@
#include "swanctl.h"
#include "load_creds.h"
+#include <asn1/asn1.h>
#include <credentials/sets/mem_cred.h>
#include <credentials/sets/callback_cred.h>
#include <credentials/containers/pkcs12.h>
+#include <credentials/containers/pem.h>
#include <collections/hashtable.h>
#include <vici_cert_info.h>
@@ -54,8 +56,8 @@ typedef struct {
/**
* Load a single certificate over vici
*/
-static bool load_cert(load_ctx_t *ctx, char *dir, certificate_type_t type,
- x509_flag_t flag, chunk_t data)
+static bool load_cert(load_ctx_t *ctx, certificate_t *cert, char *dir,
+ certificate_type_t type, x509_flag_t flag, chunk_t data)
{
vici_req_t *req;
vici_res_t *res;
@@ -87,6 +89,11 @@ static bool load_cert(load_ctx_t *ctx, char *dir, certificate_type_t type,
dir, vici_find_str(res, "", "errmsg"));
ret = FALSE;
}
+ else if (cert)
+ {
+ printf("loaded certificate '%Y' from '%s'\n", cert->get_subject(cert),
+ dir);
+ }
else
{
printf("loaded certificate from '%s'\n", dir);
@@ -95,6 +102,44 @@ static bool load_cert(load_ctx_t *ctx, char *dir, certificate_type_t type,
return ret;
}
+/**
+ * Load certificates from a PEM-encoded bundle (handles single PEM-encoded
+ * files as well)
+ */
+static void load_bundle(load_ctx_t *ctx, char *dir, certificate_type_t type,
+ x509_flag_t dir_flag, pem_t *bundle)
+{
+ enumerator_t *enumerator;
+ certificate_t *cert;
+ x509_t *x509;
+ chunk_t encoding;
+
+ enumerator = bundle->create_cert_enumerator(bundle);
+ while (enumerator->enumerate(enumerator, &cert))
+ {
+ x509_flag_t cert_flag = dir_flag;
+
+ if (cert->get_type(cert) == type &&
+ cert->get_encoding(cert, CERT_ASN1_DER, &encoding))
+ {
+ /* don't load end entity certificates as CA certificates, the vici
+ * plugin would reject them */
+ if (type == CERT_X509 && dir_flag == X509_CA)
+ {
+ x509 = (x509_t*)cert;
+ if (!(x509->get_flags(x509) & X509_CA))
+ {
+ cert_flag = X509_NONE;
+ }
+ }
+ load_cert(ctx, cert, dir, type, cert_flag, encoding);
+ free(encoding.ptr);
+ }
+ }
+ enumerator->destroy(enumerator);
+ bundle->container.destroy(&bundle->container);
+}
+
/**
* Load certificates from a directory
*/
@@ -122,7 +167,27 @@ static void load_certs(load_ctx_t *ctx, char *type_str, char *dir)
map = chunk_map(path, FALSE);
if (map)
{
- load_cert(ctx, path, type, flag, *map);
+ pem_t *bundle = NULL;
+
+ /* only load PEM bundles (possibly containing full chains)
+ * for regular X.509 certificates and CAs. for other types
+ * and flags it makes not much sense and would even silently
+ * skip files that contained at least one certificate */
+ if (!is_asn1(*map) && type == CERT_X509 &&
+ (flag == X509_NONE || flag == X509_CA))
+ {
+ bundle = lib->creds->create(lib->creds, CRED_CONTAINER,
+ CONTAINER_PEM, BUILD_BLOB,
+ *map, BUILD_END);
+ if (bundle)
+ {
+ load_bundle(ctx, path, type, flag, bundle);
+ }
+ }
+ if (!bundle)
+ {
+ load_cert(ctx, NULL, path, type, flag, *map);
+ }
chunk_unmap(map);
}
else
@@ -474,11 +539,7 @@ static bool load_pkcs12(load_ctx_t *ctx, char *path, pkcs12_t *p12)
loaded = FALSE;
if (cert->get_encoding(cert, CERT_ASN1_DER, &encoding))
{
- loaded = load_cert(ctx, path, CERT_X509, X509_NONE, encoding);
- if (loaded)
- {
- fprintf(stderr, " %Y\n", cert->get_subject(cert));
- }
+ loaded = load_cert(ctx, cert, path, CERT_X509, X509_NONE, encoding);
free(encoding.ptr);
}
else