Commit 06ef6c6d00 for strongswan.org
commit 06ef6c6d00b7b05dd83bf05b1c1d9a40b2f0653b
Author: Tobias Brunner <tobias@strongswan.org>
Date: Wed Sep 16 09:39:39 2026 +0200
charon-nm: Add support to load server/CA certificates from a PEM-encoded bundle
From a bundle, the first non-CA certificate is returned as server
certificate, which includes files that only contain a single PEM-encoded
server certificate.
It also fixes some locking issues in nm-creds.
diff --git a/src/charon-nm/nm/nm_creds.c b/src/charon-nm/nm/nm_creds.c
index 1cc1434859..fa590e8f3c 100644
--- a/src/charon-nm/nm/nm_creds.c
+++ b/src/charon-nm/nm/nm_creds.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2011-2026 Tobias Brunner
* Copyright (C) 2008 Martin Willi
*
* This program is free software; you can redistribute it and/or modify it
@@ -163,25 +164,29 @@ METHOD(credential_set_t, create_cert_enumerator, enumerator_t*,
METHOD(credential_set_t, create_private_enumerator, enumerator_t*,
private_nm_creds_t *this, key_type_t type, identification_t *id)
{
+ this->lock->read_lock(this->lock);
if (this->key == NULL)
{
- return NULL;
+ goto no_key;
}
if (type != KEY_ANY && type != this->key->get_type(this->key))
{
- return NULL;
+ goto no_key;
}
if (id && id->get_type(id) != ID_ANY)
{
if (id->get_type(id) != ID_KEY_ID ||
!this->key->has_fingerprint(this->key, id->get_encoding(id)))
{
- return NULL;
+ goto no_key;
}
}
- this->lock->read_lock(this->lock);
return enumerator_create_cleaner(enumerator_create_single(this->key, NULL),
(void*)this->lock->unlock, this->lock);
+
+no_key:
+ this->lock->unlock(this->lock);
+ return NULL;
}
/**
@@ -288,6 +293,15 @@ no_secret:
METHOD(nm_creds_t, add_certificate, void,
private_nm_creds_t *this, certificate_t *cert)
{
+ x509_t *x509 = (x509_t*)cert;
+ x509_flag_t flags DBG_UNUSED = x509->get_flags(x509);
+ bool is_ca DBG_UNUSED = flags & X509_CA;
+ bool is_im DBG_UNUSED = is_ca && !(flags & X509_SELF_SIGNED);
+
+ DBG2(DBG_CFG, "loaded %s%scertificate '%Y'",
+ is_im ? "intermediate " : "", is_ca ? "CA " : "",
+ cert->get_subject(cert));
+
this->lock->write_lock(this->lock);
this->certs->insert_last(this->certs, cert);
this->lock->unlock(this->lock);
@@ -300,7 +314,6 @@ static void load_ca_file(private_nm_creds_t *this, char *file)
{
certificate_t *cert;
- /* We add the CA constraint, as many CAs miss it */
cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
BUILD_FROM_FILE, file, BUILD_END);
if (!cert)
@@ -309,13 +322,7 @@ static void load_ca_file(private_nm_creds_t *this, char *file)
}
else
{
- DBG2(DBG_CFG, "loaded CA certificate '%Y'", cert->get_subject(cert));
- x509_t *x509 = (x509_t*)cert;
- if (!(x509->get_flags(x509) & X509_SELF_SIGNED))
- {
- DBG1(DBG_CFG, "%Y is not self signed", cert->get_subject(cert));
- }
- this->certs->insert_last(this->certs, cert);
+ add_certificate(this, cert);
}
}
@@ -395,6 +402,7 @@ METHOD(nm_creds_t, clear, void,
{
certificate_t *cert;
+ this->lock->write_lock(this->lock);
while (this->certs->remove_last(this->certs, (void**)&cert) == SUCCESS)
{
cert->destroy(cert);
@@ -411,6 +419,7 @@ METHOD(nm_creds_t, clear, void,
this->user = NULL;
this->keypass = NULL;
this->keyid = chunk_empty;
+ this->lock->unlock(this->lock);
}
METHOD(nm_creds_t, destroy, void,
diff --git a/src/charon-nm/nm/nm_creds.h b/src/charon-nm/nm/nm_creds.h
index 15b0f3068c..9669c79463 100644
--- a/src/charon-nm/nm/nm_creds.h
+++ b/src/charon-nm/nm/nm_creds.h
@@ -38,7 +38,7 @@ struct nm_creds_t {
/**
* Add a trusted gateway certificate to serve by this set.
*
- * @param cert certificate to serve
+ * @param cert X.509 certificate to serve (adopted)
*/
void (*add_certificate)(nm_creds_t *this, certificate_t *cert);
diff --git a/src/charon-nm/nm/nm_service.c b/src/charon-nm/nm/nm_service.c
index 43504cd9cc..81b5d7983b 100644
--- a/src/charon-nm/nm/nm_service.c
+++ b/src/charon-nm/nm/nm_service.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2017 Lubomir Rintel
- * Copyright (C) 2013-2023 Tobias Brunner
+ * Copyright (C) 2013-2026 Tobias Brunner
* Copyright (C) 2008-2009 Martin Willi
*
* This program is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
#include <utils/identification.h>
#include <config/peer_cfg.h>
#include <credentials/certificates/x509.h>
+#include <credentials/containers/pem.h>
#include <networking/tun_device.h>
#include <plugins/kernel_netlink/kernel_netlink_xfrmi.h>
@@ -857,6 +858,66 @@ static bool add_traffic_selectors(child_cfg_t *child_cfg, bool local,
return TRUE;
}
+/**
+ * Add the given certificate to the credential set (adopted) and return a
+ * reference if it's the first end entity certificate.
+ */
+static void add_certificate(NMStrongswanPluginPrivate *priv,
+ certificate_t *cert, certificate_t **ee_cert)
+{
+ x509_t *x509;
+
+ x509 = (x509_t*)cert;
+ if (!*ee_cert && !(x509->get_flags(x509) & X509_CA))
+ {
+ *ee_cert = cert->get_ref(cert);
+ }
+ priv->creds->add_certificate(priv->creds, cert);
+}
+
+/**
+ * Try to load the given blob as bundle first, otherwise as a single
+ * certificate. Returns the loaded end entity certificate (first non-CA cert).
+ */
+static bool load_trusted_certificates(NMStrongswanPluginPrivate *priv,
+ chunk_t file, certificate_t **ee_cert)
+{
+ enumerator_t *enumerator;
+ certificate_t *cert;
+ pem_t *bundle = NULL;
+ int added = 0;
+
+ bundle = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PEM,
+ BUILD_BLOB, file, BUILD_END);
+ if (bundle)
+ {
+ enumerator = bundle->create_cert_enumerator(bundle);
+ while (enumerator->enumerate(enumerator, &cert))
+ {
+ if (cert->get_type(cert) == CERT_X509)
+ {
+ add_certificate(priv, cert->get_ref(cert), ee_cert);
+ added++;
+ }
+ }
+ enumerator->destroy(enumerator);
+ bundle->container.destroy(&bundle->container);
+
+ if (added)
+ {
+ return TRUE;
+ }
+ }
+ cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
+ BUILD_BLOB, file, BUILD_END);
+ if (cert)
+ {
+ add_certificate(priv, cert, ee_cert);
+ return TRUE;
+ }
+ return FALSE;
+}
+
/**
* Connect function called from NM via DBUS
*/
@@ -879,7 +940,6 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
ike_sa_t *ike_sa;
auth_cfg_t *auth;
certificate_t *cert = NULL;
- x509_t *x509;
bool loose_gateway_id = FALSE;
ike_cfg_create_t ike = {
.version = IKEV2,
@@ -960,17 +1020,15 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
{
return FALSE;
}
- cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
- BUILD_BLOB, safe_file, BUILD_END);
- chunk_clear(&safe_file);
- if (!cert)
+ if (!load_trusted_certificates(priv, safe_file, &cert))
{
+ chunk_clear(&safe_file);
g_set_error(err, NM_VPN_PLUGIN_ERROR,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
"Loading gateway certificate failed.");
return FALSE;
}
- priv->creds->add_certificate(priv->creds, cert);
+ chunk_clear(&safe_file);
}
else
{
@@ -986,13 +1044,11 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
}
else if (cert)
{
- x509 = (x509_t*)cert;
- if (!(x509->get_flags(x509) & X509_CA))
- { /* for server certificates, we use the subject as identity */
- gateway = cert->get_subject(cert);
- gateway = gateway->clone(gateway);
- }
+ /* for server certificates, we use the subject as identity */
+ gateway = cert->get_subject(cert);
+ gateway = gateway->clone(gateway);
}
+ DESTROY_IF(cert);
if (!gateway || gateway->get_type(gateway) == ID_ANY)
{
/* if the user configured a CA certificate (or an invalid identity),