[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