From: Andreas Steffen Date: Thu, 15 Jun 2023 14:24:34 +0000 (+0200) Subject: openxpki: OCSP responder plugin accessing OpenXPKI X-Git-Tag: 5.9.12rc1~3^2~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=199c7083e196421ae9530db25b550ea11a17bab3;p=thirdparty%2Fstrongswan.git openxpki: OCSP responder plugin accessing OpenXPKI 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. --- diff --git a/conf/Makefile.am b/conf/Makefile.am index 362aaf0fdb..ab9e70ce5a 100644 --- a/conf/Makefile.am +++ b/conf/Makefile.am @@ -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 index 0000000000..9bf8a03763 --- /dev/null +++ b/conf/plugins/openxpki.opt @@ -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. diff --git a/configure.ac b/configure.ac index cd8e1bf1d4..0c2c42c988 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index f118c723ae..d1ffd157eb 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -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 index 0000000000..e7644dda9e --- /dev/null +++ b/src/libstrongswan/credentials/certificates/ocsp_responder.h @@ -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 . + * + * 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 + +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 index 0000000000..d10c2386fb --- /dev/null +++ b/src/libstrongswan/plugins/openxpki/Makefile.am @@ -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 index 0000000000..2eda8634f5 --- /dev/null +++ b/src/libstrongswan/plugins/openxpki/openxpki_ocsp_responder.c @@ -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 . + * + * 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 +#include + +#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 index 0000000000..5b4365c970 --- /dev/null +++ b/src/libstrongswan/plugins/openxpki/openxpki_ocsp_responder.h @@ -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 . + * + * 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 + +/** + * 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 index 0000000000..7d7138bc54 --- /dev/null +++ b/src/libstrongswan/plugins/openxpki/openxpki_plugin.c @@ -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 . + * + * 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 index 0000000000..28ff8f9082 --- /dev/null +++ b/src/libstrongswan/plugins/openxpki/openxpki_plugin.h @@ -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 . + * + * 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 + +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_ @}*/