diff --git a/conf/Makefile.am b/conf/Makefile.am index 362aaf0fd..ab9e70ce5 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 000000000..9bf8a0376 --- /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 cd8e1bf1d..0c2c42c98 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 f118c723a..d1ffd157e 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 000000000..e7644dda9 --- /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 000000000..d10c2386f --- /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 000000000..2eda8634f --- /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 000000000..5b4365c97 --- /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 000000000..7d7138bc5 --- /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 000000000..28ff8f908 --- /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_ @}*/