[rs-commit] r506 - in /mod_ca/trunk: ChangeLog Makefile.am Makefile.in config.h.in configure configure.ac mod_ca_provider.c

rs-commit at redwax.eu rs-commit at redwax.eu
Thu Aug 21 16:55:32 CEST 2025


Author: minfrin at redwax.eu
Date: Thu Aug 21 16:55:32 2025
New Revision: 506

Log:
Add mod_ca_provider to support the provider mechanism
on OpenSSL v3+.

Added:
    mod_ca/trunk/mod_ca_provider.c
Modified:
    mod_ca/trunk/ChangeLog
    mod_ca/trunk/Makefile.am
    mod_ca/trunk/Makefile.in
    mod_ca/trunk/config.h.in
    mod_ca/trunk/configure
    mod_ca/trunk/configure.ac

Modified: mod_ca/trunk/ChangeLog
==============================================================================
--- mod_ca/trunk/ChangeLog	(original)
+++ mod_ca/trunk/ChangeLog	Thu Aug 21 16:55:32 2025
@@ -1,5 +1,8 @@
 
 Changes with v1.0.0
+
+ *) Add mod_ca_provider to support the provider mechanism
+    on OpenSSL v3+. [Graham Leggett]
 
  *) mod_ca_engine: Add workaround to build on RHEL10 where
     the openssl/engine.h file was truncated. [Graham Leggett]

Modified: mod_ca/trunk/Makefile.am
==============================================================================
--- mod_ca/trunk/Makefile.am	(original)
+++ mod_ca/trunk/Makefile.am	Thu Aug 21 16:55:32 2025
@@ -1,4 +1,4 @@
-EXTRA_DIST = mod_ca.h mod_ca.c mod_ca_simple.c mod_ca_ldap.c mod_ca_crl.c mod_ca_disk.c mod_ca_engine.c mod_ca.spec
+EXTRA_DIST = mod_ca.h mod_ca.c mod_ca_simple.c mod_ca_ldap.c mod_ca_crl.c mod_ca_disk.c mod_ca_engine.c mod_ca_provider.c mod_ca.spec
 
 all-local:
 	$(APXS) "-Wc,${CFLAGS}" -c $(DEF_LDLIBS) $(AM_CFLAGS) $(AM_LDFLAGS) $(openssl_CFLAGS) $(openssl_LIBS) @srcdir@/mod_ca.c
@@ -7,6 +7,7 @@
 	$(APXS) "-Wc,${CFLAGS}" -c $(DEF_LDLIBS) $(AM_CFLAGS) $(AM_LDFLAGS) $(openssl_CFLAGS) $(openssl_LIBS) @srcdir@/mod_ca_crl.c
 	$(APXS) "-Wc,${CFLAGS}" -c $(DEF_LDLIBS) $(AM_CFLAGS) $(AM_LDFLAGS) $(openssl_CFLAGS) $(openssl_LIBS) @srcdir@/mod_ca_disk.c
 	$(APXS) "-Wc,${CFLAGS}" -c $(DEF_LDLIBS) $(AM_CFLAGS) $(AM_LDFLAGS) $(openssl_CFLAGS) $(openssl_LIBS) @srcdir@/mod_ca_engine.c
+	$(APXS) "-Wc,${CFLAGS}" -c $(DEF_LDLIBS) $(AM_CFLAGS) $(AM_LDFLAGS) $(openssl_CFLAGS) $(openssl_LIBS) @srcdir@/mod_ca_provider.c
 
 install-exec-local: 
 	if test -z "$${LIBEXECDIR}"; then LIBEXECDIR=`$(APXS) -q LIBEXECDIR`; fi;\
@@ -22,4 +23,5 @@
 	$(APXS) "-Wc,${CFLAGS}" -S LIBEXECDIR=$(DESTDIR)$${LIBEXECDIR} -c -i $(DEF_LDLIBS) $(AM_CFLAGS) $(AM_LDFLAGS) $(openssl_CFLAGS) $(openssl_LIBS) @srcdir@/mod_ca_ldap.c;\
 	$(APXS) "-Wc,${CFLAGS}" -S LIBEXECDIR=$(DESTDIR)$${LIBEXECDIR} -c -i $(DEF_LDLIBS) $(AM_CFLAGS) $(AM_LDFLAGS) $(openssl_CFLAGS) $(openssl_LIBS) @srcdir@/mod_ca_crl.c;\
 	$(APXS) "-Wc,${CFLAGS}" -S LIBEXECDIR=$(DESTDIR)$${LIBEXECDIR} -c -i $(DEF_LDLIBS) $(AM_CFLAGS) $(AM_LDFLAGS) $(openssl_CFLAGS) $(openssl_LIBS) @srcdir@/mod_ca_disk.c;\
-	$(APXS) "-Wc,${CFLAGS}" -S LIBEXECDIR=$(DESTDIR)$${LIBEXECDIR} -c -i $(DEF_LDLIBS) $(AM_CFLAGS) $(AM_LDFLAGS) $(openssl_CFLAGS) $(openssl_LIBS) @srcdir@/mod_ca_engine.c
+	$(APXS) "-Wc,${CFLAGS}" -S LIBEXECDIR=$(DESTDIR)$${LIBEXECDIR} -c -i $(DEF_LDLIBS) $(AM_CFLAGS) $(AM_LDFLAGS) $(openssl_CFLAGS) $(openssl_LIBS) @srcdir@/mod_ca_engine.c;\
+	$(APXS) "-Wc,${CFLAGS}" -S LIBEXECDIR=$(DESTDIR)$${LIBEXECDIR} -c -i $(DEF_LDLIBS) $(AM_CFLAGS) $(AM_LDFLAGS) $(openssl_CFLAGS) $(openssl_LIBS) @srcdir@/mod_ca_provider.c

Modified: mod_ca/trunk/Makefile.in
==============================================================================
--- mod_ca/trunk/Makefile.in	(original)
+++ mod_ca/trunk/Makefile.in	Thu Aug 21 16:55:32 2025
@@ -262,7 +262,7 @@
 top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
-EXTRA_DIST = mod_ca.h mod_ca.c mod_ca_simple.c mod_ca_ldap.c mod_ca_crl.c mod_ca_disk.c mod_ca_engine.c mod_ca.spec
+EXTRA_DIST = mod_ca.h mod_ca.c mod_ca_simple.c mod_ca_ldap.c mod_ca_crl.c mod_ca_disk.c mod_ca_engine.c mod_ca_provider.c mod_ca.spec
 all: config.h
 	$(MAKE) $(AM_MAKEFLAGS) all-am
 
@@ -678,6 +678,7 @@
 	$(APXS) "-Wc,${CFLAGS}" -c $(DEF_LDLIBS) $(AM_CFLAGS) $(AM_LDFLAGS) $(openssl_CFLAGS) $(openssl_LIBS) @srcdir@/mod_ca_crl.c
 	$(APXS) "-Wc,${CFLAGS}" -c $(DEF_LDLIBS) $(AM_CFLAGS) $(AM_LDFLAGS) $(openssl_CFLAGS) $(openssl_LIBS) @srcdir@/mod_ca_disk.c
 	$(APXS) "-Wc,${CFLAGS}" -c $(DEF_LDLIBS) $(AM_CFLAGS) $(AM_LDFLAGS) $(openssl_CFLAGS) $(openssl_LIBS) @srcdir@/mod_ca_engine.c
+	$(APXS) "-Wc,${CFLAGS}" -c $(DEF_LDLIBS) $(AM_CFLAGS) $(AM_LDFLAGS) $(openssl_CFLAGS) $(openssl_LIBS) @srcdir@/mod_ca_provider.c
 
 install-exec-local: 
 	if test -z "$${LIBEXECDIR}"; then LIBEXECDIR=`$(APXS) -q LIBEXECDIR`; fi;\
@@ -693,7 +694,8 @@
 	$(APXS) "-Wc,${CFLAGS}" -S LIBEXECDIR=$(DESTDIR)$${LIBEXECDIR} -c -i $(DEF_LDLIBS) $(AM_CFLAGS) $(AM_LDFLAGS) $(openssl_CFLAGS) $(openssl_LIBS) @srcdir@/mod_ca_ldap.c;\
 	$(APXS) "-Wc,${CFLAGS}" -S LIBEXECDIR=$(DESTDIR)$${LIBEXECDIR} -c -i $(DEF_LDLIBS) $(AM_CFLAGS) $(AM_LDFLAGS) $(openssl_CFLAGS) $(openssl_LIBS) @srcdir@/mod_ca_crl.c;\
 	$(APXS) "-Wc,${CFLAGS}" -S LIBEXECDIR=$(DESTDIR)$${LIBEXECDIR} -c -i $(DEF_LDLIBS) $(AM_CFLAGS) $(AM_LDFLAGS) $(openssl_CFLAGS) $(openssl_LIBS) @srcdir@/mod_ca_disk.c;\
-	$(APXS) "-Wc,${CFLAGS}" -S LIBEXECDIR=$(DESTDIR)$${LIBEXECDIR} -c -i $(DEF_LDLIBS) $(AM_CFLAGS) $(AM_LDFLAGS) $(openssl_CFLAGS) $(openssl_LIBS) @srcdir@/mod_ca_engine.c
+	$(APXS) "-Wc,${CFLAGS}" -S LIBEXECDIR=$(DESTDIR)$${LIBEXECDIR} -c -i $(DEF_LDLIBS) $(AM_CFLAGS) $(AM_LDFLAGS) $(openssl_CFLAGS) $(openssl_LIBS) @srcdir@/mod_ca_engine.c;\
+	$(APXS) "-Wc,${CFLAGS}" -S LIBEXECDIR=$(DESTDIR)$${LIBEXECDIR} -c -i $(DEF_LDLIBS) $(AM_CFLAGS) $(AM_LDFLAGS) $(openssl_CFLAGS) $(openssl_LIBS) @srcdir@/mod_ca_provider.c
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.

Modified: mod_ca/trunk/config.h.in
==============================================================================
--- mod_ca/trunk/config.h.in	(original)
+++ mod_ca/trunk/config.h.in	Thu Aug 21 16:55:32 2025
@@ -18,11 +18,17 @@
 /* Define to 1 if you have the 'ASN1_STRING_get0_data' function. */
 #undef HAVE_ASN1_STRING_GET0_DATA
 
+/* Define to 1 if you have the 'ENGINE_load_private_key' function. */
+#undef HAVE_ENGINE_LOAD_PRIVATE_KEY
+
 /* Define to 1 if you have the <inttypes.h> header file. */
 #undef HAVE_INTTYPES_H
 
 /* Define to 1 if you have the <openssl/asn1.h> header file. */
 #undef HAVE_OPENSSL_ASN1_H
+
+/* Define to 1 if you have the <openssl/configuration.h> header file. */
+#undef HAVE_OPENSSL_CONFIGURATION_H
 
 /* Define to 1 if you have the <openssl/engine.h> header file. */
 #undef HAVE_OPENSSL_ENGINE_H
@@ -33,11 +39,17 @@
 /* Define to 1 if you have the <openssl/pem.h> header file. */
 #undef HAVE_OPENSSL_PEM_H
 
+/* Define to 1 if you have the <openssl/store.h> header file. */
+#undef HAVE_OPENSSL_STORE_H
+
 /* Define to 1 if you have the <openssl/x509v3.h> header file. */
 #undef HAVE_OPENSSL_X509V3_H
 
 /* Define to 1 if you have the <openssl/x509.h> header file. */
 #undef HAVE_OPENSSL_X509_H
+
+/* Define to 1 if you have the 'OSSL_STORE_open_ex' function. */
+#undef HAVE_OSSL_STORE_OPEN_EX
 
 /* Define to 1 if you have the <stdint.h> header file. */
 #undef HAVE_STDINT_H

Modified: mod_ca/trunk/configure
==============================================================================
--- mod_ca/trunk/configure	(original)
+++ mod_ca/trunk/configure	Thu Aug 21 16:55:32 2025
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.72 for mod_ca 0.2.4.
+# Generated by GNU Autoconf 2.72 for mod_ca 1.0.0.
 #
 # Report bugs to <dev-rs at redwax.eu>.
 #
@@ -603,8 +603,8 @@
 # Identity of this package.
 PACKAGE_NAME='mod_ca'
 PACKAGE_TARNAME='mod_ca'
-PACKAGE_VERSION='0.2.4'
-PACKAGE_STRING='mod_ca 0.2.4'
+PACKAGE_VERSION='1.0.0'
+PACKAGE_STRING='mod_ca 1.0.0'
 PACKAGE_BUGREPORT='dev-rs at redwax.eu'
 PACKAGE_URL=''
 
@@ -1317,7 +1317,7 @@
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-'configure' configures mod_ca 0.2.4 to adapt to many kinds of systems.
+'configure' configures mod_ca 1.0.0 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1384,7 +1384,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of mod_ca 0.2.4:";;
+     short | recursive ) echo "Configuration of mod_ca 1.0.0:";;
    esac
   cat <<\_ACEOF
 
@@ -1494,7 +1494,7 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-mod_ca configure 0.2.4
+mod_ca configure 1.0.0
 generated by GNU Autoconf 2.72
 
 Copyright (C) 2023 Free Software Foundation, Inc.
@@ -1777,7 +1777,7 @@
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by mod_ca $as_me 0.2.4, which was
+It was created by mod_ca $as_me 1.0.0, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -3250,7 +3250,7 @@
 
 # Define the identity of the package.
  PACKAGE='mod_ca'
- VERSION='0.2.4'
+ VERSION='1.0.0'
 
 
 printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -5336,10 +5336,22 @@
   printf "%s\n" "#define HAVE_OPENSSL_ASN1_H 1" >>confdefs.h
 
 fi
+ac_fn_c_check_header_compile "$LINENO" "openssl/configuration.h" "ac_cv_header_openssl_configuration_h" "$ac_includes_default"
+if test "x$ac_cv_header_openssl_configuration_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_OPENSSL_CONFIGURATION_H 1" >>confdefs.h
+
+fi
 ac_fn_c_check_header_compile "$LINENO" "openssl/engine.h" "ac_cv_header_openssl_engine_h" "$ac_includes_default"
 if test "x$ac_cv_header_openssl_engine_h" = xyes
 then :
   printf "%s\n" "#define HAVE_OPENSSL_ENGINE_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "openssl/store.h" "ac_cv_header_openssl_store_h" "$ac_includes_default"
+if test "x$ac_cv_header_openssl_store_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_OPENSSL_STORE_H 1" >>confdefs.h
 
 fi
 ac_fn_c_check_header_compile "$LINENO" "time.h" "ac_cv_header_time_h" "$ac_includes_default"
@@ -5404,6 +5416,18 @@
 if test "x$ac_cv_func_X509_CRL_get0_nextUpdate" = xyes
 then :
   printf "%s\n" "#define HAVE_X509_CRL_GET0_NEXTUPDATE 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "ENGINE_load_private_key" "ac_cv_func_ENGINE_load_private_key"
+if test "x$ac_cv_func_ENGINE_load_private_key" = xyes
+then :
+  printf "%s\n" "#define HAVE_ENGINE_LOAD_PRIVATE_KEY 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "OSSL_STORE_open_ex" "ac_cv_func_OSSL_STORE_open_ex"
+if test "x$ac_cv_func_OSSL_STORE_open_ex" = xyes
+then :
+  printf "%s\n" "#define HAVE_OSSL_STORE_OPEN_EX 1" >>confdefs.h
 
 fi
 
@@ -5947,7 +5971,7 @@
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by mod_ca $as_me 0.2.4, which was
+This file was extended by mod_ca $as_me 1.0.0, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -6015,7 +6039,7 @@
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config='$ac_cs_config_escaped'
 ac_cs_version="\\
-mod_ca config.status 0.2.4
+mod_ca config.status 1.0.0
 configured by $0, generated by GNU Autoconf 2.72,
   with options \\"\$ac_cs_config\\"
 

Modified: mod_ca/trunk/configure.ac
==============================================================================
--- mod_ca/trunk/configure.ac	(original)
+++ mod_ca/trunk/configure.ac	Thu Aug 21 16:55:32 2025
@@ -2,7 +2,7 @@
 # Process this file with autoconf to produce a configure script.
 
 AC_PREREQ(2.59)
-AC_INIT(mod_ca, 0.2.4, dev-rs at redwax.eu)
+AC_INIT(mod_ca, 1.0.0, dev-rs at redwax.eu)
 AM_INIT_AUTOMAKE([dist-bzip2])
 AC_CONFIG_FILES([Makefile mod_ca.spec])
 AC_CONFIG_SRCDIR([mod_ca.c])
@@ -59,13 +59,32 @@
 LIBS="$LIBS $openssl_LIBS $apr_LIBS $apu_LIBS"
 
 # Checks for header files.
-AC_CHECK_HEADERS([apr_crypto.h apr_strings.h apr_hash.h openssl/err.h openssl/pem.h openssl/x509.h openssl/x509v3.h openssl/asn1.h openssl/engine.h time.h])
+AC_CHECK_HEADERS([apr_crypto.h \
+                  apr_strings.h \
+                  apr_hash.h \
+                  openssl/err.h \
+                  openssl/pem.h \
+                  openssl/x509.h \
+                  openssl/x509v3.h \
+                  openssl/asn1.h \
+                  openssl/configuration.h \
+                  openssl/engine.h \
+                  openssl/store.h \
+                  time.h])
 
 # Checks for typedefs, structures, and compiler characteristics.
 AC_TYPE_SIZE_T
 
 # Checks for library functions.
-AC_CHECK_FUNCS([apr_file_link apr_crypto_clear X509_REVOKED_get0_revocationDate X509_REVOKED_get0_serialNumber ASN1_STRING_get0_data X509_CRL_get0_lastUpdate X509_CRL_get0_nextUpdate])
+AC_CHECK_FUNCS([apr_file_link \
+                apr_crypto_clear \
+                X509_REVOKED_get0_revocationDate \
+                X509_REVOKED_get0_serialNumber \
+                ASN1_STRING_get0_data \
+                X509_CRL_get0_lastUpdate \
+                X509_CRL_get0_nextUpdate \
+                ENGINE_load_private_key \
+                OSSL_STORE_open_ex])
 
 AC_SUBST(PACKAGE_VERSION)
 AC_OUTPUT

Added: mod_ca/trunk/mod_ca_provider.c
==============================================================================
--- mod_ca/trunk/mod_ca_provider.c	(added)
+++ mod_ca/trunk/mod_ca_provider.c	Thu Aug 21 16:55:32 2025
@@ -0,0 +1,1536 @@
+/* Licensed to Stichting The Commons Conservancy (TCC) under one or more
+ * contributor license agreements.  See the AUTHORS file distributed with
+ * this work for additional information regarding copyright ownership.
+ * TCC licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Provider to sign and issue digital certificates based on an OpenSSL based
+ * provider implementation.
+ *
+ *  Author: Graham Leggett
+ *
+ */
+#include <apr_strings.h>
+#include <apr_hash.h>
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "util_script.h"
+
+#include "mod_ca.h"
+
+#undef PACKAGE_BUGREPORT
+#undef PACKAGE_NAME
+#undef PACKAGE_STRING
+#undef PACKAGE_TARNAME
+#undef PACKAGE_VERSION
+#include "config.h"
+
+#define DEFAULT_CA_DAYS 365*1
+#define DEFAULT_CA_DIGEST "SHA256"
+
+#define SERIAL_RAND_BITS 64
+
+module AP_MODULE_DECLARE_DATA ca_provider_module;
+
+#if HAVE_OSSL_STORE_OPEN_EX
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/ts.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/store.h>
+#include <openssl/param_build.h>
+#include <openssl/provider.h>
+#include <openssl/safestack.h>
+#include <openssl/ui.h>
+
+#ifndef sk_EVP_PKEY_new_null
+DEFINE_STACK_OF(EVP_PKEY)
+#endif
+
+#include <openssl/engine.h>
+
+static OSSL_LIB_CTX *libctx = NULL;
+
+typedef struct
+{
+    const char *uri;
+    const char *pq;
+} ca_uri;
+
+typedef struct
+{
+    unsigned int key_set:1;
+    unsigned int passphrase_set:1;
+    unsigned int digest_set:1;
+    unsigned int cert_set:1;
+    unsigned int days_set:1;
+    unsigned int ext_set:1;
+    unsigned int chains_set:1;
+    unsigned int cas_set:1;
+    unsigned int nextcas_set:1;
+    X509 *signer;
+    X509_NAME *signer_name;
+    unsigned char *signer_der;
+    int signer_der_len;
+    X509 *signer_ca;
+    unsigned char *signer_ca_der;
+    int signer_ca_der_len;
+    apr_time_t signer_ca_expires;
+    int signer_set;
+    X509 *next_signer;
+    unsigned char *next_signer_der;
+    int next_signer_der_len;
+    apr_time_t next_signer_expires;
+    int next_signer_set;
+    ca_uri *key;
+    ca_uri *cert;
+    apr_array_header_t *chains;
+    apr_array_header_t *cas;
+    apr_array_header_t *nextcas;
+    const char *passphrase;
+    const char *digest;
+    const char *digest_pq;
+    int days;
+    apr_hash_t *ext;
+} ca_config_rec;
+
+typedef struct
+{
+    unsigned int device_set:1;
+    const char *device;
+} ca_server_rec;
+
+typedef struct
+{
+    const char *key;
+    const char *value;
+} ca_kv;
+
+struct ap_ca_instance_t
+{
+    void *placeholder;
+};
+
+static ca_asn1_t *make_ASN1_TIME(apr_pool_t *pool, ASN1_TIME *time)
+{
+    ca_asn1_t *buf = apr_palloc(pool, sizeof(ca_asn1_t));
+    unsigned char *tmp;
+
+    buf->len = i2d_ASN1_TIME(time, NULL);
+    buf->val = tmp = apr_palloc(pool, buf->len);
+    i2d_ASN1_TIME(time, &tmp);
+
+    return buf;
+}
+
+static void log_detail(request_rec *r, apr_status_t status,
+        const char *message, const char *detail)
+{
+    int len;
+    BIO *mem = BIO_new(BIO_s_mem());
+    char *err = apr_palloc(r->pool, HUGE_STRING_LEN);
+
+    ERR_print_errors(mem);
+
+    len = BIO_gets(mem, err, HUGE_STRING_LEN - 1);
+    if (len > -1) {
+        err[len] = 0;
+    }
+
+    apr_table_setn(r->notes, "error-notes",
+            apr_pstrcat(r->pool, "Provider signing: ", ap_escape_html(
+                    r->pool, message), NULL));
+
+    /* Allow "error-notes" string to be printed by ap_send_error_response() */
+    apr_table_setn(r->notes, "verbose-error-to", "*");
+
+    if (len > 0) {
+    	if (detail) {
+            ap_log_rerror(
+                    APLOG_MARK, APLOG_ERR, status, r, "mod_ca_provider: "
+                    "%s (%s): %s", message, err, detail);
+    	}
+    	else {
+            ap_log_rerror(
+                    APLOG_MARK, APLOG_ERR, status, r, "mod_ca_provider: "
+                    "%s (%s)", message, err);
+    	}
+    }
+    else {
+    	if (detail) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, "mod_ca_provider: "
+                "%s: %s", message, detail);
+    	}
+    	else {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, "mod_ca_provider: "
+                "%s", message);
+    	}
+    }
+
+    BIO_free(mem);
+}
+static void log_message(request_rec *r, apr_status_t status,
+        const char *message)
+{
+	log_detail(r, status, message, NULL);
+}
+
+static apr_status_t ca_BIO_cleanup(void *data)
+{
+    BIO_free((BIO *) data);
+    return APR_SUCCESS;
+}
+
+static apr_status_t ca_OSSL_PROVIDER_cleanup(void *data)
+{
+	OSSL_PROVIDER_unload((OSSL_PROVIDER *) data);
+    return APR_SUCCESS;
+}
+
+static apr_status_t ca_UI_METHOD_cleanup(void *data)
+{
+	UI_destroy_method((UI_METHOD *) data);
+    return APR_SUCCESS;
+}
+
+static apr_status_t ca_OSSL_STORE_CTX_cleanup(void *data)
+{
+	OSSL_STORE_close((OSSL_STORE_CTX *) data);
+    return APR_SUCCESS;
+}
+
+static apr_status_t ca_sk_EVP_PKEY_cleanup(void *data)
+{
+	sk_EVP_PKEY_pop_free((STACK_OF(EVP_PKEY) *) data, EVP_PKEY_free);
+    return APR_SUCCESS;
+}
+
+static apr_status_t ca_sk_X509_cleanup(void *data)
+{
+    sk_X509_pop_free((STACK_OF(X509) *) data, X509_free);
+    return APR_SUCCESS;
+}
+
+static apr_status_t ca_PKCS7_cleanup(void *data)
+{
+    PKCS7_free((PKCS7 *) data);
+    return APR_SUCCESS;
+}
+
+static apr_status_t ca_X509_cleanup(void *data)
+{
+    X509_free((X509 *) data);
+    return APR_SUCCESS;
+}
+
+static apr_status_t ca_X509_EXTENSION_cleanup(void *data)
+{
+    X509_EXTENSION_free((X509_EXTENSION *) data);
+    return APR_SUCCESS;
+}
+
+static apr_status_t ca_X509_REQ_cleanup(void *data)
+{
+    X509_REQ_free((X509_REQ *) data);
+    return APR_SUCCESS;
+}
+
+static apr_status_t ca_ASN1_INTEGER_cleanup(void *data)
+{
+    ASN1_INTEGER_free((ASN1_INTEGER *) data);
+    return APR_SUCCESS;
+}
+
+static apr_status_t ca_ASN1_GENERALIZEDTIME_cleanup(void *data)
+{
+    ASN1_GENERALIZEDTIME_free((ASN1_GENERALIZEDTIME *) data);
+    return APR_SUCCESS;
+}
+
+static apr_time_t ASN1_TIME_to_gmtime(ASN1_TIME *time)
+{
+    if (time) {
+        struct tm ts;
+        memset(&ts, 0, sizeof(ts));
+
+        switch (time->type) {
+        case V_ASN1_UTCTIME: {
+            sscanf((const char *) time->data, "%02d%02d%02d%02d%02d%02dZ",
+                    &ts.tm_year, &ts.tm_mon, &ts.tm_mday, &ts.tm_hour,
+                    &ts.tm_min, &ts.tm_sec);
+            ts.tm_mon -= 1;
+            break;
+        }
+        case V_ASN1_GENERALIZEDTIME: {
+            sscanf((const char *) time->data, "%04d%02d%02d%02d%02d%02dZ",
+                    &ts.tm_year, &ts.tm_mon, &ts.tm_mday, &ts.tm_hour,
+                    &ts.tm_min, &ts.tm_sec);
+            ts.tm_year -= 1900;
+            ts.tm_mon -= 1;
+            break;
+        }
+        }
+
+        return (apr_time_t) timegm(&ts);
+    }
+
+    return 0;
+}
+
+static int ca_pass_cb(UI *ui, UI_STRING *uis)
+{
+	request_rec *r = UI_get0_user_data(ui);
+
+    ca_config_rec *conf = ap_get_module_config(r->per_dir_config,
+            &ca_provider_module);
+
+	if (!conf->passphrase) {
+
+        log_message(r, APR_SUCCESS,
+                apr_psprintf(r->pool,
+                        "Passphrase expected for '%s' but none was provided", conf->key->uri));
+
+		return 0;
+	}
+
+	UI_set_result(ui, uis, conf->passphrase);
+
+	return 1;
+}
+
+static EVP_PKEY *get_signing_key_and_cert(request_rec *r)
+{
+    ca_config_rec *conf = ap_get_module_config(r->per_dir_config,
+            &ca_provider_module);
+
+	OSSL_STORE_CTX *store = NULL;
+	UI_METHOD *ui_method;
+    EVP_PKEY *key = NULL;
+    X509 *cert = NULL;
+    STACK_OF(EVP_PKEY) *keys;
+	STACK_OF(X509) *intermediates;
+	X509 *x;
+    unsigned char *buf;
+
+	apr_size_t nkeys = 0;
+	apr_size_t ncerts = 0;
+
+	int len;
+
+	keys = sk_EVP_PKEY_new_null();
+
+    apr_pool_cleanup_register(r->pool, keys, ca_sk_EVP_PKEY_cleanup,
+            apr_pool_cleanup_null);
+
+	intermediates = sk_X509_new_null();
+
+    apr_pool_cleanup_register(r->pool, intermediates, ca_sk_X509_cleanup,
+            apr_pool_cleanup_null);
+
+    ui_method = UI_create_method("passphrase");
+    UI_method_set_reader(ui_method, ca_pass_cb);
+
+    apr_pool_cleanup_register(r->pool, ui_method, ca_UI_METHOD_cleanup,
+            apr_pool_cleanup_null);
+
+    store = OSSL_STORE_open_ex(conf->key->uri, libctx, conf->key->pq,
+                               ui_method, conf, NULL, NULL, NULL);
+
+    if (!store) {
+        log_detail(r, APR_SUCCESS,
+        		"Could not open key store", conf->key->uri);
+
+        return NULL;
+    }
+
+    apr_pool_cleanup_register(r->pool, store, ca_OSSL_STORE_CTX_cleanup,
+            apr_pool_cleanup_null);
+
+    /* get all keys */
+
+    OSSL_STORE_expect(store, OSSL_STORE_INFO_PKEY);
+
+    while (!OSSL_STORE_eof(store)) {
+
+    	int type;
+
+    	OSSL_STORE_INFO *info = OSSL_STORE_load(store);
+
+    	if (!info) {
+    		break;
+    	}
+
+    	type = OSSL_STORE_INFO_get_type(info);
+
+        if (type == OSSL_STORE_INFO_PKEY) {
+
+    		EVP_PKEY *k = OSSL_STORE_INFO_get1_PKEY(info);
+
+    		sk_EVP_PKEY_push(keys, k);
+
+    		nkeys++;
+    	}
+
+    	OSSL_STORE_INFO_free(info);
+    }
+
+    /* get all ca certs */
+
+    store = OSSL_STORE_open_ex(conf->cert->uri, libctx, conf->cert->pq,
+                               ui_method, conf, NULL, NULL, NULL);
+
+    if (!store) {
+        log_detail(r, APR_SUCCESS,
+        		"Could not open certificate store", conf->cert->uri);
+
+        return NULL;
+    }
+
+    apr_pool_cleanup_register(r->pool, store, ca_OSSL_STORE_CTX_cleanup,
+            apr_pool_cleanup_null);
+
+    OSSL_STORE_expect(store, OSSL_STORE_INFO_CERT);
+
+    while (!OSSL_STORE_eof(store)) {
+
+    	int type;
+
+    	OSSL_STORE_INFO *info = OSSL_STORE_load(store);
+
+    	if (!info) {
+    		break;
+    	}
+
+    	type = OSSL_STORE_INFO_get_type(info);
+
+    	if (type == OSSL_STORE_INFO_CERT) {
+
+    		X509 *c = OSSL_STORE_INFO_get1_CERT(info);
+
+    		int is_ca = X509_check_ca(c);
+
+            if (is_ca) {
+                 sk_X509_push(intermediates, c);
+
+                 ncerts++;
+             }
+             else {
+            	 X509_free(c);
+             }
+    	}
+
+    	OSSL_STORE_INFO_free(info);
+    }
+
+    /* for each ca cert that has a matching key, decide on best
+     * cert and key - best cert is the most recent issued.
+     */
+    while ((x = sk_X509_pop(intermediates))) {
+
+        EVP_PKEY *k = NULL;
+        int i, n, found = 0;
+
+        /* no key, skip */
+        n = sk_EVP_PKEY_num(keys);
+        for (i = 0; i < n; ++i) {
+            k = sk_EVP_PKEY_value(keys, i);
+            if (X509_check_private_key(x, k)) {
+                found = 1;
+                break;
+            }
+        }
+        if (!found) {
+       	    X509_free(x);
+            continue;
+        }
+
+        /* no best candidate yet? we're in first place */
+        if (!cert) {
+            EVP_PKEY_up_ref(k);
+            cert = x; /* don't dup, we're returning this */
+            key = k;
+            continue;
+        }
+
+        /* were we issued after the previous best? */
+        if (ASN1_TIME_compare(X509_get0_notBefore(cert),
+                  X509_get0_notBefore(x)) < 0) {
+            X509_free(cert);
+            EVP_PKEY_free(key);
+            EVP_PKEY_up_ref(k);
+            cert = x; /* don't dup, we're returning this */
+            key = k;
+            continue;
+        }
+
+        X509_free(x);
+    }
+
+    /* found no certs with keys? bail out */
+    if (!cert || !key) {
+
+        log_detail(r, APR_SUCCESS,
+        		   "No certs found matching keys",
+				   apr_psprintf(r->pool, "%" APR_SIZE_T_FMT " keys at '%s', %"
+						   APR_SIZE_T_FMT " non-leaf certs at '%s'",
+						   nkeys, conf->key->uri, ncerts, conf->cert->uri));
+
+        return NULL;
+    }
+
+    /* save away the signer certificate */
+
+    conf->signer = cert;
+
+    conf->signer_name = X509_get_subject_name(cert);
+    if (!conf->signer_name) {
+        log_message(r, APR_SUCCESS,
+                "signer certificate did not have a subject name");
+
+        return NULL;
+    }
+
+    len = i2d_X509(cert, NULL);
+    if (len <= 0) {
+        log_message(r, APR_SUCCESS,
+                "could not DER encode the signed X509");
+
+        return NULL;
+    }
+    conf->signer_der_len = len;
+
+    conf->signer_der = buf = apr_palloc(r->pool, len);
+    if (!i2d_X509(cert, &buf)) {
+        log_message(r, APR_SUCCESS,
+                "could not DER encode the signed X509");
+
+        return NULL;
+    }
+
+    apr_pool_cleanup_register(r->pool, cert, ca_X509_cleanup,
+            apr_pool_cleanup_null);
+
+    return key;
+}
+
+int ca_sign_provider(request_rec *r, apr_hash_t *params,
+        const unsigned char **buffer, apr_size_t *len)
+{
+	EVP_MD *digest;
+    X509V3_CTX ext_ctx;
+    X509 *cert = NULL;
+    X509_REQ *creq = NULL;
+    EVP_PKEY *pktmp = NULL, *key = NULL;
+    X509_NAME *subject = NULL;
+    ASN1_INTEGER *sno = NULL;
+    ASN1_GENERALIZEDTIME *t = NULL;
+    STACK_OF(X509_EXTENSION) *exts;
+    apr_hash_index_t *iter;
+    PKCS7 *p7;
+    BIO *audit = NULL;
+    const unsigned char *tmp;
+    unsigned char *tmp2;
+    const unsigned char *end;
+    X509 *xs, *next;
+    STACK_OF(X509) *chain;
+    apr_size_t size;
+    apr_time_t time;
+    int rv, i;
+
+    ca_config_rec *conf = ap_get_module_config(r->per_dir_config,
+            &ca_provider_module);
+
+    /* key and cert defined? */
+    if (!conf->key || !conf->cert) {
+        return DECLINED;
+    }
+
+    /* read in the certificate */
+    tmp = *buffer;
+    if (!d2i_X509_REQ(&creq, &tmp, *len)) {
+        log_message(r, APR_SUCCESS,
+                "could not DER decode the certificate to be signed");
+
+        return HTTP_BAD_REQUEST;
+    }
+    apr_pool_cleanup_register(r->pool, creq, ca_X509_REQ_cleanup,
+            apr_pool_cleanup_null);
+
+    /* search for all ca certificates and keys */
+
+    key = get_signing_key_and_cert(r);
+
+    if (!key) {
+    	/* error handled above */
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    /* create the cert to sign */
+    cert = X509_new();
+    if (!cert) {
+        log_message(r, APR_SUCCESS, "X509_new failed");
+
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+    apr_pool_cleanup_register(r->pool, cert, ca_X509_cleanup,
+            apr_pool_cleanup_null);
+
+    /* set v3 certificate */
+    X509_set_version(cert, 2);
+
+    subject = X509_REQ_get_subject_name(creq);
+    if (!subject) {
+        log_message(r, APR_SUCCESS, "request had no subject");
+
+        return HTTP_BAD_REQUEST;
+    }
+    X509_set_subject_name(cert, subject);
+
+    exts = X509_REQ_get_extensions(creq);
+    if (exts) {
+        int idx = -1, crit = -1;
+        GENERAL_NAMES *gens = X509V3_get_d2i(exts, NID_subject_alt_name, &crit,
+                &idx);
+        while (gens) {
+            X509_EXTENSION *san = X509V3_EXT_i2d(NID_subject_alt_name, crit,
+                    gens);
+            X509_add_ext(cert, san, -1);
+
+            gens = X509V3_get_d2i(exts, NID_subject_alt_name, &crit, &idx);
+        }
+
+    }
+
+    pktmp = X509_REQ_get_pubkey(creq);
+    if (!pktmp) {
+        log_message(r, APR_SUCCESS, "request had no public key");
+
+        return HTTP_BAD_REQUEST;
+    }
+    X509_set_pubkey(cert, pktmp);
+
+    if (!X509_set_issuer_name(cert, conf->signer_name)) {
+        log_message(r, APR_SUCCESS, "could not set the issuer name");
+
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    /* read in the time */
+    rv = ap_run_ca_gettime(r, &time, NULL, NULL, NULL);
+    if (rv == DECLINED) {
+        log_message(r, APR_SUCCESS,
+                "No module configured to generate the time (ca_get_time)");
+
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+    if (rv != OK) {
+        return rv;
+    }
+
+    t = ASN1_GENERALIZEDTIME_adj(NULL, (time_t) apr_time_sec(time), 0, 0);
+    if (!t) {
+        log_message(r, APR_SUCCESS, "Could not create a generalized time");
+
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+    apr_pool_cleanup_register(r->pool, t, ca_ASN1_GENERALIZEDTIME_cleanup,
+            apr_pool_cleanup_null);
+
+    X509_set_notBefore(cert, X509_gmtime_adj(t, (long) 60 * 60 * 24 * -1));
+    X509_set_notAfter(cert,
+            X509_gmtime_adj(t, (long) 60 * 60 * 24 * conf->days));
+
+    apr_hash_set(params, "notBefore", APR_HASH_KEY_STRING,
+            make_ASN1_TIME(r->pool, X509_get_notBefore(cert)));
+    apr_hash_set(params, "notAfter", APR_HASH_KEY_STRING,
+            make_ASN1_TIME(r->pool, X509_get_notAfter(cert)));
+
+    /* read in the serial number */
+    rv = ap_run_ca_makeserial(r, params, buffer, len);
+    if (rv == DECLINED) {
+        log_message(r, APR_SUCCESS,
+                "No module configured to generate the serial number (ca_make_serial)");
+
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+    if (rv != OK) {
+        return rv;
+    }
+
+    if (!d2i_ASN1_INTEGER(&sno, buffer, *len)) {
+        log_message(r, APR_SUCCESS,
+                "could not DER decode the serial number (ca_make_serial)");
+
+        return HTTP_BAD_REQUEST;
+    }
+    apr_pool_cleanup_register(r->pool, sno, ca_ASN1_INTEGER_cleanup,
+            apr_pool_cleanup_null);
+
+    if (!X509_set_serialNumber(cert, sno)) {
+        log_message(r, APR_SUCCESS, "could not assign serial number");
+
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    X509V3_set_ctx(&ext_ctx, conf->signer, cert, NULL, NULL, 0);
+    for (iter = apr_hash_first(r->pool, conf->ext); iter; iter = apr_hash_next(iter)) {
+        const void *vname;
+        void *vval;
+        const char *name, *val;
+
+        apr_hash_this(iter, &vname, NULL, &vval);
+        name = vname;
+        val = vval;
+
+        X509_EXTENSION *extension = X509V3_EXT_conf(NULL, &ext_ctx, (char *) name,
+                (char *) val);
+        if (!extension) {
+            log_message(r, APR_SUCCESS,
+                    apr_psprintf(r->pool,
+                            "extension '%s' could not be set to '%s'", name, val));
+
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+        apr_pool_cleanup_register(r->pool, extension, ca_X509_EXTENSION_cleanup,
+                apr_pool_cleanup_null);
+
+        X509_add_ext(cert, extension, -1);
+    }
+
+    digest = EVP_MD_fetch(libctx, conf->digest, conf->digest_pq);
+    if (!digest) {
+        log_detail(r, APR_SUCCESS,
+        		"Digest could not be fetched", apr_pstrcat(r->pool, conf->digest, conf->digest_pq ? " " : NULL, conf->digest_pq, NULL));
+
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    if (!X509_sign(cert, key, digest)) {
+        log_message(r, APR_SUCCESS, "could not sign the request");
+
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    /* create a new PKCS#7 */
+    p7 = PKCS7_new();
+    if (!p7) {
+        log_message(r, APR_SUCCESS, "could not create a PKCS7 response");
+
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+    else {
+        apr_pool_cleanup_register(r->pool, p7, ca_PKCS7_cleanup,
+                apr_pool_cleanup_null);
+    }
+    PKCS7_set_type(p7, NID_pkcs7_signed);
+
+    /* workaround to avoid :BAD OBJECT encoding in i2d_PKCS7 - https://github.com/openssl/openssl/issues/8618 */
+    p7->d.sign->contents->type=OBJ_nid2obj(NID_pkcs7_data);
+
+    /* add the generated certificate */
+    if (!PKCS7_add_certificate(p7, cert)) {
+        log_message(r, APR_SUCCESS,
+                "could not add the signed certificate to the PKCS7 response");
+
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    /* print the subject, if necessary */
+    else if (APLOGrdebug(r)) {
+        audit = BIO_new(BIO_s_mem());
+        apr_pool_cleanup_register(r->pool, audit, ca_BIO_cleanup,
+                apr_pool_cleanup_null);
+        BIO_puts(audit, "[");
+        X509_NAME_print_ex(audit, X509_get_subject_name(cert), 0, XN_FLAG_RFC2253);
+        BIO_puts(audit, "]");
+    }
+
+    /* add the signer certificate */
+    if (X509_NAME_cmp(X509_get_subject_name(conf->signer),
+            X509_get_issuer_name(conf->signer))) {
+        if (!PKCS7_add_certificate(p7, conf->signer)) {
+            log_message(r, APR_SUCCESS,
+                    "could not add the signer certificate to the PKCS7 response");
+
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+        else if (APLOGrdebug(r)) {
+            BIO_puts(audit, ", [");
+            X509_NAME_print_ex(audit, X509_get_subject_name(conf->signer), 0, XN_FLAG_RFC2253);
+            BIO_puts(audit, "]");
+        }
+    }
+
+    /* add the certificate chain */
+    tmp = NULL;
+    size = 0;
+    rv = ap_run_ca_getchain(r, &tmp, &size, NULL);
+    if (rv > OK) {
+        return rv;
+    }
+
+    if (tmp) {
+
+        chain = sk_X509_new_null();
+
+        apr_pool_cleanup_register(r->pool, chain, ca_sk_X509_cleanup,
+                apr_pool_cleanup_null);
+
+        end = tmp + size;
+        while (tmp < end) {
+            X509 *cert = NULL;
+            if (!(cert = d2i_X509(NULL, &tmp, end - tmp))) {
+                log_message(r, APR_SUCCESS,
+                        "could not DER decode the CA certificate");
+
+                return HTTP_BAD_REQUEST;
+            }
+            sk_X509_push(chain, cert);
+        }
+
+        xs = conf->signer;
+        i = chain ? sk_X509_num(chain) : 0;
+        while (i) {
+            next = X509_find_by_subject(chain, X509_get_issuer_name(xs));
+            if (next) {
+                if (!X509_NAME_cmp(X509_get_subject_name(next),
+                        X509_get_issuer_name(next))) {
+                    break;
+                }
+                if (!PKCS7_add_certificate(p7, next)) {
+                    log_message(r, APR_SUCCESS,
+                            "could not add a certificate in the chain to the PKCS7 response");
+
+                    return HTTP_INTERNAL_SERVER_ERROR;
+                }
+                else if (APLOGrdebug(r)) {
+                    BIO_puts(audit, ", [");
+                    X509_NAME_print_ex(audit, X509_get_subject_name(next), 0, XN_FLAG_RFC2253);
+                    BIO_puts(audit, "]");
+                }
+                xs = next;
+            }
+            else {
+                break;
+            }
+            i--;
+        }
+
+    }
+
+    /* write out the certificate */
+    *len = i2d_PKCS7(p7, NULL);
+    if (*len <= 0) {
+        log_message(r, APR_SUCCESS,
+                "could not DER encode the signed PKCS7");
+
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+    *buffer = tmp2 = apr_palloc(r->pool, *len);
+
+    if (!i2d_PKCS7(p7, &tmp2)) {
+        log_message(r, APR_SUCCESS,
+                "could not DER encode the signed PKCS7");
+
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    if (audit) {
+        unsigned char *buf;
+        int n = BIO_get_mem_data(audit, &buf);
+        ap_log_rerror(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, r,
+                "mod_ca_provider: Successfully signed certificate and chain: %.*s",
+                n, buf);
+    }
+
+    return OK;
+}
+
+int ca_getca_provider(request_rec *r, const unsigned char **cacert,
+        apr_size_t *len, apr_time_t *validity)
+{
+    ca_config_rec *conf = ap_get_module_config(r->per_dir_config,
+            &ca_provider_module);
+
+    BIO *out;
+
+    unsigned char *der;
+
+    ca_uri *uris = (ca_uri *)conf->cas->elts;
+
+    ASN1_TIME *nextupdate;
+    apr_time_t expires = 0;
+
+	int blen, i;
+
+    /* chain defined? */
+    if (!conf->cas->nelts) {
+        return DECLINED;
+    }
+
+    /*
+     * We try hard to find a chain, depending on the detail
+     * provided.
+     */
+
+    out = BIO_new(BIO_s_mem());
+
+    for (i = 0; i < conf->cas->nelts; i++) {
+
+    	OSSL_STORE_CTX *store = NULL;
+
+    	store = OSSL_STORE_open_ex(uris[i].uri, libctx, uris[i].pq,
+                                   NULL, NULL, NULL, NULL, NULL);
+
+        if (!store) {
+            BIO_free(out);
+
+            log_detail(r, APR_SUCCESS,
+            		"Could not open certificate authority store", uris[i].uri);
+
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        OSSL_STORE_expect(store, OSSL_STORE_INFO_CERT);
+
+        while (!OSSL_STORE_eof(store)) {
+
+        	int type;
+
+        	OSSL_STORE_INFO *info = OSSL_STORE_load(store);
+
+        	if (!info) {
+        		break;
+        	}
+
+        	type = OSSL_STORE_INFO_get_type(info);
+
+        	if (type == OSSL_STORE_INFO_CERT) {
+
+        		X509 *x = OSSL_STORE_INFO_get1_CERT(info);
+
+        		int is_ca = X509_check_ca(x);
+
+                if (is_ca) {
+                    i2d_X509_bio(out, x);
+
+                    nextupdate = X509_get_notAfter(x);
+                    if (nextupdate) {
+                    	apr_time_t e = ASN1_TIME_to_gmtime(nextupdate);
+
+                        if (!expires || expires > e) {
+                            expires = e;
+                        }
+                    }
+
+                 }
+
+           	     X509_free(x);
+        	}
+
+        	OSSL_STORE_INFO_free(info);
+        }
+
+        OSSL_STORE_close(store);
+    }
+
+    blen = BIO_ctrl_pending(out);
+    *len = blen;
+    *cacert = der = apr_palloc(r->pool, blen);
+    BIO_read(out, der, blen);
+
+    BIO_free(out);
+
+    if (validity) {
+        *validity = expires;
+    }
+
+    return OK;
+}
+
+int ca_getnextca_provider(request_rec *r, const unsigned char **cacert,
+        apr_size_t *len, apr_time_t *validity)
+{
+    ca_config_rec *conf = ap_get_module_config(r->per_dir_config,
+            &ca_provider_module);
+
+    BIO *out;
+
+    unsigned char *der;
+
+    ca_uri *uris = (ca_uri *)conf->nextcas->elts;
+
+    ASN1_TIME *nextupdate;
+    apr_time_t expires = 0;
+
+	int blen, i;
+
+    /* chain defined? */
+    if (!conf->nextcas->nelts) {
+        return DECLINED;
+    }
+
+    /*
+     * We try hard to find a chain, depending on the detail
+     * provided.
+     */
+
+    out = BIO_new(BIO_s_mem());
+
+    for (i = 0; i < conf->nextcas->nelts; i++) {
+
+    	OSSL_STORE_CTX *store = NULL;
+
+    	store = OSSL_STORE_open_ex(uris[i].uri, libctx, uris[i].pq,
+                                   NULL, NULL, NULL, NULL, NULL);
+
+        if (!store) {
+            BIO_free(out);
+
+            log_detail(r, APR_SUCCESS,
+            		"Could not open next certificate authority store", uris[i].uri);
+
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        OSSL_STORE_expect(store, OSSL_STORE_INFO_CERT);
+
+        while (!OSSL_STORE_eof(store)) {
+
+        	int type;
+
+        	OSSL_STORE_INFO *info = OSSL_STORE_load(store);
+
+        	if (!info) {
+        		break;
+        	}
+
+        	type = OSSL_STORE_INFO_get_type(info);
+
+        	if (type == OSSL_STORE_INFO_CERT) {
+
+        		X509 *x = OSSL_STORE_INFO_get1_CERT(info);
+
+        		int is_ca = X509_check_ca(x);
+
+                if (is_ca) {
+                    i2d_X509_bio(out, x);
+
+                    nextupdate = X509_get_notAfter(x);
+                    if (nextupdate) {
+                    	apr_time_t e = ASN1_TIME_to_gmtime(nextupdate);
+
+                        if (!expires || expires > e) {
+                            expires = e;
+                        }
+                    }
+
+                 }
+
+           	     X509_free(x);
+        	}
+
+        	OSSL_STORE_INFO_free(info);
+        }
+
+        OSSL_STORE_close(store);
+    }
+
+    blen = BIO_ctrl_pending(out);
+    *len = blen;
+    *cacert = der = apr_palloc(r->pool, blen);
+    BIO_read(out, der, blen);
+
+    BIO_free(out);
+
+    if (validity) {
+        *validity = expires;
+    }
+
+    return OK;
+}
+
+int ca_getchain_provider(request_rec *r, const unsigned char **chain,
+        apr_size_t *len, apr_time_t *validity)
+{
+    ca_config_rec *conf = ap_get_module_config(r->per_dir_config,
+            &ca_provider_module);
+
+    BIO *out;
+
+    unsigned char *der;
+
+    ca_uri *uris = (ca_uri *)conf->chains->elts;
+
+    ASN1_TIME *nextupdate;
+    apr_time_t expires = 0;
+
+	int blen, i;
+
+    /* chain defined? */
+    if (!conf->chains->nelts) {
+        return DECLINED;
+    }
+
+    /*
+     * We try hard to find a chain, depending on the detail
+     * provided.
+     */
+
+    out = BIO_new(BIO_s_mem());
+
+    for (i = 0; i < conf->chains->nelts; i++) {
+
+    	OSSL_STORE_CTX *store = NULL;
+
+    	store = OSSL_STORE_open_ex(uris[i].uri, libctx, uris[i].pq,
+                                   NULL, NULL, NULL, NULL, NULL);
+
+        if (!store) {
+            BIO_free(out);
+
+            log_detail(r, APR_SUCCESS,
+            		"Could not open certificate chain store", uris[i].uri);
+
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        OSSL_STORE_expect(store, OSSL_STORE_INFO_CERT);
+
+        while (!OSSL_STORE_eof(store)) {
+
+        	int type;
+
+        	OSSL_STORE_INFO *info = OSSL_STORE_load(store);
+
+        	if (!info) {
+        		break;
+        	}
+
+        	type = OSSL_STORE_INFO_get_type(info);
+
+        	if (type == OSSL_STORE_INFO_CERT) {
+
+        		X509 *x = OSSL_STORE_INFO_get1_CERT(info);
+
+        		int is_ca = X509_check_ca(x);
+
+                if (is_ca) {
+                    i2d_X509_bio(out, x);
+
+                    nextupdate = X509_get_notAfter(x);
+                    if (nextupdate) {
+                    	apr_time_t e = ASN1_TIME_to_gmtime(nextupdate);
+
+                        if (!expires || expires > e) {
+                            expires = e;
+                        }
+                    }
+
+                 }
+
+           	     X509_free(x);
+        	}
+
+        	OSSL_STORE_INFO_free(info);
+        }
+
+        OSSL_STORE_close(store);
+    }
+
+    blen = BIO_ctrl_pending(out);
+    *len = blen;
+    *chain = der = apr_palloc(r->pool, blen);
+    BIO_read(out, der, blen);
+
+    BIO_free(out);
+
+    if (validity) {
+        *validity = expires;
+    }
+
+    return OK;
+}
+
+static void *create_ca_server_config(apr_pool_t *p, server_rec *s)
+{
+    ca_server_rec *conf = apr_pcalloc(p, sizeof(ca_server_rec));
+
+    return conf;
+}
+
+static void *merge_ca_server_config(apr_pool_t *p, void *basev, void *addv)
+{
+    ca_server_rec *new = (ca_server_rec *) apr_pcalloc(p,
+            sizeof(ca_config_rec));
+    ca_server_rec *add = (ca_server_rec *) addv;
+    ca_server_rec *base = (ca_server_rec *) basev;
+
+    new->device = (add->device_set == 0) ? base->device : add->device;
+    new->device_set = add->device_set || base->device_set;
+
+    return new;
+}
+
+static void *create_ca_dir_config(apr_pool_t *p, char *d)
+{
+    ca_config_rec *conf = apr_pcalloc(p, sizeof(ca_config_rec));
+
+    conf->chains = apr_array_make(p, 10, sizeof(ca_uri));
+    conf->cas = apr_array_make(p, 1, sizeof(ca_uri));
+    conf->nextcas = apr_array_make(p, 1, sizeof(ca_uri));
+
+    conf->digest = DEFAULT_CA_DIGEST;
+    conf->days = DEFAULT_CA_DAYS;
+    conf->ext = apr_hash_make(p);
+
+    return conf;
+}
+
+static void *merge_ca_dir_config(apr_pool_t *p, void *basev, void *addv)
+{
+    ca_config_rec *new = (ca_config_rec *) apr_pcalloc(p,
+            sizeof(ca_config_rec));
+    ca_config_rec *add = (ca_config_rec *) addv;
+    ca_config_rec *base = (ca_config_rec *) basev;
+
+    new->key = (add->key_set == 0) ? base->key : add->key;
+    new->key_set = add->key_set || base->key_set;
+    new->passphrase = (add->passphrase_set == 0) ? base->passphrase : add->passphrase;
+    new->passphrase_set = add->passphrase_set || base->passphrase_set;
+    new->digest = (add->digest_set == 0) ? base->digest : add->digest;
+    new->digest_pq = (add->digest_set == 0) ? base->digest_pq : add->digest_pq;
+    new->digest_set = add->digest_set || base->digest_set;
+    new->cert = (add->cert_set == 0) ? base->cert : add->cert;
+    new->cert_set = add->cert_set || base->cert_set;
+    new->chains = (add->chains_set == 0) ? base->chains : add->chains;
+    new->chains_set = add->chains_set || base->chains_set;
+    new->cas = (add->cas_set == 0) ? base->cas : add->cas;
+    new->cas_set = add->cas_set || base->cas_set;
+    new->nextcas = (add->nextcas_set == 0) ? base->nextcas : add->nextcas;
+    new->nextcas_set = add->nextcas_set || base->nextcas_set;
+    new->days = (add->days_set == 0) ? base->days : add->days;
+    new->days_set = add->days_set || base->days_set;
+
+    new->ext =
+            (add->ext_set == 0) ?
+                    base->ext : apr_hash_overlay(p, add->ext, base->ext);
+    new->ext_set = add->ext_set || base->ext_set;
+
+    return new;
+}
+
+static const char *set_ca_key(cmd_parms *cmd, void *dconf, const char *arg, const char *pq)
+{
+    ca_config_rec *conf = dconf;
+
+    conf->key = apr_palloc(cmd->pool, sizeof(ca_uri));
+    conf->key->uri = arg;
+    conf->key->pq = pq;
+    conf->key_set = 1;
+
+    return NULL;
+}
+
+static const char *set_ca_passphrase(cmd_parms *cmd, void *dconf, const char *arg)
+{
+    ca_config_rec *conf = dconf;
+
+    conf->passphrase = arg;
+    conf->passphrase_set = 1;
+
+    return NULL;
+}
+
+static const char *set_ca_digest(cmd_parms *cmd, void *dconf, const char *arg, const char *pq)
+{
+    ca_config_rec *conf = dconf;
+
+    conf->digest = arg;
+    conf->digest_pq = pq;
+    conf->digest_set = 1;
+
+    return NULL;
+}
+
+static const char *set_ca_cert(cmd_parms *cmd, void *dconf, const char *arg, const char *pq)
+{
+    ca_config_rec *conf = dconf;
+
+    conf->cert = apr_palloc(cmd->pool, sizeof(ca_uri));
+    conf->cert->uri = arg;
+    conf->cert->pq = pq;
+    conf->cert_set = 1;
+
+    return NULL;
+}
+
+static const char *set_ca_chain(cmd_parms *cmd, void *dconf, const char *arg, const char *pq)
+{
+    ca_config_rec *conf = dconf;
+
+    ca_uri *chain = apr_array_push(conf->chains);
+    chain->uri = arg;
+    chain->pq = pq;
+
+    conf->chains_set = 1;
+
+    return NULL;
+}
+
+static const char *set_ca(cmd_parms *cmd, void *dconf, const char *arg, const char *pq)
+{
+    ca_config_rec *conf = dconf;
+
+    ca_uri *ca = apr_array_push(conf->cas);
+    ca->uri = arg;
+    ca->pq = pq;
+
+    conf->cas_set = 1;
+
+    return NULL;
+}
+
+static const char *set_ca_next(cmd_parms *cmd, void *dconf, const char *arg, const char *pq)
+{
+    ca_config_rec *conf = dconf;
+
+    ca_uri *nextca = apr_array_push(conf->nextcas);
+    nextca->uri = arg;
+    nextca->pq = pq;
+
+    conf->nextcas_set = 1;
+
+    return NULL;
+}
+
+static const char *set_ca_days(cmd_parms *cmd, void *dconf, const char *arg)
+{
+    ca_config_rec *conf = dconf;
+    char *end = NULL;
+    apr_int64_t days = apr_strtoi64(arg, &end, 10);
+
+    if ((end && *end) || days < 1 || days > APR_INT32_MAX) {
+        return "CAEngineDays argument must be a positive integer representing the days for the certificate to be signed for";
+    }
+    conf->days = (int) days;
+    conf->days_set = 1;
+
+    return NULL;
+}
+
+static const char *set_ca_extension(cmd_parms *cmd, void *dconf,
+        const char *name, const char *val)
+{
+    ca_config_rec *conf = dconf;
+
+    apr_hash_set(conf->ext, name, APR_HASH_KEY_STRING, val);
+    conf->ext_set = 1;
+
+    return NULL;
+}
+
+const char *set_provider_device(cmd_parms *cmd, void *dcfg, const char *arg)
+{
+    server_rec *s = cmd->server;
+    ca_server_rec *conf =
+            ap_get_module_config(s->module_config, &ca_provider_module);
+
+    OSSL_PARAM_BLD *bld;
+    OSSL_PARAM *params;
+    OSSL_PROVIDER *prov = NULL;
+    const char *err;
+    const char *name = NULL;
+
+    if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+        return err;
+    }
+
+    bld = OSSL_PARAM_BLD_new();
+    if (!bld) {
+        return "CAProvider: could not create new OSSL_PARAM_BLD.";
+    }
+
+    while (*arg) {
+
+    	char *word, *val;
+
+        word = ap_getword_conf(cmd->pool, &arg);
+
+        if (!name) {
+        	name = word;
+        	continue;
+        }
+
+        val = strchr(word, '=');
+        if (!val) {
+            return "Invalid CAProvider parameter. Parameter must be "
+                   "in the form 'key=value'.";
+        }
+        else {
+            *val++ = '\0';
+        }
+
+        OSSL_PARAM_BLD_push_utf8_string(bld, word, val, 0);
+    }
+
+    params = OSSL_PARAM_BLD_to_param(bld);
+    if (!params) {
+        return "CAProvider: could not OSSL_PARAM_BLD_to_param.";
+    }
+
+    if (name) {
+    	prov = OSSL_PROVIDER_load_ex(libctx, name, params);
+
+    	if (prov) {
+
+            apr_pool_cleanup_register(cmd->pool, prov, ca_OSSL_PROVIDER_cleanup,
+                    apr_pool_cleanup_null);
+
+    	}
+    	else {
+
+    	    int len;
+    	    BIO *mem = BIO_new(BIO_s_mem());
+    	    char *err = apr_palloc(cmd->pool, HUGE_STRING_LEN);
+
+    	    ERR_print_errors(mem);
+
+    	    len = BIO_gets(mem, err, HUGE_STRING_LEN - 1);
+    	    if (len > -1) {
+    	        err[len] = 0;
+    	    }
+
+    	    return apr_pstrcat(cmd->pool, "CAProvider: could not load '",
+    	    		name, "': ", err, NULL);
+    	}
+
+    }
+    else {
+        return "CAProvider: no name specified.";
+    }
+
+    conf->device_set = 1;
+    return NULL;
+}
+
+static const command_rec ca_cmds[] =
+    {
+        AP_INIT_TAKE12("CAProviderCertificate",
+                      set_ca_cert, NULL, RSRC_CONF | ACCESS_CONF,
+                      "URI of signer certificate, followed by optional property query string"),
+        AP_INIT_TAKE12("CAProviderKey",
+                       set_ca_key, NULL, RSRC_CONF | ACCESS_CONF,
+                       "URI of the signing key, followed by optional property query string"),
+        AP_INIT_TAKE1("CAProviderPassphrase",
+              	      set_ca_passphrase, NULL, RSRC_CONF | ACCESS_CONF,
+                      "Filename of the passphrase to unlock the URI."),
+        AP_INIT_TAKE12("CAProviderDigest",
+                       set_ca_digest, NULL, RSRC_CONF | ACCESS_CONF,
+                       "Name of the signing digest, followed by optional property query string"),
+        AP_INIT_TAKE12("CAProviderChain",
+                       set_ca_chain, NULL, RSRC_CONF | ACCESS_CONF,
+                       "URI of signer certificate chain, followed by optional property query string. Can be specified more than once."),
+        AP_INIT_TAKE12("CAProviderCA",
+                       set_ca, NULL, RSRC_CONF | ACCESS_CONF,
+                       "URI of the CA certificate, followed by optional property query string. Can be specified more than once."),
+        AP_INIT_TAKE12("CAProviderNextCA",
+                       set_ca_next, NULL, RSRC_CONF | ACCESS_CONF,
+                       "URI of the next CA certificate to follow this one, followed by optional property query string. Can be specified more than once."),
+        AP_INIT_TAKE1("CAProviderDays",
+                      set_ca_days, NULL, RSRC_CONF | ACCESS_CONF,
+                      "Set to the number of days the certificate must be signed for."),
+        AP_INIT_TAKE2("CAProviderExtension",
+                      set_ca_extension, NULL, RSRC_CONF | ACCESS_CONF,
+                      "Certificate extension to add to the certificate when signed."),
+        AP_INIT_RAW_ARGS("CAProvider",
+                         set_provider_device, NULL, RSRC_CONF,
+                         "Name of the crypto provider to use along with optional key=value parameters."),
+        { NULL }
+    };
+
+static apr_status_t ca_cleanup(void *data)
+{
+	OSSL_LIB_CTX_free(libctx);
+
+	return APR_SUCCESS;
+}
+
+static int ca_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
+{
+	libctx = OSSL_LIB_CTX_new();
+
+    apr_pool_cleanup_register(pconf, NULL, ca_cleanup, ca_cleanup);
+
+    return APR_SUCCESS;
+}
+
+static void register_hooks(apr_pool_t *p)
+{
+    ap_hook_pre_config(ca_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
+
+    ap_hook_ca_sign(ca_sign_provider, NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_ca_getca(ca_getca_provider, NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_ca_getnextca(ca_getnextca_provider, NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_ca_getchain(ca_getchain_provider, NULL, NULL, APR_HOOK_MIDDLE);
+
+}
+
+AP_DECLARE_MODULE(ca_provider) =
+{
+    STANDARD20_MODULE_STUFF,
+    create_ca_dir_config,    /* dir config creater */
+    merge_ca_dir_config,     /* dir merger --- default is to override */
+    create_ca_server_config, /* server config */
+    merge_ca_server_config,  /* merge server config */
+    ca_cmds,                 /* command apr_table_t */
+    register_hooks           /* register hooks */
+};
+
+#else
+
+static int ca_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
+{
+    ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                     "mod_ca_provider is not supported on this platform.");
+
+    return APR_SUCCESS;
+}
+
+static void register_hooks(apr_pool_t *p)
+{
+    ap_hook_pre_config(ca_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+AP_DECLARE_MODULE(ca_provider) =
+{
+    STANDARD20_MODULE_STUFF,
+    NULL,                 /* dir config creater */
+    NULL,                 /* dir merger --- default is to override */
+    NULL,                 /* server config */
+    NULL,                 /* merge server config */
+    NULL,                 /* command apr_table_t */
+    register_hooks        /* register hooks */
+};
+
+#endif
+



More information about the rs-commit mailing list