Commit 01c893c08b for strongswan.org

commit 01c893c08bb7dc3a62f1d1e7d676e2d6d5c83f30
Author: Tobias Brunner <tobias@strongswan.org>
Date:   Thu Nov 5 15:17:58 2026 +0100

    vici: Load first type-matching certificate from a bundle

    Usually, bundles start with the root or an intermediate CA certificate,
    so this allows using such a file in `certs` without having to reverse
    the order.  For `cacerts`, we also only load the first certificate but
    in this case we explicitly filter for CA certificates.

diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c
index 12f3303e72..61577590a0 100644
--- a/src/libcharon/plugins/vici/vici_config.c
+++ b/src/libcharon/plugins/vici/vici_config.c
@@ -47,6 +47,7 @@
 #include <daemon.h>
 #include <threading/rwlock.h>
 #include <threading/rwlock_condvar.h>
+#include <credentials/containers/pem.h>
 #include <collections/array.h>
 #include <collections/hashtable.h>
 #include <collections/linked_list.h>
@@ -1338,9 +1339,22 @@ static bool add_cert(auth_data_t *auth, auth_rule_t rule, certificate_t *cert)
 static bool parse_cert(auth_data_t *auth, auth_rule_t rule, chunk_t v)
 {
 	certificate_t *cert;
+	pem_selection_t sel;
+	pem_t *bundle;

-	cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
-							  BUILD_BLOB, v, BUILD_END);
+	bundle = lib->creds->create(lib->creds, CRED_CONTAINER,
+								CONTAINER_PEM, BUILD_BLOB, v, BUILD_END);
+	if (bundle)
+	{
+		sel = (rule == AUTH_RULE_CA_CERT) ? PEM_SELECTION_CA : PEM_SELECTION_EE;
+		cert = bundle->get_first(bundle, sel);
+		bundle->container.destroy(&bundle->container);
+	}
+	else
+	{
+		cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
+								  BUILD_BLOB, v, BUILD_END);
+	}
 	if (cert)
 	{
 		return add_cert(auth, rule, cert);
@@ -1753,6 +1767,8 @@ CALLBACK(auth_sn, bool,
 		auth_rule_t rule;
 		certificate_t *cert;
 		chunk_t handle;
+		pem_selection_t sel;
+		pem_t *bundle;

 		INIT(data,
 			.request = auth->request,
@@ -1781,8 +1797,22 @@ CALLBACK(auth_sn, bool,

 		if (data->file)
 		{
-			cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
-								BUILD_FROM_FILE, data->file, BUILD_END);
+			bundle = lib->creds->create(lib->creds, CRED_CONTAINER,
+								CONTAINER_PEM, BUILD_FROM_FILE, data->file,
+								BUILD_END);
+			if (bundle)
+			{
+				sel = strcasepfx(name, "cert") ? PEM_SELECTION_EE
+											   : PEM_SELECTION_CA;
+				cert = bundle->get_first(bundle, sel);
+				bundle->container.destroy(&bundle->container);
+			}
+			else
+			{
+				cert = lib->creds->create(lib->creds, CRED_CERTIFICATE,
+								CERT_X509, BUILD_FROM_FILE, data->file,
+								BUILD_END);
+			}
 		}
 		else
 		{
diff --git a/src/swanctl/swanctl.8.in b/src/swanctl/swanctl.8.in
index 98ef758f2e..e3249d6aaf 100644
--- a/src/swanctl/swanctl.8.in
+++ b/src/swanctl/swanctl.8.in
@@ -178,9 +178,11 @@ May contain config snippets (\fI*.conf\fR files) that are merged with the main
 .SS PEM-encoded Certificate Bundles
 In some directories, PEM-encoded certificate bundles are supported (i.e. files
 that concatenate several PEM-encoded certificates). This may be used to
-load certificate chains from a single file. However, please be aware that for
-all other uses, such as referencing a file in \fBcerts\fR or \fBcacerts\fR in
-\fIswanctl.conf\fR, only the \fBfirst\fR certificate is loaded from such a file.
+load certificate chains from a single file. However, please be aware that in
+most other cases only the first certificate is loaded from such a file. As a
+special exception, when used in \fBcerts\fR or \fBcacerts\fR in
+\fIswanctl.conf\fR, the type of certificate is considered, i.e. the \fBfirst\fR
+end entity or CA certificate is loaded, respectively.
 So if necessary, structure your files accordingly or split them up.
 .SH SEE ALSO
 .BR swanctl.conf (5)
diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt
index ea82706ac0..a397203d87 100644
--- a/src/swanctl/swanctl.opt
+++ b/src/swanctl/swanctl.opt
@@ -384,7 +384,8 @@ connections.<conn>.local<suffix>.certs =

 	Comma separated list of certificate candidates to use for authentication.
 	The certificates may use a relative path from the **swanctl** _x509_
-	directory or an absolute path.
+	directory or an absolute path. Be aware that only the first end entity
+	certificate from a PEM-encoded certificate bundle is loaded.

 	The certificate used for authentication is selected based on the received
 	certificate request payloads. If no appropriate CA can be located, the
@@ -401,7 +402,8 @@ connections.<conn>.local<suffix>.cert<suffix>.file =
 	Absolute path to the certificate to load.

 	Absolute path to the certificate to load. Passed as-is to the daemon, so it
-	must be readable by it.
+	must be readable by it. Be aware that only the first end entity certificate
+	from a PEM-encoded certificate bundle is loaded.

 	Configure either this or _handle_, but not both, in one section.

@@ -576,8 +578,8 @@ connections.<conn>.remote<suffix>.certs =

 	Comma separated list of certificates to accept for authentication.
 	The certificates may use a relative path from the **swanctl** _x509_
-	directory or an absolute path. Be aware that only the first certificate
-	from a PEM-encoded certificate bundle is loaded.
+	directory or an absolute path. Be aware that only the first end entity
+	certificate from a PEM-encoded certificate bundle is loaded.

 connections.<conn>.remote<suffix>.cert<suffix> =
 	Section for a certificate to accept for authentication.
@@ -590,7 +592,8 @@ connections.<conn>.remote<suffix>.cert<suffix>.file =
 	Absolute path to the certificate to load.

 	Absolute path to the certificate to load. Passed as-is to the daemon, so it
-	must be readable by it.
+	must be readable by it. Be aware that only the first end entity certificate
+	from a PEM-encoded certificate bundle is loaded.

 	Configure either this or _handle_, but not both, in one section.

@@ -612,7 +615,7 @@ connections.<conn>.remote<suffix>.cacerts =

 	Comma separated list of CA certificates to accept for authentication.
 	The certificates may use a relative path from the **swanctl** _x509ca_
-	directory or an absolute path. Be aware that only the first certificate
+	directory or an absolute path. Be aware that only the first CA certificate
 	from a PEM-encoded certificate bundle is loaded.

 connections.<conn>.remote<suffix>.cacert<suffix> =
@@ -626,7 +629,8 @@ connections.<conn>.remote<suffix>.cacert<suffix>.file =
 	Absolute path to the certificate to load.

 	Absolute path to the certificate to load. Passed as-is to the daemon, so it
-	must be readable by it.
+	must be readable by it. Be aware that only the first CA certificate
+	from a PEM-encoded certificate bundle is loaded.

 	Configure either this or _handle_, but not both, in one section.