]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
openxpki: OCSP responder plugin accessing OpenXPKI
authorAndreas Steffen <andreas.steffen@strongswan.org>
Thu, 15 Jun 2023 14:24:34 +0000 (16:24 +0200)
committerTobias Brunner <tobias@strongswan.org>
Mon, 13 Nov 2023 11:40:55 +0000 (12:40 +0100)
The openxpki plugin directly access the certificates table in
the OpenXPKI's MariaDB in order to retrieve the status of an
issued X.509 certificate based on its serial number.

conf/Makefile.am
conf/plugins/openxpki.opt [new file with mode: 0644]
configure.ac
src/libstrongswan/Makefile.am
src/libstrongswan/credentials/certificates/ocsp_responder.h [new file with mode: 0644]
src/libstrongswan/plugins/openxpki/Makefile.am [new file with mode: 0644]
src/libstrongswan/plugins/openxpki/openxpki_ocsp_responder.c [new file with mode: 0644]
src/libstrongswan/plugins/openxpki/openxpki_ocsp_responder.h [new file with mode: 0644]
src/libstrongswan/plugins/openxpki/openxpki_plugin.c [new file with mode: 0644]
src/libstrongswan/plugins/openxpki/openxpki_plugin.h [new file with mode: 0644]

index 362aaf0fdb4378766510a7fe9df3269f3b35d313..ab9e70ce5abedec501671c5cdbb877defe24feb5 100644 (file)
@@ -79,6 +79,7 @@ plugins = \
        plugins/lookip.opt \
        plugins/ntru.opt \
        plugins/openssl.opt \
+       plugins/openxpki.opt \
        plugins/osx-attr.opt \
        plugins/p-cscf.opt \
        plugins/pkcs11.opt \
diff --git a/conf/plugins/openxpki.opt b/conf/plugins/openxpki.opt
new file mode 100644 (file)
index 0000000..9bf8a03
--- /dev/null
@@ -0,0 +1,4 @@
+charon.plugins.openxpki.database =
+       Database URI connecting to the OpenXPKI **certificate** database. If it
+       contains a password, make sure to adjust the permissions of the config
+       file accordingly.
index cd8e1bf1d43c906751b67c3558e385b8f29b04cf..0c2c42c988b533373746a721c1a8ce4e05e39c8c 100644 (file)
@@ -178,6 +178,7 @@ ARG_DISBL_SET([pkcs12],         [disable PKCS12 container support plugin.])
 ARG_DISBL_SET([pubkey],         [disable RAW public key support plugin.])
 ARG_DISBL_SET([sshkey],         [disable SSH key decoding plugin.])
 ARG_DISBL_SET([x509],           [disable X509 certificate implementation plugin.])
+ARG_ENABL_SET([openxpki],       [enable OCSP responder accessing OpenXPKI certificate database.])
 # fetcher/resolver plugins
 ARG_ENABL_SET([curl],           [enable CURL fetcher plugin to fetch files via libcurl. Requires libcurl.])
 ARG_ENABL_SET([files],          [enable simple file:// URI fetcher.])
@@ -1592,8 +1593,9 @@ ADD_PLUGIN([curl],                 [s charon pki scripts nm cmd])
 ADD_PLUGIN([files],                [s charon pki scripts nm cmd])
 ADD_PLUGIN([winhttp],              [s charon pki scripts])
 ADD_PLUGIN([soup],                 [s charon pki scripts nm cmd])
-ADD_PLUGIN([mysql],                [s charon pool manager medsrv attest])
-ADD_PLUGIN([sqlite],               [s charon pool manager medsrv attest])
+ADD_PLUGIN([mysql],                [s charon pki pool manager medsrv attest])
+ADD_PLUGIN([sqlite],               [s charon pki pool manager medsrv attest])
+ADD_PLUGIN([openxpki],             [s pki])
 ADD_PLUGIN([attr],                 [c charon])
 ADD_PLUGIN([attr-sql],             [c charon])
 ADD_PLUGIN([load-tester],          [c charon])
@@ -1728,6 +1730,7 @@ AM_CONDITIONAL(USE_PKCS1, test x$pkcs1 = xtrue)
 AM_CONDITIONAL(USE_PKCS7, test x$pkcs7 = xtrue)
 AM_CONDITIONAL(USE_PKCS8, test x$pkcs8 = xtrue)
 AM_CONDITIONAL(USE_PKCS12, test x$pkcs12 = xtrue)
+AM_CONDITIONAL(USE_OPENXPKI, test x$openxpki = xtrue)
 AM_CONDITIONAL(USE_PGP, test x$pgp = xtrue)
 AM_CONDITIONAL(USE_DNSKEY, test x$dnskey = xtrue)
 AM_CONDITIONAL(USE_SSHKEY, test x$sshkey = xtrue)
@@ -2010,6 +2013,7 @@ AC_CONFIG_FILES([
        src/libstrongswan/plugins/pkcs7/Makefile
        src/libstrongswan/plugins/pkcs8/Makefile
        src/libstrongswan/plugins/pkcs12/Makefile
+       src/libstrongswan/plugins/openxpki/Makefile
        src/libstrongswan/plugins/pgp/Makefile
        src/libstrongswan/plugins/dnskey/Makefile
        src/libstrongswan/plugins/sshkey/Makefile
index f118c723ae7cfc25665f4d5392c77d80192544d3..d1ffd157eb8b0748ae91aa537bbc919d3068aec2 100644 (file)
@@ -92,6 +92,7 @@ credentials/certificates/certificate.h credentials/certificates/x509.h \
 credentials/certificates/ac.h credentials/certificates/crl.h \
 credentials/certificates/pkcs10.h credentials/certificates/ocsp_request.h \
 credentials/certificates/ocsp_response.h \
+credentials/certificates/ocsp_responder.h \
 credentials/certificates/pgp_certificate.h \
 credentials/certificates/certificate_printer.h \
 credentials/containers/container.h credentials/containers/pkcs7.h \
@@ -479,6 +480,13 @@ if MONOLITHIC
 endif
 endif
 
+if USE_OPENXPKI
+  SUBDIRS += plugins/openxpki
+if MONOLITHIC
+  libstrongswan_la_LIBADD += plugins/openxpki/libstrongswan-openxpki.la
+endif
+endif
+
 if USE_PGP
   SUBDIRS += plugins/pgp
 if MONOLITHIC
diff --git a/src/libstrongswan/credentials/certificates/ocsp_responder.h b/src/libstrongswan/credentials/certificates/ocsp_responder.h
new file mode 100644 (file)
index 0000000..e7644dd
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2023 Andreas Steffen, strongSec GmbH
+ *
+ * Copyright (C) secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup ocsp_responder ocsp_responder
+ * @{ @ingroup certificates
+ */
+
+#ifndef OCSP_RESPONDER_H_
+#define OCSP_RESPONDER_H_
+
+#include <credentials/certificates/crl.h>
+
+typedef struct ocsp_responder_t ocsp_responder_t;
+
+/**
+ * OCSP responder object.
+ */
+struct ocsp_responder_t {
+
+       /**
+        * Check the status of a certificate given by its serial number
+        *
+        * @param cacert                        X.509 certificate of issuer CA
+        * @param serial_number         serial number of the certificate to be checked
+        * @param revocation_time       receives time of revocation, if revoked
+        * @param reason                receives reason of revocation, if revoked
+        * @return                                      certificate validation status
+        */
+       cert_validation_t (*get_status)(ocsp_responder_t *this,
+                                                                       certificate_t *cacert,
+                                                                       chunk_t serial_number,
+                                                                       time_t *revocation_time,
+                                                                       crl_reason_t *revocation_reason);
+
+       /**
+        * Destroy an ocsp_responder_t object.
+        */
+       void (*destroy)(ocsp_responder_t *this);
+
+};
+
+#endif /** OCSP_RESPONDER_H_ @}*/
diff --git a/src/libstrongswan/plugins/openxpki/Makefile.am b/src/libstrongswan/plugins/openxpki/Makefile.am
new file mode 100644 (file)
index 0000000..d10c238
--- /dev/null
@@ -0,0 +1,17 @@
+AM_CPPFLAGS = \
+       -I$(top_srcdir)/src/libstrongswan
+
+AM_CFLAGS = \
+       $(PLUGIN_CFLAGS)
+
+if MONOLITHIC
+noinst_LTLIBRARIES = libstrongswan-openxpki.la
+else
+plugin_LTLIBRARIES = libstrongswan-openxpki.la
+endif
+
+libstrongswan_openxpki_la_SOURCES = \
+       openxpki_plugin.h openxpki_plugin.c \
+       openxpki_ocsp_responder.h openxpki_ocsp_responder.c
+
+libstrongswan_openxpki_la_LDFLAGS = -module -avoid-version
diff --git a/src/libstrongswan/plugins/openxpki/openxpki_ocsp_responder.c b/src/libstrongswan/plugins/openxpki/openxpki_ocsp_responder.c
new file mode 100644 (file)
index 0000000..2eda863
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2023 Andreas Steffen, strongSec GmbH
+ *
+ * Copyright (C) secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include <library.h>
+#include <utils/debug.h>
+
+#include "openxpki_ocsp_responder.h"
+
+typedef struct private_openxpki_ocsp_responder_t private_openxpki_ocsp_responder_t;
+
+/**
+ * Private Data of a openxpki_ocsp_responder_t object.
+ */
+struct private_openxpki_ocsp_responder_t {
+
+       /**
+        * Public data
+        */
+       openxpki_ocsp_responder_t public;
+
+       /**
+        * OpenXPKI certificate database
+        */
+       database_t *db;
+};
+
+METHOD(ocsp_responder_t, get_status, cert_validation_t,
+       private_openxpki_ocsp_responder_t *this, certificate_t *cacert,
+       chunk_t serial_number, time_t *revocation_time, crl_reason_t *reason)
+{
+       cert_validation_t validation = VALIDATION_FAILED;
+       int rev_time;
+       const int max_decimals = 49;
+       const int max_bytes = max_decimals / 2.4083;
+       char serial[max_decimals + 1], authKeyId[60];
+       char *status, *reason_code;
+       chunk_t subjectKeyId;
+       public_key_t *public;
+       enumerator_t *e;
+       bool success;
+
+       /* convert serialNumber from binary to decimal as required for the DB query.
+        * check for a potential overflow since the database table field supports
+        * up to 49 decimal digits, only.
+        */
+       if (serial_number.len > max_bytes)
+       {
+               DBG1(DBG_LIB, "serialNumber conversion exceeds %d decimals", max_decimals);
+               return VALIDATION_FAILED;
+
+       }
+       chunk_to_dec(serial_number, serial);
+
+       /* additionally use the subjectyKeyId of the issuing CA for the DB query */
+       public = cacert->get_public_key(cacert);
+       success = public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &subjectKeyId);
+       public->destroy(public);
+
+       if (!success)
+       {
+               DBG1(DBG_LIB, "failed to extract subjectKeyId from CA certificate");
+               return VALIDATION_FAILED;
+       }
+
+       /* the authKeyId of the certificate is the subjectKeyId of the issuing CA */
+       snprintf(authKeyId, sizeof(authKeyId), "%#B", &subjectKeyId);
+
+       /* query the OpenXPKI certificate database */
+       e = this->db->query(this->db, "SELECT status, reason_code, revocation_time "
+                               "FROM certificate WHERE cert_key = ? "
+                                               "AND authority_key_identifier = ?",     DB_TEXT, serial,
+                                               DB_TEXT, authKeyId, DB_TEXT, DB_TEXT, DB_INT);
+       if (e && e->enumerate(e, &status, &reason_code, &rev_time))
+       {
+               if (streq(status, "ISSUED") || streq(status, "ISSUANCE_PENDING"))
+               {
+                       validation = VALIDATION_GOOD;
+               }
+               else if (streq(status, "UNKNOWN"))
+               {
+                       validation = VALIDATION_FAILED;
+               }
+               else if (streq(status, "REVOKED") || streq(status, "HOLD") ||
+                                streq(status, "CRL_ISSUANCE_PENDING"))
+               {
+                       validation = VALIDATION_REVOKED;
+
+                       if (revocation_time)
+                       {
+                               *revocation_time = rev_time;
+                       }
+                       if (reason)
+                       {
+                               if (streq(reason_code, "unspecified"))
+                               {
+                                       *reason = CRL_REASON_UNSPECIFIED;
+                               }
+                               else if (streq(reason_code, "keyCompromise"))
+                               {
+                                       *reason = CRL_REASON_KEY_COMPROMISE;
+                               }
+                               else if (streq(reason_code, "CACompromise"))
+                               {
+                                       *reason = CRL_REASON_CA_COMPROMISE;
+                               }
+                               else if (streq(reason_code, "affiliationChanged"))
+                               {
+                                       *reason = CRL_REASON_AFFILIATION_CHANGED;
+                               }
+                               else if (streq(reason_code, "superseded"))
+                               {
+                                       *reason = CRL_REASON_SUPERSEDED;
+                               }
+                               else if (streq(reason_code, "cessationOfOperation"))
+                               {
+                                       *reason = CRL_REASON_CESSATION_OF_OPERATION;
+                               }
+                                       else if (streq(reason_code, "certificateHold"))
+                               {
+                                       *reason = CRL_REASON_CERTIFICATE_HOLD;
+                                       validation = VALIDATION_ON_HOLD;
+                               }
+                               else if (streq(reason_code, "removeFromCRL"))
+                               {
+                                       *reason = CRL_REASON_REMOVE_FROM_CRL;
+                               }
+                                       else
+                               {
+                                       *reason = CRL_REASON_UNSPECIFIED;
+                               }
+                       }
+               }
+       }
+       DESTROY_IF(e);
+
+       return validation;
+}
+
+METHOD(ocsp_responder_t, destroy, void,
+       private_openxpki_ocsp_responder_t *this)
+{
+       this->db->destroy(this->db);
+       free(this);
+}
+
+/*
+ * See header
+ */
+ocsp_responder_t *openxpki_ocsp_responder_create()
+{
+       private_openxpki_ocsp_responder_t *this;
+       database_t *db;
+       char *uri;
+
+       uri = lib->settings->get_str(lib->settings,
+                                                                "%s.plugins.openxpki.database", NULL, lib->ns);
+       if (!uri)
+       {
+               DBG1(DBG_CFG, "openxpki database URI missing");
+               return NULL;
+       }
+       db = lib->db->create(lib->db, uri);
+       if (!db)
+       {
+               DBG1(DBG_CFG, "opening openxpki database failed");
+               return NULL;
+       }
+       INIT(this,
+               .public = {
+                       .interface = {
+                               .get_status = _get_status,
+                               .destroy = _destroy,
+                       },
+               },
+               .db = db,
+       );
+
+       return &this->public.interface;
+}
diff --git a/src/libstrongswan/plugins/openxpki/openxpki_ocsp_responder.h b/src/libstrongswan/plugins/openxpki/openxpki_ocsp_responder.h
new file mode 100644 (file)
index 0000000..5b4365c
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 Andreas Steffen, strongSec GmbH
+ *
+ * Copyright (C) secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup openxpki_ocsp_responder openxpki_ocsp_responder
+ * @{ @ingroup openxpki_p
+ */
+
+#ifndef OPENXPKI_OCSP_RESPONDER_H_
+#define OPENXPKI_OCSP_RESPONDER_H_
+
+typedef struct openxpki_ocsp_responder_t openxpki_ocsp_responder_t;
+
+#include <credentials/certificates/ocsp_responder.h>
+
+/**
+ * OCSP responder implementation using OpenXPKI.
+ */
+struct openxpki_ocsp_responder_t {
+
+       /**
+        * Implements ocsp_responder interface
+        */
+       ocsp_responder_t interface;
+};
+
+/**
+ * Create a openxpki_ocsp_responder instance.
+ */
+ocsp_responder_t *openxpki_ocsp_responder_create();
+
+#endif /** OPENXPKI_OCSP_RESPONDER_H_ @}*/
diff --git a/src/libstrongswan/plugins/openxpki/openxpki_plugin.c b/src/libstrongswan/plugins/openxpki/openxpki_plugin.c
new file mode 100644 (file)
index 0000000..7d7138b
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2023 Andreas Steffen, strongSec GmbH
+ *
+ * Copyright (C) secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "openxpki_plugin.h"
+
+#include "openxpki_ocsp_responder.h"
+
+typedef struct private_openxpki_plugin_t private_openxpki_plugin_t;
+
+/**
+ * Private data of an openxpki_plugin_t object.
+ */
+struct private_openxpki_plugin_t {
+
+       /**
+        * Public interface.
+        */
+       openxpki_plugin_t public;
+
+       /**
+        * OCSP responder
+        */
+       ocsp_responder_t *ocsp_responder;
+};
+
+METHOD(plugin_t, get_name, char*,
+       private_openxpki_plugin_t *this)
+{
+       return "openxpki";
+}
+
+static bool plugin_cb(private_openxpki_plugin_t *this,
+                                         plugin_feature_t *feature, bool reg, void *cb_data)
+{
+       if (reg)
+       {
+               /* Is there already a registered OCSP responder? */
+               if (!lib->get(lib, "ocsp-responder"))
+               {
+                       this->ocsp_responder = openxpki_ocsp_responder_create();
+                       lib->set(lib, "ocsp-responder", this->ocsp_responder);
+               }
+       }
+       else
+       {
+               if (this->ocsp_responder)
+               {
+                       lib->set(lib, "ocsp-responder", NULL);
+                       this->ocsp_responder->destroy(this->ocsp_responder);
+               }
+       }
+       return TRUE;
+}
+
+METHOD(plugin_t, get_features, int,
+       private_openxpki_plugin_t *this, plugin_feature_t *features[])
+{
+       static plugin_feature_t f[] = {
+               PLUGIN_CALLBACK((plugin_feature_callback_t)plugin_cb, NULL),
+                       PLUGIN_PROVIDE(CUSTOM, "ocsp-responder"),
+                               PLUGIN_DEPENDS(DATABASE, DB_MYSQL),
+       };
+       *features = f;
+       return countof(f);
+}
+
+METHOD(plugin_t, destroy, void,
+       private_openxpki_plugin_t *this)
+{
+       free(this);
+}
+
+/*
+ * see header file
+ */
+plugin_t *openxpki_plugin_create()
+{
+       private_openxpki_plugin_t *this;
+
+       INIT(this,
+               .public = {
+                       .plugin = {
+                               .get_name = _get_name,
+                               .get_features = _get_features,
+                               .destroy = _destroy,
+                       },
+               },
+       );
+
+       return &this->public.plugin;
+}
diff --git a/src/libstrongswan/plugins/openxpki/openxpki_plugin.h b/src/libstrongswan/plugins/openxpki/openxpki_plugin.h
new file mode 100644 (file)
index 0000000..28ff8f9
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 Andreas Steffen, strongSec GmbH
+ *
+ * Copyright (C) secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup openxpki_p openxpki
+ * @ingroup plugins
+ *
+ * @defgroup openxpki_plugin openxpki_plugin
+ * @{ @ingroup openxpki_p
+ */
+
+#ifndef OPENXPKI_PLUGIN_H_
+#define OPENXPKI_PLUGIN_H_
+
+#include <plugins/plugin.h>
+
+typedef struct openxpki_plugin_t openxpki_plugin_t;
+
+/**
+ * Plugin implementing an OCSP responder based on OpenXPKI.
+ */
+struct openxpki_plugin_t {
+
+       /**
+        * implements plugin interface
+        */
+       plugin_t plugin;
+};
+
+#endif /** OPENXPKI_PLUGIN_H_ @}*/