summaryrefslogtreecommitdiffstats
path: root/modules/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'modules/ssl')
-rw-r--r--modules/ssl/Makefile.in20
-rw-r--r--modules/ssl/NWGNUmakefile327
-rw-r--r--modules/ssl/README106
-rw-r--r--modules/ssl/README.dsov.fig346
-rw-r--r--modules/ssl/README.dsov.ps1138
-rw-r--r--modules/ssl/config.m457
-rw-r--r--modules/ssl/mod_ssl.c775
-rw-r--r--modules/ssl/mod_ssl.dep1086
-rw-r--r--modules/ssl/mod_ssl.dsp195
-rw-r--r--modules/ssl/mod_ssl.h120
-rw-r--r--modules/ssl/mod_ssl.mak500
-rw-r--r--modules/ssl/mod_ssl_openssl.h113
-rw-r--r--modules/ssl/ssl_engine_config.c2158
-rw-r--r--modules/ssl/ssl_engine_init.c2367
-rw-r--r--modules/ssl/ssl_engine_io.c2419
-rw-r--r--modules/ssl/ssl_engine_kernel.c2855
-rw-r--r--modules/ssl/ssl_engine_log.c246
-rw-r--r--modules/ssl/ssl_engine_mutex.c111
-rw-r--r--modules/ssl/ssl_engine_ocsp.c312
-rw-r--r--modules/ssl/ssl_engine_pphrase.c911
-rw-r--r--modules/ssl/ssl_engine_rand.c177
-rw-r--r--modules/ssl/ssl_engine_vars.c1230
-rw-r--r--modules/ssl/ssl_private.h1174
-rw-r--r--modules/ssl/ssl_scache.c239
-rw-r--r--modules/ssl/ssl_util.c485
-rw-r--r--modules/ssl/ssl_util_ocsp.c422
-rw-r--r--modules/ssl/ssl_util_ssl.c591
-rw-r--r--modules/ssl/ssl_util_ssl.h107
-rw-r--r--modules/ssl/ssl_util_stapling.c975
29 files changed, 21562 insertions, 0 deletions
diff --git a/modules/ssl/Makefile.in b/modules/ssl/Makefile.in
new file mode 100644
index 0000000..4395bc3
--- /dev/null
+++ b/modules/ssl/Makefile.in
@@ -0,0 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF 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.
+
+#
+# standard stuff
+#
+
+include $(top_srcdir)/build/special.mk
diff --git a/modules/ssl/NWGNUmakefile b/modules/ssl/NWGNUmakefile
new file mode 100644
index 0000000..933180f
--- /dev/null
+++ b/modules/ssl/NWGNUmakefile
@@ -0,0 +1,327 @@
+#
+# This Makefile requires the environment var OSSLSDK
+# pointing to the base directory of your OpenSSL SDK.
+# If you want to use the Novell NTLS SDK instead then
+# define NTLSSDK pointing to the base directory of the
+# SDK, and also set USE_NTLS=1
+#
+
+#
+# Declare the sub-directories to be built here
+#
+
+SUBDIRS = \
+ $(EOLIST)
+
+#
+# Get the 'head' of the build environment. This includes default targets and
+# paths to tools
+#
+
+include $(AP_WORK)/build/NWGNUhead.inc
+
+#
+# build this level's files
+#
+# Make sure all needed macro's are defined
+#
+
+ifeq "$(USE_NTLS)" "1"
+SSL_INC = $(NTLSSDK)/inc
+SSL_LIB = $(NTLSSDK)/imp
+SSL_BIN = $(NTLSSDK)/bin
+SSL_APP = $(NTLSSDK)/apps
+ifneq "$(wildcard $(SSL_INC)/openssl/opensslv.h)" "$(SSL_INC)/openssl/opensslv.h"
+$(error '$(NTLSSDK)' does NOT point to a valid NTLS SDK!)
+endif
+else
+SSL_INC = $(OSSLSDK)/outinc_nw_libc
+SSL_LIB = $(OSSLSDK)/out_nw_libc
+SSL_BIN = $(OSSLSDK)/out_nw_libc
+SSL_APP = $(OSSLSDK)/apps
+ifneq "$(wildcard $(SSL_INC)/openssl/opensslv.h)" "$(SSL_INC)/openssl/opensslv.h"
+$(error '$(OSSLSDK)' does NOT point to a valid OpenSSL SDK!)
+endif
+endif
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS += \
+ $(SSL_INC) \
+ $(APR)/include \
+ $(APRUTIL)/include \
+ $(AP_WORK)/include \
+ $(AP_WORK)/modules/cache \
+ $(AP_WORK)/modules/generators \
+ $(AP_WORK)/server/mpm/NetWare \
+ $(NWOS) \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ -DHAVE_OPENSSL \
+ $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS += \
+ -l $(SSL_LIB) \
+ $(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm. If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME = mod_ssl
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+ifeq "$(USE_NTLS)" "1"
+NLM_DESCRIPTION = Apache $(VERSION_STR) SSL module (NTLS)
+else
+NLM_DESCRIPTION = Apache $(VERSION_STR) SSL module (OpenSSL)
+endif
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = $(NLM_NAME)
+
+#
+# If this is specified, it will override VERSION value in
+# $(AP_WORK)/build/NWGNUenvironment.inc
+#
+NLM_VERSION =
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE = 8192
+
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM =
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM =
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM =
+
+#
+# If this is specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS =
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled
+# by setting APACHE_UNIPROC in the environment
+#
+XDCDATA =
+
+#
+# Declare all target files (you must add your files here)
+#
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+ $(OBJDIR)/$(NLM_NAME).nlm \
+ $(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs := $(patsubst %.c,$(OBJDIR)/%.o,$(wildcard *.c))
+
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+ $(PRELUDE) \
+ $(EOLIST)
+
+ifneq "$(USE_NTLS)" "1"
+FILES_nlm_libs += \
+ $(SSL_LIB)/crypto.lib \
+ $(SSL_LIB)/ssl.lib \
+ $(EOLIST)
+endif
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+ Apache2 \
+ Libc \
+ $(EOLIST)
+
+ifeq "$(USE_NTLS)" "1"
+FILES_nlm_modules += ntls \
+ $(EOLIST)
+endif
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override $(NWOS)\copyright.txt.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+ @libc.imp \
+ @aprlib.imp \
+ @httpd.imp \
+ $(EOLIST)
+
+# Don't link with Winsock if standard sockets are being used
+ifneq "$(USE_STDSOCKETS)" "1"
+FILES_nlm_Ximports += @ws2nlm.imp \
+ $(EOLIST)
+endif
+
+ifeq "$(USE_NTLS)" "1"
+FILES_nlm_Ximports += @ntls.imp \
+ $(EOLIST)
+else
+FILES_nlm_Ximports += \
+ GetProcessSwitchCount \
+ RunningProcess \
+ GetSuperHighResolutionTimer \
+ $(EOLIST)
+endif
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ ssl_module \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+ $(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+ $(call COPY,$(OBJDIR)/*.nlm, $(INSTALLBASE)/modules/)
+ $(call COPY,$(SSL_BIN)/openssl.nlm, $(INSTALLBASE)/bin/)
+ $(call COPY,$(SSL_APP)/openssl.cnf, $(INSTALLBASE)/bin/)
+
+#
+# Any specialized rules here
+#
+vpath %.c $(AP_WORK)/modules/arch/netware
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APBUILD)/NWGNUtail.inc
+
+
diff --git a/modules/ssl/README b/modules/ssl/README
new file mode 100644
index 0000000..5fc7312
--- /dev/null
+++ b/modules/ssl/README
@@ -0,0 +1,106 @@
+SYNOPSIS
+
+ This Apache module provides strong cryptography for the Apache 2 webserver
+ via the Secure Sockets Layer (SSL v2/v3) and Transport Layer Security (TLS
+ v1) protocols by the help of the SSL/TLS implementation library OpenSSL which
+ is based on SSLeay from Eric A. Young and Tim J. Hudson.
+
+ The mod_ssl package was created in April 1998 by Ralf S. Engelschall
+ and was originally derived from software developed by Ben Laurie for
+ use in the Apache-SSL HTTP server project. The mod_ssl implementation
+ for Apache 1.3 continues to be supported by the modssl project
+ <http://www.modssl.org/>.
+
+SOURCES
+
+ See the top-level LAYOUT file for file descriptions.
+
+ The source files are written in clean ANSI C and pass the ``gcc -O -g
+ -ggdb3 -Wall -Wshadow -Wpointer-arith -Wcast-align -Wmissing-prototypes
+ -Wmissing-declarations -Wnested-externs -Winline'' compiler test
+ (assuming `gcc' is GCC 2.95.2 or newer) without any complains. When
+ you make changes or additions make sure the source still passes this
+ compiler test.
+
+FUNCTIONS
+
+ Inside the source code you will be confronted with the following types of
+ functions which can be identified by their prefixes:
+
+ ap_xxxx() ............... Apache API function
+ ssl_xxxx() .............. mod_ssl function
+ SSL_xxxx() .............. OpenSSL function (SSL library)
+ OpenSSL_xxxx() .......... OpenSSL function (SSL library)
+ X509_xxxx() ............. OpenSSL function (Crypto library)
+ PEM_xxxx() .............. OpenSSL function (Crypto library)
+ EVP_xxxx() .............. OpenSSL function (Crypto library)
+ RSA_xxxx() .............. OpenSSL function (Crypto library)
+
+DATA STRUCTURES
+
+ Inside the source code you will be confronted with the following
+ data structures:
+
+ server_rec .............. Apache (Virtual) Server
+ conn_rec ................ Apache Connection
+ request_rec ............. Apache Request
+ SSLModConfig ............ mod_ssl (Global) Module Configuration
+ SSLSrvConfig ............ mod_ssl (Virtual) Server Configuration
+ SSLDirConfig ............ mod_ssl Directory Configuration
+ SSLConnConfig ........... mod_ssl Connection Configuration
+ SSLFilterRec ............ mod_ssl Filter Context
+ SSL_CTX ................. OpenSSL Context
+ SSL_METHOD .............. OpenSSL Protocol Method
+ SSL_CIPHER .............. OpenSSL Cipher
+ SSL_SESSION ............. OpenSSL Session
+ SSL ..................... OpenSSL Connection
+ BIO ..................... OpenSSL Connection Buffer
+
+ For an overview how these are related and chained together have a look at the
+ page in README.dsov.{fig,ps}. It contains overview diagrams for those data
+ structures. It's designed for DIN A4 paper size, but you can easily generate
+ a smaller version inside XFig by specifying a magnification on the Export
+ panel.
+
+INCOMPATIBILITIES
+
+ The following intentional incompatibilities exist between mod_ssl 2.x
+ from Apache 1.3 and this mod_ssl version for Apache 2:
+
+ o The complete EAPI-based SSL_VENDOR stuff was removed.
+ o The complete EAPI-based SSL_COMPAT stuff was removed.
+ o The <IfDefine> variable MOD_SSL is no longer provided automatically
+
+MAJOR CHANGES
+
+ For a complete history of changes for Apache 2 mod_ssl, see the
+ CHANGES file in the top-level directory. The following
+ is a condensed summary of the major changes were made between
+ mod_ssl 2.x from Apache 1.3 and this mod_ssl version for Apache 2:
+
+ o The DBM based session cache is now based on APR's DBM API only.
+ o The shared memory based session cache is now based on APR's APIs.
+ o SSL I/O is now implemented in terms of filters rather than BUFF
+ o Eliminated ap_global_ctx. Storing Persistent information in
+ process_rec->pool->user_data. The ssl_pphrase_Handle_CB() and
+ ssl_config_global_* () functions have an extra parameter now -
+ "server_rec *" - which is used to retrieve the SSLModConfigRec.
+ o Properly support restarts, allowing mod_ssl to be added to a server
+ that is already running and to change server certs/keys on restart
+ o Various performance enhancements
+ o proxy support is no longer an "extension", much of the mod_ssl core
+ was re-written (ssl_engine_{init,kernel,config}.c) to be generic so
+ it could be re-used in proxy mode.
+ - the optional function ssl_proxy_enable is provide for mod_proxy
+ to enable proxy support
+ - proxy support now requires 'SSLProxyEngine on' to be configured
+ - proxy now supports SSLProxyCARevocation{Path,File} in addition to
+ the original SSLProxy* directives
+ o per-directory SSLCACertificate{File,Path} is now thread-safe but
+ requires SSL_set_cert_store patch to OpenSSL
+ o the ssl_engine_{ds,ext}.c source files are obsolete and no longer
+ exist
+
+TODO
+
+ See the top-level STATUS file for current efforts and goals.
diff --git a/modules/ssl/README.dsov.fig b/modules/ssl/README.dsov.fig
new file mode 100644
index 0000000..77cd2ca
--- /dev/null
+++ b/modules/ssl/README.dsov.fig
@@ -0,0 +1,346 @@
+#FIG 3.2
+Landscape
+Center
+Metric
+Letter
+100.00
+Single
+-2
+1200 2
+0 32 #616561
+0 33 #b6b2b6
+0 34 #f7f3f7
+0 35 #cfcfcf
+0 36 #ffffff
+6 6345 2835 7155 3150
+6 6345 2970 7110 3150
+4 0 0 200 0 20 8 0.0000 4 120 585 6345 3105 "ssl_module")\001
+-6
+4 0 0 200 0 20 8 0.0000 4 120 660 6345 2970 ap_ctx_get(...,\001
+-6
+6 10800 2610 12240 3060
+4 0 0 200 0 20 8 0.0000 4 120 1170 10800 2745 ap_get_module_config(...\001
+4 0 0 200 0 20 8 0.0000 4 120 795 10800 2880 ->per_dir_config,\001
+4 0 0 200 0 20 8 0.0000 4 120 585 10800 3015 &ssl_module)\001
+-6
+6 7920 4770 9135 4995
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 9135 4995 7920 4995 7920 4770 9135 4770 9135 4995
+4 0 0 100 0 18 12 0.0000 4 180 1065 8010 4950 request_rec\001
+-6
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 6975 3330 7425 2520
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 7200 4230 9450 2520
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 7875 4905 7200 5220
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 6750 5130 6750 4545
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 6705 5445 7155 6120
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 7875 4815 7200 4590
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 9585 2565 11475 4230
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 10170 5130 11835 4545
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 7920 6075 9855 5400
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 9990 5445 10935 5625
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 10215 5310 10935 5310
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 11925 4590 11925 5085
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 9810 5490 9810 6840
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 9945 5445 10935 6030
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 8865 4725 10800 2565
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+ 675 6075 5850 6075
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 675 6525 675 6075
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 0 1.00 60.00 120.00
+ 5850 6075 5850 6525
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+ 900 5625 5625 5625
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+ 1125 5175 5400 5175
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+ 1350 4725 5175 4725
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+ 1575 4275 4950 4275
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+ 1800 3825 4725 3825
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+ 2025 3375 4500 3375
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+ 2250 2925 4275 2925
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+ 2475 2475 4050 2475
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+ 2700 2025 3825 2025
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+ 2925 1575 3600 1575
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 900 6075 900 5625
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 1125 6525 1125 5175
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 1350 5175 1350 4725
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 1575 4725 1575 4275
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 1800 6525 1800 3825
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 2025 3825 2025 3375
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 2250 3375 2250 2925
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 2475 2925 2475 2475
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 0 1.00 60.00 120.00
+ 5625 5625 5625 6075
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 0 1.00 60.00 120.00
+ 5400 5175 5400 6525
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 0 1.00 60.00 120.00
+ 5175 4725 5175 5175
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 0 1.00 60.00 120.00
+ 4950 4275 4950 4725
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 0 1.00 60.00 120.00
+ 4725 3825 4725 6525
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 0 1.00 60.00 120.00
+ 4500 3375 4500 3825
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 0 1.00 60.00 120.00
+ 4275 2925 4275 3375
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 0 1.00 60.00 120.00
+ 4050 2475 4050 2925
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 2700 6525 2700 2025
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 0 1.00 60.00 120.00
+ 3825 2025 3825 6525
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 0 1.00 60.00 120.00
+ 3600 1575 3600 2025
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 2925 2025 2925 1575
+2 1 0 4 0 0 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 4.00 60.00 120.00
+ 540 6525 6300 6525
+2 3 0 1 7 7 800 0 20 0.000 0 0 -1 0 0 9
+ 675 6525 5850 6525 5850 6075 5625 6075 5625 5625 900 5625
+ 900 6075 675 6075 675 6525
+2 3 0 1 34 34 700 0 20 0.000 0 0 -1 0 0 13
+ 1125 6525 5355 6525 5400 5175 5175 5175 5175 4725 4950 4725
+ 4950 4275 1575 4275 1575 4725 1350 4725 1350 5175 1125 5175
+ 1125 6525
+2 3 0 1 35 35 500 0 20 0.000 0 0 -1 0 0 17
+ 1800 6525 4725 6525 4725 3825 4500 3825 4500 3375 4275 3375
+ 4275 2925 4050 2925 4050 2475 2475 2475 2475 2925 2250 2925
+ 2250 3375 2025 3375 2025 3825 1800 3825 1800 6525
+2 3 0 1 33 33 400 0 20 0.000 0 0 -1 0 0 9
+ 2700 6525 3825 6525 3825 2025 3600 2025 3600 1575 2925 1575
+ 2925 2025 2700 2025 2700 6525
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 1 2
+ 2 0 1.00 60.00 120.00
+ 2 0 1.00 60.00 120.00
+ 2700 6750 3825 6750
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 1 2
+ 2 0 1.00 60.00 120.00
+ 2 0 1.00 60.00 120.00
+ 1125 7200 5400 7200
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 1 2
+ 2 0 1.00 60.00 120.00
+ 2 0 1.00 60.00 120.00
+ 1800 6975 4725 6975
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 1 2
+ 2 0 1.00 60.00 120.00
+ 2 0 1.00 60.00 120.00
+ 675 7425 5850 7425
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+ 675 6570 675 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+ 1125 6570 1125 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+ 1800 6570 1800 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+ 2700 6570 2700 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+ 3825 6570 3825 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+ 4725 6570 4725 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+ 5400 6570 5400 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+ 5850 6570 5850 7650
+2 4 0 2 0 7 100 0 -1 0.000 0 0 20 0 0 5
+ 12600 8550 450 8550 450 225 12600 225 12600 8550
+2 4 0 1 0 34 200 0 20 0.000 0 0 20 0 0 5
+ 12600 1350 450 1350 450 225 12600 225 12600 1350
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 10170 2475 8775 2475 8775 2250 10170 2250 10170 2475
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 11925 2475 10575 2475 10575 2250 11925 2250 11925 2475
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 12375 4500 11430 4500 11430 4275 12375 4275 12375 4500
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 12375 5400 10980 5400 10980 5175 12375 5175 12375 5400
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 10170 5400 9675 5400 9675 5175 10170 5175 10170 5400
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 7875 6300 7200 6300 7200 6075 7875 6075 7875 6300
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 8190 2475 6750 2475 6750 2250 8190 2250 8190 2475
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 7605 3600 6300 3600 6300 3375 7605 3375 7605 3600
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 7335 4500 6300 4500 6300 4275 7335 4275 7335 4500
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 7200 5400 6300 5400 6300 5175 7200 5175 7200 5400
+2 1 0 6 7 7 600 0 -1 0.000 0 0 -1 0 0 2
+ 9450 4500 6075 1935
+2 1 0 6 7 7 600 0 -1 0.000 0 0 4 0 0 2
+ 9450 4500 12465 2205
+2 1 0 6 7 7 600 0 -1 0.000 0 0 4 0 0 2
+ 9450 4500 9450 7785
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 9630 5310 7245 5310
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 11385 4365 7380 4365
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 12240 5805 10980 5805 10980 5580 12240 5580 12240 5805
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 12375 6210 10980 6210 10980 5985 12375 5985 12375 6210
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 11205 6885 9900 5445
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 12285 7155 10530 7155 10530 6930 12285 6930 12285 7155
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 10170 7155 9630 7155 9630 6930 10170 6930 10170 7155
+2 1 0 6 7 7 600 0 -1 0.000 0 0 4 0 0 2
+ 12510 6435 9450 6435
+2 1 0 1 0 34 300 0 20 0.000 0 0 7 1 0 4
+ 1 1 1.00 60.00 120.00
+ 12375 4455 12510 4635 12510 6210 11970 6885
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 9850 5143 9175 4918
+3 1 0 1 34 34 800 0 20 0.000 0 0 0 41
+ 7380 1710 6390 2115 5535 2115 6075 3015 5670 3465 6165 3915
+ 5715 4410 6030 5040 6030 5310 6480 5715 6390 6255 6975 6300
+ 7065 6975 7965 6750 8100 7560 8955 7290 9360 7740 9720 7560
+ 10755 8145 12060 8280 12375 7650 12420 7200 12510 7065 12330 6660
+ 12510 6390 12420 5940 12375 5400 12510 5220 12510 4725 12600 4275
+ 12375 3645 12105 3240 12150 2745 12375 2700 12330 1980 11790 1575
+ 11250 1935 10125 1485 8955 2070 7785 1620 7695 1575
+ 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+ 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+ 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+ 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+ 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+ 1.000
+4 0 0 100 0 0 12 0.0000 4 180 1440 10575 675 Ralf S. Engelschall\001
+4 0 0 100 0 18 20 0.0000 4 270 3840 4275 675 Apache+mod_ssl+OpenSSL\001
+4 0 0 100 0 0 10 0.0000 4 135 1320 10575 855 rse@engelschall.com\001
+4 0 0 100 0 0 10 0.0000 4 135 1410 10575 1035 www.engelschall.com\001
+4 0 0 100 0 0 12 0.0000 4 135 870 900 675 Version 1.3\001
+4 0 0 100 0 0 12 0.0000 4 180 1035 900 855 12-Apr-1999\001
+4 0 0 200 0 20 8 0.0000 4 60 390 6210 4680 ->server\001
+4 0 0 200 0 20 8 0.0000 4 120 855 8280 6120 ap_ctx_get(...,"ssl")\001
+4 0 0 200 0 20 8 0.0000 4 120 1170 7740 2700 ap_get_module_config(...\001
+4 0 0 200 0 20 8 0.0000 4 120 810 7740 2835 ->module_config,\001
+4 0 0 200 0 20 8 0.0000 4 120 585 7740 2970 &ssl_module)\001
+4 0 0 100 0 18 20 0.0000 4 270 1200 9000 8100 Chaining\001
+4 0 0 100 0 18 20 0.0000 4 210 1095 2745 8100 Lifetime\001
+4 0 0 100 0 18 12 0.0000 4 180 1215 810 6255 ap_global_ctx\001
+4 0 0 100 0 18 12 0.0000 4 180 1305 990 5805 SSLModConfig\001
+4 0 0 100 0 18 12 0.0000 4 180 840 4050 4455 SSL_CTX\001
+4 0 0 100 0 18 12 0.0000 4 150 975 4455 5355 server_rec\001
+4 0 0 100 0 18 12 0.0000 4 180 1260 3870 4905 SSLSrvConfig\001
+4 0 0 100 0 18 12 0.0000 4 135 480 1845 4005 BUFF\001
+4 0 0 100 0 18 12 0.0000 4 150 810 2070 3555 conn_rec\001
+4 0 0 100 0 18 12 0.0000 4 135 345 2295 3105 BIO\001
+4 0 0 100 0 18 12 0.0000 4 135 375 2565 2655 SSL\001
+4 0 0 100 0 18 12 0.0000 4 180 1185 3645 1620 SSLDirConfig\001
+4 0 0 100 0 18 12 0.0000 4 180 1065 3915 2070 request_rec\001
+4 0 0 200 0 0 8 0.0000 4 120 1440 900 7560 Startup, Runtime, Shutdown\001
+4 0 0 200 0 0 8 0.0000 4 105 975 1350 7335 Configuration Time\001
+4 0 0 200 0 0 8 0.0000 4 90 1050 2025 7110 Connection Duration\001
+4 0 0 200 0 0 8 0.0000 4 120 885 2835 6885 Request Duration\001
+4 0 0 200 0 18 20 0.0000 4 195 90 6345 6795 t\001
+4 0 0 200 0 20 8 0.0000 4 90 345 7110 5985 ->client\001
+4 0 0 100 0 18 12 0.0000 4 180 1305 6795 2430 SSLModConfig\001
+4 0 0 100 0 18 12 0.0000 4 180 1260 8865 2430 SSLSrvConfig\001
+4 0 0 100 0 18 12 0.0000 4 180 1215 6345 3555 ap_global_ctx\001
+4 0 0 100 0 18 12 0.0000 4 150 975 6345 4455 server_rec\001
+4 0 0 100 0 18 12 0.0000 4 150 810 6345 5355 conn_rec\001
+4 0 0 100 0 18 12 0.0000 4 135 375 9720 5355 SSL\001
+4 0 0 100 0 18 12 0.0000 4 180 1185 10665 2430 SSLDirConfig\001
+4 0 0 100 0 18 12 0.0000 4 135 480 7290 6255 BUFF\001
+4 0 0 100 0 18 12 0.0000 4 180 1305 11025 5355 SSL_METHOD\001
+4 0 0 100 0 18 12 0.0000 4 180 840 11475 4455 SSL_CTX\001
+4 0 0 100 0 18 24 0.0000 4 285 4365 3915 1080 Data Structure Overview\001
+4 0 0 200 0 20 8 0.0000 4 90 615 7065 5085 ->connection\001
+4 0 0 200 0 20 8 0.0000 4 60 390 7065 4770 ->server\001
+4 0 0 200 0 20 8 0.0000 4 120 960 8010 5445 SSL_get_app_data()\001
+4 0 0 200 0 20 8 0.0000 4 120 510 10530 4050 ->pSSLCtx\001
+4 0 0 200 0 20 8 0.0000 4 120 1215 7875 4275 SSL_CTX_get_app_data()\001
+4 0 0 200 0 20 8 0.0000 4 120 1155 10305 5535 SSL_get_current_cipher()\001
+4 0 0 100 0 18 12 0.0000 4 180 1170 11025 5760 SSL_CIPHER\001
+4 0 0 100 0 18 12 0.0000 4 180 1350 10980 6165 SSL_SESSION\001
+4 0 0 200 0 20 8 0.0000 4 120 840 10440 5940 SSL_get_session()\001
+4 0 0 100 0 18 12 0.0000 4 180 1665 10575 7110 X509_STORE_CTX\001
+4 0 0 100 0 18 12 0.0000 4 135 345 9720 7110 BIO\001
+4 0 0 200 0 20 8 0.0000 4 120 840 9540 7335 SSL_get_{r,w}bio()\001
+4 0 0 100 0 18 20 0.0000 4 270 1170 8730 3465 mod_ssl\001
+4 0 0 100 0 18 20 0.0000 4 270 1050 8145 6750 Apache\001
+4 0 0 200 0 20 8 0.0000 4 120 945 10125 4680 SSL_get_SSL_CTX()\001
+4 0 0 200 0 20 8 0.0000 4 120 1170 10350 5175 SSL_get_SSL_METHOD()\001
+4 0 0 200 0 20 8 0.0000 4 90 465 11745 4770 ->method\001
+4 0 0 200 0 20 8 0.0000 4 120 1665 9945 6480 X509_STORE_CTX_get_app_data()\001
+4 0 0 200 0 20 8 0.0000 4 120 1215 10980 6705 SSL_CTX_get_cert_store()\001
+4 0 0 200 0 20 8 0.0000 4 120 1020 8280 5130 modssl_get_app_data2()\001
+4 0 0 100 0 18 20 0.0000 4 270 1290 10710 7605 OpenSSL\001
+4 0 0 100 0 18 12 0.0000 4 180 720 10710 7785 [Crypto]\001
+4 0 0 100 0 18 20 0.0000 4 270 1290 10935 3645 OpenSSL\001
+4 0 0 100 0 18 12 0.0000 4 180 495 10935 3825 [SSL]\001
diff --git a/modules/ssl/README.dsov.ps b/modules/ssl/README.dsov.ps
new file mode 100644
index 0000000..bcbf268
--- /dev/null
+++ b/modules/ssl/README.dsov.ps
@@ -0,0 +1,1138 @@
+%!PS-Adobe-2.0
+%%Title: README.dsov.ps
+%%Creator: fig2dev Version 3.2 Patchlevel 1
+%%CreationDate: Mon Apr 12 17:09:11 1999
+%%For: rse@en1.engelschall.com (Ralf S. Engelschall)
+%%Orientation: Landscape
+%%BoundingBox: 59 37 553 755
+%%Pages: 1
+%%BeginSetup
+%%IncludeFeature: *PageSize Letter
+%%EndSetup
+%%Magnification: 0.9340
+%%EndComments
+/$F2psDict 200 dict def
+$F2psDict begin
+$F2psDict /mtrx matrix put
+/col-1 {0 setgray} bind def
+/col0 {0.000 0.000 0.000 srgb} bind def
+/col1 {0.000 0.000 1.000 srgb} bind def
+/col2 {0.000 1.000 0.000 srgb} bind def
+/col3 {0.000 1.000 1.000 srgb} bind def
+/col4 {1.000 0.000 0.000 srgb} bind def
+/col5 {1.000 0.000 1.000 srgb} bind def
+/col6 {1.000 1.000 0.000 srgb} bind def
+/col7 {1.000 1.000 1.000 srgb} bind def
+/col8 {0.000 0.000 0.560 srgb} bind def
+/col9 {0.000 0.000 0.690 srgb} bind def
+/col10 {0.000 0.000 0.820 srgb} bind def
+/col11 {0.530 0.810 1.000 srgb} bind def
+/col12 {0.000 0.560 0.000 srgb} bind def
+/col13 {0.000 0.690 0.000 srgb} bind def
+/col14 {0.000 0.820 0.000 srgb} bind def
+/col15 {0.000 0.560 0.560 srgb} bind def
+/col16 {0.000 0.690 0.690 srgb} bind def
+/col17 {0.000 0.820 0.820 srgb} bind def
+/col18 {0.560 0.000 0.000 srgb} bind def
+/col19 {0.690 0.000 0.000 srgb} bind def
+/col20 {0.820 0.000 0.000 srgb} bind def
+/col21 {0.560 0.000 0.560 srgb} bind def
+/col22 {0.690 0.000 0.690 srgb} bind def
+/col23 {0.820 0.000 0.820 srgb} bind def
+/col24 {0.500 0.190 0.000 srgb} bind def
+/col25 {0.630 0.250 0.000 srgb} bind def
+/col26 {0.750 0.380 0.000 srgb} bind def
+/col27 {1.000 0.500 0.500 srgb} bind def
+/col28 {1.000 0.630 0.630 srgb} bind def
+/col29 {1.000 0.750 0.750 srgb} bind def
+/col30 {1.000 0.880 0.880 srgb} bind def
+/col31 {1.000 0.840 0.000 srgb} bind def
+/col32 {0.380 0.396 0.380 srgb} bind def
+/col33 {0.714 0.698 0.714 srgb} bind def
+/col34 {0.969 0.953 0.969 srgb} bind def
+/col35 {0.812 0.812 0.812 srgb} bind def
+/col36 {1.000 1.000 1.000 srgb} bind def
+
+end
+save
+48.0 12.0 translate
+ 90 rotate
+1 -1 scale
+
+/cp {closepath} bind def
+/ef {eofill} bind def
+/gr {grestore} bind def
+/gs {gsave} bind def
+/sa {save} bind def
+/rs {restore} bind def
+/l {lineto} bind def
+/m {moveto} bind def
+/rm {rmoveto} bind def
+/n {newpath} bind def
+/s {stroke} bind def
+/sh {show} bind def
+/slc {setlinecap} bind def
+/slj {setlinejoin} bind def
+/slw {setlinewidth} bind def
+/srgb {setrgbcolor} bind def
+/rot {rotate} bind def
+/sc {scale} bind def
+/sd {setdash} bind def
+/ff {findfont} bind def
+/sf {setfont} bind def
+/scf {scalefont} bind def
+/sw {stringwidth} bind def
+/tr {translate} bind def
+/tnt {dup dup currentrgbcolor
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
+ bind def
+/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
+ 4 -2 roll mul srgb} bind def
+/reencdict 12 dict def /ReEncode { reencdict begin
+/newcodesandnames exch def /newfontname exch def /basefontname exch def
+/basefontdict basefontname findfont def /newfont basefontdict maxlength dict def
+basefontdict { exch dup /FID ne { dup /Encoding eq
+{ exch dup length array copy newfont 3 1 roll put }
+{ exch newfont 3 1 roll put } ifelse } { pop pop } ifelse } forall
+newfont /FontName newfontname put newcodesandnames aload pop
+128 1 255 { newfont /Encoding get exch /.notdef put } for
+newcodesandnames length 2 idiv { newfont /Encoding get 3 1 roll put } repeat
+newfontname newfont definefont pop end } def
+/isovec [
+8#200 /grave 8#201 /acute 8#202 /circumflex 8#203 /tilde
+8#204 /macron 8#205 /breve 8#206 /dotaccent 8#207 /dieresis
+8#210 /ring 8#211 /cedilla 8#212 /hungarumlaut 8#213 /ogonek 8#214 /caron
+8#220 /dotlessi 8#230 /oe 8#231 /OE
+8#240 /space 8#241 /exclamdown 8#242 /cent 8#243 /sterling
+8#244 /currency 8#245 /yen 8#246 /brokenbar 8#247 /section 8#250 /dieresis
+8#251 /copyright 8#252 /ordfeminine 8#253 /guillemotleft 8#254 /logicalnot
+8#255 /endash 8#256 /registered 8#257 /macron 8#260 /degree 8#261 /plusminus
+8#262 /twosuperior 8#263 /threesuperior 8#264 /acute 8#265 /mu 8#266 /paragraph
+8#267 /periodcentered 8#270 /cedilla 8#271 /onesuperior 8#272 /ordmasculine
+8#273 /guillemotright 8#274 /onequarter 8#275 /onehalf
+8#276 /threequarters 8#277 /questiondown 8#300 /Agrave 8#301 /Aacute
+8#302 /Acircumflex 8#303 /Atilde 8#304 /Adieresis 8#305 /Aring
+8#306 /AE 8#307 /Ccedilla 8#310 /Egrave 8#311 /Eacute
+8#312 /Ecircumflex 8#313 /Edieresis 8#314 /Igrave 8#315 /Iacute
+8#316 /Icircumflex 8#317 /Idieresis 8#320 /Eth 8#321 /Ntilde 8#322 /Ograve
+8#323 /Oacute 8#324 /Ocircumflex 8#325 /Otilde 8#326 /Odieresis 8#327 /multiply
+8#330 /Oslash 8#331 /Ugrave 8#332 /Uacute 8#333 /Ucircumflex
+8#334 /Udieresis 8#335 /Yacute 8#336 /Thorn 8#337 /germandbls 8#340 /agrave
+8#341 /aacute 8#342 /acircumflex 8#343 /atilde 8#344 /adieresis 8#345 /aring
+8#346 /ae 8#347 /ccedilla 8#350 /egrave 8#351 /eacute
+8#352 /ecircumflex 8#353 /edieresis 8#354 /igrave 8#355 /iacute
+8#356 /icircumflex 8#357 /idieresis 8#360 /eth 8#361 /ntilde 8#362 /ograve
+8#363 /oacute 8#364 /ocircumflex 8#365 /otilde 8#366 /odieresis 8#367 /divide
+8#370 /oslash 8#371 /ugrave 8#372 /uacute 8#373 /ucircumflex
+8#374 /udieresis 8#375 /yacute 8#376 /thorn 8#377 /ydieresis] def
+/Times-Roman /Times-Roman-iso isovec ReEncode
+/Helvetica-Bold /Helvetica-Bold-iso isovec ReEncode
+/Helvetica-Narrow /Helvetica-Narrow-iso isovec ReEncode
+/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
+/$F2psEnd {$F2psEnteredState restore end} def
+%%EndProlog
+
+$F2psBegin
+10 setmiterlimit
+n -1000 9572 m -1000 -1000 l 13622 -1000 l 13622 9572 l cp clip
+ 0.05883 0.05883 sc
+%%Page: 1 1
+% Polyline
+7.500 slw
+n 6413 2048 m 6380 2054 l 6348 2061 l 6315 2067 l 6283 2073 l 6250 2079 l
+ 6217 2084 l 6185 2090 l 6152 2095 l 6120 2101 l 6088 2107 l
+ 6057 2113 l 6027 2120 l 5998 2126 l 5970 2134 l 5943 2141 l
+ 5918 2149 l 5894 2158 l 5873 2167 l 5853 2177 l 5835 2187 l
+ 5819 2198 l 5805 2210 l 5793 2222 l 5782 2235 l 5774 2250 l
+ 5768 2265 l 5763 2281 l 5760 2299 l 5759 2318 l 5759 2339 l
+ 5761 2360 l 5764 2383 l 5768 2408 l 5774 2433 l 5780 2460 l
+ 5788 2488 l 5797 2516 l 5806 2546 l 5815 2575 l 5825 2606 l
+ 5836 2636 l 5846 2666 l 5856 2696 l 5866 2726 l 5875 2755 l
+ 5884 2784 l 5892 2812 l 5899 2839 l 5905 2866 l 5910 2891 l
+ 5915 2916 l 5918 2940 l 5919 2968 l 5920 2995 l 5919 3022 l
+ 5916 3048 l 5912 3075 l 5908 3101 l 5902 3127 l 5895 3153 l
+ 5887 3179 l 5880 3205 l 5871 3230 l 5863 3254 l 5855 3278 l
+ 5848 3302 l 5841 3324 l 5834 3346 l 5829 3367 l 5824 3388 l
+ 5821 3408 l 5819 3427 l 5819 3446 l 5820 3465 l 5823 3484 l
+ 5827 3503 l 5833 3522 l 5840 3542 l 5848 3562 l 5858 3582 l
+ 5868 3603 l 5880 3625 l 5891 3647 l 5904 3669 l 5916 3691 l
+ 5929 3713 l 5941 3736 l 5953 3758 l 5964 3779 l 5974 3801 l
+ 5983 3822 l 5991 3843 l 5997 3863 l 6002 3883 l 6006 3903 l
+ 6008 3923 l 6008 3942 l 6006 3962 l 6003 3983 l 5998 4004 l
+ 5992 4025 l 5985 4048 l 5977 4070 l 5968 4094 l 5958 4118 l
+ 5947 4142 l 5936 4167 l 5925 4192 l 5913 4216 l 5902 4241 l
+ 5892 4266 l 5882 4291 l 5872 4315 l 5864 4339 l 5857 4362 l
+ 5851 4386 l 5846 4409 l 5843 4433 l 5840 4456 l 5840 4480 l
+ 5840 4505 l 5842 4530 l 5845 4556 l 5849 4582 l 5854 4609 l
+ 5860 4636 l 5867 4664 l 5875 4692 l 5883 4720 l 5892 4747 l
+ 5901 4774 l 5910 4801 l 5920 4827 l 5929 4852 l 5938 4875 l
+ 5947 4898 l 5955 4920 l 5963 4941 l 5971 4961 l 5978 4980 l
+ 5985 5002 l 5992 5024 l 5999 5046 l 6005 5067 l 6010 5088 l
+ 6016 5109 l 6022 5129 l 6027 5150 l 6033 5170 l 6039 5190 l
+ 6045 5209 l 6052 5228 l 6059 5246 l 6067 5264 l 6075 5281 l
+ 6084 5298 l 6094 5315 l 6105 5333 l 6115 5347 l 6125 5361 l
+ 6137 5376 l 6149 5392 l 6162 5408 l 6176 5425 l 6191 5443 l
+ 6206 5461 l 6221 5480 l 6237 5499 l 6253 5519 l 6269 5539 l
+ 6284 5559 l 6299 5579 l 6313 5599 l 6327 5619 l 6340 5639 l
+ 6352 5659 l 6363 5679 l 6373 5698 l 6382 5718 l 6390 5738 l
+ 6398 5759 l 6404 5782 l 6410 5805 l 6415 5828 l 6420 5852 l
+ 6424 5877 l 6428 5902 l 6431 5927 l 6435 5952 l 6438 5977 l
+ 6442 6001 l 6446 6025 l 6450 6048 l 6455 6069 l 6461 6090 l
+ 6467 6109 l 6474 6127 l 6483 6143 l 6492 6159 l 6503 6173 l
+ 6515 6185 l 6528 6197 l 6543 6209 l 6560 6220 l 6578 6230 l
+ 6598 6240 l 6619 6250 l 6641 6260 l 6663 6270 l 6687 6281 l
+ 6710 6291 l 6733 6302 l 6757 6312 l 6779 6324 l 6801 6335 l
+ 6821 6348 l 6841 6361 l 6859 6374 l 6876 6389 l 6893 6405 l
+ 6906 6421 l 6919 6437 l 6932 6455 l 6944 6475 l 6955 6495 l
+ 6967 6516 l 6979 6538 l 6991 6561 l 7003 6584 l 7015 6608 l
+ 7027 6631 l 7040 6654 l 7053 6677 l 7067 6699 l 7081 6720 l
+ 7096 6739 l 7111 6758 l 7127 6774 l 7144 6789 l 7161 6803 l
+ 7180 6815 l 7200 6825 l 7220 6833 l 7240 6840 l 7263 6845 l
+ 7286 6850 l 7311 6854 l 7338 6857 l 7365 6859 l 7394 6861 l
+ 7424 6862 l 7454 6864 l 7485 6865 l 7516 6866 l 7547 6867 l
+ 7578 6868 l 7609 6870 l 7639 6872 l 7668 6875 l 7696 6879 l
+ 7723 6883 l 7748 6889 l 7773 6895 l 7795 6903 l 7817 6912 l
+ 7838 6923 l 7857 6934 l 7875 6948 l 7892 6963 l 7909 6980 l
+ 7926 6998 l 7941 7017 l 7957 7038 l 7972 7060 l 7987 7083 l
+ 8002 7106 l 8017 7130 l 8031 7154 l 8046 7178 l 8061 7202 l
+ 8075 7225 l 8090 7247 l 8105 7269 l 8120 7289 l 8135 7308 l
+ 8151 7326 l 8167 7342 l 8184 7356 l 8202 7369 l 8220 7380 l
+ 8239 7390 l 8260 7397 l 8282 7404 l 8305 7409 l 8330 7413 l
+ 8356 7416 l 8383 7418 l 8412 7420 l 8441 7420 l 8471 7419 l
+ 8502 7418 l 8534 7417 l 8565 7415 l 8597 7413 l 8629 7411 l
+ 8660 7409 l 8690 7407 l 8720 7405 l 8749 7404 l 8777 7404 l
+ 8804 7404 l 8830 7405 l 8856 7407 l 8880 7410 l 8906 7414 l
+ 8931 7420 l 8956 7427 l 8981 7435 l 9005 7444 l 9029 7455 l
+ 9053 7466 l 9077 7478 l 9100 7491 l 9123 7504 l 9146 7517 l
+ 9168 7531 l 9190 7544 l 9210 7557 l 9230 7570 l 9250 7582 l
+ 9268 7593 l 9286 7604 l 9304 7613 l 9320 7621 l 9336 7629 l
+ 9353 7635 l 9370 7641 l 9388 7645 l 9406 7648 l 9425 7650 l
+ 9444 7652 l 9464 7653 l 9485 7653 l 9508 7653 l 9531 7653 l
+ 9555 7653 l 9579 7653 l 9605 7654 l 9631 7655 l 9658 7656 l
+ 9685 7659 l 9713 7662 l 9742 7666 l 9771 7672 l 9801 7679 l
+ 9833 7688 l 9853 7694 l 9874 7700 l 9895 7708 l 9918 7716 l
+ 9941 7725 l 9966 7734 l 9991 7745 l 10017 7755 l 10045 7767 l
+ 10073 7779 l 10102 7791 l 10132 7804 l 10163 7818 l 10194 7831 l
+ 10227 7845 l 10259 7860 l 10293 7874 l 10326 7889 l 10360 7903 l
+ 10394 7918 l 10429 7932 l 10463 7947 l 10497 7961 l 10531 7974 l
+ 10565 7988 l 10599 8001 l 10633 8013 l 10667 8025 l 10700 8037 l
+ 10733 8049 l 10767 8059 l 10800 8070 l 10834 8080 l 10868 8090 l
+ 10902 8099 l 10937 8108 l 10973 8117 l 11009 8125 l 11045 8133 l
+ 11083 8141 l 11120 8148 l 11158 8155 l 11197 8161 l 11236 8167 l
+ 11275 8172 l 11313 8177 l 11352 8181 l 11391 8184 l 11429 8187 l
+ 11467 8190 l 11504 8191 l 11540 8192 l 11576 8192 l 11610 8192 l
+ 11644 8191 l 11676 8189 l 11707 8187 l 11738 8184 l 11767 8180 l
+ 11794 8176 l 11821 8171 l 11847 8165 l 11871 8159 l 11895 8153 l
+ 11923 8143 l 11950 8133 l 11976 8122 l 12001 8109 l 12025 8096 l
+ 12048 8081 l 12071 8065 l 12092 8048 l 12113 8031 l 12133 8012 l
+ 12153 7992 l 12171 7972 l 12188 7951 l 12205 7930 l 12220 7909 l
+ 12235 7887 l 12248 7865 l 12260 7843 l 12272 7822 l 12282 7800 l
+ 12292 7779 l 12301 7759 l 12309 7739 l 12316 7719 l 12323 7699 l
+ 12330 7680 l 12338 7655 l 12345 7631 l 12352 7607 l 12359 7582 l
+ 12365 7558 l 12371 7533 l 12377 7508 l 12382 7484 l 12388 7460 l
+ 12392 7436 l 12397 7414 l 12401 7391 l 12405 7370 l 12409 7350 l
+ 12412 7331 l 12415 7313 l 12418 7297 l 12421 7281 l 12424 7266 l
+ 12428 7253 l 12432 7234 l 12437 7216 l 12442 7199 l 12446 7183 l
+ 12451 7166 l 12456 7150 l 12460 7134 l 12463 7117 l 12466 7101 l
+ 12468 7086 l 12469 7070 l 12469 7054 l 12467 7037 l 12465 7020 l
+ 12462 7006 l 12459 6991 l 12455 6975 l 12450 6958 l 12445 6940 l
+ 12440 6921 l 12434 6901 l 12428 6880 l 12422 6859 l 12416 6838 l
+ 12411 6817 l 12406 6796 l 12401 6776 l 12397 6756 l 12394 6736 l
+ 12392 6718 l 12390 6700 l 12390 6683 l 12390 6665 l 12392 6649 l
+ 12394 6631 l 12397 6614 l 12401 6597 l 12406 6579 l 12411 6561 l
+ 12416 6542 l 12422 6524 l 12428 6505 l 12434 6487 l 12440 6468 l
+ 12445 6450 l 12450 6432 l 12455 6414 l 12459 6396 l 12462 6378 l
+ 12465 6360 l 12467 6343 l 12468 6326 l 12469 6308 l 12469 6289 l
+ 12468 6269 l 12468 6249 l 12466 6227 l 12464 6205 l 12462 6182 l
+ 12460 6159 l 12457 6135 l 12454 6111 l 12451 6087 l 12447 6063 l
+ 12444 6040 l 12441 6016 l 12437 5993 l 12434 5970 l 12431 5948 l
+ 12428 5925 l 12424 5902 l 12421 5879 l 12419 5855 l 12416 5831 l
+ 12413 5806 l 12411 5781 l 12408 5755 l 12406 5729 l 12404 5702 l
+ 12403 5676 l 12401 5651 l 12400 5625 l 12400 5601 l 12399 5578 l
+ 12399 5555 l 12400 5534 l 12401 5514 l 12402 5495 l 12403 5477 l
+ 12405 5460 l 12408 5440 l 12411 5421 l 12416 5402 l 12420 5384 l
+ 12426 5365 l 12431 5347 l 12437 5329 l 12444 5311 l 12450 5293 l
+ 12456 5275 l 12462 5258 l 12468 5240 l 12474 5222 l 12479 5205 l
+ 12483 5186 l 12488 5168 l 12490 5152 l 12493 5135 l 12496 5117 l
+ 12498 5099 l 12500 5079 l 12502 5058 l 12504 5036 l 12506 5014 l
+ 12507 4990 l 12509 4966 l 12510 4942 l 12512 4918 l 12513 4893 l
+ 12515 4869 l 12516 4845 l 12518 4822 l 12520 4799 l 12521 4776 l
+ 12523 4754 l 12525 4733 l 12527 4713 l 12529 4693 l 12531 4673 l
+ 12534 4653 l 12536 4632 l 12539 4610 l 12541 4588 l 12543 4566 l
+ 12546 4543 l 12548 4520 l 12550 4497 l 12552 4473 l 12553 4450 l
+ 12554 4426 l 12555 4403 l 12555 4380 l 12555 4357 l 12555 4334 l
+ 12554 4312 l 12552 4290 l 12550 4267 l 12548 4245 l 12545 4224 l
+ 12541 4203 l 12537 4181 l 12533 4159 l 12528 4136 l 12523 4112 l
+ 12517 4088 l 12510 4064 l 12503 4038 l 12496 4013 l 12488 3987 l
+ 12479 3961 l 12471 3935 l 12462 3909 l 12452 3884 l 12443 3859 l
+ 12434 3835 l 12424 3811 l 12415 3788 l 12405 3766 l 12396 3744 l
+ 12386 3723 l 12377 3702 l 12368 3683 l 12357 3661 l 12347 3640 l
+ 12336 3619 l 12325 3598 l 12314 3576 l 12303 3555 l 12291 3533 l
+ 12280 3511 l 12269 3489 l 12257 3467 l 12246 3446 l 12235 3424 l
+ 12225 3402 l 12215 3381 l 12206 3360 l 12197 3340 l 12189 3320 l
+ 12181 3301 l 12174 3281 l 12168 3262 l 12162 3244 l 12158 3225 l
+ 12153 3204 l 12149 3183 l 12145 3162 l 12142 3139 l 12140 3117 l
+ 12138 3094 l 12137 3071 l 12137 3047 l 12138 3024 l 12139 3001 l
+ 12141 2978 l 12143 2956 l 12146 2935 l 12150 2915 l 12154 2896 l
+ 12158 2879 l 12163 2862 l 12168 2847 l 12174 2833 l 12180 2820 l
+ 12188 2805 l 12197 2792 l 12206 2779 l 12216 2766 l 12227 2754 l
+ 12238 2742 l 12249 2730 l 12260 2717 l 12272 2704 l 12282 2691 l
+ 12292 2676 l 12302 2661 l 12310 2645 l 12318 2627 l 12324 2608 l
+ 12330 2588 l 12334 2571 l 12336 2553 l 12339 2534 l 12341 2513 l
+ 12342 2491 l 12343 2467 l 12343 2442 l 12342 2416 l 12340 2389 l
+ 12338 2360 l 12335 2332 l 12331 2303 l 12326 2273 l 12320 2244 l
+ 12314 2215 l 12307 2187 l 12299 2159 l 12290 2132 l 12280 2106 l
+ 12270 2081 l 12259 2056 l 12248 2033 l 12236 2011 l 12224 1990 l
+ 12210 1970 l 12196 1949 l 12181 1929 l 12164 1910 l 12147 1890 l
+ 12129 1871 l 12110 1853 l 12090 1835 l 12070 1818 l 12049 1802 l
+ 12027 1787 l 12005 1773 l 11983 1761 l 11961 1749 l 11939 1739 l
+ 11917 1730 l 11895 1722 l 11874 1716 l 11852 1710 l 11831 1707 l
+ 11811 1704 l 11790 1703 l 11769 1702 l 11748 1703 l 11727 1705 l
+ 11706 1708 l 11683 1711 l 11660 1716 l 11636 1721 l 11612 1727 l
+ 11587 1733 l 11560 1740 l 11534 1747 l 11506 1754 l 11479 1761 l
+ 11450 1768 l 11422 1774 l 11393 1780 l 11364 1786 l 11334 1791 l
+ 11305 1795 l 11275 1798 l 11245 1800 l 11215 1801 l 11184 1801 l
+ 11153 1800 l 11128 1798 l 11104 1796 l 11078 1793 l 11052 1790 l
+ 11025 1785 l 10997 1781 l 10968 1776 l 10939 1770 l 10908 1764 l
+ 10877 1758 l 10844 1751 l 10811 1744 l 10778 1737 l 10743 1730 l
+ 10708 1722 l 10673 1715 l 10637 1708 l 10601 1701 l 10565 1695 l
+ 10530 1688 l 10494 1682 l 10458 1677 l 10422 1672 l 10387 1668 l
+ 10352 1664 l 10318 1661 l 10284 1658 l 10250 1657 l 10216 1656 l
+ 10183 1655 l 10150 1656 l 10118 1658 l 10087 1660 l 10055 1663 l
+ 10024 1666 l 9992 1671 l 9960 1676 l 9927 1682 l 9894 1688 l
+ 9861 1695 l 9827 1703 l 9792 1711 l 9757 1720 l 9721 1729 l
+ 9685 1738 l 9649 1748 l 9613 1757 l 9576 1767 l 9539 1778 l
+ 9502 1788 l 9465 1798 l 9429 1807 l 9392 1817 l 9356 1826 l
+ 9320 1835 l 9285 1844 l 9250 1852 l 9216 1860 l 9182 1867 l
+ 9148 1873 l 9115 1879 l 9082 1884 l 9050 1889 l 9018 1892 l
+ 8987 1895 l 8955 1898 l 8919 1899 l 8883 1900 l 8847 1899 l
+ 8811 1898 l 8774 1896 l 8737 1893 l 8699 1889 l 8661 1884 l
+ 8623 1878 l 8585 1872 l 8546 1865 l 8508 1857 l 8470 1849 l
+ 8432 1840 l 8395 1830 l 8358 1821 l 8322 1811 l 8287 1801 l
+ 8254 1790 l 8221 1780 l 8189 1770 l 8159 1760 l 8130 1750 l
+ 8102 1740 l 8076 1730 l 8051 1721 l 8028 1712 l 8006 1703 l
+ 7985 1695 l 7965 1688 l 7931 1674 l 7899 1662 l 7871 1650 l
+ 7844 1640 l 7820 1631 l 7798 1623 l 7778 1617 l 7760 1611 l
+ 7743 1607 l 7728 1603 l 7715 1601 l 7702 1600 l 7691 1600 l
+ 7680 1601 l 7669 1603 l 7658 1605 l 7648 1607 l 7638 1610 l
+ 7627 1613 l 7615 1617 l 7601 1621 l 7587 1626 l 7571 1632 l
+ 7554 1638 l 7536 1645 l 7517 1653 l 7496 1661 l 7474 1670 l
+ 7452 1679 l 7428 1689 l 7403 1699 l 7378 1709 l 7352 1720 l
+ 7325 1731 l 7297 1743 l 7268 1755 l 7247 1763 l 7226 1772 l
+ 7204 1781 l 7182 1790 l 7158 1800 l 7133 1810 l 7108 1820 l
+ 7081 1831 l 7053 1842 l 7025 1853 l 6996 1864 l 6966 1875 l
+ 6935 1886 l 6904 1898 l 6873 1909 l 6841 1921 l 6809 1932 l
+ 6776 1943 l 6744 1954 l 6712 1964 l 6680 1974 l 6649 1984 l
+ 6618 1994 l 6587 2003 l 6557 2011 l 6527 2019 l 6498 2027 l
+ 6469 2034 l 6441 2041 l cp gs col34 1.00 shd ef gr gs col34 s gr
+% Polyline
+n 675 6525 m 5850 6525 l 5850 6075 l 5625 6075 l 5625 5625 l 900 5625 l
+ 900 6075 l 675 6075 l cp gs col7 1.00 shd ef gr gs col7 s gr
+% Polyline
+n 1125 6525 m 5355 6525 l 5400 5175 l 5175 5175 l 5175 4725 l 4950 4725 l
+ 4950 4275 l 1575 4275 l 1575 4725 l 1350 4725 l 1350 5175 l
+ 1125 5175 l cp gs col34 1.00 shd ef gr gs col34 s gr
+% Polyline
+75.000 slw
+n 9450 4500 m 12465 2205 l gs col7 s gr
+% Polyline
+n 9450 4500 m 9450 7785 l gs col7 s gr
+% Polyline
+n 9450 4500 m 6075 1935 l gs col7 s gr
+% Polyline
+n 12510 6435 m 9450 6435 l gs col7 s gr
+% Polyline
+7.500 slw
+n 1800 6525 m 4725 6525 l 4725 3825 l 4500 3825 l 4500 3375 l 4275 3375 l
+ 4275 2925 l 4050 2925 l 4050 2475 l 2475 2475 l 2475 2925 l
+ 2250 2925 l 2250 3375 l 2025 3375 l 2025 3825 l 1800 3825 l
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 2700 6525 m 3825 6525 l 3825 2025 l 3600 2025 l 3600 1575 l 2925 1575 l
+ 2925 2025 l 2700 2025 l cp gs col33 1.00 shd ef gr gs col33 s gr
+% Polyline
+gs clippath
+12068 6810 m 11970 6885 l 12022 6773 l 11937 6878 l 11984 6915 l cp
+clip
+n 12375 4455 m 12510 4635 l 12510 6210 l 11970 6885 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 12068 6810 m 11970 6885 l 12022 6773 l 12045 6791 l 12068 6810 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+7113 6004 m 7155 6120 l 7063 6037 l 7138 6149 l 7188 6116 l cp
+clip
+n 6705 5445 m 7155 6120 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7113 6004 m 7155 6120 l 7063 6037 l 7088 6020 l 7113 6004 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+7304 4656 m 7200 4590 l 7323 4599 l 7195 4557 l 7176 4614 l cp
+clip
+n 7875 4815 m 7200 4590 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7304 4656 m 7200 4590 l 7323 4599 l 7314 4628 l 7304 4656 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+11405 4128 m 11475 4230 l 11365 4173 l 11466 4262 l 11506 4217 l cp
+clip
+n 9585 2565 m 11475 4230 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 11405 4128 m 11475 4230 l 11365 4173 l 11385 4151 l 11405 4128 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+11712 4556 m 11835 4545 l 11732 4613 l 11859 4568 l 11839 4512 l cp
+clip
+n 10170 5130 m 11835 4545 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 11712 4556 m 11835 4545 l 11732 4613 l 11722 4585 l 11712 4556 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+9732 5411 m 9855 5400 l 9752 5468 l 9879 5423 l 9859 5367 l cp
+clip
+n 7920 6075 m 9855 5400 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 9732 5411 m 9855 5400 l 9752 5468 l 9742 5440 l 9732 5411 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+10823 5573 m 10935 5625 l 10812 5632 l 10944 5657 l 10955 5598 l cp
+clip
+n 9990 5445 m 10935 5625 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 10823 5573 m 10935 5625 l 10812 5632 l 10817 5603 l 10823 5573 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+10815 5280 m 10935 5310 l 10815 5340 l 10950 5340 l 10950 5280 l cp
+clip
+n 10215 5310 m 10935 5310 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 10815 5280 m 10935 5310 l 10815 5340 l 10815 5310 l 10815 5280 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+11955 4965 m 11925 5085 l 11895 4965 l 11895 5100 l 11955 5100 l cp
+clip
+n 11925 4590 m 11925 5085 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 11955 4965 m 11925 5085 l 11895 4965 l 11925 4965 l 11955 4965 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+9840 6720 m 9810 6840 l 9780 6720 l 9780 6855 l 9840 6855 l cp
+clip
+n 9810 5490 m 9810 6840 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 9840 6720 m 9810 6840 l 9780 6720 l 9810 6720 l 9840 6720 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+10847 5943 m 10935 6030 l 10816 5995 l 10933 6063 l 10963 6012 l cp
+clip
+n 9945 5445 m 10935 6030 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 10847 5943 m 10935 6030 l 10816 5995 l 10832 5969 l 10847 5943 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+10698 2634 m 10800 2565 l 10742 2674 l 10832 2574 l 10788 2534 l cp
+clip
+n 8865 4725 m 10800 2565 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 10698 2634 m 10800 2565 l 10742 2674 l 10720 2654 l 10698 2634 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+30.000 slw
+n 675 6075 m 5850 6075 l gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+7.500 slw
+ [15 15] 15 sd
+gs clippath
+645 6195 m 675 6075 l 705 6195 l 705 6060 l 645 6060 l cp
+clip
+n 675 6525 m 675 6075 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 645 6195 m 675 6075 l 705 6195 l 675 6195 l 645 6195 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+5880 6405 m 5850 6525 l 5820 6405 l 5820 6540 l 5880 6540 l cp
+clip
+n 5850 6075 m 5850 6525 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 5880 6405 m 5850 6525 l 5820 6405 l 5850 6405 l 5880 6405 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+30.000 slw
+n 900 5625 m 5625 5625 l gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+n 1125 5175 m 5400 5175 l gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+n 1350 4725 m 5175 4725 l gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+n 1575 4275 m 4950 4275 l gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+n 1800 3825 m 4725 3825 l gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+n 2025 3375 m 4500 3375 l gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+n 2250 2925 m 4275 2925 l gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+n 2475 2475 m 4050 2475 l gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+n 2700 2025 m 3825 2025 l gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+n 2925 1575 m 3600 1575 l gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+7.500 slw
+ [15 15] 15 sd
+gs clippath
+870 5745 m 900 5625 l 930 5745 l 930 5610 l 870 5610 l cp
+clip
+n 900 6075 m 900 5625 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 870 5745 m 900 5625 l 930 5745 l 900 5745 l 870 5745 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+1095 5295 m 1125 5175 l 1155 5295 l 1155 5160 l 1095 5160 l cp
+clip
+n 1125 6525 m 1125 5175 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 1095 5295 m 1125 5175 l 1155 5295 l 1125 5295 l 1095 5295 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+1320 4845 m 1350 4725 l 1380 4845 l 1380 4710 l 1320 4710 l cp
+clip
+n 1350 5175 m 1350 4725 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 1320 4845 m 1350 4725 l 1380 4845 l 1350 4845 l 1320 4845 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+1545 4395 m 1575 4275 l 1605 4395 l 1605 4260 l 1545 4260 l cp
+clip
+n 1575 4725 m 1575 4275 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 1545 4395 m 1575 4275 l 1605 4395 l 1575 4395 l 1545 4395 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+1770 3945 m 1800 3825 l 1830 3945 l 1830 3810 l 1770 3810 l cp
+clip
+n 1800 6525 m 1800 3825 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 1770 3945 m 1800 3825 l 1830 3945 l 1800 3945 l 1770 3945 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+1995 3495 m 2025 3375 l 2055 3495 l 2055 3360 l 1995 3360 l cp
+clip
+n 2025 3825 m 2025 3375 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 1995 3495 m 2025 3375 l 2055 3495 l 2025 3495 l 1995 3495 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+2220 3045 m 2250 2925 l 2280 3045 l 2280 2910 l 2220 2910 l cp
+clip
+n 2250 3375 m 2250 2925 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 2220 3045 m 2250 2925 l 2280 3045 l 2250 3045 l 2220 3045 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+2445 2595 m 2475 2475 l 2505 2595 l 2505 2460 l 2445 2460 l cp
+clip
+n 2475 2925 m 2475 2475 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 2445 2595 m 2475 2475 l 2505 2595 l 2475 2595 l 2445 2595 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+5655 5955 m 5625 6075 l 5595 5955 l 5595 6090 l 5655 6090 l cp
+clip
+n 5625 5625 m 5625 6075 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 5655 5955 m 5625 6075 l 5595 5955 l 5625 5955 l 5655 5955 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+5430 6405 m 5400 6525 l 5370 6405 l 5370 6540 l 5430 6540 l cp
+clip
+n 5400 5175 m 5400 6525 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 5430 6405 m 5400 6525 l 5370 6405 l 5400 6405 l 5430 6405 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+5205 5055 m 5175 5175 l 5145 5055 l 5145 5190 l 5205 5190 l cp
+clip
+n 5175 4725 m 5175 5175 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 5205 5055 m 5175 5175 l 5145 5055 l 5175 5055 l 5205 5055 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+4980 4605 m 4950 4725 l 4920 4605 l 4920 4740 l 4980 4740 l cp
+clip
+n 4950 4275 m 4950 4725 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4980 4605 m 4950 4725 l 4920 4605 l 4950 4605 l 4980 4605 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+4755 6405 m 4725 6525 l 4695 6405 l 4695 6540 l 4755 6540 l cp
+clip
+n 4725 3825 m 4725 6525 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4755 6405 m 4725 6525 l 4695 6405 l 4725 6405 l 4755 6405 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+4530 3705 m 4500 3825 l 4470 3705 l 4470 3840 l 4530 3840 l cp
+clip
+n 4500 3375 m 4500 3825 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4530 3705 m 4500 3825 l 4470 3705 l 4500 3705 l 4530 3705 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+4305 3255 m 4275 3375 l 4245 3255 l 4245 3390 l 4305 3390 l cp
+clip
+n 4275 2925 m 4275 3375 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4305 3255 m 4275 3375 l 4245 3255 l 4275 3255 l 4305 3255 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+4080 2805 m 4050 2925 l 4020 2805 l 4020 2940 l 4080 2940 l cp
+clip
+n 4050 2475 m 4050 2925 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4080 2805 m 4050 2925 l 4020 2805 l 4050 2805 l 4080 2805 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+2670 2145 m 2700 2025 l 2730 2145 l 2730 2010 l 2670 2010 l cp
+clip
+n 2700 6525 m 2700 2025 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 2670 2145 m 2700 2025 l 2730 2145 l 2700 2145 l 2670 2145 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+3855 6405 m 3825 6525 l 3795 6405 l 3795 6540 l 3855 6540 l cp
+clip
+n 3825 2025 m 3825 6525 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 3855 6405 m 3825 6525 l 3795 6405 l 3825 6405 l 3855 6405 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+3630 1905 m 3600 2025 l 3570 1905 l 3570 2040 l 3630 2040 l cp
+clip
+n 3600 1575 m 3600 2025 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 3630 1905 m 3600 2025 l 3570 1905 l 3600 1905 l 3630 1905 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+2895 1695 m 2925 1575 l 2955 1695 l 2955 1560 l 2895 1560 l cp
+clip
+n 2925 2025 m 2925 1575 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 2895 1695 m 2925 1575 l 2955 1695 l 2925 1695 l 2895 1695 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+45.000 slw
+gs clippath
+6087 6495 m 6207 6525 l 6087 6555 l 6360 6555 l 6360 6495 l cp
+clip
+n 540 6525 m 6300 6525 l gs 0.00 setgray ef gr gs col0 s gr gr
+
+% arrowhead
+n 6087 6495 m 6207 6525 l 6087 6555 l 6087 6525 l 6087 6495 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+7.500 slw
+gs clippath
+3681 6720 m 3825 6750 l 3681 6780 l 3840 6780 l 3840 6720 l cp
+2844 6780 m 2700 6750 l 2844 6720 l 2685 6720 l 2685 6780 l cp
+clip
+n 2700 6750 m 3825 6750 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 2844 6780 m 2700 6750 l 2844 6720 l 2820 6750 l 2844 6780 l cp gs col7 1.00 shd ef gr col0 s
+% arrowhead
+n 3681 6720 m 3825 6750 l 3681 6780 l 3705 6750 l 3681 6720 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+gs clippath
+5256 7170 m 5400 7200 l 5256 7230 l 5415 7230 l 5415 7170 l cp
+1269 7230 m 1125 7200 l 1269 7170 l 1110 7170 l 1110 7230 l cp
+clip
+n 1125 7200 m 5400 7200 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 1269 7230 m 1125 7200 l 1269 7170 l 1245 7200 l 1269 7230 l cp gs col7 1.00 shd ef gr col0 s
+% arrowhead
+n 5256 7170 m 5400 7200 l 5256 7230 l 5280 7200 l 5256 7170 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+gs clippath
+4581 6945 m 4725 6975 l 4581 7005 l 4740 7005 l 4740 6945 l cp
+1944 7005 m 1800 6975 l 1944 6945 l 1785 6945 l 1785 7005 l cp
+clip
+n 1800 6975 m 4725 6975 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 1944 7005 m 1800 6975 l 1944 6945 l 1920 6975 l 1944 7005 l cp gs col7 1.00 shd ef gr col0 s
+% arrowhead
+n 4581 6945 m 4725 6975 l 4581 7005 l 4605 6975 l 4581 6945 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+gs clippath
+5706 7395 m 5850 7425 l 5706 7455 l 5865 7455 l 5865 7395 l cp
+819 7455 m 675 7425 l 819 7395 l 660 7395 l 660 7455 l cp
+clip
+n 675 7425 m 5850 7425 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 819 7455 m 675 7425 l 819 7395 l 795 7425 l 819 7455 l cp gs col7 1.00 shd ef gr col0 s
+% arrowhead
+n 5706 7395 m 5850 7425 l 5706 7455 l 5730 7425 l 5706 7395 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+1 slc
+ [15 45] 45 sd
+n 675 6570 m 675 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 1125 6570 m 1125 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 1800 6570 m 1800 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 2700 6570 m 2700 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 3825 6570 m 3825 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 4725 6570 m 4725 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 5400 6570 m 5400 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 5850 6570 m 5850 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd
+% Polyline
+0 slc
+n 750 225 m 450 225 450 1050 300 arcto 4 {pop} repeat
+ 450 1350 12300 1350 300 arcto 4 {pop} repeat
+ 12600 1350 12600 525 300 arcto 4 {pop} repeat
+ 12600 225 750 225 300 arcto 4 {pop} repeat
+ cp gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+n 8835 2250 m 8775 2250 8775 2415 60 arcto 4 {pop} repeat
+ 8775 2475 10110 2475 60 arcto 4 {pop} repeat
+ 10170 2475 10170 2310 60 arcto 4 {pop} repeat
+ 10170 2250 8835 2250 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 10635 2250 m 10575 2250 10575 2415 60 arcto 4 {pop} repeat
+ 10575 2475 11865 2475 60 arcto 4 {pop} repeat
+ 11925 2475 11925 2310 60 arcto 4 {pop} repeat
+ 11925 2250 10635 2250 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 11490 4275 m 11430 4275 11430 4440 60 arcto 4 {pop} repeat
+ 11430 4500 12315 4500 60 arcto 4 {pop} repeat
+ 12375 4500 12375 4335 60 arcto 4 {pop} repeat
+ 12375 4275 11490 4275 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 11040 5175 m 10980 5175 10980 5340 60 arcto 4 {pop} repeat
+ 10980 5400 12315 5400 60 arcto 4 {pop} repeat
+ 12375 5400 12375 5235 60 arcto 4 {pop} repeat
+ 12375 5175 11040 5175 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 9735 5175 m 9675 5175 9675 5340 60 arcto 4 {pop} repeat
+ 9675 5400 10110 5400 60 arcto 4 {pop} repeat
+ 10170 5400 10170 5235 60 arcto 4 {pop} repeat
+ 10170 5175 9735 5175 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 7260 6075 m 7200 6075 7200 6240 60 arcto 4 {pop} repeat
+ 7200 6300 7815 6300 60 arcto 4 {pop} repeat
+ 7875 6300 7875 6135 60 arcto 4 {pop} repeat
+ 7875 6075 7260 6075 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 6810 2250 m 6750 2250 6750 2415 60 arcto 4 {pop} repeat
+ 6750 2475 8130 2475 60 arcto 4 {pop} repeat
+ 8190 2475 8190 2310 60 arcto 4 {pop} repeat
+ 8190 2250 6810 2250 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 6360 3375 m 6300 3375 6300 3540 60 arcto 4 {pop} repeat
+ 6300 3600 7545 3600 60 arcto 4 {pop} repeat
+ 7605 3600 7605 3435 60 arcto 4 {pop} repeat
+ 7605 3375 6360 3375 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 6360 4275 m 6300 4275 6300 4440 60 arcto 4 {pop} repeat
+ 6300 4500 7275 4500 60 arcto 4 {pop} repeat
+ 7335 4500 7335 4335 60 arcto 4 {pop} repeat
+ 7335 4275 6360 4275 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 6360 5175 m 6300 5175 6300 5340 60 arcto 4 {pop} repeat
+ 6300 5400 7140 5400 60 arcto 4 {pop} repeat
+ 7200 5400 7200 5235 60 arcto 4 {pop} repeat
+ 7200 5175 6360 5175 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+gs clippath
+7365 5340 m 7245 5310 l 7365 5280 l 7230 5280 l 7230 5340 l cp
+clip
+n 9630 5310 m 7245 5310 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7365 5340 m 7245 5310 l 7365 5280 l 7365 5310 l 7365 5340 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+7500 4395 m 7380 4365 l 7500 4335 l 7365 4335 l 7365 4395 l cp
+clip
+n 11385 4365 m 7380 4365 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7500 4395 m 7380 4365 l 7500 4335 l 7500 4365 l 7500 4395 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+n 11040 5580 m 10980 5580 10980 5745 60 arcto 4 {pop} repeat
+ 10980 5805 12180 5805 60 arcto 4 {pop} repeat
+ 12240 5805 12240 5640 60 arcto 4 {pop} repeat
+ 12240 5580 11040 5580 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 11040 5985 m 10980 5985 10980 6150 60 arcto 4 {pop} repeat
+ 10980 6210 12315 6210 60 arcto 4 {pop} repeat
+ 12375 6210 12375 6045 60 arcto 4 {pop} repeat
+ 12375 5985 11040 5985 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+gs clippath
+9958 5554 m 9900 5445 l 10003 5514 l 9912 5414 l 9868 5454 l cp
+clip
+n 11205 6885 m 9900 5445 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 9958 5554 m 9900 5445 l 10003 5514 l 9981 5534 l 9958 5554 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+n 10590 6930 m 10530 6930 10530 7095 60 arcto 4 {pop} repeat
+ 10530 7155 12225 7155 60 arcto 4 {pop} repeat
+ 12285 7155 12285 6990 60 arcto 4 {pop} repeat
+ 12285 6930 10590 6930 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 9690 6930 m 9630 6930 9630 7095 60 arcto 4 {pop} repeat
+ 9630 7155 10110 7155 60 arcto 4 {pop} repeat
+ 10170 7155 10170 6990 60 arcto 4 {pop} repeat
+ 10170 6930 9690 6930 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+/Times-Roman-iso ff 120.00 scf sf
+900 7560 m
+gs 1 -1 sc (Startup, Runtime, Shutdown) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+6345 2970 m
+gs 1 -1 sc (ap_ctx_get\(...,) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10800 2745 m
+gs 1 -1 sc (ap_get_module_config\(...) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10800 2880 m
+gs 1 -1 sc (->per_dir_config,) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10800 3015 m
+gs 1 -1 sc (&ssl_module\)) col0 sh gr
+% Polyline
+n 7980 4770 m 7920 4770 7920 4935 60 arcto 4 {pop} repeat
+ 7920 4995 9075 4995 60 arcto 4 {pop} repeat
+ 9135 4995 9135 4830 60 arcto 4 {pop} repeat
+ 9135 4770 7980 4770 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+gs clippath
+7340 2610 m 7425 2520 l 7393 2639 l 7459 2521 l 7406 2492 l cp
+clip
+n 6975 3330 m 7425 2520 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7340 2610 m 7425 2520 l 7393 2639 l 7367 2625 l 7340 2610 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+9336 2569 m 9450 2520 l 9373 2616 l 9480 2535 l 9444 2487 l cp
+clip
+n 7200 4230 m 9450 2520 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 9336 2569 m 9450 2520 l 9373 2616 l 9354 2593 l 9336 2569 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+7321 5196 m 7200 5220 l 7296 5142 l 7174 5199 l 7199 5254 l cp
+clip
+n 7875 4905 m 7200 5220 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7321 5196 m 7200 5220 l 7296 5142 l 7309 5169 l 7321 5196 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+6720 4665 m 6750 4545 l 6780 4665 l 6780 4530 l 6720 4530 l cp
+clip
+n 6750 5130 m 6750 4545 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 6720 4665 m 6750 4545 l 6780 4665 l 6750 4665 l 6720 4665 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+9279 4984 m 9175 4918 l 9298 4927 l 9170 4885 l 9151 4942 l cp
+clip
+n 9850 5143 m 9175 4918 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 9279 4984 m 9175 4918 l 9298 4927 l 9289 4956 l 9279 4984 l cp gs 0.00 setgray ef gr col0 s
+/Helvetica-Narrow-iso ff 120.00 scf sf
+6210 4680 m
+gs 1 -1 sc (->server) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+8280 6120 m
+gs 1 -1 sc (ap_ctx_get\(...,"ssl"\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7740 2700 m
+gs 1 -1 sc (ap_get_module_config\(...) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7740 2835 m
+gs 1 -1 sc (->module_config,) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7740 2970 m
+gs 1 -1 sc (&ssl_module\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+6345 3105 m
+gs 1 -1 sc ("ssl_module"\)) col0 sh gr
+/Times-Roman-iso ff 120.00 scf sf
+1350 7335 m
+gs 1 -1 sc (Configuration Time) col0 sh gr
+/Times-Roman-iso ff 120.00 scf sf
+2025 7110 m
+gs 1 -1 sc (Connection Duration) col0 sh gr
+/Times-Roman-iso ff 120.00 scf sf
+2835 6885 m
+gs 1 -1 sc (Request Duration) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+6345 6795 m
+gs 1 -1 sc (t) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7110 5985 m
+gs 1 -1 sc (->client) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7065 5085 m
+gs 1 -1 sc (->connection) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7065 4770 m
+gs 1 -1 sc (->server) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+8010 5445 m
+gs 1 -1 sc (SSL_get_app_data\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10530 4050 m
+gs 1 -1 sc (->pSSLCtx) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7875 4275 m
+gs 1 -1 sc (SSL_CTX_get_app_data\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10305 5535 m
+gs 1 -1 sc (SSL_get_current_cipher\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10440 5940 m
+gs 1 -1 sc (SSL_get_session\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+9540 7335 m
+gs 1 -1 sc (SSL_get_{r,w}bio\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10125 4680 m
+gs 1 -1 sc (SSL_get_SSL_CTX\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10350 5175 m
+gs 1 -1 sc (SSL_get_SSL_METHOD\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+11745 4770 m
+gs 1 -1 sc (->method) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+9945 6480 m
+gs 1 -1 sc (X509_STORE_CTX_get_app_data\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10980 6705 m
+gs 1 -1 sc (SSL_CTX_get_cert_store\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+8280 5130 m
+gs 1 -1 sc (modssl_get_app_data2\(\)) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+3645 1620 m
+gs 1 -1 sc (SSLDirConfig) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+10935 3645 m
+gs 1 -1 sc (OpenSSL) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+10935 3825 m
+gs 1 -1 sc ([SSL]) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+11025 5760 m
+gs 1 -1 sc (SSL_CIPHER) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+10980 6165 m
+gs 1 -1 sc (SSL_SESSION) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+10710 7605 m
+gs 1 -1 sc (OpenSSL) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+10575 7110 m
+gs 1 -1 sc (X509_STORE_CTX) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+6795 2430 m
+gs 1 -1 sc (SSLModConfig) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+8865 2430 m
+gs 1 -1 sc (SSLSrvConfig) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+6345 3555 m
+gs 1 -1 sc (ap_global_ctx) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+6345 4455 m
+gs 1 -1 sc (server_rec) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+6345 5355 m
+gs 1 -1 sc (conn_rec) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+9720 5355 m
+gs 1 -1 sc (SSL) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+10665 2430 m
+gs 1 -1 sc (SSLDirConfig) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+7290 6255 m
+gs 1 -1 sc (BUFF) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+11025 5355 m
+gs 1 -1 sc (SSL_METHOD) col0 sh gr
+% Polyline
+15.000 slw
+n 750 225 m 450 225 450 8250 300 arcto 4 {pop} repeat
+ 450 8550 12300 8550 300 arcto 4 {pop} repeat
+ 12600 8550 12600 525 300 arcto 4 {pop} repeat
+ 12600 225 750 225 300 arcto 4 {pop} repeat
+ cp gs col0 s gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+11475 4455 m
+gs 1 -1 sc (SSL_CTX) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+8010 4950 m
+gs 1 -1 sc (request_rec) col0 sh gr
+/Times-Roman-iso ff 180.00 scf sf
+10575 675 m
+gs 1 -1 sc (Ralf S. Engelschall) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+4275 675 m
+gs 1 -1 sc (Apache+mod_ssl+OpenSSL) col0 sh gr
+/Times-Roman-iso ff 150.00 scf sf
+10575 855 m
+gs 1 -1 sc (rse@engelschall.com) col0 sh gr
+/Times-Roman-iso ff 150.00 scf sf
+10575 1035 m
+gs 1 -1 sc (www.engelschall.com) col0 sh gr
+/Times-Roman-iso ff 180.00 scf sf
+900 675 m
+gs 1 -1 sc (Version 1.3) col0 sh gr
+/Times-Roman-iso ff 180.00 scf sf
+900 855 m
+gs 1 -1 sc (12-Apr-1999) col0 sh gr
+/Helvetica-Bold-iso ff 360.00 scf sf
+3915 1080 m
+gs 1 -1 sc (Data Structure Overview) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+9720 7110 m
+gs 1 -1 sc (BIO) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+10710 7785 m
+gs 1 -1 sc ([Crypto]) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+8730 3465 m
+gs 1 -1 sc (mod_ssl) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+8145 6750 m
+gs 1 -1 sc (Apache) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+9000 8100 m
+gs 1 -1 sc (Chaining) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+2745 8100 m
+gs 1 -1 sc (Lifetime) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+810 6255 m
+gs 1 -1 sc (ap_global_ctx) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+990 5805 m
+gs 1 -1 sc (SSLModConfig) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+4050 4455 m
+gs 1 -1 sc (SSL_CTX) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+4455 5355 m
+gs 1 -1 sc (server_rec) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+3870 4905 m
+gs 1 -1 sc (SSLSrvConfig) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+1845 4005 m
+gs 1 -1 sc (BUFF) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+2070 3555 m
+gs 1 -1 sc (conn_rec) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+2295 3105 m
+gs 1 -1 sc (BIO) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+2565 2655 m
+gs 1 -1 sc (SSL) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+3915 2070 m
+gs 1 -1 sc (request_rec) col0 sh gr
+$F2psEnd
+rs
+showpage
diff --git a/modules/ssl/config.m4 b/modules/ssl/config.m4
new file mode 100644
index 0000000..45eeb43
--- /dev/null
+++ b/modules/ssl/config.m4
@@ -0,0 +1,57 @@
+dnl Licensed to the Apache Software Foundation (ASF) under one or more
+dnl contributor license agreements. See the NOTICE file distributed with
+dnl this work for additional information regarding copyright ownership.
+dnl The ASF licenses this file to You under the Apache License, Version 2.0
+dnl (the "License"); you may not use this file except in compliance with
+dnl the License. You may obtain a copy of the License at
+dnl
+dnl http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+
+dnl # start of module specific part
+APACHE_MODPATH_INIT(ssl)
+
+dnl # list of module object files
+ssl_objs="dnl
+mod_ssl.lo dnl
+ssl_engine_config.lo dnl
+ssl_engine_init.lo dnl
+ssl_engine_io.lo dnl
+ssl_engine_kernel.lo dnl
+ssl_engine_log.lo dnl
+ssl_engine_mutex.lo dnl
+ssl_engine_pphrase.lo dnl
+ssl_engine_rand.lo dnl
+ssl_engine_vars.lo dnl
+ssl_scache.lo dnl
+ssl_util_stapling.lo dnl
+ssl_util.lo dnl
+ssl_util_ssl.lo dnl
+ssl_engine_ocsp.lo dnl
+ssl_util_ocsp.lo dnl
+"
+dnl # hook module into the Autoconf mechanism (--enable-ssl option)
+APACHE_MODULE(ssl, [SSL/TLS support (mod_ssl)], $ssl_objs, , most, [
+ APACHE_CHECK_OPENSSL
+ if test "$ac_cv_openssl" = "yes" ; then
+ if test "x$enable_ssl" = "xshared"; then
+ # The only symbol which needs to be exported is the module
+ # structure, so ask libtool to hide everything else:
+ APR_ADDTO(MOD_SSL_LDADD, [-export-symbols-regex ssl_module])
+ fi
+ else
+ enable_ssl=no
+ fi
+])
+
+# Ensure that other modules can pick up mod_ssl.h
+APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/$modpath_current])
+
+dnl # end of module specific part
+APACHE_MODPATH_FINISH
+
diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c
new file mode 100644
index 0000000..5b8c4d5
--- /dev/null
+++ b/modules/ssl/mod_ssl.c
@@ -0,0 +1,775 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+/* _ _
+ * _ __ ___ ___ __| | ___ ___| | mod_ssl
+ * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| | \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ * |_____|
+ * mod_ssl.c
+ * Apache API interface structures
+ */
+
+#include "ssl_private.h"
+#include "mod_ssl.h"
+#include "mod_ssl_openssl.h"
+#include "util_md5.h"
+#include "util_mutex.h"
+#include "ap_provider.h"
+#include "http_config.h"
+
+#include "mod_proxy.h" /* for proxy_hook_section_post_config() */
+
+#include <assert.h>
+
+static int modssl_running_statically = 0;
+
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, pre_handshake,
+ (conn_rec *c,SSL *ssl,int is_proxy),
+ (c,ssl,is_proxy), OK, DECLINED);
+
+/*
+ * the table of configuration directives we provide
+ */
+
+#define SSL_CMD_ALL(name, args, desc) \
+ AP_INIT_##args("SSL"#name, ssl_cmd_SSL##name, \
+ NULL, RSRC_CONF|OR_AUTHCFG, desc),
+
+#define SSL_CMD_SRV(name, args, desc) \
+ AP_INIT_##args("SSL"#name, ssl_cmd_SSL##name, \
+ NULL, RSRC_CONF, desc),
+
+#define SSL_CMD_PXY(name, args, desc) \
+ AP_INIT_##args("SSL"#name, ssl_cmd_SSL##name, \
+ NULL, RSRC_CONF|PROXY_CONF, desc),
+
+#define SSL_CMD_DIR(name, type, args, desc) \
+ AP_INIT_##args("SSL"#name, ssl_cmd_SSL##name, \
+ NULL, OR_##type, desc),
+
+#define AP_END_CMD { NULL }
+
+static const command_rec ssl_config_cmds[] = {
+ /*
+ * Global (main-server) context configuration directives
+ */
+ SSL_CMD_SRV(PassPhraseDialog, TAKE1,
+ "SSL dialog mechanism for the pass phrase query "
+ "('builtin', '|/path/to/pipe_program', "
+ "or 'exec:/path/to/cgi_program')")
+ SSL_CMD_SRV(SessionCache, TAKE1,
+ "SSL Session Cache storage "
+ "('none', 'nonenotnull', 'dbm:/path/to/file')")
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
+ SSL_CMD_SRV(CryptoDevice, TAKE1,
+ "SSL external Crypto Device usage "
+ "('builtin', '...')")
+#endif
+ SSL_CMD_SRV(RandomSeed, TAKE23,
+ "SSL Pseudo Random Number Generator (PRNG) seeding source "
+ "('startup|connect builtin|file:/path|exec:/path [bytes]')")
+
+ /*
+ * Per-server context configuration directives
+ */
+ SSL_CMD_SRV(Engine, TAKE1,
+ "SSL switch for the protocol engine "
+ "('on', 'off')")
+ SSL_CMD_SRV(FIPS, FLAG,
+ "Enable FIPS-140 mode "
+ "(`on', `off')")
+ SSL_CMD_ALL(CipherSuite, TAKE12,
+ "Colon-delimited list of permitted SSL Ciphers, optional preceded "
+ "by protocol identifier ('XXX:...:XXX' - see manual)")
+ SSL_CMD_SRV(CertificateFile, TAKE1,
+ "SSL Server Certificate file "
+ "('/path/to/file' - PEM or DER encoded)")
+ SSL_CMD_SRV(CertificateKeyFile, TAKE1,
+ "SSL Server Private Key file "
+ "('/path/to/file' - PEM or DER encoded)")
+ SSL_CMD_SRV(CertificateChainFile, TAKE1,
+ "SSL Server CA Certificate Chain file "
+ "('/path/to/file' - PEM encoded)")
+#ifdef HAVE_TLS_SESSION_TICKETS
+ SSL_CMD_SRV(SessionTicketKeyFile, TAKE1,
+ "TLS session ticket encryption/decryption key file (RFC 5077) "
+ "('/path/to/file' - file with 48 bytes of random data)")
+#endif
+ SSL_CMD_ALL(CACertificatePath, TAKE1,
+ "SSL CA Certificate path "
+ "('/path/to/dir' - contains PEM encoded files)")
+ SSL_CMD_ALL(CACertificateFile, TAKE1,
+ "SSL CA Certificate file "
+ "('/path/to/file' - PEM encoded)")
+ SSL_CMD_SRV(CADNRequestPath, TAKE1,
+ "SSL CA Distinguished Name path "
+ "('/path/to/dir' - symlink hashes to PEM of acceptable CA names to request)")
+ SSL_CMD_SRV(CADNRequestFile, TAKE1,
+ "SSL CA Distinguished Name file "
+ "('/path/to/file' - PEM encoded to derive acceptable CA names to request)")
+ SSL_CMD_SRV(CARevocationPath, TAKE1,
+ "SSL CA Certificate Revocation List (CRL) path "
+ "('/path/to/dir' - contains PEM encoded files)")
+ SSL_CMD_SRV(CARevocationFile, TAKE1,
+ "SSL CA Certificate Revocation List (CRL) file "
+ "('/path/to/file' - PEM encoded)")
+ SSL_CMD_SRV(CARevocationCheck, RAW_ARGS,
+ "SSL CA Certificate Revocation List (CRL) checking mode")
+ SSL_CMD_ALL(VerifyClient, TAKE1,
+ "SSL Client verify type "
+ "('none', 'optional', 'require', 'optional_no_ca')")
+ SSL_CMD_ALL(VerifyDepth, TAKE1,
+ "SSL Client verify depth "
+ "('N' - number of intermediate certificates)")
+ SSL_CMD_SRV(SessionCacheTimeout, TAKE1,
+ "SSL Session Cache object lifetime "
+ "('N' - number of seconds)")
+#ifdef OPENSSL_NO_SSL3
+#define SSLv3_PROTO_PREFIX ""
+#else
+#define SSLv3_PROTO_PREFIX "SSLv3|"
+#endif
+#ifdef HAVE_TLSV1_X
+#define SSL_PROTOCOLS SSLv3_PROTO_PREFIX "TLSv1|TLSv1.1|TLSv1.2"
+#else
+#define SSL_PROTOCOLS SSLv3_PROTO_PREFIX "TLSv1"
+#endif
+ SSL_CMD_SRV(Protocol, RAW_ARGS,
+ "Enable or disable various SSL protocols "
+ "('[+-][" SSL_PROTOCOLS "] ...' - see manual)")
+ SSL_CMD_SRV(HonorCipherOrder, FLAG,
+ "Use the server's cipher ordering preference")
+ SSL_CMD_SRV(Compression, FLAG,
+ "Enable SSL level compression "
+ "(`on', `off')")
+ SSL_CMD_SRV(SessionTickets, FLAG,
+ "Enable or disable TLS session tickets"
+ "(`on', `off')")
+ SSL_CMD_SRV(InsecureRenegotiation, FLAG,
+ "Enable support for insecure renegotiation")
+ SSL_CMD_ALL(UserName, TAKE1,
+ "Set user name to SSL variable value")
+ SSL_CMD_SRV(StrictSNIVHostCheck, FLAG,
+ "Strict SNI virtual host checking")
+
+#ifdef HAVE_SRP
+ SSL_CMD_SRV(SRPVerifierFile, TAKE1,
+ "SRP verifier file "
+ "('/path/to/file' - created by srptool)")
+ SSL_CMD_SRV(SRPUnknownUserSeed, TAKE1,
+ "SRP seed for unknown users (to avoid leaking a user's existence) "
+ "('some secret text')")
+#endif
+
+ /*
+ * Proxy configuration for remote SSL connections
+ */
+ SSL_CMD_PXY(ProxyEngine, FLAG,
+ "SSL switch for the proxy protocol engine "
+ "('on', 'off')")
+ SSL_CMD_PXY(ProxyProtocol, RAW_ARGS,
+ "SSL Proxy: enable or disable SSL protocol flavors "
+ "('[+-][" SSL_PROTOCOLS "] ...' - see manual)")
+ SSL_CMD_PXY(ProxyCipherSuite, TAKE12,
+ "SSL Proxy: colon-delimited list of permitted SSL ciphers "
+ ", optionally preceded by protocol specifier ('XXX:...:XXX' - see manual)")
+ SSL_CMD_PXY(ProxyVerify, TAKE1,
+ "SSL Proxy: whether to verify the remote certificate "
+ "('on' or 'off')")
+ SSL_CMD_PXY(ProxyVerifyDepth, TAKE1,
+ "SSL Proxy: maximum certificate verification depth "
+ "('N' - number of intermediate certificates)")
+ SSL_CMD_PXY(ProxyCACertificateFile, TAKE1,
+ "SSL Proxy: file containing server certificates "
+ "('/path/to/file' - PEM encoded certificates)")
+ SSL_CMD_PXY(ProxyCACertificatePath, TAKE1,
+ "SSL Proxy: directory containing server certificates "
+ "('/path/to/dir' - contains PEM encoded certificates)")
+ SSL_CMD_PXY(ProxyCARevocationPath, TAKE1,
+ "SSL Proxy: CA Certificate Revocation List (CRL) path "
+ "('/path/to/dir' - contains PEM encoded files)")
+ SSL_CMD_PXY(ProxyCARevocationFile, TAKE1,
+ "SSL Proxy: CA Certificate Revocation List (CRL) file "
+ "('/path/to/file' - PEM encoded)")
+ SSL_CMD_PXY(ProxyCARevocationCheck, RAW_ARGS,
+ "SSL Proxy: CA Certificate Revocation List (CRL) checking mode")
+ SSL_CMD_PXY(ProxyMachineCertificateFile, TAKE1,
+ "SSL Proxy: file containing client certificates "
+ "('/path/to/file' - PEM encoded certificates)")
+ SSL_CMD_PXY(ProxyMachineCertificatePath, TAKE1,
+ "SSL Proxy: directory containing client certificates "
+ "('/path/to/dir' - contains PEM encoded certificates)")
+ SSL_CMD_PXY(ProxyMachineCertificateChainFile, TAKE1,
+ "SSL Proxy: file containing issuing certificates "
+ "of the client certificate "
+ "(`/path/to/file' - PEM encoded certificates)")
+ SSL_CMD_PXY(ProxyCheckPeerExpire, FLAG,
+ "SSL Proxy: check the peer certificate's expiration date")
+ SSL_CMD_PXY(ProxyCheckPeerCN, FLAG,
+ "SSL Proxy: check the peer certificate's CN")
+ SSL_CMD_PXY(ProxyCheckPeerName, FLAG,
+ "SSL Proxy: check the peer certificate's name "
+ "(must be present in subjectAltName extension or CN")
+
+ /*
+ * Per-directory context configuration directives
+ */
+ SSL_CMD_DIR(Options, OPTIONS, RAW_ARGS,
+ "Set one or more options to configure the SSL engine"
+ "('[+-]option[=value] ...' - see manual)")
+ SSL_CMD_DIR(RequireSSL, AUTHCFG, NO_ARGS,
+ "Require the SSL protocol for the per-directory context "
+ "(no arguments)")
+ SSL_CMD_DIR(Require, AUTHCFG, RAW_ARGS,
+ "Require a boolean expression to evaluate to true for granting access"
+ "(arbitrary complex boolean expression - see manual)")
+ SSL_CMD_DIR(RenegBufferSize, AUTHCFG, TAKE1,
+ "Configure the amount of memory that will be used for buffering the "
+ "request body if a per-location SSL renegotiation is required due to "
+ "changed access control requirements")
+
+ SSL_CMD_SRV(OCSPEnable, RAW_ARGS,
+ "Enable use of OCSP to verify certificate revocation mode ('on', 'leaf', 'off')")
+ SSL_CMD_SRV(OCSPDefaultResponder, TAKE1,
+ "URL of the default OCSP Responder")
+ SSL_CMD_SRV(OCSPOverrideResponder, FLAG,
+ "Force use of the default responder URL ('on', 'off')")
+ SSL_CMD_SRV(OCSPResponseTimeSkew, TAKE1,
+ "Maximum time difference in OCSP responses")
+ SSL_CMD_SRV(OCSPResponseMaxAge, TAKE1,
+ "Maximum age of OCSP responses")
+ SSL_CMD_SRV(OCSPResponderTimeout, TAKE1,
+ "OCSP responder query timeout")
+ SSL_CMD_SRV(OCSPUseRequestNonce, FLAG,
+ "Whether OCSP queries use a nonce or not ('on', 'off')")
+ SSL_CMD_SRV(OCSPProxyURL, TAKE1,
+ "Proxy URL to use for OCSP requests")
+
+/* Define OCSP Responder Certificate Verification Directive */
+ SSL_CMD_SRV(OCSPNoVerify, FLAG,
+ "Do not verify OCSP Responder certificate ('on', 'off')")
+/* Define OCSP Responder File Configuration Directive */
+ SSL_CMD_SRV(OCSPResponderCertificateFile, TAKE1,
+ "Trusted OCSP responder certificates"
+ "(`/path/to/file' - PEM encoded certificates)")
+
+#ifdef HAVE_OCSP_STAPLING
+ /*
+ * OCSP Stapling options
+ */
+ SSL_CMD_SRV(StaplingCache, TAKE1,
+ "SSL Stapling Response Cache storage "
+ "(`dbm:/path/to/file')")
+ SSL_CMD_SRV(UseStapling, FLAG,
+ "SSL switch for the OCSP Stapling protocol " "(`on', `off')")
+ SSL_CMD_SRV(StaplingResponseTimeSkew, TAKE1,
+ "SSL stapling option for maximum time difference in OCSP responses")
+ SSL_CMD_SRV(StaplingResponderTimeout, TAKE1,
+ "SSL stapling option for OCSP responder timeout")
+ SSL_CMD_SRV(StaplingResponseMaxAge, TAKE1,
+ "SSL stapling option for maximum age of OCSP responses")
+ SSL_CMD_SRV(StaplingStandardCacheTimeout, TAKE1,
+ "SSL stapling option for normal OCSP Response Cache Lifetime")
+ SSL_CMD_SRV(StaplingReturnResponderErrors, FLAG,
+ "SSL stapling switch to return Status Errors Back to Client"
+ "(`on', `off')")
+ SSL_CMD_SRV(StaplingFakeTryLater, FLAG,
+ "SSL stapling switch to send tryLater response to client on error "
+ "(`on', `off')")
+ SSL_CMD_SRV(StaplingErrorCacheTimeout, TAKE1,
+ "SSL stapling option for OCSP Response Error Cache Lifetime")
+ SSL_CMD_SRV(StaplingForceURL, TAKE1,
+ "SSL stapling option to Force the OCSP Stapling URL")
+#endif
+
+#ifdef HAVE_SSL_CONF_CMD
+ SSL_CMD_SRV(OpenSSLConfCmd, TAKE2,
+ "OpenSSL configuration command")
+#endif
+
+ /* Deprecated directives. */
+ AP_INIT_RAW_ARGS("SSLLog", ap_set_deprecated, NULL, OR_ALL,
+ "SSLLog directive is no longer supported - use ErrorLog."),
+ AP_INIT_RAW_ARGS("SSLLogLevel", ap_set_deprecated, NULL, OR_ALL,
+ "SSLLogLevel directive is no longer supported - use LogLevel."),
+
+ AP_END_CMD
+};
+
+/*
+ * the various processing hooks
+ */
+static int modssl_is_prelinked(void)
+{
+ apr_size_t i = 0;
+ const module *mod;
+ while ((mod = ap_prelinked_modules[i++])) {
+ if (strcmp(mod->name, "mod_ssl.c") == 0) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static apr_status_t ssl_cleanup_pre_config(void *data)
+{
+#if HAVE_OPENSSL_INIT_SSL || (OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+ !defined(LIBRESSL_VERSION_NUMBER))
+ /* Openssl v1.1+ handles all termination automatically from
+ * OPENSSL_init_ssl(). Do nothing in this case.
+ */
+
+#else
+ /* Termination below is for legacy Openssl versions v1.0.x and
+ * older.
+ */
+
+ /* Corresponds to OBJ_create()s */
+ OBJ_cleanup();
+ /* Corresponds to OPENSSL_load_builtin_modules() */
+ CONF_modules_free();
+ /* Corresponds to SSL_library_init: */
+ EVP_cleanup();
+#if HAVE_ENGINE_LOAD_BUILTIN_ENGINES
+ ENGINE_cleanup();
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x1000200fL
+#ifndef OPENSSL_NO_COMP
+ SSL_COMP_free_compression_methods();
+#endif
+#endif
+
+ /* Usually needed per thread, but this parent process is single-threaded */
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
+#if OPENSSL_VERSION_NUMBER >= 0x1000000fL
+ ERR_remove_thread_state(NULL);
+#else
+ ERR_remove_state(0);
+#endif
+#endif
+
+ /* Don't call ERR_free_strings in earlier versions, ERR_load_*_strings only
+ * actually loaded the error strings once per process due to static
+ * variable abuse in OpenSSL. */
+#if (OPENSSL_VERSION_NUMBER >= 0x00090805f)
+ ERR_free_strings();
+#endif
+
+ /* Also don't call CRYPTO_cleanup_all_ex_data when linked statically here;
+ * any registered ex_data indices may have been cached in static variables
+ * in OpenSSL; removing them may cause havoc. Notably, with OpenSSL
+ * versions >= 0.9.8f, COMP_CTX cleanups would not be run, which
+ * could result in a per-connection memory leak (!). */
+ if (!modssl_running_statically) {
+ CRYPTO_cleanup_all_ex_data();
+ }
+#endif
+
+ /*
+ * TODO: determine somewhere we can safely shove out diagnostics
+ * (when enabled) at this late stage in the game:
+ * CRYPTO_mem_leaks_fp(stderr);
+ */
+
+ return APR_SUCCESS;
+}
+
+static int ssl_hook_pre_config(apr_pool_t *pconf,
+ apr_pool_t *plog,
+ apr_pool_t *ptemp)
+{
+ modssl_running_statically = modssl_is_prelinked();
+
+#if HAVE_OPENSSL_INIT_SSL || (OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+ !defined(LIBRESSL_VERSION_NUMBER))
+ /* Openssl v1.1+ handles all initialisation automatically, apart
+ * from hints as to how we want to use the library.
+ *
+ * We tell openssl we want to include engine support.
+ */
+ OPENSSL_init_ssl(OPENSSL_INIT_ENGINE_ALL_BUILTIN, NULL);
+
+#else
+ /* Configuration below is for legacy versions Openssl v1.0 and
+ * older.
+ */
+
+#if APR_HAS_THREADS && MODSSL_USE_OPENSSL_PRE_1_1_API
+ ssl_util_thread_id_setup(pconf);
+#endif
+#if MODSSL_USE_OPENSSL_PRE_1_1_API || defined(LIBRESSL_VERSION_NUMBER)
+ (void)CRYPTO_malloc_init();
+#else
+ OPENSSL_malloc_init();
+#endif
+ ERR_load_crypto_strings();
+ SSL_load_error_strings();
+ SSL_library_init();
+#if HAVE_ENGINE_LOAD_BUILTIN_ENGINES
+ ENGINE_load_builtin_engines();
+#endif
+ OpenSSL_add_all_algorithms();
+ OPENSSL_load_builtin_modules();
+#endif
+
+ if (OBJ_txt2nid("id-on-dnsSRV") == NID_undef) {
+ (void)OBJ_create("1.3.6.1.5.5.7.8.7", "id-on-dnsSRV",
+ "SRVName otherName form");
+ }
+
+ /* Start w/o errors (e.g. OBJ_txt2nid() above) */
+ ERR_clear_error();
+
+ /*
+ * Let us cleanup the ssl library when the module is unloaded
+ */
+ apr_pool_cleanup_register(pconf, NULL, ssl_cleanup_pre_config,
+ apr_pool_cleanup_null);
+
+ /* Register us to handle mod_log_config %c/%x variables */
+ ssl_var_log_config_register(pconf);
+
+ /* Register to handle mod_status status page generation */
+ ssl_scache_status_register(pconf);
+
+ /* Register mutex type names so they can be configured with Mutex */
+ ap_mutex_register(pconf, SSL_CACHE_MUTEX_TYPE, NULL, APR_LOCK_DEFAULT, 0);
+#ifdef HAVE_OCSP_STAPLING
+ ap_mutex_register(pconf, SSL_STAPLING_CACHE_MUTEX_TYPE, NULL,
+ APR_LOCK_DEFAULT, 0);
+ ap_mutex_register(pconf, SSL_STAPLING_REFRESH_MUTEX_TYPE, NULL,
+ APR_LOCK_DEFAULT, 0);
+#endif
+
+ return OK;
+}
+
+static SSLConnRec *ssl_init_connection_ctx(conn_rec *c,
+ ap_conf_vector_t *per_dir_config,
+ int reinit)
+{
+ SSLConnRec *sslconn = myConnConfig(c);
+ int need_setup = 0;
+
+ /* mod_proxy's (r->)per_dir_config has the lifetime of the request, thus
+ * it uses ssl_engine_set() to reset sslconn->dc when reusing SSL backend
+ * connections, so we must fall through here. But in the case where we are
+ * called from ssl_init_ssl_connection() with no per_dir_config (which also
+ * includes mod_proxy's later run_pre_connection call), sslconn->dc should
+ * be preserved if it's already set.
+ */
+ if (!sslconn) {
+ sslconn = apr_pcalloc(c->pool, sizeof(*sslconn));
+ need_setup = 1;
+ }
+ else if (!reinit) {
+ return sslconn;
+ }
+
+ /* Reinit dc in any case because it may be r->per_dir_config scoped
+ * and thus a caller like mod_proxy needs to update it per request.
+ */
+ if (per_dir_config) {
+ sslconn->dc = ap_get_module_config(per_dir_config, &ssl_module);
+ }
+ else {
+ sslconn->dc = ap_get_module_config(c->base_server->lookup_defaults,
+ &ssl_module);
+ }
+
+ if (need_setup) {
+ sslconn->server = c->base_server;
+ sslconn->verify_depth = UNSET;
+ if (c->outgoing) {
+ sslconn->cipher_suite = sslconn->dc->proxy->auth.cipher_suite;
+ }
+ else {
+ SSLSrvConfigRec *sc = mySrvConfig(c->base_server);
+ sslconn->cipher_suite = sc->server->auth.cipher_suite;
+ }
+
+ myConnConfigSet(c, sslconn);
+ }
+
+ return sslconn;
+}
+
+static int ssl_engine_status(conn_rec *c, SSLConnRec *sslconn)
+{
+ if (c->master) {
+ return DECLINED;
+ }
+ if (sslconn) {
+ /* This connection has already been configured. Check what applies. */
+ if (sslconn->disabled) {
+ return SUSPENDED;
+ }
+ if (c->outgoing) {
+ if (!sslconn->dc->proxy_enabled) {
+ return DECLINED;
+ }
+ }
+ else {
+ if (mySrvConfig(sslconn->server)->enabled != SSL_ENABLED_TRUE) {
+ return DECLINED;
+ }
+ }
+ }
+ else {
+ /* we decline by default for outgoing connections and for incoming
+ * where the base_server is not enabled. */
+ if (c->outgoing || mySrvConfig(c->base_server)->enabled != SSL_ENABLED_TRUE) {
+ return DECLINED;
+ }
+ }
+ return OK;
+}
+
+static int ssl_hook_ssl_bind_outgoing(conn_rec *c,
+ ap_conf_vector_t *per_dir_config,
+ int enable_ssl)
+{
+ SSLConnRec *sslconn;
+ int status;
+
+ sslconn = ssl_init_connection_ctx(c, per_dir_config, 1);
+ if (sslconn->ssl) {
+ /* we are already bound to this connection. We have rebound
+ * or removed the reference to a previous per_dir_config,
+ * there is nothing more to do. */
+ return OK;
+ }
+
+ status = ssl_engine_status(c, sslconn);
+ if (enable_ssl) {
+ if (status != OK) {
+ SSLSrvConfigRec *sc = mySrvConfig(sslconn->server);
+ sslconn->disabled = 1;
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(10272)
+ "SSL Proxy requested for %s but not enabled for us.",
+ sc->vhost_id);
+ }
+ else {
+ sslconn->disabled = 0;
+ return OK;
+ }
+ }
+ else {
+ sslconn->disabled = 1;
+ }
+ return DECLINED;
+}
+
+int ssl_init_ssl_connection(conn_rec *c, request_rec *r)
+{
+ SSLSrvConfigRec *sc;
+ SSL *ssl;
+ SSLConnRec *sslconn;
+ char *vhost_md5;
+ int rc;
+ modssl_ctx_t *mctx;
+ server_rec *server;
+
+ /*
+ * Create or retrieve SSL context
+ */
+ sslconn = ssl_init_connection_ctx(c, r ? r->per_dir_config : NULL, 0);
+ server = sslconn->server;
+ sc = mySrvConfig(server);
+
+ /*
+ * Seed the Pseudo Random Number Generator (PRNG)
+ */
+ ssl_rand_seed(server, c->pool, SSL_RSCTX_CONNECT,
+ c->outgoing ? "Proxy: " : "Server: ");
+
+ mctx = myConnCtxConfig(c, sc);
+
+ /*
+ * Create a new SSL connection with the configured server SSL context and
+ * attach this to the socket. Additionally we register this attachment
+ * so we can detach later.
+ */
+ if (!(sslconn->ssl = ssl = SSL_new(mctx->ssl_ctx))) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01962)
+ "Unable to create a new SSL connection from the SSL "
+ "context");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, server);
+
+ c->aborted = 1;
+
+ return DECLINED; /* XXX */
+ }
+
+ rc = ssl_run_pre_handshake(c, ssl, c->outgoing ? 1 : 0);
+ if (rc != OK && rc != DECLINED) {
+ return rc;
+ }
+
+ vhost_md5 = ap_md5_binary(c->pool, (unsigned char *)sc->vhost_id,
+ sc->vhost_id_len);
+
+ if (!SSL_set_session_id_context(ssl, (unsigned char *)vhost_md5,
+ APR_MD5_DIGESTSIZE*2))
+ {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01963)
+ "Unable to set session id context to '%s'", vhost_md5);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, server);
+
+ c->aborted = 1;
+
+ return DECLINED; /* XXX */
+ }
+
+ SSL_set_app_data(ssl, c);
+ modssl_set_app_data2(ssl, NULL); /* will be request_rec */
+
+ SSL_set_verify_result(ssl, X509_V_OK);
+
+ ssl_io_filter_init(c, r, ssl);
+
+ return APR_SUCCESS;
+}
+
+static const char *ssl_hook_http_scheme(const request_rec *r)
+{
+ return modssl_request_is_tls(r, NULL) ? "https" : NULL;
+}
+
+static apr_port_t ssl_hook_default_port(const request_rec *r)
+{
+ return modssl_request_is_tls(r, NULL) ? 443 : 0;
+}
+
+static int ssl_hook_pre_connection(conn_rec *c, void *csd)
+{
+ SSLSrvConfigRec *sc;
+ SSLConnRec *sslconn = myConnConfig(c);
+
+ /*
+ * Immediately stop processing if SSL is disabled for this connection
+ */
+ if (ssl_engine_status(c, sslconn) != OK) {
+ return DECLINED;
+ }
+
+ if (sslconn) {
+ sc = mySrvConfig(sslconn->server);
+ }
+ else {
+ sc = mySrvConfig(c->base_server);
+ }
+
+ /*
+ * Remember the connection information for
+ * later access inside callback functions
+ */
+
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(01964)
+ "Connection to child %ld established "
+ "(server %s)", c->id, sc->vhost_id);
+
+ return ssl_init_ssl_connection(c, NULL);
+}
+
+static int ssl_hook_process_connection(conn_rec* c)
+{
+ SSLConnRec *sslconn = myConnConfig(c);
+
+ if (sslconn && !sslconn->disabled) {
+ /* On an active SSL connection, let the input filters initialize
+ * themselves which triggers the handshake, which again triggers
+ * all kinds of useful things such as SNI and ALPN.
+ */
+ apr_bucket_brigade* temp;
+
+ temp = apr_brigade_create(c->pool, c->bucket_alloc);
+ ap_get_brigade(c->input_filters, temp,
+ AP_MODE_INIT, APR_BLOCK_READ, 0);
+ apr_brigade_destroy(temp);
+ }
+
+ return DECLINED;
+}
+
+/*
+ * the module registration phase
+ */
+
+static void ssl_register_hooks(apr_pool_t *p)
+{
+ /* ssl_hook_ReadReq needs to use the BrowserMatch settings so must
+ * run after mod_setenvif's post_read_request hook. */
+ static const char *pre_prr[] = { "mod_setenvif.c", NULL };
+ /* The ssl_init_Module post_config hook should run before mod_proxy's
+ * for the ssl proxy main configs to be merged with vhosts' before being
+ * themselves merged with mod_proxy's in proxy_hook_section_post_config.
+ */
+ static const char *b_pc[] = { "mod_proxy.c", NULL};
+
+
+ ssl_io_filter_register(p);
+
+ ap_hook_pre_connection(ssl_hook_pre_connection,NULL,NULL, APR_HOOK_MIDDLE);
+ ap_hook_process_connection(ssl_hook_process_connection,
+ NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_test_config (ssl_hook_ConfigTest, NULL,NULL, APR_HOOK_MIDDLE);
+ ap_hook_post_config (ssl_init_Module, NULL,b_pc, APR_HOOK_MIDDLE);
+ ap_hook_http_scheme (ssl_hook_http_scheme, NULL,NULL, APR_HOOK_MIDDLE);
+ ap_hook_default_port (ssl_hook_default_port, NULL,NULL, APR_HOOK_MIDDLE);
+ ap_hook_pre_config (ssl_hook_pre_config, NULL,NULL, APR_HOOK_MIDDLE);
+ ap_hook_child_init (ssl_init_Child, NULL,NULL, APR_HOOK_MIDDLE);
+ ap_hook_check_authn (ssl_hook_UserCheck, NULL,NULL, APR_HOOK_FIRST,
+ AP_AUTH_INTERNAL_PER_CONF);
+ ap_hook_fixups (ssl_hook_Fixup, NULL,NULL, APR_HOOK_MIDDLE);
+ ap_hook_check_access (ssl_hook_Access, NULL,NULL, APR_HOOK_MIDDLE,
+ AP_AUTH_INTERNAL_PER_CONF);
+ ap_hook_check_authz (ssl_hook_Auth, NULL,NULL, APR_HOOK_MIDDLE,
+ AP_AUTH_INTERNAL_PER_CONF);
+ ap_hook_post_read_request(ssl_hook_ReadReq, pre_prr,NULL, APR_HOOK_MIDDLE);
+
+ APR_OPTIONAL_HOOK(proxy, section_post_config,
+ ssl_proxy_section_post_config, NULL, NULL,
+ APR_HOOK_MIDDLE);
+
+ ssl_var_register(p);
+ ap_hook_ssl_bind_outgoing (ssl_hook_ssl_bind_outgoing, NULL, NULL, APR_HOOK_MIDDLE);
+
+ ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "ssl",
+ AUTHZ_PROVIDER_VERSION,
+ &ssl_authz_provider_require_ssl,
+ AP_AUTH_INTERNAL_PER_CONF);
+
+ ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "ssl-verify-client",
+ AUTHZ_PROVIDER_VERSION,
+ &ssl_authz_provider_verify_client,
+ AP_AUTH_INTERNAL_PER_CONF);
+}
+
+module AP_MODULE_DECLARE_DATA ssl_module = {
+ STANDARD20_MODULE_STUFF,
+ ssl_config_perdir_create, /* create per-dir config structures */
+ ssl_config_perdir_merge, /* merge per-dir config structures */
+ ssl_config_server_create, /* create per-server config structures */
+ ssl_config_server_merge, /* merge per-server config structures */
+ ssl_config_cmds, /* table of configuration directives */
+ ssl_register_hooks /* register hooks */
+#if defined(AP_MODULE_HAS_FLAGS)
+ ,AP_MODULE_FLAG_ALWAYS_MERGE /* flags */
+#endif
+};
diff --git a/modules/ssl/mod_ssl.dep b/modules/ssl/mod_ssl.dep
new file mode 100644
index 0000000..323d0f6
--- /dev/null
+++ b/modules/ssl/mod_ssl.dep
@@ -0,0 +1,1086 @@
+# Microsoft Developer Studio Generated Dependency File, included by mod_ssl.mak
+
+.\mod_ssl.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_provider.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\ap_socache.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_connection.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_main.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\http_vhost.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\mod_auth.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_charset.h"\
+ "..\..\include\util_ebcdic.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\include\util_md5.h"\
+ "..\..\include\util_mutex.h"\
+ "..\..\include\util_script.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_md5.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apr_xlate.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_fnmatch.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ ".\mod_ssl.h"\
+ ".\mod_ssl_openssl.h"\
+ ".\ssl_private.h"\
+ ".\ssl_util_ssl.h"\
+
+
+.\ssl_engine_config.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_provider.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\ap_socache.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_connection.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_main.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\http_vhost.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\mod_auth.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_charset.h"\
+ "..\..\include\util_ebcdic.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\include\util_mutex.h"\
+ "..\..\include\util_script.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apr_xlate.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_fnmatch.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ ".\ssl_private.h"\
+ ".\ssl_util_ssl.h"\
+
+
+.\ssl_engine_init.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_mpm.h"\
+ "..\..\include\ap_provider.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\ap_socache.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_connection.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_main.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\http_vhost.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\mod_auth.h"\
+ "..\..\include\mpm_common.h"\
+ "..\..\include\os.h"\
+ "..\..\include\scoreboard.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_charset.h"\
+ "..\..\include\util_ebcdic.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\include\util_mutex.h"\
+ "..\..\include\util_script.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apr_xlate.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_fnmatch.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ ".\mod_ssl.h"\
+ ".\mod_ssl_openssl.h"\
+ ".\ssl_private.h"\
+ ".\ssl_util_ssl.h"\
+
+
+.\ssl_engine_io.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_provider.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\ap_socache.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_connection.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_main.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\http_vhost.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\mod_auth.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_charset.h"\
+ "..\..\include\util_ebcdic.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\include\util_mutex.h"\
+ "..\..\include\util_script.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_date.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apr_xlate.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_fnmatch.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ ".\mod_ssl.h"\
+ ".\mod_ssl_openssl.h"\
+ ".\ssl_private.h"\
+ ".\ssl_util_ssl.h"\
+
+
+.\ssl_engine_kernel.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_provider.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\ap_socache.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_connection.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_main.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\http_vhost.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\mod_auth.h"\
+ "..\..\include\os.h"\
+ "..\..\include\scoreboard.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_charset.h"\
+ "..\..\include\util_ebcdic.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\include\util_md5.h"\
+ "..\..\include\util_mutex.h"\
+ "..\..\include\util_script.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_md5.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apr_xlate.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_fnmatch.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ ".\mod_ssl.h"\
+ ".\ssl_private.h"\
+ ".\ssl_util_ssl.h"\
+
+
+.\ssl_engine_log.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_provider.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\ap_socache.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_connection.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_main.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\http_vhost.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\mod_auth.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_charset.h"\
+ "..\..\include\util_ebcdic.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\include\util_mutex.h"\
+ "..\..\include\util_script.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apr_xlate.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_fnmatch.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ ".\ssl_private.h"\
+ ".\ssl_util_ssl.h"\
+
+
+.\ssl_engine_mutex.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_provider.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\ap_socache.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_connection.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_main.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\http_vhost.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\mod_auth.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_charset.h"\
+ "..\..\include\util_ebcdic.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\include\util_mutex.h"\
+ "..\..\include\util_script.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apr_xlate.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_fnmatch.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ ".\ssl_private.h"\
+ ".\ssl_util_ssl.h"\
+
+
+.\ssl_engine_ocsp.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_provider.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\ap_socache.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_connection.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_main.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\http_vhost.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\mod_auth.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_charset.h"\
+ "..\..\include\util_ebcdic.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\include\util_mutex.h"\
+ "..\..\include\util_script.h"\
+ "..\..\srclib\apr-util\include\apr_base64.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apr_xlate.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_fnmatch.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ ".\ssl_private.h"\
+ ".\ssl_util_ssl.h"\
+
+
+.\ssl_engine_pphrase.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_provider.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\ap_socache.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_connection.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_main.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\http_vhost.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\mod_auth.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_charset.h"\
+ "..\..\include\util_ebcdic.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\include\util_mutex.h"\
+ "..\..\include\util_script.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apr_xlate.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_fnmatch.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ ".\ssl_private.h"\
+ ".\ssl_util_ssl.h"\
+
+
+.\ssl_engine_rand.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_provider.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\ap_socache.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_connection.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_main.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\http_vhost.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\mod_auth.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_charset.h"\
+ "..\..\include\util_ebcdic.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\include\util_mutex.h"\
+ "..\..\include\util_script.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apr_xlate.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_fnmatch.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ ".\ssl_private.h"\
+ ".\ssl_util_ssl.h"\
+
+
+.\ssl_engine_vars.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_provider.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\ap_socache.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_connection.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_main.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\http_vhost.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\mod_auth.h"\
+ "..\..\include\os.h"\
+ "..\..\include\scoreboard.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_charset.h"\
+ "..\..\include\util_ebcdic.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\include\util_mutex.h"\
+ "..\..\include\util_script.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apr_xlate.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_fnmatch.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ "..\loggers\mod_log_config.h"\
+ ".\mod_ssl.h"\
+ ".\ssl_private.h"\
+ ".\ssl_util_ssl.h"\
+
+
+.\ssl_scache.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_provider.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\ap_socache.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_connection.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_main.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\http_vhost.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\mod_auth.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_charset.h"\
+ "..\..\include\util_ebcdic.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\include\util_mutex.h"\
+ "..\..\include\util_script.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apr_xlate.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_fnmatch.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ "..\generators\mod_status.h"\
+ ".\ssl_private.h"\
+ ".\ssl_util_ssl.h"\
+
+
+.\ssl_util.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_mpm.h"\
+ "..\..\include\ap_provider.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\ap_socache.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_connection.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_main.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\http_vhost.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\mod_auth.h"\
+ "..\..\include\os.h"\
+ "..\..\include\scoreboard.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_charset.h"\
+ "..\..\include\util_ebcdic.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\include\util_mutex.h"\
+ "..\..\include\util_script.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apr_xlate.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_fnmatch.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ ".\ssl_private.h"\
+ ".\ssl_util_ssl.h"\
+
+
+.\ssl_util_ocsp.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_provider.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\ap_socache.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_connection.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_main.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\http_vhost.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\mod_auth.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_charset.h"\
+ "..\..\include\util_ebcdic.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\include\util_mutex.h"\
+ "..\..\include\util_script.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apr_xlate.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_fnmatch.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ ".\ssl_private.h"\
+ ".\ssl_util_ssl.h"\
+
+
+.\ssl_util_ssl.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_provider.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\ap_socache.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_connection.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_main.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\http_vhost.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\mod_auth.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_charset.h"\
+ "..\..\include\util_ebcdic.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\include\util_mutex.h"\
+ "..\..\include\util_script.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apr_xlate.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_fnmatch.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ ".\ssl_private.h"\
+ ".\ssl_util_ssl.h"\
+
+
+.\ssl_util_stapling.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_mpm.h"\
+ "..\..\include\ap_provider.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\ap_socache.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_connection.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_main.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\http_vhost.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\mod_auth.h"\
+ "..\..\include\os.h"\
+ "..\..\include\scoreboard.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_charset.h"\
+ "..\..\include\util_ebcdic.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\include\util_mutex.h"\
+ "..\..\include\util_script.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apr_xlate.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_fnmatch.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ ".\ssl_private.h"\
+ ".\ssl_util_ssl.h"\
+
+
+..\..\build\win32\httpd.rc : \
+ "..\..\include\ap_release.h"\
+
diff --git a/modules/ssl/mod_ssl.dsp b/modules/ssl/mod_ssl.dsp
new file mode 100644
index 0000000..65b554d
--- /dev/null
+++ b/modules/ssl/mod_ssl.dsp
@@ -0,0 +1,195 @@
+# Microsoft Developer Studio Project File - Name="mod_ssl" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_ssl - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "mod_ssl.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "mod_ssl.mak" CFG="mod_ssl - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_ssl - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_ssl - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "mod_ssl - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../generators" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../../srclib/openssl/inc32" /I "../md" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "WIN32_LEAN_AND_MEAN" /D "NO_IDEA" /D "NO_RC5" /D "NO_MDC2" /D "OPENSSL_NO_IDEA" /D "OPENSSL_NO_RC5" /D "OPENSSL_NO_MDC2" /D "HAVE_OPENSSL" /D "HAVE_SSL_SET_STATE" /D "HAVE_OPENSSL_ENGINE_H" /D "HAVE_ENGINE_INIT" /D "HAVE_ENGINE_LOAD_BUILTIN_ENGINES" /D "SSL_DECLARE_EXPORT" /Fd"Release\mod_ssl_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"Release/mod_ssl.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_ssl.so" /d LONG_NAME="proxy_ssl_module for Apache"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:".\Release\mod_ssl.so" /base:@..\..\os\win32\BaseAddr.ref,mod_ssl.so
+# ADD LINK32 kernel32.lib user32.lib wsock32.lib ws2_32.lib advapi32.lib gdi32.lib libeay32.lib ssleay32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_ssl.so" /libpath:"../../srclib/openssl/out32dll" /libpath:"../../srclib/openssl/out32" /base:@..\..\os\win32\BaseAddr.ref,mod_ssl.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_ssl.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "mod_ssl - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../generators" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../../srclib/openssl/inc32" /I "../md" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "WIN32_LEAN_AND_MEAN" /D "NO_IDEA" /D "NO_RC5" /D "NO_MDC2" /D "OPENSSL_NO_IDEA" /D "OPENSSL_NO_RC5" /D "OPENSSL_NO_MDC2" /D "HAVE_OPENSSL" /D "HAVE_SSL_SET_STATE" /D "HAVE_OPENSSL_ENGINE_H" /D "HAVE_ENGINE_INIT" /D "HAVE_ENGINE_LOAD_BUILTIN_ENGINES" /D "SSL_DECLARE_EXPORT" /Fd"Debug\mod_ssl_src" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"Debug/mod_ssl.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_ssl.so" /d LONG_NAME="proxy_ssl_module for Apache"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_ssl.so" /base:@..\..\os\win32\BaseAddr.ref,mod_ssl.so
+# ADD LINK32 kernel32.lib user32.lib wsock32.lib ws2_32.lib advapi32.lib gdi32.lib libeay32.lib ssleay32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_ssl.so" /libpath:"../../srclib/openssl/out32dll.dbg" /libpath:"../../srclib/openssl/out32.dbg" /libpath:"../../srclib/openssl/out32dll" /libpath:"../../srclib/openssl/out32" /base:@..\..\os\win32\BaseAddr.ref,mod_ssl.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_ssl.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_ssl - Win32 Release"
+# Name "mod_ssl - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "*.c"
+# Begin Source File
+
+SOURCE=.\mod_ssl.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_config.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_init.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_io.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_kernel.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_log.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_mutex.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_pphrase.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_rand.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_vars.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_ocsp.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_util_ocsp.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_scache.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_util_stapling.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_util.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_util_ssl.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "*.h"
+# Begin Source File
+
+SOURCE=.\mod_ssl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_private.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_util_ssl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_util_table.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project
diff --git a/modules/ssl/mod_ssl.h b/modules/ssl/mod_ssl.h
new file mode 100644
index 0000000..a360911
--- /dev/null
+++ b/modules/ssl/mod_ssl.h
@@ -0,0 +1,120 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+/**
+ * @file mod_ssl.h
+ * @brief SSL extension module for Apache
+ *
+ * @defgroup MOD_SSL mod_ssl
+ * @ingroup APACHE_MODS
+ * @{
+ */
+
+#ifndef __MOD_SSL_H__
+#define __MOD_SSL_H__
+
+#include "httpd.h"
+#include "http_config.h"
+#include "apr_optional.h"
+#include "apr_tables.h" /* for apr_array_header_t */
+
+/* Create a set of SSL_DECLARE(type), SSL_DECLARE_NONSTD(type) and
+ * SSL_DECLARE_DATA with appropriate export and import tags for the platform
+ */
+#if !defined(WIN32)
+#define SSL_DECLARE(type) type
+#define SSL_DECLARE_NONSTD(type) type
+#define SSL_DECLARE_DATA
+#elif defined(SSL_DECLARE_STATIC)
+#define SSL_DECLARE(type) type __stdcall
+#define SSL_DECLARE_NONSTD(type) type
+#define SSL_DECLARE_DATA
+#elif defined(SSL_DECLARE_EXPORT)
+#define SSL_DECLARE(type) __declspec(dllexport) type __stdcall
+#define SSL_DECLARE_NONSTD(type) __declspec(dllexport) type
+#define SSL_DECLARE_DATA __declspec(dllexport)
+#else
+#define SSL_DECLARE(type) __declspec(dllimport) type __stdcall
+#define SSL_DECLARE_NONSTD(type) __declspec(dllimport) type
+#define SSL_DECLARE_DATA __declspec(dllimport)
+#endif
+
+/** The ssl_var_lookup() optional function retrieves SSL environment
+ * variables. */
+APR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup,
+ (apr_pool_t *, server_rec *,
+ conn_rec *, request_rec *,
+ char *));
+
+/** The ssl_ext_list() optional function attempts to build an array
+ * of all the values contained in the named X.509 extension. The
+ * returned array will be created in the supplied pool.
+ * The client certificate is used if peer is non-zero; the server
+ * certificate is used otherwise.
+ * Extension specifies the extensions to use as a string. This can be
+ * one of the "known" long or short names, or a numeric OID,
+ * e.g. "1.2.3.4", 'nsComment' and 'DN' are all valid.
+ * A pointer to an apr_array_header_t structure is returned if at
+ * least one matching extension is found, NULL otherwise.
+ */
+APR_DECLARE_OPTIONAL_FN(apr_array_header_t *, ssl_ext_list,
+ (apr_pool_t *p, conn_rec *c, int peer,
+ const char *extension));
+
+/** An optional function which returns non-zero if the given connection
+ * is using SSL/TLS. */
+APR_DECLARE_OPTIONAL_FN(int, ssl_is_https, (conn_rec *));
+
+/** The ssl_proxy_enable() and ssl_engine_{set,disable}() optional
+ * functions are used by mod_proxy to enable use of SSL for outgoing
+ * connections. */
+
+APR_DECLARE_OPTIONAL_FN(int, ssl_proxy_enable, (conn_rec *));
+APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *));
+APR_DECLARE_OPTIONAL_FN(int, ssl_engine_set, (conn_rec *,
+ ap_conf_vector_t *,
+ int proxy, int enable));
+
+/* Check for availability of new hooks */
+#define SSL_CERT_HOOKS
+#ifdef SSL_CERT_HOOKS
+
+/** Lets others add certificate and key files to the given server.
+ * For each cert a key must also be added.
+ * @param cert_file and array of const char* with the path to the certificate chain
+ * @param key_file and array of const char* with the path to the private key file
+ */
+APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, add_cert_files,
+ (server_rec *s, apr_pool_t *p,
+ apr_array_header_t *cert_files,
+ apr_array_header_t *key_files))
+
+/** In case no certificates are available for a server, this
+ * lets other modules add a fallback certificate for the time
+ * being. Regular requests against this server will be answered
+ * with a 503.
+ * @param cert_file and array of const char* with the path to the certificate chain
+ * @param key_file and array of const char* with the path to the private key file
+ */
+APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, add_fallback_cert_files,
+ (server_rec *s, apr_pool_t *p,
+ apr_array_header_t *cert_files,
+ apr_array_header_t *key_files))
+
+#endif /* SSL_CERT_HOOKS */
+
+#endif /* __MOD_SSL_H__ */
+/** @} */
diff --git a/modules/ssl/mod_ssl.mak b/modules/ssl/mod_ssl.mak
new file mode 100644
index 0000000..ea77195
--- /dev/null
+++ b/modules/ssl/mod_ssl.mak
@@ -0,0 +1,500 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on mod_ssl.dsp
+!IF "$(CFG)" == ""
+CFG=mod_ssl - Win32 Release
+!MESSAGE No configuration specified. Defaulting to mod_ssl - Win32 Release.
+!ENDIF
+
+!IF "$(CFG)" != "mod_ssl - Win32 Release" && "$(CFG)" != "mod_ssl - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "mod_ssl.mak" CFG="mod_ssl - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_ssl - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_ssl - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+!IF "$(_HAVE_OSSL110)" == "1"
+SSLCRP=libcrypto
+SSLLIB=libssl
+SSLINC=/I ../../srclib/openssl/include
+SSLBIN=../../srclib/openssl
+!ELSE
+SSLCRP=libeay32
+SSLLIB=ssleay32
+SSLINC=/I ../../srclib/openssl/inc32
+SSLBIN=../../srclib/openssl/out32dll
+!ENDIF
+
+
+!IF "$(CFG)" == "mod_ssl - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_ssl.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_ssl.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_ssl.obj"
+ -@erase "$(INTDIR)\mod_ssl.res"
+ -@erase "$(INTDIR)\mod_ssl_src.idb"
+ -@erase "$(INTDIR)\mod_ssl_src.pdb"
+ -@erase "$(INTDIR)\ssl_engine_config.obj"
+ -@erase "$(INTDIR)\ssl_engine_init.obj"
+ -@erase "$(INTDIR)\ssl_engine_io.obj"
+ -@erase "$(INTDIR)\ssl_engine_kernel.obj"
+ -@erase "$(INTDIR)\ssl_engine_log.obj"
+ -@erase "$(INTDIR)\ssl_engine_mutex.obj"
+ -@erase "$(INTDIR)\ssl_engine_ocsp.obj"
+ -@erase "$(INTDIR)\ssl_engine_pphrase.obj"
+ -@erase "$(INTDIR)\ssl_engine_rand.obj"
+ -@erase "$(INTDIR)\ssl_engine_vars.obj"
+ -@erase "$(INTDIR)\ssl_scache.obj"
+ -@erase "$(INTDIR)\ssl_util.obj"
+ -@erase "$(INTDIR)\ssl_util_ocsp.obj"
+ -@erase "$(INTDIR)\ssl_util_ssl.obj"
+ -@erase "$(INTDIR)\ssl_util_stapling.obj"
+ -@erase "$(OUTDIR)\mod_ssl.exp"
+ -@erase "$(OUTDIR)\mod_ssl.lib"
+ -@erase "$(OUTDIR)\mod_ssl.pdb"
+ -@erase "$(OUTDIR)\mod_ssl.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../generators" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../md" $(SSLINC) /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "WIN32_LEAN_AND_MEAN" /D "NO_IDEA" /D "NO_RC5" /D "NO_MDC2" /D "OPENSSL_NO_IDEA" /D "OPENSSL_NO_RC5" /D "OPENSSL_NO_MDC2" /D "HAVE_OPENSSL" /D "HAVE_SSL_SET_STATE" /D "HAVE_OPENSSL_ENGINE_H" /D "HAVE_ENGINE_INIT" /D "HAVE_ENGINE_LOAD_BUILTIN_ENGINES" /D "SSL_DECLARE_EXPORT" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_ssl_src" /FD /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_ssl.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_ssl.so" /d LONG_NAME="proxy_ssl_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_ssl.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib user32.lib wsock32.lib ws2_32.lib advapi32.lib gdi32.lib $(SSLCRP).lib $(SSLLIB).lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_ssl.pdb" /debug /out:"$(OUTDIR)\mod_ssl.so" /implib:"$(OUTDIR)\mod_ssl.lib" /libpath:"$(SSLBIN)" /libpath:"../../srclib/openssl/out32" /base:@..\..\os\win32\BaseAddr.ref,mod_ssl.so /opt:ref
+LINK32_OBJS= \
+ "$(INTDIR)\mod_ssl.obj" \
+ "$(INTDIR)\ssl_engine_config.obj" \
+ "$(INTDIR)\ssl_engine_init.obj" \
+ "$(INTDIR)\ssl_engine_io.obj" \
+ "$(INTDIR)\ssl_engine_kernel.obj" \
+ "$(INTDIR)\ssl_engine_log.obj" \
+ "$(INTDIR)\ssl_engine_mutex.obj" \
+ "$(INTDIR)\ssl_engine_pphrase.obj" \
+ "$(INTDIR)\ssl_engine_rand.obj" \
+ "$(INTDIR)\ssl_engine_vars.obj" \
+ "$(INTDIR)\ssl_engine_ocsp.obj" \
+ "$(INTDIR)\ssl_util_ocsp.obj" \
+ "$(INTDIR)\ssl_scache.obj" \
+ "$(INTDIR)\ssl_util_stapling.obj" \
+ "$(INTDIR)\ssl_util.obj" \
+ "$(INTDIR)\ssl_util_ssl.obj" \
+ "$(INTDIR)\mod_ssl.res" \
+ "..\..\srclib\apr\Release\libapr-1.lib" \
+ "..\..\srclib\apr-util\Release\libaprutil-1.lib" \
+ "..\..\Release\libhttpd.lib"
+
+"$(OUTDIR)\mod_ssl.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Release\mod_ssl.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_ssl.so"
+ if exist .\Release\mod_ssl.so.manifest mt.exe -manifest .\Release\mod_ssl.so.manifest -outputresource:.\Release\mod_ssl.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ELSEIF "$(CFG)" == "mod_ssl - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_ssl.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_ssl.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_ssl.obj"
+ -@erase "$(INTDIR)\mod_ssl.res"
+ -@erase "$(INTDIR)\mod_ssl_src.idb"
+ -@erase "$(INTDIR)\mod_ssl_src.pdb"
+ -@erase "$(INTDIR)\ssl_engine_config.obj"
+ -@erase "$(INTDIR)\ssl_engine_init.obj"
+ -@erase "$(INTDIR)\ssl_engine_io.obj"
+ -@erase "$(INTDIR)\ssl_engine_kernel.obj"
+ -@erase "$(INTDIR)\ssl_engine_log.obj"
+ -@erase "$(INTDIR)\ssl_engine_mutex.obj"
+ -@erase "$(INTDIR)\ssl_engine_ocsp.obj"
+ -@erase "$(INTDIR)\ssl_engine_pphrase.obj"
+ -@erase "$(INTDIR)\ssl_engine_rand.obj"
+ -@erase "$(INTDIR)\ssl_engine_vars.obj"
+ -@erase "$(INTDIR)\ssl_scache.obj"
+ -@erase "$(INTDIR)\ssl_util.obj"
+ -@erase "$(INTDIR)\ssl_util_ocsp.obj"
+ -@erase "$(INTDIR)\ssl_util_ssl.obj"
+ -@erase "$(INTDIR)\ssl_util_stapling.obj"
+ -@erase "$(OUTDIR)\mod_ssl.exp"
+ -@erase "$(OUTDIR)\mod_ssl.lib"
+ -@erase "$(OUTDIR)\mod_ssl.pdb"
+ -@erase "$(OUTDIR)\mod_ssl.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../generators" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../md" $(SSLINC) /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "WIN32_LEAN_AND_MEAN" /D "NO_IDEA" /D "NO_RC5" /D "NO_MDC2" /D "OPENSSL_NO_IDEA" /D "OPENSSL_NO_RC5" /D "OPENSSL_NO_MDC2" /D "HAVE_OPENSSL" /D "HAVE_SSL_SET_STATE" /D "HAVE_OPENSSL_ENGINE_H" /D "HAVE_ENGINE_INIT" /D "HAVE_ENGINE_LOAD_BUILTIN_ENGINES" /D "SSL_DECLARE_EXPORT" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_ssl_src" /FD /EHsc /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_ssl.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_ssl.so" /d LONG_NAME="proxy_ssl_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_ssl.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib user32.lib wsock32.lib ws2_32.lib advapi32.lib gdi32.lib $(SSLCRP).lib $(SSLLIB).lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_ssl.pdb" /debug /out:"$(OUTDIR)\mod_ssl.so" /implib:"$(OUTDIR)\mod_ssl.lib" /libpath:"../../srclib/openssl/out32dll.dbg" /libpath:"../../srclib/openssl/out32.dbg" /libpath:"$(SSLBIN)" /libpath:"../../srclib/openssl/out32" /base:@..\..\os\win32\BaseAddr.ref,mod_ssl.so
+LINK32_OBJS= \
+ "$(INTDIR)\mod_ssl.obj" \
+ "$(INTDIR)\ssl_engine_config.obj" \
+ "$(INTDIR)\ssl_engine_init.obj" \
+ "$(INTDIR)\ssl_engine_io.obj" \
+ "$(INTDIR)\ssl_engine_kernel.obj" \
+ "$(INTDIR)\ssl_engine_log.obj" \
+ "$(INTDIR)\ssl_engine_mutex.obj" \
+ "$(INTDIR)\ssl_engine_pphrase.obj" \
+ "$(INTDIR)\ssl_engine_rand.obj" \
+ "$(INTDIR)\ssl_engine_vars.obj" \
+ "$(INTDIR)\ssl_engine_ocsp.obj" \
+ "$(INTDIR)\ssl_util_ocsp.obj" \
+ "$(INTDIR)\ssl_scache.obj" \
+ "$(INTDIR)\ssl_util_stapling.obj" \
+ "$(INTDIR)\ssl_util.obj" \
+ "$(INTDIR)\ssl_util_ssl.obj" \
+ "$(INTDIR)\mod_ssl.res" \
+ "..\..\srclib\apr\Debug\libapr-1.lib" \
+ "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \
+ "..\..\Debug\libhttpd.lib"
+
+"$(OUTDIR)\mod_ssl.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Debug\mod_ssl.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_ssl.so"
+ if exist .\Debug\mod_ssl.so.manifest mt.exe -manifest .\Debug\mod_ssl.so.manifest -outputresource:.\Debug\mod_ssl.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+
+!IF "$(NO_EXTERNAL_DEPS)" != "1"
+!IF EXISTS("mod_ssl.dep")
+!INCLUDE "mod_ssl.dep"
+!ELSE
+!MESSAGE Warning: cannot find "mod_ssl.dep"
+!ENDIF
+!ENDIF
+
+
+!IF "$(CFG)" == "mod_ssl - Win32 Release" || "$(CFG)" == "mod_ssl - Win32 Debug"
+SOURCE=.\mod_ssl.c
+
+"$(INTDIR)\mod_ssl.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\ssl_engine_config.c
+
+"$(INTDIR)\ssl_engine_config.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\ssl_engine_init.c
+
+"$(INTDIR)\ssl_engine_init.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\ssl_engine_io.c
+
+"$(INTDIR)\ssl_engine_io.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\ssl_engine_kernel.c
+
+"$(INTDIR)\ssl_engine_kernel.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\ssl_engine_log.c
+
+"$(INTDIR)\ssl_engine_log.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\ssl_engine_mutex.c
+
+"$(INTDIR)\ssl_engine_mutex.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\ssl_engine_ocsp.c
+
+"$(INTDIR)\ssl_engine_ocsp.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\ssl_engine_pphrase.c
+
+"$(INTDIR)\ssl_engine_pphrase.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\ssl_engine_rand.c
+
+"$(INTDIR)\ssl_engine_rand.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\ssl_engine_vars.c
+
+"$(INTDIR)\ssl_engine_vars.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\ssl_scache.c
+
+"$(INTDIR)\ssl_scache.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\ssl_util.c
+
+"$(INTDIR)\ssl_util.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\ssl_util_ocsp.c
+
+"$(INTDIR)\ssl_util_ocsp.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\ssl_util_ssl.c
+
+"$(INTDIR)\ssl_util_ssl.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\ssl_util_stapling.c
+
+"$(INTDIR)\ssl_util_stapling.obj" : $(SOURCE) "$(INTDIR)"
+
+
+!IF "$(CFG)" == "mod_ssl - Win32 Release"
+
+"libapr - Win32 Release" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release"
+ cd "..\..\modules\ssl"
+
+"libapr - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\ssl"
+
+!ELSEIF "$(CFG)" == "mod_ssl - Win32 Debug"
+
+"libapr - Win32 Debug" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug"
+ cd "..\..\modules\ssl"
+
+"libapr - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\ssl"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_ssl - Win32 Release"
+
+"libaprutil - Win32 Release" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release"
+ cd "..\..\modules\ssl"
+
+"libaprutil - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\ssl"
+
+!ELSEIF "$(CFG)" == "mod_ssl - Win32 Debug"
+
+"libaprutil - Win32 Debug" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug"
+ cd "..\..\modules\ssl"
+
+"libaprutil - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\ssl"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_ssl - Win32 Release"
+
+"libhttpd - Win32 Release" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release"
+ cd ".\modules\ssl"
+
+"libhttpd - Win32 ReleaseCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN
+ cd ".\modules\ssl"
+
+!ELSEIF "$(CFG)" == "mod_ssl - Win32 Debug"
+
+"libhttpd - Win32 Debug" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug"
+ cd ".\modules\ssl"
+
+"libhttpd - Win32 DebugCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN
+ cd ".\modules\ssl"
+
+!ENDIF
+
+SOURCE=..\..\build\win32\httpd.rc
+
+!IF "$(CFG)" == "mod_ssl - Win32 Release"
+
+
+"$(INTDIR)\mod_ssl.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_ssl.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_ssl.so" /d LONG_NAME="proxy_ssl_module for Apache" $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "mod_ssl - Win32 Debug"
+
+
+"$(INTDIR)\mod_ssl.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_ssl.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_ssl.so" /d LONG_NAME="proxy_ssl_module for Apache" $(SOURCE)
+
+
+!ENDIF
+
+
+!ENDIF
+
diff --git a/modules/ssl/mod_ssl_openssl.h b/modules/ssl/mod_ssl_openssl.h
new file mode 100644
index 0000000..d4f684f
--- /dev/null
+++ b/modules/ssl/mod_ssl_openssl.h
@@ -0,0 +1,113 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+/**
+ * @file mod_ssl_openssl.h
+ * @brief Interface to OpenSSL-specific APIs provided by mod_ssl
+ *
+ * @defgroup MOD_SSL mod_ssl_openssl
+ * @ingroup APACHE_MODS
+ * @{
+ */
+
+#ifndef __MOD_SSL_OPENSSL_H__
+#define __MOD_SSL_OPENSSL_H__
+
+#include "mod_ssl.h"
+
+/* OpenSSL headers */
+
+#ifndef SSL_PRIVATE_H
+#include <openssl/opensslv.h>
+#if (OPENSSL_VERSION_NUMBER >= 0x10001000)
+/* must be defined before including ssl.h */
+#define OPENSSL_NO_SSL_INTERN
+#endif
+#include <openssl/ssl.h>
+#endif
+
+/**
+ * init_server hook -- allow SSL_CTX-specific initialization to be performed by
+ * a module for each SSL-enabled server (one at a time)
+ * @param s SSL-enabled [virtual] server
+ * @param p pconf pool
+ * @param is_proxy 1 if this server supports backend connections
+ * over SSL/TLS, 0 if it supports client connections over SSL/TLS
+ * @param ctx OpenSSL SSL Context for the server
+ */
+APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, init_server,
+ (server_rec *s, apr_pool_t *p, int is_proxy, SSL_CTX *ctx))
+
+/**
+ * pre_handshake hook
+ * @param c conn_rec for new connection from client or to backend server
+ * @param ssl OpenSSL SSL Connection for the client or backend server
+ * @param is_proxy 1 if this handshake is for a backend connection, 0 otherwise
+ */
+APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, pre_handshake,
+ (conn_rec *c, SSL *ssl, int is_proxy))
+
+/**
+ * proxy_post_handshake hook -- allow module to abort after successful
+ * handshake with backend server and subsequent peer checks
+ * @param c conn_rec for connection to backend server
+ * @param ssl OpenSSL SSL Connection for the client or backend server
+ */
+APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, proxy_post_handshake,
+ (conn_rec *c, SSL *ssl))
+
+/** On TLS connections that do not relate to a configured virtual host,
+ * allow other modules to provide a X509 certificate and EVP_PKEY to
+ * be used on the connection. This first hook which does not
+ * return DECLINED will determine the outcome. */
+APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, answer_challenge,
+ (conn_rec *c, const char *server_name,
+ X509 **pcert, EVP_PKEY **pkey))
+
+/** During post_config phase, ask around if someone wants to provide
+ * OCSP stapling status information for the given cert (with the also
+ * provided issuer certificate). The first hook which does not
+ * return DECLINED promises to take responsibility (and respond
+ * in later calls via hook ssl_get_stapling_status).
+ * If no hook takes over, mod_ssl's own stapling implementation will
+ * be applied (if configured).
+ */
+APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, init_stapling_status,
+ (server_rec *s, apr_pool_t *p,
+ X509 *cert, X509 *issuer))
+
+/** Anyone answering positive to ssl_init_stapling_status for a
+ * certificate, needs to register here and supply the actual OCSP stapling
+ * status data (OCSP_RESP) for a new connection.
+ * A hook supplying the response data must return APR_SUCCESS.
+ * The data is returned in DER encoded bytes via pder and pderlen. The
+ * returned pointer may be NULL, which indicates that data is (currently)
+ * unavailable.
+ * If DER data is returned, it MUST come from a response with
+ * status OCSP_RESPONSE_STATUS_SUCCESSFUL and V_OCSP_CERTSTATUS_GOOD
+ * or V_OCSP_CERTSTATUS_REVOKED, not V_OCSP_CERTSTATUS_UNKNOWN. This means
+ * errors in OCSP retrieval are to be handled/logged by the hook and
+ * are not done by mod_ssl.
+ * Any DER bytes returned MUST be allocated via malloc() and ownership
+ * passes to mod_ssl. Meaning, the hook must return a malloced copy of
+ * the data it has. mod_ssl (or OpenSSL) will free it.
+ */
+APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, get_stapling_status,
+ (unsigned char **pder, int *pderlen,
+ conn_rec *c, server_rec *s, X509 *cert))
+
+#endif /* __MOD_SSL_OPENSSL_H__ */
+/** @} */
diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c
new file mode 100644
index 0000000..de18b8f
--- /dev/null
+++ b/modules/ssl/ssl_engine_config.c
@@ -0,0 +1,2158 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+/* _ _
+ * _ __ ___ ___ __| | ___ ___| | mod_ssl
+ * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| | \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ * |_____|
+ * ssl_engine_config.c
+ * Apache Configuration Directives
+ */
+ /* ``Damned if you do,
+ damned if you don't.''
+ -- Unknown */
+#include "ssl_private.h"
+#include "util_mutex.h"
+#include "ap_provider.h"
+
+/* _________________________________________________________________
+**
+** Support for Global Configuration
+** _________________________________________________________________
+*/
+
+#define SSL_MOD_CONFIG_KEY "ssl_module"
+
+SSLModConfigRec *ssl_config_global_create(server_rec *s)
+{
+ apr_pool_t *pool = s->process->pool;
+ SSLModConfigRec *mc;
+ void *vmc;
+
+ apr_pool_userdata_get(&vmc, SSL_MOD_CONFIG_KEY, pool);
+ if (vmc) {
+ return vmc; /* reused for lifetime of the server */
+ }
+
+ /*
+ * allocate an own subpool which survives server restarts
+ */
+ mc = (SSLModConfigRec *)apr_palloc(pool, sizeof(*mc));
+ mc->pPool = pool;
+ mc->bFixed = FALSE;
+
+ /*
+ * initialize per-module configuration
+ */
+ mc->sesscache_mode = SSL_SESS_CACHE_OFF;
+ mc->sesscache = NULL;
+ mc->pMutex = NULL;
+ mc->aRandSeed = apr_array_make(pool, 4,
+ sizeof(ssl_randseed_t));
+ mc->tVHostKeys = apr_hash_make(pool);
+ mc->tPrivateKey = apr_hash_make(pool);
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
+ mc->szCryptoDevice = NULL;
+#endif
+#ifdef HAVE_OCSP_STAPLING
+ mc->stapling_cache = NULL;
+ mc->stapling_cache_mutex = NULL;
+ mc->stapling_refresh_mutex = NULL;
+#endif
+
+#ifdef HAVE_OPENSSL_KEYLOG
+ mc->keylog_file = NULL;
+#endif
+#ifdef HAVE_FIPS
+ mc->fips = UNSET;
+#endif
+
+ apr_pool_userdata_set(mc, SSL_MOD_CONFIG_KEY,
+ apr_pool_cleanup_null,
+ pool);
+
+ return mc;
+}
+
+void ssl_config_global_fix(SSLModConfigRec *mc)
+{
+ mc->bFixed = TRUE;
+}
+
+BOOL ssl_config_global_isfixed(SSLModConfigRec *mc)
+{
+ return mc->bFixed;
+}
+
+/* _________________________________________________________________
+**
+** Configuration handling
+** _________________________________________________________________
+*/
+
+#ifdef HAVE_SSL_CONF_CMD
+static apr_status_t modssl_ctx_config_cleanup(void *ctx)
+{
+ SSL_CONF_CTX_free(ctx);
+ return APR_SUCCESS;
+}
+#endif
+
+static void modssl_ctx_init(modssl_ctx_t *mctx, apr_pool_t *p)
+{
+ mctx->sc = NULL; /* set during module init */
+
+ mctx->ssl_ctx = NULL; /* set during module init */
+
+ mctx->pks = NULL;
+ mctx->pkp = NULL;
+
+#ifdef HAVE_TLS_SESSION_TICKETS
+ mctx->ticket_key = NULL;
+#endif
+
+ mctx->protocol = SSL_PROTOCOL_DEFAULT;
+ mctx->protocol_set = 0;
+
+ mctx->pphrase_dialog_type = SSL_PPTYPE_UNSET;
+ mctx->pphrase_dialog_path = NULL;
+
+ mctx->cert_chain = NULL;
+
+ mctx->crl_path = NULL;
+ mctx->crl_file = NULL;
+ mctx->crl_check_mask = UNSET;
+
+ mctx->auth.ca_cert_path = NULL;
+ mctx->auth.ca_cert_file = NULL;
+ mctx->auth.cipher_suite = NULL;
+ mctx->auth.verify_depth = UNSET;
+ mctx->auth.verify_mode = SSL_CVERIFY_UNSET;
+ mctx->auth.tls13_ciphers = NULL;
+
+ mctx->ocsp_mask = UNSET;
+ mctx->ocsp_force_default = UNSET;
+ mctx->ocsp_responder = NULL;
+ mctx->ocsp_resptime_skew = UNSET;
+ mctx->ocsp_resp_maxage = UNSET;
+ mctx->ocsp_responder_timeout = UNSET;
+ mctx->ocsp_use_request_nonce = UNSET;
+ mctx->proxy_uri = NULL;
+
+/* Set OCSP Responder Certificate Verification variable */
+ mctx->ocsp_noverify = UNSET;
+/* Set OCSP Responder File variables */
+ mctx->ocsp_verify_flags = 0;
+ mctx->ocsp_certs_file = NULL;
+ mctx->ocsp_certs = NULL;
+
+#ifdef HAVE_OCSP_STAPLING
+ mctx->stapling_enabled = UNSET;
+ mctx->stapling_resptime_skew = UNSET;
+ mctx->stapling_resp_maxage = UNSET;
+ mctx->stapling_cache_timeout = UNSET;
+ mctx->stapling_return_errors = UNSET;
+ mctx->stapling_fake_trylater = UNSET;
+ mctx->stapling_errcache_timeout = UNSET;
+ mctx->stapling_responder_timeout = UNSET;
+ mctx->stapling_force_url = NULL;
+#endif
+
+#ifdef HAVE_SRP
+ mctx->srp_vfile = NULL;
+ mctx->srp_unknown_user_seed = NULL;
+ mctx->srp_vbase = NULL;
+#endif
+#ifdef HAVE_SSL_CONF_CMD
+ mctx->ssl_ctx_config = SSL_CONF_CTX_new();
+ apr_pool_cleanup_register(p, mctx->ssl_ctx_config,
+ modssl_ctx_config_cleanup,
+ apr_pool_cleanup_null);
+ SSL_CONF_CTX_set_flags(mctx->ssl_ctx_config, SSL_CONF_FLAG_FILE);
+ SSL_CONF_CTX_set_flags(mctx->ssl_ctx_config, SSL_CONF_FLAG_SERVER);
+ SSL_CONF_CTX_set_flags(mctx->ssl_ctx_config, SSL_CONF_FLAG_CERTIFICATE);
+ mctx->ssl_ctx_param = apr_array_make(p, 5, sizeof(ssl_ctx_param_t));
+#endif
+
+ mctx->ssl_check_peer_cn = UNSET;
+ mctx->ssl_check_peer_name = UNSET;
+ mctx->ssl_check_peer_expire = UNSET;
+}
+
+static void modssl_ctx_init_server(SSLSrvConfigRec *sc,
+ apr_pool_t *p)
+{
+ modssl_ctx_t *mctx;
+
+ mctx = sc->server = apr_palloc(p, sizeof(*sc->server));
+
+ modssl_ctx_init(mctx, p);
+
+ mctx->pks = apr_pcalloc(p, sizeof(*mctx->pks));
+
+ mctx->pks->cert_files = apr_array_make(p, 3, sizeof(char *));
+ mctx->pks->key_files = apr_array_make(p, 3, sizeof(char *));
+
+#ifdef HAVE_TLS_SESSION_TICKETS
+ mctx->ticket_key = apr_pcalloc(p, sizeof(*mctx->ticket_key));
+#endif
+}
+
+static SSLSrvConfigRec *ssl_config_server_new(apr_pool_t *p)
+{
+ SSLSrvConfigRec *sc = apr_palloc(p, sizeof(*sc));
+
+ sc->mc = NULL;
+ sc->enabled = SSL_ENABLED_UNSET;
+ sc->vhost_id = NULL; /* set during module init */
+ sc->vhost_id_len = 0; /* set during module init */
+ sc->session_cache_timeout = UNSET;
+ sc->cipher_server_pref = UNSET;
+ sc->insecure_reneg = UNSET;
+#ifdef HAVE_TLSEXT
+ sc->strict_sni_vhost_check = SSL_ENABLED_UNSET;
+#endif
+#ifndef OPENSSL_NO_COMP
+ sc->compression = UNSET;
+#endif
+ sc->session_tickets = UNSET;
+
+ modssl_ctx_init_server(sc, p);
+
+ return sc;
+}
+
+/*
+ * Create per-server SSL configuration
+ */
+void *ssl_config_server_create(apr_pool_t *p, server_rec *s)
+{
+ SSLSrvConfigRec *sc = ssl_config_server_new(p);
+
+ sc->mc = ssl_config_global_create(s);
+
+ return sc;
+}
+
+#define cfgMerge(el,unset) mrg->el = (add->el == (unset)) ? base->el : add->el
+#define cfgMergeArray(el) mrg->el = apr_array_append(p, base->el, add->el)
+#define cfgMergeString(el) cfgMerge(el, NULL)
+#define cfgMergeBool(el) cfgMerge(el, UNSET)
+#define cfgMergeInt(el) cfgMerge(el, UNSET)
+
+/*
+ * Merge per-server SSL configurations
+ */
+
+static void modssl_ctx_cfg_merge(apr_pool_t *p,
+ modssl_ctx_t *base,
+ modssl_ctx_t *add,
+ modssl_ctx_t *mrg)
+{
+ if (add->protocol_set) {
+ mrg->protocol_set = 1;
+ mrg->protocol = add->protocol;
+ }
+ else {
+ mrg->protocol_set = base->protocol_set;
+ mrg->protocol = base->protocol;
+ }
+
+ cfgMerge(pphrase_dialog_type, SSL_PPTYPE_UNSET);
+ cfgMergeString(pphrase_dialog_path);
+
+ cfgMergeString(cert_chain);
+
+ cfgMerge(crl_path, NULL);
+ cfgMerge(crl_file, NULL);
+ cfgMergeInt(crl_check_mask);
+
+ cfgMergeString(auth.ca_cert_path);
+ cfgMergeString(auth.ca_cert_file);
+ cfgMergeString(auth.cipher_suite);
+ cfgMergeInt(auth.verify_depth);
+ cfgMerge(auth.verify_mode, SSL_CVERIFY_UNSET);
+ cfgMergeString(auth.tls13_ciphers);
+
+ cfgMergeInt(ocsp_mask);
+ cfgMergeBool(ocsp_force_default);
+ cfgMerge(ocsp_responder, NULL);
+ cfgMergeInt(ocsp_resptime_skew);
+ cfgMergeInt(ocsp_resp_maxage);
+ cfgMergeInt(ocsp_responder_timeout);
+ cfgMergeBool(ocsp_use_request_nonce);
+ cfgMerge(proxy_uri, NULL);
+
+/* Set OCSP Responder Certificate Verification directive */
+ cfgMergeBool(ocsp_noverify);
+/* Set OCSP Responder File directive for importing */
+ cfgMerge(ocsp_certs_file, NULL);
+
+#ifdef HAVE_OCSP_STAPLING
+ cfgMergeBool(stapling_enabled);
+ cfgMergeInt(stapling_resptime_skew);
+ cfgMergeInt(stapling_resp_maxage);
+ cfgMergeInt(stapling_cache_timeout);
+ cfgMergeBool(stapling_return_errors);
+ cfgMergeBool(stapling_fake_trylater);
+ cfgMergeInt(stapling_errcache_timeout);
+ cfgMergeInt(stapling_responder_timeout);
+ cfgMerge(stapling_force_url, NULL);
+#endif
+
+#ifdef HAVE_SRP
+ cfgMergeString(srp_vfile);
+ cfgMergeString(srp_unknown_user_seed);
+#endif
+
+#ifdef HAVE_SSL_CONF_CMD
+ cfgMergeArray(ssl_ctx_param);
+#endif
+
+ cfgMergeBool(ssl_check_peer_cn);
+ cfgMergeBool(ssl_check_peer_name);
+ cfgMergeBool(ssl_check_peer_expire);
+}
+
+static void modssl_ctx_cfg_merge_certkeys_array(apr_pool_t *p,
+ apr_array_header_t *base,
+ apr_array_header_t *add,
+ apr_array_header_t *mrg)
+{
+ int i;
+
+ /*
+ * pick up to CERTKEYS_IDX_MAX+1 entries from "add" (in which case they
+ * they "knock out" their corresponding entries in "base", emulating
+ * the behavior with cfgMergeString in releases up to 2.4.7)
+ */
+ for (i = 0; i < add->nelts && i <= CERTKEYS_IDX_MAX; i++) {
+ APR_ARRAY_PUSH(mrg, const char *) = APR_ARRAY_IDX(add, i, const char *);
+ }
+
+ /* add remaining ones from "base" */
+ while (i < base->nelts) {
+ APR_ARRAY_PUSH(mrg, const char *) = APR_ARRAY_IDX(base, i, const char *);
+ i++;
+ }
+
+ /* and finally, append the rest of "add" (if there are any) */
+ for (i = CERTKEYS_IDX_MAX+1; i < add->nelts; i++) {
+ APR_ARRAY_PUSH(mrg, const char *) = APR_ARRAY_IDX(add, i, const char *);
+ }
+}
+
+static void modssl_ctx_cfg_merge_server(apr_pool_t *p,
+ modssl_ctx_t *base,
+ modssl_ctx_t *add,
+ modssl_ctx_t *mrg)
+{
+ modssl_ctx_cfg_merge(p, base, add, mrg);
+
+ /*
+ * For better backwards compatibility with releases up to 2.4.7,
+ * merging global and vhost-level SSLCertificateFile and
+ * SSLCertificateKeyFile directives needs special treatment.
+ * See also PR 56306 and 56353.
+ */
+ modssl_ctx_cfg_merge_certkeys_array(p, base->pks->cert_files,
+ add->pks->cert_files,
+ mrg->pks->cert_files);
+ modssl_ctx_cfg_merge_certkeys_array(p, base->pks->key_files,
+ add->pks->key_files,
+ mrg->pks->key_files);
+
+ cfgMergeString(pks->ca_name_path);
+ cfgMergeString(pks->ca_name_file);
+
+#ifdef HAVE_TLS_SESSION_TICKETS
+ cfgMergeString(ticket_key->file_path);
+#endif
+}
+
+void *ssl_config_server_merge(apr_pool_t *p, void *basev, void *addv)
+{
+ SSLSrvConfigRec *base = (SSLSrvConfigRec *)basev;
+ SSLSrvConfigRec *add = (SSLSrvConfigRec *)addv;
+ SSLSrvConfigRec *mrg = ssl_config_server_new(p);
+
+ cfgMerge(mc, NULL);
+ cfgMerge(enabled, SSL_ENABLED_UNSET);
+ cfgMergeInt(session_cache_timeout);
+ cfgMergeBool(cipher_server_pref);
+ cfgMergeBool(insecure_reneg);
+#ifdef HAVE_TLSEXT
+ cfgMerge(strict_sni_vhost_check, SSL_ENABLED_UNSET);
+#endif
+#ifndef OPENSSL_NO_COMP
+ cfgMergeBool(compression);
+#endif
+ cfgMergeBool(session_tickets);
+
+ modssl_ctx_cfg_merge_server(p, base->server, add->server, mrg->server);
+
+ return mrg;
+}
+
+/*
+ * Create per-directory SSL configuration
+ */
+
+static void modssl_ctx_init_proxy(SSLDirConfigRec *dc,
+ apr_pool_t *p)
+{
+ modssl_ctx_t *mctx;
+
+ mctx = dc->proxy = apr_palloc(p, sizeof(*dc->proxy));
+
+ modssl_ctx_init(mctx, p);
+
+ mctx->pkp = apr_palloc(p, sizeof(*mctx->pkp));
+
+ mctx->pkp->cert_file = NULL;
+ mctx->pkp->cert_path = NULL;
+ mctx->pkp->ca_cert_file = NULL;
+ mctx->pkp->certs = NULL;
+ mctx->pkp->ca_certs = NULL;
+}
+
+void *ssl_config_perdir_create(apr_pool_t *p, char *dir)
+{
+ SSLDirConfigRec *dc = apr_palloc(p, sizeof(*dc));
+
+ dc->bSSLRequired = FALSE;
+ dc->aRequirement = apr_array_make(p, 4, sizeof(ssl_require_t));
+ dc->nOptions = SSL_OPT_NONE|SSL_OPT_RELSET;
+ dc->nOptionsAdd = SSL_OPT_NONE;
+ dc->nOptionsDel = SSL_OPT_NONE;
+
+ dc->szCipherSuite = NULL;
+ dc->nVerifyClient = SSL_CVERIFY_UNSET;
+ dc->nVerifyDepth = UNSET;
+
+ dc->szUserName = NULL;
+
+ dc->nRenegBufferSize = UNSET;
+
+ dc->proxy_enabled = UNSET;
+ modssl_ctx_init_proxy(dc, p);
+ dc->proxy_post_config = FALSE;
+
+ return dc;
+}
+
+/*
+ * Merge per-directory SSL configurations
+ */
+
+static void modssl_ctx_cfg_merge_proxy(apr_pool_t *p,
+ modssl_ctx_t *base,
+ modssl_ctx_t *add,
+ modssl_ctx_t *mrg)
+{
+ modssl_ctx_cfg_merge(p, base, add, mrg);
+
+ cfgMergeString(pkp->cert_file);
+ cfgMergeString(pkp->cert_path);
+ cfgMergeString(pkp->ca_cert_file);
+ cfgMergeString(pkp->certs);
+ cfgMergeString(pkp->ca_certs);
+}
+
+void *ssl_config_perdir_merge(apr_pool_t *p, void *basev, void *addv)
+{
+ SSLDirConfigRec *base = (SSLDirConfigRec *)basev;
+ SSLDirConfigRec *add = (SSLDirConfigRec *)addv;
+ SSLDirConfigRec *mrg = (SSLDirConfigRec *)apr_palloc(p, sizeof(*mrg));
+
+ cfgMerge(bSSLRequired, FALSE);
+ cfgMergeArray(aRequirement);
+
+ if (add->nOptions & SSL_OPT_RELSET) {
+ mrg->nOptionsAdd =
+ (base->nOptionsAdd & ~(add->nOptionsDel)) | add->nOptionsAdd;
+ mrg->nOptionsDel =
+ (base->nOptionsDel & ~(add->nOptionsAdd)) | add->nOptionsDel;
+ mrg->nOptions =
+ (base->nOptions & ~(mrg->nOptionsDel)) | mrg->nOptionsAdd;
+ }
+ else {
+ mrg->nOptions = add->nOptions;
+ mrg->nOptionsAdd = add->nOptionsAdd;
+ mrg->nOptionsDel = add->nOptionsDel;
+ }
+
+ cfgMergeString(szCipherSuite);
+ cfgMerge(nVerifyClient, SSL_CVERIFY_UNSET);
+ cfgMergeInt(nVerifyDepth);
+
+ cfgMergeString(szUserName);
+
+ cfgMergeInt(nRenegBufferSize);
+
+ mrg->proxy_post_config = add->proxy_post_config;
+ if (!mrg->proxy_post_config) {
+ cfgMergeBool(proxy_enabled);
+ modssl_ctx_init_proxy(mrg, p);
+ modssl_ctx_cfg_merge_proxy(p, base->proxy, add->proxy, mrg->proxy);
+
+ /* Since ssl_proxy_section_post_config() hook won't be called if there
+ * is no SSLProxy* in this dir config, the ssl_ctx may still be NULL
+ * here at runtime. Merging it is either a no-op (NULL => NULL) because
+ * we are still before post config, or we really want to reuse the one
+ * from the upper/server context (outside of <Proxy> sections).
+ */
+ cfgMerge(proxy->ssl_ctx, NULL);
+ }
+ else {
+ /* The post_config hook has already merged and initialized the
+ * proxy context, use it.
+ */
+ mrg->proxy_enabled = add->proxy_enabled;
+ mrg->proxy = add->proxy;
+ }
+
+ return mrg;
+}
+
+/* Simply merge conf with base into conf, no third party. */
+void ssl_config_proxy_merge(apr_pool_t *p,
+ SSLDirConfigRec *base,
+ SSLDirConfigRec *conf)
+{
+ if (conf->proxy_enabled == UNSET) {
+ conf->proxy_enabled = base->proxy_enabled;
+ }
+ modssl_ctx_cfg_merge_proxy(p, base->proxy, conf->proxy, conf->proxy);
+}
+
+/*
+ * Configuration functions for particular directives
+ */
+
+const char *ssl_cmd_SSLPassPhraseDialog(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ const char *err;
+ int arglen = strlen(arg);
+
+ if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+ return err;
+ }
+
+ if (strcEQ(arg, "builtin")) {
+ sc->server->pphrase_dialog_type = SSL_PPTYPE_BUILTIN;
+ sc->server->pphrase_dialog_path = NULL;
+ }
+ else if ((arglen > 5) && strEQn(arg, "exec:", 5)) {
+ sc->server->pphrase_dialog_type = SSL_PPTYPE_FILTER;
+ sc->server->pphrase_dialog_path =
+ ap_server_root_relative(cmd->pool, arg+5);
+ if (!sc->server->pphrase_dialog_path) {
+ return apr_pstrcat(cmd->pool,
+ "Invalid SSLPassPhraseDialog exec: path ",
+ arg+5, NULL);
+ }
+ if (!ssl_util_path_check(SSL_PCM_EXISTS,
+ sc->server->pphrase_dialog_path,
+ cmd->pool))
+ {
+ return apr_pstrcat(cmd->pool,
+ "SSLPassPhraseDialog: file '",
+ sc->server->pphrase_dialog_path,
+ "' does not exist", NULL);
+ }
+
+ }
+ else if ((arglen > 1) && (arg[0] == '|')) {
+ sc->server->pphrase_dialog_type = SSL_PPTYPE_PIPE;
+ sc->server->pphrase_dialog_path = arg + 1;
+ }
+ else {
+ return "SSLPassPhraseDialog: Invalid argument";
+ }
+
+ return NULL;
+}
+
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
+const char *ssl_cmd_SSLCryptoDevice(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLModConfigRec *mc = myModConfig(cmd->server);
+ const char *err;
+ ENGINE *e;
+
+ if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+ return err;
+ }
+
+ if (strcEQ(arg, "builtin")) {
+ mc->szCryptoDevice = NULL;
+ }
+ else if ((e = ENGINE_by_id(arg))) {
+ mc->szCryptoDevice = arg;
+ ENGINE_free(e);
+ }
+ else {
+ err = "SSLCryptoDevice: Invalid argument; must be one of: "
+ "'builtin' (none)";
+ e = ENGINE_get_first();
+ while (e) {
+ err = apr_pstrcat(cmd->pool, err, ", '", ENGINE_get_id(e),
+ "' (", ENGINE_get_name(e), ")", NULL);
+ /* Iterate; this call implicitly decrements the refcount
+ * on the 'old' e, per the docs in engine.h. */
+ e = ENGINE_get_next(e);
+ }
+ return err;
+ }
+
+ return NULL;
+}
+#endif
+
+const char *ssl_cmd_SSLRandomSeed(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg1,
+ const char *arg2,
+ const char *arg3)
+{
+ SSLModConfigRec *mc = myModConfig(cmd->server);
+ const char *err;
+ ssl_randseed_t *seed;
+ int arg2len = strlen(arg2);
+
+ if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+ return err;
+ }
+
+ if (ssl_config_global_isfixed(mc)) {
+ return NULL;
+ }
+
+ seed = apr_array_push(mc->aRandSeed);
+
+ if (strcEQ(arg1, "startup")) {
+ seed->nCtx = SSL_RSCTX_STARTUP;
+ }
+ else if (strcEQ(arg1, "connect")) {
+ seed->nCtx = SSL_RSCTX_CONNECT;
+ }
+ else {
+ return apr_pstrcat(cmd->pool, "SSLRandomSeed: "
+ "invalid context: `", arg1, "'",
+ NULL);
+ }
+
+ if ((arg2len > 5) && strEQn(arg2, "file:", 5)) {
+ seed->nSrc = SSL_RSSRC_FILE;
+ seed->cpPath = ap_server_root_relative(mc->pPool, arg2+5);
+ }
+ else if ((arg2len > 5) && strEQn(arg2, "exec:", 5)) {
+ seed->nSrc = SSL_RSSRC_EXEC;
+ seed->cpPath = ap_server_root_relative(mc->pPool, arg2+5);
+ }
+ else if ((arg2len > 4) && strEQn(arg2, "egd:", 4)) {
+#ifdef HAVE_RAND_EGD
+ seed->nSrc = SSL_RSSRC_EGD;
+ seed->cpPath = ap_server_root_relative(mc->pPool, arg2+4);
+#else
+ return apr_pstrcat(cmd->pool, "Invalid SSLRandomSeed entropy source `",
+ arg2, "': This version of " MODSSL_LIBRARY_NAME
+ " does not support the Entropy Gathering Daemon "
+ "(EGD).", NULL);
+#endif
+ }
+ else if (strcEQ(arg2, "builtin")) {
+ seed->nSrc = SSL_RSSRC_BUILTIN;
+ seed->cpPath = NULL;
+ }
+ else {
+ seed->nSrc = SSL_RSSRC_FILE;
+ seed->cpPath = ap_server_root_relative(mc->pPool, arg2);
+ }
+
+ if (seed->nSrc != SSL_RSSRC_BUILTIN) {
+ if (!seed->cpPath) {
+ return apr_pstrcat(cmd->pool,
+ "Invalid SSLRandomSeed path ",
+ arg2, NULL);
+ }
+ if (!ssl_util_path_check(SSL_PCM_EXISTS, seed->cpPath, cmd->pool)) {
+ return apr_pstrcat(cmd->pool,
+ "SSLRandomSeed: source path '",
+ seed->cpPath, "' does not exist", NULL);
+ }
+ }
+
+ if (!arg3) {
+ seed->nBytes = 0; /* read whole file */
+ }
+ else {
+ if (seed->nSrc == SSL_RSSRC_BUILTIN) {
+ return "SSLRandomSeed: byte specification not "
+ "allowed for builtin seed source";
+ }
+
+ seed->nBytes = atoi(arg3);
+
+ if (seed->nBytes < 0) {
+ return "SSLRandomSeed: invalid number of bytes specified";
+ }
+ }
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLEngine(cmd_parms *cmd, void *dcfg, const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+ if (!strcasecmp(arg, "On")) {
+ sc->enabled = SSL_ENABLED_TRUE;
+ return NULL;
+ }
+ else if (!strcasecmp(arg, "Off")) {
+ sc->enabled = SSL_ENABLED_FALSE;
+ return NULL;
+ }
+ else if (!strcasecmp(arg, "Optional")) {
+ sc->enabled = SSL_ENABLED_OPTIONAL;
+ return NULL;
+ }
+
+ return "Argument must be On, Off, or Optional";
+}
+
+const char *ssl_cmd_SSLFIPS(cmd_parms *cmd, void *dcfg, int flag)
+{
+#ifdef HAVE_FIPS
+ SSLModConfigRec *mc = myModConfig(cmd->server);
+#endif
+ const char *err;
+
+ if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+ return err;
+ }
+
+#ifdef HAVE_FIPS
+ if ((mc->fips != UNSET) && (mc->fips != (BOOL)(flag ? TRUE : FALSE)))
+ return "Conflicting SSLFIPS options, cannot be both On and Off";
+ mc->fips = flag ? TRUE : FALSE;
+#else
+ if (flag)
+ return "SSLFIPS invalid, rebuild httpd and openssl compiled for FIPS";
+#endif
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLCipherSuite(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg1, const char *arg2)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+
+ if (arg2 == NULL) {
+ arg2 = arg1;
+ arg1 = "SSL";
+ }
+
+ if (!strcmp("SSL", arg1)) {
+ /* always disable null and export ciphers */
+ arg2 = apr_pstrcat(cmd->pool, arg2, ":!aNULL:!eNULL:!EXP", NULL);
+ if (cmd->path) {
+ dc->szCipherSuite = arg2;
+ }
+ else {
+ sc->server->auth.cipher_suite = arg2;
+ }
+ return NULL;
+ }
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+ else if (!strcmp("TLSv1.3", arg1)) {
+ if (cmd->path) {
+ return "TLSv1.3 ciphers cannot be set inside a directory context";
+ }
+ sc->server->auth.tls13_ciphers = arg2;
+ return NULL;
+ }
+#endif
+ return apr_pstrcat(cmd->pool, "protocol '", arg1, "' not supported", NULL);
+}
+
+#define SSL_FLAGS_CHECK_FILE \
+ (SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO)
+
+#define SSL_FLAGS_CHECK_DIR \
+ (SSL_PCM_EXISTS|SSL_PCM_ISDIR)
+
+static const char *ssl_cmd_check_file(cmd_parms *parms,
+ const char **file)
+{
+ const char *filepath;
+
+ /* If only dumping the config, don't verify the paths */
+ if (ap_state_query(AP_SQ_RUN_MODE) == AP_SQ_RM_CONFIG_DUMP) {
+ return NULL;
+ }
+
+ filepath = ap_server_root_relative(parms->pool, *file);
+ if (!filepath) {
+ return apr_pstrcat(parms->pool, parms->cmd->name,
+ ": Invalid file path ", *file, NULL);
+ }
+ *file = filepath;
+
+ if (ssl_util_path_check(SSL_FLAGS_CHECK_FILE, *file, parms->pool)) {
+ return NULL;
+ }
+
+ return apr_pstrcat(parms->pool, parms->cmd->name,
+ ": file '", *file,
+ "' does not exist or is empty", NULL);
+
+}
+
+const char *ssl_cmd_SSLCompression(cmd_parms *cmd, void *dcfg, int flag)
+{
+#if !defined(OPENSSL_NO_COMP)
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+#ifndef SSL_OP_NO_COMPRESSION
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err)
+ return "This version of OpenSSL does not support enabling "
+ "SSLCompression within <VirtualHost> sections.";
+#endif
+ if (flag) {
+ /* Some (packaged) versions of OpenSSL do not support
+ * compression by default. Enabling this directive would not
+ * have the desired effect, so fail with an error. */
+ STACK_OF(SSL_COMP) *meths = SSL_COMP_get_compression_methods();
+
+ if (sk_SSL_COMP_num(meths) == 0) {
+ return "This version of OpenSSL does not have any compression methods "
+ "available, cannot enable SSLCompression.";
+ }
+ }
+ sc->compression = flag ? TRUE : FALSE;
+ return NULL;
+#else
+ return "Setting Compression mode unsupported; not implemented by the SSL library";
+#endif
+}
+
+const char *ssl_cmd_SSLHonorCipherOrder(cmd_parms *cmd, void *dcfg, int flag)
+{
+#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ sc->cipher_server_pref = flag?TRUE:FALSE;
+ return NULL;
+#else
+ return "SSLHonorCipherOrder unsupported; not implemented by the SSL library";
+#endif
+}
+
+const char *ssl_cmd_SSLSessionTickets(cmd_parms *cmd, void *dcfg, int flag)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+#ifndef SSL_OP_NO_TICKET
+ return "This version of OpenSSL does not support using "
+ "SSLSessionTickets.";
+#endif
+ sc->session_tickets = flag ? TRUE : FALSE;
+ return NULL;
+}
+
+const char *ssl_cmd_SSLInsecureRenegotiation(cmd_parms *cmd, void *dcfg, int flag)
+{
+#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ sc->insecure_reneg = flag?TRUE:FALSE;
+ return NULL;
+#else
+ return "The SSLInsecureRenegotiation directive is not available "
+ "with this SSL library";
+#endif
+}
+
+
+static const char *ssl_cmd_check_dir(cmd_parms *parms,
+ const char **dir)
+{
+ const char *dirpath = ap_server_root_relative(parms->pool, *dir);
+
+ if (!dirpath) {
+ return apr_pstrcat(parms->pool, parms->cmd->name,
+ ": Invalid dir path ", *dir, NULL);
+ }
+ *dir = dirpath;
+
+ if (ssl_util_path_check(SSL_FLAGS_CHECK_DIR, *dir, parms->pool)) {
+ return NULL;
+ }
+
+ return apr_pstrcat(parms->pool, parms->cmd->name,
+ ": directory '", *dir,
+ "' does not exist", NULL);
+
+}
+
+const char *ssl_cmd_SSLCertificateFile(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ const char *err;
+
+ /* Only check for non-ENGINE based certs. */
+ if (!modssl_is_engine_id(arg)
+ && (err = ssl_cmd_check_file(cmd, &arg))) {
+ return err;
+ }
+
+ *(const char **)apr_array_push(sc->server->pks->cert_files) = arg;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLCertificateKeyFile(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ const char *err;
+
+ /* Check keyfile exists for non-ENGINE keys. */
+ if (!modssl_is_engine_id(arg)
+ && (err = ssl_cmd_check_file(cmd, &arg))) {
+ return err;
+ }
+
+ *(const char **)apr_array_push(sc->server->pks->key_files) = arg;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLCertificateChainFile(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ const char *err;
+
+ if ((err = ssl_cmd_check_file(cmd, &arg))) {
+ return err;
+ }
+
+ sc->server->cert_chain = arg;
+
+ return NULL;
+}
+
+#ifdef HAVE_TLS_SESSION_TICKETS
+const char *ssl_cmd_SSLSessionTicketKeyFile(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ const char *err;
+
+ if ((err = ssl_cmd_check_file(cmd, &arg))) {
+ return err;
+ }
+
+ sc->server->ticket_key->file_path = arg;
+
+ return NULL;
+}
+#endif
+
+#define NO_PER_DIR_SSL_CA \
+ "Your SSL library does not have support for per-directory CA"
+
+const char *ssl_cmd_SSLCACertificatePath(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ /*SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;*/
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ const char *err;
+
+ if ((err = ssl_cmd_check_dir(cmd, &arg))) {
+ return err;
+ }
+
+ if (cmd->path) {
+ return NO_PER_DIR_SSL_CA;
+ }
+
+ /* XXX: bring back per-dir */
+ sc->server->auth.ca_cert_path = arg;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLCACertificateFile(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ /*SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;*/
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ const char *err;
+
+ if ((err = ssl_cmd_check_file(cmd, &arg))) {
+ return err;
+ }
+
+ if (cmd->path) {
+ return NO_PER_DIR_SSL_CA;
+ }
+
+ /* XXX: bring back per-dir */
+ sc->server->auth.ca_cert_file = arg;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLCADNRequestPath(cmd_parms *cmd, void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ const char *err;
+
+ if ((err = ssl_cmd_check_dir(cmd, &arg))) {
+ return err;
+ }
+
+ sc->server->pks->ca_name_path = arg;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLCADNRequestFile(cmd_parms *cmd, void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ const char *err;
+
+ if ((err = ssl_cmd_check_file(cmd, &arg))) {
+ return err;
+ }
+
+ sc->server->pks->ca_name_file = arg;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLCARevocationPath(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ const char *err;
+
+ if ((err = ssl_cmd_check_dir(cmd, &arg))) {
+ return err;
+ }
+
+ sc->server->crl_path = arg;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLCARevocationFile(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ const char *err;
+
+ if ((err = ssl_cmd_check_file(cmd, &arg))) {
+ return err;
+ }
+
+ sc->server->crl_file = arg;
+
+ return NULL;
+}
+
+static const char *ssl_cmd_crlcheck_parse(cmd_parms *parms,
+ const char *arg,
+ int *mask)
+{
+ const char *w;
+
+ w = ap_getword_conf(parms->temp_pool, &arg);
+ if (strcEQ(w, "none")) {
+ *mask = SSL_CRLCHECK_NONE;
+ }
+ else if (strcEQ(w, "leaf")) {
+ *mask = SSL_CRLCHECK_LEAF;
+ }
+ else if (strcEQ(w, "chain")) {
+ *mask = SSL_CRLCHECK_CHAIN;
+ }
+ else {
+ return apr_pstrcat(parms->temp_pool, parms->cmd->name,
+ ": Invalid argument '", w, "'",
+ NULL);
+ }
+
+ while (*arg) {
+ w = ap_getword_conf(parms->temp_pool, &arg);
+ if (strcEQ(w, "no_crl_for_cert_ok")) {
+ *mask |= SSL_CRLCHECK_NO_CRL_FOR_CERT_OK;
+ }
+ else {
+ return apr_pstrcat(parms->temp_pool, parms->cmd->name,
+ ": Invalid argument '", w, "'",
+ NULL);
+ }
+ }
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLCARevocationCheck(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+ return ssl_cmd_crlcheck_parse(cmd, arg, &sc->server->crl_check_mask);
+}
+
+static const char *ssl_cmd_verify_parse(cmd_parms *parms,
+ const char *arg,
+ ssl_verify_t *id)
+{
+ if (strcEQ(arg, "none") || strcEQ(arg, "off")) {
+ *id = SSL_CVERIFY_NONE;
+ }
+ else if (strcEQ(arg, "optional")) {
+ *id = SSL_CVERIFY_OPTIONAL;
+ }
+ else if (strcEQ(arg, "require") || strcEQ(arg, "on")) {
+ *id = SSL_CVERIFY_REQUIRE;
+ }
+ else if (strcEQ(arg, "optional_no_ca")) {
+ *id = SSL_CVERIFY_OPTIONAL_NO_CA;
+ }
+ else {
+ return apr_pstrcat(parms->temp_pool, parms->cmd->name,
+ ": Invalid argument '", arg, "'",
+ NULL);
+ }
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLVerifyClient(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ ssl_verify_t mode = SSL_CVERIFY_NONE;
+ const char *err;
+
+ if ((err = ssl_cmd_verify_parse(cmd, arg, &mode))) {
+ return err;
+ }
+
+ if (cmd->path) {
+ dc->nVerifyClient = mode;
+ }
+ else {
+ sc->server->auth.verify_mode = mode;
+ }
+
+ return NULL;
+}
+
+static const char *ssl_cmd_verify_depth_parse(cmd_parms *parms,
+ const char *arg,
+ int *depth)
+{
+ if ((*depth = atoi(arg)) >= 0) {
+ return NULL;
+ }
+
+ return apr_pstrcat(parms->temp_pool, parms->cmd->name,
+ ": Invalid argument '", arg, "'",
+ NULL);
+}
+
+const char *ssl_cmd_SSLVerifyDepth(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ int depth;
+ const char *err;
+
+ if ((err = ssl_cmd_verify_depth_parse(cmd, arg, &depth))) {
+ return err;
+ }
+
+ if (cmd->path) {
+ dc->nVerifyDepth = depth;
+ }
+ else {
+ sc->server->auth.verify_depth = depth;
+ }
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLSessionCache(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLModConfigRec *mc = myModConfig(cmd->server);
+ const char *err, *sep, *name;
+ long enabled_flags;
+
+ if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+ return err;
+ }
+
+ /* The OpenSSL session cache mode must have both the flags
+ * SSL_SESS_CACHE_SERVER and SSL_SESS_CACHE_NO_INTERNAL set if a
+ * session cache is configured; NO_INTERNAL prevents the
+ * OpenSSL-internal session cache being used in addition to the
+ * "external" (mod_ssl-provided) cache, which otherwise causes
+ * additional memory consumption. */
+ enabled_flags = SSL_SESS_CACHE_SERVER | SSL_SESS_CACHE_NO_INTERNAL;
+
+ if (strcEQ(arg, "none")) {
+ /* Nothing to do; session cache will be off. */
+ }
+ else if (strcEQ(arg, "nonenotnull")) {
+ /* ### Having a separate mode for this seems logically
+ * unnecessary; the stated purpose of sending non-empty
+ * session IDs would be better fixed in OpenSSL or simply
+ * doing it by default if "none" is used. */
+ mc->sesscache_mode = enabled_flags;
+ }
+ else {
+ /* Argument is of form 'name:args' or just 'name'. */
+ sep = ap_strchr_c(arg, ':');
+ if (sep) {
+ name = apr_pstrmemdup(cmd->pool, arg, sep - arg);
+ sep++;
+ }
+ else {
+ name = arg;
+ }
+
+ /* Find the provider of given name. */
+ mc->sesscache = ap_lookup_provider(AP_SOCACHE_PROVIDER_GROUP,
+ name,
+ AP_SOCACHE_PROVIDER_VERSION);
+ if (mc->sesscache) {
+ /* Cache found; create it, passing anything beyond the colon. */
+ mc->sesscache_mode = enabled_flags;
+ err = mc->sesscache->create(&mc->sesscache_context, sep,
+ cmd->temp_pool, cmd->pool);
+ }
+ else {
+ apr_array_header_t *name_list;
+ const char *all_names;
+
+ /* Build a comma-separated list of all registered provider
+ * names: */
+ name_list = ap_list_provider_names(cmd->pool,
+ AP_SOCACHE_PROVIDER_GROUP,
+ AP_SOCACHE_PROVIDER_VERSION);
+ all_names = apr_array_pstrcat(cmd->pool, name_list, ',');
+
+ err = apr_psprintf(cmd->pool, "'%s' session cache not supported "
+ "(known names: %s). Maybe you need to load the "
+ "appropriate socache module (mod_socache_%s?).",
+ name, all_names, name);
+ }
+ }
+
+ if (err) {
+ return apr_psprintf(cmd->pool, "SSLSessionCache: %s", err);
+ }
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLSessionCacheTimeout(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+ sc->session_cache_timeout = atoi(arg);
+
+ if (sc->session_cache_timeout < 0) {
+ return "SSLSessionCacheTimeout: Invalid argument";
+ }
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLOptions(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+ ssl_opt_t opt;
+ int first = TRUE;
+ char action, *w;
+
+ while (*arg) {
+ w = ap_getword_conf(cmd->temp_pool, &arg);
+ action = NUL;
+
+ if ((*w == '+') || (*w == '-')) {
+ action = *(w++);
+ }
+ else if (first) {
+ dc->nOptions = SSL_OPT_NONE;
+ first = FALSE;
+ }
+
+ if (strcEQ(w, "StdEnvVars")) {
+ opt = SSL_OPT_STDENVVARS;
+ }
+ else if (strcEQ(w, "ExportCertData")) {
+ opt = SSL_OPT_EXPORTCERTDATA;
+ }
+ else if (strcEQ(w, "FakeBasicAuth")) {
+ opt = SSL_OPT_FAKEBASICAUTH;
+ }
+ else if (strcEQ(w, "StrictRequire")) {
+ opt = SSL_OPT_STRICTREQUIRE;
+ }
+ else if (strcEQ(w, "OptRenegotiate")) {
+ opt = SSL_OPT_OPTRENEGOTIATE;
+ }
+ else if (strcEQ(w, "LegacyDNStringFormat")) {
+ opt = SSL_OPT_LEGACYDNFORMAT;
+ }
+ else {
+ return apr_pstrcat(cmd->pool,
+ "SSLOptions: Illegal option '", w, "'",
+ NULL);
+ }
+
+ if (action == '-') {
+ dc->nOptionsAdd &= ~opt;
+ dc->nOptionsDel |= opt;
+ dc->nOptions &= ~opt;
+ }
+ else if (action == '+') {
+ dc->nOptionsAdd |= opt;
+ dc->nOptionsDel &= ~opt;
+ dc->nOptions |= opt;
+ }
+ else {
+ dc->nOptions = opt;
+ dc->nOptionsAdd = opt;
+ dc->nOptionsDel = SSL_OPT_NONE;
+ }
+ }
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLRequireSSL(cmd_parms *cmd, void *dcfg)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+
+ dc->bSSLRequired = TRUE;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLRequire(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+ ap_expr_info_t *info = apr_pcalloc(cmd->pool, sizeof(ap_expr_info_t));
+ ssl_require_t *require;
+ const char *errstring;
+
+ info->flags = AP_EXPR_FLAG_SSL_EXPR_COMPAT;
+ info->filename = cmd->directive->filename;
+ info->line_number = cmd->directive->line_num;
+ info->module_index = APLOG_MODULE_INDEX;
+ errstring = ap_expr_parse(cmd->pool, cmd->temp_pool, info, arg, NULL);
+ if (errstring) {
+ return apr_pstrcat(cmd->pool, "SSLRequire: ", errstring, NULL);
+ }
+
+ require = apr_array_push(dc->aRequirement);
+ require->cpExpr = arg;
+ require->mpExpr = info;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLRenegBufferSize(cmd_parms *cmd, void *dcfg, const char *arg)
+{
+ SSLDirConfigRec *dc = dcfg;
+ int val;
+
+ val = atoi(arg);
+ if (val < 0) {
+ return apr_pstrcat(cmd->pool, "Invalid size for SSLRenegBufferSize: ",
+ arg, NULL);
+ }
+ dc->nRenegBufferSize = val;
+
+ return NULL;
+}
+
+static const char *ssl_cmd_protocol_parse(cmd_parms *parms,
+ const char *arg,
+ ssl_proto_t *options)
+{
+ ssl_proto_t thisopt;
+
+ *options = SSL_PROTOCOL_NONE;
+
+ while (*arg) {
+ char *w = ap_getword_conf(parms->temp_pool, &arg);
+ char action = '\0';
+
+ if ((*w == '+') || (*w == '-')) {
+ action = *(w++);
+ }
+
+ if (strcEQ(w, "SSLv2")) {
+ if (action == '-') {
+ continue;
+ }
+ else {
+ return "SSLProtocol: SSLv2 is no longer supported";
+ }
+ }
+ else if (strcEQ(w, "SSLv3")) {
+#ifdef OPENSSL_NO_SSL3
+ if (action != '-') {
+ return "SSLv3 not supported by this version of OpenSSL";
+ }
+ /* Nothing to do, the flag is not present to be toggled */
+ continue;
+#else
+ thisopt = SSL_PROTOCOL_SSLV3;
+#endif
+ }
+ else if (strcEQ(w, "TLSv1")) {
+ thisopt = SSL_PROTOCOL_TLSV1;
+ }
+#ifdef HAVE_TLSV1_X
+ else if (strcEQ(w, "TLSv1.1")) {
+ thisopt = SSL_PROTOCOL_TLSV1_1;
+ }
+ else if (strcEQ(w, "TLSv1.2")) {
+ thisopt = SSL_PROTOCOL_TLSV1_2;
+ }
+ else if (SSL_HAVE_PROTOCOL_TLSV1_3 && strcEQ(w, "TLSv1.3")) {
+ thisopt = SSL_PROTOCOL_TLSV1_3;
+ }
+#endif
+ else if (strcEQ(w, "all")) {
+ thisopt = SSL_PROTOCOL_ALL;
+ }
+ else {
+ return apr_pstrcat(parms->temp_pool,
+ parms->cmd->name,
+ ": Illegal protocol '", w, "'", NULL);
+ }
+
+ if (action == '-') {
+ *options &= ~thisopt;
+ }
+ else if (action == '+') {
+ *options |= thisopt;
+ }
+ else {
+ if (*options != SSL_PROTOCOL_NONE) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, parms->server, APLOGNO(02532)
+ "%s: Protocol '%s' overrides already set parameter(s). "
+ "Check if a +/- prefix is missing.",
+ parms->cmd->name, w);
+ }
+ *options = thisopt;
+ }
+ }
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLProtocol(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+ sc->server->protocol_set = 1;
+ return ssl_cmd_protocol_parse(cmd, arg, &sc->server->protocol);
+}
+
+const char *ssl_cmd_SSLProxyEngine(cmd_parms *cmd, void *dcfg, int flag)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+
+ dc->proxy_enabled = flag ? TRUE : FALSE;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLProxyProtocol(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+
+ dc->proxy->protocol_set = 1;
+ return ssl_cmd_protocol_parse(cmd, arg, &dc->proxy->protocol);
+}
+
+const char *ssl_cmd_SSLProxyCipherSuite(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg1, const char *arg2)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+
+ if (arg2 == NULL) {
+ arg2 = arg1;
+ arg1 = "SSL";
+ }
+
+ if (!strcmp("SSL", arg1)) {
+ /* always disable null and export ciphers */
+ arg2 = apr_pstrcat(cmd->pool, arg2, ":!aNULL:!eNULL:!EXP", NULL);
+ dc->proxy->auth.cipher_suite = arg2;
+ return NULL;
+ }
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+ else if (!strcmp("TLSv1.3", arg1)) {
+ dc->proxy->auth.tls13_ciphers = arg2;
+ return NULL;
+ }
+#endif
+ return apr_pstrcat(cmd->pool, "protocol '", arg1, "' not supported", NULL);
+}
+
+const char *ssl_cmd_SSLProxyVerify(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+ ssl_verify_t mode = SSL_CVERIFY_NONE;
+ const char *err;
+
+ if ((err = ssl_cmd_verify_parse(cmd, arg, &mode))) {
+ return err;
+ }
+
+ dc->proxy->auth.verify_mode = mode;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLProxyVerifyDepth(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+ int depth;
+ const char *err;
+
+ if ((err = ssl_cmd_verify_depth_parse(cmd, arg, &depth))) {
+ return err;
+ }
+
+ dc->proxy->auth.verify_depth = depth;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCACertificateFile(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+ const char *err;
+
+ if ((err = ssl_cmd_check_file(cmd, &arg))) {
+ return err;
+ }
+
+ dc->proxy->auth.ca_cert_file = arg;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCACertificatePath(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+ const char *err;
+
+ if ((err = ssl_cmd_check_dir(cmd, &arg))) {
+ return err;
+ }
+
+ dc->proxy->auth.ca_cert_path = arg;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCARevocationPath(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+ const char *err;
+
+ if ((err = ssl_cmd_check_dir(cmd, &arg))) {
+ return err;
+ }
+
+ dc->proxy->crl_path = arg;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCARevocationFile(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+ const char *err;
+
+ if ((err = ssl_cmd_check_file(cmd, &arg))) {
+ return err;
+ }
+
+ dc->proxy->crl_file = arg;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCARevocationCheck(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+
+ return ssl_cmd_crlcheck_parse(cmd, arg, &dc->proxy->crl_check_mask);
+}
+
+const char *ssl_cmd_SSLProxyMachineCertificateFile(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+ const char *err;
+
+ if ((err = ssl_cmd_check_file(cmd, &arg))) {
+ return err;
+ }
+
+ dc->proxy->pkp->cert_file = arg;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLProxyMachineCertificatePath(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+ const char *err;
+
+ if ((err = ssl_cmd_check_dir(cmd, &arg))) {
+ return err;
+ }
+
+ dc->proxy->pkp->cert_path = arg;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLProxyMachineCertificateChainFile(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+ const char *err;
+
+ if ((err = ssl_cmd_check_file(cmd, &arg))) {
+ return err;
+ }
+
+ dc->proxy->pkp->ca_cert_file = arg;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLUserName(cmd_parms *cmd, void *dcfg,
+ const char *arg)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+ dc->szUserName = arg;
+ return NULL;
+}
+
+static const char *ssl_cmd_ocspcheck_parse(cmd_parms *parms,
+ const char *arg,
+ int *mask)
+{
+ const char *w;
+
+ w = ap_getword_conf(parms->temp_pool, &arg);
+ if (strcEQ(w, "off")) {
+ *mask = SSL_OCSPCHECK_NONE;
+ }
+ else if (strcEQ(w, "leaf")) {
+ *mask = SSL_OCSPCHECK_LEAF;
+ }
+ else if (strcEQ(w, "on")) {
+ *mask = SSL_OCSPCHECK_CHAIN;
+ }
+ else {
+ return apr_pstrcat(parms->temp_pool, parms->cmd->name,
+ ": Invalid argument '", w, "'",
+ NULL);
+ }
+
+ while (*arg) {
+ w = ap_getword_conf(parms->temp_pool, &arg);
+ if (strcEQ(w, "no_ocsp_for_cert_ok")) {
+ *mask |= SSL_OCSPCHECK_NO_OCSP_FOR_CERT_OK;
+ }
+ else {
+ return apr_pstrcat(parms->temp_pool, parms->cmd->name,
+ ": Invalid argument '", w, "'",
+ NULL);
+ }
+ }
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLOCSPEnable(cmd_parms *cmd, void *dcfg, const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+#ifdef OPENSSL_NO_OCSP
+ if (flag) {
+ return "OCSP support disabled in SSL library; cannot enable "
+ "OCSP validation";
+ }
+#endif
+
+ return ssl_cmd_ocspcheck_parse(cmd, arg, &sc->server->ocsp_mask);
+}
+
+const char *ssl_cmd_SSLOCSPOverrideResponder(cmd_parms *cmd, void *dcfg, int flag)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+ sc->server->ocsp_force_default = flag ? TRUE : FALSE;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLOCSPDefaultResponder(cmd_parms *cmd, void *dcfg, const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+ sc->server->ocsp_responder = arg;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLOCSPResponseTimeSkew(cmd_parms *cmd, void *dcfg, const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ sc->server->ocsp_resptime_skew = atoi(arg);
+ if (sc->server->ocsp_resptime_skew < 0) {
+ return "SSLOCSPResponseTimeSkew: invalid argument";
+ }
+ return NULL;
+}
+
+const char *ssl_cmd_SSLOCSPResponseMaxAge(cmd_parms *cmd, void *dcfg, const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ sc->server->ocsp_resp_maxage = atoi(arg);
+ if (sc->server->ocsp_resp_maxage < 0) {
+ return "SSLOCSPResponseMaxAge: invalid argument";
+ }
+ return NULL;
+}
+
+const char *ssl_cmd_SSLOCSPResponderTimeout(cmd_parms *cmd, void *dcfg, const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ sc->server->ocsp_responder_timeout = apr_time_from_sec(atoi(arg));
+ if (sc->server->ocsp_responder_timeout < 0) {
+ return "SSLOCSPResponderTimeout: invalid argument";
+ }
+ return NULL;
+}
+
+const char *ssl_cmd_SSLOCSPUseRequestNonce(cmd_parms *cmd, void *dcfg, int flag)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+ sc->server->ocsp_use_request_nonce = flag ? TRUE : FALSE;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLOCSPProxyURL(cmd_parms *cmd, void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ sc->server->proxy_uri = apr_palloc(cmd->pool, sizeof(apr_uri_t));
+ if (apr_uri_parse(cmd->pool, arg, sc->server->proxy_uri) != APR_SUCCESS) {
+ return apr_psprintf(cmd->pool,
+ "SSLOCSPProxyURL: Cannot parse URL %s", arg);
+ }
+ return NULL;
+}
+
+/* Set OCSP responder certificate verification directive */
+const char *ssl_cmd_SSLOCSPNoVerify(cmd_parms *cmd, void *dcfg, int flag)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+ sc->server->ocsp_noverify = flag ? TRUE : FALSE;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCheckPeerExpire(cmd_parms *cmd, void *dcfg, int flag)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+
+ dc->proxy->ssl_check_peer_expire = flag ? TRUE : FALSE;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCheckPeerCN(cmd_parms *cmd, void *dcfg, int flag)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+
+ dc->proxy->ssl_check_peer_cn = flag ? TRUE : FALSE;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCheckPeerName(cmd_parms *cmd, void *dcfg, int flag)
+{
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+
+ dc->proxy->ssl_check_peer_name = flag ? TRUE : FALSE;
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLStrictSNIVHostCheck(cmd_parms *cmd, void *dcfg, int flag)
+{
+#ifdef HAVE_TLSEXT
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+ sc->strict_sni_vhost_check = flag ? SSL_ENABLED_TRUE : SSL_ENABLED_FALSE;
+
+ return NULL;
+#else
+ return "SSLStrictSNIVHostCheck failed; OpenSSL is not built with support "
+ "for TLS extensions and SNI indication. Refer to the "
+ "documentation, and build a compatible version of OpenSSL.";
+#endif
+}
+
+#ifdef HAVE_OCSP_STAPLING
+
+const char *ssl_cmd_SSLStaplingCache(cmd_parms *cmd,
+ void *dcfg,
+ const char *arg)
+{
+ SSLModConfigRec *mc = myModConfig(cmd->server);
+ const char *err, *sep, *name;
+
+ if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+ return err;
+ }
+
+ /* Argument is of form 'name:args' or just 'name'. */
+ sep = ap_strchr_c(arg, ':');
+ if (sep) {
+ name = apr_pstrmemdup(cmd->pool, arg, sep - arg);
+ sep++;
+ }
+ else {
+ name = arg;
+ }
+
+ /* Find the provider of given name. */
+ mc->stapling_cache = ap_lookup_provider(AP_SOCACHE_PROVIDER_GROUP,
+ name,
+ AP_SOCACHE_PROVIDER_VERSION);
+ if (mc->stapling_cache) {
+ /* Cache found; create it, passing anything beyond the colon. */
+ err = mc->stapling_cache->create(&mc->stapling_cache_context,
+ sep, cmd->temp_pool,
+ cmd->pool);
+ }
+ else {
+ apr_array_header_t *name_list;
+ const char *all_names;
+
+ /* Build a comma-separated list of all registered provider
+ * names: */
+ name_list = ap_list_provider_names(cmd->pool,
+ AP_SOCACHE_PROVIDER_GROUP,
+ AP_SOCACHE_PROVIDER_VERSION);
+ all_names = apr_array_pstrcat(cmd->pool, name_list, ',');
+
+ err = apr_psprintf(cmd->pool, "'%s' stapling cache not supported "
+ "(known names: %s) Maybe you need to load the "
+ "appropriate socache module (mod_socache_%s?)",
+ name, all_names, name);
+ }
+
+ if (err) {
+ return apr_psprintf(cmd->pool, "SSLStaplingCache: %s", err);
+ }
+
+ return NULL;
+}
+
+const char *ssl_cmd_SSLUseStapling(cmd_parms *cmd, void *dcfg, int flag)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ sc->server->stapling_enabled = flag ? TRUE : FALSE;
+ return NULL;
+}
+
+const char *ssl_cmd_SSLStaplingResponseTimeSkew(cmd_parms *cmd, void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ sc->server->stapling_resptime_skew = atoi(arg);
+ if (sc->server->stapling_resptime_skew < 0) {
+ return "SSLStaplingResponseTimeSkew: invalid argument";
+ }
+ return NULL;
+}
+
+const char *ssl_cmd_SSLStaplingResponseMaxAge(cmd_parms *cmd, void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ sc->server->stapling_resp_maxage = atoi(arg);
+ if (sc->server->stapling_resp_maxage < 0) {
+ return "SSLStaplingResponseMaxAge: invalid argument";
+ }
+ return NULL;
+}
+
+const char *ssl_cmd_SSLStaplingStandardCacheTimeout(cmd_parms *cmd, void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ sc->server->stapling_cache_timeout = atoi(arg);
+ if (sc->server->stapling_cache_timeout < 0) {
+ return "SSLStaplingStandardCacheTimeout: invalid argument";
+ }
+ return NULL;
+}
+
+const char *ssl_cmd_SSLStaplingErrorCacheTimeout(cmd_parms *cmd, void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ sc->server->stapling_errcache_timeout = atoi(arg);
+ if (sc->server->stapling_errcache_timeout < 0) {
+ return "SSLStaplingErrorCacheTimeout: invalid argument";
+ }
+ return NULL;
+}
+
+const char *ssl_cmd_SSLStaplingReturnResponderErrors(cmd_parms *cmd,
+ void *dcfg, int flag)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ sc->server->stapling_return_errors = flag ? TRUE : FALSE;
+ return NULL;
+}
+
+const char *ssl_cmd_SSLStaplingFakeTryLater(cmd_parms *cmd,
+ void *dcfg, int flag)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ sc->server->stapling_fake_trylater = flag ? TRUE : FALSE;
+ return NULL;
+}
+
+const char *ssl_cmd_SSLStaplingResponderTimeout(cmd_parms *cmd, void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ sc->server->stapling_responder_timeout = atoi(arg);
+ sc->server->stapling_responder_timeout *= APR_USEC_PER_SEC;
+ if (sc->server->stapling_responder_timeout < 0) {
+ return "SSLStaplingResponderTimeout: invalid argument";
+ }
+ return NULL;
+}
+
+const char *ssl_cmd_SSLStaplingForceURL(cmd_parms *cmd, void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ sc->server->stapling_force_url = arg;
+ return NULL;
+}
+
+#endif /* HAVE_OCSP_STAPLING */
+
+#ifdef HAVE_SSL_CONF_CMD
+const char *ssl_cmd_SSLOpenSSLConfCmd(cmd_parms *cmd, void *dcfg,
+ const char *arg1, const char *arg2)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ SSL_CONF_CTX *cctx = sc->server->ssl_ctx_config;
+ int value_type = SSL_CONF_cmd_value_type(cctx, arg1);
+ const char *err;
+ ssl_ctx_param_t *param;
+
+ if (value_type == SSL_CONF_TYPE_UNKNOWN) {
+ return apr_psprintf(cmd->pool,
+ "'%s': invalid OpenSSL configuration command",
+ arg1);
+ }
+
+ if (value_type == SSL_CONF_TYPE_FILE) {
+ if ((err = ssl_cmd_check_file(cmd, &arg2)))
+ return err;
+ }
+ else if (value_type == SSL_CONF_TYPE_DIR) {
+ if ((err = ssl_cmd_check_dir(cmd, &arg2)))
+ return err;
+ }
+
+ if (strcEQ(arg1, "CipherString")) {
+ /* always disable null and export ciphers */
+ arg2 = apr_pstrcat(cmd->pool, arg2, ":!aNULL:!eNULL:!EXP", NULL);
+ }
+
+ param = apr_array_push(sc->server->ssl_ctx_param);
+ param->name = arg1;
+ param->value = arg2;
+ return NULL;
+}
+#endif
+
+#ifdef HAVE_SRP
+
+const char *ssl_cmd_SSLSRPVerifierFile(cmd_parms *cmd, void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ const char *err;
+
+ if ((err = ssl_cmd_check_file(cmd, &arg)))
+ return err;
+ /* SRP_VBASE_init takes char*, not const char* */
+ sc->server->srp_vfile = apr_pstrdup(cmd->pool, arg);
+ return NULL;
+}
+
+const char *ssl_cmd_SSLSRPUnknownUserSeed(cmd_parms *cmd, void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ /* SRP_VBASE_new takes char*, not const char* */
+ sc->server->srp_unknown_user_seed = apr_pstrdup(cmd->pool, arg);
+ return NULL;
+}
+
+#endif /* HAVE_SRP */
+
+/* OCSP Responder File Function to read in value */
+const char *ssl_cmd_SSLOCSPResponderCertificateFile(cmd_parms *cmd, void *dcfg,
+ const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ const char *err;
+
+ if ((err = ssl_cmd_check_file(cmd, &arg))) {
+ return err;
+ }
+
+ sc->server->ocsp_certs_file = arg;
+ return NULL;
+}
+
+void ssl_hook_ConfigTest(apr_pool_t *pconf, server_rec *s)
+{
+ apr_file_t *out = NULL;
+ if (!ap_exists_config_define("DUMP_CERTS")) {
+ return;
+ }
+ apr_file_open_stdout(&out, pconf);
+ apr_file_printf(out, "Server certificates:\n");
+
+ /* Dump the filenames of all configured server certificates to
+ * stdout. */
+ while (s) {
+ SSLSrvConfigRec *sc = mySrvConfig(s);
+
+ if (sc && sc->server && sc->server->pks) {
+ modssl_pk_server_t *const pks = sc->server->pks;
+ int i;
+
+ for (i = 0; (i < pks->cert_files->nelts) &&
+ APR_ARRAY_IDX(pks->cert_files, i, const char *);
+ i++) {
+ apr_file_printf(out, " %s\n",
+ APR_ARRAY_IDX(pks->cert_files,
+ i, const char *));
+ }
+ }
+
+ s = s->next;
+ }
+
+}
+
diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c
new file mode 100644
index 0000000..825621d
--- /dev/null
+++ b/modules/ssl/ssl_engine_init.c
@@ -0,0 +1,2367 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+/* _ _
+ * _ __ ___ ___ __| | ___ ___| | mod_ssl
+ * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| | \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ * |_____|
+ * ssl_engine_init.c
+ * Initialization of Servers
+ */
+ /* ``Recursive, adj.;
+ see Recursive.''
+ -- Unknown */
+#include "ssl_private.h"
+#include "mod_ssl.h"
+#include "mod_ssl_openssl.h"
+#include "mpm_common.h"
+#include "mod_md.h"
+
+static apr_status_t ssl_init_ca_cert_path(server_rec *, apr_pool_t *, const char *,
+ STACK_OF(X509_NAME) *, STACK_OF(X509_INFO) *);
+
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, init_server,
+ (server_rec *s,apr_pool_t *p,int is_proxy,SSL_CTX *ctx),
+ (s,p,is_proxy,ctx), OK, DECLINED)
+
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, add_cert_files,
+ (server_rec *s, apr_pool_t *p,
+ apr_array_header_t *cert_files, apr_array_header_t *key_files),
+ (s, p, cert_files, key_files),
+ OK, DECLINED)
+
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, add_fallback_cert_files,
+ (server_rec *s, apr_pool_t *p,
+ apr_array_header_t *cert_files, apr_array_header_t *key_files),
+ (s, p, cert_files, key_files),
+ OK, DECLINED)
+
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, answer_challenge,
+ (conn_rec *c, const char *server_name,
+ X509 **pcert, EVP_PKEY **pkey),
+ (c, server_name, pcert, pkey),
+ DECLINED, DECLINED)
+
+
+/* _________________________________________________________________
+**
+** Module Initialization
+** _________________________________________________________________
+*/
+
+#ifdef HAVE_ECC
+#define KEYTYPES "RSA, DSA or ECC"
+#else
+#define KEYTYPES "RSA or DSA"
+#endif
+
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
+/* OpenSSL Pre-1.1.0 compatibility */
+/* Taken from OpenSSL 1.1.0 snapshot 20160410 */
+static int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
+{
+ /* q is optional */
+ if (p == NULL || g == NULL)
+ return 0;
+ BN_free(dh->p);
+ BN_free(dh->q);
+ BN_free(dh->g);
+ dh->p = p;
+ dh->q = q;
+ dh->g = g;
+
+ if (q != NULL) {
+ dh->length = BN_num_bits(q);
+ }
+
+ return 1;
+}
+
+/*
+ * Grab well-defined DH parameters from OpenSSL, see the BN_get_rfc*
+ * functions in <openssl/bn.h> for all available primes.
+ */
+static DH *make_dh_params(BIGNUM *(*prime)(BIGNUM *))
+{
+ DH *dh = DH_new();
+ BIGNUM *p, *g;
+
+ if (!dh) {
+ return NULL;
+ }
+ p = prime(NULL);
+ g = BN_new();
+ if (g != NULL) {
+ BN_set_word(g, 2);
+ }
+ if (!p || !g || !DH_set0_pqg(dh, p, NULL, g)) {
+ DH_free(dh);
+ BN_free(p);
+ BN_free(g);
+ return NULL;
+ }
+ return dh;
+}
+
+/* Storage and initialization for DH parameters. */
+static struct dhparam {
+ BIGNUM *(*const prime)(BIGNUM *); /* function to generate... */
+ DH *dh; /* ...this, used for keys.... */
+ const unsigned int min; /* ...of length >= this. */
+} dhparams[] = {
+ { BN_get_rfc3526_prime_8192, NULL, 6145 },
+ { BN_get_rfc3526_prime_6144, NULL, 4097 },
+ { BN_get_rfc3526_prime_4096, NULL, 3073 },
+ { BN_get_rfc3526_prime_3072, NULL, 2049 },
+ { BN_get_rfc3526_prime_2048, NULL, 1025 },
+ { BN_get_rfc2409_prime_1024, NULL, 0 }
+};
+
+static void init_dh_params(void)
+{
+ unsigned n;
+
+ for (n = 0; n < sizeof(dhparams)/sizeof(dhparams[0]); n++)
+ dhparams[n].dh = make_dh_params(dhparams[n].prime);
+}
+
+static void free_dh_params(void)
+{
+ unsigned n;
+
+ /* DH_free() is a noop for a NULL parameter, so these are harmless
+ * in the (unexpected) case where these variables are already
+ * NULL. */
+ for (n = 0; n < sizeof(dhparams)/sizeof(dhparams[0]); n++) {
+ DH_free(dhparams[n].dh);
+ dhparams[n].dh = NULL;
+ }
+}
+
+/* Hand out the same DH structure though once generated as we leak
+ * memory otherwise and freeing the structure up after use would be
+ * hard to track and in fact is not needed at all as it is safe to
+ * use the same parameters over and over again security wise (in
+ * contrast to the keys itself) and code safe as the returned structure
+ * is duplicated by OpenSSL anyway. Hence no modification happens
+ * to our copy. */
+DH *modssl_get_dh_params(unsigned keylen)
+{
+ unsigned n;
+
+ for (n = 0; n < sizeof(dhparams)/sizeof(dhparams[0]); n++)
+ if (keylen >= dhparams[n].min)
+ return dhparams[n].dh;
+
+ return NULL; /* impossible to reach. */
+}
+#endif
+
+static void ssl_add_version_components(apr_pool_t *ptemp, apr_pool_t *pconf,
+ server_rec *s)
+{
+ char *modver = ssl_var_lookup(ptemp, s, NULL, NULL, "SSL_VERSION_INTERFACE");
+ char *libver = ssl_var_lookup(ptemp, s, NULL, NULL, "SSL_VERSION_LIBRARY");
+ char *incver = ssl_var_lookup(ptemp, s, NULL, NULL,
+ "SSL_VERSION_LIBRARY_INTERFACE");
+
+ ap_add_version_component(pconf, libver);
+
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01876)
+ "%s compiled against Server: %s, Library: %s",
+ modver, AP_SERVER_BASEVERSION, incver);
+}
+
+/* _________________________________________________________________
+**
+** Let other answer special connection attempts.
+** Used in ACME challenge handling by mod_md.
+** _________________________________________________________________
+*/
+
+int ssl_is_challenge(conn_rec *c, const char *servername,
+ X509 **pcert, EVP_PKEY **pkey,
+ const char **pcert_pem, const char **pkey_pem)
+{
+ *pcert = NULL;
+ *pkey = NULL;
+ *pcert_pem = *pkey_pem = NULL;
+ if (ap_ssl_answer_challenge(c, servername, pcert_pem, pkey_pem)) {
+ return 1;
+ }
+ else if (OK == ssl_run_answer_challenge(c, servername, pcert, pkey)) {
+ return 1;
+ }
+ return 0;
+}
+
+#ifdef HAVE_FIPS
+static apr_status_t modssl_fips_cleanup(void *data)
+{
+ modssl_fips_enable(0);
+ return APR_SUCCESS;
+}
+#endif
+
+/*
+ * Per-module initialization
+ */
+apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp,
+ server_rec *base_server)
+{
+ SSLModConfigRec *mc = myModConfig(base_server);
+ SSLSrvConfigRec *sc;
+ server_rec *s;
+ apr_status_t rv;
+ apr_array_header_t *pphrases;
+
+ if (SSLeay() < MODSSL_LIBRARY_VERSION) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server, APLOGNO(01882)
+ "Init: this version of mod_ssl was compiled against "
+ "a newer library (%s, version currently loaded is %s)"
+ " - may result in undefined or erroneous behavior",
+ MODSSL_LIBRARY_TEXT, MODSSL_LIBRARY_DYNTEXT);
+ }
+
+ /* We initialize mc->pid per-process in the child init,
+ * but it should be initialized for startup before we
+ * call ssl_rand_seed() below.
+ */
+ mc->pid = getpid();
+
+ /*
+ * Let us cleanup on restarts and exits
+ */
+ apr_pool_cleanup_register(p, base_server,
+ ssl_init_ModuleKill,
+ apr_pool_cleanup_null);
+
+ /*
+ * Any init round fixes the global config
+ */
+ ssl_config_global_create(base_server); /* just to avoid problems */
+ ssl_config_global_fix(mc);
+
+ /*
+ * try to fix the configuration and open the dedicated SSL
+ * logfile as early as possible
+ */
+ for (s = base_server; s; s = s->next) {
+ sc = mySrvConfig(s);
+
+ if (sc->server) {
+ sc->server->sc = sc;
+ }
+
+ /*
+ * Create the server host:port string because we need it a lot
+ */
+ if (sc->vhost_id) {
+ /* already set. This should only happen if this config rec is
+ * shared with another server. Argh! */
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10104)
+ "%s, SSLSrvConfigRec shared from %s",
+ ssl_util_vhostid(p, s), sc->vhost_id);
+ }
+ sc->vhost_id = ssl_util_vhostid(p, s);
+ sc->vhost_id_len = strlen(sc->vhost_id);
+
+ /* Default to enabled if SSLEngine is not set explicitly, and
+ * the protocol is https. */
+ if (ap_get_server_protocol(s)
+ && strcmp("https", ap_get_server_protocol(s)) == 0
+ && sc->enabled == SSL_ENABLED_UNSET
+ && (!apr_is_empty_array(sc->server->pks->cert_files))) {
+ sc->enabled = SSL_ENABLED_TRUE;
+ }
+
+ /* Fix up stuff that may not have been set. If sc->enabled is
+ * UNSET, then SSL is disabled on this vhost. */
+ if (sc->enabled == SSL_ENABLED_UNSET) {
+ sc->enabled = SSL_ENABLED_FALSE;
+ }
+
+ if (sc->session_cache_timeout == UNSET) {
+ sc->session_cache_timeout = SSL_SESSION_CACHE_TIMEOUT;
+ }
+
+ if (sc->server && sc->server->pphrase_dialog_type == SSL_PPTYPE_UNSET) {
+ sc->server->pphrase_dialog_type = SSL_PPTYPE_BUILTIN;
+ }
+ }
+
+#if APR_HAS_THREADS && MODSSL_USE_OPENSSL_PRE_1_1_API
+ ssl_util_thread_setup(p);
+#endif
+
+ /*
+ * SSL external crypto device ("engine") support
+ */
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
+ if ((rv = ssl_init_Engine(base_server, p)) != APR_SUCCESS) {
+ return rv;
+ }
+#endif
+
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, base_server, APLOGNO(01883)
+ "Init: Initialized %s library", MODSSL_LIBRARY_NAME);
+
+ /*
+ * Seed the Pseudo Random Number Generator (PRNG)
+ * only need ptemp here; nothing inside allocated from the pool
+ * needs to live once we return from ssl_rand_seed().
+ */
+ ssl_rand_seed(base_server, ptemp, SSL_RSCTX_STARTUP, "Init: ");
+
+#ifdef HAVE_FIPS
+ if (!modssl_fips_is_enabled() && mc->fips == TRUE) {
+ if (!modssl_fips_enable(1)) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, base_server, APLOGNO(01885)
+ "Could not enable FIPS mode");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, base_server);
+ return ssl_die(base_server);
+ }
+
+ apr_pool_cleanup_register(p, NULL, modssl_fips_cleanup,
+ apr_pool_cleanup_null);
+ }
+
+ /* Log actual FIPS mode which the SSL library is operating under,
+ * which may have been set outside of the mod_ssl
+ * configuration. */
+ if (modssl_fips_is_enabled()) {
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, base_server, APLOGNO(01884)
+ MODSSL_LIBRARY_NAME " has FIPS mode enabled");
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server, APLOGNO(01886)
+ MODSSL_LIBRARY_NAME " has FIPS mode disabled");
+ }
+#endif
+
+ /*
+ * initialize the mutex handling
+ */
+ if (!ssl_mutex_init(base_server, p)) {
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+#ifdef HAVE_OCSP_STAPLING
+ ssl_stapling_certinfo_hash_init(p);
+#endif
+
+ /*
+ * initialize session caching
+ */
+ if ((rv = ssl_scache_init(base_server, p)) != APR_SUCCESS) {
+ return rv;
+ }
+
+ pphrases = apr_array_make(ptemp, 2, sizeof(char *));
+
+ /*
+ * initialize servers
+ */
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, base_server, APLOGNO(01887)
+ "Init: Initializing (virtual) servers for SSL");
+
+ for (s = base_server; s; s = s->next) {
+ sc = mySrvConfig(s);
+ /*
+ * Either now skip this server when SSL is disabled for
+ * it or give out some information about what we're
+ * configuring.
+ */
+
+ /*
+ * Read the server certificate and key
+ */
+ if ((rv = ssl_init_ConfigureServer(s, p, ptemp, sc, pphrases))
+ != APR_SUCCESS) {
+ return rv;
+ }
+ }
+
+ if (pphrases->nelts > 0) {
+ memset(pphrases->elts, 0, pphrases->elt_size * pphrases->nelts);
+ pphrases->nelts = 0;
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(02560)
+ "Init: Wiped out the queried pass phrases from memory");
+ }
+
+ /*
+ * Configuration consistency checks
+ */
+ if ((rv = ssl_init_CheckServers(base_server, ptemp)) != APR_SUCCESS) {
+ return rv;
+ }
+
+ for (s = base_server; s; s = s->next) {
+ SSLDirConfigRec *sdc = ap_get_module_config(s->lookup_defaults,
+ &ssl_module);
+
+ sc = mySrvConfig(s);
+ if (sc->enabled == SSL_ENABLED_TRUE || sc->enabled == SSL_ENABLED_OPTIONAL) {
+ if ((rv = ssl_run_init_server(s, p, 0, sc->server->ssl_ctx)) != APR_SUCCESS) {
+ return rv;
+ }
+ }
+
+ if (sdc->proxy_enabled) {
+ rv = ssl_run_init_server(s, p, 1, sdc->proxy->ssl_ctx);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ }
+ }
+
+ /*
+ * Announce mod_ssl and SSL library in HTTP Server field
+ * as ``mod_ssl/X.X.X OpenSSL/X.X.X''
+ */
+ ssl_add_version_components(ptemp, p, base_server);
+
+ modssl_init_app_data2_idx(); /* for modssl_get_app_data2() at request time */
+
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
+ init_dh_params();
+#else
+ init_bio_methods();
+#endif
+
+#ifdef HAVE_OPENSSL_KEYLOG
+ {
+ const char *logfn = getenv("SSLKEYLOGFILE");
+
+ if (logfn) {
+ rv = apr_file_open(&mc->keylog_file, logfn,
+ APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_APPEND|APR_FOPEN_LARGEFILE,
+ APR_FPROT_UREAD|APR_FPROT_UWRITE,
+ mc->pPool);
+ if (rv) {
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, s, APLOGNO(10226)
+ "Could not open log file '%s' configured via SSLKEYLOGFILE",
+ logfn);
+ return rv;
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(10227)
+ "Init: Logging SSL private key material to %s", logfn);
+ }
+ }
+#endif
+
+ return OK;
+}
+
+/*
+ * Support for external a Crypto Device ("engine"), usually
+ * a hardware accelerator card for crypto operations.
+ */
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
+apr_status_t ssl_init_Engine(server_rec *s, apr_pool_t *p)
+{
+ SSLModConfigRec *mc = myModConfig(s);
+ ENGINE *e;
+
+ if (mc->szCryptoDevice) {
+ if (!(e = ENGINE_by_id(mc->szCryptoDevice))) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01888)
+ "Init: Failed to load Crypto Device API `%s'",
+ mc->szCryptoDevice);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+#ifdef ENGINE_CTRL_CHIL_SET_FORKCHECK
+ if (strEQ(mc->szCryptoDevice, "chil")) {
+ ENGINE_ctrl(e, ENGINE_CTRL_CHIL_SET_FORKCHECK, 1, 0, 0);
+ }
+#endif
+
+ if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01889)
+ "Init: Failed to enable Crypto Device API `%s'",
+ mc->szCryptoDevice);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01890)
+ "Init: loaded Crypto Device API `%s'",
+ mc->szCryptoDevice);
+
+ ENGINE_free(e);
+ }
+
+ return APR_SUCCESS;
+}
+#endif
+
+#ifdef HAVE_TLSEXT
+static apr_status_t ssl_init_ctx_tls_extensions(server_rec *s,
+ apr_pool_t *p,
+ apr_pool_t *ptemp,
+ modssl_ctx_t *mctx)
+{
+ apr_status_t rv;
+
+ /*
+ * Configure TLS extensions support
+ */
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01893)
+ "Configuring TLS extension handling");
+
+ /*
+ * The Server Name Indication (SNI) provided by the ClientHello can be
+ * used to select the right (name-based-)vhost and its SSL configuration
+ * before the handshake takes place.
+ */
+ if (!SSL_CTX_set_tlsext_servername_callback(mctx->ssl_ctx,
+ ssl_callback_ServerNameIndication) ||
+ !SSL_CTX_set_tlsext_servername_arg(mctx->ssl_ctx, mctx)) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01894)
+ "Unable to initialize TLS servername extension "
+ "callback (incompatible OpenSSL version?)");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
+ /*
+ * The ClientHello callback also allows to retrieve the SNI, but since it
+ * runs at the earliest possible connection stage we can even set the TLS
+ * protocol version(s) according to the selected (name-based-)vhost, which
+ * is not possible at the SNI callback stage (due to OpenSSL internals).
+ */
+ SSL_CTX_set_client_hello_cb(mctx->ssl_ctx, ssl_callback_ClientHello, NULL);
+#endif
+
+#ifdef HAVE_OCSP_STAPLING
+ /*
+ * OCSP Stapling support, status_request extension
+ */
+ if ((mctx->pkp == FALSE) && (mctx->stapling_enabled == TRUE)) {
+ if ((rv = modssl_init_stapling(s, p, ptemp, mctx)) != APR_SUCCESS) {
+ return rv;
+ }
+ }
+#endif
+
+#ifdef HAVE_SRP
+ /*
+ * TLS-SRP support
+ */
+ if (mctx->srp_vfile != NULL) {
+ int err;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02308)
+ "Using SRP verifier file [%s]", mctx->srp_vfile);
+
+ if (!(mctx->srp_vbase = SRP_VBASE_new(mctx->srp_unknown_user_seed))) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02309)
+ "Unable to initialize SRP verifier structure "
+ "[%s seed]",
+ mctx->srp_unknown_user_seed ? "with" : "without");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+ err = SRP_VBASE_init(mctx->srp_vbase, mctx->srp_vfile);
+ if (err != SRP_NO_ERROR) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02310)
+ "Unable to load SRP verifier file [error %d]", err);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+ SSL_CTX_set_srp_username_callback(mctx->ssl_ctx,
+ ssl_callback_SRPServerParams);
+ SSL_CTX_set_srp_cb_arg(mctx->ssl_ctx, mctx);
+ }
+#endif
+ return APR_SUCCESS;
+}
+#endif
+
+static apr_status_t ssl_init_ctx_protocol(server_rec *s,
+ apr_pool_t *p,
+ apr_pool_t *ptemp,
+ modssl_ctx_t *mctx)
+{
+ SSL_CTX *ctx = NULL;
+ MODSSL_SSL_METHOD_CONST SSL_METHOD *method = NULL;
+ char *cp;
+ int protocol = mctx->protocol;
+ SSLSrvConfigRec *sc = mySrvConfig(s);
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ int prot;
+#endif
+
+ /*
+ * Create the new per-server SSL context
+ */
+ if (protocol == SSL_PROTOCOL_NONE) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02231)
+ "No SSL protocols available [hint: SSLProtocol]");
+ return ssl_die(s);
+ }
+
+ cp = apr_pstrcat(p,
+#ifndef OPENSSL_NO_SSL3
+ (protocol & SSL_PROTOCOL_SSLV3 ? "SSLv3, " : ""),
+#endif
+ (protocol & SSL_PROTOCOL_TLSV1 ? "TLSv1, " : ""),
+#ifdef HAVE_TLSV1_X
+ (protocol & SSL_PROTOCOL_TLSV1_1 ? "TLSv1.1, " : ""),
+ (protocol & SSL_PROTOCOL_TLSV1_2 ? "TLSv1.2, " : ""),
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+ (protocol & SSL_PROTOCOL_TLSV1_3 ? "TLSv1.3, " : ""),
+#endif
+#endif
+ NULL);
+ cp[strlen(cp)-2] = NUL;
+
+ ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s,
+ "Creating new SSL context (protocols: %s)", cp);
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#ifndef OPENSSL_NO_SSL3
+ if (protocol == SSL_PROTOCOL_SSLV3) {
+ method = mctx->pkp ?
+ SSLv3_client_method() : /* proxy */
+ SSLv3_server_method(); /* server */
+ }
+ else
+#endif
+ if (protocol == SSL_PROTOCOL_TLSV1) {
+ method = mctx->pkp ?
+ TLSv1_client_method() : /* proxy */
+ TLSv1_server_method(); /* server */
+ }
+#ifdef HAVE_TLSV1_X
+ else if (protocol == SSL_PROTOCOL_TLSV1_1) {
+ method = mctx->pkp ?
+ TLSv1_1_client_method() : /* proxy */
+ TLSv1_1_server_method(); /* server */
+ }
+ else if (protocol == SSL_PROTOCOL_TLSV1_2) {
+ method = mctx->pkp ?
+ TLSv1_2_client_method() : /* proxy */
+ TLSv1_2_server_method(); /* server */
+ }
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+ else if (protocol == SSL_PROTOCOL_TLSV1_3) {
+ method = mctx->pkp ?
+ TLSv1_3_client_method() : /* proxy */
+ TLSv1_3_server_method(); /* server */
+ }
+#endif
+#endif
+ else { /* For multiple protocols, we need a flexible method */
+ method = mctx->pkp ?
+ SSLv23_client_method() : /* proxy */
+ SSLv23_server_method(); /* server */
+ }
+#else
+ method = mctx->pkp ?
+ TLS_client_method() : /* proxy */
+ TLS_server_method(); /* server */
+#endif
+ ctx = SSL_CTX_new(method);
+
+ mctx->ssl_ctx = ctx;
+
+ SSL_CTX_set_options(ctx, SSL_OP_ALL);
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20800000L)
+ /* always disable SSLv2, as per RFC 6176 */
+ SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
+
+#ifndef OPENSSL_NO_SSL3
+ if (!(protocol & SSL_PROTOCOL_SSLV3)) {
+ SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
+ }
+#endif
+
+ if (!(protocol & SSL_PROTOCOL_TLSV1)) {
+ SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);
+ }
+
+#ifdef HAVE_TLSV1_X
+ if (!(protocol & SSL_PROTOCOL_TLSV1_1)) {
+ SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_1);
+ }
+
+ if (!(protocol & SSL_PROTOCOL_TLSV1_2)) {
+ SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_2);
+ }
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+ ssl_set_ctx_protocol_option(s, ctx, SSL_OP_NO_TLSv1_3,
+ protocol & SSL_PROTOCOL_TLSV1_3, "TLSv1.3");
+#endif
+#endif
+
+#else /* #if OPENSSL_VERSION_NUMBER < 0x10100000L */
+ /* We first determine the maximum protocol version we should provide */
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+ if (protocol & SSL_PROTOCOL_TLSV1_3) {
+ prot = TLS1_3_VERSION;
+ } else
+#endif
+ if (protocol & SSL_PROTOCOL_TLSV1_2) {
+ prot = TLS1_2_VERSION;
+ } else if (protocol & SSL_PROTOCOL_TLSV1_1) {
+ prot = TLS1_1_VERSION;
+ } else if (protocol & SSL_PROTOCOL_TLSV1) {
+ prot = TLS1_VERSION;
+#ifndef OPENSSL_NO_SSL3
+ } else if (protocol & SSL_PROTOCOL_SSLV3) {
+ prot = SSL3_VERSION;
+#endif
+ } else {
+ SSL_CTX_free(ctx);
+ mctx->ssl_ctx = NULL;
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(03378)
+ "No SSL protocols available [hint: SSLProtocol]");
+ return ssl_die(s);
+ }
+ SSL_CTX_set_max_proto_version(ctx, prot);
+
+ /* Next we scan for the minimal protocol version we should provide,
+ * but we do not allow holes between max and min */
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+ if (prot == TLS1_3_VERSION && protocol & SSL_PROTOCOL_TLSV1_2) {
+ prot = TLS1_2_VERSION;
+ }
+#endif
+ if (prot == TLS1_2_VERSION && protocol & SSL_PROTOCOL_TLSV1_1) {
+ prot = TLS1_1_VERSION;
+ }
+ if (prot == TLS1_1_VERSION && protocol & SSL_PROTOCOL_TLSV1) {
+ prot = TLS1_VERSION;
+ }
+#ifndef OPENSSL_NO_SSL3
+ if (prot == TLS1_VERSION && protocol & SSL_PROTOCOL_SSLV3) {
+ prot = SSL3_VERSION;
+ }
+#endif
+ SSL_CTX_set_min_proto_version(ctx, prot);
+#endif /* if OPENSSL_VERSION_NUMBER < 0x10100000L */
+
+#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
+ if (sc->cipher_server_pref == TRUE) {
+ SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
+ }
+#endif
+
+
+#ifndef OPENSSL_NO_COMP
+ if (sc->compression != TRUE) {
+#ifdef SSL_OP_NO_COMPRESSION
+ /* OpenSSL >= 1.0 only */
+ SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION);
+#else
+ sk_SSL_COMP_zero(SSL_COMP_get_compression_methods());
+#endif
+ }
+#endif
+
+#ifdef SSL_OP_NO_TICKET
+ /*
+ * Configure using RFC 5077 TLS session tickets
+ * for session resumption.
+ */
+ if (sc->session_tickets == FALSE) {
+ SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET);
+ }
+#endif
+
+#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
+ if (sc->insecure_reneg == TRUE) {
+ SSL_CTX_set_options(ctx, SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
+ }
+#endif
+
+ SSL_CTX_set_app_data(ctx, s);
+
+ /*
+ * Configure additional context ingredients
+ */
+ SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE);
+#ifdef HAVE_ECC
+ SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE);
+#endif
+
+#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
+ /*
+ * Disallow a session from being resumed during a renegotiation,
+ * so that an acceptable cipher suite can be negotiated.
+ */
+ SSL_CTX_set_options(ctx, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
+#endif
+
+#ifdef SSL_MODE_RELEASE_BUFFERS
+ /* If httpd is configured to reduce mem usage, ask openssl to do so, too */
+ if (ap_max_mem_free != APR_ALLOCATOR_MAX_FREE_UNLIMITED)
+ SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x1010100fL
+ /* For OpenSSL >=1.1.1, disable auto-retry mode so it's possible
+ * to consume handshake records without blocking for app-data.
+ * https://github.com/openssl/openssl/issues/7178 */
+ SSL_CTX_clear_mode(ctx, SSL_MODE_AUTO_RETRY);
+#endif
+
+#ifdef HAVE_OPENSSL_KEYLOG
+ if (mctx->sc->mc->keylog_file) {
+ SSL_CTX_set_keylog_callback(ctx, modssl_callback_keylog);
+ }
+#endif
+
+ return APR_SUCCESS;
+}
+
+static void ssl_init_ctx_session_cache(server_rec *s,
+ apr_pool_t *p,
+ apr_pool_t *ptemp,
+ modssl_ctx_t *mctx)
+{
+ SSL_CTX *ctx = mctx->ssl_ctx;
+ SSLModConfigRec *mc = myModConfig(s);
+
+ SSL_CTX_set_session_cache_mode(ctx, mc->sesscache_mode);
+
+ if (mc->sesscache) {
+ SSL_CTX_sess_set_new_cb(ctx, ssl_callback_NewSessionCacheEntry);
+ SSL_CTX_sess_set_get_cb(ctx, ssl_callback_GetSessionCacheEntry);
+ SSL_CTX_sess_set_remove_cb(ctx, ssl_callback_DelSessionCacheEntry);
+ }
+}
+
+static void ssl_init_ctx_callbacks(server_rec *s,
+ apr_pool_t *p,
+ apr_pool_t *ptemp,
+ modssl_ctx_t *mctx)
+{
+ SSL_CTX *ctx = mctx->ssl_ctx;
+
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
+ /* Note that for OpenSSL>=1.1, auto selection is enabled via
+ * SSL_CTX_set_dh_auto(,1) if no parameter is configured. */
+ SSL_CTX_set_tmp_dh_callback(ctx, ssl_callback_TmpDH);
+#endif
+
+ SSL_CTX_set_info_callback(ctx, ssl_callback_Info);
+
+#ifdef HAVE_TLS_ALPN
+ SSL_CTX_set_alpn_select_cb(ctx, ssl_callback_alpn_select, NULL);
+#endif
+}
+
+static APR_INLINE
+int modssl_CTX_load_verify_locations(SSL_CTX *ctx,
+ const char *file,
+ const char *path)
+{
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ if (!SSL_CTX_load_verify_locations(ctx, file, path))
+ return 0;
+#else
+ if (file && !SSL_CTX_load_verify_file(ctx, file))
+ return 0;
+ if (path && !SSL_CTX_load_verify_dir(ctx, path))
+ return 0;
+#endif
+ return 1;
+}
+
+static apr_status_t ssl_init_ctx_verify(server_rec *s,
+ apr_pool_t *p,
+ apr_pool_t *ptemp,
+ modssl_ctx_t *mctx)
+{
+ SSL_CTX *ctx = mctx->ssl_ctx;
+
+ int verify = SSL_VERIFY_NONE;
+ STACK_OF(X509_NAME) *ca_list;
+
+ if (mctx->auth.verify_mode == SSL_CVERIFY_UNSET) {
+ mctx->auth.verify_mode = SSL_CVERIFY_NONE;
+ }
+
+ if (mctx->auth.verify_depth == UNSET) {
+ mctx->auth.verify_depth = 1;
+ }
+
+ /*
+ * Configure callbacks for SSL context
+ */
+ if (mctx->auth.verify_mode == SSL_CVERIFY_REQUIRE) {
+ verify |= SSL_VERIFY_PEER_STRICT;
+ }
+
+ if ((mctx->auth.verify_mode == SSL_CVERIFY_OPTIONAL) ||
+ (mctx->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA))
+ {
+ verify |= SSL_VERIFY_PEER;
+ }
+
+ SSL_CTX_set_verify(ctx, verify, ssl_callback_SSLVerify);
+
+ /*
+ * Configure Client Authentication details
+ */
+ if (mctx->auth.ca_cert_file || mctx->auth.ca_cert_path) {
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s,
+ "Configuring client authentication");
+
+ if (!modssl_CTX_load_verify_locations(ctx, mctx->auth.ca_cert_file,
+ mctx->auth.ca_cert_path)) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01895)
+ "Unable to configure verify locations "
+ "for client authentication");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+ if (mctx->pks && (mctx->pks->ca_name_file || mctx->pks->ca_name_path)) {
+ ca_list = ssl_init_FindCAList(s, ptemp,
+ mctx->pks->ca_name_file,
+ mctx->pks->ca_name_path);
+ } else
+ ca_list = ssl_init_FindCAList(s, ptemp,
+ mctx->auth.ca_cert_file,
+ mctx->auth.ca_cert_path);
+ if (sk_X509_NAME_num(ca_list) <= 0) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01896)
+ "Unable to determine list of acceptable "
+ "CA certificates for client authentication");
+ return ssl_die(s);
+ }
+
+ SSL_CTX_set_client_CA_list(ctx, ca_list);
+ }
+
+ /*
+ * Give a warning when no CAs were configured but client authentication
+ * should take place. This cannot work.
+ */
+ if (mctx->auth.verify_mode == SSL_CVERIFY_REQUIRE) {
+ ca_list = SSL_CTX_get_client_CA_list(ctx);
+
+ if (sk_X509_NAME_num(ca_list) == 0) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(01897)
+ "Init: Oops, you want to request client "
+ "authentication, but no CAs are known for "
+ "verification!? [Hint: SSLCACertificate*]");
+ }
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t ssl_init_ctx_cipher_suite(server_rec *s,
+ apr_pool_t *p,
+ apr_pool_t *ptemp,
+ modssl_ctx_t *mctx)
+{
+ SSL_CTX *ctx = mctx->ssl_ctx;
+ const char *suite;
+
+ /*
+ * Configure SSL Cipher Suite. Always disable NULL and export ciphers,
+ * see also ssl_engine_config.c:ssl_cmd_SSLCipherSuite().
+ * OpenSSL's SSL_DEFAULT_CIPHER_LIST includes !aNULL:!eNULL from 0.9.8f,
+ * and !EXP from 0.9.8zf/1.0.1m/1.0.2a, so append them while we support
+ * earlier versions.
+ */
+ suite = mctx->auth.cipher_suite ? mctx->auth.cipher_suite :
+ apr_pstrcat(ptemp, SSL_DEFAULT_CIPHER_LIST, ":!aNULL:!eNULL:!EXP",
+ NULL);
+
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s,
+ "Configuring permitted SSL ciphers [%s]",
+ suite);
+
+ if (!SSL_CTX_set_cipher_list(ctx, suite)) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01898)
+ "Unable to configure permitted SSL ciphers");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+ if (mctx->auth.tls13_ciphers
+ && !SSL_CTX_set_ciphersuites(ctx, mctx->auth.tls13_ciphers)) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10127)
+ "Unable to configure permitted TLSv1.3 ciphers");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+#endif
+ return APR_SUCCESS;
+}
+
+static APR_INLINE
+int modssl_X509_STORE_load_locations(X509_STORE *store,
+ const char *file,
+ const char *path)
+{
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ if (!X509_STORE_load_locations(store, file, path))
+ return 0;
+#else
+ if (file && !X509_STORE_load_file(store, file))
+ return 0;
+ if (path && !X509_STORE_load_path(store, path))
+ return 0;
+#endif
+ return 1;
+}
+
+static apr_status_t ssl_init_ctx_crl(server_rec *s,
+ apr_pool_t *p,
+ apr_pool_t *ptemp,
+ modssl_ctx_t *mctx)
+{
+ X509_STORE *store = SSL_CTX_get_cert_store(mctx->ssl_ctx);
+ unsigned long crlflags = 0;
+ char *cfgp = mctx->pkp ? "SSLProxy" : "SSL";
+ int crl_check_mode;
+
+ if (mctx->ocsp_mask == UNSET) {
+ mctx->ocsp_mask = SSL_OCSPCHECK_NONE;
+ }
+
+ if (mctx->crl_check_mask == UNSET) {
+ mctx->crl_check_mask = SSL_CRLCHECK_NONE;
+ }
+ crl_check_mode = mctx->crl_check_mask & ~SSL_CRLCHECK_FLAGS;
+
+ /*
+ * Configure Certificate Revocation List (CRL) Details
+ */
+
+ if (!(mctx->crl_file || mctx->crl_path)) {
+ if (crl_check_mode == SSL_CRLCHECK_LEAF ||
+ crl_check_mode == SSL_CRLCHECK_CHAIN) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01899)
+ "Host %s: CRL checking has been enabled, but "
+ "neither %sCARevocationFile nor %sCARevocationPath "
+ "is configured", mctx->sc->vhost_id, cfgp, cfgp);
+ return ssl_die(s);
+ }
+ return APR_SUCCESS;
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01900)
+ "Configuring certificate revocation facility");
+
+ if (!store || !modssl_X509_STORE_load_locations(store, mctx->crl_file,
+ mctx->crl_path)) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01901)
+ "Host %s: unable to configure X.509 CRL storage "
+ "for certificate revocation", mctx->sc->vhost_id);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+ switch (crl_check_mode) {
+ case SSL_CRLCHECK_LEAF:
+ crlflags = X509_V_FLAG_CRL_CHECK;
+ break;
+ case SSL_CRLCHECK_CHAIN:
+ crlflags = X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL;
+ break;
+ default:
+ crlflags = 0;
+ }
+
+ if (crlflags) {
+ X509_STORE_set_flags(store, crlflags);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(01902)
+ "Host %s: X.509 CRL storage locations configured, "
+ "but CRL checking (%sCARevocationCheck) is not "
+ "enabled", mctx->sc->vhost_id, cfgp);
+ }
+
+ return APR_SUCCESS;
+}
+
+/*
+ * Read a file that optionally contains the server certificate in PEM
+ * format, possibly followed by a sequence of CA certificates that
+ * should be sent to the peer in the SSL Certificate message.
+ */
+static int use_certificate_chain(
+ SSL_CTX *ctx, char *file, int skipfirst, pem_password_cb *cb)
+{
+ BIO *bio;
+ X509 *x509;
+ unsigned long err;
+ int n;
+
+ if ((bio = BIO_new(BIO_s_file())) == NULL)
+ return -1;
+ if (BIO_read_filename(bio, file) <= 0) {
+ BIO_free(bio);
+ return -1;
+ }
+ /* optionally skip a leading server certificate */
+ if (skipfirst) {
+ if ((x509 = PEM_read_bio_X509(bio, NULL, cb, NULL)) == NULL) {
+ BIO_free(bio);
+ return -1;
+ }
+ X509_free(x509);
+ }
+ /* free a perhaps already configured extra chain */
+#ifdef OPENSSL_NO_SSL_INTERN
+ SSL_CTX_clear_extra_chain_certs(ctx);
+#else
+ if (ctx->extra_certs != NULL) {
+ sk_X509_pop_free((STACK_OF(X509) *)ctx->extra_certs, X509_free);
+ ctx->extra_certs = NULL;
+ }
+#endif
+
+ /* create new extra chain by loading the certs */
+ n = 0;
+ ERR_clear_error();
+ while ((x509 = PEM_read_bio_X509(bio, NULL, cb, NULL)) != NULL) {
+ if (!SSL_CTX_add_extra_chain_cert(ctx, x509)) {
+ X509_free(x509);
+ BIO_free(bio);
+ return -1;
+ }
+ n++;
+ }
+ /* Make sure that only the error is just an EOF */
+ if ((err = ERR_peek_error()) > 0) {
+ if (!( ERR_GET_LIB(err) == ERR_LIB_PEM
+ && ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) {
+ BIO_free(bio);
+ return -1;
+ }
+ while (ERR_get_error() > 0) ;
+ }
+ BIO_free(bio);
+ return n;
+}
+
+static apr_status_t ssl_init_ctx_cert_chain(server_rec *s,
+ apr_pool_t *p,
+ apr_pool_t *ptemp,
+ modssl_ctx_t *mctx)
+{
+ BOOL skip_first = FALSE;
+ int i, n;
+ const char *chain = mctx->cert_chain;
+
+ /*
+ * Optionally configure extra server certificate chain certificates.
+ * This is usually done by OpenSSL automatically when one of the
+ * server cert issuers are found under SSLCACertificatePath or in
+ * SSLCACertificateFile. But because these are intended for client
+ * authentication it can conflict. For instance when you use a
+ * Global ID server certificate you've to send out the intermediate
+ * CA certificate, too. When you would just configure this with
+ * SSLCACertificateFile and also use client authentication mod_ssl
+ * would accept all clients also issued by this CA. Obviously this
+ * isn't what we want in this situation. So this feature here exists
+ * to allow one to explicitly configure CA certificates which are
+ * used only for the server certificate chain.
+ */
+ if (!chain) {
+ return APR_SUCCESS;
+ }
+
+ for (i = 0; (i < mctx->pks->cert_files->nelts) &&
+ APR_ARRAY_IDX(mctx->pks->cert_files, i, const char *); i++) {
+ if (strEQ(APR_ARRAY_IDX(mctx->pks->cert_files, i, const char *), chain)) {
+ skip_first = TRUE;
+ break;
+ }
+ }
+
+ n = use_certificate_chain(mctx->ssl_ctx, (char *)chain, skip_first, NULL);
+ if (n < 0) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01903)
+ "Failed to configure CA certificate chain!");
+ return ssl_die(s);
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01904)
+ "Configuring server certificate chain "
+ "(%d CA certificate%s)",
+ n, n == 1 ? "" : "s");
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t ssl_init_ctx(server_rec *s,
+ apr_pool_t *p,
+ apr_pool_t *ptemp,
+ modssl_ctx_t *mctx)
+{
+ apr_status_t rv;
+
+ if ((rv = ssl_init_ctx_protocol(s, p, ptemp, mctx)) != APR_SUCCESS) {
+ return rv;
+ }
+
+ ssl_init_ctx_session_cache(s, p, ptemp, mctx);
+
+ ssl_init_ctx_callbacks(s, p, ptemp, mctx);
+
+ if ((rv = ssl_init_ctx_verify(s, p, ptemp, mctx)) != APR_SUCCESS) {
+ return rv;
+ }
+
+ if ((rv = ssl_init_ctx_cipher_suite(s, p, ptemp, mctx)) != APR_SUCCESS) {
+ return rv;
+ }
+
+ if ((rv = ssl_init_ctx_crl(s, p, ptemp, mctx)) != APR_SUCCESS) {
+ return rv;
+ }
+
+ if (mctx->pks) {
+ /* XXX: proxy support? */
+ if ((rv = ssl_init_ctx_cert_chain(s, p, ptemp, mctx)) != APR_SUCCESS) {
+ return rv;
+ }
+#ifdef HAVE_TLSEXT
+ if ((rv = ssl_init_ctx_tls_extensions(s, p, ptemp, mctx)) !=
+ APR_SUCCESS) {
+ return rv;
+ }
+#endif
+ }
+
+ return APR_SUCCESS;
+}
+
+static void ssl_check_public_cert(server_rec *s,
+ apr_pool_t *ptemp,
+ X509 *cert,
+ const char *key_id)
+{
+ int is_ca, pathlen;
+
+ if (!cert) {
+ return;
+ }
+
+ /*
+ * Some information about the certificate(s)
+ */
+
+ if (modssl_X509_getBC(cert, &is_ca, &pathlen)) {
+ if (is_ca) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(01906)
+ "%s server certificate is a CA certificate "
+ "(BasicConstraints: CA == TRUE !?)", key_id);
+ }
+
+ if (pathlen > 0) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(01907)
+ "%s server certificate is not a leaf certificate "
+ "(BasicConstraints: pathlen == %d > 0 !?)",
+ key_id, pathlen);
+ }
+ }
+
+ if (modssl_X509_match_name(ptemp, cert, (const char *)s->server_hostname,
+ TRUE, s) == FALSE) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(01909)
+ "%s server certificate does NOT include an ID "
+ "which matches the server name", key_id);
+ }
+}
+
+/* prevent OpenSSL from showing its "Enter PEM pass phrase:" prompt */
+static int ssl_no_passwd_prompt_cb(char *buf, int size, int rwflag,
+ void *userdata) {
+ return 0;
+}
+
+static APR_INLINE int modssl_DH_bits(DH *dh)
+{
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ return DH_bits(dh);
+#else
+ return BN_num_bits(DH_get0_p(dh));
+#endif
+}
+
+/* SSL_CTX_use_PrivateKey_file() can fail either because the private
+ * key was encrypted, or due to a mismatch between an already-loaded
+ * cert and the key - a common misconfiguration - from calling
+ * X509_check_private_key(). This macro is passed the last error code
+ * off the OpenSSL stack and evaluates to true only for the first
+ * case. With OpenSSL < 3 the second case is identifiable by the
+ * function code, but function codes are not used from 3.0. */
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+#define CHECK_PRIVKEY_ERROR(ec) (ERR_GET_FUNC(ec) != X509_F_X509_CHECK_PRIVATE_KEY)
+#else
+#define CHECK_PRIVKEY_ERROR(ec) (ERR_GET_LIB(ec) != ERR_LIB_X509 \
+ || (ERR_GET_REASON(ec) != X509_R_KEY_TYPE_MISMATCH \
+ && ERR_GET_REASON(ec) != X509_R_KEY_VALUES_MISMATCH \
+ && ERR_GET_REASON(ec) != X509_R_UNKNOWN_KEY_TYPE))
+#endif
+
+static apr_status_t ssl_init_server_certs(server_rec *s,
+ apr_pool_t *p,
+ apr_pool_t *ptemp,
+ modssl_ctx_t *mctx,
+ apr_array_header_t *pphrases)
+{
+ SSLModConfigRec *mc = myModConfig(s);
+ const char *vhost_id = mctx->sc->vhost_id, *key_id, *certfile, *keyfile;
+ int i;
+ X509 *cert;
+ DH *dh;
+#ifdef HAVE_ECC
+ EC_GROUP *ecparams = NULL;
+ int nid;
+ EC_KEY *eckey = NULL;
+#endif
+#ifndef HAVE_SSL_CONF_CMD
+ SSL *ssl;
+#endif
+
+ /* no OpenSSL default prompts for any of the SSL_CTX_use_* calls, please */
+ SSL_CTX_set_default_passwd_cb(mctx->ssl_ctx, ssl_no_passwd_prompt_cb);
+
+ /* Iterate over the SSLCertificateFile array */
+ for (i = 0; (i < mctx->pks->cert_files->nelts) &&
+ (certfile = APR_ARRAY_IDX(mctx->pks->cert_files, i,
+ const char *));
+ i++) {
+ EVP_PKEY *pkey;
+ const char *engine_certfile = NULL;
+
+ key_id = apr_psprintf(ptemp, "%s:%d", vhost_id, i);
+
+ ERR_clear_error();
+
+ /* first the certificate (public key) */
+ if (modssl_is_engine_id(certfile)) {
+ engine_certfile = certfile;
+ }
+ else if (mctx->cert_chain) {
+ if ((SSL_CTX_use_certificate_file(mctx->ssl_ctx, certfile,
+ SSL_FILETYPE_PEM) < 1)) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02561)
+ "Failed to configure certificate %s, check %s",
+ key_id, certfile);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return APR_EGENERAL;
+ }
+ } else {
+ if ((SSL_CTX_use_certificate_chain_file(mctx->ssl_ctx,
+ certfile) < 1)) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02562)
+ "Failed to configure certificate %s (with chain),"
+ " check %s", key_id, certfile);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return APR_EGENERAL;
+ }
+ }
+
+ /* and second, the private key */
+ if (i < mctx->pks->key_files->nelts) {
+ keyfile = APR_ARRAY_IDX(mctx->pks->key_files, i, const char *);
+ } else {
+ keyfile = certfile;
+ }
+
+ ERR_clear_error();
+
+ if (modssl_is_engine_id(keyfile)) {
+ apr_status_t rv;
+
+ cert = NULL;
+
+ if ((rv = modssl_load_engine_keypair(s, ptemp, vhost_id,
+ engine_certfile, keyfile,
+ &cert, &pkey))) {
+ return rv;
+ }
+
+ if (cert) {
+ if (SSL_CTX_use_certificate(mctx->ssl_ctx, cert) < 1) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10137)
+ "Failed to configure engine certificate %s, check %s",
+ key_id, certfile);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return APR_EGENERAL;
+ }
+
+ /* SSL_CTX now owns the cert. */
+ X509_free(cert);
+ }
+
+ if (SSL_CTX_use_PrivateKey(mctx->ssl_ctx, pkey) < 1) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10130)
+ "Failed to configure private key %s from engine",
+ keyfile);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return APR_EGENERAL;
+ }
+
+ /* SSL_CTX now owns the key */
+ EVP_PKEY_free(pkey);
+ }
+ else if ((SSL_CTX_use_PrivateKey_file(mctx->ssl_ctx, keyfile,
+ SSL_FILETYPE_PEM) < 1)
+ && CHECK_PRIVKEY_ERROR(ERR_peek_last_error())) {
+ ssl_asn1_t *asn1;
+ const unsigned char *ptr;
+
+ ERR_clear_error();
+
+ /* perhaps it's an encrypted private key, so try again */
+ ssl_load_encrypted_pkey(s, ptemp, i, keyfile, &pphrases);
+
+ if (!(asn1 = ssl_asn1_table_get(mc->tPrivateKey, key_id)) ||
+ !(ptr = asn1->cpData) ||
+ !(pkey = d2i_AutoPrivateKey(NULL, &ptr, asn1->nData)) ||
+ (SSL_CTX_use_PrivateKey(mctx->ssl_ctx, pkey) < 1)) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02564)
+ "Failed to configure encrypted (?) private key %s,"
+ " check %s", key_id, keyfile);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return APR_EGENERAL;
+ }
+ }
+
+ if (SSL_CTX_check_private_key(mctx->ssl_ctx) < 1) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02565)
+ "Certificate and private key %s from %s and %s "
+ "do not match", key_id, certfile, keyfile);
+ return APR_EGENERAL;
+ }
+
+#ifdef HAVE_SSL_CONF_CMD
+ /*
+ * workaround for those OpenSSL versions where SSL_CTX_get0_certificate
+ * is not yet available: create an SSL struct which we dispose of
+ * as soon as we no longer need access to the cert. (Strictly speaking,
+ * SSL_CTX_get0_certificate does not depend on the SSL_CONF stuff,
+ * but there's no reliable way to check for its existence, so we
+ * assume that if SSL_CONF is available, it's OpenSSL 1.0.2 or later,
+ * and SSL_CTX_get0_certificate is implemented.)
+ */
+ if (!(cert = SSL_CTX_get0_certificate(mctx->ssl_ctx))) {
+#else
+ ssl = SSL_new(mctx->ssl_ctx);
+ if (ssl) {
+ /* Workaround bug in SSL_get_certificate in OpenSSL 0.9.8y */
+ SSL_set_connect_state(ssl);
+ cert = SSL_get_certificate(ssl);
+ }
+ if (!ssl || !cert) {
+#endif
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02566)
+ "Unable to retrieve certificate %s", key_id);
+#ifndef HAVE_SSL_CONF_CMD
+ if (ssl)
+ SSL_free(ssl);
+#endif
+ return APR_EGENERAL;
+ }
+
+ /* warn about potential cert issues */
+ ssl_check_public_cert(s, ptemp, cert, key_id);
+
+#if defined(HAVE_OCSP_STAPLING) && !defined(SSL_CTRL_SET_CURRENT_CERT)
+ /*
+ * OpenSSL up to 1.0.1: configure stapling as we go. In 1.0.2
+ * and later, there's SSL_CTX_set_current_cert, which allows
+ * iterating over all certs in an SSL_CTX (including those possibly
+ * loaded via SSLOpenSSLConfCmd Certificate), so for 1.0.2 and
+ * later, we defer to the code in ssl_init_server_ctx.
+ */
+ if (!ssl_stapling_init_cert(s, p, ptemp, mctx, cert)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02567)
+ "Unable to configure certificate %s for stapling",
+ key_id);
+ }
+#endif
+
+#ifndef HAVE_SSL_CONF_CMD
+ SSL_free(ssl);
+#endif
+
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(02568)
+ "Certificate and private key %s configured from %s and %s",
+ key_id, certfile, keyfile);
+ }
+
+ /*
+ * Try to read DH parameters from the (first) SSLCertificateFile
+ */
+ certfile = APR_ARRAY_IDX(mctx->pks->cert_files, 0, const char *);
+ if (certfile && !modssl_is_engine_id(certfile)
+ && (dh = ssl_dh_GetParamFromFile(certfile))) {
+ /* ### This should be replaced with SSL_CTX_set0_tmp_dh_pkey()
+ * for OpenSSL 3.0+. */
+ SSL_CTX_set_tmp_dh(mctx->ssl_ctx, dh);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02540)
+ "Custom DH parameters (%d bits) for %s loaded from %s",
+ modssl_DH_bits(dh), vhost_id, certfile);
+ DH_free(dh);
+ }
+#if !MODSSL_USE_OPENSSL_PRE_1_1_API
+ else {
+ /* If no parameter is manually configured, enable auto
+ * selection. */
+ SSL_CTX_set_dh_auto(mctx->ssl_ctx, 1);
+ }
+#endif
+
+#ifdef HAVE_ECC
+ /*
+ * Similarly, try to read the ECDH curve name from SSLCertificateFile...
+ */
+ if (certfile && !modssl_is_engine_id(certfile)
+ && (ecparams = ssl_ec_GetParamFromFile(certfile))
+ && (nid = EC_GROUP_get_curve_name(ecparams))
+ && (eckey = EC_KEY_new_by_curve_name(nid))) {
+ SSL_CTX_set_tmp_ecdh(mctx->ssl_ctx, eckey);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02541)
+ "ECDH curve %s for %s specified in %s",
+ OBJ_nid2sn(nid), vhost_id, certfile);
+ }
+ /*
+ * ...otherwise, enable auto curve selection (OpenSSL 1.0.2)
+ * or configure NIST P-256 (required to enable ECDHE for earlier versions)
+ * ECDH is always enabled in 1.1.0 unless excluded from SSLCipherList
+ */
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
+ else {
+#if defined(SSL_CTX_set_ecdh_auto)
+ SSL_CTX_set_ecdh_auto(mctx->ssl_ctx, 1);
+#else
+ eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+ SSL_CTX_set_tmp_ecdh(mctx->ssl_ctx, eckey);
+#endif
+ }
+#endif
+ /* OpenSSL assures us that _free() is NULL-safe */
+ EC_KEY_free(eckey);
+ EC_GROUP_free(ecparams);
+#endif
+
+ return APR_SUCCESS;
+}
+
+#ifdef HAVE_TLS_SESSION_TICKETS
+static apr_status_t ssl_init_ticket_key(server_rec *s,
+ apr_pool_t *p,
+ apr_pool_t *ptemp,
+ modssl_ctx_t *mctx)
+{
+ apr_status_t rv;
+ apr_file_t *fp;
+ apr_size_t len;
+ char buf[TLSEXT_TICKET_KEY_LEN];
+ char *path;
+ modssl_ticket_key_t *ticket_key = mctx->ticket_key;
+ int res;
+
+ if (!ticket_key->file_path) {
+ return APR_SUCCESS;
+ }
+
+ path = ap_server_root_relative(p, ticket_key->file_path);
+
+ rv = apr_file_open(&fp, path, APR_READ|APR_BINARY,
+ APR_OS_DEFAULT, ptemp);
+
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02286)
+ "Failed to open ticket key file %s: (%d) %pm",
+ path, rv, &rv);
+ return ssl_die(s);
+ }
+
+ rv = apr_file_read_full(fp, &buf[0], TLSEXT_TICKET_KEY_LEN, &len);
+
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02287)
+ "Failed to read %d bytes from %s: (%d) %pm",
+ TLSEXT_TICKET_KEY_LEN, path, rv, &rv);
+ return ssl_die(s);
+ }
+
+ memcpy(ticket_key->key_name, buf, 16);
+ memcpy(ticket_key->aes_key, buf + 32, 16);
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ memcpy(ticket_key->hmac_secret, buf + 16, 16);
+ res = SSL_CTX_set_tlsext_ticket_key_cb(mctx->ssl_ctx,
+ ssl_callback_SessionTicket);
+#else
+ ticket_key->mac_params[0] =
+ OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY, buf + 16, 16);
+ ticket_key->mac_params[1] =
+ OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, "sha256", 0);
+ ticket_key->mac_params[2] =
+ OSSL_PARAM_construct_end();
+ res = SSL_CTX_set_tlsext_ticket_key_evp_cb(mctx->ssl_ctx,
+ ssl_callback_SessionTicket);
+#endif
+ if (!res) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01913)
+ "Unable to initialize TLS session ticket key callback "
+ "(incompatible OpenSSL version?)");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(02288)
+ "TLS session ticket key for %s successfully loaded from %s",
+ (mySrvConfig(s))->vhost_id, path);
+
+ return APR_SUCCESS;
+}
+#endif
+
+static BOOL load_x509_info(apr_pool_t *ptemp,
+ STACK_OF(X509_INFO) *sk,
+ const char *filename)
+{
+ BIO *in;
+
+ if (!(in = BIO_new(BIO_s_file()))) {
+ return FALSE;
+ }
+
+ if (BIO_read_filename(in, filename) <= 0) {
+ BIO_free(in);
+ return FALSE;
+ }
+
+ ERR_clear_error();
+
+ PEM_X509_INFO_read_bio(in, sk, NULL, NULL);
+
+ BIO_free(in);
+
+ return TRUE;
+}
+
+static apr_status_t ssl_init_proxy_certs(server_rec *s,
+ apr_pool_t *p,
+ apr_pool_t *ptemp,
+ modssl_ctx_t *mctx)
+{
+ int n, ncerts = 0;
+ STACK_OF(X509_INFO) *sk;
+ modssl_pk_proxy_t *pkp = mctx->pkp;
+ STACK_OF(X509) *chain;
+ X509_STORE_CTX *sctx;
+ X509_STORE *store = SSL_CTX_get_cert_store(mctx->ssl_ctx);
+
+#if OPENSSL_VERSION_NUMBER >= 0x1010100fL
+ /* For OpenSSL >=1.1.1, turn on client cert support which is
+ * otherwise turned off by default (by design).
+ * https://github.com/openssl/openssl/issues/6933 */
+ SSL_CTX_set_post_handshake_auth(mctx->ssl_ctx, 1);
+#endif
+
+ SSL_CTX_set_client_cert_cb(mctx->ssl_ctx,
+ ssl_callback_proxy_cert);
+
+ if (!(pkp->cert_file || pkp->cert_path)) {
+ return APR_SUCCESS;
+ }
+
+ sk = sk_X509_INFO_new_null();
+
+ if (pkp->cert_file) {
+ load_x509_info(ptemp, sk, pkp->cert_file);
+ }
+
+ if (pkp->cert_path) {
+ ssl_init_ca_cert_path(s, ptemp, pkp->cert_path, NULL, sk);
+ }
+
+ if ((ncerts = sk_X509_INFO_num(sk)) <= 0) {
+ sk_X509_INFO_free(sk);
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(02206)
+ "no client certs found for SSL proxy");
+ return APR_SUCCESS;
+ }
+
+ /* Check that all client certs have got certificates and private
+ * keys. */
+ for (n = 0; n < ncerts; n++) {
+ X509_INFO *inf = sk_X509_INFO_value(sk, n);
+
+ if (!inf->x509 || !inf->x_pkey || !inf->x_pkey->dec_pkey ||
+ inf->enc_data) {
+ sk_X509_INFO_free(sk);
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s, APLOGNO(02252)
+ "incomplete client cert configured for SSL proxy "
+ "(missing or encrypted private key?)");
+ return ssl_die(s);
+ }
+
+ if (X509_check_private_key(inf->x509, inf->x_pkey->dec_pkey) != 1) {
+ ssl_log_xerror(SSLLOG_MARK, APLOG_STARTUP, 0, ptemp, s, inf->x509,
+ APLOGNO(02326) "proxy client certificate and "
+ "private key do not match");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
+ return ssl_die(s);
+ }
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02207)
+ "loaded %d client certs for SSL proxy",
+ ncerts);
+ pkp->certs = sk;
+
+
+ if (!pkp->ca_cert_file || !store) {
+ return APR_SUCCESS;
+ }
+
+ /* If SSLProxyMachineCertificateChainFile is configured, load all
+ * the CA certs and have OpenSSL attempt to construct a full chain
+ * from each configured end-entity cert up to a root. This will
+ * allow selection of the correct cert given a list of root CA
+ * names in the certificate request from the server. */
+ pkp->ca_certs = (STACK_OF(X509) **) apr_pcalloc(p, ncerts * sizeof(sk));
+ sctx = X509_STORE_CTX_new();
+
+ if (!sctx) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02208)
+ "SSL proxy client cert initialization failed");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+ modssl_X509_STORE_load_locations(store, pkp->ca_cert_file, NULL);
+
+ for (n = 0; n < ncerts; n++) {
+ int i;
+
+ X509_INFO *inf = sk_X509_INFO_value(pkp->certs, n);
+ X509_STORE_CTX_init(sctx, store, inf->x509, NULL);
+
+ /* Attempt to verify the client cert */
+ if (X509_verify_cert(sctx) != 1) {
+ int err = X509_STORE_CTX_get_error(sctx);
+ ssl_log_xerror(SSLLOG_MARK, APLOG_WARNING, 0, ptemp, s, inf->x509,
+ APLOGNO(02270) "SSL proxy client cert chain "
+ "verification failed: %s :",
+ X509_verify_cert_error_string(err));
+ }
+
+ /* Clear X509_verify_cert errors */
+ ERR_clear_error();
+
+ /* Obtain a copy of the verified chain */
+ chain = X509_STORE_CTX_get1_chain(sctx);
+
+ if (chain != NULL) {
+ /* Discard end entity cert from the chain */
+ X509_free(sk_X509_shift(chain));
+
+ if ((i = sk_X509_num(chain)) > 0) {
+ /* Store the chain for later use */
+ pkp->ca_certs[n] = chain;
+ }
+ else {
+ /* Discard empty chain */
+ sk_X509_pop_free(chain, X509_free);
+ pkp->ca_certs[n] = NULL;
+ }
+
+ ssl_log_xerror(SSLLOG_MARK, APLOG_DEBUG, 0, ptemp, s, inf->x509,
+ APLOGNO(02271)
+ "loaded %i intermediate CA%s for cert %i: ",
+ i, i == 1 ? "" : "s", n);
+ if (i > 0) {
+ int j;
+ for (j = 0; j < i; j++) {
+ ssl_log_xerror(SSLLOG_MARK, APLOG_DEBUG, 0, ptemp, s,
+ sk_X509_value(chain, j), APLOGNO(03039)
+ "%i:", j);
+ }
+ }
+ }
+
+ /* get ready for next X509_STORE_CTX_init */
+ X509_STORE_CTX_cleanup(sctx);
+ }
+
+ X509_STORE_CTX_free(sctx);
+
+ return APR_SUCCESS;
+}
+
+#define MODSSL_CFG_ITEM_FREE(func, item) \
+ if (item) { \
+ func(item); \
+ item = NULL; \
+ }
+
+static void ssl_init_ctx_cleanup(modssl_ctx_t *mctx)
+{
+ MODSSL_CFG_ITEM_FREE(SSL_CTX_free, mctx->ssl_ctx);
+
+#ifdef HAVE_SRP
+ if (mctx->srp_vbase != NULL) {
+ SRP_VBASE_free(mctx->srp_vbase);
+ mctx->srp_vbase = NULL;
+ }
+#endif
+}
+
+static apr_status_t ssl_cleanup_proxy_ctx(void *data)
+{
+ modssl_ctx_t *mctx = data;
+
+ ssl_init_ctx_cleanup(mctx);
+
+ if (mctx->pkp->certs) {
+ int i = 0;
+ int ncerts = sk_X509_INFO_num(mctx->pkp->certs);
+
+ if (mctx->pkp->ca_certs) {
+ for (i = 0; i < ncerts; i++) {
+ if (mctx->pkp->ca_certs[i] != NULL) {
+ sk_X509_pop_free(mctx->pkp->ca_certs[i], X509_free);
+ }
+ }
+ }
+
+ sk_X509_INFO_pop_free(mctx->pkp->certs, X509_INFO_free);
+ mctx->pkp->certs = NULL;
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t ssl_init_proxy_ctx(server_rec *s,
+ apr_pool_t *p,
+ apr_pool_t *ptemp,
+ modssl_ctx_t *proxy)
+{
+ apr_status_t rv;
+
+ if (proxy->ssl_ctx) {
+ /* Merged/initialized already */
+ return APR_SUCCESS;
+ }
+
+ apr_pool_cleanup_register(p, proxy,
+ ssl_cleanup_proxy_ctx,
+ apr_pool_cleanup_null);
+
+ if ((rv = ssl_init_ctx(s, p, ptemp, proxy)) != APR_SUCCESS) {
+ return rv;
+ }
+
+ if ((rv = ssl_init_proxy_certs(s, p, ptemp, proxy)) != APR_SUCCESS) {
+ return rv;
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t ssl_init_server_ctx(server_rec *s,
+ apr_pool_t *p,
+ apr_pool_t *ptemp,
+ SSLSrvConfigRec *sc,
+ apr_array_header_t *pphrases)
+{
+ apr_status_t rv;
+ modssl_pk_server_t *pks;
+#ifdef HAVE_SSL_CONF_CMD
+ ssl_ctx_param_t *param = (ssl_ctx_param_t *)sc->server->ssl_ctx_param->elts;
+ SSL_CONF_CTX *cctx = sc->server->ssl_ctx_config;
+ int i;
+#endif
+ int n;
+
+ /*
+ * Check for problematic re-initializations
+ */
+ if (sc->server->ssl_ctx) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02569)
+ "Illegal attempt to re-initialise SSL for server "
+ "(SSLEngine On should go in the VirtualHost, not in global scope.)");
+ return APR_EGENERAL;
+ }
+
+ /* Allow others to provide certificate files */
+ pks = sc->server->pks;
+ n = pks->cert_files->nelts;
+ ap_ssl_add_cert_files(s, p, pks->cert_files, pks->key_files);
+ ssl_run_add_cert_files(s, p, pks->cert_files, pks->key_files);
+
+ if (apr_is_empty_array(pks->cert_files)) {
+ /* does someone propose a certiciate to fall back on here? */
+ ap_ssl_add_fallback_cert_files(s, p, pks->cert_files, pks->key_files);
+ ssl_run_add_fallback_cert_files(s, p, pks->cert_files, pks->key_files);
+ if (n < pks->cert_files->nelts) {
+ pks->service_unavailable = 1;
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10085)
+ "Init: %s will respond with '503 Service Unavailable' for now. There "
+ "are no SSL certificates configured and no other module contributed any.",
+ ssl_util_vhostid(p, s));
+ }
+ }
+
+ if (n < pks->cert_files->nelts) {
+ /* additionally installed certs overrides any old chain configuration */
+ sc->server->cert_chain = NULL;
+ }
+
+ if ((rv = ssl_init_ctx(s, p, ptemp, sc->server)) != APR_SUCCESS) {
+ return rv;
+ }
+
+ if ((rv = ssl_init_server_certs(s, p, ptemp, sc->server, pphrases))
+ != APR_SUCCESS) {
+ return rv;
+ }
+
+#ifdef HAVE_SSL_CONF_CMD
+ SSL_CONF_CTX_set_ssl_ctx(cctx, sc->server->ssl_ctx);
+ for (i = 0; i < sc->server->ssl_ctx_param->nelts; i++, param++) {
+ ERR_clear_error();
+ if (SSL_CONF_cmd(cctx, param->name, param->value) <= 0) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02407)
+ "\"SSLOpenSSLConfCmd %s %s\" failed for %s",
+ param->name, param->value, sc->vhost_id);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02556)
+ "\"SSLOpenSSLConfCmd %s %s\" applied to %s",
+ param->name, param->value, sc->vhost_id);
+ }
+ }
+
+ if (SSL_CONF_CTX_finish(cctx) == 0) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02547)
+ "SSL_CONF_CTX_finish() failed");
+ SSL_CONF_CTX_free(cctx);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+#endif
+
+ if (SSL_CTX_check_private_key(sc->server->ssl_ctx) != 1) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02572)
+ "Failed to configure at least one certificate and key "
+ "for %s", sc->vhost_id);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+#if defined(HAVE_OCSP_STAPLING) && defined(SSL_CTRL_SET_CURRENT_CERT)
+ /*
+ * OpenSSL 1.0.2 and later allows iterating over all SSL_CTX certs
+ * by means of SSL_CTX_set_current_cert. Enabling stapling at this
+ * (late) point makes sure that we catch both certificates loaded
+ * via SSLCertificateFile and SSLOpenSSLConfCmd Certificate.
+ */
+ do {
+ X509 *cert;
+ int i = 0;
+ int ret = SSL_CTX_set_current_cert(sc->server->ssl_ctx,
+ SSL_CERT_SET_FIRST);
+ while (ret) {
+ cert = SSL_CTX_get0_certificate(sc->server->ssl_ctx);
+ if (!cert || !ssl_stapling_init_cert(s, p, ptemp, sc->server,
+ cert)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02604)
+ "Unable to configure certificate %s:%d "
+ "for stapling", sc->vhost_id, i);
+ }
+ ret = SSL_CTX_set_current_cert(sc->server->ssl_ctx,
+ SSL_CERT_SET_NEXT);
+ i++;
+ }
+ } while(0);
+#endif
+
+#ifdef HAVE_TLS_SESSION_TICKETS
+ if ((rv = ssl_init_ticket_key(s, p, ptemp, sc->server)) != APR_SUCCESS) {
+ return rv;
+ }
+#endif
+
+ SSL_CTX_set_timeout(sc->server->ssl_ctx,
+ sc->session_cache_timeout == UNSET ?
+ SSL_SESSION_CACHE_TIMEOUT : sc->session_cache_timeout);
+
+ return APR_SUCCESS;
+}
+
+/*
+ * Configure a particular server
+ */
+apr_status_t ssl_init_ConfigureServer(server_rec *s,
+ apr_pool_t *p,
+ apr_pool_t *ptemp,
+ SSLSrvConfigRec *sc,
+ apr_array_header_t *pphrases)
+{
+ SSLDirConfigRec *sdc = ap_get_module_config(s->lookup_defaults,
+ &ssl_module);
+ apr_status_t rv;
+
+ /* Initialize the server if SSL is enabled or optional.
+ */
+ if ((sc->enabled == SSL_ENABLED_TRUE) || (sc->enabled == SSL_ENABLED_OPTIONAL)) {
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01914)
+ "Configuring server %s for SSL protocol", sc->vhost_id);
+ if ((rv = ssl_init_server_ctx(s, p, ptemp, sc, pphrases))
+ != APR_SUCCESS) {
+ return rv;
+ }
+
+ /* Initialize OCSP Responder certificate if OCSP enabled */
+ #ifndef OPENSSL_NO_OCSP
+ ssl_init_ocsp_certificates(s, sc->server);
+ #endif
+
+ }
+
+ sdc->proxy->sc = sc;
+ if (sdc->proxy_enabled == TRUE) {
+ rv = ssl_init_proxy_ctx(s, p, ptemp, sdc->proxy);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ }
+ else {
+ sdc->proxy_enabled = FALSE;
+ }
+ sdc->proxy_post_config = 1;
+
+ return APR_SUCCESS;
+}
+
+apr_status_t ssl_init_CheckServers(server_rec *base_server, apr_pool_t *p)
+{
+ server_rec *s;
+ SSLSrvConfigRec *sc;
+#ifndef HAVE_TLSEXT
+ server_rec *ps;
+ apr_hash_t *table;
+ const char *key;
+ apr_ssize_t klen;
+
+ BOOL conflict = FALSE;
+#endif
+
+ /*
+ * Give out warnings when a server has HTTPS configured
+ * for the HTTP port or vice versa
+ */
+ for (s = base_server; s; s = s->next) {
+ sc = mySrvConfig(s);
+
+ if ((sc->enabled == SSL_ENABLED_TRUE) && (s->port == DEFAULT_HTTP_PORT)) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
+ base_server, APLOGNO(01915)
+ "Init: (%s) You configured HTTPS(%d) "
+ "on the standard HTTP(%d) port!",
+ ssl_util_vhostid(p, s),
+ DEFAULT_HTTPS_PORT, DEFAULT_HTTP_PORT);
+ }
+
+ if ((sc->enabled == SSL_ENABLED_FALSE) && (s->port == DEFAULT_HTTPS_PORT)) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
+ base_server, APLOGNO(01916)
+ "Init: (%s) You configured HTTP(%d) "
+ "on the standard HTTPS(%d) port!",
+ ssl_util_vhostid(p, s),
+ DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT);
+ }
+ }
+
+#ifndef HAVE_TLSEXT
+ /*
+ * Give out warnings when more than one SSL-aware virtual server uses the
+ * same IP:port and an OpenSSL version without support for TLS extensions
+ * (SNI in particular) is used.
+ */
+ table = apr_hash_make(p);
+
+ for (s = base_server; s; s = s->next) {
+ char *addr;
+
+ sc = mySrvConfig(s);
+
+ if (!((sc->enabled == SSL_ENABLED_TRUE) && s->addrs)) {
+ continue;
+ }
+
+ apr_sockaddr_ip_get(&addr, s->addrs->host_addr);
+ key = apr_psprintf(p, "%s:%u", addr, s->addrs->host_port);
+ klen = strlen(key);
+
+ if ((ps = (server_rec *)apr_hash_get(table, key, klen))) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server, APLOGNO(02662)
+ "Init: SSL server IP/port conflict: "
+ "%s (%s:%d) vs. %s (%s:%d)",
+ ssl_util_vhostid(p, s),
+ (s->defn_name ? s->defn_name : "unknown"),
+ s->defn_line_number,
+ ssl_util_vhostid(p, ps),
+ (ps->defn_name ? ps->defn_name : "unknown"),
+ ps->defn_line_number);
+ conflict = TRUE;
+ continue;
+ }
+
+ apr_hash_set(table, key, klen, s);
+ }
+
+ if (conflict) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server, APLOGNO(01917)
+ "Init: Name-based SSL virtual hosts require "
+ "an OpenSSL version with support for TLS extensions "
+ "(RFC 6066 - Server Name Indication / SNI), "
+ "but the currently used library version (%s) is "
+ "lacking this feature", MODSSL_LIBRARY_DYNTEXT);
+ }
+#endif
+
+ return APR_SUCCESS;
+}
+
+int ssl_proxy_section_post_config(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s,
+ ap_conf_vector_t *section_config)
+{
+ SSLDirConfigRec *sdc = ap_get_module_config(s->lookup_defaults,
+ &ssl_module);
+ SSLDirConfigRec *pdc = ap_get_module_config(section_config,
+ &ssl_module);
+ if (pdc) {
+ pdc->proxy->sc = mySrvConfig(s);
+ ssl_config_proxy_merge(p, sdc, pdc);
+ if (pdc->proxy_enabled) {
+ apr_status_t rv;
+
+ rv = ssl_init_proxy_ctx(s, p, ptemp, pdc->proxy);
+ if (rv != APR_SUCCESS) {
+ return !OK;
+ }
+
+ rv = ssl_run_init_server(s, p, 1, pdc->proxy->ssl_ctx);
+ if (rv != APR_SUCCESS) {
+ return !OK;
+ }
+ }
+ pdc->proxy_post_config = 1;
+ }
+ return OK;
+}
+
+static int ssl_init_FindCAList_X509NameCmp(const X509_NAME * const *a,
+ const X509_NAME * const *b)
+{
+ return(X509_NAME_cmp(*a, *b));
+}
+
+static void ssl_init_PushCAList(STACK_OF(X509_NAME) *ca_list,
+ server_rec *s, apr_pool_t *ptemp,
+ const char *file)
+{
+ int n;
+ STACK_OF(X509_NAME) *sk;
+
+ sk = (STACK_OF(X509_NAME) *)
+ SSL_load_client_CA_file(file);
+
+ if (!sk) {
+ return;
+ }
+
+ for (n = 0; n < sk_X509_NAME_num(sk); n++) {
+ X509_NAME *name = sk_X509_NAME_value(sk, n);
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02209)
+ "CA certificate: %s",
+ modssl_X509_NAME_to_string(ptemp, name, 0));
+
+ /*
+ * note that SSL_load_client_CA_file() checks for duplicates,
+ * but since we call it multiple times when reading a directory
+ * we must also check for duplicates ourselves.
+ */
+
+ if (sk_X509_NAME_find(ca_list, name) < 0) {
+ /* this will be freed when ca_list is */
+ sk_X509_NAME_push(ca_list, name);
+ }
+ else {
+ /* need to free this ourselves, else it will leak */
+ X509_NAME_free(name);
+ }
+ }
+
+ sk_X509_NAME_free(sk);
+}
+
+static apr_status_t ssl_init_ca_cert_path(server_rec *s,
+ apr_pool_t *ptemp,
+ const char *path,
+ STACK_OF(X509_NAME) *ca_list,
+ STACK_OF(X509_INFO) *xi_list)
+{
+ apr_dir_t *dir;
+ apr_finfo_t direntry;
+ apr_int32_t finfo_flags = APR_FINFO_TYPE|APR_FINFO_NAME;
+
+ if (!path || (!ca_list && !xi_list) ||
+ (apr_dir_open(&dir, path, ptemp) != APR_SUCCESS)) {
+ return APR_EGENERAL;
+ }
+
+ while ((apr_dir_read(&direntry, finfo_flags, dir)) == APR_SUCCESS) {
+ const char *file;
+ if (direntry.filetype == APR_DIR) {
+ continue; /* don't try to load directories */
+ }
+ file = apr_pstrcat(ptemp, path, "/", direntry.name, NULL);
+ if (ca_list) {
+ ssl_init_PushCAList(ca_list, s, ptemp, file);
+ }
+ if (xi_list) {
+ load_x509_info(ptemp, xi_list, file);
+ }
+ }
+
+ apr_dir_close(dir);
+
+ return APR_SUCCESS;
+}
+
+STACK_OF(X509_NAME) *ssl_init_FindCAList(server_rec *s,
+ apr_pool_t *ptemp,
+ const char *ca_file,
+ const char *ca_path)
+{
+ STACK_OF(X509_NAME) *ca_list;
+
+ /*
+ * Start with a empty stack/list where new
+ * entries get added in sorted order.
+ */
+ ca_list = sk_X509_NAME_new(ssl_init_FindCAList_X509NameCmp);
+
+ /*
+ * Process CA certificate bundle file
+ */
+ if (ca_file) {
+ ssl_init_PushCAList(ca_list, s, ptemp, ca_file);
+ /*
+ * If ca_list is still empty after trying to load ca_file
+ * then the file failed to load, and users should hear about that.
+ */
+ if (sk_X509_NAME_num(ca_list) == 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02210)
+ "Failed to load SSLCACertificateFile: %s", ca_file);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
+ }
+ }
+
+ /*
+ * Process CA certificate path files
+ */
+ if (ca_path &&
+ ssl_init_ca_cert_path(s, ptemp,
+ ca_path, ca_list, NULL) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02211)
+ "Failed to open Certificate Path `%s'", ca_path);
+ sk_X509_NAME_pop_free(ca_list, X509_NAME_free);
+ return NULL;
+ }
+
+ /*
+ * Cleanup
+ */
+ (void) sk_X509_NAME_set_cmp_func(ca_list, NULL);
+
+ return ca_list;
+}
+
+void ssl_init_Child(apr_pool_t *p, server_rec *s)
+{
+ SSLModConfigRec *mc = myModConfig(s);
+ mc->pid = getpid(); /* only call getpid() once per-process */
+
+ /* XXX: there should be an ap_srand() function */
+ srand((unsigned int)time(NULL));
+
+ /* open the mutex lockfile */
+ ssl_mutex_reinit(s, p);
+#ifdef HAVE_OCSP_STAPLING
+ ssl_stapling_mutex_reinit(s, p);
+#endif
+}
+
+apr_status_t ssl_init_ModuleKill(void *data)
+{
+ SSLSrvConfigRec *sc;
+ server_rec *base_server = (server_rec *)data;
+ server_rec *s;
+
+ /*
+ * Drop the session cache and mutex
+ */
+ ssl_scache_kill(base_server);
+
+ /*
+ * Free the non-pool allocated structures
+ * in the per-server configurations
+ */
+ for (s = base_server; s; s = s->next) {
+ sc = mySrvConfig(s);
+
+ ssl_init_ctx_cleanup(sc->server);
+
+ /* Not Sure but possibly clear X509 trusted cert file */
+ #ifndef OPENSSL_NO_OCSP
+ sk_X509_pop_free(sc->server->ocsp_certs, X509_free);
+ #endif
+
+ }
+
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
+ free_dh_params();
+#else
+ free_bio_methods();
+#endif
+
+ return APR_SUCCESS;
+}
diff --git a/modules/ssl/ssl_engine_io.c b/modules/ssl/ssl_engine_io.c
new file mode 100644
index 0000000..f14fc9b
--- /dev/null
+++ b/modules/ssl/ssl_engine_io.c
@@ -0,0 +1,2419 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+/* _ _
+ * _ __ ___ ___ __| | ___ ___| | mod_ssl
+ * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| | \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ * |_____|
+ * ssl_engine_io.c
+ * I/O Functions
+ */
+ /* ``MY HACK: This universe.
+ Just one little problem:
+ core keeps dumping.''
+ -- Unknown */
+#include "ssl_private.h"
+#include "mod_ssl.h"
+#include "mod_ssl_openssl.h"
+#include "apr_date.h"
+
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, proxy_post_handshake,
+ (conn_rec *c,SSL *ssl),
+ (c,ssl),OK,DECLINED);
+
+/* _________________________________________________________________
+**
+** I/O Hooks
+** _________________________________________________________________
+*/
+
+/* This file is designed to be the bridge between OpenSSL and httpd.
+ * However, we really don't expect anyone (let alone ourselves) to
+ * remember what is in this file. So, first, a quick overview.
+ *
+ * In this file, you will find:
+ * - ssl_io_filter_input (Apache input filter)
+ * - ssl_io_filter_output (Apache output filter)
+ *
+ * - bio_filter_in_* (OpenSSL input filter)
+ * - bio_filter_out_* (OpenSSL output filter)
+ *
+ * The input chain is roughly:
+ *
+ * ssl_io_filter_input->ssl_io_input_read->SSL_read->...
+ * ...->bio_filter_in_read->ap_get_brigade/next-httpd-filter
+ *
+ * In mortal terminology, we do the following:
+ * - Receive a request for data to the SSL input filter
+ * - Call a helper function once we know we should perform a read
+ * - Call OpenSSL's SSL_read()
+ * - SSL_read() will then call bio_filter_in_read
+ * - bio_filter_in_read will then try to fetch data from the next httpd filter
+ * - bio_filter_in_read will flatten that data and return it to SSL_read
+ * - SSL_read will then decrypt the data
+ * - ssl_io_input_read will then receive decrypted data as a char* and
+ * ensure that there were no read errors
+ * - The char* is placed in a brigade and returned
+ *
+ * Since connection-level input filters in httpd need to be able to
+ * handle AP_MODE_GETLINE calls (namely identifying LF-terminated strings),
+ * ssl_io_input_getline which will handle this special case.
+ *
+ * Due to AP_MODE_GETLINE and AP_MODE_SPECULATIVE, we may sometimes have
+ * 'leftover' decoded data which must be setaside for the next read. That
+ * is currently handled by the char_buffer_{read|write} functions. So,
+ * ssl_io_input_read may be able to fulfill reads without invoking
+ * SSL_read().
+ *
+ * Note that the filter context of ssl_io_filter_input and bio_filter_in_*
+ * are shared as bio_filter_in_ctx_t.
+ *
+ * Note that the filter is by choice limited to reading at most
+ * AP_IOBUFSIZE (8192 bytes) per call.
+ *
+ */
+
+/* this custom BIO allows us to hook SSL_write directly into
+ * an apr_bucket_brigade and use transient buckets with the SSL
+ * malloc-ed buffer, rather than copying into a mem BIO.
+ * also allows us to pass the brigade as data is being written
+ * rather than buffering up the entire response in the mem BIO.
+ *
+ * when SSL needs to flush (e.g. SSL_accept()), it will call BIO_flush()
+ * which will trigger a call to bio_filter_out_ctrl() -> bio_filter_out_flush().
+ * so we only need to flush the output ourselves if we receive an
+ * EOS or FLUSH bucket. this was not possible with the mem BIO where we
+ * had to flush all over the place not really knowing when it was required
+ * to do so.
+ */
+
+typedef struct {
+ SSL *pssl;
+ BIO *pbioRead;
+ BIO *pbioWrite;
+ ap_filter_t *pInputFilter;
+ ap_filter_t *pOutputFilter;
+ SSLConnRec *config;
+} ssl_filter_ctx_t;
+
+typedef struct {
+ ssl_filter_ctx_t *filter_ctx;
+ conn_rec *c;
+ apr_bucket_brigade *bb; /* Brigade used as a buffer. */
+ apr_status_t rc;
+} bio_filter_out_ctx_t;
+
+static bio_filter_out_ctx_t *bio_filter_out_ctx_new(ssl_filter_ctx_t *filter_ctx,
+ conn_rec *c)
+{
+ bio_filter_out_ctx_t *outctx = apr_palloc(c->pool, sizeof(*outctx));
+
+ outctx->filter_ctx = filter_ctx;
+ outctx->c = c;
+ outctx->bb = apr_brigade_create(c->pool, c->bucket_alloc);
+
+ return outctx;
+}
+
+/* Pass an output brigade down the filter stack; returns 1 on success
+ * or -1 on failure. */
+static int bio_filter_out_pass(bio_filter_out_ctx_t *outctx)
+{
+ AP_DEBUG_ASSERT(!APR_BRIGADE_EMPTY(outctx->bb));
+
+ outctx->rc = ap_pass_brigade(outctx->filter_ctx->pOutputFilter->next,
+ outctx->bb);
+ /* Fail if the connection was reset: */
+ if (outctx->rc == APR_SUCCESS && outctx->c->aborted) {
+ outctx->rc = APR_ECONNRESET;
+ }
+ return (outctx->rc == APR_SUCCESS) ? 1 : -1;
+}
+
+/* Send a FLUSH bucket down the output filter stack; returns 1 on
+ * success, -1 on failure. */
+static int bio_filter_out_flush(BIO *bio)
+{
+ bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)BIO_get_data(bio);
+ apr_bucket *e;
+
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, outctx->c,
+ "bio_filter_out_write: flush");
+
+ AP_DEBUG_ASSERT(APR_BRIGADE_EMPTY(outctx->bb));
+
+ e = apr_bucket_flush_create(outctx->bb->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(outctx->bb, e);
+
+ return bio_filter_out_pass(outctx);
+}
+
+static int bio_filter_create(BIO *bio)
+{
+ BIO_set_shutdown(bio, 1);
+ BIO_set_init(bio, 1);
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
+ /* No setter method for OpenSSL 1.1.0 available,
+ * but I can't find any functional use of the
+ * "num" field there either.
+ */
+ bio->num = -1;
+#endif
+ BIO_set_data(bio, NULL);
+
+ return 1;
+}
+
+static int bio_filter_destroy(BIO *bio)
+{
+ if (bio == NULL) {
+ return 0;
+ }
+
+ /* nothing to free here.
+ * apache will destroy the bucket brigade for us
+ */
+ return 1;
+}
+
+static int bio_filter_out_read(BIO *bio, char *out, int outl)
+{
+ /* this is never called */
+ bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)BIO_get_data(bio);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, outctx->c,
+ "BUG: %s() should not be called", "bio_filter_out_read");
+ AP_DEBUG_ASSERT(0);
+ return -1;
+}
+
+static int bio_filter_out_write(BIO *bio, const char *in, int inl)
+{
+ bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)BIO_get_data(bio);
+ apr_bucket *e;
+ int need_flush;
+
+ BIO_clear_retry_flags(bio);
+
+ /* Abort early if the client has initiated a renegotiation. */
+ if (outctx->filter_ctx->config->reneg_state == RENEG_ABORT) {
+ outctx->rc = APR_ECONNABORTED;
+ return -1;
+ }
+
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, outctx->c,
+ "bio_filter_out_write: %i bytes", inl);
+
+ /* Use a transient bucket for the output data - any downstream
+ * filter must setaside if necessary. */
+ e = apr_bucket_transient_create(in, inl, outctx->bb->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(outctx->bb, e);
+
+ /* In theory, OpenSSL should flush as necessary, but it is known
+ * not to do so correctly in some cases (< 0.9.8m; see PR 46952),
+ * or on the proxy/client side (after ssl23_client_hello(), e.g.
+ * ssl/proxy.t test suite).
+ *
+ * Historically, this flush call was performed only for an SSLv2
+ * connection or for a proxy connection. Calling _out_flush can
+ * be expensive in cases where requests/responses are pipelined,
+ * so limit the performance impact to handshake time.
+ */
+#if OPENSSL_VERSION_NUMBER < 0x0009080df
+ need_flush = !SSL_is_init_finished(outctx->filter_ctx->pssl);
+#else
+ need_flush = SSL_in_connect_init(outctx->filter_ctx->pssl);
+#endif
+ if (need_flush) {
+ e = apr_bucket_flush_create(outctx->bb->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(outctx->bb, e);
+ }
+
+ if (bio_filter_out_pass(outctx) < 0) {
+ return -1;
+ }
+
+ return inl;
+}
+
+static long bio_filter_out_ctrl(BIO *bio, int cmd, long num, void *ptr)
+{
+ long ret = 1;
+ bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)BIO_get_data(bio);
+
+ switch (cmd) {
+ case BIO_CTRL_RESET:
+ case BIO_CTRL_EOF:
+ case BIO_C_SET_BUF_MEM_EOF_RETURN:
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, outctx->c,
+ "output bio: unhandled control %d", cmd);
+ ret = 0;
+ break;
+ case BIO_CTRL_WPENDING:
+ case BIO_CTRL_PENDING:
+ case BIO_CTRL_INFO:
+ ret = 0;
+ break;
+ case BIO_CTRL_GET_CLOSE:
+ ret = (long)BIO_get_shutdown(bio);
+ break;
+ case BIO_CTRL_SET_CLOSE:
+ BIO_set_shutdown(bio, (int)num);
+ break;
+ case BIO_CTRL_FLUSH:
+ ret = bio_filter_out_flush(bio);
+ break;
+ case BIO_CTRL_DUP:
+ ret = 1;
+ break;
+ /* N/A */
+ case BIO_C_SET_BUF_MEM:
+ case BIO_C_GET_BUF_MEM_PTR:
+ /* we don't care */
+ case BIO_CTRL_PUSH:
+ case BIO_CTRL_POP:
+ default:
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+static int bio_filter_out_gets(BIO *bio, char *buf, int size)
+{
+ /* this is never called */
+ bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)BIO_get_data(bio);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, outctx->c,
+ "BUG: %s() should not be called", "bio_filter_out_gets");
+ AP_DEBUG_ASSERT(0);
+ return -1;
+}
+
+static int bio_filter_out_puts(BIO *bio, const char *str)
+{
+ /* this is never called */
+ bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)BIO_get_data(bio);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, outctx->c,
+ "BUG: %s() should not be called", "bio_filter_out_puts");
+ AP_DEBUG_ASSERT(0);
+ return -1;
+}
+
+typedef struct {
+ int length;
+ char *value;
+} char_buffer_t;
+
+typedef struct {
+ SSL *ssl;
+ BIO *bio_out;
+ ap_filter_t *f;
+ apr_status_t rc;
+ ap_input_mode_t mode;
+ apr_read_type_e block;
+ apr_bucket_brigade *bb;
+ char_buffer_t cbuf;
+ apr_pool_t *pool;
+ char buffer[AP_IOBUFSIZE];
+ ssl_filter_ctx_t *filter_ctx;
+} bio_filter_in_ctx_t;
+
+/*
+ * this char_buffer api might seem silly, but we don't need to copy
+ * any of this data and we need to remember the length.
+ */
+
+/* Copy up to INL bytes from the char_buffer BUFFER into IN. Note
+ * that due to the strange way this API is designed/used, the
+ * char_buffer object is used to cache a segment of inctx->buffer, and
+ * then this function called to copy (part of) that segment to the
+ * beginning of inctx->buffer. So the segments to copy cannot be
+ * presumed to be non-overlapping, and memmove must be used. */
+static int char_buffer_read(char_buffer_t *buffer, char *in, int inl)
+{
+ if (!buffer->length) {
+ return 0;
+ }
+
+ if (buffer->length > inl) {
+ /* we have enough to fill the caller's buffer */
+ memmove(in, buffer->value, inl);
+ buffer->value += inl;
+ buffer->length -= inl;
+ }
+ else {
+ /* swallow remainder of the buffer */
+ memmove(in, buffer->value, buffer->length);
+ inl = buffer->length;
+ buffer->value = NULL;
+ buffer->length = 0;
+ }
+
+ return inl;
+}
+
+static int char_buffer_write(char_buffer_t *buffer, char *in, int inl)
+{
+ buffer->value = in;
+ buffer->length = inl;
+ return inl;
+}
+
+/* This function will read from a brigade and discard the read buckets as it
+ * proceeds. It will read at most *len bytes.
+ */
+static apr_status_t brigade_consume(apr_bucket_brigade *bb,
+ apr_read_type_e block,
+ char *c, apr_size_t *len)
+{
+ apr_size_t actual = 0;
+ apr_status_t status = APR_SUCCESS;
+
+ while (!APR_BRIGADE_EMPTY(bb)) {
+ apr_bucket *b = APR_BRIGADE_FIRST(bb);
+ const char *str;
+ apr_size_t str_len;
+ apr_size_t consume;
+
+ /* Justin points out this is an http-ism that might
+ * not fit if brigade_consume is added to APR. Perhaps
+ * apr_bucket_read(eos_bucket) should return APR_EOF?
+ * Then this becomes mainline instead of a one-off.
+ */
+ if (APR_BUCKET_IS_EOS(b)) {
+ status = APR_EOF;
+ break;
+ }
+
+ /* The reason I'm not offering brigade_consume yet
+ * across to apr-util is that the following call
+ * illustrates how borked that API really is. For
+ * this sort of case (caller provided buffer) it
+ * would be much more trivial for apr_bucket_consume
+ * to do all the work that follows, based on the
+ * particular characteristics of the bucket we are
+ * consuming here.
+ */
+ status = apr_bucket_read(b, &str, &str_len, block);
+
+ if (status != APR_SUCCESS) {
+ if (APR_STATUS_IS_EOF(status)) {
+ /* This stream bucket was consumed */
+ apr_bucket_delete(b);
+ continue;
+ }
+ break;
+ }
+
+ if (str_len > 0) {
+ /* Do not block once some data has been consumed */
+ block = APR_NONBLOCK_READ;
+
+ /* Assure we don't overflow. */
+ consume = (str_len + actual > *len) ? *len - actual : str_len;
+
+ memcpy(c, str, consume);
+
+ c += consume;
+ actual += consume;
+
+ if (consume >= b->length) {
+ /* This physical bucket was consumed */
+ apr_bucket_delete(b);
+ }
+ else {
+ /* Only part of this physical bucket was consumed */
+ b->start += consume;
+ b->length -= consume;
+ }
+ }
+ else if (b->length == 0) {
+ apr_bucket_delete(b);
+ }
+
+ /* This could probably be actual == *len, but be safe from stray
+ * photons. */
+ if (actual >= *len) {
+ break;
+ }
+ }
+
+ *len = actual;
+ return status;
+}
+
+/*
+ * this is the function called by SSL_read()
+ */
+static int bio_filter_in_read(BIO *bio, char *in, int inlen)
+{
+ apr_size_t inl = inlen;
+ bio_filter_in_ctx_t *inctx = (bio_filter_in_ctx_t *)BIO_get_data(bio);
+ apr_read_type_e block = inctx->block;
+
+ inctx->rc = APR_SUCCESS;
+
+ /* OpenSSL catches this case, so should we. */
+ if (!in)
+ return 0;
+
+ BIO_clear_retry_flags(bio);
+
+ /* Abort early if the client has initiated a renegotiation. */
+ if (inctx->filter_ctx->config->reneg_state == RENEG_ABORT) {
+ inctx->rc = APR_ECONNABORTED;
+ return -1;
+ }
+
+ if (!inctx->bb) {
+ inctx->rc = APR_EOF;
+ return -1;
+ }
+
+ if (APR_BRIGADE_EMPTY(inctx->bb)) {
+
+ inctx->rc = ap_get_brigade(inctx->f->next, inctx->bb,
+ AP_MODE_READBYTES, block,
+ inl);
+
+ /* If the read returns EAGAIN or success with an empty
+ * brigade, return an error after setting the retry flag;
+ * SSL_read() will then return -1, and SSL_get_error() will
+ * indicate SSL_ERROR_WANT_READ. */
+ if (APR_STATUS_IS_EAGAIN(inctx->rc) || APR_STATUS_IS_EINTR(inctx->rc)
+ || (inctx->rc == APR_SUCCESS && APR_BRIGADE_EMPTY(inctx->bb))) {
+ BIO_set_retry_read(bio);
+ return -1;
+ }
+
+ if (block == APR_BLOCK_READ
+ && APR_STATUS_IS_TIMEUP(inctx->rc)
+ && APR_BRIGADE_EMPTY(inctx->bb)) {
+ /* don't give up, just return the timeout */
+ return -1;
+ }
+ if (inctx->rc != APR_SUCCESS) {
+ /* Unexpected errors discard the brigade */
+ apr_brigade_cleanup(inctx->bb);
+ inctx->bb = NULL;
+ return -1;
+ }
+ }
+
+ inctx->rc = brigade_consume(inctx->bb, block, in, &inl);
+
+ if (inctx->rc == APR_SUCCESS) {
+ return (int)inl;
+ }
+
+ if (APR_STATUS_IS_EAGAIN(inctx->rc)
+ || APR_STATUS_IS_EINTR(inctx->rc)) {
+ BIO_set_retry_read(bio);
+ return (int)inl;
+ }
+
+ /* Unexpected errors and APR_EOF clean out the brigade.
+ * Subsequent calls will return APR_EOF.
+ */
+ apr_brigade_cleanup(inctx->bb);
+ inctx->bb = NULL;
+
+ if (APR_STATUS_IS_EOF(inctx->rc) && inl) {
+ /* Provide the results of this read pass,
+ * without resetting the BIO retry_read flag
+ */
+ return (int)inl;
+ }
+
+ return -1;
+}
+
+static int bio_filter_in_write(BIO *bio, const char *in, int inl)
+{
+ bio_filter_in_ctx_t *inctx = (bio_filter_in_ctx_t *)BIO_get_data(bio);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, inctx->f->c,
+ "BUG: %s() should not be called", "bio_filter_in_write");
+ AP_DEBUG_ASSERT(0);
+ return -1;
+}
+
+static int bio_filter_in_puts(BIO *bio, const char *str)
+{
+ bio_filter_in_ctx_t *inctx = (bio_filter_in_ctx_t *)BIO_get_data(bio);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, inctx->f->c,
+ "BUG: %s() should not be called", "bio_filter_in_puts");
+ AP_DEBUG_ASSERT(0);
+ return -1;
+}
+
+static int bio_filter_in_gets(BIO *bio, char *buf, int size)
+{
+ bio_filter_in_ctx_t *inctx = (bio_filter_in_ctx_t *)BIO_get_data(bio);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, inctx->f->c,
+ "BUG: %s() should not be called", "bio_filter_in_gets");
+ AP_DEBUG_ASSERT(0);
+ return -1;
+}
+
+static long bio_filter_in_ctrl(BIO *bio, int cmd, long num, void *ptr)
+{
+ bio_filter_in_ctx_t *inctx = (bio_filter_in_ctx_t *)BIO_get_data(bio);
+ switch (cmd) {
+#ifdef BIO_CTRL_EOF
+ case BIO_CTRL_EOF:
+ return inctx->rc == APR_EOF;
+#endif
+ default:
+ break;
+ }
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, inctx->f->c,
+ "BUG: bio_filter_in_ctrl() should not be called with cmd=%i",
+ cmd);
+ return 0;
+}
+
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
+
+static BIO_METHOD bio_filter_out_method = {
+ BIO_TYPE_MEM,
+ "APR output filter",
+ bio_filter_out_write,
+ bio_filter_out_read, /* read is never called */
+ bio_filter_out_puts, /* puts is never called */
+ bio_filter_out_gets, /* gets is never called */
+ bio_filter_out_ctrl,
+ bio_filter_create,
+ bio_filter_destroy,
+ NULL
+};
+
+static BIO_METHOD bio_filter_in_method = {
+ BIO_TYPE_MEM,
+ "APR input filter",
+ bio_filter_in_write, /* write is never called */
+ bio_filter_in_read,
+ bio_filter_in_puts, /* puts is never called */
+ bio_filter_in_gets, /* gets is never called */
+ bio_filter_in_ctrl, /* ctrl is called for EOF check */
+ bio_filter_create,
+ bio_filter_destroy,
+ NULL
+};
+
+#else
+
+static BIO_METHOD *bio_filter_out_method = NULL;
+static BIO_METHOD *bio_filter_in_method = NULL;
+
+void init_bio_methods(void)
+{
+ bio_filter_out_method = BIO_meth_new(BIO_TYPE_MEM, "APR output filter");
+ BIO_meth_set_write(bio_filter_out_method, &bio_filter_out_write);
+ BIO_meth_set_read(bio_filter_out_method, &bio_filter_out_read); /* read is never called */
+ BIO_meth_set_puts(bio_filter_out_method, &bio_filter_out_puts); /* puts is never called */
+ BIO_meth_set_gets(bio_filter_out_method, &bio_filter_out_gets); /* gets is never called */
+ BIO_meth_set_ctrl(bio_filter_out_method, &bio_filter_out_ctrl);
+ BIO_meth_set_create(bio_filter_out_method, &bio_filter_create);
+ BIO_meth_set_destroy(bio_filter_out_method, &bio_filter_destroy);
+
+ bio_filter_in_method = BIO_meth_new(BIO_TYPE_MEM, "APR input filter");
+ BIO_meth_set_write(bio_filter_in_method, &bio_filter_in_write); /* write is never called */
+ BIO_meth_set_read(bio_filter_in_method, &bio_filter_in_read);
+ BIO_meth_set_puts(bio_filter_in_method, &bio_filter_in_puts); /* puts is never called */
+ BIO_meth_set_gets(bio_filter_in_method, &bio_filter_in_gets); /* gets is never called */
+ BIO_meth_set_ctrl(bio_filter_in_method, &bio_filter_in_ctrl); /* ctrl is never called */
+ BIO_meth_set_create(bio_filter_in_method, &bio_filter_create);
+ BIO_meth_set_destroy(bio_filter_in_method, &bio_filter_destroy);
+}
+
+void free_bio_methods(void)
+{
+ BIO_meth_free(bio_filter_out_method);
+ BIO_meth_free(bio_filter_in_method);
+}
+#endif
+
+static apr_status_t ssl_io_input_read(bio_filter_in_ctx_t *inctx,
+ char *buf,
+ apr_size_t *len)
+{
+ apr_size_t wanted = *len;
+ apr_size_t bytes = 0;
+ int rc;
+
+ *len = 0;
+
+ /* If we have something leftover from last time, try that first. */
+ if ((bytes = char_buffer_read(&inctx->cbuf, buf, wanted))) {
+ *len = bytes;
+ if (inctx->mode == AP_MODE_SPECULATIVE) {
+ /* We want to rollback this read. */
+ if (inctx->cbuf.length > 0) {
+ inctx->cbuf.value -= bytes;
+ inctx->cbuf.length += bytes;
+ } else {
+ char_buffer_write(&inctx->cbuf, buf, (int)bytes);
+ }
+ return APR_SUCCESS;
+ }
+ /* This could probably be *len == wanted, but be safe from stray
+ * photons.
+ */
+ if (*len >= wanted) {
+ return APR_SUCCESS;
+ }
+ if (inctx->mode == AP_MODE_GETLINE) {
+ if (memchr(buf, APR_ASCII_LF, *len)) {
+ return APR_SUCCESS;
+ }
+ }
+ else {
+ /* Down to a nonblock pattern as we have some data already
+ */
+ inctx->block = APR_NONBLOCK_READ;
+ }
+ }
+
+ while (1) {
+
+ if (!inctx->filter_ctx->pssl) {
+ /* Ensure a non-zero error code is returned */
+ if (inctx->rc == APR_SUCCESS) {
+ inctx->rc = APR_EGENERAL;
+ }
+ break;
+ }
+
+ /* We rely on SSL_get_error() after the read, which requires an empty
+ * error queue before the read in order to work properly.
+ */
+ ERR_clear_error();
+
+ /* SSL_read may not read because we haven't taken enough data
+ * from the stack. This is where we want to consider all of
+ * the blocking and SPECULATIVE semantics
+ */
+ rc = SSL_read(inctx->filter_ctx->pssl, buf + bytes, wanted - bytes);
+
+ if (rc > 0) {
+ *len += rc;
+ if (inctx->mode == AP_MODE_SPECULATIVE) {
+ /* We want to rollback this read. */
+ char_buffer_write(&inctx->cbuf, buf, rc);
+ }
+ return inctx->rc;
+ }
+ else /* (rc <= 0) */ {
+ int ssl_err;
+ conn_rec *c;
+ if (rc == 0) {
+ /* If EAGAIN, we will loop given a blocking read,
+ * otherwise consider ourselves at EOF.
+ */
+ if (APR_STATUS_IS_EAGAIN(inctx->rc)
+ || APR_STATUS_IS_EINTR(inctx->rc)) {
+ /* Already read something, return APR_SUCCESS instead.
+ * On win32 in particular, but perhaps on other kernels,
+ * a blocking call isn't 'always' blocking.
+ */
+ if (*len > 0) {
+ inctx->rc = APR_SUCCESS;
+ break;
+ }
+ if (inctx->block == APR_NONBLOCK_READ) {
+ break;
+ }
+ }
+ else {
+ if (*len > 0) {
+ inctx->rc = APR_SUCCESS;
+ break;
+ }
+ }
+ }
+ ssl_err = SSL_get_error(inctx->filter_ctx->pssl, rc);
+ c = (conn_rec*)SSL_get_app_data(inctx->filter_ctx->pssl);
+
+ if (ssl_err == SSL_ERROR_WANT_READ) {
+ /*
+ * If OpenSSL wants to read more, and we were nonblocking,
+ * report as an EAGAIN. Otherwise loop, pulling more
+ * data from network filter.
+ *
+ * (This is usually the case when the client forces an SSL
+ * renegotiation which is handled implicitly by OpenSSL.)
+ */
+ inctx->rc = APR_EAGAIN;
+
+ if (*len > 0) {
+ inctx->rc = APR_SUCCESS;
+ break;
+ }
+ if (inctx->block == APR_NONBLOCK_READ) {
+ break;
+ }
+ continue; /* Blocking and nothing yet? Try again. */
+ }
+ else if (ssl_err == SSL_ERROR_SYSCALL) {
+ if (APR_STATUS_IS_EAGAIN(inctx->rc)
+ || APR_STATUS_IS_EINTR(inctx->rc)) {
+ /* Already read something, return APR_SUCCESS instead. */
+ if (*len > 0) {
+ inctx->rc = APR_SUCCESS;
+ break;
+ }
+ if (inctx->block == APR_NONBLOCK_READ) {
+ break;
+ }
+ continue; /* Blocking and nothing yet? Try again. */
+ }
+ else if (APR_STATUS_IS_TIMEUP(inctx->rc)) {
+ /* just return it, the calling layer might be fine with it,
+ and we do not want to bloat the log. */
+ }
+ else {
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, inctx->rc, c, APLOGNO(01991)
+ "SSL input filter read failed.");
+ }
+ }
+ else if (rc == 0 && ssl_err == SSL_ERROR_ZERO_RETURN) {
+ inctx->rc = APR_EOF;
+ break;
+ }
+ else /* if (ssl_err == SSL_ERROR_SSL) */ {
+ /*
+ * Log SSL errors and any unexpected conditions.
+ */
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, inctx->rc, c, APLOGNO(01992)
+ "SSL library error %d reading data", ssl_err);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_INFO, mySrvFromConn(c));
+
+ }
+ if (rc == 0) {
+ inctx->rc = APR_EOF;
+ break;
+ }
+ if (inctx->rc == APR_SUCCESS) {
+ inctx->rc = APR_EGENERAL;
+ }
+ break;
+ }
+ }
+ return inctx->rc;
+}
+
+/* Read a line of input from the SSL input layer into buffer BUF of
+ * length *LEN; updating *len to reflect the length of the line
+ * including the LF character. */
+static apr_status_t ssl_io_input_getline(bio_filter_in_ctx_t *inctx,
+ char *buf,
+ apr_size_t *len)
+{
+ const char *pos = NULL;
+ apr_status_t status;
+ apr_size_t tmplen = *len, buflen = *len, offset = 0;
+
+ *len = 0;
+
+ /*
+ * in most cases we get all the headers on the first SSL_read.
+ * however, in certain cases SSL_read will only get a partial
+ * chunk of the headers, so we try to read until LF is seen.
+ */
+
+ while (tmplen > 0) {
+ status = ssl_io_input_read(inctx, buf + offset, &tmplen);
+
+ if (status != APR_SUCCESS) {
+ if (APR_STATUS_IS_EAGAIN(status) && (*len > 0)) {
+ /* Save the part of the line we already got */
+ char_buffer_write(&inctx->cbuf, buf, *len);
+ }
+ return status;
+ }
+
+ *len += tmplen;
+
+ if ((pos = memchr(buf, APR_ASCII_LF, *len))) {
+ break;
+ }
+
+ offset += tmplen;
+ tmplen = buflen - offset;
+ }
+
+ if (pos) {
+ char *value;
+ int length;
+ apr_size_t bytes = pos - buf;
+
+ bytes += 1;
+ value = buf + bytes;
+ length = *len - bytes;
+
+ char_buffer_write(&inctx->cbuf, value, length);
+
+ *len = bytes;
+ }
+
+ return APR_SUCCESS;
+}
+
+
+static apr_status_t ssl_filter_write(ap_filter_t *f,
+ const char *data,
+ apr_size_t len)
+{
+ ssl_filter_ctx_t *filter_ctx = f->ctx;
+ bio_filter_out_ctx_t *outctx;
+ int res;
+
+ /* write SSL */
+ if (filter_ctx->pssl == NULL) {
+ return APR_EGENERAL;
+ }
+
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, f->c,
+ "ssl_filter_write: %"APR_SIZE_T_FMT" bytes", len);
+
+ /* We rely on SSL_get_error() after the write, which requires an empty error
+ * queue before the write in order to work properly.
+ */
+ ERR_clear_error();
+
+ outctx = (bio_filter_out_ctx_t *)BIO_get_data(filter_ctx->pbioWrite);
+ res = SSL_write(filter_ctx->pssl, (unsigned char *)data, len);
+
+ if (res < 0) {
+ int ssl_err = SSL_get_error(filter_ctx->pssl, res);
+ conn_rec *c = (conn_rec*)SSL_get_app_data(outctx->filter_ctx->pssl);
+
+ if (ssl_err == SSL_ERROR_WANT_WRITE) {
+ /*
+ * If OpenSSL wants to write more, and we were nonblocking,
+ * report as an EAGAIN. Otherwise loop, pushing more
+ * data at the network filter.
+ *
+ * (This is usually the case when the client forces an SSL
+ * renegotiation which is handled implicitly by OpenSSL.)
+ */
+ outctx->rc = APR_EAGAIN;
+ }
+ else if (ssl_err == SSL_ERROR_WANT_READ) {
+ /*
+ * If OpenSSL wants to read during write, and we were
+ * nonblocking, set the sense explicitly to read and
+ * report as an EAGAIN.
+ *
+ * (This is usually the case when the client forces an SSL
+ * renegotiation which is handled implicitly by OpenSSL.)
+ */
+ outctx->c->cs->sense = CONN_SENSE_WANT_READ;
+ outctx->rc = APR_EAGAIN;
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, outctx->c,
+ "Want read during nonblocking write");
+ }
+ else if (ssl_err == SSL_ERROR_SYSCALL) {
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, outctx->rc, c, APLOGNO(01993)
+ "SSL output filter write failed.");
+ }
+ else /* if (ssl_err == SSL_ERROR_SSL) */ {
+ /*
+ * Log SSL errors
+ */
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, outctx->rc, c, APLOGNO(01994)
+ "SSL library error %d writing data", ssl_err);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_INFO, mySrvFromConn(c));
+ }
+ if (outctx->rc == APR_SUCCESS) {
+ outctx->rc = APR_EGENERAL;
+ }
+ }
+ else if ((apr_size_t)res != len) {
+ conn_rec *c = f->c;
+ char *reason = "reason unknown";
+
+ /* XXX: probably a better way to determine this */
+ if (SSL_total_renegotiations(filter_ctx->pssl)) {
+ reason = "likely due to failed renegotiation";
+ }
+
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, outctx->rc, c, APLOGNO(01995)
+ "failed to write %" APR_SSIZE_T_FMT
+ " of %" APR_SIZE_T_FMT " bytes (%s)",
+ len - (apr_size_t)res, len, reason);
+
+ outctx->rc = APR_EGENERAL;
+ }
+ return outctx->rc;
+}
+
+/* Just use a simple request. Any request will work for this, because
+ * we use a flag in the conn_rec->conn_vector now. The fake request just
+ * gets the request back to the Apache core so that a response can be sent.
+ * Since we use an HTTP/1.x request, we also have to inject the empty line
+ * that terminates the headers, or the core will read more data from the
+ * socket.
+ */
+#define HTTP_ON_HTTPS_PORT \
+ "GET / HTTP/1.0" CRLF
+
+#define HTTP_ON_HTTPS_PORT_BUCKET(alloc) \
+ apr_bucket_immortal_create(HTTP_ON_HTTPS_PORT, \
+ sizeof(HTTP_ON_HTTPS_PORT) - 1, \
+ alloc)
+
+/* Custom apr_status_t error code, used when a plain HTTP request is
+ * received on an SSL port. */
+#define MODSSL_ERROR_HTTP_ON_HTTPS (APR_OS_START_USERERR + 0)
+
+/* Custom apr_status_t error code, used when the proxy cannot
+ * establish an outgoing SSL connection. */
+#define MODSSL_ERROR_BAD_GATEWAY (APR_OS_START_USERERR + 1)
+
+static void ssl_io_filter_disable(SSLConnRec *sslconn,
+ bio_filter_in_ctx_t *inctx)
+{
+ SSL_free(inctx->ssl);
+ sslconn->ssl = NULL;
+ inctx->ssl = NULL;
+ inctx->filter_ctx->pssl = NULL;
+}
+
+static apr_status_t ssl_io_filter_error(bio_filter_in_ctx_t *inctx,
+ apr_bucket_brigade *bb,
+ apr_status_t status,
+ int is_init)
+{
+ ap_filter_t *f = inctx->f;
+ SSLConnRec *sslconn = myConnConfig(f->c);
+ apr_bucket *bucket;
+ int send_eos = 1;
+
+ switch (status) {
+ case MODSSL_ERROR_HTTP_ON_HTTPS:
+ /* log the situation */
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, f->c, APLOGNO(01996)
+ "SSL handshake failed: HTTP spoken on HTTPS port; "
+ "trying to send HTML error page");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_INFO, sslconn->server);
+
+ ssl_io_filter_disable(sslconn, inctx);
+ f->c->keepalive = AP_CONN_CLOSE;
+ if (is_init) {
+ sslconn->non_ssl_request = NON_SSL_SEND_REQLINE;
+ return APR_EGENERAL;
+ }
+ sslconn->non_ssl_request = NON_SSL_SEND_HDR_SEP;
+
+ /* fake the request line */
+ bucket = HTTP_ON_HTTPS_PORT_BUCKET(f->c->bucket_alloc);
+ send_eos = 0;
+ break;
+
+ case MODSSL_ERROR_BAD_GATEWAY:
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, f->c, APLOGNO(01997)
+ "SSL handshake failed: sending 502");
+ f->c->aborted = 1;
+ return APR_EGENERAL;
+
+ default:
+ return status;
+ }
+
+ APR_BRIGADE_INSERT_TAIL(bb, bucket);
+ if (send_eos) {
+ bucket = apr_bucket_eos_create(f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, bucket);
+ }
+ return APR_SUCCESS;
+}
+
+static const char ssl_io_filter[] = "SSL/TLS Filter";
+static const char ssl_io_buffer[] = "SSL/TLS Buffer";
+static const char ssl_io_coalesce[] = "SSL/TLS Coalescing Filter";
+
+/*
+ * Close the SSL part of the socket connection
+ * (called immediately _before_ the socket is closed)
+ * or called with
+ */
+static void ssl_filter_io_shutdown(ssl_filter_ctx_t *filter_ctx,
+ conn_rec *c, int abortive)
+{
+ SSL *ssl = filter_ctx->pssl;
+ const char *type = "";
+ SSLConnRec *sslconn = myConnConfig(c);
+ int shutdown_type;
+ int loglevel = APLOG_DEBUG;
+ const char *logno;
+
+ if (!ssl) {
+ return;
+ }
+
+ /*
+ * Now close the SSL layer of the connection. We've to take
+ * the TLSv1 standard into account here:
+ *
+ * | 7.2.1. Closure alerts
+ * |
+ * | The client and the server must share knowledge that the connection is
+ * | ending in order to avoid a truncation attack. Either party may
+ * | initiate the exchange of closing messages.
+ * |
+ * | close_notify
+ * | This message notifies the recipient that the sender will not send
+ * | any more messages on this connection. The session becomes
+ * | unresumable if any connection is terminated without proper
+ * | close_notify messages with level equal to warning.
+ * |
+ * | Either party may initiate a close by sending a close_notify alert.
+ * | Any data received after a closure alert is ignored.
+ * |
+ * | Each party is required to send a close_notify alert before closing
+ * | the write side of the connection. It is required that the other party
+ * | respond with a close_notify alert of its own and close down the
+ * | connection immediately, discarding any pending writes. It is not
+ * | required for the initiator of the close to wait for the responding
+ * | close_notify alert before closing the read side of the connection.
+ *
+ * This means we've to send a close notify message, but haven't to wait
+ * for the close notify of the client. Actually we cannot wait for the
+ * close notify of the client because some clients (including Netscape
+ * 4.x) don't send one, so we would hang.
+ */
+
+ /*
+ * exchange close notify messages, but allow the user
+ * to force the type of handshake via SetEnvIf directive
+ */
+ if (abortive) {
+ shutdown_type = SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN;
+ type = "abortive";
+ logno = APLOGNO(01998);
+ loglevel = APLOG_INFO;
+ }
+ else switch (sslconn->shutdown_type) {
+ case SSL_SHUTDOWN_TYPE_UNCLEAN:
+ /* perform no close notify handshake at all
+ (violates the SSL/TLS standard!) */
+ shutdown_type = SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN;
+ type = "unclean";
+ logno = APLOGNO(01999);
+ break;
+ case SSL_SHUTDOWN_TYPE_ACCURATE:
+ /* send close notify and wait for clients close notify
+ (standard compliant, but usually causes connection hangs) */
+ shutdown_type = 0;
+ type = "accurate";
+ logno = APLOGNO(02000);
+ break;
+ default:
+ /*
+ * case SSL_SHUTDOWN_TYPE_UNSET:
+ * case SSL_SHUTDOWN_TYPE_STANDARD:
+ */
+ /* send close notify, but don't wait for clients close notify
+ (standard compliant and safe, so it's the DEFAULT!) */
+ shutdown_type = SSL_RECEIVED_SHUTDOWN;
+ type = "standard";
+ logno = APLOGNO(02001);
+ break;
+ }
+
+ SSL_set_shutdown(ssl, shutdown_type);
+ modssl_smart_shutdown(ssl);
+
+ /* and finally log the fact that we've closed the connection */
+ if (APLOG_CS_IS_LEVEL(c, mySrvFromConn(c), loglevel)) {
+ /* Intentional no APLOGNO */
+ /* logno provides APLOGNO */
+ ap_log_cserror(APLOG_MARK, loglevel, 0, c, mySrvFromConn(c),
+ "%sConnection closed to child %ld with %s shutdown "
+ "(server %s)",
+ logno, c->id, type,
+ ssl_util_vhostid(c->pool, mySrvFromConn(c)));
+ }
+
+ /* deallocate the SSL connection */
+ if (sslconn->client_cert) {
+ X509_free(sslconn->client_cert);
+ sslconn->client_cert = NULL;
+ }
+ SSL_free(ssl);
+ sslconn->ssl = NULL;
+ filter_ctx->pssl = NULL; /* so filters know we've been shutdown */
+
+ if (abortive) {
+ /* prevent any further I/O */
+ c->aborted = 1;
+ }
+}
+
+static apr_status_t ssl_io_filter_cleanup(void *data)
+{
+ ssl_filter_ctx_t *filter_ctx = data;
+
+ if (filter_ctx->pssl) {
+ conn_rec *c = (conn_rec *)SSL_get_app_data(filter_ctx->pssl);
+ SSLConnRec *sslconn = myConnConfig(c);
+
+ SSL_free(filter_ctx->pssl);
+ sslconn->ssl = filter_ctx->pssl = NULL;
+ }
+
+ return APR_SUCCESS;
+}
+
+/*
+ * The hook is NOT registered with ap_hook_process_connection. Instead, it is
+ * called manually from the churn () before it tries to read any data.
+ * There is some problem if I accept conn_rec *. Still investigating..
+ * Adv. if conn_rec * can be accepted is we can hook this function using the
+ * ap_hook_process_connection hook.
+ */
+
+/* Perform the SSL handshake (whether in client or server mode), if
+ * necessary, for the given connection. */
+static apr_status_t ssl_io_filter_handshake(ssl_filter_ctx_t *filter_ctx)
+{
+ conn_rec *c = (conn_rec *)SSL_get_app_data(filter_ctx->pssl);
+ SSLConnRec *sslconn = myConnConfig(c);
+ SSLSrvConfigRec *sc;
+ X509 *cert;
+ int n;
+ int ssl_err;
+ long verify_result;
+ server_rec *server;
+
+ if (SSL_is_init_finished(filter_ctx->pssl)) {
+ return APR_SUCCESS;
+ }
+
+ server = sslconn->server;
+ if (c->outgoing) {
+#ifdef HAVE_TLSEXT
+ apr_ipsubnet_t *ip;
+#ifdef HAVE_TLS_ALPN
+ const char *alpn_note;
+ apr_array_header_t *alpn_proposed = NULL;
+ int alpn_empty_ok = 1;
+#endif
+#endif
+ const char *hostname_note = apr_table_get(c->notes,
+ "proxy-request-hostname");
+ BOOL proxy_ssl_check_peer_ok = TRUE;
+ int post_handshake_rc = OK;
+ SSLDirConfigRec *dc;
+
+ dc = sslconn->dc;
+ sc = mySrvConfig(server);
+
+#ifdef HAVE_TLSEXT
+#ifdef HAVE_TLS_ALPN
+ alpn_note = apr_table_get(c->notes, "proxy-request-alpn-protos");
+ if (alpn_note) {
+ char *protos, *s, *p, *last, *proto;
+ apr_size_t len;
+
+ /* Transform the note into a protocol formatted byte array:
+ * (len-byte proto-char+)*
+ * We need the remote server to agree on one of these, unless 'http/1.1'
+ * is also among our proposals. Because pre-ALPN remotes will speak this.
+ */
+ alpn_proposed = apr_array_make(c->pool, 3, sizeof(const char*));
+ alpn_empty_ok = 0;
+ s = protos = apr_pcalloc(c->pool, strlen(alpn_note)+1);
+ p = apr_pstrdup(c->pool, alpn_note);
+ while ((p = apr_strtok(p, ", ", &last))) {
+ len = last - p - (*last? 1 : 0);
+ if (len > 255) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(03309)
+ "ALPN proxy protocol identifier too long: %s",
+ p);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, server);
+ return APR_EGENERAL;
+ }
+ proto = apr_pstrndup(c->pool, p, len);
+ APR_ARRAY_PUSH(alpn_proposed, const char*) = proto;
+ if (!strcmp("http/1.1", proto)) {
+ alpn_empty_ok = 1;
+ }
+ *s++ = (unsigned char)len;
+ while (len--) {
+ *s++ = *p++;
+ }
+ p = NULL;
+ }
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+ "setting alpn protos from '%s', protolen=%d",
+ alpn_note, (int)(s - protos));
+ if (protos != s && SSL_set_alpn_protos(filter_ctx->pssl,
+ (unsigned char *)protos,
+ s - protos)) {
+ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(03310)
+ "error setting alpn protos from '%s'", alpn_note);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_WARNING, server);
+ /* If ALPN was requested and we cannot do it, we must fail */
+ return MODSSL_ERROR_BAD_GATEWAY;
+ }
+ }
+#endif /* defined HAVE_TLS_ALPN */
+ /*
+ * Enable SNI for backend requests. Make sure we don't do it for
+ * pure SSLv3 connections, and also prevent IP addresses
+ * from being included in the SNI extension. (OpenSSL would simply
+ * pass them on, but RFC 6066 is quite clear on this: "Literal
+ * IPv4 and IPv6 addresses are not permitted".)
+ */
+ if (hostname_note &&
+#ifndef OPENSSL_NO_SSL3
+ dc->proxy->protocol != SSL_PROTOCOL_SSLV3 &&
+#endif
+ apr_ipsubnet_create(&ip, hostname_note, NULL,
+ c->pool) != APR_SUCCESS) {
+ if (SSL_set_tlsext_host_name(filter_ctx->pssl, hostname_note)) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c,
+ "SNI extension for SSL Proxy request set to '%s'",
+ hostname_note);
+ } else {
+ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(02002)
+ "Failed to set SNI extension for SSL Proxy "
+ "request to '%s'", hostname_note);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_WARNING, server);
+ }
+ }
+#endif /* defined HAVE_TLSEXT */
+
+ if ((n = SSL_connect(filter_ctx->pssl)) <= 0) {
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02003)
+ "SSL Proxy connect failed");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_INFO, server);
+ /* ensure that the SSL structures etc are freed, etc: */
+ ssl_filter_io_shutdown(filter_ctx, c, 1);
+ apr_table_setn(c->notes, "SSL_connect_rv", "err");
+ return MODSSL_ERROR_BAD_GATEWAY;
+ }
+
+ cert = SSL_get_peer_certificate(filter_ctx->pssl);
+
+ if (dc->proxy->ssl_check_peer_expire != FALSE) {
+ if (!cert
+ || (X509_cmp_current_time(
+ X509_get_notBefore(cert)) >= 0)
+ || (X509_cmp_current_time(
+ X509_get_notAfter(cert)) <= 0)) {
+ proxy_ssl_check_peer_ok = FALSE;
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02004)
+ "SSL Proxy: Peer certificate is expired");
+ }
+ }
+ if ((dc->proxy->ssl_check_peer_name != FALSE) &&
+ ((dc->proxy->ssl_check_peer_cn != FALSE) ||
+ (dc->proxy->ssl_check_peer_name == TRUE)) &&
+ hostname_note) {
+ if (!cert
+ || modssl_X509_match_name(c->pool, cert, hostname_note,
+ TRUE, server) == FALSE) {
+ proxy_ssl_check_peer_ok = FALSE;
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02411)
+ "SSL Proxy: Peer certificate does not match "
+ "for hostname %s", hostname_note);
+ }
+ }
+ else if ((dc->proxy->ssl_check_peer_cn == TRUE) &&
+ hostname_note) {
+ const char *hostname;
+ int match = 0;
+
+ hostname = ssl_var_lookup(NULL, server, c, NULL,
+ "SSL_CLIENT_S_DN_CN");
+
+ /* Do string match or simplest wildcard match if that
+ * fails. */
+ match = strcasecmp(hostname, hostname_note) == 0;
+ if (!match && strncmp(hostname, "*.", 2) == 0) {
+ const char *p = ap_strchr_c(hostname_note, '.');
+
+ match = p && strcasecmp(p, hostname + 1) == 0;
+ }
+
+ if (!match) {
+ proxy_ssl_check_peer_ok = FALSE;
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02005)
+ "SSL Proxy: Peer certificate CN mismatch:"
+ " Certificate CN: %s Requested hostname: %s",
+ hostname, hostname_note);
+ }
+ }
+
+#ifdef HAVE_TLS_ALPN
+ /* If we proposed ALPN protocol(s), we need to check if the server
+ * agreed to one of them. While <https://www.rfc-editor.org/rfc/rfc7301.txt>
+ * chapter 3.2 says the server SHALL error the handshake in such a case,
+ * the reality is that some servers fall back to their default, e.g. http/1.1.
+ * (we also do this right now)
+ * We need to treat this as an error for security reasons.
+ */
+ if (alpn_proposed && alpn_proposed->nelts > 0) {
+ const char *selected;
+ unsigned int slen;
+
+ SSL_get0_alpn_selected(filter_ctx->pssl, (const unsigned char**)&selected, &slen);
+ if (!selected || !slen) {
+ /* No ALPN selection reported by the remote server. This could mean
+ * it does not support ALPN (old server) or that it does not support
+ * any of our proposals (Apache itself up to 2.4.48 at least did that). */
+ if (!alpn_empty_ok) {
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(10273)
+ "SSL Proxy: Peer did not select any of our ALPN protocols [%s].",
+ alpn_note);
+ proxy_ssl_check_peer_ok = FALSE;
+ }
+ }
+ else {
+ const char *proto;
+ int i, found = 0;
+ for (i = 0; !found && i < alpn_proposed->nelts; ++i) {
+ proto = APR_ARRAY_IDX(alpn_proposed, i, const char *);
+ found = !strncmp(selected, proto, slen);
+ }
+ if (!found) {
+ /* From a conforming peer, this should never happen,
+ * but life always finds a way... */
+ proto = apr_pstrndup(c->pool, selected, slen);
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(10274)
+ "SSL Proxy: Peer proposed ALPN protocol %s which is none "
+ "of our proposals [%s].", proto, alpn_note);
+ proxy_ssl_check_peer_ok = FALSE;
+ }
+ }
+ }
+#endif
+
+ if (proxy_ssl_check_peer_ok == TRUE) {
+ /* another chance to fail */
+ post_handshake_rc = ssl_run_proxy_post_handshake(c, filter_ctx->pssl);
+ }
+
+ if (cert) {
+ X509_free(cert);
+ }
+
+ if (proxy_ssl_check_peer_ok != TRUE
+ || (post_handshake_rc != OK && post_handshake_rc != DECLINED)) {
+ /* ensure that the SSL structures etc are freed, etc: */
+ ssl_filter_io_shutdown(filter_ctx, c, 1);
+ apr_table_setn(c->notes, "SSL_connect_rv", "err");
+ return MODSSL_ERROR_BAD_GATEWAY;
+ }
+
+ apr_table_setn(c->notes, "SSL_connect_rv", "ok");
+ return APR_SUCCESS;
+ }
+
+ /* We rely on SSL_get_error() after the accept, which requires an empty
+ * error queue before the accept in order to work properly.
+ */
+ ERR_clear_error();
+
+ if ((n = SSL_accept(filter_ctx->pssl)) <= 0) {
+ bio_filter_in_ctx_t *inctx = (bio_filter_in_ctx_t *)
+ BIO_get_data(filter_ctx->pbioRead);
+ bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)
+ BIO_get_data(filter_ctx->pbioWrite);
+ apr_status_t rc = inctx->rc ? inctx->rc : outctx->rc ;
+ ssl_err = SSL_get_error(filter_ctx->pssl, n);
+
+ if (ssl_err == SSL_ERROR_ZERO_RETURN) {
+ /*
+ * The case where the connection was closed before any data
+ * was transferred. That's not a real error and can occur
+ * sporadically with some clients.
+ */
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, rc, c, APLOGNO(02006)
+ "SSL handshake stopped: connection was closed");
+ }
+ else if (ssl_err == SSL_ERROR_WANT_READ) {
+ /*
+ * This is in addition to what was present earlier. It is
+ * borrowed from openssl_state_machine.c [mod_tls].
+ * TBD.
+ */
+ outctx->rc = APR_EAGAIN;
+ return APR_EAGAIN;
+ }
+ else if (ERR_GET_LIB(ERR_peek_error()) == ERR_LIB_SSL &&
+ ERR_GET_REASON(ERR_peek_error()) == SSL_R_HTTP_REQUEST) {
+ /*
+ * The case where OpenSSL has recognized a HTTP request:
+ * This means the client speaks plain HTTP on our HTTPS port.
+ * ssl_io_filter_error will disable the ssl filters when it
+ * sees this status code.
+ */
+ return MODSSL_ERROR_HTTP_ON_HTTPS;
+ }
+ else if (ssl_err == SSL_ERROR_SYSCALL) {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rc, c, APLOGNO(02007)
+ "SSL handshake interrupted by system "
+ "[Hint: Stop button pressed in browser?!]");
+ }
+ else /* if (ssl_err == SSL_ERROR_SSL) */ {
+ /*
+ * Log SSL errors and any unexpected conditions.
+ */
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, rc, c, APLOGNO(02008)
+ "SSL library error %d in handshake "
+ "(server %s)", ssl_err,
+ ssl_util_vhostid(c->pool, server));
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_INFO, server);
+
+ }
+ if (inctx->rc == APR_SUCCESS) {
+ inctx->rc = APR_EGENERAL;
+ }
+
+ ssl_filter_io_shutdown(filter_ctx, c, 1);
+ return inctx->rc;
+ }
+ sc = mySrvConfig(sslconn->server);
+
+ /*
+ * Check for failed client authentication
+ */
+ verify_result = SSL_get_verify_result(filter_ctx->pssl);
+
+ if ((verify_result != X509_V_OK) ||
+ sslconn->verify_error)
+ {
+ if (ssl_verify_error_is_optional(verify_result) &&
+ (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA))
+ {
+ /* leaving this log message as an error for the moment,
+ * according to the mod_ssl docs:
+ * "level optional_no_ca is actually against the idea
+ * of authentication (but can be used to establish
+ * SSL test pages, etc.)"
+ * optional_no_ca doesn't appear to work as advertised
+ * in 1.x
+ */
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02009)
+ "SSL client authentication failed, "
+ "accepting certificate based on "
+ "\"SSLVerifyClient optional_no_ca\" "
+ "configuration");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_INFO, server);
+ }
+ else {
+ const char *error = sslconn->verify_error ?
+ sslconn->verify_error :
+ X509_verify_cert_error_string(verify_result);
+
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02010)
+ "SSL client authentication failed: %s",
+ error ? error : "unknown");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_INFO, server);
+
+ ssl_filter_io_shutdown(filter_ctx, c, 1);
+ return APR_ECONNABORTED;
+ }
+ }
+
+ /*
+ * Remember the peer certificate's DN
+ */
+ if ((cert = SSL_get_peer_certificate(filter_ctx->pssl))) {
+ if (sslconn->client_cert) {
+ X509_free(sslconn->client_cert);
+ }
+ sslconn->client_cert = cert;
+ sslconn->client_dn = NULL;
+ }
+
+ /*
+ * Make really sure that when a peer certificate
+ * is required we really got one... (be paranoid)
+ */
+ if ((sc->server->auth.verify_mode == SSL_CVERIFY_REQUIRE) &&
+ !sslconn->client_cert)
+ {
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02011)
+ "No acceptable peer certificate available");
+
+ ssl_filter_io_shutdown(filter_ctx, c, 1);
+ return APR_ECONNABORTED;
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t ssl_io_filter_input(ap_filter_t *f,
+ apr_bucket_brigade *bb,
+ ap_input_mode_t mode,
+ apr_read_type_e block,
+ apr_off_t readbytes)
+{
+ apr_status_t status;
+ bio_filter_in_ctx_t *inctx = f->ctx;
+ const char *start = inctx->buffer; /* start of block to return */
+ apr_size_t len = sizeof(inctx->buffer); /* length of block to return */
+ int is_init = (mode == AP_MODE_INIT);
+ apr_bucket *bucket;
+
+ if (f->c->aborted) {
+ /* XXX: Ok, if we aborted, we ARE at the EOS. We also have
+ * aborted. This 'double protection' is probably redundant,
+ * but also effective against just about anything.
+ */
+ bucket = apr_bucket_eos_create(f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, bucket);
+ return APR_ECONNABORTED;
+ }
+
+ if (!inctx->ssl) {
+ SSLConnRec *sslconn = myConnConfig(f->c);
+ if (sslconn->non_ssl_request == NON_SSL_SEND_REQLINE) {
+ bucket = HTTP_ON_HTTPS_PORT_BUCKET(f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, bucket);
+ if (mode != AP_MODE_SPECULATIVE) {
+ sslconn->non_ssl_request = NON_SSL_SEND_HDR_SEP;
+ }
+ return APR_SUCCESS;
+ }
+ if (sslconn->non_ssl_request == NON_SSL_SEND_HDR_SEP) {
+ bucket = apr_bucket_immortal_create(CRLF, 2, f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, bucket);
+ if (mode != AP_MODE_SPECULATIVE) {
+ sslconn->non_ssl_request = NON_SSL_SET_ERROR_MSG;
+ }
+ return APR_SUCCESS;
+ }
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ }
+
+ /* XXX: we don't currently support anything other than these modes. */
+ if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE &&
+ mode != AP_MODE_SPECULATIVE && mode != AP_MODE_INIT) {
+ return APR_ENOTIMPL;
+ }
+
+ inctx->mode = mode;
+ inctx->block = block;
+
+ /* XXX: we could actually move ssl_io_filter_handshake to an
+ * ap_hook_process_connection but would still need to call it for
+ * AP_MODE_INIT for protocols that may upgrade the connection
+ * rather than have SSLEngine On configured.
+ */
+ if ((status = ssl_io_filter_handshake(inctx->filter_ctx)) != APR_SUCCESS) {
+ return ssl_io_filter_error(inctx, bb, status, is_init);
+ }
+
+ if (is_init) {
+ /* protocol module needs to handshake before sending
+ * data to client (e.g. NNTP or FTP)
+ */
+ return APR_SUCCESS;
+ }
+
+ if (inctx->mode == AP_MODE_READBYTES ||
+ inctx->mode == AP_MODE_SPECULATIVE) {
+ /* Protected from truncation, readbytes < MAX_SIZE_T
+ * FIXME: No, it's *not* protected. -- jre */
+ if (readbytes < len) {
+ len = (apr_size_t)readbytes;
+ }
+ status = ssl_io_input_read(inctx, inctx->buffer, &len);
+ }
+ else if (inctx->mode == AP_MODE_GETLINE) {
+ const char *pos;
+
+ /* Satisfy the read directly out of the buffer if possible;
+ * invoking ssl_io_input_getline will mean the entire buffer
+ * is copied once (unnecessarily) for each GETLINE call. */
+ if (inctx->cbuf.length
+ && (pos = memchr(inctx->cbuf.value, APR_ASCII_LF,
+ inctx->cbuf.length)) != NULL) {
+ start = inctx->cbuf.value;
+ len = 1 + pos - start; /* +1 to include LF */
+ /* Buffer contents now consumed. */
+ inctx->cbuf.value += len;
+ inctx->cbuf.length -= len;
+ status = APR_SUCCESS;
+ }
+ else {
+ /* Otherwise fall back to the hard way. */
+ status = ssl_io_input_getline(inctx, inctx->buffer, &len);
+ }
+ }
+ else {
+ /* We have no idea what you are talking about, so return an error. */
+ status = APR_ENOTIMPL;
+ }
+
+ /* It is possible for mod_ssl's BIO to be used outside of the
+ * direct control of mod_ssl's input or output filter -- notably,
+ * when mod_ssl initiates a renegotiation. Switching the BIO mode
+ * back to "blocking" here ensures such operations don't fail with
+ * SSL_ERROR_WANT_READ. */
+ inctx->block = APR_BLOCK_READ;
+
+ /* Handle custom errors. */
+ if (status != APR_SUCCESS) {
+ return ssl_io_filter_error(inctx, bb, status, 0);
+ }
+
+ /* Create a transient bucket out of the decrypted data. */
+ if (len > 0) {
+ bucket =
+ apr_bucket_transient_create(start, len, f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, bucket);
+ }
+
+ return APR_SUCCESS;
+}
+
+
+/* ssl_io_filter_output() produces one SSL/TLS record per bucket
+ * passed down the output filter stack. This results in a high
+ * overhead (more network packets & TLS processing) for any output
+ * comprising many small buckets. SSI output passed through the HTTP
+ * chunk filter, for example, may produce many brigades containing
+ * small buckets - [chunk-size CRLF] [chunk-data] [CRLF].
+ *
+ * Sending HTTP response headers as a separate TLS record to the
+ * response body also reveals information to a network observer (the
+ * size of headers) which can be significant.
+ *
+ * The coalescing filter merges data buckets with the aim of producing
+ * fewer, larger TLS records - without copying/buffering all content
+ * and introducing unnecessary overhead.
+ *
+ * ### This buffering could be probably be done more comprehensively
+ * ### in ssl_io_filter_output itself.
+ *
+ * ### Another possible performance optimisation in particular for the
+ * ### [HEAP] [FILE] HTTP response case is using a brigade rather than
+ * ### a char array to buffer; using apr_brigade_write() to append
+ * ### will use already-allocated memory from the HEAP, reducing # of
+ * ### copies.
+ */
+
+#define COALESCE_BYTES (AP_IOBUFSIZE)
+
+struct coalesce_ctx {
+ char buffer[COALESCE_BYTES];
+ apr_size_t bytes; /* number of bytes of buffer used. */
+};
+
+static apr_status_t ssl_io_filter_coalesce(ap_filter_t *f,
+ apr_bucket_brigade *bb)
+{
+ apr_bucket *e, *upto;
+ apr_size_t bytes = 0;
+ struct coalesce_ctx *ctx = f->ctx;
+ apr_size_t buffered = ctx ? ctx->bytes : 0; /* space used on entry */
+ unsigned count = 0;
+
+ /* The brigade consists of zero-or-more small data buckets which
+ * can be coalesced (referred to as the "prefix"), followed by the
+ * remainder of the brigade.
+ *
+ * Find the last bucket - if any - of that prefix. count gives
+ * the number of buckets in the prefix. The "prefix" must contain
+ * only data buckets with known length, and must be of a total
+ * size which fits into the buffer.
+ *
+ * N.B.: The process here could be repeated throughout the brigade
+ * (coalesce any run of consecutive data buckets) but this would
+ * add significant complexity, particularly to memory
+ * management. */
+ for (e = APR_BRIGADE_FIRST(bb);
+ e != APR_BRIGADE_SENTINEL(bb)
+ && !APR_BUCKET_IS_METADATA(e)
+ && e->length != (apr_size_t)-1
+ && e->length <= COALESCE_BYTES
+ && (buffered + bytes + e->length) <= COALESCE_BYTES;
+ e = APR_BUCKET_NEXT(e)) {
+ /* don't count zero-length buckets */
+ if (e->length) {
+ bytes += e->length;
+ count++;
+ }
+ }
+
+ /* If there is room remaining and the next bucket is a data
+ * bucket, try to include it in the prefix to coalesce. For a
+ * typical [HEAP] [FILE] HTTP response brigade, this handles
+ * merging the headers and the start of the body into a single TLS
+ * record. */
+ if (bytes + buffered > 0
+ && bytes + buffered < COALESCE_BYTES
+ && e != APR_BRIGADE_SENTINEL(bb)
+ && !APR_BUCKET_IS_METADATA(e)) {
+ apr_status_t rv = APR_SUCCESS;
+
+ /* For an indeterminate length bucket (PIPE/CGI/...), try a
+ * non-blocking read to have it morph into a HEAP. If the
+ * read fails with EAGAIN, it is harmless to try a split
+ * anyway, split is ENOTIMPL for most PIPE-like buckets. */
+ if (e->length == (apr_size_t)-1) {
+ const char *discard;
+ apr_size_t ignore;
+
+ rv = apr_bucket_read(e, &discard, &ignore, APR_NONBLOCK_READ);
+ if (rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv)) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, f->c, APLOGNO(10232)
+ "coalesce failed to read from %s bucket",
+ e->type->name);
+ return AP_FILTER_ERROR;
+ }
+ }
+
+ if (rv == APR_SUCCESS) {
+ /* If the read above made the bucket morph, it may now fit
+ * entirely within the buffer. Otherwise, split it so it does
+ * fit. */
+ if (e->length > COALESCE_BYTES
+ || e->length + buffered + bytes > COALESCE_BYTES) {
+ rv = apr_bucket_split(e, COALESCE_BYTES - (buffered + bytes));
+ }
+
+ if (rv == APR_SUCCESS && e->length == 0) {
+ /* As above, don't count in the prefix if the bucket is
+ * now zero-length. */
+ }
+ else if (rv == APR_SUCCESS) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, f->c,
+ "coalesce: adding %" APR_SIZE_T_FMT " bytes "
+ "from split %s bucket, total %" APR_SIZE_T_FMT,
+ e->length, e->type->name, bytes + buffered);
+
+ count++;
+ bytes += e->length;
+ e = APR_BUCKET_NEXT(e);
+ }
+ else if (rv != APR_ENOTIMPL) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, f->c, APLOGNO(10233)
+ "coalesce: failed to split data bucket");
+ return AP_FILTER_ERROR;
+ }
+ }
+ }
+
+ /* The prefix is zero or more buckets. upto now points to the
+ * bucket AFTER the end of the prefix, which may be the brigade
+ * sentinel. */
+ upto = e;
+
+ /* Coalesce the prefix, if any of the following are true:
+ *
+ * a) the prefix is more than one bucket
+ * OR
+ * b) the prefix is the entire brigade, which is a single bucket
+ * AND the prefix length is smaller than the buffer size,
+ * OR
+ * c) the prefix is a single bucket
+ * AND there is buffered data from a previous pass.
+ *
+ * The aim with (b) is to buffer a small bucket so it can be
+ * coalesced with future invocations of this filter. e.g. three
+ * calls each with a single 100 byte HEAP bucket should get
+ * coalesced together. But an invocation with a 8192 byte HEAP
+ * should pass through untouched.
+ */
+ if (bytes > 0
+ && (count > 1
+ || (upto == APR_BRIGADE_SENTINEL(bb)
+ && bytes < COALESCE_BYTES)
+ || (ctx && ctx->bytes > 0))) {
+ /* If coalescing some bytes, ensure a context has been
+ * created. */
+ if (!ctx) {
+ f->ctx = ctx = apr_palloc(f->c->pool, sizeof *ctx);
+ ctx->bytes = 0;
+ }
+
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, f->c,
+ "coalesce: have %" APR_SIZE_T_FMT " bytes, "
+ "adding %" APR_SIZE_T_FMT " more (buckets=%u)",
+ ctx->bytes, bytes, count);
+
+ /* Iterate through the prefix segment. For non-fatal errors
+ * in this loop it is safe to break out and fall back to the
+ * normal path of sending the buffer + remaining buckets in
+ * brigade. */
+ e = APR_BRIGADE_FIRST(bb);
+ while (e != upto) {
+ apr_size_t len;
+ const char *data;
+ apr_bucket *next;
+
+ if (APR_BUCKET_IS_METADATA(e)
+ || e->length == (apr_size_t)-1) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, APLOGNO(02012)
+ "unexpected %s bucket during coalesce",
+ e->type->name);
+ break; /* non-fatal error; break out */
+ }
+
+ if (e->length) {
+ apr_status_t rv;
+
+ /* A blocking read should be fine here for a
+ * known-length data bucket, rather than the usual
+ * non-block/flush/block. */
+ rv = apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
+ if (rv) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, f->c, APLOGNO(02013)
+ "coalesce failed to read from data bucket");
+ return AP_FILTER_ERROR;
+ }
+
+ /* Be paranoid. */
+ if (len > sizeof ctx->buffer
+ || (len + ctx->bytes > sizeof ctx->buffer)) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, APLOGNO(02014)
+ "unexpected coalesced bucket data length");
+ break; /* non-fatal error; break out */
+ }
+
+ memcpy(ctx->buffer + ctx->bytes, data, len);
+ ctx->bytes += len;
+ }
+
+ next = APR_BUCKET_NEXT(e);
+ apr_bucket_delete(e);
+ e = next;
+ }
+ }
+
+ if (APR_BRIGADE_EMPTY(bb)) {
+ /* If the brigade is now empty, our work here is done. */
+ return APR_SUCCESS;
+ }
+
+ /* If anything remains in the brigade, it must now be passed down
+ * the filter stack, first prepending anything that has been
+ * coalesced. */
+ if (ctx && ctx->bytes) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, f->c,
+ "coalesce: passing on %" APR_SIZE_T_FMT " bytes", ctx->bytes);
+
+ e = apr_bucket_transient_create(ctx->buffer, ctx->bytes, bb->bucket_alloc);
+ APR_BRIGADE_INSERT_HEAD(bb, e);
+ ctx->bytes = 0; /* buffer now emptied. */
+ }
+
+ return ap_pass_brigade(f->next, bb);
+}
+
+static apr_status_t ssl_io_filter_output(ap_filter_t *f,
+ apr_bucket_brigade *bb)
+{
+ apr_status_t status = APR_SUCCESS;
+ ssl_filter_ctx_t *filter_ctx = f->ctx;
+ bio_filter_in_ctx_t *inctx;
+ bio_filter_out_ctx_t *outctx;
+ apr_read_type_e rblock = APR_NONBLOCK_READ;
+
+ if (f->c->aborted) {
+ apr_brigade_cleanup(bb);
+ return APR_ECONNABORTED;
+ }
+
+ if (!filter_ctx->pssl) {
+ /* ssl_filter_io_shutdown was called */
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ inctx = (bio_filter_in_ctx_t *)BIO_get_data(filter_ctx->pbioRead);
+ outctx = (bio_filter_out_ctx_t *)BIO_get_data(filter_ctx->pbioWrite);
+
+ /* When we are the writer, we must initialize the inctx
+ * mode so that we block for any required ssl input, because
+ * output filtering is always nonblocking.
+ */
+ inctx->mode = AP_MODE_READBYTES;
+ inctx->block = APR_BLOCK_READ;
+
+ if ((status = ssl_io_filter_handshake(filter_ctx)) != APR_SUCCESS) {
+ return ssl_io_filter_error(inctx, bb, status, 0);
+ }
+
+ while (!APR_BRIGADE_EMPTY(bb) && status == APR_SUCCESS) {
+ apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
+
+ if (APR_BUCKET_IS_METADATA(bucket)) {
+ /* Pass through metadata buckets untouched. EOC is
+ * special; terminate the SSL layer first. */
+ if (AP_BUCKET_IS_EOC(bucket)) {
+ ssl_filter_io_shutdown(filter_ctx, f->c, 0);
+ }
+ AP_DEBUG_ASSERT(APR_BRIGADE_EMPTY(outctx->bb));
+
+ /* Metadata buckets are passed one per brigade; it might
+ * be more efficient (but also more complex) to use
+ * outctx->bb as a true buffer and interleave these with
+ * data buckets. */
+ APR_BUCKET_REMOVE(bucket);
+ APR_BRIGADE_INSERT_HEAD(outctx->bb, bucket);
+ status = ap_pass_brigade(f->next, outctx->bb);
+ if (status == APR_SUCCESS && f->c->aborted)
+ status = APR_ECONNRESET;
+ apr_brigade_cleanup(outctx->bb);
+ }
+ else {
+ /* Filter a data bucket. */
+ const char *data;
+ apr_size_t len;
+
+ status = apr_bucket_read(bucket, &data, &len, rblock);
+
+ if (APR_STATUS_IS_EAGAIN(status)) {
+ /* No data available: flush... */
+ if (bio_filter_out_flush(filter_ctx->pbioWrite) < 0) {
+ status = outctx->rc;
+ break;
+ }
+ rblock = APR_BLOCK_READ;
+ /* and try again with a blocking read. */
+ status = APR_SUCCESS;
+ continue;
+ }
+
+ rblock = APR_NONBLOCK_READ;
+
+ if (!APR_STATUS_IS_EOF(status) && (status != APR_SUCCESS)) {
+ break;
+ }
+
+ status = ssl_filter_write(f, data, len);
+ apr_bucket_delete(bucket);
+ }
+
+ }
+
+ return status;
+}
+
+struct modssl_buffer_ctx {
+ apr_bucket_brigade *bb;
+};
+
+int ssl_io_buffer_fill(request_rec *r, apr_size_t maxlen)
+{
+ conn_rec *c = r->connection;
+ struct modssl_buffer_ctx *ctx;
+ apr_bucket_brigade *tempb;
+ apr_off_t total = 0; /* total length buffered */
+ int eos = 0; /* non-zero once EOS is seen */
+
+ /* Create the context which will be passed to the input filter;
+ * containing a setaside pool and a brigade which constrain the
+ * lifetime of the buffered data. */
+ ctx = apr_palloc(r->pool, sizeof *ctx);
+ ctx->bb = apr_brigade_create(r->pool, c->bucket_alloc);
+
+ /* ... and a temporary brigade. */
+ tempb = apr_brigade_create(r->pool, c->bucket_alloc);
+
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, c, "filling buffer, max size "
+ "%" APR_SIZE_T_FMT " bytes", maxlen);
+
+ do {
+ apr_status_t rv;
+ apr_bucket *e, *next;
+
+ /* The request body is read from the protocol-level input
+ * filters; the buffering filter will reinject it from that
+ * level, allowing content/resource filters to run later, if
+ * necessary. */
+
+ rv = ap_get_brigade(r->proto_input_filters, tempb, AP_MODE_READBYTES,
+ APR_BLOCK_READ, 8192);
+ if (rv) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02015)
+ "could not read request body for SSL buffer");
+ return ap_map_http_request_error(rv, HTTP_INTERNAL_SERVER_ERROR);
+ }
+
+ /* Iterate through the returned brigade: setaside each bucket
+ * into the context's pool and move it into the brigade. */
+ for (e = APR_BRIGADE_FIRST(tempb);
+ e != APR_BRIGADE_SENTINEL(tempb) && !eos; e = next) {
+ const char *data;
+ apr_size_t len;
+
+ next = APR_BUCKET_NEXT(e);
+
+ if (APR_BUCKET_IS_EOS(e)) {
+ eos = 1;
+ } else if (!APR_BUCKET_IS_METADATA(e)) {
+ rv = apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
+ if (rv != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02016)
+ "could not read bucket for SSL buffer");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ total += len;
+ }
+
+ rv = apr_bucket_setaside(e, r->pool);
+ if (rv != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02017)
+ "could not setaside bucket for SSL buffer");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+ }
+
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, c,
+ "total of %" APR_OFF_T_FMT " bytes in buffer, eos=%d",
+ total, eos);
+
+ /* Fail if this exceeds the maximum buffer size. */
+ if (total > maxlen) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02018)
+ "request body exceeds maximum size (%" APR_SIZE_T_FMT
+ ") for SSL buffer", maxlen);
+ return HTTP_REQUEST_ENTITY_TOO_LARGE;
+ }
+
+ } while (!eos);
+
+ apr_brigade_destroy(tempb);
+
+ /* After consuming all protocol-level input, remove all protocol-level
+ * filters. It should strictly only be necessary to remove filters
+ * at exactly ftype == AP_FTYPE_PROTOCOL, since this filter will
+ * precede all > AP_FTYPE_PROTOCOL anyway. */
+ while (r->proto_input_filters->frec->ftype < AP_FTYPE_CONNECTION) {
+ ap_remove_input_filter(r->proto_input_filters);
+ }
+
+ /* Insert the filter which will supply the buffered content. */
+ ap_add_input_filter(ssl_io_buffer, ctx, r, c);
+
+ return 0;
+}
+
+/* This input filter supplies the buffered request body to the caller
+ * from the brigade stored in f->ctx. Note that the placement of this
+ * filter in the filter stack is important; it must be the first
+ * r->proto_input_filter; lower-typed filters will not be preserved
+ * across internal redirects (see PR 43738). */
+static apr_status_t ssl_io_filter_buffer(ap_filter_t *f,
+ apr_bucket_brigade *bb,
+ ap_input_mode_t mode,
+ apr_read_type_e block,
+ apr_off_t bytes)
+{
+ struct modssl_buffer_ctx *ctx = f->ctx;
+ apr_status_t rv;
+ apr_bucket *e, *d;
+
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, f->c,
+ "read from buffered SSL brigade, mode %d, "
+ "%" APR_OFF_T_FMT " bytes",
+ mode, bytes);
+
+ if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE) {
+ return APR_ENOTIMPL;
+ }
+
+ if (APR_BRIGADE_EMPTY(ctx->bb)) {
+ /* Surprisingly (and perhaps, wrongly), the request body can be
+ * pulled from the input filter stack more than once; a
+ * handler may read it, and ap_discard_request_body() will
+ * attempt to do so again after *every* request. So input
+ * filters must be prepared to give up an EOS if invoked after
+ * initially reading the request. The HTTP_IN filter does this
+ * with its ->eos_sent flag. */
+
+ APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_eos_create(f->c->bucket_alloc));
+ return APR_SUCCESS;
+ }
+
+ if (mode == AP_MODE_READBYTES) {
+ /* Partition the buffered brigade. */
+ rv = apr_brigade_partition(ctx->bb, bytes, &e);
+ if (rv && rv != APR_INCOMPLETE) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, f->c, APLOGNO(02019)
+ "could not partition buffered SSL brigade");
+ ap_remove_input_filter(f);
+ return rv;
+ }
+
+ /* If the buffered brigade contains less then the requested
+ * length, just pass it all back. */
+ if (rv == APR_INCOMPLETE) {
+ APR_BRIGADE_CONCAT(bb, ctx->bb);
+ } else {
+ d = APR_BRIGADE_FIRST(ctx->bb);
+
+ e = APR_BUCKET_PREV(e);
+
+ /* Unsplice the partitioned segment and move it into the
+ * passed-in brigade; no convenient way to do this with
+ * the APR_BRIGADE_* macros. */
+ APR_RING_UNSPLICE(d, e, link);
+ APR_RING_SPLICE_HEAD(&bb->list, d, e, apr_bucket, link);
+
+ APR_BRIGADE_CHECK_CONSISTENCY(bb);
+ APR_BRIGADE_CHECK_CONSISTENCY(ctx->bb);
+ }
+ }
+ else {
+ /* Split a line into the passed-in brigade. */
+ rv = apr_brigade_split_line(bb, ctx->bb, block, bytes);
+
+ if (rv) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, f->c, APLOGNO(02020)
+ "could not split line from buffered SSL brigade");
+ ap_remove_input_filter(f);
+ return rv;
+ }
+ }
+
+ if (APR_BRIGADE_EMPTY(ctx->bb)) {
+ e = APR_BRIGADE_LAST(bb);
+
+ /* Ensure that the brigade is terminated by an EOS if the
+ * buffered request body has been entirely consumed. */
+ if (e == APR_BRIGADE_SENTINEL(bb) || !APR_BUCKET_IS_EOS(e)) {
+ e = apr_bucket_eos_create(f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, e);
+ }
+
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, f->c,
+ "buffered SSL brigade exhausted");
+ /* Note that the filter must *not* be removed here; it may be
+ * invoked again, see comment above. */
+ }
+
+ return APR_SUCCESS;
+}
+
+/* The request_rec pointer is passed in here only to ensure that the
+ * filter chain is modified correctly when doing a TLS upgrade. It
+ * must *not* be used otherwise. */
+static void ssl_io_input_add_filter(ssl_filter_ctx_t *filter_ctx, conn_rec *c,
+ request_rec *r, SSL *ssl)
+{
+ bio_filter_in_ctx_t *inctx;
+
+ inctx = apr_palloc(c->pool, sizeof(*inctx));
+
+ filter_ctx->pInputFilter = ap_add_input_filter(ssl_io_filter, inctx, r, c);
+
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
+ filter_ctx->pbioRead = BIO_new(&bio_filter_in_method);
+#else
+ filter_ctx->pbioRead = BIO_new(bio_filter_in_method);
+#endif
+ BIO_set_data(filter_ctx->pbioRead, (void *)inctx);
+
+ inctx->ssl = ssl;
+ inctx->bio_out = filter_ctx->pbioWrite;
+ inctx->f = filter_ctx->pInputFilter;
+ inctx->rc = APR_SUCCESS;
+ inctx->mode = AP_MODE_READBYTES;
+ inctx->cbuf.length = 0;
+ inctx->bb = apr_brigade_create(c->pool, c->bucket_alloc);
+ inctx->block = APR_BLOCK_READ;
+ inctx->pool = c->pool;
+ inctx->filter_ctx = filter_ctx;
+}
+
+/* The request_rec pointer is passed in here only to ensure that the
+ * filter chain is modified correctly when doing a TLS upgrade. It
+ * must *not* be used otherwise. */
+void ssl_io_filter_init(conn_rec *c, request_rec *r, SSL *ssl)
+{
+ ssl_filter_ctx_t *filter_ctx;
+
+ filter_ctx = apr_palloc(c->pool, sizeof(ssl_filter_ctx_t));
+
+ filter_ctx->config = myConnConfig(c);
+
+ ap_add_output_filter(ssl_io_coalesce, NULL, r, c);
+
+ filter_ctx->pOutputFilter = ap_add_output_filter(ssl_io_filter,
+ filter_ctx, r, c);
+
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
+ filter_ctx->pbioWrite = BIO_new(&bio_filter_out_method);
+#else
+ filter_ctx->pbioWrite = BIO_new(bio_filter_out_method);
+#endif
+ BIO_set_data(filter_ctx->pbioWrite, (void *)bio_filter_out_ctx_new(filter_ctx, c));
+
+ /* write is non blocking for the benefit of async mpm */
+ if (c->cs) {
+ BIO_set_nbio(filter_ctx->pbioWrite, 1);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, c,
+ "Enabling non-blocking writes");
+ }
+
+ ssl_io_input_add_filter(filter_ctx, c, r, ssl);
+
+ SSL_set_bio(ssl, filter_ctx->pbioRead, filter_ctx->pbioWrite);
+ filter_ctx->pssl = ssl;
+
+ apr_pool_cleanup_register(c->pool, (void*)filter_ctx,
+ ssl_io_filter_cleanup, apr_pool_cleanup_null);
+
+ if (APLOG_CS_IS_LEVEL(c, mySrvFromConn(c), APLOG_TRACE4)) {
+ BIO *rbio = SSL_get_rbio(ssl),
+ *wbio = SSL_get_wbio(ssl);
+ BIO_set_callback(rbio, ssl_io_data_cb);
+ BIO_set_callback_arg(rbio, (void *)ssl);
+ if (wbio && wbio != rbio) {
+ BIO_set_callback(wbio, ssl_io_data_cb);
+ BIO_set_callback_arg(wbio, (void *)ssl);
+ }
+ }
+
+ return;
+}
+
+void ssl_io_filter_register(apr_pool_t *p)
+{
+ ap_register_input_filter (ssl_io_filter, ssl_io_filter_input, NULL, AP_FTYPE_CONNECTION + 5);
+ ap_register_output_filter (ssl_io_coalesce, ssl_io_filter_coalesce, NULL, AP_FTYPE_CONNECTION + 4);
+ ap_register_output_filter (ssl_io_filter, ssl_io_filter_output, NULL, AP_FTYPE_CONNECTION + 5);
+
+ ap_register_input_filter (ssl_io_buffer, ssl_io_filter_buffer, NULL, AP_FTYPE_PROTOCOL);
+
+ return;
+}
+
+/* _________________________________________________________________
+**
+** I/O Data Debugging
+** _________________________________________________________________
+*/
+
+#define DUMP_WIDTH 16
+
+static void ssl_io_data_dump(conn_rec *c, server_rec *s,
+ const char *b, long len)
+{
+ char buf[256];
+ int i, j, rows, trunc, pos;
+ unsigned char ch;
+
+ trunc = 0;
+ for (; (len > 0) && ((b[len-1] == ' ') || (b[len-1] == '\0')); len--)
+ trunc++;
+ rows = (len / DUMP_WIDTH);
+ if ((rows * DUMP_WIDTH) < len)
+ rows++;
+ ap_log_cserror(APLOG_MARK, APLOG_TRACE7, 0, c, s,
+ "+-------------------------------------------------------------------------+");
+ for (i = 0 ; i < rows; i++) {
+#if APR_CHARSET_EBCDIC
+ char ebcdic_text[DUMP_WIDTH];
+ j = DUMP_WIDTH;
+ if ((i * DUMP_WIDTH + j) > len)
+ j = len % DUMP_WIDTH;
+ if (j == 0)
+ j = DUMP_WIDTH;
+ memcpy(ebcdic_text,(char *)(b) + i * DUMP_WIDTH, j);
+ ap_xlate_proto_from_ascii(ebcdic_text, j);
+#endif /* APR_CHARSET_EBCDIC */
+ pos = 0;
+ pos += apr_snprintf(buf, sizeof(buf)-pos, "| %04x: ", i * DUMP_WIDTH);
+ for (j = 0; j < DUMP_WIDTH; j++) {
+ if (((i * DUMP_WIDTH) + j) >= len)
+ pos += apr_snprintf(buf+pos, sizeof(buf)-pos, " ");
+ else {
+ ch = ((unsigned char)*((char *)(b) + i * DUMP_WIDTH + j)) & 0xff;
+ pos += apr_snprintf(buf+pos, sizeof(buf)-pos, "%02x%c", ch , j==7 ? '-' : ' ');
+ }
+ }
+ pos += apr_snprintf(buf+pos, sizeof(buf)-pos, " ");
+ for (j = 0; j < DUMP_WIDTH; j++) {
+ if (((i * DUMP_WIDTH) + j) >= len)
+ pos += apr_snprintf(buf+pos, sizeof(buf)-pos, " ");
+ else {
+ ch = ((unsigned char)*((char *)(b) + i * DUMP_WIDTH + j)) & 0xff;
+#if APR_CHARSET_EBCDIC
+ pos += apr_snprintf(buf+pos, sizeof(buf)-pos, "%c", (ch >= 0x20 && ch <= 0x7F) ? ebcdic_text[j] : '.');
+#else /* APR_CHARSET_EBCDIC */
+ pos += apr_snprintf(buf+pos, sizeof(buf)-pos, "%c", ((ch >= ' ') && (ch <= '~')) ? ch : '.');
+#endif /* APR_CHARSET_EBCDIC */
+ }
+ }
+ pos += apr_snprintf(buf+pos, sizeof(buf)-pos, " |");
+ ap_log_cserror(APLOG_MARK, APLOG_TRACE7, 0, c, s, "%s", buf);
+ }
+ if (trunc > 0)
+ ap_log_cserror(APLOG_MARK, APLOG_TRACE7, 0, c, s,
+ "| %04ld - <SPACES/NULS>", len + trunc);
+ ap_log_cserror(APLOG_MARK, APLOG_TRACE7, 0, c, s,
+ "+-------------------------------------------------------------------------+");
+}
+
+long ssl_io_data_cb(BIO *bio, int cmd,
+ const char *argp,
+ int argi, long argl, long rc)
+{
+ SSL *ssl;
+ conn_rec *c;
+ server_rec *s;
+
+ if ((ssl = (SSL *)BIO_get_callback_arg(bio)) == NULL)
+ return rc;
+ if ((c = (conn_rec *)SSL_get_app_data(ssl)) == NULL)
+ return rc;
+ s = mySrvFromConn(c);
+
+ if ( cmd == (BIO_CB_WRITE|BIO_CB_RETURN)
+ || cmd == (BIO_CB_READ |BIO_CB_RETURN) ) {
+ if (rc >= 0) {
+ const char *dump = "";
+ if (APLOG_CS_IS_LEVEL(c, s, APLOG_TRACE7)) {
+ if (argp != NULL)
+ dump = "(BIO dump follows)";
+ else
+ dump = "(Oops, no memory buffer?)";
+ }
+ ap_log_cserror(APLOG_MARK, APLOG_TRACE4, 0, c, s,
+ "%s: %s %ld/%d bytes %s BIO#%pp [mem: %pp] %s",
+ MODSSL_LIBRARY_NAME,
+ (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "write" : "read"),
+ rc, argi, (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "to" : "from"),
+ bio, argp, dump);
+ if (*dump != '\0' && argp != NULL)
+ ssl_io_data_dump(c, s, argp, rc);
+ }
+ else {
+ ap_log_cserror(APLOG_MARK, APLOG_TRACE4, 0, c, s,
+ "%s: I/O error, %d bytes expected to %s on BIO#%pp [mem: %pp]",
+ MODSSL_LIBRARY_NAME, argi,
+ (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "write" : "read"),
+ bio, argp);
+ }
+ }
+ return rc;
+}
diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c
new file mode 100644
index 0000000..591f6ae
--- /dev/null
+++ b/modules/ssl/ssl_engine_kernel.c
@@ -0,0 +1,2855 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+/* _ _
+ * _ __ ___ ___ __| | ___ ___| | mod_ssl
+ * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| | \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ * |_____|
+ * ssl_engine_kernel.c
+ * The SSL engine kernel
+ */
+ /* ``It took me fifteen years to discover
+ I had no talent for programming, but
+ I couldn't give it up because by that
+ time I was too famous.''
+ -- Unknown */
+#include "ssl_private.h"
+#include "mod_ssl.h"
+#include "util_md5.h"
+#include "scoreboard.h"
+
+static void ssl_configure_env(request_rec *r, SSLConnRec *sslconn);
+#ifdef HAVE_TLSEXT
+static int ssl_find_vhost(void *servername, conn_rec *c, server_rec *s);
+#endif
+
+#define SWITCH_STATUS_LINE "HTTP/1.1 101 Switching Protocols"
+#define UPGRADE_HEADER "Upgrade: TLS/1.0, HTTP/1.1"
+#define CONNECTION_HEADER "Connection: Upgrade"
+
+/* Perform an upgrade-to-TLS for the given request, per RFC 2817. */
+static apr_status_t upgrade_connection(request_rec *r)
+{
+ struct conn_rec *conn = r->connection;
+ apr_bucket_brigade *bb;
+ SSLConnRec *sslconn;
+ apr_status_t rv;
+ SSL *ssl;
+
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02028)
+ "upgrading connection to TLS");
+
+ bb = apr_brigade_create(r->pool, conn->bucket_alloc);
+
+ rv = ap_fputs(conn->output_filters, bb, SWITCH_STATUS_LINE CRLF
+ UPGRADE_HEADER CRLF CONNECTION_HEADER CRLF CRLF);
+ if (rv == APR_SUCCESS) {
+ APR_BRIGADE_INSERT_TAIL(bb,
+ apr_bucket_flush_create(conn->bucket_alloc));
+ rv = ap_pass_brigade(conn->output_filters, bb);
+ }
+
+ if (rv) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02029)
+ "failed to send 101 interim response for connection "
+ "upgrade");
+ return rv;
+ }
+
+ ssl_init_ssl_connection(conn, r);
+
+ sslconn = myConnConfig(conn);
+ ssl = sslconn->ssl;
+
+ /* Perform initial SSL handshake. */
+ SSL_set_accept_state(ssl);
+ SSL_do_handshake(ssl);
+
+ if (!SSL_is_init_finished(ssl)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02030)
+ "TLS upgrade handshake failed");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, r->server);
+
+ return APR_ECONNABORTED;
+ }
+
+ return APR_SUCCESS;
+}
+
+/* Perform a speculative (and non-blocking) read from the connection
+ * filters for the given request, to determine whether there is any
+ * pending data to read. Return non-zero if there is, else zero. */
+static int has_buffered_data(request_rec *r)
+{
+ apr_bucket_brigade *bb;
+ apr_off_t len;
+ apr_status_t rv;
+ int result;
+
+ bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+
+ rv = ap_get_brigade(r->connection->input_filters, bb, AP_MODE_SPECULATIVE,
+ APR_NONBLOCK_READ, 1);
+ result = rv == APR_SUCCESS
+ && apr_brigade_length(bb, 1, &len) == APR_SUCCESS
+ && len > 0;
+
+ apr_brigade_destroy(bb);
+
+ return result;
+}
+
+/* If a renegotiation is required for the location, and the request
+ * includes a message body (and the client has not requested a "100
+ * Continue" response), then the client will be streaming the request
+ * body over the wire already. In that case, it is not possible to
+ * stop and perform a new SSL handshake immediately; once the SSL
+ * library moves to the "accept" state, it will reject the SSL packets
+ * which the client is sending for the request body.
+ *
+ * To allow authentication to complete in the hook, the solution used
+ * here is to fill a (bounded) buffer with the request body, and then
+ * to reinject that request body later.
+ *
+ * This function is called to fill the renegotiation buffer for the
+ * location as required, or fail. Returns zero on success or HTTP_
+ * error code on failure.
+ */
+static int fill_reneg_buffer(request_rec *r, SSLDirConfigRec *dc)
+{
+ int rv;
+ apr_size_t rsize;
+
+ /* ### this is HTTP/1.1 specific, special case for protocol? */
+ if (r->expecting_100 || !ap_request_has_body(r)) {
+ return 0;
+ }
+
+ rsize = dc->nRenegBufferSize == UNSET ? DEFAULT_RENEG_BUFFER_SIZE : dc->nRenegBufferSize;
+ if (rsize > 0) {
+ /* Fill the I/O buffer with the request body if possible. */
+ rv = ssl_io_buffer_fill(r, rsize);
+ }
+ else {
+ /* If the reneg buffer size is set to zero, just fail. */
+ rv = HTTP_REQUEST_ENTITY_TOO_LARGE;
+ }
+
+ return rv;
+}
+
+#ifdef HAVE_TLSEXT
+static int ap_array_same_str_set(apr_array_header_t *s1, apr_array_header_t *s2)
+{
+ int i;
+ const char *c;
+
+ if (s1 == s2) {
+ return 1;
+ }
+ else if (!s1 || !s2 || (s1->nelts != s2->nelts)) {
+ return 0;
+ }
+
+ for (i = 0; i < s1->nelts; i++) {
+ c = APR_ARRAY_IDX(s1, i, const char *);
+ if (!c || !ap_array_str_contains(s2, c)) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int ssl_pk_server_compatible(modssl_pk_server_t *pks1,
+ modssl_pk_server_t *pks2)
+{
+ if (!pks1 || !pks2) {
+ return 0;
+ }
+ /* both have the same certificates? */
+ if ((pks1->ca_name_path != pks2->ca_name_path)
+ && (!pks1->ca_name_path || !pks2->ca_name_path
+ || strcmp(pks1->ca_name_path, pks2->ca_name_path))) {
+ return 0;
+ }
+ if ((pks1->ca_name_file != pks2->ca_name_file)
+ && (!pks1->ca_name_file || !pks2->ca_name_file
+ || strcmp(pks1->ca_name_file, pks2->ca_name_file))) {
+ return 0;
+ }
+ if (!ap_array_same_str_set(pks1->cert_files, pks2->cert_files)
+ || !ap_array_same_str_set(pks1->key_files, pks2->key_files)) {
+ return 0;
+ }
+ return 1;
+}
+
+static int ssl_auth_compatible(modssl_auth_ctx_t *a1,
+ modssl_auth_ctx_t *a2)
+{
+ if (!a1 || !a2) {
+ return 0;
+ }
+ /* both have the same verification */
+ if ((a1->verify_depth != a2->verify_depth)
+ || (a1->verify_mode != a2->verify_mode)) {
+ return 0;
+ }
+ /* both have the same ca path/file */
+ if ((a1->ca_cert_path != a2->ca_cert_path)
+ && (!a1->ca_cert_path || !a2->ca_cert_path
+ || strcmp(a1->ca_cert_path, a2->ca_cert_path))) {
+ return 0;
+ }
+ if ((a1->ca_cert_file != a2->ca_cert_file)
+ && (!a1->ca_cert_file || !a2->ca_cert_file
+ || strcmp(a1->ca_cert_file, a2->ca_cert_file))) {
+ return 0;
+ }
+ /* both have the same ca cipher suite string */
+ if ((a1->cipher_suite != a2->cipher_suite)
+ && (!a1->cipher_suite || !a2->cipher_suite
+ || strcmp(a1->cipher_suite, a2->cipher_suite))) {
+ return 0;
+ }
+ /* both have the same ca cipher suite string */
+ if ((a1->tls13_ciphers != a2->tls13_ciphers)
+ && (!a1->tls13_ciphers || !a2->tls13_ciphers
+ || strcmp(a1->tls13_ciphers, a2->tls13_ciphers))) {
+ return 0;
+ }
+ return 1;
+}
+
+static int ssl_ctx_compatible(modssl_ctx_t *ctx1,
+ modssl_ctx_t *ctx2)
+{
+ if (!ctx1 || !ctx2
+ || (ctx1->protocol != ctx2->protocol)
+ || !ssl_auth_compatible(&ctx1->auth, &ctx2->auth)
+ || !ssl_pk_server_compatible(ctx1->pks, ctx2->pks)) {
+ return 0;
+ }
+ return 1;
+}
+
+static int ssl_server_compatible(server_rec *s1, server_rec *s2)
+{
+ SSLSrvConfigRec *sc1 = s1? mySrvConfig(s1) : NULL;
+ SSLSrvConfigRec *sc2 = s2? mySrvConfig(s2) : NULL;
+
+ /* both use the same TLS protocol? */
+ if (!sc1 || !sc2
+ || !ssl_ctx_compatible(sc1->server, sc2->server)) {
+ return 0;
+ }
+
+ return 1;
+}
+#endif
+
+/*
+ * Post Read Request Handler
+ */
+int ssl_hook_ReadReq(request_rec *r)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(r->server);
+ SSLConnRec *sslconn;
+ const char *upgrade;
+#ifdef HAVE_TLSEXT
+ const char *servername;
+#endif
+ SSL *ssl;
+
+ /* Perform TLS upgrade here if "SSLEngine optional" is configured,
+ * SSL is not already set up for this connection, and the client
+ * has sent a suitable Upgrade header. */
+ if (sc->enabled == SSL_ENABLED_OPTIONAL && !myConnConfig(r->connection)
+ && (upgrade = apr_table_get(r->headers_in, "Upgrade")) != NULL
+ && ap_find_token(r->pool, upgrade, "TLS/1.0")) {
+ if (upgrade_connection(r)) {
+ return AP_FILTER_ERROR;
+ }
+ }
+
+ /* If we are on a slave connection, we do not expect to have an SSLConnRec,
+ * but our master connection might. */
+ sslconn = myConnConfig(r->connection);
+ if (!(sslconn && sslconn->ssl) && r->connection->master) {
+ sslconn = myConnConfig(r->connection->master);
+ }
+
+ /* If "SSLEngine optional" is configured, this is not an SSL
+ * connection, and this isn't a subrequest, send an Upgrade
+ * response header. Note this must happen before map_to_storage
+ * and OPTIONS * request processing is completed.
+ */
+ if (sc->enabled == SSL_ENABLED_OPTIONAL && !(sslconn && sslconn->ssl)
+ && !r->main) {
+ apr_table_setn(r->headers_out, "Upgrade", "TLS/1.0, HTTP/1.1");
+ apr_table_mergen(r->headers_out, "Connection", "upgrade");
+ }
+
+ if (!sslconn) {
+ return DECLINED;
+ }
+
+ if (sslconn->service_unavailable) {
+ /* This is set when the SSL properties of this connection are
+ * incomplete or if this connection was made to challenge a
+ * particular hostname (ACME). We never serve any request on
+ * such a connection. */
+ /* TODO: a retry-after indicator would be nice here */
+ return HTTP_SERVICE_UNAVAILABLE;
+ }
+
+ if (sslconn->non_ssl_request == NON_SSL_SET_ERROR_MSG) {
+ apr_table_setn(r->notes, "error-notes",
+ "Reason: You're speaking plain HTTP to an SSL-enabled "
+ "server port.<br />\n Instead use the HTTPS scheme to "
+ "access this URL, please.<br />\n");
+
+ /* Now that we have caught this error, forget it. we are done
+ * with using SSL on this request.
+ */
+ sslconn->non_ssl_request = NON_SSL_OK;
+
+ return HTTP_BAD_REQUEST;
+ }
+
+ /*
+ * Get the SSL connection structure and perform the
+ * delayed interlinking from SSL back to request_rec
+ */
+ ssl = sslconn->ssl;
+ if (!ssl) {
+ return DECLINED;
+ }
+#ifdef HAVE_TLSEXT
+ /*
+ * Perform SNI checks only on the initial request. In particular,
+ * if these checks detect a problem, the checks shouldn't return an
+ * error again when processing an ErrorDocument redirect for the
+ * original problem.
+ */
+ if (r->proxyreq != PROXYREQ_PROXY && ap_is_initial_req(r)) {
+ server_rec *handshakeserver = sslconn->server;
+ SSLSrvConfigRec *hssc = mySrvConfig(handshakeserver);
+
+ if ((servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) {
+ /*
+ * The SNI extension supplied a hostname. So don't accept requests
+ * with either no hostname or a hostname that selected a different
+ * virtual host than the one used for the handshake, causing
+ * different SSL parameters to be applied, such as SSLProtocol,
+ * SSLCACertificateFile/Path and SSLCADNRequestFile/Path which
+ * cannot be renegotiated (SSLCA* due to current limitations in
+ * OpenSSL, see:
+ * http://mail-archives.apache.org/mod_mbox/httpd-dev/200806.mbox/%3C48592955.2090303@velox.ch%3E
+ * and
+ * http://mail-archives.apache.org/mod_mbox/httpd-dev/201312.mbox/%3CCAKQ1sVNpOrdiBm-UPw1hEdSN7YQXRRjeaT-MCWbW_7mN%3DuFiOw%40mail.gmail.com%3E
+ * )
+ */
+ if (!r->hostname) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02031)
+ "Hostname %s provided via SNI, but no hostname"
+ " provided in HTTP request", servername);
+ return HTTP_BAD_REQUEST;
+ }
+ if (r->server != handshakeserver
+ && !ssl_server_compatible(sslconn->server, r->server)) {
+ /*
+ * The request does not select the virtual host that was
+ * selected by the SNI and its SSL parameters are different
+ */
+
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02032)
+ "Hostname %s provided via SNI and hostname %s provided"
+ " via HTTP have no compatible SSL setup",
+ servername, r->hostname);
+ return HTTP_MISDIRECTED_REQUEST;
+ }
+ }
+ else if (((sc->strict_sni_vhost_check == SSL_ENABLED_TRUE)
+ || hssc->strict_sni_vhost_check == SSL_ENABLED_TRUE)
+ && r->connection->vhost_lookup_data) {
+ /*
+ * We are using a name based configuration here, but no hostname was
+ * provided via SNI. Don't allow that if are requested to do strict
+ * checking. Check whether this strict checking was set up either in the
+ * server config we used for handshaking or in our current server.
+ * This should avoid insecure configuration by accident.
+ */
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02033)
+ "No hostname was provided via SNI for a name based"
+ " virtual host");
+ apr_table_setn(r->notes, "error-notes",
+ "Reason: The client software did not provide a "
+ "hostname using Server Name Indication (SNI), "
+ "which is required to access this server.<br />\n");
+ return HTTP_FORBIDDEN;
+ }
+ }
+#endif
+ modssl_set_app_data2(ssl, r);
+
+ /*
+ * Log information about incoming HTTPS requests
+ */
+ if (APLOGrinfo(r) && ap_is_initial_req(r)) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02034)
+ "%s HTTPS request received for child %ld (server %s)",
+ (r->connection->keepalives <= 0 ?
+ "Initial (No.1)" :
+ apr_psprintf(r->pool, "Subsequent (No.%d)",
+ r->connection->keepalives+1)),
+ r->connection->id,
+ ssl_util_vhostid(r->pool, r->server));
+ }
+
+ /* SetEnvIf ssl-*-shutdown flags can only be per-server,
+ * so they won't change across keepalive requests
+ */
+ if (sslconn->shutdown_type == SSL_SHUTDOWN_TYPE_UNSET) {
+ ssl_configure_env(r, sslconn);
+ }
+
+ return DECLINED;
+}
+
+/*
+ * Move SetEnvIf information from request_rec to conn_rec/BUFF
+ * to allow the close connection handler to use them.
+ */
+
+static void ssl_configure_env(request_rec *r, SSLConnRec *sslconn)
+{
+ int i;
+ const apr_array_header_t *arr = apr_table_elts(r->subprocess_env);
+ const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts;
+
+ sslconn->shutdown_type = SSL_SHUTDOWN_TYPE_STANDARD;
+
+ for (i = 0; i < arr->nelts; i++) {
+ const char *key = elts[i].key;
+
+ switch (*key) {
+ case 's':
+ /* being case-sensitive here.
+ * and not checking for the -shutdown since these are the only
+ * SetEnvIf "flags" we support
+ */
+ if (!strncmp(key+1, "sl-", 3)) {
+ key += 4;
+ if (!strncmp(key, "unclean", 7)) {
+ sslconn->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN;
+ }
+ else if (!strncmp(key, "accurate", 8)) {
+ sslconn->shutdown_type = SSL_SHUTDOWN_TYPE_ACCURATE;
+ }
+ return; /* should only ever be one ssl-*-shutdown */
+ }
+ break;
+ }
+ }
+}
+
+static int ssl_check_post_client_verify(request_rec *r, SSLSrvConfigRec *sc,
+ SSLDirConfigRec *dc, SSLConnRec *sslconn,
+ SSL *ssl)
+{
+ X509 *cert;
+
+ /*
+ * Remember the peer certificate's DN
+ */
+ if ((cert = SSL_get_peer_certificate(ssl))) {
+ if (sslconn->client_cert) {
+ X509_free(sslconn->client_cert);
+ }
+ sslconn->client_cert = cert;
+ sslconn->client_dn = NULL;
+ }
+
+ /*
+ * Finally check for acceptable renegotiation results
+ */
+ if ((dc->nVerifyClient != SSL_CVERIFY_NONE) ||
+ (sc->server->auth.verify_mode != SSL_CVERIFY_NONE)) {
+ BOOL do_verify = ((dc->nVerifyClient == SSL_CVERIFY_REQUIRE) ||
+ (sc->server->auth.verify_mode == SSL_CVERIFY_REQUIRE));
+
+ if (do_verify && (SSL_get_verify_result(ssl) != X509_V_OK)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02262)
+ "Re-negotiation handshake failed: "
+ "Client verification failed");
+
+ return HTTP_FORBIDDEN;
+ }
+
+ if (do_verify) {
+ if (cert == NULL) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02263)
+ "Re-negotiation handshake failed: "
+ "Client certificate missing");
+
+ return HTTP_FORBIDDEN;
+ }
+ }
+ }
+ return OK;
+}
+
+/*
+ * Access Handler, classic flavour, for SSL/TLS up to v1.2
+ * where everything can be renegotiated and no one is happy.
+ */
+static int ssl_hook_Access_classic(request_rec *r, SSLSrvConfigRec *sc, SSLDirConfigRec *dc,
+ SSLConnRec *sslconn, SSL *ssl)
+{
+ server_rec *handshakeserver = sslconn ? sslconn->server : NULL;
+ SSLSrvConfigRec *hssc = handshakeserver? mySrvConfig(handshakeserver) : NULL;
+ SSL_CTX *ctx = ssl ? SSL_get_SSL_CTX(ssl) : NULL;
+ BOOL renegotiate = FALSE, renegotiate_quick = FALSE;
+ X509 *peercert;
+ X509_STORE *cert_store = NULL;
+ X509_STORE_CTX *cert_store_ctx;
+ STACK_OF(SSL_CIPHER) *cipher_list_old = NULL, *cipher_list = NULL;
+ const SSL_CIPHER *cipher = NULL;
+ int depth, verify_old, verify, n, rc;
+ const char *ncipher_suite;
+
+#ifdef HAVE_SRP
+ /*
+ * Support for per-directory reconfigured SSL connection parameters
+ *
+ * We do not force any renegotiation if the user is already authenticated
+ * via SRP.
+ *
+ */
+ if (SSL_get_srp_username(ssl)) {
+ return DECLINED;
+ }
+#endif
+
+ /*
+ * Support for per-directory reconfigured SSL connection parameters.
+ *
+ * This is implemented by forcing an SSL renegotiation with the
+ * reconfigured parameter suite. But Apache's internal API processing
+ * makes our life very hard here, because when internal sub-requests occur
+ * we nevertheless should avoid multiple unnecessary SSL handshakes (they
+ * require extra network I/O and especially time to perform).
+ *
+ * But the optimization for filtering out the unnecessary handshakes isn't
+ * obvious and trivial. Especially because while Apache is in its
+ * sub-request processing the client could force additional handshakes,
+ * too. And these take place perhaps without our notice. So the only
+ * possibility is to explicitly _ask_ OpenSSL whether the renegotiation
+ * has to be performed or not. It has to performed when some parameters
+ * which were previously known (by us) are not those we've now
+ * reconfigured (as known by OpenSSL) or (in optimized way) at least when
+ * the reconfigured parameter suite is stronger (more restrictions) than
+ * the currently active one.
+ */
+
+ /*
+ * Override of SSLCipherSuite
+ *
+ * We provide two options here:
+ *
+ * o The paranoid and default approach where we force a renegotiation when
+ * the cipher suite changed in _any_ way (which is straight-forward but
+ * often forces renegotiations too often and is perhaps not what the
+ * user actually wanted).
+ *
+ * o The optimized and still secure way where we force a renegotiation
+ * only if the currently active cipher is no longer contained in the
+ * reconfigured/new cipher suite. Any other changes are not important
+ * because it's the servers choice to select a cipher from the ones the
+ * client supports. So as long as the current cipher is still in the new
+ * cipher suite we're happy. Because we can assume we would have
+ * selected it again even when other (better) ciphers exists now in the
+ * new cipher suite. This approach is fine because the user explicitly
+ * has to enable this via ``SSLOptions +OptRenegotiate''. So we do no
+ * implicit optimizations.
+ */
+ ncipher_suite = (dc->szCipherSuite?
+ dc->szCipherSuite : (r->server != handshakeserver)?
+ sc->server->auth.cipher_suite : NULL);
+
+ if (ncipher_suite && (!sslconn->cipher_suite
+ || strcmp(ncipher_suite, sslconn->cipher_suite))) {
+ /* remember old state */
+
+ if (dc->nOptions & SSL_OPT_OPTRENEGOTIATE) {
+ cipher = SSL_get_current_cipher(ssl);
+ }
+ else {
+ cipher_list_old = (STACK_OF(SSL_CIPHER) *)SSL_get_ciphers(ssl);
+
+ if (cipher_list_old) {
+ cipher_list_old = sk_SSL_CIPHER_dup(cipher_list_old);
+ }
+ }
+
+ /* configure new state */
+ if (r->connection->master) {
+ /* TODO: this categorically fails changed cipher suite settings
+ * on slave connections. We could do better by
+ * - create a new SSL* from our SSL_CTX and set cipher suite there,
+ * and retrieve ciphers, free afterwards
+ * Modifying the SSL on a slave connection is no good.
+ */
+ apr_table_setn(r->notes, "ssl-renegotiate-forbidden", "cipher-suite");
+ return HTTP_FORBIDDEN;
+ }
+
+ if (!SSL_set_cipher_list(ssl, ncipher_suite)) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02253)
+ "Unable to reconfigure (per-directory) "
+ "permitted SSL ciphers");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, r->server);
+
+ if (cipher_list_old) {
+ sk_SSL_CIPHER_free(cipher_list_old);
+ }
+
+ return HTTP_FORBIDDEN;
+ }
+
+ /* determine whether a renegotiation has to be forced */
+ cipher_list = (STACK_OF(SSL_CIPHER) *)SSL_get_ciphers(ssl);
+
+ if (dc->nOptions & SSL_OPT_OPTRENEGOTIATE) {
+ /* optimized way */
+ if ((!cipher && cipher_list) ||
+ (cipher && !cipher_list))
+ {
+ renegotiate = TRUE;
+ }
+ else if (cipher && cipher_list &&
+ (sk_SSL_CIPHER_find(cipher_list, cipher) < 0))
+ {
+ renegotiate = TRUE;
+ }
+ }
+ else {
+ /* paranoid way */
+ if ((!cipher_list_old && cipher_list) ||
+ (cipher_list_old && !cipher_list))
+ {
+ renegotiate = TRUE;
+ }
+ else if (cipher_list_old && cipher_list) {
+ for (n = 0;
+ !renegotiate && (n < sk_SSL_CIPHER_num(cipher_list));
+ n++)
+ {
+ const SSL_CIPHER *value = sk_SSL_CIPHER_value(cipher_list, n);
+
+ if (sk_SSL_CIPHER_find(cipher_list_old, value) < 0) {
+ renegotiate = TRUE;
+ }
+ }
+
+ for (n = 0;
+ !renegotiate && (n < sk_SSL_CIPHER_num(cipher_list_old));
+ n++)
+ {
+ const SSL_CIPHER *value = sk_SSL_CIPHER_value(cipher_list_old, n);
+
+ if (sk_SSL_CIPHER_find(cipher_list, value) < 0) {
+ renegotiate = TRUE;
+ }
+ }
+ }
+ }
+
+ /* cleanup */
+ if (cipher_list_old) {
+ sk_SSL_CIPHER_free(cipher_list_old);
+ }
+
+ if (renegotiate) {
+ if (r->connection->master) {
+ /* The request causes renegotiation on a slave connection.
+ * This is not allowed since we might have concurrent requests
+ * on this connection.
+ */
+ apr_table_setn(r->notes, "ssl-renegotiate-forbidden", "cipher-suite");
+ return HTTP_FORBIDDEN;
+ }
+
+#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
+ if (sc->cipher_server_pref == TRUE) {
+ SSL_set_options(ssl, SSL_OP_CIPHER_SERVER_PREFERENCE);
+ }
+#endif
+ /* tracing */
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02220)
+ "Reconfigured cipher suite will force renegotiation");
+ }
+ }
+
+ /*
+ * override of SSLVerifyClient
+ *
+ * We force a renegotiation if the reconfigured/new verify type is
+ * stronger than the currently active verify type.
+ *
+ * The order is: none << optional_no_ca << optional << require
+ *
+ * Additionally the following optimization is possible here: When the
+ * currently active verify type is "none" but a client certificate is
+ * already known/present, it's enough to manually force a client
+ * verification but at least skip the I/O-intensive renegotiation
+ * handshake.
+ */
+ if ((dc->nVerifyClient != SSL_CVERIFY_UNSET) ||
+ (sc->server->auth.verify_mode != SSL_CVERIFY_UNSET)) {
+
+ /* remember old state */
+ verify_old = SSL_get_verify_mode(ssl);
+ /* configure new state */
+ verify = SSL_VERIFY_NONE;
+
+ if ((dc->nVerifyClient == SSL_CVERIFY_REQUIRE) ||
+ (sc->server->auth.verify_mode == SSL_CVERIFY_REQUIRE)) {
+ verify |= SSL_VERIFY_PEER_STRICT;
+ }
+
+ if ((dc->nVerifyClient == SSL_CVERIFY_OPTIONAL) ||
+ (dc->nVerifyClient == SSL_CVERIFY_OPTIONAL_NO_CA) ||
+ (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL) ||
+ (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA))
+ {
+ verify |= SSL_VERIFY_PEER;
+ }
+
+ /* TODO: this seems premature since we do not know if there
+ * are any changes required.
+ */
+ SSL_set_verify(ssl, verify, ssl_callback_SSLVerify);
+ SSL_set_verify_result(ssl, X509_V_OK);
+
+ /* determine whether we've to force a renegotiation */
+ if (!renegotiate && verify != verify_old) {
+ if (((verify_old == SSL_VERIFY_NONE) &&
+ (verify != SSL_VERIFY_NONE)) ||
+
+ (!(verify_old & SSL_VERIFY_PEER) &&
+ (verify & SSL_VERIFY_PEER)) ||
+
+ (!(verify_old & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) &&
+ (verify & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)))
+ {
+ renegotiate = TRUE;
+ if (r->connection->master) {
+ /* The request causes renegotiation on a slave connection.
+ * This is not allowed since we might have concurrent requests
+ * on this connection.
+ */
+ apr_table_setn(r->notes, "ssl-renegotiate-forbidden", "verify-client");
+ SSL_set_verify(ssl, verify_old, ssl_callback_SSLVerify);
+ return HTTP_FORBIDDEN;
+ }
+ /* optimization */
+
+ if ((dc->nOptions & SSL_OPT_OPTRENEGOTIATE) &&
+ (verify_old == SSL_VERIFY_NONE) &&
+ ((peercert = SSL_get_peer_certificate(ssl)) != NULL))
+ {
+ renegotiate_quick = TRUE;
+ X509_free(peercert);
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02255)
+ "Changed client verification type will force "
+ "%srenegotiation",
+ renegotiate_quick ? "quick " : "");
+ }
+ else if (verify != SSL_VERIFY_NONE) {
+ /*
+ * override of SSLVerifyDepth
+ *
+ * The depth checks are handled by us manually inside the
+ * verify callback function and not by OpenSSL internally
+ * (and our function is aware of both the per-server and
+ * per-directory contexts). So we cannot ask OpenSSL about
+ * the currently verify depth. Instead we remember it in our
+ * SSLConnRec attached to the SSL* of OpenSSL. We've to force
+ * the renegotiation if the reconfigured/new verify depth is
+ * less than the currently active/remembered verify depth
+ * (because this means more restriction on the certificate
+ * chain).
+ */
+ n = (sslconn->verify_depth != UNSET)
+ ? sslconn->verify_depth
+ : hssc->server->auth.verify_depth;
+ /* determine the new depth */
+ sslconn->verify_depth = (dc->nVerifyDepth != UNSET)
+ ? dc->nVerifyDepth
+ : sc->server->auth.verify_depth;
+ if (sslconn->verify_depth < n) {
+ renegotiate = TRUE;
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02254)
+ "Reduced client verification depth will "
+ "force renegotiation");
+ }
+ }
+ }
+ /* If we're handling a request for a vhost other than the default one,
+ * then we need to make sure that client authentication is properly
+ * enforced. For clients supplying an SNI extension, the peer
+ * certificate verification has happened in the handshake already
+ * (and r->server == handshakeserver). For non-SNI requests,
+ * an additional check is needed here. If client authentication
+ * is configured as mandatory, then we can only proceed if the
+ * CA list doesn't have to be changed (OpenSSL doesn't provide
+ * an option to change the list for an existing session).
+ */
+ if ((r->server != handshakeserver)
+ && renegotiate
+ && ((verify & SSL_VERIFY_PEER) ||
+ (verify & SSL_VERIFY_FAIL_IF_NO_PEER_CERT))) {
+#define MODSSL_CFG_CA_NE(f, sc1, sc2) \
+ (sc1->server->auth.f && \
+ (!sc2->server->auth.f || \
+ strNE(sc1->server->auth.f, sc2->server->auth.f)))
+
+ if (MODSSL_CFG_CA_NE(ca_cert_file, sc, hssc) ||
+ MODSSL_CFG_CA_NE(ca_cert_path, sc, hssc)) {
+ if (verify & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02256)
+ "Non-default virtual host with SSLVerify set to "
+ "'require' and VirtualHost-specific CA certificate "
+ "list is only available to clients with TLS server "
+ "name indication (SNI) support");
+ SSL_set_verify(ssl, verify_old, NULL);
+ return HTTP_FORBIDDEN;
+ } else
+ /* let it pass, possibly with an "incorrect" peer cert,
+ * so make sure the SSL_CLIENT_VERIFY environment variable
+ * will indicate partial success only, later on.
+ */
+ sslconn->verify_info = "GENEROUS";
+ }
+ }
+ }
+
+ /* Fill reneg buffer if required. */
+ if (renegotiate && !renegotiate_quick) {
+ rc = fill_reneg_buffer(r, dc);
+ if (rc) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02257)
+ "could not buffer message body to allow "
+ "SSL renegotiation to proceed");
+ return rc;
+ }
+ }
+
+ /*
+ * now do the renegotiation if anything was actually reconfigured
+ */
+ if (renegotiate) {
+ /*
+ * Now we force the SSL renegotiation by sending the Hello Request
+ * message to the client. Here we have to do a workaround: Actually
+ * OpenSSL returns immediately after sending the Hello Request (the
+ * intent AFAIK is because the SSL/TLS protocol says it's not a must
+ * that the client replies to a Hello Request). But because we insist
+ * on a reply (anything else is an error for us) we have to go to the
+ * ACCEPT state manually. Using SSL_set_accept_state() doesn't work
+ * here because it resets too much of the connection. So we set the
+ * state explicitly and continue the handshake manually.
+ */
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02221)
+ "Requesting connection re-negotiation");
+
+ if (renegotiate_quick) {
+ STACK_OF(X509) *cert_stack;
+ X509 *cert;
+
+ /* perform just a manual re-verification of the peer */
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02258)
+ "Performing quick renegotiation: "
+ "just re-verifying the peer");
+
+ cert_stack = (STACK_OF(X509) *)SSL_get_peer_cert_chain(ssl);
+
+ cert = SSL_get_peer_certificate(ssl);
+
+ if (!cert_stack || (sk_X509_num(cert_stack) == 0)) {
+ if (!cert) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02222)
+ "Cannot find peer certificate chain");
+
+ return HTTP_FORBIDDEN;
+ }
+
+ /* client cert is in the session cache, but there is
+ * no chain, since ssl3_get_client_certificate()
+ * sk_X509_shift-ed the peer cert out of the chain.
+ * we put it back here for the purpose of quick_renegotiation.
+ */
+ cert_stack = sk_X509_new_null();
+ sk_X509_push(cert_stack, cert);
+ }
+
+ if (!(cert_store ||
+ (cert_store = SSL_CTX_get_cert_store(ctx))))
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02223)
+ "Cannot find certificate storage");
+
+ return HTTP_FORBIDDEN;
+ }
+
+ if (!cert) {
+ cert = sk_X509_value(cert_stack, 0);
+ }
+
+ cert_store_ctx = X509_STORE_CTX_new();
+ X509_STORE_CTX_init(cert_store_ctx, cert_store, cert, cert_stack);
+ depth = SSL_get_verify_depth(ssl);
+
+ if (depth >= 0) {
+ X509_STORE_CTX_set_depth(cert_store_ctx, depth);
+ }
+
+ X509_STORE_CTX_set_ex_data(cert_store_ctx,
+ SSL_get_ex_data_X509_STORE_CTX_idx(),
+ (char *)ssl);
+
+ if (!X509_verify_cert(cert_store_ctx)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02224)
+ "Re-negotiation verification step failed");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, r->server);
+ }
+
+ SSL_set_verify_result(ssl, X509_STORE_CTX_get_error(cert_store_ctx));
+ X509_STORE_CTX_cleanup(cert_store_ctx);
+ X509_STORE_CTX_free(cert_store_ctx);
+
+ if (cert_stack != SSL_get_peer_cert_chain(ssl)) {
+ /* we created this ourselves, so free it */
+ sk_X509_pop_free(cert_stack, X509_free);
+ }
+ }
+ else {
+ char peekbuf[1];
+ const char *reneg_support;
+ request_rec *id = r->main ? r->main : r;
+
+ /* Additional mitigation for CVE-2009-3555: At this point,
+ * before renegotiating, an (entire) request has been read
+ * from the connection. An attacker may have sent further
+ * data to "prefix" any subsequent request by the victim's
+ * client after the renegotiation; this data may already
+ * have been read and buffered. Forcing a connection
+ * closure after the response ensures such data will be
+ * discarded. Legimately pipelined HTTP requests will be
+ * retried anyway with this approach. */
+ if (has_buffered_data(r)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02259)
+ "insecure SSL re-negotiation required, but "
+ "a pipelined request is present; keepalive "
+ "disabled");
+ r->connection->keepalive = AP_CONN_CLOSE;
+ }
+
+#if defined(SSL_get_secure_renegotiation_support)
+ reneg_support = SSL_get_secure_renegotiation_support(ssl) ?
+ "client does" : "client does not";
+#else
+ reneg_support = "server does not";
+#endif
+ /* Perform a full renegotiation. */
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02260)
+ "Performing full renegotiation: complete handshake "
+ "protocol (%s support secure renegotiation)",
+ reneg_support);
+
+ SSL_set_session_id_context(ssl,
+ (unsigned char *)&id,
+ sizeof(id));
+
+ /* Toggle the renegotiation state to allow the new
+ * handshake to proceed. */
+ sslconn->reneg_state = RENEG_ALLOW;
+
+ SSL_renegotiate(ssl);
+ SSL_do_handshake(ssl);
+
+ if (!SSL_is_init_finished(ssl)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02225)
+ "Re-negotiation request failed");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, r->server);
+
+ r->connection->keepalive = AP_CONN_CLOSE;
+ return HTTP_FORBIDDEN;
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02226)
+ "Awaiting re-negotiation handshake");
+
+ /* XXX: Should replace setting state with SSL_renegotiate(ssl);
+ * However, this causes failures in perl-framework currently,
+ * perhaps pre-test if we have already negotiated?
+ */
+ /* Need to trigger renegotiation handshake by reading.
+ * Peeking 0 bytes actually works.
+ * See: http://marc.info/?t=145493359200002&r=1&w=2
+ */
+ SSL_peek(ssl, peekbuf, 0);
+
+ sslconn->reneg_state = RENEG_REJECT;
+
+ if (!SSL_is_init_finished(ssl)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02261)
+ "Re-negotiation handshake failed");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, r->server);
+
+ r->connection->keepalive = AP_CONN_CLOSE;
+ return HTTP_FORBIDDEN;
+ }
+
+ /* Full renegotiation successful, we now have handshaken with
+ * this server's parameters.
+ */
+ sslconn->server = r->server;
+ }
+
+ /*
+ * Finally check for acceptable renegotiation results
+ */
+ if (OK != (rc = ssl_check_post_client_verify(r, sc, dc, sslconn, ssl))) {
+ return rc;
+ }
+
+ /*
+ * Also check that SSLCipherSuite has been enforced as expected.
+ */
+ if (cipher_list) {
+ cipher = SSL_get_current_cipher(ssl);
+ if (sk_SSL_CIPHER_find(cipher_list, cipher) < 0) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02264)
+ "SSL cipher suite not renegotiated: "
+ "access to %s denied using cipher %s",
+ r->filename,
+ SSL_CIPHER_get_name(cipher));
+ return HTTP_FORBIDDEN;
+ }
+ }
+ /* remember any new cipher suite used in renegotiation */
+ if (ncipher_suite) {
+ sslconn->cipher_suite = ncipher_suite;
+ }
+ }
+
+ return DECLINED;
+}
+
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+/*
+ * Access Handler, modern flavour, for SSL/TLS v1.3 and onward.
+ * Only client certificates can be requested, everything else stays.
+ */
+static int ssl_hook_Access_modern(request_rec *r, SSLSrvConfigRec *sc, SSLDirConfigRec *dc,
+ SSLConnRec *sslconn, SSL *ssl)
+{
+ if ((dc->nVerifyClient != SSL_CVERIFY_UNSET) ||
+ (sc->server->auth.verify_mode != SSL_CVERIFY_UNSET)) {
+ int vmode_inplace, vmode_needed;
+ int change_vmode = FALSE;
+ int old_state, n, rc;
+
+ vmode_inplace = SSL_get_verify_mode(ssl);
+ vmode_needed = SSL_VERIFY_NONE;
+
+ if ((dc->nVerifyClient == SSL_CVERIFY_REQUIRE) ||
+ (sc->server->auth.verify_mode == SSL_CVERIFY_REQUIRE)) {
+ vmode_needed |= SSL_VERIFY_PEER_STRICT;
+ }
+
+ if ((dc->nVerifyClient == SSL_CVERIFY_OPTIONAL) ||
+ (dc->nVerifyClient == SSL_CVERIFY_OPTIONAL_NO_CA) ||
+ (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL) ||
+ (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA))
+ {
+ vmode_needed |= SSL_VERIFY_PEER;
+ }
+
+ if (vmode_needed == SSL_VERIFY_NONE) {
+ return DECLINED;
+ }
+
+ vmode_needed |= SSL_VERIFY_CLIENT_ONCE;
+ if (vmode_inplace != vmode_needed) {
+ /* Need to change, if new setting is more restrictive than existing one */
+
+ if ((vmode_inplace == SSL_VERIFY_NONE)
+ || (!(vmode_inplace & SSL_VERIFY_PEER)
+ && (vmode_needed & SSL_VERIFY_PEER))
+ || (!(vmode_inplace & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
+ && (vmode_needed & SSL_VERIFY_FAIL_IF_NO_PEER_CERT))) {
+ /* need to change the effective verify mode */
+ change_vmode = TRUE;
+ }
+ else {
+ /* FIXME: does this work with TLSv1.3? Is this more than re-inspecting
+ * the certificate we should already have? */
+ /*
+ * override of SSLVerifyDepth
+ *
+ * The depth checks are handled by us manually inside the
+ * verify callback function and not by OpenSSL internally
+ * (and our function is aware of both the per-server and
+ * per-directory contexts). So we cannot ask OpenSSL about
+ * the currently verify depth. Instead we remember it in our
+ * SSLConnRec attached to the SSL* of OpenSSL. We've to force
+ * the renegotiation if the reconfigured/new verify depth is
+ * less than the currently active/remembered verify depth
+ * (because this means more restriction on the certificate
+ * chain).
+ */
+ n = (sslconn->verify_depth != UNSET)?
+ sslconn->verify_depth : sc->server->auth.verify_depth;
+ /* determine the new depth */
+ sslconn->verify_depth = (dc->nVerifyDepth != UNSET)
+ ? dc->nVerifyDepth
+ : sc->server->auth.verify_depth;
+ if (sslconn->verify_depth < n) {
+ change_vmode = TRUE;
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(10128)
+ "Reduced client verification depth will "
+ "force renegotiation");
+ }
+ }
+ }
+
+ /* Fill reneg buffer if required. */
+ if (change_vmode) {
+ char peekbuf[1];
+
+ if (r->connection->master) {
+ /* FIXME: modifying the SSL on a slave connection is no good.
+ * We would need to push this back to the master connection
+ * somehow.
+ */
+ apr_table_setn(r->notes, "ssl-renegotiate-forbidden", "verify-client");
+ return HTTP_FORBIDDEN;
+ }
+
+ rc = fill_reneg_buffer(r, dc);
+ if (rc) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10228)
+ "could not buffer message body to allow "
+ "TLS Post-Handshake Authentication to proceed");
+ return rc;
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(10129)
+ "verify client post handshake");
+
+ SSL_set_verify(ssl, vmode_needed, ssl_callback_SSLVerify);
+
+ if (SSL_verify_client_post_handshake(ssl) != 1) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10158)
+ "cannot perform post-handshake authentication");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, r->server);
+ apr_table_setn(r->notes, "error-notes",
+ "Reason: Cannot perform Post-Handshake Authentication.<br />");
+ SSL_set_verify(ssl, vmode_inplace, NULL);
+ return HTTP_FORBIDDEN;
+ }
+
+ old_state = sslconn->reneg_state;
+ sslconn->reneg_state = RENEG_ALLOW;
+ modssl_set_app_data2(ssl, r);
+
+ SSL_do_handshake(ssl);
+ /* Need to trigger renegotiation handshake by reading.
+ * Peeking 0 bytes actually works.
+ * See: http://marc.info/?t=145493359200002&r=1&w=2
+ */
+ SSL_peek(ssl, peekbuf, 0);
+
+ sslconn->reneg_state = old_state;
+ modssl_set_app_data2(ssl, NULL);
+
+ /*
+ * Finally check for acceptable renegotiation results
+ */
+ if (OK != (rc = ssl_check_post_client_verify(r, sc, dc, sslconn, ssl))) {
+ SSL_set_verify(ssl, vmode_inplace, NULL);
+ return rc;
+ }
+ }
+ }
+
+ return DECLINED;
+}
+#endif
+
+int ssl_hook_Access(request_rec *r)
+{
+ SSLDirConfigRec *dc = myDirConfig(r);
+ SSLSrvConfigRec *sc = mySrvConfig(r->server);
+ SSLConnRec *sslconn = myConnConfig(r->connection);
+ SSL *ssl = sslconn ? sslconn->ssl : NULL;
+ apr_array_header_t *requires;
+ ssl_require_t *ssl_requires;
+ int ok, i, ret;
+
+ /* On a slave connection, we do not expect to have an SSLConnRec, but
+ * our master connection might have one. */
+ if (!(sslconn && ssl) && r->connection->master) {
+ sslconn = myConnConfig(r->connection->master);
+ ssl = sslconn ? sslconn->ssl : NULL;
+ }
+
+ /*
+ * We should have handshaken here, otherwise we are being
+ * redirected (ErrorDocument) from a renegotiation failure below.
+ * The access is still forbidden in the latter case, let ap_die() handle
+ * this recursive (same) error.
+ */
+ if (ssl && !SSL_is_init_finished(ssl)) {
+ return HTTP_FORBIDDEN;
+ }
+
+ /*
+ * Support for SSLRequireSSL directive
+ */
+ if (dc->bSSLRequired && !ssl) {
+ if ((sc->enabled == SSL_ENABLED_OPTIONAL) && !r->connection->master) {
+ /* This vhost was configured for optional SSL, just tell the
+ * client that we need to upgrade.
+ */
+ apr_table_setn(r->err_headers_out, "Upgrade", "TLS/1.0, HTTP/1.1");
+ apr_table_setn(r->err_headers_out, "Connection", "Upgrade");
+
+ return HTTP_UPGRADE_REQUIRED;
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02219)
+ "access to %s failed, reason: %s",
+ r->filename, "SSL connection required");
+
+ /* remember forbidden access for strict require option */
+ apr_table_setn(r->notes, "ssl-access-forbidden", "1");
+
+ return HTTP_FORBIDDEN;
+ }
+
+ /*
+ * Check to see whether SSL is in use; if it's not, then no
+ * further access control checks are relevant. (the test for
+ * sc->enabled is probably strictly unnecessary)
+ */
+ if (sc->enabled == SSL_ENABLED_FALSE || !ssl) {
+ return DECLINED;
+ }
+
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+ /* TLSv1.3+ is less complicated here. Branch off into a new codeline
+ * and avoid messing with the past. */
+ if (SSL_version(ssl) >= TLS1_3_VERSION) {
+ ret = ssl_hook_Access_modern(r, sc, dc, sslconn, ssl);
+ }
+ else
+#endif
+ {
+ ret = ssl_hook_Access_classic(r, sc, dc, sslconn, ssl);
+ }
+
+ if (ret != DECLINED) {
+ return ret;
+ }
+
+ /* If we're trying to have the user name set from a client
+ * certificate then we need to set it here. This should be safe as
+ * the user name probably isn't important from an auth checking point
+ * of view as the certificate supplied acts in that capacity.
+ * However, if FakeAuth is being used then this isn't the case so
+ * we need to postpone setting the username until later.
+ */
+ if ((dc->nOptions & SSL_OPT_FAKEBASICAUTH) == 0 && dc->szUserName) {
+ char *val = ssl_var_lookup(r->pool, r->server, r->connection,
+ r, (char *)dc->szUserName);
+ if (val && val[0])
+ r->user = val;
+ else
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02227)
+ "Failed to set r->user to '%s'", dc->szUserName);
+ }
+
+ /*
+ * Check SSLRequire boolean expressions
+ */
+ requires = dc->aRequirement;
+ ssl_requires = (ssl_require_t *)requires->elts;
+
+ for (i = 0; i < requires->nelts; i++) {
+ ssl_require_t *req = &ssl_requires[i];
+ const char *errstring;
+ ok = ap_expr_exec(r, req->mpExpr, &errstring);
+
+ if (ok < 0) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02265)
+ "access to %s failed, reason: Failed to execute "
+ "SSL requirement expression: %s",
+ r->filename, errstring);
+
+ /* remember forbidden access for strict require option */
+ apr_table_setn(r->notes, "ssl-access-forbidden", "1");
+
+ return HTTP_FORBIDDEN;
+ }
+
+ if (ok != 1) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02266)
+ "Access to %s denied for %s "
+ "(requirement expression not fulfilled)",
+ r->filename, r->useragent_ip);
+
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02228)
+ "Failed expression: %s", req->cpExpr);
+
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02229)
+ "access to %s failed, reason: %s",
+ r->filename,
+ "SSL requirement expression not fulfilled");
+
+ /* remember forbidden access for strict require option */
+ apr_table_setn(r->notes, "ssl-access-forbidden", "1");
+
+ return HTTP_FORBIDDEN;
+ }
+ }
+
+ /*
+ * Else access is granted from our point of view (except vendor
+ * handlers override). But we have to return DECLINED here instead
+ * of OK, because mod_auth and other modules still might want to
+ * deny access.
+ */
+
+ return DECLINED;
+}
+
+/*
+ * Authentication Handler:
+ * Fake a Basic authentication from the X509 client certificate.
+ *
+ * This must be run fairly early on to prevent a real authentication from
+ * occurring, in particular it must be run before anything else that
+ * authenticates a user. This means that the Module statement for this
+ * module should be LAST in the Configuration file.
+ */
+int ssl_hook_UserCheck(request_rec *r)
+{
+ SSLConnRec *sslconn;
+ SSLDirConfigRec *dc = myDirConfig(r);
+ char *clientdn;
+ const char *auth_line, *username, *password;
+
+ /*
+ * Additionally forbid access (again)
+ * when strict require option is used.
+ */
+ if ((dc->nOptions & SSL_OPT_STRICTREQUIRE) &&
+ (apr_table_get(r->notes, "ssl-access-forbidden")))
+ {
+ return HTTP_FORBIDDEN;
+ }
+
+ /*
+ * We decline when we are in a subrequest. The Authorization header
+ * would already be present if it was added in the main request.
+ */
+ if (!ap_is_initial_req(r)) {
+ return DECLINED;
+ }
+
+ /*
+ * Make sure the user is not able to fake the client certificate
+ * based authentication by just entering an X.509 Subject DN
+ * ("/XX=YYY/XX=YYY/..") as the username and "password" as the
+ * password.
+ */
+ if ((auth_line = apr_table_get(r->headers_in, "Authorization"))) {
+ if (strcEQ(ap_getword(r->pool, &auth_line, ' '), "Basic")) {
+ while ((*auth_line == ' ') || (*auth_line == '\t')) {
+ auth_line++;
+ }
+
+ auth_line = ap_pbase64decode(r->pool, auth_line);
+ username = ap_getword_nulls(r->pool, &auth_line, ':');
+ password = auth_line;
+
+ if ((username[0] == '/') && strEQ(password, "password")) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02035)
+ "Encountered FakeBasicAuth spoof: %s", username);
+ return HTTP_FORBIDDEN;
+ }
+ }
+ }
+
+ /*
+ * We decline operation in various situations...
+ * - TLS not enabled
+ * - client did not present a certificate
+ * - SSLOptions +FakeBasicAuth not configured
+ * - r->user already authenticated
+ */
+ if (!modssl_request_is_tls(r, &sslconn)
+ || !sslconn->client_cert
+ || !(dc->nOptions & SSL_OPT_FAKEBASICAUTH)
+ || r->user) {
+ return DECLINED;
+ }
+
+ if (!sslconn->client_dn) {
+ X509_NAME *name = X509_get_subject_name(sslconn->client_cert);
+ char *cp = X509_NAME_oneline(name, NULL, 0);
+ sslconn->client_dn = apr_pstrdup(r->connection->pool, cp);
+ OPENSSL_free(cp);
+ }
+
+ clientdn = (char *)sslconn->client_dn;
+
+ /*
+ * Fake a password - which one would be immaterial, as, it seems, an empty
+ * password in the users file would match ALL incoming passwords, if only
+ * we were using the standard crypt library routine. Unfortunately, OpenSSL
+ * "fixes" a "bug" in crypt and thus prevents blank passwords from
+ * working. (IMHO what they really fix is a bug in the users of the code
+ * - failing to program correctly for shadow passwords). We need,
+ * therefore, to provide a password. This password can be matched by
+ * adding the string "xxj31ZMTZzkVA" as the password in the user file.
+ * This is just the crypted variant of the word "password" ;-)
+ */
+ auth_line = apr_pstrcat(r->pool, "Basic ",
+ ap_pbase64encode(r->pool,
+ apr_pstrcat(r->pool, clientdn,
+ ":password", NULL)),
+ NULL);
+ apr_table_setn(r->headers_in, "Authorization", auth_line);
+
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02036)
+ "Faking HTTP Basic Auth header: \"Authorization: %s\"",
+ auth_line);
+
+ return DECLINED;
+}
+
+/* authorization phase */
+int ssl_hook_Auth(request_rec *r)
+{
+ SSLDirConfigRec *dc = myDirConfig(r);
+
+ /*
+ * Additionally forbid access (again)
+ * when strict require option is used.
+ */
+ if ((dc->nOptions & SSL_OPT_STRICTREQUIRE) &&
+ (apr_table_get(r->notes, "ssl-access-forbidden")))
+ {
+ return HTTP_FORBIDDEN;
+ }
+
+ return DECLINED;
+}
+
+/*
+ * Fixup Handler
+ */
+
+static const char *const ssl_hook_Fixup_vars[] = {
+ "SSL_VERSION_INTERFACE",
+ "SSL_VERSION_LIBRARY",
+ "SSL_PROTOCOL",
+ "SSL_SECURE_RENEG",
+ "SSL_COMPRESS_METHOD",
+ "SSL_CIPHER",
+ "SSL_CIPHER_EXPORT",
+ "SSL_CIPHER_USEKEYSIZE",
+ "SSL_CIPHER_ALGKEYSIZE",
+ "SSL_CLIENT_VERIFY",
+ "SSL_CLIENT_M_VERSION",
+ "SSL_CLIENT_M_SERIAL",
+ "SSL_CLIENT_V_START",
+ "SSL_CLIENT_V_END",
+ "SSL_CLIENT_V_REMAIN",
+ "SSL_CLIENT_S_DN",
+ "SSL_CLIENT_I_DN",
+ "SSL_CLIENT_A_KEY",
+ "SSL_CLIENT_A_SIG",
+ "SSL_CLIENT_CERT_RFC4523_CEA",
+ "SSL_SERVER_M_VERSION",
+ "SSL_SERVER_M_SERIAL",
+ "SSL_SERVER_V_START",
+ "SSL_SERVER_V_END",
+ "SSL_SERVER_S_DN",
+ "SSL_SERVER_I_DN",
+ "SSL_SERVER_A_KEY",
+ "SSL_SERVER_A_SIG",
+ "SSL_SESSION_ID",
+ "SSL_SESSION_RESUMED",
+#ifdef HAVE_SRP
+ "SSL_SRP_USER",
+ "SSL_SRP_USERINFO",
+#endif
+ NULL
+};
+
+int ssl_hook_Fixup(request_rec *r)
+{
+ SSLDirConfigRec *dc = myDirConfig(r);
+ apr_table_t *env = r->subprocess_env;
+ char *var, *val = "";
+#ifdef HAVE_TLSEXT
+ const char *servername;
+#endif
+ STACK_OF(X509) *peer_certs;
+ SSLConnRec *sslconn;
+ SSL *ssl;
+ int i;
+
+ if (!modssl_request_is_tls(r, &sslconn)) {
+ return DECLINED;
+ }
+ ssl = sslconn->ssl;
+
+ /*
+ * Annotate the SSI/CGI environment with standard SSL information
+ */
+ /* the always present HTTPS (=HTTP over SSL) flag! */
+ apr_table_setn(env, "HTTPS", "on");
+
+#ifdef HAVE_TLSEXT
+ /* add content of SNI TLS extension (if supplied with ClientHello) */
+ if ((servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) {
+ apr_table_set(env, "SSL_TLS_SNI", servername);
+ }
+#endif
+
+ /* standard SSL environment variables */
+ if (dc->nOptions & SSL_OPT_STDENVVARS) {
+ modssl_var_extract_dns(env, ssl, r->pool);
+ modssl_var_extract_san_entries(env, ssl, r->pool);
+
+ for (i = 0; ssl_hook_Fixup_vars[i]; i++) {
+ var = (char *)ssl_hook_Fixup_vars[i];
+ val = ssl_var_lookup(r->pool, r->server, r->connection, r, var);
+ if (!strIsEmpty(val)) {
+ apr_table_setn(env, var, val);
+ }
+ }
+ }
+
+ /*
+ * On-demand bloat up the SSI/CGI environment with certificate data
+ */
+ if (dc->nOptions & SSL_OPT_EXPORTCERTDATA) {
+ val = ssl_var_lookup(r->pool, r->server, r->connection,
+ r, "SSL_SERVER_CERT");
+
+ apr_table_setn(env, "SSL_SERVER_CERT", val);
+
+ val = ssl_var_lookup(r->pool, r->server, r->connection,
+ r, "SSL_CLIENT_CERT");
+
+ apr_table_setn(env, "SSL_CLIENT_CERT", val);
+
+ if ((peer_certs = (STACK_OF(X509) *)SSL_get_peer_cert_chain(ssl))) {
+ for (i = 0; i < sk_X509_num(peer_certs); i++) {
+ var = apr_psprintf(r->pool, "SSL_CLIENT_CERT_CHAIN_%d", i);
+ val = ssl_var_lookup(r->pool, r->server, r->connection,
+ r, var);
+ if (val) {
+ apr_table_setn(env, var, val);
+ }
+ }
+ }
+ }
+
+
+#ifdef SSL_get_secure_renegotiation_support
+ apr_table_setn(r->notes, "ssl-secure-reneg",
+ SSL_get_secure_renegotiation_support(ssl) ? "1" : "0");
+#endif
+
+ return DECLINED;
+}
+
+/* _________________________________________________________________
+**
+** Authz providers for use with mod_authz_core
+** _________________________________________________________________
+*/
+
+static authz_status ssl_authz_require_ssl_check(request_rec *r,
+ const char *require_line,
+ const void *parsed)
+{
+ if (modssl_request_is_tls(r, NULL))
+ return AUTHZ_GRANTED;
+ else
+ return AUTHZ_DENIED;
+}
+
+static const char *ssl_authz_require_ssl_parse(cmd_parms *cmd,
+ const char *require_line,
+ const void **parsed)
+{
+ if (require_line && require_line[0])
+ return "'Require ssl' does not take arguments";
+
+ return NULL;
+}
+
+const authz_provider ssl_authz_provider_require_ssl =
+{
+ &ssl_authz_require_ssl_check,
+ &ssl_authz_require_ssl_parse,
+};
+
+static authz_status ssl_authz_verify_client_check(request_rec *r,
+ const char *require_line,
+ const void *parsed)
+{
+ SSLConnRec *sslconn = myConnConfig(r->connection);
+ SSL *ssl = sslconn ? sslconn->ssl : NULL;
+
+ if (!ssl)
+ return AUTHZ_DENIED;
+
+ if (sslconn->verify_error == NULL &&
+ sslconn->verify_info == NULL &&
+ SSL_get_verify_result(ssl) == X509_V_OK)
+ {
+ X509 *xs = SSL_get_peer_certificate(ssl);
+
+ if (xs) {
+ X509_free(xs);
+ return AUTHZ_GRANTED;
+ }
+ else {
+ X509_free(xs);
+ }
+ }
+
+ return AUTHZ_DENIED;
+}
+
+static const char *ssl_authz_verify_client_parse(cmd_parms *cmd,
+ const char *require_line,
+ const void **parsed)
+{
+ if (require_line && require_line[0])
+ return "'Require ssl-verify-client' does not take arguments";
+
+ return NULL;
+}
+
+const authz_provider ssl_authz_provider_verify_client =
+{
+ &ssl_authz_verify_client_check,
+ &ssl_authz_verify_client_parse,
+};
+
+
+
+/* _________________________________________________________________
+**
+** OpenSSL Callback Functions
+** _________________________________________________________________
+*/
+
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
+/*
+ * Hand out standard DH parameters, based on the authentication strength
+ */
+DH *ssl_callback_TmpDH(SSL *ssl, int export, int keylen)
+{
+ conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
+ EVP_PKEY *pkey;
+ int type;
+
+#ifdef SSL_CERT_SET_SERVER
+ /*
+ * When multiple certs/keys are configured for the SSL_CTX: make sure
+ * that we get the private key which is indeed used for the current
+ * SSL connection (available in OpenSSL 1.0.2 or later only)
+ */
+ SSL_set_current_cert(ssl, SSL_CERT_SET_SERVER);
+#endif
+ pkey = SSL_get_privatekey(ssl);
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ type = pkey ? EVP_PKEY_type(pkey->type) : EVP_PKEY_NONE;
+#else
+ type = pkey ? EVP_PKEY_base_id(pkey) : EVP_PKEY_NONE;
+#endif
+
+ /*
+ * OpenSSL will call us with either keylen == 512 or keylen == 1024
+ * (see the definition of SSL_EXPORT_PKEYLENGTH in ssl_locl.h).
+ * Adjust the DH parameter length according to the size of the
+ * RSA/DSA private key used for the current connection, and always
+ * use at least 1024-bit parameters.
+ * Note: This may cause interoperability issues with implementations
+ * which limit their DH support to 1024 bit - e.g. Java 7 and earlier.
+ * In this case, SSLCertificateFile can be used to specify fixed
+ * 1024-bit DH parameters (with the effect that OpenSSL skips this
+ * callback).
+ */
+ if ((type == EVP_PKEY_RSA) || (type == EVP_PKEY_DSA)) {
+ keylen = EVP_PKEY_bits(pkey);
+ }
+
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
+ "handing out built-in DH parameters for %d-bit authenticated connection", keylen);
+
+ return modssl_get_dh_params(keylen);
+}
+#endif
+
+/*
+ * This OpenSSL callback function is called when OpenSSL
+ * does client authentication and verifies the certificate chain.
+ */
+int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
+{
+ /* Get Apache context back through OpenSSL context */
+ SSL *ssl = X509_STORE_CTX_get_ex_data(ctx,
+ SSL_get_ex_data_X509_STORE_CTX_idx());
+ conn_rec *conn = (conn_rec *)SSL_get_app_data(ssl);
+ request_rec *r = (request_rec *)modssl_get_app_data2(ssl);
+ server_rec *s = r ? r->server : mySrvFromConn(conn);
+
+ SSLSrvConfigRec *sc = mySrvConfig(s);
+ SSLConnRec *sslconn = myConnConfig(conn);
+ SSLDirConfigRec *dc = r ? myDirConfig(r) : sslconn->dc;
+ modssl_ctx_t *mctx = myConnCtxConfig(conn, sc);
+ int crl_check_mode = mctx->crl_check_mask & ~SSL_CRLCHECK_FLAGS;
+
+ /* Get verify ingredients */
+ int errnum = X509_STORE_CTX_get_error(ctx);
+ int errdepth = X509_STORE_CTX_get_error_depth(ctx);
+ int depth = UNSET;
+ int verify = SSL_CVERIFY_UNSET;
+
+ /*
+ * Log verification information
+ */
+ ssl_log_cxerror(SSLLOG_MARK, APLOG_DEBUG, 0, conn,
+ X509_STORE_CTX_get_current_cert(ctx), APLOGNO(02275)
+ "Certificate Verification, depth %d, "
+ "CRL checking mode: %s (%x)", errdepth,
+ crl_check_mode == SSL_CRLCHECK_CHAIN ? "chain" :
+ crl_check_mode == SSL_CRLCHECK_LEAF ? "leaf" : "none",
+ mctx->crl_check_mask);
+
+ /*
+ * Check for optionally acceptable non-verifiable issuer situation
+ */
+ if (dc) {
+ if (conn->outgoing) {
+ verify = dc->proxy->auth.verify_mode;
+ }
+ else {
+ verify = dc->nVerifyClient;
+ }
+ }
+ if (!dc || (verify == SSL_CVERIFY_UNSET)) {
+ verify = mctx->auth.verify_mode;
+ }
+
+ if (verify == SSL_CVERIFY_NONE) {
+ /*
+ * SSLProxyVerify is either not configured or set to "none".
+ * (this callback doesn't happen in the server context if SSLVerify
+ * is not configured or set to "none")
+ */
+ return TRUE;
+ }
+
+ if (ssl_verify_error_is_optional(errnum) &&
+ (verify == SSL_CVERIFY_OPTIONAL_NO_CA))
+ {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, conn, APLOGNO(02037)
+ "Certificate Verification: Verifiable Issuer is "
+ "configured as optional, therefore we're accepting "
+ "the certificate");
+
+ sslconn->verify_info = "GENEROUS";
+ ok = TRUE;
+ }
+
+ /*
+ * Expired certificates vs. "expired" CRLs: by default, OpenSSL
+ * turns X509_V_ERR_CRL_HAS_EXPIRED into a "certificate_expired(45)"
+ * SSL alert, but that's not really the message we should convey to the
+ * peer (at the very least, it's confusing, and in many cases, it's also
+ * inaccurate, as the certificate itself may very well not have expired
+ * yet). We set the X509_STORE_CTX error to something which OpenSSL's
+ * s3_both.c:ssl_verify_alarm_type() maps to SSL_AD_CERTIFICATE_UNKNOWN,
+ * i.e. the peer will receive a "certificate_unknown(46)" alert.
+ * We do not touch errnum, though, so that later on we will still log
+ * the "real" error, as returned by OpenSSL.
+ */
+ if (!ok && errnum == X509_V_ERR_CRL_HAS_EXPIRED) {
+ X509_STORE_CTX_set_error(ctx, -1);
+ }
+
+ if (!ok && errnum == X509_V_ERR_UNABLE_TO_GET_CRL
+ && (mctx->crl_check_mask & SSL_CRLCHECK_NO_CRL_FOR_CERT_OK)) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, conn,
+ "Certificate Verification: Temporary error (%d): %s: "
+ "optional therefore we're accepting the certificate",
+ errnum, X509_verify_cert_error_string(errnum));
+ X509_STORE_CTX_set_error(ctx, X509_V_OK);
+ errnum = X509_V_OK;
+ ok = TRUE;
+ }
+
+#ifndef OPENSSL_NO_OCSP
+ /*
+ * Perform OCSP-based revocation checks
+ */
+ if (ok && ((mctx->ocsp_mask & SSL_OCSPCHECK_CHAIN) ||
+ (errdepth == 0 && (mctx->ocsp_mask & SSL_OCSPCHECK_LEAF)))) {
+ /* If there was an optional verification error, it's not
+ * possible to perform OCSP validation since the issuer may be
+ * missing/untrusted. Fail in that case. */
+ if (ssl_verify_error_is_optional(errnum)) {
+ X509_STORE_CTX_set_error(ctx, X509_V_ERR_APPLICATION_VERIFICATION);
+ errnum = X509_V_ERR_APPLICATION_VERIFICATION;
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, conn, APLOGNO(02038)
+ "cannot perform OCSP validation for cert "
+ "if issuer has not been verified "
+ "(optional_no_ca configured)");
+ ok = FALSE;
+ } else {
+ ok = modssl_verify_ocsp(ctx, sc, s, conn, conn->pool);
+ if (!ok) {
+ errnum = X509_STORE_CTX_get_error(ctx);
+ }
+ }
+ }
+#endif
+
+ /*
+ * If we already know it's not ok, log the real reason
+ */
+ if (!ok) {
+ if (APLOGcinfo(conn)) {
+ ssl_log_cxerror(SSLLOG_MARK, APLOG_INFO, 0, conn,
+ X509_STORE_CTX_get_current_cert(ctx), APLOGNO(02276)
+ "Certificate Verification: Error (%d): %s",
+ errnum, X509_verify_cert_error_string(errnum));
+ } else {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, conn, APLOGNO(02039)
+ "Certificate Verification: Error (%d): %s",
+ errnum, X509_verify_cert_error_string(errnum));
+ }
+
+ if (sslconn->client_cert) {
+ X509_free(sslconn->client_cert);
+ sslconn->client_cert = NULL;
+ }
+ sslconn->client_dn = NULL;
+ sslconn->verify_error = X509_verify_cert_error_string(errnum);
+ }
+
+ /*
+ * Finally check the depth of the certificate verification
+ */
+ if (dc) {
+ if (conn->outgoing) {
+ depth = dc->proxy->auth.verify_depth;
+ }
+ else {
+ depth = dc->nVerifyDepth;
+ }
+ }
+ if (!dc || (depth == UNSET)) {
+ depth = mctx->auth.verify_depth;
+ }
+
+ if (errdepth > depth) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, conn, APLOGNO(02040)
+ "Certificate Verification: Certificate Chain too long "
+ "(chain has %d certificates, but maximum allowed are "
+ "only %d)",
+ errdepth, depth);
+
+ errnum = X509_V_ERR_CERT_CHAIN_TOO_LONG;
+ sslconn->verify_error = X509_verify_cert_error_string(errnum);
+
+ ok = FALSE;
+ }
+
+ /*
+ * And finally signal OpenSSL the (perhaps changed) state
+ */
+ return ok;
+}
+
+#define SSLPROXY_CERT_CB_LOG_FMT \
+ "Proxy client certificate callback: (%s) "
+
+static void modssl_proxy_info_log(conn_rec *c,
+ X509_INFO *info,
+ const char *msg)
+{
+ ssl_log_cxerror(SSLLOG_MARK, APLOG_DEBUG, 0, c, info->x509, APLOGNO(02277)
+ SSLPROXY_CERT_CB_LOG_FMT "%s, sending",
+ (mySrvConfigFromConn(c))->vhost_id, msg);
+}
+
+/*
+ * caller will decrement the cert and key reference
+ * so we need to increment here to prevent them from
+ * being freed.
+ */
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
+#define modssl_set_cert_info(info, cert, pkey) \
+ *cert = info->x509; \
+ CRYPTO_add(&(*cert)->references, +1, CRYPTO_LOCK_X509); \
+ *pkey = info->x_pkey->dec_pkey; \
+ CRYPTO_add(&(*pkey)->references, +1, CRYPTO_LOCK_EVP_PKEY)
+#else
+#define modssl_set_cert_info(info, cert, pkey) \
+ *cert = info->x509; \
+ X509_up_ref(*cert); \
+ *pkey = info->x_pkey->dec_pkey; \
+ EVP_PKEY_up_ref(*pkey);
+#endif
+
+int ssl_callback_proxy_cert(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
+{
+ conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
+ server_rec *s = mySrvFromConn(c);
+ SSLSrvConfigRec *sc = mySrvConfig(s);
+ SSLDirConfigRec *dc = myDirConfigFromConn(c);
+ X509_NAME *ca_name, *issuer, *ca_issuer;
+ X509_INFO *info;
+ X509 *ca_cert;
+ STACK_OF(X509_NAME) *ca_list;
+ STACK_OF(X509_INFO) *certs;
+ STACK_OF(X509) *ca_certs;
+ STACK_OF(X509) **ca_cert_chains;
+ int i, j, k;
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02267)
+ SSLPROXY_CERT_CB_LOG_FMT "entered",
+ sc->vhost_id);
+
+ certs = (dc && dc->proxy) ? dc->proxy->pkp->certs : NULL;
+ if (!certs || (sk_X509_INFO_num(certs) <= 0)) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(02268)
+ SSLPROXY_CERT_CB_LOG_FMT
+ "downstream server wanted client certificate "
+ "but none are configured", sc->vhost_id);
+ return FALSE;
+ }
+
+ ca_list = SSL_get_client_CA_list(ssl);
+
+ if (!ca_list || (sk_X509_NAME_num(ca_list) <= 0)) {
+ /*
+ * downstream server didn't send us a list of acceptable CA certs,
+ * so we send the first client cert in the list.
+ */
+ info = sk_X509_INFO_value(certs, 0);
+
+ modssl_proxy_info_log(c, info, APLOGNO(02278) "no acceptable CA list");
+
+ modssl_set_cert_info(info, x509, pkey);
+
+ return TRUE;
+ }
+
+ ca_cert_chains = dc->proxy->pkp->ca_certs;
+ for (i = 0; i < sk_X509_NAME_num(ca_list); i++) {
+ ca_name = sk_X509_NAME_value(ca_list, i);
+
+ for (j = 0; j < sk_X509_INFO_num(certs); j++) {
+ info = sk_X509_INFO_value(certs, j);
+ issuer = X509_get_issuer_name(info->x509);
+
+ /* Search certs (by issuer name) one by one*/
+ if (X509_NAME_cmp(issuer, ca_name) == 0) {
+ modssl_proxy_info_log(c, info, APLOGNO(02279)
+ "found acceptable cert");
+
+ modssl_set_cert_info(info, x509, pkey);
+
+ return TRUE;
+ }
+
+ if (ca_cert_chains) {
+ /*
+ * Failed to find direct issuer - search intermediates
+ * (by issuer name), if provided.
+ */
+ ca_certs = ca_cert_chains[j];
+ for (k = 0; k < sk_X509_num(ca_certs); k++) {
+ ca_cert = sk_X509_value(ca_certs, k);
+ ca_issuer = X509_get_issuer_name(ca_cert);
+
+ if(X509_NAME_cmp(ca_issuer, ca_name) == 0 ) {
+ modssl_proxy_info_log(c, info, APLOGNO(02280)
+ "found acceptable cert by intermediate CA");
+
+ modssl_set_cert_info(info, x509, pkey);
+
+ return TRUE;
+ }
+ } /* end loop through chained certs */
+ }
+ } /* end loop through available certs */
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02269)
+ SSLPROXY_CERT_CB_LOG_FMT
+ "no client certificate found!?", sc->vhost_id);
+
+ return FALSE;
+}
+
+static void ssl_session_log(server_rec *s,
+ const char *request,
+ IDCONST unsigned char *id,
+ unsigned int idlen,
+ const char *status,
+ const char *result,
+ long timeout)
+{
+ char buf[MODSSL_SESSION_ID_STRING_LEN];
+ char timeout_str[56] = {'\0'};
+
+ if (!APLOGdebug(s)) {
+ return;
+ }
+
+ if (timeout) {
+ apr_snprintf(timeout_str, sizeof(timeout_str),
+ "timeout=%lds ", timeout);
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, s,
+ "Inter-Process Session Cache: "
+ "request=%s status=%s id=%s %s(session %s)",
+ request, status,
+ modssl_SSL_SESSION_id2sz(id, idlen, buf, sizeof(buf)),
+ timeout_str, result);
+}
+
+/*
+ * This callback function is executed by OpenSSL whenever a new SSL_SESSION is
+ * added to the internal OpenSSL session cache. We use this hook to spread the
+ * SSL_SESSION also to the inter-process disk-cache to make share it with our
+ * other Apache pre-forked server processes.
+ */
+int ssl_callback_NewSessionCacheEntry(SSL *ssl, SSL_SESSION *session)
+{
+ /* Get Apache context back through OpenSSL context */
+ conn_rec *conn = (conn_rec *)SSL_get_app_data(ssl);
+ server_rec *s = mySrvFromConn(conn);
+ SSLSrvConfigRec *sc = mySrvConfig(s);
+ long timeout = sc->session_cache_timeout;
+ BOOL rc;
+ IDCONST unsigned char *id;
+ unsigned int idlen;
+
+ /*
+ * Set the timeout also for the internal OpenSSL cache, because this way
+ * our inter-process cache is consulted only when it's really necessary.
+ */
+ SSL_set_timeout(session, timeout);
+
+ /*
+ * Store the SSL_SESSION in the inter-process cache with the
+ * same expire time, so it expires automatically there, too.
+ */
+#ifdef OPENSSL_NO_SSL_INTERN
+ id = (unsigned char *)SSL_SESSION_get_id(session, &idlen);
+#else
+ id = session->session_id;
+ idlen = session->session_id_length;
+#endif
+
+ rc = ssl_scache_store(s, id, idlen,
+ apr_time_from_sec(SSL_SESSION_get_time(session)
+ + timeout),
+ session, conn->pool);
+
+ ssl_session_log(s, "SET", id, idlen,
+ rc == TRUE ? "OK" : "BAD",
+ "caching", timeout);
+
+ /*
+ * return 0 which means to OpenSSL that the session is still
+ * valid and was not freed by us with SSL_SESSION_free().
+ */
+ return 0;
+}
+
+/*
+ * This callback function is executed by OpenSSL whenever a
+ * SSL_SESSION is looked up in the internal OpenSSL cache and it
+ * was not found. We use this to lookup the SSL_SESSION in the
+ * inter-process disk-cache where it was perhaps stored by one
+ * of our other Apache pre-forked server processes.
+ */
+SSL_SESSION *ssl_callback_GetSessionCacheEntry(SSL *ssl,
+ IDCONST unsigned char *id,
+ int idlen, int *do_copy)
+{
+ /* Get Apache context back through OpenSSL context */
+ conn_rec *conn = (conn_rec *)SSL_get_app_data(ssl);
+ server_rec *s = mySrvFromConn(conn);
+ SSL_SESSION *session;
+
+ /*
+ * Try to retrieve the SSL_SESSION from the inter-process cache
+ */
+ session = ssl_scache_retrieve(s, id, idlen, conn->pool);
+
+ ssl_session_log(s, "GET", id, idlen,
+ session ? "FOUND" : "MISSED",
+ session ? "reuse" : "renewal", 0);
+
+ /*
+ * Return NULL or the retrieved SSL_SESSION. But indicate (by
+ * setting do_copy to 0) that the reference count on the
+ * SSL_SESSION should not be incremented by the SSL library,
+ * because we will no longer hold a reference to it ourself.
+ */
+ *do_copy = 0;
+
+ return session;
+}
+
+/*
+ * This callback function is executed by OpenSSL whenever a
+ * SSL_SESSION is removed from the internal OpenSSL cache.
+ * We use this to remove the SSL_SESSION in the inter-process
+ * disk-cache, too.
+ */
+void ssl_callback_DelSessionCacheEntry(SSL_CTX *ctx,
+ SSL_SESSION *session)
+{
+ server_rec *s;
+ SSLSrvConfigRec *sc;
+ IDCONST unsigned char *id;
+ unsigned int idlen;
+
+ /*
+ * Get Apache context back through OpenSSL context
+ */
+ if (!(s = (server_rec *)SSL_CTX_get_app_data(ctx))) {
+ return; /* on server shutdown Apache is already gone */
+ }
+
+ sc = mySrvConfig(s);
+
+ /*
+ * Remove the SSL_SESSION from the inter-process cache
+ */
+#ifdef OPENSSL_NO_SSL_INTERN
+ id = (unsigned char *)SSL_SESSION_get_id(session, &idlen);
+#else
+ id = session->session_id;
+ idlen = session->session_id_length;
+#endif
+
+ /* TODO: Do we need a temp pool here, or are we always shutting down? */
+ ssl_scache_remove(s, id, idlen, sc->mc->pPool);
+
+ ssl_session_log(s, "REM", id, idlen,
+ "OK", "dead", 0);
+
+ return;
+}
+
+/* Dump debugginfo trace to the log file. */
+static void log_tracing_state(const SSL *ssl, conn_rec *c,
+ server_rec *s, int where, int rc)
+{
+ /*
+ * create the various trace messages
+ */
+ if (where & SSL_CB_HANDSHAKE_START) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c,
+ "%s: Handshake: start", MODSSL_LIBRARY_NAME);
+ }
+ else if (where & SSL_CB_HANDSHAKE_DONE) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c,
+ "%s: Handshake: done", MODSSL_LIBRARY_NAME);
+ }
+ else if (where & SSL_CB_LOOP) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c,
+ "%s: Loop: %s",
+ MODSSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+ }
+ else if (where & SSL_CB_READ) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c,
+ "%s: Read: %s",
+ MODSSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+ }
+ else if (where & SSL_CB_WRITE) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c,
+ "%s: Write: %s",
+ MODSSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+ }
+ else if (where & SSL_CB_ALERT) {
+ char *str = (where & SSL_CB_READ) ? "read" : "write";
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c,
+ "%s: Alert: %s:%s:%s",
+ MODSSL_LIBRARY_NAME, str,
+ SSL_alert_type_string_long(rc),
+ SSL_alert_desc_string_long(rc));
+ }
+ else if (where & SSL_CB_EXIT) {
+ if (rc == 0) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c,
+ "%s: Exit: failed in %s",
+ MODSSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+ }
+ else if (rc < 0) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c,
+ "%s: Exit: error in %s",
+ MODSSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+ }
+ }
+
+ /*
+ * Because SSL renegotiations can happen at any time (not only after
+ * SSL_accept()), the best way to log the current connection details is
+ * right after a finished handshake.
+ */
+ if (where & SSL_CB_HANDSHAKE_DONE) {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02041)
+ "Protocol: %s, Cipher: %s (%s/%s bits)",
+ ssl_var_lookup(NULL, s, c, NULL, "SSL_PROTOCOL"),
+ ssl_var_lookup(NULL, s, c, NULL, "SSL_CIPHER"),
+ ssl_var_lookup(NULL, s, c, NULL, "SSL_CIPHER_USEKEYSIZE"),
+ ssl_var_lookup(NULL, s, c, NULL, "SSL_CIPHER_ALGKEYSIZE"));
+ }
+}
+
+/*
+ * This callback function is executed while OpenSSL processes the SSL
+ * handshake and does SSL record layer stuff. It's used to trap
+ * client-initiated renegotiations, and for dumping everything to the
+ * log.
+ */
+void ssl_callback_Info(const SSL *ssl, int where, int rc)
+{
+ conn_rec *c;
+ server_rec *s;
+
+ /* Retrieve the conn_rec and the associated SSLConnRec. */
+ if ((c = (conn_rec *)SSL_get_app_data((SSL *)ssl)) == NULL) {
+ return;
+ }
+
+ /* With TLS 1.3 this callback may be called multiple times on the first
+ * negotiation, so the below logic to detect renegotiations can't work.
+ * Fortunately renegotiations are forbidden starting with TLS 1.3, and
+ * this is enforced by OpenSSL so there's nothing to be done here.
+ */
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+ if (SSL_version(ssl) < TLS1_3_VERSION)
+#endif
+ {
+ SSLConnRec *sslconn;
+
+ if ((sslconn = myConnConfig(c)) == NULL) {
+ return;
+ }
+
+ /* If the reneg state is to reject renegotiations, check the SSL
+ * state machine and move to ABORT if a Client Hello is being
+ * read. */
+ if (!c->outgoing &&
+ (where & SSL_CB_HANDSHAKE_START) &&
+ sslconn->reneg_state == RENEG_REJECT) {
+ sslconn->reneg_state = RENEG_ABORT;
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02042)
+ "rejecting client initiated renegotiation");
+ }
+ /* If the first handshake is complete, change state to reject any
+ * subsequent client-initiated renegotiation. */
+ else if ((where & SSL_CB_HANDSHAKE_DONE)
+ && sslconn->reneg_state == RENEG_INIT) {
+ sslconn->reneg_state = RENEG_REJECT;
+ }
+ }
+
+ s = mySrvFromConn(c);
+ if (s && APLOGdebug(s)) {
+ log_tracing_state(ssl, c, s, where, rc);
+ }
+}
+
+#ifdef HAVE_TLSEXT
+
+static apr_status_t set_challenge_creds(conn_rec *c, const char *servername,
+ SSL *ssl, X509 *cert, EVP_PKEY *key,
+ const char *cert_pem, const char *key_pem)
+{
+ SSLConnRec *sslcon = myConnConfig(c);
+ apr_status_t rv = APR_SUCCESS;
+ int our_data = 0;
+
+ sslcon->service_unavailable = 1;
+ if (cert_pem) {
+ cert = NULL;
+ key = NULL;
+ our_data = 1;
+
+ rv = modssl_read_cert(c->pool, cert_pem, key_pem, NULL, NULL, &cert, &key);
+ if (rv != APR_SUCCESS) {
+ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10266)
+ "Failed to parse PEM of challenge certificate %s",
+ servername);
+ goto cleanup;
+ }
+ }
+
+ if ((SSL_use_certificate(ssl, cert) < 1)) {
+ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10086)
+ "Failed to configure challenge certificate %s",
+ servername);
+ rv = APR_EGENERAL;
+ goto cleanup;
+ }
+
+ if (!SSL_use_PrivateKey(ssl, key)) {
+ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10087)
+ "error '%s' using Challenge key: %s",
+ ERR_error_string(ERR_peek_last_error(), NULL),
+ servername);
+ rv = APR_EGENERAL;
+ goto cleanup;
+ }
+
+ if (SSL_check_private_key(ssl) < 1) {
+ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10088)
+ "Challenge certificate and private key %s "
+ "do not match", servername);
+ rv = APR_EGENERAL;
+ goto cleanup;
+ }
+
+cleanup:
+ if (our_data && cert) X509_free(cert);
+ if (our_data && key) EVP_PKEY_free(key);
+ return APR_SUCCESS;
+}
+
+/*
+ * This function sets the virtual host from an extended
+ * client hello with a server name indication extension ("SNI", cf. RFC 6066).
+ */
+static apr_status_t init_vhost(conn_rec *c, SSL *ssl, const char *servername)
+{
+ if (c) {
+ SSLConnRec *sslcon = myConnConfig(c);
+
+ if (sslcon->vhost_found) {
+ /* already found the vhost? */
+ return sslcon->vhost_found > 0 ? APR_SUCCESS : APR_NOTFOUND;
+ }
+ sslcon->vhost_found = -1;
+
+ if (!servername) {
+ servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+ }
+ if (servername) {
+ if (ap_vhost_iterate_given_conn(c, ssl_find_vhost,
+ (void *)servername)) {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02043)
+ "SSL virtual host for servername %s found",
+ servername);
+
+ sslcon->vhost_found = +1;
+ return APR_SUCCESS;
+ }
+ else {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02044)
+ "No matching SSL virtual host for servername "
+ "%s found (using default/first virtual host)",
+ servername);
+ /*
+ * RFC 6066 section 3 says "It is NOT RECOMMENDED to send
+ * a warning-level unrecognized_name(112) alert, because
+ * the client's behavior in response to warning-level alerts
+ * is unpredictable."
+ *
+ * To maintain backwards compatibility in mod_ssl, we
+ * no longer send any alert (neither warning- nor fatal-level),
+ * i.e. we take the second action suggested in RFC 6066:
+ * "If the server understood the ClientHello extension but
+ * does not recognize the server name, the server SHOULD take
+ * one of two actions: either abort the handshake by sending
+ * a fatal-level unrecognized_name(112) alert or continue
+ * the handshake."
+ */
+ }
+ }
+ else {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02645)
+ "Server name not provided via TLS extension "
+ "(using default/first virtual host)");
+ }
+ }
+
+ return APR_NOTFOUND;
+}
+
+/*
+ * This callback function is executed when OpenSSL encounters an extended
+ * client hello with a server name indication extension ("SNI", cf. RFC 6066).
+ */
+int ssl_callback_ServerNameIndication(SSL *ssl, int *al, modssl_ctx_t *mctx)
+{
+ conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
+ apr_status_t status = init_vhost(c, ssl, NULL);
+
+ return (status == APR_SUCCESS)? SSL_TLSEXT_ERR_OK : SSL_TLSEXT_ERR_NOACK;
+}
+
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
+/*
+ * This callback function is called when the ClientHello is received.
+ */
+int ssl_callback_ClientHello(SSL *ssl, int *al, void *arg)
+{
+ char *servername = NULL;
+ conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
+ const unsigned char *pos;
+ size_t len, remaining;
+ (void)arg;
+
+ /* We can't use SSL_get_servername() at this earliest OpenSSL connection
+ * stage, and there is no SSL_client_hello_get0_servername() provided as
+ * of OpenSSL 1.1.1. So the code below, that extracts the SNI from the
+ * ClientHello's TLS extensions, is taken from some test code in OpenSSL,
+ * i.e. client_hello_select_server_ctx() in "test/handshake_helper.c".
+ */
+
+ /*
+ * The server_name extension was given too much extensibility when it
+ * was written, so parsing the normal case is a bit complex.
+ */
+ if (!SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_server_name, &pos,
+ &remaining)
+ || remaining <= 2)
+ goto give_up;
+
+ /* Extract the length of the supplied list of names. */
+ len = (*(pos++) << 8);
+ len += *(pos++);
+ if (len + 2 != remaining)
+ goto give_up;
+ remaining = len;
+
+ /*
+ * The list in practice only has a single element, so we only consider
+ * the first one.
+ */
+ if (remaining <= 3 || *pos++ != TLSEXT_NAMETYPE_host_name)
+ goto give_up;
+ remaining--;
+
+ /* Now we can finally pull out the byte array with the actual hostname. */
+ len = (*(pos++) << 8);
+ len += *(pos++);
+ if (len + 2 != remaining)
+ goto give_up;
+
+ /* Use the SNI to switch to the relevant vhost, should it differ from
+ * c->base_server.
+ */
+ servername = apr_pstrmemdup(c->pool, (const char *)pos, len);
+
+give_up:
+ init_vhost(c, ssl, servername);
+ return SSL_CLIENT_HELLO_SUCCESS;
+}
+#endif /* OPENSSL_VERSION_NUMBER < 0x10101000L */
+
+/*
+ * Find a (name-based) SSL virtual host where either the ServerName
+ * or one of the ServerAliases matches the supplied name (to be used
+ * with ap_vhost_iterate_given_conn())
+ */
+static int ssl_find_vhost(void *servername, conn_rec *c, server_rec *s)
+{
+ SSLSrvConfigRec *sc;
+ SSL *ssl;
+ BOOL found;
+ SSLConnRec *sslcon;
+
+ found = ssl_util_vhost_matches(servername, s);
+
+ /* set SSL_CTX (if matched) */
+ sslcon = myConnConfig(c);
+ if (found && (ssl = sslcon->ssl) &&
+ (sc = mySrvConfig(s))) {
+ SSL_CTX *ctx = SSL_set_SSL_CTX(ssl, sc->server->ssl_ctx);
+
+ /*
+ * SSL_set_SSL_CTX() only deals with the server cert,
+ * so we need to duplicate a few additional settings
+ * from the ctx by hand
+ */
+ SSL_set_options(ssl, SSL_CTX_get_options(ctx));
+#if OPENSSL_VERSION_NUMBER >= 0x1010007fL \
+ && (!defined(LIBRESSL_VERSION_NUMBER) \
+ || LIBRESSL_VERSION_NUMBER >= 0x20800000L)
+ /*
+ * Don't switch the protocol if none is configured for this vhost,
+ * the default in this case is still the base server's SSLProtocol.
+ */
+ if (myConnCtxConfig(c, sc)->protocol_set) {
+ SSL_set_min_proto_version(ssl, SSL_CTX_get_min_proto_version(ctx));
+ SSL_set_max_proto_version(ssl, SSL_CTX_get_max_proto_version(ctx));
+ }
+#endif
+ if ((SSL_get_verify_mode(ssl) == SSL_VERIFY_NONE) ||
+ (SSL_num_renegotiations(ssl) == 0)) {
+ /*
+ * Only initialize the verification settings from the ctx
+ * if they are not yet set, or if we're called when a new
+ * SSL connection is set up (num_renegotiations == 0).
+ * Otherwise, we would possibly reset a per-directory
+ * configuration which was put into effect by ssl_hook_Access.
+ */
+ SSL_set_verify(ssl, SSL_CTX_get_verify_mode(ctx),
+ SSL_CTX_get_verify_callback(ctx));
+ }
+
+ /*
+ * Adjust the session id context. ssl_init_ssl_connection()
+ * always picks the configuration of the first vhost when
+ * calling SSL_new(), but we want to tie the session to the
+ * vhost we have just switched to. Again, we have to make sure
+ * that we're not overwriting a session id context which was
+ * possibly set in ssl_hook_Access(), before triggering
+ * a renegotiation.
+ */
+ if (SSL_num_renegotiations(ssl) == 0) {
+ unsigned char *sid_ctx =
+ (unsigned char *)ap_md5_binary(c->pool,
+ (unsigned char *)sc->vhost_id,
+ sc->vhost_id_len);
+ SSL_set_session_id_context(ssl, sid_ctx, APR_MD5_DIGESTSIZE*2);
+ }
+
+ /*
+ * Save the found server into our SSLConnRec for later
+ * retrieval
+ */
+ sslcon->server = s;
+ sslcon->cipher_suite = sc->server->auth.cipher_suite;
+ sslcon->service_unavailable = sc->server->pks?
+ sc->server->pks->service_unavailable : 0;
+
+ ap_update_child_status_from_server(c->sbh, SERVER_BUSY_READ, c, s);
+ /*
+ * There is one special filter callback, which is set
+ * very early depending on the base_server's log level.
+ * If this is not the first vhost we're now selecting
+ * (and the first vhost doesn't use APLOG_TRACE4), then
+ * we need to set that callback here.
+ */
+ if (APLOGtrace4(s)) {
+ BIO *rbio = SSL_get_rbio(ssl),
+ *wbio = SSL_get_wbio(ssl);
+ BIO_set_callback(rbio, ssl_io_data_cb);
+ BIO_set_callback_arg(rbio, (void *)ssl);
+ if (wbio && wbio != rbio) {
+ BIO_set_callback(wbio, ssl_io_data_cb);
+ BIO_set_callback_arg(wbio, (void *)ssl);
+ }
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+#endif /* HAVE_TLSEXT */
+
+#ifdef HAVE_TLS_SESSION_TICKETS
+/*
+ * This callback function is executed when OpenSSL needs a key for encrypting/
+ * decrypting a TLS session ticket (RFC 5077) and a ticket key file has been
+ * configured through SSLSessionTicketKeyFile.
+ */
+int ssl_callback_SessionTicket(SSL *ssl,
+ unsigned char *keyname,
+ unsigned char *iv,
+ EVP_CIPHER_CTX *cipher_ctx,
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ HMAC_CTX *hmac_ctx,
+#else
+ EVP_MAC_CTX *mac_ctx,
+#endif
+ int mode)
+{
+ conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
+ server_rec *s = mySrvFromConn(c);
+ SSLSrvConfigRec *sc = mySrvConfig(s);
+ modssl_ctx_t *mctx = myConnCtxConfig(c, sc);
+ modssl_ticket_key_t *ticket_key = mctx->ticket_key;
+
+ if (mode == 1) {
+ /*
+ * OpenSSL is asking for a key for encrypting a ticket,
+ * see s3_srvr.c:ssl3_send_newsession_ticket()
+ */
+
+ if (ticket_key == NULL) {
+ /* should never happen, but better safe than sorry */
+ return -1;
+ }
+
+ memcpy(keyname, ticket_key->key_name, 16);
+ if (RAND_bytes(iv, EVP_MAX_IV_LENGTH) != 1) {
+ return -1;
+ }
+ EVP_EncryptInit_ex(cipher_ctx, EVP_aes_128_cbc(), NULL,
+ ticket_key->aes_key, iv);
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ HMAC_Init_ex(hmac_ctx, ticket_key->hmac_secret, 16,
+ tlsext_tick_md(), NULL);
+#else
+ EVP_MAC_CTX_set_params(mac_ctx, ticket_key->mac_params);
+#endif
+
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02289)
+ "TLS session ticket key for %s successfully set, "
+ "creating new session ticket", sc->vhost_id);
+
+ return 1;
+ }
+ else if (mode == 0) {
+ /*
+ * OpenSSL is asking for the decryption key,
+ * see t1_lib.c:tls_decrypt_ticket()
+ */
+
+ /* check key name */
+ if (ticket_key == NULL || memcmp(keyname, ticket_key->key_name, 16)) {
+ return 0;
+ }
+
+ EVP_DecryptInit_ex(cipher_ctx, EVP_aes_128_cbc(), NULL,
+ ticket_key->aes_key, iv);
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ HMAC_Init_ex(hmac_ctx, ticket_key->hmac_secret, 16,
+ tlsext_tick_md(), NULL);
+#else
+ EVP_MAC_CTX_set_params(mac_ctx, ticket_key->mac_params);
+#endif
+
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02290)
+ "TLS session ticket key for %s successfully set, "
+ "decrypting existing session ticket", sc->vhost_id);
+
+ return 1;
+ }
+
+ /* OpenSSL is not expected to call us with modes other than 1 or 0 */
+ return -1;
+}
+#endif /* HAVE_TLS_SESSION_TICKETS */
+
+#ifdef HAVE_TLS_ALPN
+
+/*
+ * This callback function is executed when the TLS Application-Layer
+ * Protocol Negotiation Extension (ALPN, RFC 7301) is triggered by the Client
+ * Hello, giving a list of desired protocol names (in descending preference)
+ * to the server.
+ * The callback has to select a protocol name or return an error if none of
+ * the clients preferences is supported.
+ * The selected protocol does not have to be on the client list, according
+ * to RFC 7301, so no checks are performed.
+ * The client protocol list is serialized as length byte followed by ASCII
+ * characters (not null-terminated), followed by the next protocol name.
+ */
+int ssl_callback_alpn_select(SSL *ssl,
+ const unsigned char **out, unsigned char *outlen,
+ const unsigned char *in, unsigned int inlen,
+ void *arg)
+{
+ conn_rec *c = (conn_rec*)SSL_get_app_data(ssl);
+ SSLConnRec *sslconn;
+ apr_array_header_t *client_protos;
+ const char *proposed;
+ size_t len;
+ int i;
+
+ /* If the connection object is not available,
+ * then there's nothing for us to do. */
+ if (c == NULL) {
+ return SSL_TLSEXT_ERR_OK;
+ }
+ sslconn = myConnConfig(c);
+
+ if (inlen == 0) {
+ /* someone tries to trick us? */
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02837)
+ "ALPN client protocol list empty");
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+
+ client_protos = apr_array_make(c->pool, 0, sizeof(char *));
+ for (i = 0; i < inlen; /**/) {
+ unsigned int plen = in[i++];
+ if (plen + i > inlen) {
+ /* someone tries to trick us? */
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02838)
+ "ALPN protocol identifier too long");
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+ APR_ARRAY_PUSH(client_protos, char *) =
+ apr_pstrndup(c->pool, (const char *)in+i, plen);
+ i += plen;
+ }
+
+ /* The order the callbacks are invoked from TLS extensions is, unfortunately
+ * not defined and older openssl versions do call ALPN selection before
+ * they callback the SNI. We need to make sure that we know which vhost
+ * we are dealing with so we respect the correct protocols.
+ */
+ init_vhost(c, ssl, NULL);
+
+ proposed = ap_select_protocol(c, NULL, sslconn->server, client_protos);
+ if (!proposed) {
+ proposed = ap_get_protocol(c);
+ }
+
+ len = strlen(proposed);
+ if (len > 255) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02840)
+ "ALPN negotiated protocol name too long");
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+ *out = (const unsigned char *)proposed;
+ *outlen = (unsigned char)len;
+
+ if (strcmp(proposed, ap_get_protocol(c))) {
+ apr_status_t status;
+
+ status = ap_switch_protocol(c, NULL, sslconn->server, proposed);
+ if (status != APR_SUCCESS) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, status, c,
+ APLOGNO(02908) "protocol switch to '%s' failed",
+ proposed);
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+
+ /* protocol was switched, this could be a challenge protocol such as "acme-tls/1".
+ * For that to work, we need to allow overrides to our ssl certificate.
+ * However, exclude challenge checks on our best known traffic protocol.
+ * (http/1.1 is the default, we never switch to it anyway.)
+ */
+ if (strcmp("h2", proposed)) {
+ const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+ X509 *cert;
+ EVP_PKEY *key;
+ const char *cert_pem, *key_pem;
+
+ if (ssl_is_challenge(c, servername, &cert, &key, &cert_pem, &key_pem)) {
+ if (set_challenge_creds(c, servername, ssl, cert, key,
+ cert_pem, key_pem) != APR_SUCCESS) {
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+ SSL_set_verify(ssl, SSL_VERIFY_NONE, ssl_callback_SSLVerify);
+ }
+ }
+ }
+
+ return SSL_TLSEXT_ERR_OK;
+}
+#endif /* HAVE_TLS_ALPN */
+
+#ifdef HAVE_SRP
+
+int ssl_callback_SRPServerParams(SSL *ssl, int *ad, void *arg)
+{
+ modssl_ctx_t *mctx = (modssl_ctx_t *)arg;
+ char *username = SSL_get_srp_username(ssl);
+ SRP_user_pwd *u;
+
+ if (username == NULL
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ || (u = SRP_VBASE_get_by_user(mctx->srp_vbase, username)) == NULL) {
+#else
+ || (u = SRP_VBASE_get1_by_user(mctx->srp_vbase, username)) == NULL) {
+#endif
+ *ad = SSL_AD_UNKNOWN_PSK_IDENTITY;
+ return SSL3_AL_FATAL;
+ }
+
+ if (SSL_set_srp_server_param(ssl, u->N, u->g, u->s, u->v, u->info) < 0) {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ SRP_user_pwd_free(u);
+#endif
+ *ad = SSL_AD_INTERNAL_ERROR;
+ return SSL3_AL_FATAL;
+ }
+
+ /* reset all other options */
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ SRP_user_pwd_free(u);
+#endif
+ SSL_set_verify(ssl, SSL_VERIFY_NONE, ssl_callback_SSLVerify);
+ return SSL_ERROR_NONE;
+}
+
+#endif /* HAVE_SRP */
+
+
+#ifdef HAVE_OPENSSL_KEYLOG
+/* Callback used with SSL_CTX_set_keylog_callback. */
+void modssl_callback_keylog(const SSL *ssl, const char *line)
+{
+ conn_rec *conn = SSL_get_app_data(ssl);
+ SSLSrvConfigRec *sc = mySrvConfig(conn->base_server);
+
+ if (sc && sc->mc->keylog_file) {
+ apr_file_printf(sc->mc->keylog_file, "%s\n", line);
+ }
+}
+#endif
diff --git a/modules/ssl/ssl_engine_log.c b/modules/ssl/ssl_engine_log.c
new file mode 100644
index 0000000..3b3ceac
--- /dev/null
+++ b/modules/ssl/ssl_engine_log.c
@@ -0,0 +1,246 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+/* _ _
+ * _ __ ___ ___ __| | ___ ___| | mod_ssl
+ * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| | \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ * |_____|
+ * ssl_engine_log.c
+ * Logging Facility
+ */
+ /* ``The difference between a computer
+ industry job and open-source software
+ hacking is about 30 hours a week.''
+ -- Ralf S. Engelschall */
+#include "ssl_private.h"
+
+/* _________________________________________________________________
+**
+** Logfile Support
+** _________________________________________________________________
+*/
+
+static const struct {
+ const char *cpPattern;
+ const char *cpAnnotation;
+} ssl_log_annotate[] = {
+ { "*envelope*bad*decrypt*", "wrong pass phrase!?" },
+ { "*CLIENT_HELLO*unknown*protocol*", "speaking not SSL to HTTPS port!?" },
+ { "*CLIENT_HELLO*http*request*", "speaking HTTP to HTTPS port!?" },
+ { "*SSL3_READ_BYTES:sslv3*alert*bad*certificate*", "Subject CN in certificate not server name or identical to CA!?" },
+ { "*self signed certificate in certificate chain*", "Client certificate signed by CA not known to server?" },
+ { "*peer did not return a certificate*", "No CAs known to server for verification?" },
+ { "*no shared cipher*", "Too restrictive SSLCipherSuite or using DSA server certificate?" },
+ { "*no start line*", "Bad file contents or format - or even just a forgotten SSLCertificateKeyFile?" },
+ { "*bad password read*", "You entered an incorrect pass phrase!?" },
+ { "*bad mac decode*", "Browser still remembered details of a re-created server certificate?" },
+ { NULL, NULL }
+};
+
+static const char *ssl_log_annotation(const char *error)
+{
+ int i = 0;
+
+ while (ssl_log_annotate[i].cpPattern != NULL
+ && ap_strcmp_match(error, ssl_log_annotate[i].cpPattern) != 0)
+ i++;
+
+ return ssl_log_annotate[i].cpAnnotation;
+}
+
+apr_status_t ssl_die(server_rec *s)
+{
+ if (s != NULL && s->is_virtual && s->error_fname != NULL)
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, NULL, APLOGNO(02311)
+ "Fatal error initialising mod_ssl, exiting. "
+ "See %s for more information",
+ ap_server_root_relative(s->process->pool,
+ s->error_fname));
+ else
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, NULL, APLOGNO(02312)
+ "Fatal error initialising mod_ssl, exiting.");
+
+ return APR_EGENERAL;
+}
+
+static APR_INLINE
+unsigned long modssl_ERR_peek_error_data(const char **data, int *flags)
+{
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ return ERR_peek_error_line_data(NULL, NULL, data, flags);
+#else
+ return ERR_peek_error_data(data, flags);
+#endif
+}
+
+/*
+ * Prints the SSL library error information.
+ */
+void ssl_log_ssl_error(const char *file, int line, int level, server_rec *s)
+{
+ unsigned long e;
+ const char *data;
+ int flags;
+
+ while ((e = modssl_ERR_peek_error_data(&data, &flags))) {
+ const char *annotation;
+ char err[256];
+
+ if (!(flags & ERR_TXT_STRING)) {
+ data = NULL;
+ }
+
+ ERR_error_string_n(e, err, sizeof err);
+ annotation = ssl_log_annotation(err);
+
+ ap_log_error(file, line, APLOG_MODULE_INDEX, level, 0, s,
+ "SSL Library Error: %s%s%s%s%s%s",
+ /* %s */
+ err,
+ /* %s%s%s */
+ data ? " (" : "", data ? data : "", data ? ")" : "",
+ /* %s%s */
+ annotation ? " -- " : "",
+ annotation ? annotation : "");
+
+ /* Pop the error off the stack: */
+ ERR_get_error();
+ }
+}
+
+static void ssl_log_cert_error(const char *file, int line, int level,
+ apr_status_t rv, const server_rec *s,
+ const conn_rec *c, const request_rec *r,
+ apr_pool_t *p, X509 *cert, const char *format,
+ va_list ap)
+{
+ char buf[HUGE_STRING_LEN];
+ int msglen, n;
+ char *name;
+
+ msglen = apr_vsnprintf(buf, sizeof buf, format, ap);
+
+ if (cert) {
+ BIO *bio = BIO_new(BIO_s_mem());
+
+ if (bio) {
+ /*
+ * Limit the maximum length of the subject and issuer DN strings
+ * in the log message. 300 characters should always be sufficient
+ * for holding both the timestamp, module name, pid etc. stuff
+ * at the beginning of the line and the trailing information about
+ * serial, notbefore and notafter.
+ */
+ int maxdnlen = (HUGE_STRING_LEN - msglen - 300) / 2;
+
+ BIO_puts(bio, " [subject: ");
+ name = modssl_X509_NAME_to_string(p, X509_get_subject_name(cert),
+ maxdnlen);
+ if (!strIsEmpty(name)) {
+ BIO_puts(bio, name);
+ } else {
+ BIO_puts(bio, "-empty-");
+ }
+
+ BIO_puts(bio, " / issuer: ");
+ name = modssl_X509_NAME_to_string(p, X509_get_issuer_name(cert),
+ maxdnlen);
+ if (!strIsEmpty(name)) {
+ BIO_puts(bio, name);
+ } else {
+ BIO_puts(bio, "-empty-");
+ }
+
+ BIO_puts(bio, " / serial: ");
+ if (i2a_ASN1_INTEGER(bio, X509_get_serialNumber(cert)) == -1)
+ BIO_puts(bio, "(ERROR)");
+
+ BIO_puts(bio, " / notbefore: ");
+ ASN1_TIME_print(bio, X509_get_notBefore(cert));
+
+ BIO_puts(bio, " / notafter: ");
+ ASN1_TIME_print(bio, X509_get_notAfter(cert));
+
+ BIO_puts(bio, "]");
+
+ n = BIO_read(bio, buf + msglen, sizeof buf - msglen - 1);
+ if (n > 0)
+ buf[msglen + n] = '\0';
+
+ BIO_free(bio);
+ }
+ }
+ else {
+ apr_snprintf(buf + msglen, sizeof buf - msglen,
+ " [certificate: -not available-]");
+ }
+
+ if (r) {
+ ap_log_rerror(file, line, APLOG_MODULE_INDEX, level, rv, r, "%s", buf);
+ }
+ else if (c) {
+ ap_log_cerror(file, line, APLOG_MODULE_INDEX, level, rv, c, "%s", buf);
+ }
+ else if (s) {
+ ap_log_error(file, line, APLOG_MODULE_INDEX, level, rv, s, "%s", buf);
+ }
+
+}
+
+/*
+ * Wrappers for ap_log_error/ap_log_cerror/ap_log_rerror which log additional
+ * details of the X509 cert. For ssl_log_xerror, a pool needs to be passed in
+ * as well (for temporary allocation of the cert's subject/issuer name strings,
+ * in the other cases we use the connection and request pool, respectively).
+ */
+void ssl_log_xerror(const char *file, int line, int level, apr_status_t rv,
+ apr_pool_t *ptemp, server_rec *s, X509 *cert,
+ const char *fmt, ...)
+{
+ if (APLOG_IS_LEVEL(s,level)) {
+ va_list ap;
+ va_start(ap, fmt);
+ ssl_log_cert_error(file, line, level, rv, s, NULL, NULL, ptemp,
+ cert, fmt, ap);
+ va_end(ap);
+ }
+}
+
+void ssl_log_cxerror(const char *file, int line, int level, apr_status_t rv,
+ conn_rec *c, X509 *cert, const char *fmt, ...)
+{
+ if (APLOG_IS_LEVEL(mySrvFromConn(c),level)) {
+ va_list ap;
+ va_start(ap, fmt);
+ ssl_log_cert_error(file, line, level, rv, NULL, c, NULL, c->pool,
+ cert, fmt, ap);
+ va_end(ap);
+ }
+}
+
+void ssl_log_rxerror(const char *file, int line, int level, apr_status_t rv,
+ request_rec *r, X509 *cert, const char *fmt, ...)
+{
+ if (APLOG_R_IS_LEVEL(r,level)) {
+ va_list ap;
+ va_start(ap, fmt);
+ ssl_log_cert_error(file, line, level, rv, NULL, NULL, r, r->pool,
+ cert, fmt, ap);
+ va_end(ap);
+ }
+}
diff --git a/modules/ssl/ssl_engine_mutex.c b/modules/ssl/ssl_engine_mutex.c
new file mode 100644
index 0000000..e915a16
--- /dev/null
+++ b/modules/ssl/ssl_engine_mutex.c
@@ -0,0 +1,111 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+/* _ _
+ * _ __ ___ ___ __| | ___ ___| | mod_ssl
+ * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| | \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ * |_____|
+ * ssl_engine_mutex.c
+ * Semaphore for Mutual Exclusion
+ */
+ /* ``Real programmers confuse
+ Christmas and Halloween
+ because DEC 25 = OCT 31.''
+ -- Unknown */
+
+#include "ssl_private.h"
+
+int ssl_mutex_init(server_rec *s, apr_pool_t *p)
+{
+ SSLModConfigRec *mc = myModConfig(s);
+ apr_status_t rv;
+
+ /* A mutex is only needed if a session cache is configured, and
+ * the provider used is not internally multi-process/thread
+ * safe. */
+ if (!mc->sesscache
+ || (mc->sesscache->flags & AP_SOCACHE_FLAG_NOTMPSAFE) == 0) {
+ return TRUE;
+ }
+
+ if (mc->pMutex) {
+ return TRUE;
+ }
+
+ if ((rv = ap_global_mutex_create(&mc->pMutex, NULL, SSL_CACHE_MUTEX_TYPE,
+ NULL, s, s->process->pool, 0))
+ != APR_SUCCESS) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+int ssl_mutex_reinit(server_rec *s, apr_pool_t *p)
+{
+ SSLModConfigRec *mc = myModConfig(s);
+ apr_status_t rv;
+ const char *lockfile;
+
+ if (mc->pMutex == NULL || !mc->sesscache
+ || (mc->sesscache->flags & AP_SOCACHE_FLAG_NOTMPSAFE) == 0) {
+ return TRUE;
+ }
+
+ lockfile = apr_global_mutex_lockfile(mc->pMutex);
+ if ((rv = apr_global_mutex_child_init(&mc->pMutex,
+ lockfile,
+ p)) != APR_SUCCESS) {
+ if (lockfile)
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(02024)
+ "Cannot reinit %s mutex with file `%s'",
+ SSL_CACHE_MUTEX_TYPE, lockfile);
+ else
+ ap_log_error(APLOG_MARK, APLOG_WARNING, rv, s, APLOGNO(02025)
+ "Cannot reinit %s mutex", SSL_CACHE_MUTEX_TYPE);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+int ssl_mutex_on(server_rec *s)
+{
+ SSLModConfigRec *mc = myModConfig(s);
+ apr_status_t rv;
+
+ if ((rv = apr_global_mutex_lock(mc->pMutex)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, rv, s, APLOGNO(02026)
+ "Failed to acquire SSL session cache lock");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+int ssl_mutex_off(server_rec *s)
+{
+ SSLModConfigRec *mc = myModConfig(s);
+ apr_status_t rv;
+
+ if ((rv = apr_global_mutex_unlock(mc->pMutex)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, rv, s, APLOGNO(02027)
+ "Failed to release SSL session cache lock");
+ return FALSE;
+ }
+ return TRUE;
+}
+
diff --git a/modules/ssl/ssl_engine_ocsp.c b/modules/ssl/ssl_engine_ocsp.c
new file mode 100644
index 0000000..5e04512
--- /dev/null
+++ b/modules/ssl/ssl_engine_ocsp.c
@@ -0,0 +1,312 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+#include "ssl_private.h"
+
+#ifndef OPENSSL_NO_OCSP
+#include "apr_base64.h"
+
+/* Return the responder URI specified in the given certificate, or
+ * NULL if none specified. */
+static const char *extract_responder_uri(X509 *cert, apr_pool_t *pool)
+{
+ STACK_OF(ACCESS_DESCRIPTION) *values;
+ char *result = NULL;
+ int j;
+
+ values = X509_get_ext_d2i(cert, NID_info_access, NULL, NULL);
+ if (!values) {
+ return NULL;
+ }
+
+ for (j = 0; j < sk_ACCESS_DESCRIPTION_num(values) && !result; j++) {
+ ACCESS_DESCRIPTION *value = sk_ACCESS_DESCRIPTION_value(values, j);
+
+ /* Name found in extension, and is a URI: */
+ if (OBJ_obj2nid(value->method) == NID_ad_OCSP
+ && value->location->type == GEN_URI) {
+ result = apr_pstrdup(pool,
+ (char *)value->location->d.uniformResourceIdentifier->data);
+ }
+ }
+
+ AUTHORITY_INFO_ACCESS_free(values);
+
+ return result;
+}
+
+/* Return the responder URI object which should be used in the given
+ * configuration for the given certificate, or NULL if none can be
+ * determined. */
+static apr_uri_t *determine_responder_uri(SSLSrvConfigRec *sc, X509 *cert,
+ conn_rec *c, apr_pool_t *p)
+{
+ apr_uri_t *u = apr_palloc(p, sizeof *u);
+ const char *s;
+ apr_status_t rv;
+
+ /* Use default responder URL if forced by configuration, else use
+ * certificate-specified responder, falling back to default if
+ * necessary and possible. */
+ if (sc->server->ocsp_force_default == TRUE) {
+ s = sc->server->ocsp_responder;
+ }
+ else {
+ s = extract_responder_uri(cert, p);
+
+ if (s == NULL && sc->server->ocsp_responder) {
+ s = sc->server->ocsp_responder;
+ }
+ }
+
+ if (s == NULL) {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01918)
+ "no OCSP responder specified in certificate and "
+ "no default configured");
+ return NULL;
+ }
+
+ rv = apr_uri_parse(p, s, u);
+ if (rv || !u->hostname) {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c, APLOGNO(01919)
+ "failed to parse OCSP responder URI '%s'", s);
+ return NULL;
+ }
+
+ if (ap_cstr_casecmp(u->scheme, "http") != 0) {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c, APLOGNO(01920)
+ "cannot handle OCSP responder URI '%s'", s);
+ return NULL;
+ }
+
+ if (!u->port) {
+ u->port = apr_uri_port_of_scheme(u->scheme);
+ }
+
+ return u;
+}
+
+/* Create an OCSP request for the given certificate; returning the
+ * certificate ID in *certid and *issuer on success. Returns the
+ * request object on success, or NULL on error. */
+static OCSP_REQUEST *create_request(X509_STORE_CTX *ctx, X509 *cert,
+ OCSP_CERTID **certid,
+ server_rec *s, apr_pool_t *p,
+ SSLSrvConfigRec *sc)
+{
+ OCSP_REQUEST *req = OCSP_REQUEST_new();
+
+ *certid = OCSP_cert_to_id(NULL, cert, X509_STORE_CTX_get0_current_issuer(ctx));
+ if (!*certid || !OCSP_request_add0_id(req, *certid)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01921)
+ "could not retrieve certificate id");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
+ return NULL;
+ }
+
+ if (sc->server->ocsp_use_request_nonce != FALSE) {
+ OCSP_request_add1_nonce(req, 0, -1);
+ }
+
+ return req;
+}
+
+/* Verify the OCSP status of given certificate. Returns
+ * V_OCSP_CERTSTATUS_* result code. */
+static int verify_ocsp_status(X509 *cert, X509_STORE_CTX *ctx, conn_rec *c,
+ SSLSrvConfigRec *sc, server_rec *s,
+ apr_pool_t *pool)
+{
+ int rc = V_OCSP_CERTSTATUS_GOOD;
+ OCSP_RESPONSE *response = NULL;
+ OCSP_BASICRESP *basicResponse = NULL;
+ OCSP_REQUEST *request = NULL;
+ OCSP_CERTID *certID = NULL;
+ apr_uri_t *ruri;
+
+ ruri = determine_responder_uri(sc, cert, c, pool);
+ if (!ruri) {
+ if (sc->server->ocsp_mask & SSL_OCSPCHECK_NO_OCSP_FOR_CERT_OK) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
+ "Skipping OCSP check for certificate cos no OCSP URL"
+ " found and no_ocsp_for_cert_ok is set");
+ return V_OCSP_CERTSTATUS_GOOD;
+ } else {
+ return V_OCSP_CERTSTATUS_UNKNOWN;
+ }
+ }
+
+ request = create_request(ctx, cert, &certID, s, pool, sc);
+ if (request) {
+ apr_interval_time_t to = sc->server->ocsp_responder_timeout == UNSET ?
+ apr_time_from_sec(DEFAULT_OCSP_TIMEOUT) :
+ sc->server->ocsp_responder_timeout;
+ response = modssl_dispatch_ocsp_request(ruri, to, request, c, pool);
+ }
+
+ if (!request || !response) {
+ rc = V_OCSP_CERTSTATUS_UNKNOWN;
+ }
+
+ if (rc == V_OCSP_CERTSTATUS_GOOD) {
+ int r = OCSP_response_status(response);
+
+ if (r != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01922)
+ "OCSP response not successful: %d", r);
+ rc = V_OCSP_CERTSTATUS_UNKNOWN;
+ }
+ }
+
+ if (rc == V_OCSP_CERTSTATUS_GOOD) {
+ basicResponse = OCSP_response_get1_basic(response);
+ if (!basicResponse) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01923)
+ "could not retrieve OCSP basic response");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
+ rc = V_OCSP_CERTSTATUS_UNKNOWN;
+ }
+ }
+
+ if (rc == V_OCSP_CERTSTATUS_GOOD &&
+ sc->server->ocsp_use_request_nonce != FALSE &&
+ OCSP_check_nonce(request, basicResponse) != 1) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01924)
+ "Bad OCSP responder answer (bad nonce)");
+ rc = V_OCSP_CERTSTATUS_UNKNOWN;
+ }
+
+ if (rc == V_OCSP_CERTSTATUS_GOOD) {
+ /* Check if OCSP certificate verification required */
+ if (sc->server->ocsp_noverify != TRUE) {
+ /* Modify OCSP response verification to include OCSP Responder cert */
+ if (OCSP_basic_verify(basicResponse, sc->server->ocsp_certs, X509_STORE_CTX_get0_store(ctx),
+ sc->server->ocsp_verify_flags) != 1) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01925)
+ "failed to verify the OCSP response");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
+ rc = V_OCSP_CERTSTATUS_UNKNOWN;
+ }
+ }
+ }
+
+ if (rc == V_OCSP_CERTSTATUS_GOOD) {
+ int reason = -1, status;
+ ASN1_GENERALIZEDTIME *thisup = NULL, *nextup = NULL;
+
+ rc = OCSP_resp_find_status(basicResponse, certID, &status,
+ &reason, NULL, &thisup, &nextup);
+ if (rc != 1) {
+ ssl_log_cxerror(SSLLOG_MARK, APLOG_ERR, 0, c, cert, APLOGNO(02272)
+ "failed to retrieve OCSP response status");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
+ rc = V_OCSP_CERTSTATUS_UNKNOWN;
+ }
+ else {
+ rc = status;
+ }
+
+ /* Check whether the response is inside the defined validity
+ * period; otherwise fail. */
+ if (rc != V_OCSP_CERTSTATUS_UNKNOWN) {
+ long resptime_skew = sc->server->ocsp_resptime_skew == UNSET ?
+ DEFAULT_OCSP_MAX_SKEW : sc->server->ocsp_resptime_skew;
+ /* oscp_resp_maxage can be passed verbatim - UNSET (-1) means
+ * that responses can be of any age as long as nextup is in the
+ * future. */
+ int vrc = OCSP_check_validity(thisup, nextup, resptime_skew,
+ sc->server->ocsp_resp_maxage);
+ if (vrc != 1) {
+ ssl_log_cxerror(SSLLOG_MARK, APLOG_ERR, 0, c, cert, APLOGNO(02273)
+ "OCSP response outside validity period");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
+ rc = V_OCSP_CERTSTATUS_UNKNOWN;
+ }
+ }
+
+ {
+ int level =
+ (status == V_OCSP_CERTSTATUS_GOOD) ? APLOG_INFO : APLOG_ERR;
+ const char *result =
+ status == V_OCSP_CERTSTATUS_GOOD ? "good" :
+ (status == V_OCSP_CERTSTATUS_REVOKED ? "revoked" : "unknown");
+
+ ssl_log_cxerror(SSLLOG_MARK, level, 0, c, cert, APLOGNO(03239)
+ "OCSP validation completed, "
+ "certificate status: %s (%d, %d)",
+ result, status, reason);
+ }
+ }
+
+ if (request) OCSP_REQUEST_free(request);
+ if (response) OCSP_RESPONSE_free(response);
+ if (basicResponse) OCSP_BASICRESP_free(basicResponse);
+ /* certID is freed when the request is freed */
+
+ return rc;
+}
+
+int modssl_verify_ocsp(X509_STORE_CTX *ctx, SSLSrvConfigRec *sc,
+ server_rec *s, conn_rec *c, apr_pool_t *pool)
+{
+ X509 *cert = X509_STORE_CTX_get_current_cert(ctx);
+ apr_pool_t *vpool;
+ int rv;
+
+ if (!cert) {
+ /* starting with OpenSSL 1.0, X509_STORE_CTX_get_current_cert()
+ * may yield NULL. Return early, but leave the ctx error as is. */
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
+ "No cert available to check with OCSP");
+ return 1;
+ }
+ else if (X509_check_issued(cert,cert) == X509_V_OK) {
+ /* don't do OCSP checking for valid self-issued certs */
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
+ "Skipping OCSP check for valid self-issued cert");
+ X509_STORE_CTX_set_error(ctx, X509_V_OK);
+ return 1;
+ }
+
+ /* Create a temporary pool to constrain memory use (the passed-in
+ * pool may be e.g. a connection pool). */
+ apr_pool_create(&vpool, pool);
+ apr_pool_tag(vpool, "modssl_verify_ocsp");
+
+ rv = verify_ocsp_status(cert, ctx, c, sc, s, vpool);
+
+ apr_pool_destroy(vpool);
+
+ /* Propagate the verification status back to the passed-in
+ * context. */
+ switch (rv) {
+ case V_OCSP_CERTSTATUS_GOOD:
+ X509_STORE_CTX_set_error(ctx, X509_V_OK);
+ break;
+
+ case V_OCSP_CERTSTATUS_REVOKED:
+ X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED);
+ break;
+
+ case V_OCSP_CERTSTATUS_UNKNOWN:
+ /* correct error code for application errors? */
+ X509_STORE_CTX_set_error(ctx, X509_V_ERR_APPLICATION_VERIFICATION);
+ break;
+ }
+
+ return rv == V_OCSP_CERTSTATUS_GOOD;
+}
+#endif /* HAVE_OCSP */
diff --git a/modules/ssl/ssl_engine_pphrase.c b/modules/ssl/ssl_engine_pphrase.c
new file mode 100644
index 0000000..d1859f7
--- /dev/null
+++ b/modules/ssl/ssl_engine_pphrase.c
@@ -0,0 +1,911 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+/* _ _
+ * _ __ ___ ___ __| | ___ ___| | mod_ssl
+ * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| | \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ * |_____|
+ * ssl_engine_pphrase.c
+ * Pass Phrase Dialog
+ */
+ /* ``Treat your password like your
+ toothbrush. Don't let anybody
+ else use it, and get a new one
+ every six months.''
+ -- Clifford Stoll */
+#include "ssl_private.h"
+
+typedef struct {
+ server_rec *s;
+ apr_pool_t *p;
+ apr_array_header_t *aPassPhrase;
+ int nPassPhraseCur;
+ char *cpPassPhraseCur;
+ int nPassPhraseDialog;
+ int nPassPhraseDialogCur;
+ BOOL bPassPhraseDialogOnce;
+ const char *key_id;
+ const char *pkey_file;
+} pphrase_cb_arg_t;
+
+#ifdef HAVE_ECC
+static const char *key_types[] = {"RSA", "DSA", "ECC"};
+#else
+static const char *key_types[] = {"RSA", "DSA"};
+#endif
+
+/*
+ * Return true if the named file exists and is readable
+ */
+
+static apr_status_t exists_and_readable(const char *fname, apr_pool_t *pool,
+ apr_time_t *mtime)
+{
+ apr_status_t stat;
+ apr_finfo_t sbuf;
+ apr_file_t *fd;
+
+ if ((stat = apr_stat(&sbuf, fname, APR_FINFO_MIN, pool)) != APR_SUCCESS)
+ return stat;
+
+ if (sbuf.filetype != APR_REG)
+ return APR_EGENERAL;
+
+ if ((stat = apr_file_open(&fd, fname, APR_READ, 0, pool)) != APR_SUCCESS)
+ return stat;
+
+ if (mtime) {
+ *mtime = sbuf.mtime;
+ }
+
+ apr_file_close(fd);
+ return APR_SUCCESS;
+}
+
+/*
+ * reuse vhost keys for asn1 tables where keys are allocated out
+ * of s->process->pool to prevent "leaking" each time we format
+ * a vhost key. since the key is stored in a table with lifetime
+ * of s->process->pool, the key needs to have the same lifetime.
+ *
+ * XXX: probably seems silly to use a hash table with keys and values
+ * being the same, but it is easier than doing a linear search
+ * and will make it easier to remove keys if needed in the future.
+ * also have the problem with apr_array_header_t that if we
+ * underestimate the number of vhost keys when we apr_array_make(),
+ * the array will get resized when we push past the initial number
+ * of elts. this resizing in the s->process->pool means "leaking"
+ * since apr_array_push() will apr_alloc arr->nalloc * 2 elts,
+ * leaving the original arr->elts to waste.
+ */
+static const char *asn1_table_vhost_key(SSLModConfigRec *mc, apr_pool_t *p,
+ const char *id, int i)
+{
+ /* 'p' pool used here is cleared on restarts (or sooner) */
+ char *key = apr_psprintf(p, "%s:%d", id, i);
+ void *keyptr = apr_hash_get(mc->tVHostKeys, key,
+ APR_HASH_KEY_STRING);
+
+ if (!keyptr) {
+ /* make a copy out of s->process->pool */
+ keyptr = apr_pstrdup(mc->pPool, key);
+ apr_hash_set(mc->tVHostKeys, keyptr,
+ APR_HASH_KEY_STRING, keyptr);
+ }
+
+ return (char *)keyptr;
+}
+
+/* _________________________________________________________________
+**
+** Pass Phrase and Private Key Handling
+** _________________________________________________________________
+*/
+
+#define BUILTIN_DIALOG_BACKOFF 2
+#define BUILTIN_DIALOG_RETRIES 5
+
+static apr_file_t *writetty = NULL;
+static apr_file_t *readtty = NULL;
+
+int ssl_pphrase_Handle_CB(char *, int, int, void *);
+
+static char *pphrase_array_get(apr_array_header_t *arr, int idx)
+{
+ if ((idx < 0) || (idx >= arr->nelts)) {
+ return NULL;
+ }
+
+ return ((char **)arr->elts)[idx];
+}
+
+apr_status_t ssl_load_encrypted_pkey(server_rec *s, apr_pool_t *p, int idx,
+ const char *pkey_file,
+ apr_array_header_t **pphrases)
+{
+ SSLModConfigRec *mc = myModConfig(s);
+ SSLSrvConfigRec *sc = mySrvConfig(s);
+ const char *key_id = asn1_table_vhost_key(mc, p, sc->vhost_id, idx);
+ EVP_PKEY *pPrivateKey = NULL;
+ ssl_asn1_t *asn1;
+ int nPassPhrase = (*pphrases)->nelts;
+ int nPassPhraseRetry = 0;
+ apr_time_t pkey_mtime = 0;
+ apr_status_t rv;
+ pphrase_cb_arg_t ppcb_arg;
+
+ if (!pkey_file) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02573)
+ "Init: No private key specified for %s", key_id);
+ return ssl_die(s);
+ }
+ else if ((rv = exists_and_readable(pkey_file, p, &pkey_mtime))
+ != APR_SUCCESS ) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(02574)
+ "Init: Can't open server private key file %s", pkey_file);
+ return ssl_die(s);
+ }
+
+ ppcb_arg.s = s;
+ ppcb_arg.p = p;
+ ppcb_arg.aPassPhrase = *pphrases;
+ ppcb_arg.nPassPhraseCur = 0;
+ ppcb_arg.cpPassPhraseCur = NULL;
+ ppcb_arg.nPassPhraseDialog = 0;
+ ppcb_arg.nPassPhraseDialogCur = 0;
+ ppcb_arg.bPassPhraseDialogOnce = TRUE;
+ ppcb_arg.key_id = key_id;
+ ppcb_arg.pkey_file = pkey_file;
+
+ /*
+ * if the private key is encrypted and SSLPassPhraseDialog
+ * is configured to "builtin" it isn't possible to prompt for
+ * a password after httpd has detached from the tty.
+ * in this case if we already have a private key and the
+ * file name/mtime hasn't changed, then reuse the existing key.
+ * we also reuse existing private keys that were encrypted for
+ * exec: and pipe: dialogs to minimize chances to snoop the
+ * password. that and pipe: dialogs might prompt the user
+ * for password, which on win32 for example could happen 4
+ * times at startup. twice for each child and twice within
+ * each since apache "restarts itself" on startup.
+ * of course this will not work for the builtin dialog if
+ * the server was started without LoadModule ssl_module
+ * configured, then restarted with it configured.
+ * but we fall through with a chance of success if the key
+ * is not encrypted or can be handled via exec or pipe dialog.
+ * and in the case of fallthrough, pkey_mtime and isatty()
+ * are used to give a better idea as to what failed.
+ */
+ if (pkey_mtime) {
+ ssl_asn1_t *asn1 = ssl_asn1_table_get(mc->tPrivateKey, key_id);
+ if (asn1 && (asn1->source_mtime == pkey_mtime)) {
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(02575)
+ "Reusing existing private key from %s on restart",
+ ppcb_arg.pkey_file);
+ return APR_SUCCESS;
+ }
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(02576)
+ "Attempting to load encrypted (?) private key %s", key_id);
+
+ for (;;) {
+ /*
+ * Try to read the private key file with the help of
+ * the callback function which serves the pass
+ * phrases to OpenSSL
+ */
+
+ ppcb_arg.cpPassPhraseCur = NULL;
+
+ /* Ensure that the error stack is empty; some SSL
+ * functions will fail spuriously if the error stack
+ * is not empty. */
+ ERR_clear_error();
+
+ pPrivateKey = modssl_read_privatekey(ppcb_arg.pkey_file,
+ ssl_pphrase_Handle_CB, &ppcb_arg);
+ /* If the private key was successfully read, nothing more to
+ do here. */
+ if (pPrivateKey != NULL)
+ break;
+
+ /*
+ * when we have more remembered pass phrases
+ * try to reuse these first.
+ */
+ if (ppcb_arg.nPassPhraseCur < nPassPhrase) {
+ ppcb_arg.nPassPhraseCur++;
+ continue;
+ }
+
+ /*
+ * else it's not readable and we have no more
+ * remembered pass phrases. Then this has to mean
+ * that the callback function popped up the dialog
+ * but a wrong pass phrase was entered. We give the
+ * user (but not the dialog program) a few more
+ * chances...
+ */
+#ifndef WIN32
+ if ((sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
+ || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE)
+#else
+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE
+#endif
+ && ppcb_arg.cpPassPhraseCur != NULL
+ && nPassPhraseRetry < BUILTIN_DIALOG_RETRIES ) {
+ apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase incorrect "
+ "(%d more retr%s permitted).\n",
+ (BUILTIN_DIALOG_RETRIES-nPassPhraseRetry),
+ (BUILTIN_DIALOG_RETRIES-nPassPhraseRetry) == 1 ? "y" : "ies");
+ nPassPhraseRetry++;
+ if (nPassPhraseRetry > BUILTIN_DIALOG_BACKOFF)
+ apr_sleep((nPassPhraseRetry-BUILTIN_DIALOG_BACKOFF)
+ * 5 * APR_USEC_PER_SEC);
+ continue;
+ }
+#ifdef WIN32
+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02577)
+ "Init: SSLPassPhraseDialog builtin is not "
+ "supported on Win32 (key file "
+ "%s)", ppcb_arg.pkey_file);
+ return ssl_die(s);
+ }
+#endif /* WIN32 */
+
+ /*
+ * Ok, anything else now means a fatal error.
+ */
+ if (ppcb_arg.cpPassPhraseCur == NULL) {
+ if (ppcb_arg.nPassPhraseDialogCur && pkey_mtime &&
+ !isatty(fileno(stdout))) /* XXX: apr_isatty() */
+ {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0,
+ s, APLOGNO(02578)
+ "Init: Unable to read pass phrase "
+ "[Hint: key introduced or changed "
+ "before restart?]");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0,
+ s, APLOGNO(02579) "Init: Private key not found");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
+ }
+ if (writetty) {
+ apr_file_printf(writetty, "Apache:mod_ssl:Error: Private key not found.\n");
+ apr_file_printf(writetty, "**Stopped\n");
+ }
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02580)
+ "Init: Pass phrase incorrect for key %s",
+ key_id);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+
+ if (writetty) {
+ apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase incorrect.\n");
+ apr_file_printf(writetty, "**Stopped\n");
+ }
+ }
+ return ssl_die(s);
+ }
+
+ if (pPrivateKey == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02581)
+ "Init: Unable to read server private key from file %s",
+ ppcb_arg.pkey_file);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+ /*
+ * Log the type of reading
+ */
+ if (ppcb_arg.nPassPhraseDialogCur == 0) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02582)
+ "unencrypted %s private key - pass phrase not "
+ "required", key_id);
+ }
+ else {
+ if (ppcb_arg.cpPassPhraseCur != NULL) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
+ s, APLOGNO(02583)
+ "encrypted %s private key - pass phrase "
+ "requested", key_id);
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
+ s, APLOGNO(02584)
+ "encrypted %s private key - pass phrase"
+ " reused", key_id);
+ }
+ }
+
+ /*
+ * Ok, when we have one more pass phrase store it
+ */
+ if (ppcb_arg.cpPassPhraseCur != NULL) {
+ *(const char **)apr_array_push(ppcb_arg.aPassPhrase) =
+ ppcb_arg.cpPassPhraseCur;
+ nPassPhrase++;
+ }
+
+ /* Cache the private key in the global module configuration so it
+ * can be used after subsequent reloads. */
+ asn1 = ssl_asn1_table_set(mc->tPrivateKey, key_id, pPrivateKey);
+
+ if (ppcb_arg.nPassPhraseDialogCur != 0) {
+ /* remember mtime of encrypted keys */
+ asn1->source_mtime = pkey_mtime;
+ }
+
+ /*
+ * Free the private key structure
+ */
+ EVP_PKEY_free(pPrivateKey);
+
+ /*
+ * Let the user know when we're successful.
+ */
+ if ((ppcb_arg.nPassPhraseDialog > 0) &&
+ (ppcb_arg.cpPassPhraseCur != NULL)) {
+ if (writetty) {
+ apr_file_printf(writetty, "\n"
+ "OK: Pass Phrase Dialog successful.\n");
+ }
+ }
+
+ /* Close the pipes if they were opened
+ */
+ if (readtty) {
+ apr_file_close(readtty);
+ apr_file_close(writetty);
+ readtty = writetty = NULL;
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t ssl_pipe_child_create(apr_pool_t *p, const char *progname)
+{
+ /* Child process code for 'ErrorLog "|..."';
+ * may want a common framework for this, since I expect it will
+ * be common for other foo-loggers to want this sort of thing...
+ */
+ apr_status_t rc;
+ apr_procattr_t *procattr;
+ apr_proc_t *procnew;
+
+ if (((rc = apr_procattr_create(&procattr, p)) == APR_SUCCESS) &&
+ ((rc = apr_procattr_io_set(procattr,
+ APR_FULL_BLOCK,
+ APR_FULL_BLOCK,
+ APR_NO_PIPE)) == APR_SUCCESS)) {
+ char **args;
+
+ apr_tokenize_to_argv(progname, &args, p);
+ procnew = (apr_proc_t *)apr_pcalloc(p, sizeof(*procnew));
+ rc = apr_proc_create(procnew, args[0], (const char * const *)args,
+ NULL, procattr, p);
+ if (rc == APR_SUCCESS) {
+ /* XXX: not sure if we aught to...
+ * apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT);
+ */
+ writetty = procnew->in;
+ readtty = procnew->out;
+ }
+ }
+
+ return rc;
+}
+
+static int pipe_get_passwd_cb(char *buf, int length, char *prompt, int verify)
+{
+ apr_status_t rc;
+ char *p;
+
+ apr_file_puts(prompt, writetty);
+
+ buf[0]='\0';
+ rc = apr_file_gets(buf, length, readtty);
+ apr_file_puts(APR_EOL_STR, writetty);
+
+ if (rc != APR_SUCCESS || apr_file_eof(readtty)) {
+ memset(buf, 0, length);
+ return 1; /* failure */
+ }
+ if ((p = strchr(buf, '\n')) != NULL) {
+ *p = '\0';
+ }
+#ifdef WIN32
+ /* XXX: apr_sometest */
+ if ((p = strchr(buf, '\r')) != NULL) {
+ *p = '\0';
+ }
+#endif
+ return 0;
+}
+
+int ssl_pphrase_Handle_CB(char *buf, int bufsize, int verify, void *srv)
+{
+ pphrase_cb_arg_t *ppcb_arg = (pphrase_cb_arg_t *)srv;
+ SSLSrvConfigRec *sc = mySrvConfig(ppcb_arg->s);
+ char *cpp;
+ int len = -1;
+
+ ppcb_arg->nPassPhraseDialog++;
+ ppcb_arg->nPassPhraseDialogCur++;
+
+ /*
+ * When remembered pass phrases are available use them...
+ */
+ if ((cpp = pphrase_array_get(ppcb_arg->aPassPhrase,
+ ppcb_arg->nPassPhraseCur)) != NULL) {
+ apr_cpystrn(buf, cpp, bufsize);
+ len = strlen(buf);
+ return len;
+ }
+
+ /*
+ * Builtin or Pipe dialog
+ */
+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
+ || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
+ char *prompt;
+ int i;
+
+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
+ if (!readtty) {
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb_arg->s,
+ APLOGNO(01965)
+ "Init: Creating pass phrase dialog pipe child "
+ "'%s'", sc->server->pphrase_dialog_path);
+ if (ssl_pipe_child_create(ppcb_arg->p,
+ sc->server->pphrase_dialog_path)
+ != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ppcb_arg->s,
+ APLOGNO(01966)
+ "Init: Failed to create pass phrase pipe '%s'",
+ sc->server->pphrase_dialog_path);
+ PEMerr(PEM_F_PEM_DEF_CALLBACK,
+ PEM_R_PROBLEMS_GETTING_PASSWORD);
+ memset(buf, 0, (unsigned int)bufsize);
+ return (-1);
+ }
+ }
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb_arg->s, APLOGNO(01967)
+ "Init: Requesting pass phrase via piped dialog");
+ }
+ else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
+#ifdef WIN32
+ PEMerr(PEM_F_PEM_DEF_CALLBACK, PEM_R_PROBLEMS_GETTING_PASSWORD);
+ memset(buf, 0, (unsigned int)bufsize);
+ return (-1);
+#else
+ /*
+ * stderr has already been redirected to the error_log.
+ * rather than attempting to temporarily rehook it to the terminal,
+ * we print the prompt to stdout before EVP_read_pw_string turns
+ * off tty echo
+ */
+ apr_file_open_stdout(&writetty, ppcb_arg->p);
+
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb_arg->s, APLOGNO(01968)
+ "Init: Requesting pass phrase via builtin terminal "
+ "dialog");
+#endif
+ }
+
+ /*
+ * The first time display a header to inform the user about what
+ * program he actually speaks to, which module is responsible for
+ * this terminal dialog and why to the hell he has to enter
+ * something...
+ */
+ if (ppcb_arg->nPassPhraseDialog == 1) {
+ apr_file_printf(writetty, "%s mod_ssl (Pass Phrase Dialog)\n",
+ AP_SERVER_BASEVERSION);
+ apr_file_printf(writetty, "Some of your private key files are encrypted for security reasons.\n");
+ apr_file_printf(writetty, "In order to read them you have to provide the pass phrases.\n");
+ }
+ if (ppcb_arg->bPassPhraseDialogOnce) {
+ ppcb_arg->bPassPhraseDialogOnce = FALSE;
+ apr_file_printf(writetty, "\n");
+ apr_file_printf(writetty, "Private key %s (%s)\n",
+ ppcb_arg->key_id, ppcb_arg->pkey_file);
+ }
+
+ /*
+ * Emulate the OpenSSL internal pass phrase dialog
+ * (see crypto/pem/pem_lib.c:def_callback() for details)
+ */
+ prompt = "Enter pass phrase:";
+
+ for (;;) {
+ apr_file_puts(prompt, writetty);
+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
+ i = pipe_get_passwd_cb(buf, bufsize, "", FALSE);
+ }
+ else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
+ i = EVP_read_pw_string(buf, bufsize, "", FALSE);
+ }
+ if (i != 0) {
+ PEMerr(PEM_F_PEM_DEF_CALLBACK,PEM_R_PROBLEMS_GETTING_PASSWORD);
+ memset(buf, 0, (unsigned int)bufsize);
+ return (-1);
+ }
+ len = strlen(buf);
+ if (len < 1)
+ apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase empty (needs to be at least 1 character).\n");
+ else
+ break;
+ }
+ }
+
+ /*
+ * Filter program
+ */
+ else if (sc->server->pphrase_dialog_type == SSL_PPTYPE_FILTER) {
+ const char *cmd = sc->server->pphrase_dialog_path;
+ const char **argv = apr_palloc(ppcb_arg->p, sizeof(char *) * 4);
+ const char *idx = ap_strrchr_c(ppcb_arg->key_id, ':') + 1;
+ char *result;
+ int i;
+
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb_arg->s, APLOGNO(01969)
+ "Init: Requesting pass phrase from dialog filter "
+ "program (%s)", cmd);
+
+ argv[0] = cmd;
+ argv[1] = apr_pstrndup(ppcb_arg->p, ppcb_arg->key_id,
+ idx-1 - ppcb_arg->key_id);
+ if ((i = atoi(idx)) < CERTKEYS_IDX_MAX+1) {
+ /*
+ * For compatibility with existing 2.4.x configurations, use
+ * "RSA", "DSA" and "ECC" strings for the first two/three keys
+ */
+ argv[2] = key_types[i];
+ } else {
+ /* Four and above: use the integer index */
+ argv[2] = apr_pstrdup(ppcb_arg->p, idx);
+ }
+ argv[3] = NULL;
+
+ result = ssl_util_readfilter(ppcb_arg->s, ppcb_arg->p, cmd, argv);
+ apr_cpystrn(buf, result, bufsize);
+ len = strlen(buf);
+ }
+
+ /*
+ * Ok, we now have the pass phrase, so give it back
+ */
+ ppcb_arg->cpPassPhraseCur = apr_pstrdup(ppcb_arg->p, buf);
+
+ /*
+ * And return its length to OpenSSL...
+ */
+ return (len);
+}
+
+
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
+
+/* OpenSSL UI implementation for passphrase entry; largely duplicated
+ * from ssl_pphrase_Handle_CB but adjusted for UI API. TODO: Might be
+ * worth trying to shift pphrase handling over to the UI API
+ * completely. */
+static int passphrase_ui_open(UI *ui)
+{
+ pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui);
+ SSLSrvConfigRec *sc = mySrvConfig(ppcb->s);
+
+ ppcb->nPassPhraseDialog++;
+ ppcb->nPassPhraseDialogCur++;
+
+ /*
+ * Builtin or Pipe dialog
+ */
+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
+ || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
+ if (!readtty) {
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s,
+ APLOGNO(10143)
+ "Init: Creating pass phrase dialog pipe child "
+ "'%s'", sc->server->pphrase_dialog_path);
+ if (ssl_pipe_child_create(ppcb->p,
+ sc->server->pphrase_dialog_path)
+ != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ppcb->s,
+ APLOGNO(10144)
+ "Init: Failed to create pass phrase pipe '%s'",
+ sc->server->pphrase_dialog_path);
+ return 0;
+ }
+ }
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO(10145)
+ "Init: Requesting pass phrase via piped dialog");
+ }
+ else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
+#ifdef WIN32
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ppcb->s, APLOGNO(10146)
+ "Init: Failed to create pass phrase pipe '%s'",
+ sc->server->pphrase_dialog_path);
+ return 0;
+#else
+ /*
+ * stderr has already been redirected to the error_log.
+ * rather than attempting to temporarily rehook it to the terminal,
+ * we print the prompt to stdout before EVP_read_pw_string turns
+ * off tty echo
+ */
+ apr_file_open_stdout(&writetty, ppcb->p);
+
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO(10147)
+ "Init: Requesting pass phrase via builtin terminal "
+ "dialog");
+#endif
+ }
+
+ /*
+ * The first time display a header to inform the user about what
+ * program he actually speaks to, which module is responsible for
+ * this terminal dialog and why to the hell he has to enter
+ * something...
+ */
+ if (ppcb->nPassPhraseDialog == 1) {
+ apr_file_printf(writetty, "%s mod_ssl (Pass Phrase Dialog)\n",
+ AP_SERVER_BASEVERSION);
+ apr_file_printf(writetty,
+ "A pass phrase is required to access the private key.\n");
+ }
+ if (ppcb->bPassPhraseDialogOnce) {
+ ppcb->bPassPhraseDialogOnce = FALSE;
+ apr_file_printf(writetty, "\n");
+ apr_file_printf(writetty, "Private key %s (%s)\n",
+ ppcb->key_id, ppcb->pkey_file);
+ }
+ }
+
+ return 1;
+}
+
+static int passphrase_ui_read(UI *ui, UI_STRING *uis)
+{
+ pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui);
+ SSLSrvConfigRec *sc = mySrvConfig(ppcb->s);
+ const char *prompt;
+ int i;
+ int bufsize;
+ int len;
+ char *buf;
+
+ prompt = UI_get0_output_string(uis);
+ if (prompt == NULL) {
+ prompt = "Enter pass phrase:";
+ }
+
+ /*
+ * Get the maximum expected size and allocate the buffer
+ */
+ bufsize = UI_get_result_maxsize(uis);
+ buf = apr_pcalloc(ppcb->p, bufsize);
+
+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
+ || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
+ /*
+ * Get the pass phrase through a callback.
+ * Empty input is not accepted.
+ */
+ for (;;) {
+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
+ i = pipe_get_passwd_cb(buf, bufsize, "", FALSE);
+ }
+ else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
+ i = EVP_read_pw_string(buf, bufsize, "", FALSE);
+ }
+ if (i != 0) {
+ OPENSSL_cleanse(buf, bufsize);
+ return 0;
+ }
+ len = strlen(buf);
+ if (len < 1){
+ apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase"
+ "empty (needs to be at least 1 character).\n");
+ apr_file_puts(prompt, writetty);
+ }
+ else {
+ break;
+ }
+ }
+ }
+ /*
+ * Filter program
+ */
+ else if (sc->server->pphrase_dialog_type == SSL_PPTYPE_FILTER) {
+ const char *cmd = sc->server->pphrase_dialog_path;
+ const char **argv = apr_palloc(ppcb->p, sizeof(char *) * 3);
+ char *result;
+
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO(10148)
+ "Init: Requesting pass phrase from dialog filter "
+ "program (%s)", cmd);
+
+ argv[0] = cmd;
+ argv[1] = ppcb->key_id;
+ argv[2] = NULL;
+
+ result = ssl_util_readfilter(ppcb->s, ppcb->p, cmd, argv);
+ apr_cpystrn(buf, result, bufsize);
+ len = strlen(buf);
+ }
+
+ /*
+ * Ok, we now have the pass phrase, so give it back
+ */
+ ppcb->cpPassPhraseCur = apr_pstrdup(ppcb->p, buf);
+ UI_set_result(ui, uis, buf);
+
+ /* Clear sensitive data. */
+ OPENSSL_cleanse(buf, bufsize);
+ return 1;
+}
+
+static int passphrase_ui_write(UI *ui, UI_STRING *uis)
+{
+ pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui);
+ SSLSrvConfigRec *sc;
+ const char *prompt;
+
+ sc = mySrvConfig(ppcb->s);
+
+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
+ || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
+ prompt = UI_get0_output_string(uis);
+ apr_file_puts(prompt, writetty);
+ }
+
+ return 1;
+}
+
+static int passphrase_ui_close(UI *ui)
+{
+ /*
+ * Close the pipes if they were opened
+ */
+ if (readtty) {
+ apr_file_close(readtty);
+ apr_file_close(writetty);
+ readtty = writetty = NULL;
+ }
+ return 1;
+}
+
+static apr_status_t pp_ui_method_cleanup(void *uip)
+{
+ UI_METHOD *uim = uip;
+
+ UI_destroy_method(uim);
+
+ return APR_SUCCESS;
+}
+
+static UI_METHOD *get_passphrase_ui(apr_pool_t *p)
+{
+ UI_METHOD *ui_method = UI_create_method("Passphrase UI");
+
+ UI_method_set_opener(ui_method, passphrase_ui_open);
+ UI_method_set_reader(ui_method, passphrase_ui_read);
+ UI_method_set_writer(ui_method, passphrase_ui_write);
+ UI_method_set_closer(ui_method, passphrase_ui_close);
+
+ apr_pool_cleanup_register(p, ui_method, pp_ui_method_cleanup,
+ pp_ui_method_cleanup);
+
+ return ui_method;
+}
+#endif
+
+
+apr_status_t modssl_load_engine_keypair(server_rec *s, apr_pool_t *p,
+ const char *vhostid,
+ const char *certid, const char *keyid,
+ X509 **pubkey, EVP_PKEY **privkey)
+{
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
+ const char *c, *scheme;
+ ENGINE *e;
+ UI_METHOD *ui_method = get_passphrase_ui(p);
+ pphrase_cb_arg_t ppcb;
+
+ memset(&ppcb, 0, sizeof ppcb);
+ ppcb.s = s;
+ ppcb.p = p;
+ ppcb.bPassPhraseDialogOnce = TRUE;
+ ppcb.key_id = vhostid;
+ ppcb.pkey_file = keyid;
+
+ c = ap_strchr_c(keyid, ':');
+ if (!c || c == keyid) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10131)
+ "Init: Unrecognized private key identifier `%s'",
+ keyid);
+ return ssl_die(s);
+ }
+
+ scheme = apr_pstrmemdup(p, keyid, c - keyid);
+ if (!(e = ENGINE_by_id(scheme))) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10132)
+ "Init: Failed to load engine for private key %s",
+ keyid);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+ if (!ENGINE_init(e)) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10149)
+ "Init: Failed to initialize engine %s for private key %s",
+ scheme, keyid);
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "Init: Initialized engine %s for private key %s",
+ scheme, keyid);
+
+ if (APLOGdebug(s)) {
+ ENGINE_ctrl_cmd_string(e, "VERBOSE", NULL, 0);
+ }
+
+ if (certid) {
+ struct {
+ const char *cert_id;
+ X509 *cert;
+ } params = { certid, NULL };
+
+ if (!ENGINE_ctrl_cmd(e, "LOAD_CERT_CTRL", 0, &params, NULL, 1)) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10136)
+ "Init: Unable to get the certificate");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+ *pubkey = params.cert;
+ }
+
+ *privkey = ENGINE_load_private_key(e, keyid, ui_method, &ppcb);
+ if (*privkey == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10133)
+ "Init: Unable to get the private key");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+
+ ENGINE_finish(e);
+ ENGINE_free(e);
+
+ return APR_SUCCESS;
+#else
+ return APR_ENOTIMPL;
+#endif
+}
diff --git a/modules/ssl/ssl_engine_rand.c b/modules/ssl/ssl_engine_rand.c
new file mode 100644
index 0000000..4e1a9c1
--- /dev/null
+++ b/modules/ssl/ssl_engine_rand.c
@@ -0,0 +1,177 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+/* _ _
+ * _ __ ___ ___ __| | ___ ___| | mod_ssl
+ * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| | \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ * |_____|
+ * ssl_engine_rand.c
+ * Random Number Generator Seeding
+ */
+ /* ``The generation of random
+ numbers is too important
+ to be left to chance.'' */
+
+#include "ssl_private.h"
+
+/* _________________________________________________________________
+**
+** Support for better seeding of SSL library's RNG
+** _________________________________________________________________
+*/
+
+static int ssl_rand_choosenum(int, int);
+static int ssl_rand_feedfp(apr_pool_t *, apr_file_t *, int);
+
+int ssl_rand_seed(server_rec *s, apr_pool_t *p, ssl_rsctx_t nCtx, char *prefix)
+{
+ SSLModConfigRec *mc;
+ apr_array_header_t *apRandSeed;
+ ssl_randseed_t *pRandSeeds;
+ ssl_randseed_t *pRandSeed;
+ unsigned char stackdata[256];
+ int nDone;
+ apr_file_t *fp;
+ int i, n, l;
+
+ mc = myModConfig(s);
+ nDone = 0;
+ apRandSeed = mc->aRandSeed;
+ pRandSeeds = (ssl_randseed_t *)apRandSeed->elts;
+ for (i = 0; i < apRandSeed->nelts; i++) {
+ pRandSeed = &pRandSeeds[i];
+ if (pRandSeed->nCtx == nCtx) {
+ if (pRandSeed->nSrc == SSL_RSSRC_FILE) {
+ /*
+ * seed in contents of an external file
+ */
+ if (apr_file_open(&fp, pRandSeed->cpPath,
+ APR_READ, APR_OS_DEFAULT, p) != APR_SUCCESS)
+ continue;
+ nDone += ssl_rand_feedfp(p, fp, pRandSeed->nBytes);
+ apr_file_close(fp);
+ }
+ else if (pRandSeed->nSrc == SSL_RSSRC_EXEC) {
+ const char *cmd = pRandSeed->cpPath;
+ const char **argv = apr_palloc(p, sizeof(char *) * 3);
+ /*
+ * seed in contents generated by an external program
+ */
+ argv[0] = cmd;
+ argv[1] = apr_itoa(p, pRandSeed->nBytes);
+ argv[2] = NULL;
+
+ if ((fp = ssl_util_ppopen(s, p, cmd, argv)) == NULL)
+ continue;
+ nDone += ssl_rand_feedfp(p, fp, pRandSeed->nBytes);
+ ssl_util_ppclose(s, p, fp);
+ }
+#ifdef HAVE_RAND_EGD
+ else if (pRandSeed->nSrc == SSL_RSSRC_EGD) {
+ /*
+ * seed in contents provided by the external
+ * Entropy Gathering Daemon (EGD)
+ */
+ if ((n = RAND_egd(pRandSeed->cpPath)) == -1)
+ continue;
+ nDone += n;
+ }
+#endif
+ else if (pRandSeed->nSrc == SSL_RSSRC_BUILTIN) {
+ struct {
+ time_t t;
+ pid_t pid;
+ } my_seed;
+
+ /*
+ * seed in the current time (usually just 4 bytes)
+ */
+ my_seed.t = time(NULL);
+
+ /*
+ * seed in the current process id (usually just 4 bytes)
+ */
+ my_seed.pid = mc->pid;
+
+ l = sizeof(my_seed);
+ RAND_seed((unsigned char *)&my_seed, l);
+ nDone += l;
+
+ /*
+ * seed in some current state of the run-time stack (128 bytes)
+ */
+ n = ssl_rand_choosenum(0, sizeof(stackdata)-128-1);
+ RAND_seed(stackdata+n, 128);
+ nDone += 128;
+
+ }
+ }
+ }
+ ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, s,
+ "%sSeeding PRNG with %d bytes of entropy", prefix, nDone);
+
+ if (RAND_status() == 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(01990)
+ "%sPRNG still contains insufficient entropy!", prefix);
+
+ return nDone;
+}
+
+#define BUFSIZE 8192
+
+static int ssl_rand_feedfp(apr_pool_t *p, apr_file_t *fp, int nReq)
+{
+ apr_size_t nDone;
+ unsigned char caBuf[BUFSIZE];
+ apr_size_t nBuf;
+ apr_size_t nRead;
+ apr_size_t nTodo;
+
+ nDone = 0;
+ nRead = BUFSIZE;
+ nTodo = nReq;
+ while (1) {
+ if (nReq > 0)
+ nRead = (nTodo < BUFSIZE ? nTodo : BUFSIZE);
+ nBuf = nRead;
+ if (apr_file_read(fp, caBuf, &nBuf) != APR_SUCCESS)
+ break;
+ RAND_seed(caBuf, nBuf);
+ nDone += nBuf;
+ if (nReq > 0) {
+ nTodo -= nBuf;
+ if (nTodo <= 0)
+ break;
+ }
+ }
+ return nDone;
+}
+
+static int ssl_rand_choosenum(int l, int h)
+{
+ int i;
+ char buf[50];
+
+ apr_snprintf(buf, sizeof(buf), "%.0f",
+ (((double)(rand()%RAND_MAX)/RAND_MAX)*(h-l)));
+ i = atoi(buf)+1;
+ if (i < l) i = l;
+ if (i > h) i = h;
+ return i;
+}
+
diff --git a/modules/ssl/ssl_engine_vars.c b/modules/ssl/ssl_engine_vars.c
new file mode 100644
index 0000000..418d849
--- /dev/null
+++ b/modules/ssl/ssl_engine_vars.c
@@ -0,0 +1,1230 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+/* _ _
+ * _ __ ___ ___ __| | ___ ___| | mod_ssl
+ * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| | \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ * |_____|
+ * ssl_engine_vars.c
+ * Variable Lookup Facility
+ */
+ /* ``Those of you who think they
+ know everything are very annoying
+ to those of us who do.''
+ -- Unknown */
+#include "ssl_private.h"
+#include "mod_ssl.h"
+#include "ap_expr.h"
+
+#include "apr_time.h"
+
+/* _________________________________________________________________
+**
+** Variable Lookup
+** _________________________________________________________________
+*/
+
+static char *ssl_var_lookup_ssl(apr_pool_t *p, SSLConnRec *sslconn, request_rec *r, char *var);
+static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, request_rec *r, X509 *xs, char *var);
+static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, const char *var);
+static char *ssl_var_lookup_ssl_cert_san(apr_pool_t *p, X509 *xs, char *var);
+static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_TIME *tm);
+static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_TIME *tm);
+static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs);
+static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var);
+static char *ssl_var_lookup_ssl_cert_rfc4523_cea(apr_pool_t *p, SSL *ssl);
+static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs);
+static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, SSLConnRec *sslconn);
+static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, SSLConnRec *sslconn, char *var);
+static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize);
+static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var);
+static char *ssl_var_lookup_ssl_compress_meth(SSL *ssl);
+
+static SSLConnRec *ssl_get_effective_config(conn_rec *c)
+{
+ SSLConnRec *sslconn = myConnConfig(c);
+ if (!(sslconn && sslconn->ssl) && c->master) {
+ /* use master connection if no SSL defined here */
+ sslconn = myConnConfig(c->master);
+ }
+ return sslconn;
+}
+
+static int ssl_conn_is_ssl(conn_rec *c)
+{
+ const SSLConnRec *sslconn = ssl_get_effective_config(c);
+ return (sslconn && sslconn->ssl)? OK : DECLINED;
+}
+
+static const char var_interface[] = "mod_ssl/" AP_SERVER_BASEREVISION;
+static char var_library_interface[] = MODSSL_LIBRARY_TEXT;
+static char *var_library = NULL;
+
+static apr_array_header_t *expr_peer_ext_list_fn(ap_expr_eval_ctx_t *ctx,
+ const void *dummy,
+ const char *arg)
+{
+ return ssl_ext_list(ctx->p, ctx->c, 1, arg);
+}
+
+static const char *expr_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
+{
+ char *var = (char *)data;
+ SSLConnRec *sslconn = ssl_get_effective_config(ctx->c);
+
+ return sslconn ? ssl_var_lookup_ssl(ctx->p, sslconn, ctx->r, var) : NULL;
+}
+
+static const char *expr_func_fn(ap_expr_eval_ctx_t *ctx, const void *data,
+ const char *arg)
+{
+ char *var = (char *)arg;
+
+ return var ? ssl_var_lookup(ctx->p, ctx->s, ctx->c, ctx->r, var) : NULL;
+}
+
+static int ssl_expr_lookup(ap_expr_lookup_parms *parms)
+{
+ switch (parms->type) {
+ case AP_EXPR_FUNC_VAR:
+ /* for now, we just handle everything that starts with SSL_, but
+ * register our hook as APR_HOOK_LAST
+ * XXX: This can be optimized
+ */
+ if (strcEQn(parms->name, "SSL_", 4)) {
+ *parms->func = expr_var_fn;
+ *parms->data = parms->name + 4;
+ return OK;
+ }
+ break;
+ case AP_EXPR_FUNC_STRING:
+ /* Function SSL() is implemented by us.
+ */
+ if (strcEQ(parms->name, "SSL")) {
+ *parms->func = expr_func_fn;
+ *parms->data = NULL;
+ return OK;
+ }
+ break;
+ case AP_EXPR_FUNC_LIST:
+ if (strcEQ(parms->name, "PeerExtList")) {
+ *parms->func = expr_peer_ext_list_fn;
+ *parms->data = "PeerExtList";
+ return OK;
+ }
+ break;
+ }
+ return DECLINED;
+}
+
+
+void ssl_var_register(apr_pool_t *p)
+{
+ char *cp, *cp2;
+
+ ap_hook_ssl_conn_is_ssl(ssl_conn_is_ssl, NULL, NULL, APR_HOOK_MIDDLE);
+ APR_REGISTER_OPTIONAL_FN(ssl_var_lookup);
+ APR_REGISTER_OPTIONAL_FN(ssl_ext_list);
+
+ /* Perform once-per-process library version determination: */
+ var_library = apr_pstrdup(p, MODSSL_LIBRARY_DYNTEXT);
+
+ if ((cp = strchr(var_library, ' ')) != NULL) {
+ *cp = '/';
+ if ((cp2 = strchr(cp, ' ')) != NULL)
+ *cp2 = NUL;
+ }
+
+ if ((cp = strchr(var_library_interface, ' ')) != NULL) {
+ *cp = '/';
+ if ((cp2 = strchr(cp, ' ')) != NULL)
+ *cp2 = NUL;
+ }
+
+ ap_hook_expr_lookup(ssl_expr_lookup, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+/* This function must remain safe to use for a non-SSL connection. */
+char *ssl_var_lookup(apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, char *var)
+{
+ SSLModConfigRec *mc = myModConfig(s);
+ const char *result;
+ BOOL resdup;
+ apr_time_exp_t tm;
+
+ result = NULL;
+ resdup = TRUE;
+
+ /*
+ * When no pool is given try to find one
+ */
+ if (p == NULL) {
+ if (r != NULL)
+ p = r->pool;
+ else if (c != NULL)
+ p = c->pool;
+ else
+ p = mc->pPool;
+ }
+
+ /*
+ * Request dependent stuff
+ */
+ if (r != NULL) {
+ switch (var[0]) {
+ case 'H':
+ case 'h':
+ if (strcEQ(var, "HTTP_USER_AGENT"))
+ result = apr_table_get(r->headers_in, "User-Agent");
+ else if (strcEQ(var, "HTTP_REFERER"))
+ result = apr_table_get(r->headers_in, "Referer");
+ else if (strcEQ(var, "HTTP_COOKIE"))
+ result = apr_table_get(r->headers_in, "Cookie");
+ else if (strcEQ(var, "HTTP_FORWARDED"))
+ result = apr_table_get(r->headers_in, "Forwarded");
+ else if (strcEQ(var, "HTTP_HOST"))
+ result = apr_table_get(r->headers_in, "Host");
+ else if (strcEQ(var, "HTTP_PROXY_CONNECTION"))
+ result = apr_table_get(r->headers_in, "Proxy-Connection");
+ else if (strcEQ(var, "HTTP_ACCEPT"))
+ result = apr_table_get(r->headers_in, "Accept");
+ else if (strlen(var) > 5 && strcEQn(var, "HTTP:", 5))
+ /* all other headers from which we are still not know about */
+ result = apr_table_get(r->headers_in, var+5);
+ break;
+
+ case 'R':
+ case 'r':
+ if (strcEQ(var, "REQUEST_METHOD"))
+ result = r->method;
+ else if (strcEQ(var, "REQUEST_SCHEME"))
+ result = ap_http_scheme(r);
+ else if (strcEQ(var, "REQUEST_URI"))
+ result = r->uri;
+ else if (strcEQ(var, "REQUEST_FILENAME"))
+ result = r->filename;
+ else if (strcEQ(var, "REMOTE_ADDR"))
+ result = r->useragent_ip;
+ else if (strcEQ(var, "REMOTE_HOST"))
+ result = ap_get_useragent_host(r, REMOTE_NAME, NULL);
+ else if (strcEQ(var, "REMOTE_IDENT"))
+ result = ap_get_remote_logname(r);
+ else if (strcEQ(var, "REMOTE_USER"))
+ result = r->user;
+ break;
+
+ case 'S':
+ case 's':
+ if (strcEQn(var, "SSL", 3)) break; /* shortcut common case */
+
+ if (strcEQ(var, "SERVER_ADMIN"))
+ result = r->server->server_admin;
+ else if (strcEQ(var, "SERVER_NAME"))
+ result = ap_get_server_name_for_url(r);
+ else if (strcEQ(var, "SERVER_PORT"))
+ result = apr_psprintf(p, "%u", ap_get_server_port(r));
+ else if (strcEQ(var, "SERVER_PROTOCOL"))
+ result = r->protocol;
+ else if (strcEQ(var, "SCRIPT_FILENAME"))
+ result = r->filename;
+ break;
+
+ default:
+ if (strcEQ(var, "PATH_INFO"))
+ result = r->path_info;
+ else if (strcEQ(var, "QUERY_STRING"))
+ result = r->args;
+ else if (strcEQ(var, "IS_SUBREQ"))
+ result = (r->main != NULL ? "true" : "false");
+ else if (strcEQ(var, "DOCUMENT_ROOT"))
+ result = ap_document_root(r);
+ else if (strcEQ(var, "AUTH_TYPE"))
+ result = r->ap_auth_type;
+ else if (strcEQ(var, "THE_REQUEST"))
+ result = r->the_request;
+ else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) {
+ result = apr_table_get(r->notes, var+4);
+ if (result == NULL)
+ result = apr_table_get(r->subprocess_env, var+4);
+ }
+ break;
+ }
+ }
+
+ /*
+ * Connection stuff
+ */
+ if (result == NULL && c != NULL) {
+ SSLConnRec *sslconn = ssl_get_effective_config(c);
+ if (strlen(var) > 4 && strcEQn(var, "SSL_", 4)
+ && sslconn && sslconn->ssl)
+ result = ssl_var_lookup_ssl(p, sslconn, r, var+4);
+ else if (strcEQ(var, "HTTPS")) {
+ if (sslconn && sslconn->ssl)
+ result = "on";
+ else
+ result = "off";
+ }
+ }
+
+ /*
+ * Totally independent stuff
+ */
+ if (result == NULL) {
+ if (strlen(var) > 12 && strcEQn(var, "SSL_VERSION_", 12))
+ result = ssl_var_lookup_ssl_version(p, var+12);
+ else if (strcEQ(var, "SERVER_SOFTWARE"))
+ result = ap_get_server_banner();
+ else if (strcEQ(var, "API_VERSION")) {
+ result = apr_itoa(p, MODULE_MAGIC_NUMBER_MAJOR);
+ resdup = FALSE;
+ }
+ else if (strcEQ(var, "TIME_YEAR")) {
+ apr_time_exp_lt(&tm, apr_time_now());
+ result = apr_psprintf(p, "%02d%02d",
+ (tm.tm_year / 100) + 19, tm.tm_year % 100);
+ resdup = FALSE;
+ }
+#define MKTIMESTR(format, tmfield) \
+ apr_time_exp_lt(&tm, apr_time_now()); \
+ result = apr_psprintf(p, format, tm.tmfield); \
+ resdup = FALSE;
+ else if (strcEQ(var, "TIME_MON")) {
+ MKTIMESTR("%02d", tm_mon+1)
+ }
+ else if (strcEQ(var, "TIME_DAY")) {
+ MKTIMESTR("%02d", tm_mday)
+ }
+ else if (strcEQ(var, "TIME_HOUR")) {
+ MKTIMESTR("%02d", tm_hour)
+ }
+ else if (strcEQ(var, "TIME_MIN")) {
+ MKTIMESTR("%02d", tm_min)
+ }
+ else if (strcEQ(var, "TIME_SEC")) {
+ MKTIMESTR("%02d", tm_sec)
+ }
+ else if (strcEQ(var, "TIME_WDAY")) {
+ MKTIMESTR("%d", tm_wday)
+ }
+ else if (strcEQ(var, "TIME")) {
+ apr_time_exp_lt(&tm, apr_time_now());
+ result = apr_psprintf(p,
+ "%02d%02d%02d%02d%02d%02d%02d", (tm.tm_year / 100) + 19,
+ (tm.tm_year % 100), tm.tm_mon+1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+ resdup = FALSE;
+ }
+ /* all other env-variables from the parent Apache process */
+ else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) {
+ result = getenv(var+4);
+ }
+ }
+
+ if (result != NULL && resdup)
+ result = apr_pstrdup(p, result);
+ if (result == NULL)
+ result = "";
+ return (char *)result;
+}
+
+static char *ssl_var_lookup_ssl(apr_pool_t *p, SSLConnRec *sslconn,
+ request_rec *r, char *var)
+{
+ char *result;
+ X509 *xs;
+ STACK_OF(X509) *sk;
+ SSL *ssl;
+
+ result = NULL;
+
+ ssl = sslconn->ssl;
+ if (strlen(var) > 8 && strcEQn(var, "VERSION_", 8)) {
+ result = ssl_var_lookup_ssl_version(p, var+8);
+ }
+ else if (ssl != NULL && strcEQ(var, "PROTOCOL")) {
+ result = (char *)SSL_get_version(ssl);
+ }
+ else if (ssl != NULL && strcEQ(var, "SESSION_ID")) {
+ char buf[MODSSL_SESSION_ID_STRING_LEN];
+ SSL_SESSION *pSession = SSL_get_session(ssl);
+ if (pSession) {
+ IDCONST unsigned char *id;
+ unsigned int idlen;
+
+#ifdef OPENSSL_NO_SSL_INTERN
+ id = (unsigned char *)SSL_SESSION_get_id(pSession, &idlen);
+#else
+ id = pSession->session_id;
+ idlen = pSession->session_id_length;
+#endif
+
+ result = apr_pstrdup(p, modssl_SSL_SESSION_id2sz(id, idlen,
+ buf, sizeof(buf)));
+ }
+ }
+ else if(ssl != NULL && strcEQ(var, "SESSION_RESUMED")) {
+ if (SSL_session_reused(ssl) == 1)
+ result = "Resumed";
+ else
+ result = "Initial";
+ }
+ else if (ssl != NULL && strlen(var) >= 6 && strcEQn(var, "CIPHER", 6)) {
+ result = ssl_var_lookup_ssl_cipher(p, sslconn, var+6);
+ }
+ else if (ssl != NULL && strlen(var) > 18 && strcEQn(var, "CLIENT_CERT_CHAIN_", 18)) {
+ sk = SSL_get_peer_cert_chain(ssl);
+ result = ssl_var_lookup_ssl_cert_chain(p, sk, var+18);
+ }
+ else if (ssl != NULL && strcEQ(var, "CLIENT_CERT_RFC4523_CEA")) {
+ result = ssl_var_lookup_ssl_cert_rfc4523_cea(p, ssl);
+ }
+ else if (ssl != NULL && strcEQ(var, "CLIENT_VERIFY")) {
+ result = ssl_var_lookup_ssl_cert_verify(p, sslconn);
+ }
+ else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "CLIENT_", 7)) {
+ if ((xs = SSL_get_peer_certificate(ssl)) != NULL) {
+ result = ssl_var_lookup_ssl_cert(p, r, xs, var+7);
+ X509_free(xs);
+ }
+ }
+ else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "SERVER_", 7)) {
+ if ((xs = SSL_get_certificate(ssl)) != NULL) {
+ result = ssl_var_lookup_ssl_cert(p, r, xs, var+7);
+ /* SSL_get_certificate is different from SSL_get_peer_certificate.
+ * No need to X509_free(xs).
+ */
+ }
+ }
+ else if (ssl != NULL && strcEQ(var, "COMPRESS_METHOD")) {
+ result = ssl_var_lookup_ssl_compress_meth(ssl);
+ }
+#ifdef HAVE_TLSEXT
+ else if (ssl != NULL && strcEQ(var, "TLS_SNI")) {
+ result = apr_pstrdup(p, SSL_get_servername(ssl,
+ TLSEXT_NAMETYPE_host_name));
+ }
+#endif
+ else if (ssl != NULL && strcEQ(var, "SECURE_RENEG")) {
+ int flag = 0;
+#ifdef SSL_get_secure_renegotiation_support
+ flag = SSL_get_secure_renegotiation_support(ssl);
+#endif
+ result = apr_pstrdup(p, flag ? "true" : "false");
+ }
+#ifdef HAVE_SRP
+ else if (ssl != NULL && strcEQ(var, "SRP_USER")) {
+ if ((result = SSL_get_srp_username(ssl)) != NULL) {
+ result = apr_pstrdup(p, result);
+ }
+ }
+ else if (ssl != NULL && strcEQ(var, "SRP_USERINFO")) {
+ if ((result = SSL_get_srp_userinfo(ssl)) != NULL) {
+ result = apr_pstrdup(p, result);
+ }
+ }
+#endif
+
+ return result;
+}
+
+static char *ssl_var_lookup_ssl_cert_dn_oneline(apr_pool_t *p, request_rec *r,
+ X509_NAME *xsname)
+{
+ char *result = NULL;
+ SSLDirConfigRec *dc;
+ int legacy_format = 0;
+ if (r) {
+ dc = myDirConfig(r);
+ legacy_format = dc->nOptions & SSL_OPT_LEGACYDNFORMAT;
+ }
+ if (legacy_format) {
+ char *cp = X509_NAME_oneline(xsname, NULL, 0);
+ result = apr_pstrdup(p, cp);
+ OPENSSL_free(cp);
+ }
+ else {
+ BIO* bio;
+ unsigned long flags = XN_FLAG_RFC2253 & ~ASN1_STRFLGS_ESC_MSB;
+
+ if ((bio = BIO_new(BIO_s_mem())) == NULL)
+ return NULL;
+ X509_NAME_print_ex(bio, xsname, 0, flags);
+
+ result = modssl_bio_free_read(p, bio);
+ }
+ return result;
+}
+
+static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, request_rec *r, X509 *xs,
+ char *var)
+{
+ char *result;
+ BOOL resdup;
+ X509_NAME *xsname;
+ int nid;
+
+ result = NULL;
+ resdup = TRUE;
+
+ if (strcEQ(var, "M_VERSION")) {
+ result = apr_psprintf(p, "%lu", X509_get_version(xs)+1);
+ resdup = FALSE;
+ }
+ else if (strcEQ(var, "M_SERIAL")) {
+ result = ssl_var_lookup_ssl_cert_serial(p, xs);
+ }
+ else if (strcEQ(var, "V_START")) {
+ result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notBefore(xs));
+ }
+ else if (strcEQ(var, "V_END")) {
+ result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notAfter(xs));
+ }
+ else if (strcEQ(var, "V_REMAIN")) {
+ result = ssl_var_lookup_ssl_cert_remain(p, X509_get_notAfter(xs));
+ resdup = FALSE;
+ }
+ else if (*var && strcEQ(var+1, "_DN")) {
+ if (*var == 'S')
+ xsname = X509_get_subject_name(xs);
+ else if (*var == 'I')
+ xsname = X509_get_issuer_name(xs);
+ else
+ return NULL;
+ result = ssl_var_lookup_ssl_cert_dn_oneline(p, r, xsname);
+ resdup = FALSE;
+ }
+ else if (strlen(var) > 5 && strcEQn(var+1, "_DN_", 4)) {
+ if (*var == 'S')
+ xsname = X509_get_subject_name(xs);
+ else if (*var == 'I')
+ xsname = X509_get_issuer_name(xs);
+ else
+ return NULL;
+ result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
+ resdup = FALSE;
+ }
+ else if (strlen(var) > 4 && strcEQn(var, "SAN_", 4)) {
+ result = ssl_var_lookup_ssl_cert_san(p, xs, var+4);
+ resdup = FALSE;
+ }
+ else if (strcEQ(var, "A_SIG")) {
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
+ nid = OBJ_obj2nid((ASN1_OBJECT *)(xs->cert_info->signature->algorithm));
+#else
+ const ASN1_OBJECT *paobj;
+ X509_ALGOR_get0(&paobj, NULL, NULL, X509_get0_tbs_sigalg(xs));
+ nid = OBJ_obj2nid(paobj);
+#endif
+ result = apr_pstrdup(p,
+ (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
+ resdup = FALSE;
+ }
+ else if (strcEQ(var, "A_KEY")) {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ nid = OBJ_obj2nid((ASN1_OBJECT *)(xs->cert_info->key->algor->algorithm));
+#else
+ ASN1_OBJECT *paobj;
+ X509_PUBKEY_get0_param(&paobj, NULL, 0, NULL, X509_get_X509_PUBKEY(xs));
+ nid = OBJ_obj2nid(paobj);
+#endif
+ result = apr_pstrdup(p,
+ (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
+ resdup = FALSE;
+ }
+ else if (strcEQ(var, "CERT")) {
+ result = ssl_var_lookup_ssl_cert_PEM(p, xs);
+ }
+
+ if (resdup)
+ result = apr_pstrdup(p, result);
+ return result;
+}
+
+/* In this table, .extract is non-zero if RDNs using the NID should be
+ * extracted to for the SSL_{CLIENT,SERVER}_{I,S}_DN_* environment
+ * variables. */
+static const struct {
+ char *name;
+ int nid;
+ int extract;
+} ssl_var_lookup_ssl_cert_dn_rec[] = {
+ { "C", NID_countryName, 1 },
+ { "ST", NID_stateOrProvinceName, 1 }, /* officially (RFC2156) */
+ { "SP", NID_stateOrProvinceName, 0 }, /* compatibility (SSLeay) */
+ { "L", NID_localityName, 1 },
+ { "O", NID_organizationName, 1 },
+ { "OU", NID_organizationalUnitName, 1 },
+ { "CN", NID_commonName, 1 },
+ { "T", NID_title, 1 },
+ { "I", NID_initials, 1 },
+ { "G", NID_givenName, 1 },
+ { "S", NID_surname, 1 },
+ { "D", NID_description, 1 },
+#ifdef NID_userId
+ { "UID", NID_userId, 1 },
+#endif
+ { "Email", NID_pkcs9_emailAddress, 1 },
+ { NULL, 0, 0 }
+};
+
+static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname,
+ const char *var)
+{
+ const char *ptr;
+ char *result;
+ X509_NAME_ENTRY *xsne;
+ int i, j, n, idx = 0, raw = 0;
+ apr_size_t varlen;
+
+ ptr = ap_strrchr_c(var, '_');
+ if (ptr && ptr > var && strcmp(ptr + 1, "RAW") == 0) {
+ var = apr_pstrmemdup(p, var, ptr - var);
+ raw = 1;
+ }
+
+ /* if an _N suffix is used, find the Nth attribute of given name */
+ ptr = ap_strchr_c(var, '_');
+ if (ptr != NULL && strspn(ptr + 1, "0123456789") == strlen(ptr + 1)) {
+ idx = atoi(ptr + 1);
+ varlen = ptr - var;
+ } else {
+ varlen = strlen(var);
+ }
+
+ result = NULL;
+
+ for (i = 0; ssl_var_lookup_ssl_cert_dn_rec[i].name != NULL; i++) {
+ if (strEQn(var, ssl_var_lookup_ssl_cert_dn_rec[i].name, varlen)
+ && strlen(ssl_var_lookup_ssl_cert_dn_rec[i].name) == varlen) {
+ for (j = 0; j < X509_NAME_entry_count(xsname); j++) {
+ xsne = X509_NAME_get_entry(xsname, j);
+
+ n =OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
+
+ if (n == ssl_var_lookup_ssl_cert_dn_rec[i].nid && idx-- == 0) {
+ result = modssl_X509_NAME_ENTRY_to_string(p, xsne, raw);
+ break;
+ }
+ }
+ break;
+ }
+ }
+ return result;
+}
+
+static char *ssl_var_lookup_ssl_cert_san(apr_pool_t *p, X509 *xs, char *var)
+{
+ int type;
+ apr_size_t numlen;
+ const char *onf = NULL;
+ apr_array_header_t *entries;
+
+ if (strcEQn(var, "Email_", 6)) {
+ type = GEN_EMAIL;
+ var += 6;
+ }
+ else if (strcEQn(var, "DNS_", 4)) {
+ type = GEN_DNS;
+ var += 4;
+ }
+ else if (strcEQn(var, "OTHER_", 6)) {
+ type = GEN_OTHERNAME;
+ var += 6;
+ if (strEQn(var, "msUPN_", 6)) {
+ var += 6;
+ onf = "msUPN";
+ }
+ else if (strEQn(var, "dnsSRV_", 7)) {
+ var += 7;
+ onf = "id-on-dnsSRV";
+ }
+ else
+ return NULL;
+ }
+ else
+ return NULL;
+
+ /* sanity check: number must be between 1 and 4 digits */
+ numlen = strspn(var, "0123456789");
+ if ((numlen < 1) || (numlen > 4) || (numlen != strlen(var)))
+ return NULL;
+
+ if (modssl_X509_getSAN(p, xs, type, onf, atoi(var), &entries))
+ /* return the first entry from this 1-element array */
+ return APR_ARRAY_IDX(entries, 0, char *);
+ else
+ return NULL;
+}
+
+static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_TIME *tm)
+{
+ BIO* bio;
+
+ if ((bio = BIO_new(BIO_s_mem())) == NULL)
+ return NULL;
+ ASN1_TIME_print(bio, tm);
+
+ return modssl_bio_free_read(p, bio);
+}
+
+#define DIGIT2NUM(x) (((x)[0] - '0') * 10 + (x)[1] - '0')
+
+/* Return a string giving the number of days remaining until 'tm', or
+ * "0" if this can't be determined. */
+static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_TIME *tm)
+{
+ apr_time_t then, now = apr_time_now();
+ apr_time_exp_t exp = {0};
+ long diff;
+ unsigned char *dp;
+
+ /* Fail if the time isn't a valid ASN.1 TIME; RFC3280 mandates
+ * that the seconds digits are present even though ASN.1
+ * doesn't. */
+ if ((tm->type == V_ASN1_UTCTIME && tm->length < 11) ||
+ (tm->type == V_ASN1_GENERALIZEDTIME && tm->length < 13) ||
+ !ASN1_TIME_check(tm)) {
+ return apr_pstrdup(p, "0");
+ }
+
+ if (tm->type == V_ASN1_UTCTIME) {
+ exp.tm_year = DIGIT2NUM(tm->data);
+ if (exp.tm_year <= 50) exp.tm_year += 100;
+ dp = tm->data + 2;
+ } else {
+ exp.tm_year = DIGIT2NUM(tm->data) * 100 + DIGIT2NUM(tm->data + 2) - 1900;
+ dp = tm->data + 4;
+ }
+
+ exp.tm_mon = DIGIT2NUM(dp) - 1;
+ exp.tm_mday = DIGIT2NUM(dp + 2) + 1;
+ exp.tm_hour = DIGIT2NUM(dp + 4);
+ exp.tm_min = DIGIT2NUM(dp + 6);
+ exp.tm_sec = DIGIT2NUM(dp + 8);
+
+ if (apr_time_exp_gmt_get(&then, &exp) != APR_SUCCESS) {
+ return apr_pstrdup(p, "0");
+ }
+
+ diff = (long)((apr_time_sec(then) - apr_time_sec(now)) / (60*60*24));
+
+ return diff > 0 ? apr_ltoa(p, diff) : apr_pstrdup(p, "0");
+}
+
+static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs)
+{
+ BIO *bio;
+
+ if ((bio = BIO_new(BIO_s_mem())) == NULL)
+ return NULL;
+ i2a_ASN1_INTEGER(bio, X509_get_serialNumber(xs));
+
+ return modssl_bio_free_read(p, bio);
+}
+
+static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var)
+{
+ char *result;
+ X509 *xs;
+ int n;
+
+ result = NULL;
+
+ if (strspn(var, "0123456789") == strlen(var)) {
+ n = atoi(var);
+ if (n < sk_X509_num(sk)) {
+ xs = sk_X509_value(sk, n);
+ result = ssl_var_lookup_ssl_cert_PEM(p, xs);
+ }
+ }
+
+ return result;
+}
+
+static char *ssl_var_lookup_ssl_cert_rfc4523_cea(apr_pool_t *p, SSL *ssl)
+{
+ char *result;
+ X509 *xs;
+
+ ASN1_INTEGER *serialNumber;
+
+ if (!(xs = SSL_get_peer_certificate(ssl))) {
+ return NULL;
+ }
+
+ result = NULL;
+
+ serialNumber = X509_get_serialNumber(xs);
+ if (serialNumber) {
+ X509_NAME *issuer = X509_get_issuer_name(xs);
+ if (issuer) {
+ BIGNUM *bn = ASN1_INTEGER_to_BN(serialNumber, NULL);
+ char *decimal = BN_bn2dec(bn);
+ result = apr_pstrcat(p, "{ serialNumber ", decimal,
+ ", issuer rdnSequence:\"",
+ modssl_X509_NAME_to_string(p, issuer, 0), "\" }", NULL);
+ OPENSSL_free(decimal);
+ BN_free(bn);
+ }
+ }
+
+ X509_free(xs);
+ return result;
+}
+
+static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs)
+{
+ BIO *bio;
+
+ if ((bio = BIO_new(BIO_s_mem())) == NULL)
+ return NULL;
+ PEM_write_bio_X509(bio, xs);
+
+ return modssl_bio_free_read(p, bio);
+}
+
+static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, SSLConnRec *sslconn)
+{
+ char *result;
+ long vrc;
+ const char *verr;
+ const char *vinfo;
+ SSL *ssl;
+ X509 *xs;
+
+ result = NULL;
+ ssl = sslconn->ssl;
+ verr = sslconn->verify_error;
+ vinfo = sslconn->verify_info;
+ vrc = SSL_get_verify_result(ssl);
+ xs = SSL_get_peer_certificate(ssl);
+
+ if (vrc == X509_V_OK && verr == NULL && xs == NULL)
+ /* no client verification done at all */
+ result = "NONE";
+ else if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs != NULL)
+ /* client verification done successful */
+ result = "SUCCESS";
+ else if (vrc == X509_V_OK && vinfo != NULL && strEQ(vinfo, "GENEROUS"))
+ /* client verification done in generous way */
+ result = "GENEROUS";
+ else
+ /* client verification failed */
+ result = apr_psprintf(p, "FAILED:%s",
+ verr ? verr : X509_verify_cert_error_string(vrc));
+
+ if (xs)
+ X509_free(xs);
+ return result;
+}
+
+static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, SSLConnRec *sslconn, char *var)
+{
+ char *result;
+ BOOL resdup;
+ int usekeysize, algkeysize;
+ SSL *ssl;
+
+ result = NULL;
+ resdup = TRUE;
+
+ ssl = sslconn->ssl;
+ ssl_var_lookup_ssl_cipher_bits(ssl, &usekeysize, &algkeysize);
+
+ if (ssl && strEQ(var, "")) {
+ MODSSL_SSL_CIPHER_CONST SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
+ result = (cipher != NULL ? (char *)SSL_CIPHER_get_name(cipher) : NULL);
+ }
+ else if (strcEQ(var, "_EXPORT"))
+ result = (usekeysize < 56 ? "true" : "false");
+ else if (strcEQ(var, "_USEKEYSIZE")) {
+ result = apr_itoa(p, usekeysize);
+ resdup = FALSE;
+ }
+ else if (strcEQ(var, "_ALGKEYSIZE")) {
+ result = apr_itoa(p, algkeysize);
+ resdup = FALSE;
+ }
+
+ if (result != NULL && resdup)
+ result = apr_pstrdup(p, result);
+ return result;
+}
+
+static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize)
+{
+ MODSSL_SSL_CIPHER_CONST SSL_CIPHER *cipher;
+
+ *usekeysize = 0;
+ *algkeysize = 0;
+ if (ssl != NULL)
+ if ((cipher = SSL_get_current_cipher(ssl)) != NULL)
+ *usekeysize = SSL_CIPHER_get_bits(cipher, algkeysize);
+ return;
+}
+
+static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var)
+{
+ if (strEQ(var, "INTERFACE")) {
+ return apr_pstrdup(p, var_interface);
+ }
+ else if (strEQ(var, "LIBRARY_INTERFACE")) {
+ return apr_pstrdup(p, var_library_interface);
+ }
+ else if (strEQ(var, "LIBRARY")) {
+ return apr_pstrdup(p, var_library);
+ }
+ return NULL;
+}
+
+/* Add each RDN in 'xn' to the table 't' where the NID is present in
+ * 'nids', using key prefix 'pfx'. */
+static void extract_dn(apr_table_t *t, apr_hash_t *nids, const char *pfx,
+ X509_NAME *xn, apr_pool_t *p)
+{
+ X509_NAME_ENTRY *xsne;
+ apr_hash_t *count;
+ int i, nid;
+
+ /* Hash of (int) NID -> (int *) counter to count each time an RDN
+ * with the given NID has been seen. */
+ count = apr_hash_make(p);
+
+ /* For each RDN... */
+ for (i = 0; i < X509_NAME_entry_count(xn); i++) {
+ const char *tag;
+ xsne = X509_NAME_get_entry(xn, i);
+
+ /* Retrieve the nid, and check whether this is one of the nids
+ * which are to be extracted. */
+ nid = OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
+
+ tag = apr_hash_get(nids, &nid, sizeof nid);
+ if (tag) {
+ const char *key;
+ int *dup;
+ char *value;
+
+ /* Check whether a variable with this nid was already
+ * been used; if so, use the foo_N=bar syntax. */
+ dup = apr_hash_get(count, &nid, sizeof nid);
+ if (dup) {
+ key = apr_psprintf(p, "%s%s_%d", pfx, tag, ++(*dup));
+ }
+ else {
+ /* Otherwise, use the plain foo=bar syntax. */
+ dup = apr_pcalloc(p, sizeof *dup);
+ apr_hash_set(count, &nid, sizeof nid, dup);
+ key = apr_pstrcat(p, pfx, tag, NULL);
+ }
+ value = modssl_X509_NAME_ENTRY_to_string(p, xsne, 0);
+ apr_table_setn(t, key, value);
+ }
+ }
+}
+
+void modssl_var_extract_dns(apr_table_t *t, SSL *ssl, apr_pool_t *p)
+{
+ apr_hash_t *nids;
+ unsigned n;
+ X509 *xs;
+
+ /* Build up a hash table of (int *)NID->(char *)short-name for all
+ * the tags which are to be extracted: */
+ nids = apr_hash_make(p);
+ for (n = 0; ssl_var_lookup_ssl_cert_dn_rec[n].name; n++) {
+ if (ssl_var_lookup_ssl_cert_dn_rec[n].extract) {
+ apr_hash_set(nids, &ssl_var_lookup_ssl_cert_dn_rec[n].nid,
+ sizeof(ssl_var_lookup_ssl_cert_dn_rec[0].nid),
+ ssl_var_lookup_ssl_cert_dn_rec[n].name);
+ }
+ }
+
+ /* Extract the server cert DNS -- note that the refcount does NOT
+ * increase: */
+ xs = SSL_get_certificate(ssl);
+ if (xs) {
+ extract_dn(t, nids, "SSL_SERVER_S_DN_", X509_get_subject_name(xs), p);
+ extract_dn(t, nids, "SSL_SERVER_I_DN_", X509_get_issuer_name(xs), p);
+ }
+
+ /* Extract the client cert DNs -- note that the refcount DOES
+ * increase: */
+ xs = SSL_get_peer_certificate(ssl);
+ if (xs) {
+ extract_dn(t, nids, "SSL_CLIENT_S_DN_", X509_get_subject_name(xs), p);
+ extract_dn(t, nids, "SSL_CLIENT_I_DN_", X509_get_issuer_name(xs), p);
+ X509_free(xs);
+ }
+}
+
+static void extract_san_array(apr_table_t *t, const char *pfx,
+ apr_array_header_t *entries, apr_pool_t *p)
+{
+ int i;
+
+ for (i = 0; i < entries->nelts; i++) {
+ const char *key = apr_psprintf(p, "%s_%d", pfx, i);
+ apr_table_setn(t, key, APR_ARRAY_IDX(entries, i, const char *));
+ }
+}
+
+void modssl_var_extract_san_entries(apr_table_t *t, SSL *ssl, apr_pool_t *p)
+{
+ X509 *xs;
+ apr_array_header_t *entries;
+
+ /* subjectAltName entries of the server certificate */
+ xs = SSL_get_certificate(ssl);
+ if (xs) {
+ if (modssl_X509_getSAN(p, xs, GEN_EMAIL, NULL, -1, &entries)) {
+ extract_san_array(t, "SSL_SERVER_SAN_Email", entries, p);
+ }
+ if (modssl_X509_getSAN(p, xs, GEN_DNS, NULL, -1, &entries)) {
+ extract_san_array(t, "SSL_SERVER_SAN_DNS", entries, p);
+ }
+ if (modssl_X509_getSAN(p, xs, GEN_OTHERNAME, "id-on-dnsSRV", -1,
+ &entries)) {
+ extract_san_array(t, "SSL_SERVER_SAN_OTHER_dnsSRV", entries, p);
+ }
+ /* no need to free xs (refcount does not increase) */
+ }
+
+ /* subjectAltName entries of the client certificate */
+ xs = SSL_get_peer_certificate(ssl);
+ if (xs) {
+ if (modssl_X509_getSAN(p, xs, GEN_EMAIL, NULL, -1, &entries)) {
+ extract_san_array(t, "SSL_CLIENT_SAN_Email", entries, p);
+ }
+ if (modssl_X509_getSAN(p, xs, GEN_DNS, NULL, -1, &entries)) {
+ extract_san_array(t, "SSL_CLIENT_SAN_DNS", entries, p);
+ }
+ if (modssl_X509_getSAN(p, xs, GEN_OTHERNAME, "msUPN", -1, &entries)) {
+ extract_san_array(t, "SSL_CLIENT_SAN_OTHER_msUPN", entries, p);
+ }
+ X509_free(xs);
+ }
+}
+
+/* For an extension type which OpenSSL does not recognize, attempt to
+ * parse the extension type as a primitive string. This will fail for
+ * any structured extension type per the docs. Returns non-zero on
+ * success and writes the string to the given bio. */
+static int dump_extn_value(BIO *bio, ASN1_OCTET_STRING *str)
+{
+ const unsigned char *pp = str->data;
+ ASN1_STRING *ret = ASN1_STRING_new();
+ int rv = 0;
+
+ /* This allows UTF8String, IA5String, VisibleString, or BMPString;
+ * conversion to UTF-8 is forced. */
+ if (d2i_DISPLAYTEXT(&ret, &pp, str->length)) {
+ ASN1_STRING_print_ex(bio, ret, ASN1_STRFLGS_UTF8_CONVERT);
+ rv = 1;
+ }
+
+ ASN1_STRING_free(ret);
+ return rv;
+}
+
+apr_array_header_t *ssl_ext_list(apr_pool_t *p, conn_rec *c, int peer,
+ const char *extension)
+{
+ SSLConnRec *sslconn = ssl_get_effective_config(c);
+ SSL *ssl = NULL;
+ apr_array_header_t *array = NULL;
+ X509 *xs = NULL;
+ ASN1_OBJECT *oid = NULL;
+ int count = 0, j;
+
+ if (!sslconn || !sslconn->ssl || !extension) {
+ return NULL;
+ }
+ ssl = sslconn->ssl;
+
+ /* We accept the "extension" string to be converted as
+ * a long name (nsComment), short name (DN) or
+ * numeric OID (1.2.3.4).
+ */
+ oid = OBJ_txt2obj(extension, 0);
+ if (!oid) {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01970)
+ "could not parse OID '%s'", extension);
+ ERR_clear_error();
+ return NULL;
+ }
+
+ xs = peer ? SSL_get_peer_certificate(ssl) : SSL_get_certificate(ssl);
+ if (xs == NULL) {
+ return NULL;
+ }
+
+ count = X509_get_ext_count(xs);
+ /* Create an array large enough to accommodate every extension. This is
+ * likely overkill, but safe.
+ */
+ array = apr_array_make(p, count, sizeof(char *));
+ for (j = 0; j < count; j++) {
+ X509_EXTENSION *ext = X509_get_ext(xs, j);
+
+ if (OBJ_cmp(X509_EXTENSION_get_object(ext), oid) == 0) {
+ BIO *bio = BIO_new(BIO_s_mem());
+
+ /* We want to obtain a string representation of the extensions
+ * value and add it to the array we're building.
+ * X509V3_EXT_print() doesn't know about all the possible
+ * data types, but the value is stored as an ASN1_OCTET_STRING
+ * allowing us a fallback in case of X509V3_EXT_print
+ * not knowing how to handle the data.
+ */
+ if (X509V3_EXT_print(bio, ext, 0, 0) == 1 ||
+ dump_extn_value(bio, X509_EXTENSION_get_data(ext)) == 1) {
+ BUF_MEM *buf;
+ char **ptr = apr_array_push(array);
+ BIO_get_mem_ptr(bio, &buf);
+ *ptr = apr_pstrmemdup(p, buf->data, buf->length);
+ } else {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01971)
+ "Found an extension '%s', but failed to "
+ "create a string from it", extension);
+ }
+ BIO_vfree(bio);
+ }
+ }
+
+ if (array->nelts == 0)
+ array = NULL;
+
+ if (peer) {
+ /* only SSL_get_peer_certificate raises the refcount */
+ X509_free(xs);
+ }
+
+ ASN1_OBJECT_free(oid);
+ ERR_clear_error();
+ return array;
+}
+
+static char *ssl_var_lookup_ssl_compress_meth(SSL *ssl)
+{
+ char *result = "NULL";
+#ifndef OPENSSL_NO_COMP
+ SSL_SESSION *pSession = SSL_get_session(ssl);
+
+ if (pSession) {
+#ifdef OPENSSL_NO_SSL_INTERN
+ switch (SSL_SESSION_get_compress_id(pSession)) {
+#else
+ switch (pSession->compress_meth) {
+#endif
+ case 0:
+ /* default "NULL" already set */
+ break;
+
+ /* Defined by RFC 3749, deflate is coded by "1" */
+ case 1:
+ result = "DEFLATE";
+ break;
+
+ /* IANA assigned compression number for LZS */
+ case 0x40:
+ result = "LZS";
+ break;
+
+ default:
+ result = "UNKNOWN";
+ break;
+ }
+ }
+#endif
+ return result;
+}
+
+/* _________________________________________________________________
+**
+** SSL Extension to mod_log_config
+** _________________________________________________________________
+*/
+
+#include "../../modules/loggers/mod_log_config.h"
+
+static const char *ssl_var_log_handler_c(request_rec *r, char *a);
+static const char *ssl_var_log_handler_x(request_rec *r, char *a);
+
+/*
+ * register us for the mod_log_config function registering phase
+ * to establish %{...}c and to be able to expand %{...}x variables.
+ */
+void ssl_var_log_config_register(apr_pool_t *p)
+{
+ APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register;
+
+ log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
+
+ if (log_pfn_register) {
+ log_pfn_register(p, "c", ssl_var_log_handler_c, 0);
+ log_pfn_register(p, "x", ssl_var_log_handler_x, 0);
+ }
+ return;
+}
+
+/*
+ * implement the %{..}c log function
+ * (we are the only function)
+ */
+static const char *ssl_var_log_handler_c(request_rec *r, char *a)
+{
+ SSLConnRec *sslconn = ssl_get_effective_config(r->connection);
+ char *result;
+
+ if (sslconn == NULL || sslconn->ssl == NULL)
+ return NULL;
+ result = NULL;
+ if (strEQ(a, "version"))
+ result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_PROTOCOL");
+ else if (strEQ(a, "cipher"))
+ result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER");
+ else if (strEQ(a, "subjectdn") || strEQ(a, "clientcert"))
+ result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_S_DN");
+ else if (strEQ(a, "issuerdn") || strEQ(a, "cacert"))
+ result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_I_DN");
+ else if (strEQ(a, "errcode"))
+ result = "-";
+ else if (strEQ(a, "errstr"))
+ result = (char *)sslconn->verify_error;
+ if (result != NULL && result[0] == NUL)
+ result = NULL;
+ return result;
+}
+
+/*
+ * extend the implementation of the %{..}x log function
+ * (there can be more functions)
+ */
+static const char *ssl_var_log_handler_x(request_rec *r, char *a)
+{
+ char *result;
+
+ result = ssl_var_lookup(r->pool, r->server, r->connection, r, a);
+ if (result != NULL && result[0] == NUL)
+ result = NULL;
+ return result;
+}
+
+
diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h
new file mode 100644
index 0000000..cd8df07
--- /dev/null
+++ b/modules/ssl/ssl_private.h
@@ -0,0 +1,1174 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+#ifndef SSL_PRIVATE_H
+#define SSL_PRIVATE_H
+
+/**
+ * @file ssl_private.h
+ * @brief Internal interfaces private to mod_ssl.
+ *
+ * @defgroup MOD_SSL_PRIVATE Private
+ * @ingroup MOD_SSL
+ * @{
+ */
+
+/** Apache headers */
+#include "ap_config.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "http_connection.h"
+#include "http_request.h"
+#include "http_protocol.h"
+#include "http_ssl.h"
+#include "http_vhost.h"
+#include "util_script.h"
+#include "util_filter.h"
+#include "util_ebcdic.h"
+#include "util_mutex.h"
+#include "apr.h"
+#include "apr_strings.h"
+#define APR_WANT_STRFUNC
+#define APR_WANT_MEMFUNC
+#include "apr_want.h"
+#include "apr_tables.h"
+#include "apr_lib.h"
+#include "apr_fnmatch.h"
+#include "apr_strings.h"
+#include "apr_global_mutex.h"
+#include "apr_optional.h"
+#include "ap_socache.h"
+#include "mod_auth.h"
+
+/* The #ifdef macros are only defined AFTER including the above
+ * therefore we cannot include these system files at the top :-(
+ */
+#if APR_HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if APR_HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#if APR_HAVE_UNISTD_H
+#include <unistd.h> /* needed for STDIN_FILENO et.al., at least on FreeBSD */
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE !FALSE
+#endif
+
+#ifndef BOOL
+#define BOOL unsigned int
+#endif
+
+#include "ap_expr.h"
+
+/* OpenSSL headers */
+#include <openssl/opensslv.h>
+#if (OPENSSL_VERSION_NUMBER >= 0x10001000)
+/* must be defined before including ssl.h */
+#define OPENSSL_NO_SSL_INTERN
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x30000000
+#include <openssl/core_names.h>
+#endif
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <openssl/x509v3.h>
+#include <openssl/x509_vfy.h>
+#include <openssl/ocsp.h>
+
+/* Avoid tripping over an engine build installed globally and detected
+ * when the user points at an explicit non-engine flavor of OpenSSL
+ */
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
+#include <openssl/engine.h>
+#endif
+
+#if (OPENSSL_VERSION_NUMBER < 0x0090801f)
+#error mod_ssl requires OpenSSL 0.9.8a or later
+#endif
+
+/**
+ * ...shifting sands of OpenSSL...
+ * Note: when adding support for new OpenSSL features, avoid explicit
+ * version number checks whenever possible, and use "feature-based"
+ * detection instead (check for definitions of constants or functions)
+ */
+#if (OPENSSL_VERSION_NUMBER >= 0x10000000)
+#define MODSSL_SSL_CIPHER_CONST const
+#define MODSSL_SSL_METHOD_CONST const
+#else
+#define MODSSL_SSL_CIPHER_CONST
+#define MODSSL_SSL_METHOD_CONST
+#endif
+
+#if defined(LIBRESSL_VERSION_NUMBER)
+/* Missing from LibreSSL */
+#if LIBRESSL_VERSION_NUMBER < 0x2060000f
+#define SSL_CTRL_SET_MIN_PROTO_VERSION 123
+#define SSL_CTRL_SET_MAX_PROTO_VERSION 124
+#define SSL_CTX_set_min_proto_version(ctx, version) \
+ SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MIN_PROTO_VERSION, version, NULL)
+#define SSL_CTX_set_max_proto_version(ctx, version) \
+ SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MAX_PROTO_VERSION, version, NULL)
+#endif /* LIBRESSL_VERSION_NUMBER < 0x2060000f */
+/* LibreSSL before 2.7 declares OPENSSL_VERSION_NUMBER == 2.0 but does not
+ * include most changes from OpenSSL >= 1.1 (new functions, macros,
+ * deprecations, ...), so we have to work around this...
+ */
+#define MODSSL_USE_OPENSSL_PRE_1_1_API (LIBRESSL_VERSION_NUMBER < 0x2070000f)
+#else /* defined(LIBRESSL_VERSION_NUMBER) */
+#define MODSSL_USE_OPENSSL_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L)
+#endif
+
+#if defined(OPENSSL_FIPS) || OPENSSL_VERSION_NUMBER >= 0x30000000L
+#define HAVE_FIPS
+#endif
+
+#if defined(SSL_OP_NO_TLSv1_2)
+#define HAVE_TLSV1_X
+#endif
+
+#if defined(SSL_CONF_FLAG_FILE)
+#define HAVE_SSL_CONF_CMD
+#endif
+
+/* session id constness */
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
+#define IDCONST
+#else
+#define IDCONST const
+#endif
+
+/**
+ * The following features all depend on TLS extension support.
+ * Within this block, check again for features (not version numbers).
+ */
+#if !defined(OPENSSL_NO_TLSEXT) && defined(SSL_set_tlsext_host_name)
+
+#define HAVE_TLSEXT
+
+/* ECC: make sure we have at least 1.0.0 */
+#if !defined(OPENSSL_NO_EC) && defined(TLSEXT_ECPOINTFORMAT_uncompressed)
+#define HAVE_ECC
+#endif
+
+/* OCSP stapling */
+#if !defined(OPENSSL_NO_OCSP) && defined(SSL_CTX_set_tlsext_status_cb)
+#define HAVE_OCSP_STAPLING
+/* All exist but are no longer macros since OpenSSL 1.1.0 */
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+/* backward compatibility with OpenSSL < 1.0 */
+#ifndef sk_OPENSSL_STRING_num
+#define sk_OPENSSL_STRING_num sk_num
+#endif
+#ifndef sk_OPENSSL_STRING_value
+#define sk_OPENSSL_STRING_value sk_value
+#endif
+#ifndef sk_OPENSSL_STRING_pop
+#define sk_OPENSSL_STRING_pop sk_pop
+#endif
+#endif /* if OPENSSL_VERSION_NUMBER < 0x10100000L */
+#endif /* if !defined(OPENSSL_NO_OCSP) && defined(SSL_CTX_set_tlsext_status_cb) */
+
+/* TLS session tickets */
+#if defined(SSL_CTX_set_tlsext_ticket_key_cb)
+#define HAVE_TLS_SESSION_TICKETS
+#define TLSEXT_TICKET_KEY_LEN 48
+#ifndef tlsext_tick_md
+#ifdef OPENSSL_NO_SHA256
+#define tlsext_tick_md EVP_sha1
+#else
+#define tlsext_tick_md EVP_sha256
+#endif
+#endif
+#endif
+
+/* Secure Remote Password */
+#if !defined(OPENSSL_NO_SRP) && defined(SSL_CTRL_SET_TLS_EXT_SRP_USERNAME_CB)
+#define HAVE_SRP
+#include <openssl/srp.h>
+#endif
+
+/* ALPN Protocol Negotiation */
+#if defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
+#define HAVE_TLS_ALPN
+#endif
+
+#endif /* !defined(OPENSSL_NO_TLSEXT) && defined(SSL_set_tlsext_host_name) */
+
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
+#define BN_get_rfc2409_prime_768 get_rfc2409_prime_768
+#define BN_get_rfc2409_prime_1024 get_rfc2409_prime_1024
+#define BN_get_rfc3526_prime_1536 get_rfc3526_prime_1536
+#define BN_get_rfc3526_prime_2048 get_rfc3526_prime_2048
+#define BN_get_rfc3526_prime_3072 get_rfc3526_prime_3072
+#define BN_get_rfc3526_prime_4096 get_rfc3526_prime_4096
+#define BN_get_rfc3526_prime_6144 get_rfc3526_prime_6144
+#define BN_get_rfc3526_prime_8192 get_rfc3526_prime_8192
+#define BIO_set_init(x,v) (x->init=v)
+#define BIO_get_data(x) (x->ptr)
+#define BIO_set_data(x,v) (x->ptr=v)
+#define BIO_get_shutdown(x) (x->shutdown)
+#define BIO_set_shutdown(x,v) (x->shutdown=v)
+#define DH_bits(x) (BN_num_bits(x->p))
+#else
+void init_bio_methods(void);
+void free_bio_methods(void);
+#endif
+
+#if OPENSSL_VERSION_NUMBER < 0x10002000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000f)
+#define X509_STORE_CTX_get0_store(x) (x->ctx)
+#endif
+
+#if OPENSSL_VERSION_NUMBER < 0x10000000L
+#ifndef X509_STORE_CTX_get0_current_issuer
+#define X509_STORE_CTX_get0_current_issuer(x) (x->current_issuer)
+#endif
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
+#define HAVE_OPENSSL_KEYLOG
+#endif
+
+#ifdef HAVE_FIPS
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#define modssl_fips_is_enabled() EVP_default_properties_is_fips_enabled(NULL)
+#define modssl_fips_enable(to) EVP_default_properties_enable_fips(NULL, (to))
+#else
+#define modssl_fips_is_enabled() FIPS_mode()
+#define modssl_fips_enable(to) FIPS_mode_set((to))
+#endif
+#endif /* HAVE_FIPS */
+
+/* mod_ssl headers */
+#include "ssl_util_ssl.h"
+
+APLOG_USE_MODULE(ssl);
+
+/*
+ * Provide reasonable default for some defines
+ */
+#ifndef PFALSE
+#define PFALSE ((void *)FALSE)
+#endif
+#ifndef PTRUE
+#define PTRUE ((void *)TRUE)
+#endif
+#ifndef UNSET
+#define UNSET (-1)
+#endif
+#ifndef NUL
+#define NUL '\0'
+#endif
+#ifndef RAND_MAX
+#include <limits.h>
+#define RAND_MAX INT_MAX
+#endif
+
+/**
+ * Provide reasonable defines for some types
+ */
+#ifndef UCHAR
+#define UCHAR unsigned char
+#endif
+
+/**
+ * Provide useful shorthands
+ */
+#define strEQ(s1,s2) (strcmp(s1,s2) == 0)
+#define strNE(s1,s2) (strcmp(s1,s2) != 0)
+#define strEQn(s1,s2,n) (strncmp(s1,s2,n) == 0)
+#define strNEn(s1,s2,n) (strncmp(s1,s2,n) != 0)
+
+#define strcEQ(s1,s2) (strcasecmp(s1,s2) == 0)
+#define strcNE(s1,s2) (strcasecmp(s1,s2) != 0)
+#define strcEQn(s1,s2,n) (strncasecmp(s1,s2,n) == 0)
+#define strcNEn(s1,s2,n) (strncasecmp(s1,s2,n) != 0)
+
+#define strIsEmpty(s) (s == NULL || s[0] == NUL)
+
+#define myConnConfig(c) \
+ ((SSLConnRec *)ap_get_module_config(c->conn_config, &ssl_module))
+#define myConnConfigSet(c, val) \
+ ap_set_module_config(c->conn_config, &ssl_module, val)
+#define mySrvConfig(srv) \
+ ((SSLSrvConfigRec *)ap_get_module_config(srv->module_config, &ssl_module))
+#define myDirConfig(req) \
+ ((SSLDirConfigRec *)ap_get_module_config(req->per_dir_config, &ssl_module))
+#define myConnCtxConfig(c, sc) \
+ (c->outgoing ? myConnConfig(c)->dc->proxy : sc->server)
+#define myModConfig(srv) mySrvConfig((srv))->mc
+#define mySrvFromConn(c) myConnConfig(c)->server
+#define myDirConfigFromConn(c) myConnConfig(c)->dc
+#define mySrvConfigFromConn(c) mySrvConfig(mySrvFromConn(c))
+#define myModConfigFromConn(c) myModConfig(mySrvFromConn(c))
+
+/**
+ * Defaults for the configuration
+ */
+#ifndef SSL_SESSION_CACHE_TIMEOUT
+#define SSL_SESSION_CACHE_TIMEOUT 300
+#endif
+
+/* Default setting for per-dir reneg buffer. */
+#ifndef DEFAULT_RENEG_BUFFER_SIZE
+#define DEFAULT_RENEG_BUFFER_SIZE (128 * 1024)
+#endif
+
+/* Default for OCSP response validity */
+#ifndef DEFAULT_OCSP_MAX_SKEW
+#define DEFAULT_OCSP_MAX_SKEW (60 * 5)
+#endif
+
+/* Default timeout for OCSP queries */
+#ifndef DEFAULT_OCSP_TIMEOUT
+#define DEFAULT_OCSP_TIMEOUT 10
+#endif
+
+/*
+ * For better backwards compatibility with the SSLCertificate[Key]File
+ * and SSLPassPhraseDialog ("exec" type) directives in 2.4.7 and earlier
+ */
+#ifdef HAVE_ECC
+#define CERTKEYS_IDX_MAX 2
+#else
+#define CERTKEYS_IDX_MAX 1
+#endif
+
+/**
+ * Define the SSL options
+ */
+#define SSL_OPT_NONE (0)
+#define SSL_OPT_RELSET (1<<0)
+#define SSL_OPT_STDENVVARS (1<<1)
+#define SSL_OPT_EXPORTCERTDATA (1<<3)
+#define SSL_OPT_FAKEBASICAUTH (1<<4)
+#define SSL_OPT_STRICTREQUIRE (1<<5)
+#define SSL_OPT_OPTRENEGOTIATE (1<<6)
+#define SSL_OPT_LEGACYDNFORMAT (1<<7)
+typedef int ssl_opt_t;
+
+/**
+ * Define the SSL Protocol options
+ */
+#define SSL_PROTOCOL_NONE (0)
+#ifndef OPENSSL_NO_SSL3
+#define SSL_PROTOCOL_SSLV3 (1<<1)
+#endif
+#define SSL_PROTOCOL_TLSV1 (1<<2)
+#ifndef OPENSSL_NO_SSL3
+#define SSL_PROTOCOL_BASIC (SSL_PROTOCOL_SSLV3|SSL_PROTOCOL_TLSV1)
+#else
+#define SSL_PROTOCOL_BASIC (SSL_PROTOCOL_TLSV1)
+#endif
+#ifdef HAVE_TLSV1_X
+#define SSL_PROTOCOL_TLSV1_1 (1<<3)
+#define SSL_PROTOCOL_TLSV1_2 (1<<4)
+#define SSL_PROTOCOL_TLSV1_3 (1<<5)
+
+#ifdef SSL_OP_NO_TLSv1_3
+#define SSL_HAVE_PROTOCOL_TLSV1_3 (1)
+#define SSL_PROTOCOL_ALL (SSL_PROTOCOL_BASIC| \
+ SSL_PROTOCOL_TLSV1_1|SSL_PROTOCOL_TLSV1_2|SSL_PROTOCOL_TLSV1_3)
+#else
+#define SSL_HAVE_PROTOCOL_TLSV1_3 (0)
+#define SSL_PROTOCOL_ALL (SSL_PROTOCOL_BASIC| \
+ SSL_PROTOCOL_TLSV1_1|SSL_PROTOCOL_TLSV1_2)
+#endif
+#else
+#define SSL_PROTOCOL_ALL (SSL_PROTOCOL_BASIC)
+#endif
+#ifndef OPENSSL_NO_SSL3
+#define SSL_PROTOCOL_DEFAULT (SSL_PROTOCOL_ALL & ~SSL_PROTOCOL_SSLV3)
+#else
+#define SSL_PROTOCOL_DEFAULT (SSL_PROTOCOL_ALL)
+#endif
+typedef int ssl_proto_t;
+
+/**
+ * Define the SSL verify levels
+ */
+typedef enum {
+ SSL_CVERIFY_UNSET = UNSET,
+ SSL_CVERIFY_NONE = 0,
+ SSL_CVERIFY_OPTIONAL = 1,
+ SSL_CVERIFY_REQUIRE = 2,
+ SSL_CVERIFY_OPTIONAL_NO_CA = 3
+} ssl_verify_t;
+
+#define SSL_VERIFY_PEER_STRICT \
+ (SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
+
+#define ssl_verify_error_is_optional(errnum) \
+ ((errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) \
+ || (errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) \
+ || (errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) \
+ || (errnum == X509_V_ERR_CERT_UNTRUSTED) \
+ || (errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE))
+
+/**
+ * CRL checking mask (mode | flags)
+ */
+typedef enum {
+ SSL_CRLCHECK_NONE = (0),
+ SSL_CRLCHECK_LEAF = (1 << 0),
+ SSL_CRLCHECK_CHAIN = (1 << 1),
+
+#define SSL_CRLCHECK_FLAGS (~0x3)
+ SSL_CRLCHECK_NO_CRL_FOR_CERT_OK = (1 << 2)
+} ssl_crlcheck_t;
+
+/**
+ * OCSP checking mask (mode | flags)
+ */
+typedef enum {
+ SSL_OCSPCHECK_NONE = (0),
+ SSL_OCSPCHECK_LEAF = (1 << 0),
+ SSL_OCSPCHECK_CHAIN = (1 << 1),
+ SSL_OCSPCHECK_NO_OCSP_FOR_CERT_OK = (1 << 2)
+} ssl_ocspcheck_t;
+
+/**
+ * Define the SSL pass phrase dialog types
+ */
+typedef enum {
+ SSL_PPTYPE_UNSET = UNSET,
+ SSL_PPTYPE_BUILTIN = 0,
+ SSL_PPTYPE_FILTER = 1,
+ SSL_PPTYPE_PIPE = 2
+} ssl_pphrase_t;
+
+/**
+ * Define the Path Checking modes
+ */
+#define SSL_PCM_EXISTS 1
+#define SSL_PCM_ISREG 2
+#define SSL_PCM_ISDIR 4
+#define SSL_PCM_ISNONZERO 8
+typedef unsigned int ssl_pathcheck_t;
+
+/**
+ * Define the SSL enabled state
+ */
+typedef enum {
+ SSL_ENABLED_UNSET = UNSET,
+ SSL_ENABLED_FALSE = 0,
+ SSL_ENABLED_TRUE = 1,
+ SSL_ENABLED_OPTIONAL = 3
+} ssl_enabled_t;
+
+/**
+ * Define the SSL requirement structure
+ */
+typedef struct {
+ const char *cpExpr;
+ ap_expr_info_t *mpExpr;
+} ssl_require_t;
+
+/**
+ * Define the SSL random number generator seeding source
+ */
+typedef enum {
+ SSL_RSCTX_STARTUP = 1,
+ SSL_RSCTX_CONNECT = 2
+} ssl_rsctx_t;
+typedef enum {
+ SSL_RSSRC_BUILTIN = 1,
+ SSL_RSSRC_FILE = 2,
+ SSL_RSSRC_EXEC = 3,
+ SSL_RSSRC_EGD = 4
+} ssl_rssrc_t;
+typedef struct {
+ ssl_rsctx_t nCtx;
+ ssl_rssrc_t nSrc;
+ char *cpPath;
+ int nBytes;
+} ssl_randseed_t;
+
+/**
+ * Define the structure of an ASN.1 anything
+ */
+typedef struct {
+ long int nData;
+ unsigned char *cpData;
+ apr_time_t source_mtime;
+} ssl_asn1_t;
+
+/**
+ * Define the mod_ssl per-module configuration structure
+ * (i.e. the global configuration for each httpd process)
+ */
+
+typedef struct SSLSrvConfigRec SSLSrvConfigRec;
+typedef struct SSLDirConfigRec SSLDirConfigRec;
+
+typedef enum {
+ SSL_SHUTDOWN_TYPE_UNSET,
+ SSL_SHUTDOWN_TYPE_STANDARD,
+ SSL_SHUTDOWN_TYPE_UNCLEAN,
+ SSL_SHUTDOWN_TYPE_ACCURATE
+} ssl_shutdown_type_e;
+
+typedef struct {
+ SSL *ssl;
+ const char *client_dn;
+ X509 *client_cert;
+ ssl_shutdown_type_e shutdown_type;
+ const char *verify_info;
+ const char *verify_error;
+ int verify_depth;
+ int disabled;
+ enum {
+ NON_SSL_OK = 0, /* is SSL request, or error handling completed */
+ NON_SSL_SEND_REQLINE, /* Need to send the fake request line */
+ NON_SSL_SEND_HDR_SEP, /* Need to send the header separator */
+ NON_SSL_SET_ERROR_MSG /* Need to set the error message */
+ } non_ssl_request;
+
+ /* Track the handshake/renegotiation state for the connection so
+ * that all client-initiated renegotiations can be rejected, as a
+ * partial fix for CVE-2009-3555. */
+ enum {
+ RENEG_INIT = 0, /* Before initial handshake */
+ RENEG_REJECT, /* After initial handshake; any client-initiated
+ * renegotiation should be rejected */
+ RENEG_ALLOW, /* A server-initiated renegotiation is taking
+ * place (as dictated by configuration) */
+ RENEG_ABORT /* Renegotiation initiated by client, abort the
+ * connection */
+ } reneg_state;
+
+ server_rec *server;
+ SSLDirConfigRec *dc;
+
+ const char *cipher_suite; /* cipher suite used in last reneg */
+ int service_unavailable; /* thouugh we negotiate SSL, no requests will be served */
+ int vhost_found; /* whether we found vhost from SNI already */
+} SSLConnRec;
+
+/* BIG FAT WARNING: SSLModConfigRec has unusual memory lifetime: it is
+ * allocated out of the "process" pool and only a single such
+ * structure is created and used for the lifetime of the process.
+ * (The process pool is s->process->pool and is stored in the .pPool
+ * field.) Most members of this structure are likewise allocated out
+ * of the process pool, but notably sesscache and sesscache_context
+ * are not.
+ *
+ * The structure is treated as mostly immutable after a single config
+ * parse has completed; the post_config hook (ssl_init_Module) flips
+ * the bFixed flag to true and subsequent invocations of the config
+ * callbacks hence do nothing.
+ *
+ * This odd lifetime strategy is used so that encrypted private keys
+ * can be decrypted once at startup and continue to be used across
+ * subsequent server reloads where the interactive password prompt is
+ * not possible.
+
+ * It is really an ABI nightmare waiting to happen since DSOs are
+ * reloaded across restarts, and nothing prevents the struct type
+ * changing across such reloads, yet the cached structure will be
+ * assumed to match regardless.
+ *
+ * This should really be fixed using a smaller structure which only
+ * stores that which is absolutely necessary (the private keys, maybe
+ * the random seed), and have that structure be strictly ABI-versioned
+ * for safety.
+ */
+typedef struct {
+ pid_t pid;
+ apr_pool_t *pPool;
+ BOOL bFixed;
+
+ /* OpenSSL SSL_SESS_CACHE_* flags: */
+ long sesscache_mode;
+
+ /* The configured provider, and associated private data
+ * structure. */
+ const ap_socache_provider_t *sesscache;
+ ap_socache_instance_t *sesscache_context;
+
+ apr_global_mutex_t *pMutex;
+ apr_array_header_t *aRandSeed;
+ apr_hash_t *tVHostKeys;
+
+ /* A hash table of pointers to ssl_asn1_t structures. The structures
+ * are used to store private keys in raw DER format (serialized OpenSSL
+ * PrivateKey structures). The table is indexed by (vhost-id,
+ * index), for example the string "vhost.example.com:443:0". */
+ apr_hash_t *tPrivateKey;
+
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
+ const char *szCryptoDevice;
+#endif
+
+#ifdef HAVE_OCSP_STAPLING
+ const ap_socache_provider_t *stapling_cache;
+ ap_socache_instance_t *stapling_cache_context;
+ apr_global_mutex_t *stapling_cache_mutex;
+ apr_global_mutex_t *stapling_refresh_mutex;
+#endif
+#ifdef HAVE_OPENSSL_KEYLOG
+ /* Used for logging if SSLKEYLOGFILE is set at startup. */
+ apr_file_t *keylog_file;
+#endif
+
+#ifdef HAVE_FIPS
+ BOOL fips;
+#endif
+} SSLModConfigRec;
+
+/** Structure representing configured filenames for certs and keys for
+ * a given vhost */
+typedef struct {
+ /* Lists of configured certs and keys for this server */
+ apr_array_header_t *cert_files;
+ apr_array_header_t *key_files;
+
+ /** Certificates which specify the set of CA names which should be
+ * sent in the CertificateRequest message: */
+ const char *ca_name_path;
+ const char *ca_name_file;
+
+ /* TLS service for this server is suspended */
+ int service_unavailable;
+} modssl_pk_server_t;
+
+typedef struct {
+ /** proxy can have any number of cert/key pairs */
+ const char *cert_file;
+ const char *cert_path;
+ const char *ca_cert_file;
+ /* certs is a stack of configured cert, key pairs. */
+ STACK_OF(X509_INFO) *certs;
+ /* ca_certs contains ONLY chain certs for each item in certs.
+ * ca_certs[n] is a pointer to the (STACK_OF(X509) *) stack which
+ * holds the cert chain for the 'n'th cert in the certs stack, or
+ * NULL if no chain is configured. */
+ STACK_OF(X509) **ca_certs;
+} modssl_pk_proxy_t;
+
+/** stuff related to authentication that can also be per-dir */
+typedef struct {
+ /** known/trusted CAs */
+ const char *ca_cert_path;
+ const char *ca_cert_file;
+
+ const char *cipher_suite;
+
+ /** for client or downstream server authentication */
+ int verify_depth;
+ ssl_verify_t verify_mode;
+
+ /** TLSv1.3 has its separate cipher list, separate from the
+ settings for older TLS protocol versions. Since which one takes
+ effect is a matter of negotiation, we need separate settings */
+ const char *tls13_ciphers;
+} modssl_auth_ctx_t;
+
+#ifdef HAVE_TLS_SESSION_TICKETS
+typedef struct {
+ const char *file_path;
+ unsigned char key_name[16];
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ unsigned char hmac_secret[16];
+#else
+ OSSL_PARAM mac_params[3];
+#endif
+ unsigned char aes_key[16];
+} modssl_ticket_key_t;
+#endif
+
+#ifdef HAVE_SSL_CONF_CMD
+typedef struct {
+ const char *name;
+ const char *value;
+} ssl_ctx_param_t;
+#endif
+
+typedef struct {
+ SSLSrvConfigRec *sc; /** pointer back to server config */
+ SSL_CTX *ssl_ctx;
+
+ /** we are one or the other */
+ modssl_pk_server_t *pks;
+ modssl_pk_proxy_t *pkp;
+
+#ifdef HAVE_TLS_SESSION_TICKETS
+ modssl_ticket_key_t *ticket_key;
+#endif
+
+ ssl_proto_t protocol;
+ int protocol_set;
+
+ /** config for handling encrypted keys */
+ ssl_pphrase_t pphrase_dialog_type;
+ const char *pphrase_dialog_path;
+
+ const char *cert_chain;
+
+ /** certificate revocation list */
+ const char *crl_path;
+ const char *crl_file;
+ int crl_check_mask;
+
+#ifdef HAVE_OCSP_STAPLING
+ /** OCSP stapling options */
+ BOOL stapling_enabled;
+ long stapling_resptime_skew;
+ long stapling_resp_maxage;
+ int stapling_cache_timeout;
+ BOOL stapling_return_errors;
+ BOOL stapling_fake_trylater;
+ int stapling_errcache_timeout;
+ apr_interval_time_t stapling_responder_timeout;
+ const char *stapling_force_url;
+#endif
+
+#ifdef HAVE_SRP
+ char *srp_vfile;
+ char *srp_unknown_user_seed;
+ SRP_VBASE *srp_vbase;
+#endif
+
+ modssl_auth_ctx_t auth;
+
+ int ocsp_mask;
+ BOOL ocsp_force_default; /* true if the default responder URL is
+ * used regardless of per-cert URL */
+ const char *ocsp_responder; /* default responder URL */
+ long ocsp_resptime_skew;
+ long ocsp_resp_maxage;
+ apr_interval_time_t ocsp_responder_timeout;
+ BOOL ocsp_use_request_nonce;
+ apr_uri_t *proxy_uri;
+
+ BOOL ocsp_noverify; /* true if skipping OCSP certification verification like openssl -noverify */
+ /* Declare variables for using OCSP Responder Certs for OCSP verification */
+ int ocsp_verify_flags; /* Flags to use when verifying OCSP response */
+ const char *ocsp_certs_file; /* OCSP other certificates filename */
+ STACK_OF(X509) *ocsp_certs; /* OCSP other certificates */
+
+#ifdef HAVE_SSL_CONF_CMD
+ SSL_CONF_CTX *ssl_ctx_config; /* Configuration context */
+ apr_array_header_t *ssl_ctx_param; /* parameters to pass to SSL_CTX */
+#endif
+
+ BOOL ssl_check_peer_cn;
+ BOOL ssl_check_peer_name;
+ BOOL ssl_check_peer_expire;
+} modssl_ctx_t;
+
+struct SSLSrvConfigRec {
+ SSLModConfigRec *mc;
+ ssl_enabled_t enabled;
+ const char *vhost_id;
+ int vhost_id_len;
+ int session_cache_timeout;
+ BOOL cipher_server_pref;
+ BOOL insecure_reneg;
+ modssl_ctx_t *server;
+#ifdef HAVE_TLSEXT
+ ssl_enabled_t strict_sni_vhost_check;
+#endif
+#ifndef OPENSSL_NO_COMP
+ BOOL compression;
+#endif
+ BOOL session_tickets;
+};
+
+/**
+ * Define the mod_ssl per-directory configuration structure
+ * (i.e. the local configuration for all &lt;Directory>
+ * and .htaccess contexts)
+ */
+struct SSLDirConfigRec {
+ BOOL bSSLRequired;
+ apr_array_header_t *aRequirement;
+ ssl_opt_t nOptions;
+ ssl_opt_t nOptionsAdd;
+ ssl_opt_t nOptionsDel;
+ const char *szCipherSuite;
+ ssl_verify_t nVerifyClient;
+ int nVerifyDepth;
+ const char *szUserName;
+ apr_size_t nRenegBufferSize;
+
+ modssl_ctx_t *proxy;
+ BOOL proxy_enabled;
+ BOOL proxy_post_config;
+};
+
+/**
+ * function prototypes
+ */
+
+/** API glue structures */
+extern module AP_MODULE_DECLARE_DATA ssl_module;
+
+/** configuration handling */
+SSLModConfigRec *ssl_config_global_create(server_rec *);
+void ssl_config_global_fix(SSLModConfigRec *);
+BOOL ssl_config_global_isfixed(SSLModConfigRec *);
+void *ssl_config_server_create(apr_pool_t *, server_rec *);
+void *ssl_config_server_merge(apr_pool_t *, void *, void *);
+void *ssl_config_perdir_create(apr_pool_t *, char *);
+void *ssl_config_perdir_merge(apr_pool_t *, void *, void *);
+void ssl_config_proxy_merge(apr_pool_t *,
+ SSLDirConfigRec *, SSLDirConfigRec *);
+const char *ssl_cmd_SSLPassPhraseDialog(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLCryptoDevice(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLRandomSeed(cmd_parms *, void *, const char *, const char *, const char *);
+const char *ssl_cmd_SSLEngine(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLCipherSuite(cmd_parms *, void *, const char *, const char *);
+const char *ssl_cmd_SSLCertificateFile(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLCertificateKeyFile(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLCertificateChainFile(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLCACertificatePath(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLCACertificateFile(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLCADNRequestPath(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLCADNRequestFile(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLCARevocationPath(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLCARevocationFile(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLCARevocationCheck(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLHonorCipherOrder(cmd_parms *cmd, void *dcfg, int flag);
+const char *ssl_cmd_SSLCompression(cmd_parms *, void *, int flag);
+const char *ssl_cmd_SSLSessionTickets(cmd_parms *, void *, int flag);
+const char *ssl_cmd_SSLVerifyClient(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLVerifyDepth(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLSessionCache(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLSessionCacheTimeout(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLProtocol(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLOptions(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLRequireSSL(cmd_parms *, void *);
+const char *ssl_cmd_SSLRequire(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLUserName(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLRenegBufferSize(cmd_parms *cmd, void *dcfg, const char *arg);
+const char *ssl_cmd_SSLStrictSNIVHostCheck(cmd_parms *cmd, void *dcfg, int flag);
+const char *ssl_cmd_SSLInsecureRenegotiation(cmd_parms *cmd, void *dcfg, int flag);
+
+const char *ssl_cmd_SSLProxyEngine(cmd_parms *cmd, void *dcfg, int flag);
+const char *ssl_cmd_SSLProxyProtocol(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLProxyCipherSuite(cmd_parms *, void *, const char *, const char *);
+const char *ssl_cmd_SSLProxyVerify(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLProxyVerifyDepth(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLProxyCACertificatePath(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLProxyCACertificateFile(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLProxyCARevocationPath(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLProxyCARevocationFile(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLProxyCARevocationCheck(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLProxyMachineCertificatePath(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLProxyMachineCertificateFile(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLProxyMachineCertificateChainFile(cmd_parms *, void *, const char *);
+#ifdef HAVE_TLS_SESSION_TICKETS
+const char *ssl_cmd_SSLSessionTicketKeyFile(cmd_parms *cmd, void *dcfg, const char *arg);
+#endif
+const char *ssl_cmd_SSLProxyCheckPeerExpire(cmd_parms *cmd, void *dcfg, int flag);
+const char *ssl_cmd_SSLProxyCheckPeerCN(cmd_parms *cmd, void *dcfg, int flag);
+const char *ssl_cmd_SSLProxyCheckPeerName(cmd_parms *cmd, void *dcfg, int flag);
+
+const char *ssl_cmd_SSLOCSPOverrideResponder(cmd_parms *cmd, void *dcfg, int flag);
+const char *ssl_cmd_SSLOCSPDefaultResponder(cmd_parms *cmd, void *dcfg, const char *arg);
+const char *ssl_cmd_SSLOCSPResponseTimeSkew(cmd_parms *cmd, void *dcfg, const char *arg);
+const char *ssl_cmd_SSLOCSPResponseMaxAge(cmd_parms *cmd, void *dcfg, const char *arg);
+const char *ssl_cmd_SSLOCSPResponderTimeout(cmd_parms *cmd, void *dcfg, const char *arg);
+const char *ssl_cmd_SSLOCSPUseRequestNonce(cmd_parms *cmd, void *dcfg, int flag);
+const char *ssl_cmd_SSLOCSPEnable(cmd_parms *cmd, void *dcfg, const char *arg);
+const char *ssl_cmd_SSLOCSPProxyURL(cmd_parms *cmd, void *dcfg, const char *arg);
+
+/* Declare OCSP Responder Certificate Verification Directive */
+const char *ssl_cmd_SSLOCSPNoVerify(cmd_parms *cmd, void *dcfg, int flag);
+/* Declare OCSP Responder Certificate File Directive */
+const char *ssl_cmd_SSLOCSPResponderCertificateFile(cmd_parms *cmd, void *dcfg, const char *arg);
+
+#ifdef HAVE_SSL_CONF_CMD
+const char *ssl_cmd_SSLOpenSSLConfCmd(cmd_parms *cmd, void *dcfg, const char *arg1, const char *arg2);
+#endif
+
+#ifdef HAVE_SRP
+const char *ssl_cmd_SSLSRPVerifierFile(cmd_parms *cmd, void *dcfg, const char *arg);
+const char *ssl_cmd_SSLSRPUnknownUserSeed(cmd_parms *cmd, void *dcfg, const char *arg);
+#endif
+
+const char *ssl_cmd_SSLFIPS(cmd_parms *cmd, void *dcfg, int flag);
+
+/** module initialization */
+apr_status_t ssl_init_Module(apr_pool_t *, apr_pool_t *, apr_pool_t *, server_rec *);
+apr_status_t ssl_init_Engine(server_rec *, apr_pool_t *);
+apr_status_t ssl_init_ConfigureServer(server_rec *, apr_pool_t *, apr_pool_t *, SSLSrvConfigRec *,
+ apr_array_header_t *);
+apr_status_t ssl_init_CheckServers(server_rec *, apr_pool_t *);
+int ssl_proxy_section_post_config(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s,
+ ap_conf_vector_t *section_config);
+STACK_OF(X509_NAME)
+ *ssl_init_FindCAList(server_rec *, apr_pool_t *, const char *, const char *);
+void ssl_init_Child(apr_pool_t *, server_rec *);
+apr_status_t ssl_init_ModuleKill(void *data);
+
+/** Apache API hooks */
+int ssl_hook_Auth(request_rec *);
+int ssl_hook_UserCheck(request_rec *);
+int ssl_hook_Access(request_rec *);
+int ssl_hook_Fixup(request_rec *);
+int ssl_hook_ReadReq(request_rec *);
+int ssl_hook_Upgrade(request_rec *);
+void ssl_hook_ConfigTest(apr_pool_t *pconf, server_rec *s);
+
+/** Apache authz provisders */
+extern const authz_provider ssl_authz_provider_require_ssl;
+extern const authz_provider ssl_authz_provider_verify_client;
+
+/** OpenSSL callbacks */
+DH *ssl_callback_TmpDH(SSL *, int, int);
+int ssl_callback_SSLVerify(int, X509_STORE_CTX *);
+int ssl_callback_SSLVerify_CRL(int, X509_STORE_CTX *, conn_rec *);
+int ssl_callback_proxy_cert(SSL *ssl, X509 **x509, EVP_PKEY **pkey);
+int ssl_callback_NewSessionCacheEntry(SSL *, SSL_SESSION *);
+SSL_SESSION *ssl_callback_GetSessionCacheEntry(SSL *, IDCONST unsigned char *, int, int *);
+void ssl_callback_DelSessionCacheEntry(SSL_CTX *, SSL_SESSION *);
+void ssl_callback_Info(const SSL *, int, int);
+#ifdef HAVE_TLSEXT
+int ssl_callback_ServerNameIndication(SSL *, int *, modssl_ctx_t *);
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
+int ssl_callback_ClientHello(SSL *, int *, void *);
+#endif
+#ifdef HAVE_TLS_SESSION_TICKETS
+int ssl_callback_SessionTicket(SSL *ssl,
+ unsigned char *keyname,
+ unsigned char *iv,
+ EVP_CIPHER_CTX *cipher_ctx,
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ HMAC_CTX *hmac_ctx,
+#else
+ EVP_MAC_CTX *mac_ctx,
+#endif
+ int mode);
+#endif
+
+#ifdef HAVE_TLS_ALPN
+int ssl_callback_alpn_select(SSL *ssl, const unsigned char **out,
+ unsigned char *outlen, const unsigned char *in,
+ unsigned int inlen, void *arg);
+#endif
+
+/** Session Cache Support */
+apr_status_t ssl_scache_init(server_rec *, apr_pool_t *);
+void ssl_scache_status_register(apr_pool_t *p);
+void ssl_scache_kill(server_rec *);
+BOOL ssl_scache_store(server_rec *, IDCONST UCHAR *, int,
+ apr_time_t, SSL_SESSION *, apr_pool_t *);
+SSL_SESSION *ssl_scache_retrieve(server_rec *, IDCONST UCHAR *, int, apr_pool_t *);
+void ssl_scache_remove(server_rec *, IDCONST UCHAR *, int,
+ apr_pool_t *);
+
+/** OCSP Stapling Support */
+#ifdef HAVE_OCSP_STAPLING
+const char *ssl_cmd_SSLStaplingCache(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLUseStapling(cmd_parms *, void *, int);
+const char *ssl_cmd_SSLStaplingResponseTimeSkew(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLStaplingResponseMaxAge(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLStaplingStandardCacheTimeout(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLStaplingErrorCacheTimeout(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLStaplingReturnResponderErrors(cmd_parms *, void *, int);
+const char *ssl_cmd_SSLStaplingFakeTryLater(cmd_parms *, void *, int);
+const char *ssl_cmd_SSLStaplingResponderTimeout(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLStaplingForceURL(cmd_parms *, void *, const char *);
+apr_status_t modssl_init_stapling(server_rec *, apr_pool_t *, apr_pool_t *, modssl_ctx_t *);
+void ssl_stapling_certinfo_hash_init(apr_pool_t *);
+int ssl_stapling_init_cert(server_rec *, apr_pool_t *, apr_pool_t *,
+ modssl_ctx_t *, X509 *);
+#endif
+#ifdef HAVE_SRP
+int ssl_callback_SRPServerParams(SSL *, int *, void *);
+#endif
+
+#ifdef HAVE_OPENSSL_KEYLOG
+/* Callback used with SSL_CTX_set_keylog_callback. */
+void modssl_callback_keylog(const SSL *ssl, const char *line);
+#endif
+
+/** I/O */
+void ssl_io_filter_init(conn_rec *, request_rec *r, SSL *);
+void ssl_io_filter_register(apr_pool_t *);
+long ssl_io_data_cb(BIO *, int, const char *, int, long, long);
+
+/* ssl_io_buffer_fill fills the setaside buffering of the HTTP request
+ * to allow an SSL renegotiation to take place. */
+int ssl_io_buffer_fill(request_rec *r, apr_size_t maxlen);
+
+/** PRNG */
+int ssl_rand_seed(server_rec *, apr_pool_t *, ssl_rsctx_t, char *);
+
+/** Utility Functions */
+char *ssl_util_vhostid(apr_pool_t *, server_rec *);
+apr_file_t *ssl_util_ppopen(server_rec *, apr_pool_t *, const char *,
+ const char * const *);
+void ssl_util_ppclose(server_rec *, apr_pool_t *, apr_file_t *);
+char *ssl_util_readfilter(server_rec *, apr_pool_t *, const char *,
+ const char * const *);
+BOOL ssl_util_path_check(ssl_pathcheck_t, const char *, apr_pool_t *);
+#if APR_HAS_THREADS && MODSSL_USE_OPENSSL_PRE_1_1_API
+void ssl_util_thread_setup(apr_pool_t *);
+void ssl_util_thread_id_setup(apr_pool_t *);
+#endif
+int ssl_init_ssl_connection(conn_rec *c, request_rec *r);
+
+BOOL ssl_util_vhost_matches(const char *servername, server_rec *s);
+
+/** Pass Phrase Support */
+apr_status_t ssl_load_encrypted_pkey(server_rec *, apr_pool_t *, int,
+ const char *, apr_array_header_t **);
+
+/* Load public and/or private key from the configured ENGINE. Private
+ * key returned as *pkey. certid can be NULL, in which case *pubkey
+ * is not altered. Errors logged on failure. */
+apr_status_t modssl_load_engine_keypair(server_rec *s, apr_pool_t *p,
+ const char *vhostid,
+ const char *certid, const char *keyid,
+ X509 **pubkey, EVP_PKEY **privkey);
+
+/** Diffie-Hellman Parameter Support */
+DH *ssl_dh_GetParamFromFile(const char *);
+#ifdef HAVE_ECC
+EC_GROUP *ssl_ec_GetParamFromFile(const char *);
+#endif
+
+/* Store the EVP_PKEY key (serialized into DER) in the hash table with
+ * key, returning the ssl_asn1_t structure pointer. */
+ssl_asn1_t *ssl_asn1_table_set(apr_hash_t *table, const char *key,
+ EVP_PKEY *pkey);
+/* Retrieve the ssl_asn1_t structure with given key from the hash. */
+ssl_asn1_t *ssl_asn1_table_get(apr_hash_t *table, const char *key);
+/* Remove and free the ssl_asn1_t structure with given key. */
+void ssl_asn1_table_unset(apr_hash_t *table, const char *key);
+
+/** Mutex Support */
+int ssl_mutex_init(server_rec *, apr_pool_t *);
+int ssl_mutex_reinit(server_rec *, apr_pool_t *);
+int ssl_mutex_on(server_rec *);
+int ssl_mutex_off(server_rec *);
+
+int ssl_stapling_mutex_reinit(server_rec *, apr_pool_t *);
+
+/* mutex type names for Mutex directive */
+#define SSL_CACHE_MUTEX_TYPE "ssl-cache"
+#define SSL_STAPLING_CACHE_MUTEX_TYPE "ssl-stapling"
+#define SSL_STAPLING_REFRESH_MUTEX_TYPE "ssl-stapling-refresh"
+
+apr_status_t ssl_die(server_rec *);
+
+/** Logfile Support */
+void ssl_log_ssl_error(const char *, int, int, server_rec *);
+
+/* ssl_log_xerror, ssl_log_cxerror and ssl_log_rxerror are wrappers for the
+ * respective ap_log_*error functions and take a certificate as an
+ * additional argument (whose details are appended to the log message).
+ * The other arguments are interpreted exactly as with their ap_log_*error
+ * counterparts. */
+void ssl_log_xerror(const char *file, int line, int level,
+ apr_status_t rv, apr_pool_t *p, server_rec *s,
+ X509 *cert, const char *format, ...)
+ __attribute__((format(printf,8,9)));
+
+void ssl_log_cxerror(const char *file, int line, int level,
+ apr_status_t rv, conn_rec *c, X509 *cert,
+ const char *format, ...)
+ __attribute__((format(printf,7,8)));
+
+void ssl_log_rxerror(const char *file, int line, int level,
+ apr_status_t rv, request_rec *r, X509 *cert,
+ const char *format, ...)
+ __attribute__((format(printf,7,8)));
+
+#define SSLLOG_MARK __FILE__,__LINE__
+
+/** Variables */
+
+/* Register variables for the lifetime of the process pool 'p'. */
+void ssl_var_register(apr_pool_t *p);
+char *ssl_var_lookup(apr_pool_t *, server_rec *, conn_rec *, request_rec *, char *);
+apr_array_header_t *ssl_ext_list(apr_pool_t *p, conn_rec *c, int peer, const char *extension);
+
+void ssl_var_log_config_register(apr_pool_t *p);
+
+/* Extract SSL_*_DN_* variables into table 't' from SSL object 'ssl',
+ * allocating from 'p': */
+void modssl_var_extract_dns(apr_table_t *t, SSL *ssl, apr_pool_t *p);
+
+/* Extract SSL_*_SAN_* variables (subjectAltName entries) into table 't'
+ * from SSL object 'ssl', allocating from 'p'. */
+void modssl_var_extract_san_entries(apr_table_t *t, SSL *ssl, apr_pool_t *p);
+
+#ifndef OPENSSL_NO_OCSP
+/* Perform OCSP validation of the current cert in the given context.
+ * Returns non-zero on success or zero on failure. On failure, the
+ * context error code is set. */
+int modssl_verify_ocsp(X509_STORE_CTX *ctx, SSLSrvConfigRec *sc,
+ server_rec *s, conn_rec *c, apr_pool_t *pool);
+
+/* OCSP helper interface; dispatches the given OCSP request to the
+ * responder at the given URI. Returns the decoded OCSP response
+ * object, or NULL on error (in which case, errors will have been
+ * logged). Pool 'p' is used for temporary allocations. */
+OCSP_RESPONSE *modssl_dispatch_ocsp_request(const apr_uri_t *uri,
+ apr_interval_time_t timeout,
+ OCSP_REQUEST *request,
+ conn_rec *c, apr_pool_t *p);
+
+/* Initialize OCSP trusted certificate list */
+void ssl_init_ocsp_certificates(server_rec *s, modssl_ctx_t *mctx);
+
+#endif
+
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
+/* Retrieve DH parameters for given key length. Return value should
+ * be treated as unmutable, since it is stored in process-global
+ * memory. */
+DH *modssl_get_dh_params(unsigned keylen);
+#endif
+
+/* Returns non-zero if the request was made over SSL/TLS. If sslconn
+ * is non-NULL and the request is using SSL/TLS, sets *sslconn to the
+ * corresponding SSLConnRec structure for the connection. */
+int modssl_request_is_tls(const request_rec *r, SSLConnRec **sslconn);
+
+int ssl_is_challenge(conn_rec *c, const char *servername,
+ X509 **pcert, EVP_PKEY **pkey,
+ const char **pcert_file, const char **pkey_file);
+
+/* Returns non-zero if the cert/key filename should be handled through
+ * the configured ENGINE. */
+int modssl_is_engine_id(const char *name);
+
+#endif /* SSL_PRIVATE_H */
+/** @} */
+
diff --git a/modules/ssl/ssl_scache.c b/modules/ssl/ssl_scache.c
new file mode 100644
index 0000000..c0a0950
--- /dev/null
+++ b/modules/ssl/ssl_scache.c
@@ -0,0 +1,239 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+/* _ _
+ * _ __ ___ ___ __| | ___ ___| | mod_ssl
+ * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| | \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ * |_____|
+ * ssl_scache.c
+ * Session Cache Abstraction
+ */
+ /* ``Open-Source Software: generous
+ programmers from around the world all
+ join forces to help you shoot
+ yourself in the foot for free.''
+ -- Unknown */
+#include "ssl_private.h"
+#include "mod_status.h"
+
+/* _________________________________________________________________
+**
+** Session Cache: Common Abstraction Layer
+** _________________________________________________________________
+*/
+
+apr_status_t ssl_scache_init(server_rec *s, apr_pool_t *p)
+{
+ SSLModConfigRec *mc = myModConfig(s);
+ apr_status_t rv;
+ struct ap_socache_hints hints;
+
+ /* The very first invocation of this function will be the
+ * post_config invocation during server startup; do nothing for
+ * this first (and only the first) time through, since the pool
+ * will be immediately cleared anyway. For every subsequent
+ * invocation, initialize the configured cache. */
+ if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG)
+ return APR_SUCCESS;
+
+#ifdef HAVE_OCSP_STAPLING
+ if (mc->stapling_cache) {
+ memset(&hints, 0, sizeof hints);
+ hints.avg_obj_size = 1500;
+ hints.avg_id_len = 20;
+ hints.expiry_interval = 300;
+
+ rv = mc->stapling_cache->init(mc->stapling_cache_context,
+ "mod_ssl-staple", &hints, s, p);
+ if (rv) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01872)
+ "Could not initialize stapling cache. Exiting.");
+ return ssl_die(s);
+ }
+ }
+#endif
+
+ /*
+ * Warn the user that he should use the session cache.
+ * But we can operate without it, of course.
+ */
+ if (mc->sesscache == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(01873)
+ "Init: Session Cache is not configured "
+ "[hint: SSLSessionCache]");
+ return APR_SUCCESS;
+ }
+
+ memset(&hints, 0, sizeof hints);
+ hints.avg_obj_size = 150;
+ hints.avg_id_len = 30;
+ hints.expiry_interval = 30;
+
+ rv = mc->sesscache->init(mc->sesscache_context, "mod_ssl-sess", &hints, s, p);
+ if (rv) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01874)
+ "Could not initialize session cache. Exiting.");
+ return ssl_die(s);
+ }
+
+ return APR_SUCCESS;
+}
+
+void ssl_scache_kill(server_rec *s)
+{
+ SSLModConfigRec *mc = myModConfig(s);
+
+ if (mc->sesscache) {
+ mc->sesscache->destroy(mc->sesscache_context, s);
+ }
+
+#ifdef HAVE_OCSP_STAPLING
+ if (mc->stapling_cache) {
+ mc->stapling_cache->destroy(mc->stapling_cache_context, s);
+ }
+#endif
+
+}
+
+BOOL ssl_scache_store(server_rec *s, IDCONST UCHAR *id, int idlen,
+ apr_time_t expiry, SSL_SESSION *sess,
+ apr_pool_t *p)
+{
+ SSLModConfigRec *mc = myModConfig(s);
+ unsigned char encoded[MODSSL_SESSION_MAX_DER], *ptr;
+ unsigned int len;
+ apr_status_t rv;
+
+ /* Serialise the session. */
+ len = i2d_SSL_SESSION(sess, NULL);
+ if (len > sizeof encoded) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01875)
+ "session is too big (%u bytes)", len);
+ return FALSE;
+ }
+
+ ptr = encoded;
+ len = i2d_SSL_SESSION(sess, &ptr);
+
+ if (mc->sesscache->flags & AP_SOCACHE_FLAG_NOTMPSAFE) {
+ ssl_mutex_on(s);
+ }
+
+ rv = mc->sesscache->store(mc->sesscache_context, s, id, idlen,
+ expiry, encoded, len, p);
+
+ if (mc->sesscache->flags & AP_SOCACHE_FLAG_NOTMPSAFE) {
+ ssl_mutex_off(s);
+ }
+
+ return rv == APR_SUCCESS ? TRUE : FALSE;
+}
+
+SSL_SESSION *ssl_scache_retrieve(server_rec *s, IDCONST UCHAR *id, int idlen,
+ apr_pool_t *p)
+{
+ SSLModConfigRec *mc = myModConfig(s);
+ unsigned char dest[MODSSL_SESSION_MAX_DER];
+ unsigned int destlen = MODSSL_SESSION_MAX_DER;
+ const unsigned char *ptr;
+ apr_status_t rv;
+
+ if (mc->sesscache->flags & AP_SOCACHE_FLAG_NOTMPSAFE) {
+ ssl_mutex_on(s);
+ }
+
+ rv = mc->sesscache->retrieve(mc->sesscache_context, s, id, idlen,
+ dest, &destlen, p);
+
+ if (mc->sesscache->flags & AP_SOCACHE_FLAG_NOTMPSAFE) {
+ ssl_mutex_off(s);
+ }
+
+ if (rv != APR_SUCCESS) {
+ return NULL;
+ }
+
+ ptr = dest;
+
+ return d2i_SSL_SESSION(NULL, &ptr, destlen);
+}
+
+void ssl_scache_remove(server_rec *s, IDCONST UCHAR *id, int idlen,
+ apr_pool_t *p)
+{
+ SSLModConfigRec *mc = myModConfig(s);
+
+ if (mc->sesscache->flags & AP_SOCACHE_FLAG_NOTMPSAFE) {
+ ssl_mutex_on(s);
+ }
+
+ mc->sesscache->remove(mc->sesscache_context, s, id, idlen, p);
+
+ if (mc->sesscache->flags & AP_SOCACHE_FLAG_NOTMPSAFE) {
+ ssl_mutex_off(s);
+ }
+}
+
+/* _________________________________________________________________
+**
+** SSL Extension to mod_status
+** _________________________________________________________________
+*/
+static int ssl_ext_status_hook(request_rec *r, int flags)
+{
+ SSLModConfigRec *mc = myModConfig(r->server);
+
+ if (mc == NULL || mc->sesscache == NULL)
+ return OK;
+
+ if (!(flags & AP_STATUS_SHORT)) {
+ ap_rputs("<hr>\n", r);
+ ap_rputs("<table cellspacing=0 cellpadding=0>\n", r);
+ ap_rputs("<tr><td bgcolor=\"#000000\">\n", r);
+ ap_rputs("<b><font color=\"#ffffff\" face=\"Arial,Helvetica\">SSL/TLS Session Cache Status:</font></b>\r", r);
+ ap_rputs("</td></tr>\n", r);
+ ap_rputs("<tr><td bgcolor=\"#ffffff\">\n", r);
+ }
+ else {
+ ap_rputs("TLSSessionCacheStatus\n", r);
+ }
+
+ if (mc->sesscache->flags & AP_SOCACHE_FLAG_NOTMPSAFE) {
+ ssl_mutex_on(r->server);
+ }
+
+ mc->sesscache->status(mc->sesscache_context, r, flags);
+
+ if (mc->sesscache->flags & AP_SOCACHE_FLAG_NOTMPSAFE) {
+ ssl_mutex_off(r->server);
+ }
+
+ if (!(flags & AP_STATUS_SHORT)) {
+ ap_rputs("</td></tr>\n", r);
+ ap_rputs("</table>\n", r);
+ }
+
+ return OK;
+}
+
+void ssl_scache_status_register(apr_pool_t *p)
+{
+ APR_OPTIONAL_HOOK(ap, status_hook, ssl_ext_status_hook, NULL, NULL,
+ APR_HOOK_MIDDLE);
+}
+
diff --git a/modules/ssl/ssl_util.c b/modules/ssl/ssl_util.c
new file mode 100644
index 0000000..c889295
--- /dev/null
+++ b/modules/ssl/ssl_util.c
@@ -0,0 +1,485 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+/* _ _
+ * _ __ ___ ___ __| | ___ ___| | mod_ssl
+ * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| | \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ * |_____|
+ * ssl_util.c
+ * Utility Functions
+ */
+ /* ``Every day of my life
+ I am forced to add another
+ name to the list of people
+ who piss me off!''
+ -- Calvin */
+
+#include "ssl_private.h"
+#include "ap_mpm.h"
+#include "apr_thread_mutex.h"
+
+/* _________________________________________________________________
+**
+** Utility Functions
+** _________________________________________________________________
+*/
+
+char *ssl_util_vhostid(apr_pool_t *p, server_rec *s)
+{
+ SSLSrvConfigRec *sc;
+ apr_port_t port;
+
+ if (s->port != 0)
+ port = s->port;
+ else {
+ sc = mySrvConfig(s);
+ port = sc->enabled == TRUE ? DEFAULT_HTTPS_PORT : DEFAULT_HTTP_PORT;
+ }
+
+ return apr_psprintf(p, "%s:%lu", s->server_hostname, (unsigned long)port);
+}
+
+/*
+ * Return TRUE iff the given servername matches the server record when
+ * selecting virtual hosts.
+ */
+BOOL ssl_util_vhost_matches(const char *servername, server_rec *s)
+{
+ apr_array_header_t *names;
+ int i;
+
+ /* check ServerName */
+ if (!strcasecmp(servername, s->server_hostname)) {
+ return TRUE;
+ }
+
+ /*
+ * if not matched yet, check ServerAlias entries
+ * (adapted from vhost.c:matches_aliases())
+ */
+ names = s->names;
+ if (names) {
+ char **name = (char **)names->elts;
+ for (i = 0; i < names->nelts; ++i) {
+ if (!name[i])
+ continue;
+ if (!strcasecmp(servername, name[i])) {
+ return TRUE;
+ }
+ }
+ }
+
+ /* if still no match, check ServerAlias entries with wildcards */
+ names = s->wild_names;
+ if (names) {
+ char **name = (char **)names->elts;
+ for (i = 0; i < names->nelts; ++i) {
+ if (!name[i])
+ continue;
+ if (!ap_strcasecmp_match(servername, name[i])) {
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+int modssl_request_is_tls(const request_rec *r, SSLConnRec **scout)
+{
+ SSLConnRec *sslconn = myConnConfig(r->connection);
+ SSLSrvConfigRec *sc = mySrvConfig(r->server);
+
+ if (!(sslconn && sslconn->ssl) && r->connection->master) {
+ sslconn = myConnConfig(r->connection->master);
+ }
+
+ if (sc->enabled == SSL_ENABLED_FALSE || !sslconn || !sslconn->ssl)
+ return 0;
+
+ if (scout) *scout = sslconn;
+
+ return 1;
+}
+
+apr_file_t *ssl_util_ppopen(server_rec *s, apr_pool_t *p, const char *cmd,
+ const char * const *argv)
+{
+ apr_procattr_t *procattr;
+ apr_proc_t *proc;
+
+ if (apr_procattr_create(&procattr, p) != APR_SUCCESS)
+ return NULL;
+ if (apr_procattr_io_set(procattr, APR_FULL_BLOCK, APR_FULL_BLOCK,
+ APR_FULL_BLOCK) != APR_SUCCESS)
+ return NULL;
+ if (apr_procattr_dir_set(procattr,
+ ap_make_dirstr_parent(p, cmd)) != APR_SUCCESS)
+ return NULL;
+ if (apr_procattr_cmdtype_set(procattr, APR_PROGRAM) != APR_SUCCESS)
+ return NULL;
+ proc = apr_pcalloc(p, sizeof(apr_proc_t));
+ if (apr_proc_create(proc, cmd, argv, NULL, procattr, p) != APR_SUCCESS)
+ return NULL;
+ return proc->out;
+}
+
+void ssl_util_ppclose(server_rec *s, apr_pool_t *p, apr_file_t *fp)
+{
+ apr_file_close(fp);
+ return;
+}
+
+/*
+ * Run a filter program and read the first line of its stdout output
+ */
+char *ssl_util_readfilter(server_rec *s, apr_pool_t *p, const char *cmd,
+ const char * const *argv)
+{
+ static char buf[MAX_STRING_LEN];
+ apr_file_t *fp;
+ apr_size_t nbytes = 1;
+ char c;
+ int k;
+
+ if ((fp = ssl_util_ppopen(s, p, cmd, argv)) == NULL)
+ return NULL;
+ /* XXX: we are reading 1 byte at a time here */
+ for (k = 0; apr_file_read(fp, &c, &nbytes) == APR_SUCCESS
+ && nbytes == 1 && (k < MAX_STRING_LEN-1) ; ) {
+ if (c == '\n' || c == '\r')
+ break;
+ buf[k++] = c;
+ }
+ buf[k] = NUL;
+ ssl_util_ppclose(s, p, fp);
+
+ return buf;
+}
+
+BOOL ssl_util_path_check(ssl_pathcheck_t pcm, const char *path, apr_pool_t *p)
+{
+ apr_finfo_t finfo;
+
+ if (path == NULL)
+ return FALSE;
+ if (pcm & SSL_PCM_EXISTS && apr_stat(&finfo, path,
+ APR_FINFO_TYPE|APR_FINFO_SIZE, p) != 0)
+ return FALSE;
+ AP_DEBUG_ASSERT((pcm & SSL_PCM_EXISTS) ||
+ !(pcm & (SSL_PCM_ISREG|SSL_PCM_ISDIR|SSL_PCM_ISNONZERO)));
+ if (pcm & SSL_PCM_ISREG && finfo.filetype != APR_REG)
+ return FALSE;
+ if (pcm & SSL_PCM_ISDIR && finfo.filetype != APR_DIR)
+ return FALSE;
+ if (pcm & SSL_PCM_ISNONZERO && finfo.size <= 0)
+ return FALSE;
+ return TRUE;
+}
+
+/* Decrypted private keys are cached to survive restarts. The cached
+ * data must have lifetime of the process (hence malloc/free rather
+ * than pools), and uses raw DER since the EVP_PKEY structure
+ * internals may not survive across a module reload. */
+ssl_asn1_t *ssl_asn1_table_set(apr_hash_t *table, const char *key,
+ EVP_PKEY *pkey)
+{
+ apr_ssize_t klen = strlen(key);
+ ssl_asn1_t *asn1 = apr_hash_get(table, key, klen);
+ apr_size_t length = i2d_PrivateKey(pkey, NULL);
+ unsigned char *p;
+
+ /* Re-use structure if cached previously. */
+ if (asn1) {
+ if (asn1->nData != length) {
+ asn1->cpData = ap_realloc(asn1->cpData, length);
+ }
+ }
+ else {
+ asn1 = ap_malloc(sizeof(*asn1));
+ asn1->source_mtime = 0; /* used as a note for encrypted private keys */
+ asn1->cpData = ap_malloc(length);
+
+ apr_hash_set(table, key, klen, asn1);
+ }
+
+ asn1->nData = length;
+ p = asn1->cpData;
+ i2d_PrivateKey(pkey, &p); /* increases p by length */
+
+ return asn1;
+}
+
+ssl_asn1_t *ssl_asn1_table_get(apr_hash_t *table,
+ const char *key)
+{
+ return (ssl_asn1_t *)apr_hash_get(table, key, APR_HASH_KEY_STRING);
+}
+
+void ssl_asn1_table_unset(apr_hash_t *table,
+ const char *key)
+{
+ apr_ssize_t klen = strlen(key);
+ ssl_asn1_t *asn1 = apr_hash_get(table, key, klen);
+
+ if (!asn1) {
+ return;
+ }
+
+ if (asn1->cpData) {
+ free(asn1->cpData);
+ }
+ free(asn1);
+
+ apr_hash_set(table, key, klen, NULL);
+}
+
+#if APR_HAS_THREADS && MODSSL_USE_OPENSSL_PRE_1_1_API
+
+/*
+ * To ensure thread-safetyness in OpenSSL - work in progress
+ */
+
+static apr_thread_mutex_t **lock_cs;
+static int lock_num_locks;
+
+static void ssl_util_thr_lock(int mode, int type,
+ const char *file, int line)
+{
+ if (type < lock_num_locks) {
+ if (mode & CRYPTO_LOCK) {
+ apr_thread_mutex_lock(lock_cs[type]);
+ }
+ else {
+ apr_thread_mutex_unlock(lock_cs[type]);
+ }
+ }
+}
+
+/* Dynamic lock structure */
+struct CRYPTO_dynlock_value {
+ apr_pool_t *pool;
+ const char* file;
+ int line;
+ apr_thread_mutex_t *mutex;
+};
+
+/* Global reference to the pool passed into ssl_util_thread_setup() */
+apr_pool_t *dynlockpool = NULL;
+
+/*
+ * Dynamic lock creation callback
+ */
+static struct CRYPTO_dynlock_value *ssl_dyn_create_function(const char *file,
+ int line)
+{
+ struct CRYPTO_dynlock_value *value;
+ apr_pool_t *p;
+ apr_status_t rv;
+
+ /*
+ * We need a pool to allocate our mutex. Since we can't clear
+ * allocated memory from a pool, create a subpool that we can blow
+ * away in the destruction callback.
+ */
+ apr_pool_create(&p, dynlockpool);
+ apr_pool_tag(p, "modssl_dynlock_value");
+ ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_TRACE1, 0, p,
+ "Creating dynamic lock");
+
+ value = apr_palloc(p, sizeof(struct CRYPTO_dynlock_value));
+ value->pool = p;
+ /* Keep our own copy of the place from which we were created,
+ using our own pool. */
+ value->file = apr_pstrdup(p, file);
+ value->line = line;
+ rv = apr_thread_mutex_create(&(value->mutex), APR_THREAD_MUTEX_DEFAULT,
+ p);
+ if (rv != APR_SUCCESS) {
+ ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_ERR, rv, p, APLOGNO(02186)
+ "Failed to create thread mutex for dynamic lock");
+ apr_pool_destroy(p);
+ return NULL;
+ }
+ return value;
+}
+
+/*
+ * Dynamic locking and unlocking function
+ */
+
+static void ssl_dyn_lock_function(int mode, struct CRYPTO_dynlock_value *l,
+ const char *file, int line)
+{
+ apr_status_t rv;
+
+ if (mode & CRYPTO_LOCK) {
+ ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_TRACE3, 0, l->pool,
+ "Acquiring mutex %s:%d", l->file, l->line);
+ rv = apr_thread_mutex_lock(l->mutex);
+ ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_TRACE3, rv, l->pool,
+ "Mutex %s:%d acquired!", l->file, l->line);
+ }
+ else {
+ ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_TRACE3, 0, l->pool,
+ "Releasing mutex %s:%d", l->file, l->line);
+ rv = apr_thread_mutex_unlock(l->mutex);
+ ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_TRACE3, rv, l->pool,
+ "Mutex %s:%d released!", l->file, l->line);
+ }
+}
+
+/*
+ * Dynamic lock destruction callback
+ */
+static void ssl_dyn_destroy_function(struct CRYPTO_dynlock_value *l,
+ const char *file, int line)
+{
+ apr_status_t rv;
+
+ ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_TRACE1, 0, l->pool,
+ "Destroying dynamic lock %s:%d", l->file, l->line);
+ rv = apr_thread_mutex_destroy(l->mutex);
+ if (rv != APR_SUCCESS) {
+ ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_ERR, rv, l->pool,
+ APLOGNO(02192) "Failed to destroy mutex for dynamic "
+ "lock %s:%d", l->file, l->line);
+ }
+
+ /* Trust that whomever owned the CRYPTO_dynlock_value we were
+ * passed has no future use for it...
+ */
+ apr_pool_destroy(l->pool);
+}
+
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+
+static void ssl_util_thr_id(CRYPTO_THREADID *id)
+{
+ /* OpenSSL needs this to return an unsigned long. On OS/390, the pthread
+ * id is a structure twice that big. Use the TCB pointer instead as a
+ * unique unsigned long.
+ */
+#ifdef __MVS__
+ struct PSA {
+ char unmapped[540]; /* PSATOLD is at offset 540 in the PSA */
+ unsigned long PSATOLD;
+ } *psaptr = 0; /* PSA is at address 0 */
+
+ CRYPTO_THREADID_set_numeric(id, psaptr->PSATOLD);
+#else
+ CRYPTO_THREADID_set_numeric(id, (unsigned long) apr_os_thread_current());
+#endif
+}
+
+static apr_status_t ssl_util_thr_id_cleanup(void *old)
+{
+ CRYPTO_THREADID_set_callback(old);
+ return APR_SUCCESS;
+}
+
+#else
+
+static unsigned long ssl_util_thr_id(void)
+{
+ /* OpenSSL needs this to return an unsigned long. On OS/390, the pthread
+ * id is a structure twice that big. Use the TCB pointer instead as a
+ * unique unsigned long.
+ */
+#ifdef __MVS__
+ struct PSA {
+ char unmapped[540];
+ unsigned long PSATOLD;
+ } *psaptr = 0;
+
+ return psaptr->PSATOLD;
+#else
+ return (unsigned long) apr_os_thread_current();
+#endif
+}
+
+static apr_status_t ssl_util_thr_id_cleanup(void *old)
+{
+ CRYPTO_set_id_callback(old);
+ return APR_SUCCESS;
+}
+
+#endif
+
+static apr_status_t ssl_util_thread_cleanup(void *data)
+{
+ CRYPTO_set_locking_callback(NULL);
+
+ CRYPTO_set_dynlock_create_callback(NULL);
+ CRYPTO_set_dynlock_lock_callback(NULL);
+ CRYPTO_set_dynlock_destroy_callback(NULL);
+
+ dynlockpool = NULL;
+
+ /* Let the registered mutex cleanups do their own thing
+ */
+ return APR_SUCCESS;
+}
+
+void ssl_util_thread_setup(apr_pool_t *p)
+{
+ int i;
+
+ lock_num_locks = CRYPTO_num_locks();
+ lock_cs = apr_palloc(p, lock_num_locks * sizeof(*lock_cs));
+
+ for (i = 0; i < lock_num_locks; i++) {
+ apr_thread_mutex_create(&(lock_cs[i]), APR_THREAD_MUTEX_DEFAULT, p);
+ }
+
+ CRYPTO_set_locking_callback(ssl_util_thr_lock);
+
+ /* Set up dynamic locking scaffolding for OpenSSL to use at its
+ * convenience.
+ */
+ dynlockpool = p;
+ CRYPTO_set_dynlock_create_callback(ssl_dyn_create_function);
+ CRYPTO_set_dynlock_lock_callback(ssl_dyn_lock_function);
+ CRYPTO_set_dynlock_destroy_callback(ssl_dyn_destroy_function);
+
+ apr_pool_cleanup_register(p, NULL, ssl_util_thread_cleanup,
+ apr_pool_cleanup_null);
+}
+
+void ssl_util_thread_id_setup(apr_pool_t *p)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+ CRYPTO_THREADID_set_callback(ssl_util_thr_id);
+#else
+ CRYPTO_set_id_callback(ssl_util_thr_id);
+#endif
+ apr_pool_cleanup_register(p, NULL, ssl_util_thr_id_cleanup,
+ apr_pool_cleanup_null);
+}
+
+#endif /* #if APR_HAS_THREADS && MODSSL_USE_OPENSSL_PRE_1_1_API */
+
+int modssl_is_engine_id(const char *name)
+{
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
+ /* ### Can handle any other special ENGINE key names here? */
+ return strncmp(name, "pkcs11:", 7) == 0;
+#else
+ return 0;
+#endif
+}
diff --git a/modules/ssl/ssl_util_ocsp.c b/modules/ssl/ssl_util_ocsp.c
new file mode 100644
index 0000000..b9c8a0b
--- /dev/null
+++ b/modules/ssl/ssl_util_ocsp.c
@@ -0,0 +1,422 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+/* This file implements an OCSP client including a toy HTTP/1.0
+ * client. Once httpd depends on a real HTTP client library, most of
+ * this can be thrown away. */
+
+#include "ssl_private.h"
+
+#ifndef OPENSSL_NO_OCSP
+
+#include "apr_buckets.h"
+#include "apr_uri.h"
+
+/* Serialize an OCSP request which will be sent to the responder at
+ * given URI to a memory BIO object, which is returned. */
+static BIO *serialize_request(OCSP_REQUEST *req, const apr_uri_t *uri,
+ const apr_uri_t *proxy_uri)
+{
+ BIO *bio;
+ int len;
+
+ len = i2d_OCSP_REQUEST(req, NULL);
+
+ bio = BIO_new(BIO_s_mem());
+
+ BIO_printf(bio, "POST ");
+ /* Use full URL instead of URI in case of a request through a proxy */
+ if (proxy_uri) {
+ BIO_printf(bio, "http://%s:%d",
+ uri->hostname, uri->port);
+ }
+ BIO_printf(bio, "%s%s%s HTTP/1.0\r\n"
+ "Host: %s:%d\r\n"
+ "Content-Type: application/ocsp-request\r\n"
+ "Connection: close\r\n"
+ "Content-Length: %d\r\n"
+ "\r\n",
+ uri->path ? uri->path : "/",
+ uri->query ? "?" : "", uri->query ? uri->query : "",
+ uri->hostname, uri->port, len);
+
+ if (i2d_OCSP_REQUEST_bio(bio, req) != 1) {
+ BIO_free(bio);
+ return NULL;
+ }
+
+ return bio;
+}
+
+/* Send the OCSP request serialized into BIO 'request' to the
+ * responder at given server given by URI. Returns socket object or
+ * NULL on error. */
+static apr_socket_t *send_request(BIO *request, const apr_uri_t *uri,
+ apr_interval_time_t timeout,
+ conn_rec *c, apr_pool_t *p,
+ const apr_uri_t *proxy_uri)
+{
+ apr_status_t rv;
+ apr_sockaddr_t *sa;
+ apr_socket_t *sd;
+ char buf[HUGE_STRING_LEN];
+ int len;
+ const apr_uri_t *next_hop_uri;
+
+ if (proxy_uri) {
+ next_hop_uri = proxy_uri;
+ }
+ else {
+ next_hop_uri = uri;
+ }
+
+ rv = apr_sockaddr_info_get(&sa, next_hop_uri->hostname, APR_UNSPEC,
+ next_hop_uri->port, 0, p);
+ if (rv) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01972)
+ "could not resolve address of %s %s",
+ proxy_uri ? "proxy" : "OCSP responder",
+ next_hop_uri->hostinfo);
+ return NULL;
+ }
+
+ /* establish a connection to the OCSP responder */
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01973)
+ "connecting to %s '%s'",
+ proxy_uri ? "proxy" : "OCSP responder",
+ uri->hostinfo);
+
+ /* Cycle through address until a connect() succeeds. */
+ for (; sa; sa = sa->next) {
+ rv = apr_socket_create(&sd, sa->family, SOCK_STREAM, APR_PROTO_TCP, p);
+ if (rv == APR_SUCCESS) {
+ apr_socket_timeout_set(sd, timeout);
+
+ rv = apr_socket_connect(sd, sa);
+ if (rv == APR_SUCCESS) {
+ break;
+ }
+ apr_socket_close(sd);
+ }
+ }
+
+ if (sa == NULL) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01974)
+ "could not connect to %s '%s'",
+ proxy_uri ? "proxy" : "OCSP responder",
+ next_hop_uri->hostinfo);
+ return NULL;
+ }
+
+ /* send the request and get a response */
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01975)
+ "sending request to OCSP responder");
+
+ while ((len = BIO_read(request, buf, sizeof buf)) > 0) {
+ char *wbuf = buf;
+ apr_size_t remain = len;
+
+ do {
+ apr_size_t wlen = remain;
+
+ rv = apr_socket_send(sd, wbuf, &wlen);
+ wbuf += remain;
+ remain -= wlen;
+ } while (rv == APR_SUCCESS && remain > 0);
+
+ if (rv) {
+ apr_socket_close(sd);
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01976)
+ "failed to send request to OCSP responder '%s'",
+ uri->hostinfo);
+ return NULL;
+ }
+ }
+
+ return sd;
+}
+
+/* Return a pool-allocated NUL-terminated line, with CRLF stripped,
+ * read from brigade 'bbin' using 'bbout' as temporary storage. */
+static char *get_line(apr_bucket_brigade *bbout, apr_bucket_brigade *bbin,
+ conn_rec *c, apr_pool_t *p)
+{
+ apr_status_t rv;
+ apr_size_t len;
+ char *line;
+
+ apr_brigade_cleanup(bbout);
+
+ rv = apr_brigade_split_line(bbout, bbin, APR_BLOCK_READ, 8192);
+ if (rv) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01977)
+ "failed reading line from OCSP server");
+ return NULL;
+ }
+
+ rv = apr_brigade_pflatten(bbout, &line, &len, p);
+ if (rv) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01978)
+ "failed reading line from OCSP server");
+ return NULL;
+ }
+
+ if (len == 0) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(02321)
+ "empty response from OCSP server");
+ return NULL;
+ }
+
+ if (line[len-1] != APR_ASCII_LF) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01979)
+ "response header line too long from OCSP server");
+ return NULL;
+ }
+
+ line[len-1] = '\0';
+ if (len > 1 && line[len-2] == APR_ASCII_CR) {
+ line[len-2] = '\0';
+ }
+
+ return line;
+}
+
+/* Maximum values to prevent eating RAM forever. */
+#define MAX_HEADERS (256)
+#define MAX_CONTENT (2048 * 1024)
+
+/* Read the OCSP response from the socket 'sd', using temporary memory
+ * BIO 'bio', and return the decoded OCSP response object, or NULL on
+ * error. */
+static OCSP_RESPONSE *read_response(apr_socket_t *sd, BIO *bio, conn_rec *c,
+ apr_pool_t *p)
+{
+ apr_bucket_brigade *bb, *tmpbb;
+ OCSP_RESPONSE *response;
+ char *line;
+ apr_size_t count;
+ apr_int64_t code;
+
+ /* Using brigades for response parsing is much simpler than using
+ * apr_socket_* directly. */
+ bb = apr_brigade_create(p, c->bucket_alloc);
+ tmpbb = apr_brigade_create(p, c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_socket_create(sd, c->bucket_alloc));
+
+ line = get_line(tmpbb, bb, c, p);
+ if (!line || strncmp(line, "HTTP/", 5)
+ || (line = ap_strchr(line, ' ')) == NULL
+ || (code = apr_atoi64(++line)) < 200 || code > 299) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01980)
+ "bad response from OCSP server: %s",
+ line ? line : "(none)");
+ return NULL;
+ }
+
+ /* Read till end of headers; don't have to even bother parsing the
+ * Content-Length since the server is obliged to close the
+ * connection after the response anyway for HTTP/1.0. */
+ count = 0;
+ while ((line = get_line(tmpbb, bb, c, p)) != NULL && line[0]
+ && ++count < MAX_HEADERS) {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01981)
+ "OCSP response header: %s", line);
+ }
+
+ if (count == MAX_HEADERS) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01982)
+ "could not read response headers from OCSP server, "
+ "exceeded maximum count (%u)", MAX_HEADERS);
+ return NULL;
+ }
+ else if (!line) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01983)
+ "could not read response header from OCSP server");
+ return NULL;
+ }
+
+ /* Read the response body into the memory BIO. */
+ count = 0;
+ while (!APR_BRIGADE_EMPTY(bb)) {
+ const char *data;
+ apr_size_t len;
+ apr_status_t rv;
+ apr_bucket *e = APR_BRIGADE_FIRST(bb);
+
+ rv = apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
+ if (rv == APR_EOF) {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01984)
+ "OCSP response: got EOF");
+ break;
+ }
+ if (rv != APR_SUCCESS) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01985)
+ "error reading response from OCSP server");
+ return NULL;
+ }
+ if (len == 0) {
+ /* Ignore zero-length buckets (possible side-effect of
+ * line splitting). */
+ apr_bucket_delete(e);
+ continue;
+ }
+ count += len;
+ if (count > MAX_CONTENT) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01986)
+ "OCSP response size exceeds %u byte limit",
+ MAX_CONTENT);
+ return NULL;
+ }
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01987)
+ "OCSP response: got %" APR_SIZE_T_FMT
+ " bytes, %" APR_SIZE_T_FMT " total", len, count);
+
+ BIO_write(bio, data, (int)len);
+ apr_bucket_delete(e);
+ }
+
+ apr_brigade_destroy(bb);
+ apr_brigade_destroy(tmpbb);
+
+ /* Finally decode the OCSP response from what's stored in the
+ * bio. */
+ response = d2i_OCSP_RESPONSE_bio(bio, NULL);
+ if (response == NULL) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01988)
+ "failed to decode OCSP response data");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, mySrvFromConn(c));
+ }
+
+ return response;
+}
+
+OCSP_RESPONSE *modssl_dispatch_ocsp_request(const apr_uri_t *uri,
+ apr_interval_time_t timeout,
+ OCSP_REQUEST *request,
+ conn_rec *c, apr_pool_t *p)
+{
+ OCSP_RESPONSE *response = NULL;
+ apr_socket_t *sd;
+ BIO *bio;
+ const apr_uri_t *proxy_uri;
+
+ proxy_uri = (mySrvConfigFromConn(c))->server->proxy_uri;
+ bio = serialize_request(request, uri, proxy_uri);
+ if (bio == NULL) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01989)
+ "could not serialize OCSP request");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, mySrvFromConn(c));
+ return NULL;
+ }
+
+ sd = send_request(bio, uri, timeout, c, p, proxy_uri);
+ if (sd == NULL) {
+ /* Errors already logged. */
+ BIO_free(bio);
+ return NULL;
+ }
+
+ /* Clear the BIO contents, ready for the response. */
+ (void)BIO_reset(bio);
+
+ response = read_response(sd, bio, c, p);
+
+ apr_socket_close(sd);
+ BIO_free(bio);
+
+ return response;
+}
+
+/* _________________________________________________________________
+**
+** OCSP other certificate support
+** _________________________________________________________________
+*/
+
+/*
+ * Read a file that contains certificates in PEM format and
+ * return as a STACK.
+ */
+
+static STACK_OF(X509) *modssl_read_ocsp_certificates(const char *file)
+{
+ BIO *bio;
+ X509 *x509;
+ unsigned long err;
+ STACK_OF(X509) *other_certs = NULL;
+
+ if ((bio = BIO_new(BIO_s_file())) == NULL)
+ return NULL;
+ if (BIO_read_filename(bio, file) <= 0) {
+ BIO_free(bio);
+ return NULL;
+ }
+
+ /* create new extra chain by loading the certs */
+ ERR_clear_error();
+ while ((x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) {
+ if (!other_certs) {
+ other_certs = sk_X509_new_null();
+ if (!other_certs)
+ return NULL;
+ }
+
+ if (!sk_X509_push(other_certs, x509)) {
+ X509_free(x509);
+ sk_X509_pop_free(other_certs, X509_free);
+ BIO_free(bio);
+ return NULL;
+ }
+ }
+ /* Make sure that only the error is just an EOF */
+ if ((err = ERR_peek_error()) > 0) {
+ if (!( ERR_GET_LIB(err) == ERR_LIB_PEM
+ && ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) {
+ BIO_free(bio);
+ sk_X509_pop_free(other_certs, X509_free);
+ return NULL;
+ }
+ while (ERR_get_error() > 0) ;
+ }
+ BIO_free(bio);
+ return other_certs;
+}
+
+void ssl_init_ocsp_certificates(server_rec *s, modssl_ctx_t *mctx)
+{
+ /*
+ * Configure Trusted OCSP certificates.
+ */
+
+ if (!mctx->ocsp_certs_file) {
+ return;
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "Configuring Trusted OCSP certificates");
+
+ mctx->ocsp_certs = modssl_read_ocsp_certificates(mctx->ocsp_certs_file);
+
+ if (!mctx->ocsp_certs) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+ "Unable to configure OCSP Trusted Certificates");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
+ ssl_die(s);
+ }
+ mctx->ocsp_verify_flags |= OCSP_TRUSTOTHER;
+}
+
+#endif /* HAVE_OCSP */
diff --git a/modules/ssl/ssl_util_ssl.c b/modules/ssl/ssl_util_ssl.c
new file mode 100644
index 0000000..38079a9
--- /dev/null
+++ b/modules/ssl/ssl_util_ssl.c
@@ -0,0 +1,591 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+/* _ _
+ * _ __ ___ ___ __| | ___ ___| | mod_ssl
+ * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| | \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ * |_____|
+ * ssl_util_ssl.c
+ * Additional Utility Functions for OpenSSL
+ */
+
+#include "ssl_private.h"
+
+/* _________________________________________________________________
+**
+** Additional High-Level Functions for OpenSSL
+** _________________________________________________________________
+*/
+
+/* we initialize this index at startup time
+ * and never write to it at request time,
+ * so this static is thread safe.
+ * also note that OpenSSL increments at static variable when
+ * SSL_get_ex_new_index() is called, so we _must_ do this at startup.
+ */
+static int app_data2_idx = -1;
+
+void modssl_init_app_data2_idx(void)
+{
+ int i;
+
+ if (app_data2_idx > -1) {
+ return;
+ }
+
+ /* we _do_ need to call this twice */
+ for (i = 0; i <= 1; i++) {
+ app_data2_idx =
+ SSL_get_ex_new_index(0,
+ "Second Application Data for SSL",
+ NULL, NULL, NULL);
+ }
+}
+
+void *modssl_get_app_data2(SSL *ssl)
+{
+ return (void *)SSL_get_ex_data(ssl, app_data2_idx);
+}
+
+void modssl_set_app_data2(SSL *ssl, void *arg)
+{
+ SSL_set_ex_data(ssl, app_data2_idx, (char *)arg);
+ return;
+}
+
+/* _________________________________________________________________
+**
+** High-Level Private Key Loading
+** _________________________________________________________________
+*/
+
+EVP_PKEY *modssl_read_privatekey(const char *filename, pem_password_cb *cb, void *s)
+{
+ EVP_PKEY *rc;
+ BIO *bioS;
+ BIO *bioF;
+
+ /* 1. try PEM (= DER+Base64+headers) */
+ if ((bioS=BIO_new_file(filename, "r")) == NULL)
+ return NULL;
+ rc = PEM_read_bio_PrivateKey(bioS, NULL, cb, s);
+ BIO_free(bioS);
+
+ if (rc == NULL) {
+ /* 2. try DER+Base64 */
+ if ((bioS = BIO_new_file(filename, "r")) == NULL)
+ return NULL;
+
+ if ((bioF = BIO_new(BIO_f_base64())) == NULL) {
+ BIO_free(bioS);
+ return NULL;
+ }
+ bioS = BIO_push(bioF, bioS);
+ rc = d2i_PrivateKey_bio(bioS, NULL);
+ BIO_free_all(bioS);
+
+ if (rc == NULL) {
+ /* 3. try plain DER */
+ if ((bioS = BIO_new_file(filename, "r")) == NULL)
+ return NULL;
+ rc = d2i_PrivateKey_bio(bioS, NULL);
+ BIO_free(bioS);
+ }
+ }
+ return rc;
+}
+
+/* _________________________________________________________________
+**
+** Smart shutdown
+** _________________________________________________________________
+*/
+
+int modssl_smart_shutdown(SSL *ssl)
+{
+ int i;
+ int rc;
+ int flush;
+
+ /*
+ * Repeat the calls, because SSL_shutdown internally dispatches through a
+ * little state machine. Usually only one or two iterations should be
+ * needed, so we restrict the total number of restrictions in order to
+ * avoid process hangs in case the client played bad with the socket
+ * connection and OpenSSL cannot recognize it.
+ */
+ rc = 0;
+ flush = !(SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN);
+ for (i = 0; i < 4 /* max 2x pending + 2x data = 4 */; i++) {
+ rc = SSL_shutdown(ssl);
+ if (rc >= 0 && flush && (SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN)) {
+ /* Once the close notify is sent through the output filters,
+ * ensure it is flushed through the socket.
+ */
+ if (BIO_flush(SSL_get_wbio(ssl)) <= 0) {
+ rc = -1;
+ break;
+ }
+ flush = 0;
+ }
+ if (rc != 0)
+ break;
+ }
+ return rc;
+}
+
+/* _________________________________________________________________
+**
+** Certificate Checks
+** _________________________________________________________________
+*/
+
+/* retrieve basic constraints ingredients */
+BOOL modssl_X509_getBC(X509 *cert, int *ca, int *pathlen)
+{
+ BASIC_CONSTRAINTS *bc;
+ BIGNUM *bn = NULL;
+ char *cp;
+
+ bc = X509_get_ext_d2i(cert, NID_basic_constraints, NULL, NULL);
+ if (bc == NULL)
+ return FALSE;
+ *ca = bc->ca;
+ *pathlen = -1 /* unlimited */;
+ if (bc->pathlen != NULL) {
+ if ((bn = ASN1_INTEGER_to_BN(bc->pathlen, NULL)) == NULL) {
+ BASIC_CONSTRAINTS_free(bc);
+ return FALSE;
+ }
+ if ((cp = BN_bn2dec(bn)) == NULL) {
+ BN_free(bn);
+ BASIC_CONSTRAINTS_free(bc);
+ return FALSE;
+ }
+ *pathlen = atoi(cp);
+ OPENSSL_free(cp);
+ BN_free(bn);
+ }
+ BASIC_CONSTRAINTS_free(bc);
+ return TRUE;
+}
+
+char *modssl_bio_free_read(apr_pool_t *p, BIO *bio)
+{
+ int len = BIO_pending(bio);
+ char *result = NULL;
+
+ if (len > 0) {
+ result = apr_palloc(p, len+1);
+ len = BIO_read(bio, result, len);
+ result[len] = NUL;
+ }
+ BIO_free(bio);
+ return result;
+}
+
+/* Convert ASN.1 string to a pool-allocated char * string, escaping
+ * control characters. If raw is zero, convert to UTF-8, otherwise
+ * unchanged from the character set. */
+static char *asn1_string_convert(apr_pool_t *p, ASN1_STRING *asn1str, int raw)
+{
+ BIO *bio;
+ int flags = ASN1_STRFLGS_ESC_CTRL;
+
+ if ((bio = BIO_new(BIO_s_mem())) == NULL)
+ return NULL;
+
+ if (!raw) flags |= ASN1_STRFLGS_UTF8_CONVERT;
+
+ ASN1_STRING_print_ex(bio, asn1str, flags);
+
+ return modssl_bio_free_read(p, bio);
+}
+
+#define asn1_string_to_utf8(p, a) asn1_string_convert(p, a, 0)
+
+/* convert a NAME_ENTRY to UTF8 string */
+char *modssl_X509_NAME_ENTRY_to_string(apr_pool_t *p, X509_NAME_ENTRY *xsne,
+ int raw)
+{
+ char *result = asn1_string_convert(p, X509_NAME_ENTRY_get_data(xsne), raw);
+ ap_xlate_proto_from_ascii(result, len);
+ return result;
+}
+
+/*
+ * convert an X509_NAME to an RFC 2253 formatted string, optionally truncated
+ * to maxlen characters (specify a maxlen of 0 for no length limit)
+ */
+char *modssl_X509_NAME_to_string(apr_pool_t *p, X509_NAME *dn, int maxlen)
+{
+ char *result = NULL;
+ BIO *bio;
+ int len;
+
+ if ((bio = BIO_new(BIO_s_mem())) == NULL)
+ return NULL;
+ X509_NAME_print_ex(bio, dn, 0, XN_FLAG_RFC2253);
+ len = BIO_pending(bio);
+ if (len > 0) {
+ result = apr_palloc(p, (maxlen > 0) ? maxlen+1 : len+1);
+ if (maxlen > 0 && maxlen < len) {
+ len = BIO_read(bio, result, maxlen);
+ if (maxlen > 2) {
+ /* insert trailing ellipsis if there's enough space */
+ apr_snprintf(result + maxlen - 3, 4, "...");
+ }
+ } else {
+ len = BIO_read(bio, result, len);
+ }
+ result[len] = NUL;
+ }
+ BIO_free(bio);
+
+ return result;
+}
+
+static void parse_otherName_value(apr_pool_t *p, ASN1_TYPE *value,
+ const char *onf, apr_array_header_t **entries)
+{
+ const char *str;
+ int nid = onf ? OBJ_txt2nid(onf) : NID_undef;
+
+ if (!value || (nid == NID_undef) || !*entries)
+ return;
+
+ /*
+ * Currently supported otherName forms (values for "onf"):
+ * "msUPN" (1.3.6.1.4.1.311.20.2.3): Microsoft User Principal Name
+ * "id-on-dnsSRV" (1.3.6.1.5.5.7.8.7): SRVName, as specified in RFC 4985
+ */
+ if ((nid == NID_ms_upn) && (value->type == V_ASN1_UTF8STRING) &&
+ (str = asn1_string_to_utf8(p, value->value.utf8string))) {
+ APR_ARRAY_PUSH(*entries, const char *) = str;
+ } else if (strEQ(onf, "id-on-dnsSRV") &&
+ (value->type == V_ASN1_IA5STRING) &&
+ (str = asn1_string_to_utf8(p, value->value.ia5string))) {
+ APR_ARRAY_PUSH(*entries, const char *) = str;
+ }
+}
+
+/*
+ * Return an array of subjectAltName entries of type "type". If idx is -1,
+ * return all entries of the given type, otherwise return an array consisting
+ * of the n-th occurrence of that type only. Currently supported types:
+ * GEN_EMAIL (rfc822Name)
+ * GEN_DNS (dNSName)
+ * GEN_OTHERNAME (requires the otherName form ["onf"] argument to be supplied,
+ * see parse_otherName_value for the currently supported forms)
+ */
+BOOL modssl_X509_getSAN(apr_pool_t *p, X509 *x509, int type, const char *onf,
+ int idx, apr_array_header_t **entries)
+{
+ STACK_OF(GENERAL_NAME) *names;
+ int nid = onf ? OBJ_txt2nid(onf) : NID_undef;
+
+ if (!x509 || (type < GEN_OTHERNAME) ||
+ ((type == GEN_OTHERNAME) && (nid == NID_undef)) ||
+ (type > GEN_RID) || (idx < -1) ||
+ !(*entries = apr_array_make(p, 0, sizeof(char *)))) {
+ *entries = NULL;
+ return FALSE;
+ }
+
+ if ((names = X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL))) {
+ int i, n = 0;
+ GENERAL_NAME *name;
+ const char *utf8str;
+
+ for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
+ name = sk_GENERAL_NAME_value(names, i);
+
+ if (name->type != type)
+ continue;
+
+ switch (type) {
+ case GEN_EMAIL:
+ case GEN_DNS:
+ if (((idx == -1) || (n == idx)) &&
+ (utf8str = asn1_string_to_utf8(p, name->d.ia5))) {
+ APR_ARRAY_PUSH(*entries, const char *) = utf8str;
+ }
+ n++;
+ break;
+ case GEN_OTHERNAME:
+ if (OBJ_obj2nid(name->d.otherName->type_id) == nid) {
+ if (((idx == -1) || (n == idx))) {
+ parse_otherName_value(p, name->d.otherName->value,
+ onf, entries);
+ }
+ n++;
+ }
+ break;
+ default:
+ /*
+ * Not implemented right now:
+ * GEN_X400 (x400Address)
+ * GEN_DIRNAME (directoryName)
+ * GEN_EDIPARTY (ediPartyName)
+ * GEN_URI (uniformResourceIdentifier)
+ * GEN_IPADD (iPAddress)
+ * GEN_RID (registeredID)
+ */
+ break;
+ }
+
+ if ((idx != -1) && (n > idx))
+ break;
+ }
+
+ sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
+ }
+
+ return apr_is_empty_array(*entries) ? FALSE : TRUE;
+}
+
+/* return an array of (RFC 6125 coined) DNS-IDs and CN-IDs in a certificate */
+static BOOL getIDs(apr_pool_t *p, X509 *x509, apr_array_header_t **ids)
+{
+ X509_NAME *subj;
+ int i = -1;
+
+ /* First, the DNS-IDs (dNSName entries in the subjectAltName extension) */
+ if (!x509 ||
+ (modssl_X509_getSAN(p, x509, GEN_DNS, NULL, -1, ids) == FALSE && !*ids)) {
+ *ids = NULL;
+ return FALSE;
+ }
+
+ /* Second, the CN-IDs (commonName attributes in the subject DN) */
+ subj = X509_get_subject_name(x509);
+ while ((i = X509_NAME_get_index_by_NID(subj, NID_commonName, i)) != -1) {
+ APR_ARRAY_PUSH(*ids, const char *) =
+ modssl_X509_NAME_ENTRY_to_string(p, X509_NAME_get_entry(subj, i), 0);
+ }
+
+ return apr_is_empty_array(*ids) ? FALSE : TRUE;
+}
+
+/*
+ * Check if a certificate matches for a particular name, by iterating over its
+ * DNS-IDs and CN-IDs (RFC 6125), optionally with basic wildcard matching.
+ * If server_rec is non-NULL, some (debug/trace) logging is enabled.
+ */
+BOOL modssl_X509_match_name(apr_pool_t *p, X509 *x509, const char *name,
+ BOOL allow_wildcard, server_rec *s)
+{
+ BOOL matched = FALSE;
+ apr_array_header_t *ids;
+
+ /*
+ * At some day in the future, this might be replaced with X509_check_host()
+ * (available in OpenSSL 1.0.2 and later), but two points should be noted:
+ * 1) wildcard matching in X509_check_host() might yield different
+ * results (by default, it supports a broader set of patterns, e.g.
+ * wildcards in non-initial positions);
+ * 2) we lose the option of logging each DNS- and CN-ID (until a match
+ * is found).
+ */
+
+ if (getIDs(p, x509, &ids)) {
+ const char *cp;
+ int i;
+ char **id = (char **)ids->elts;
+ BOOL is_wildcard;
+
+ for (i = 0; i < ids->nelts; i++) {
+ if (!id[i])
+ continue;
+
+ /*
+ * Determine if it is a wildcard ID - we're restrictive
+ * in the sense that we require the wildcard character to be
+ * THE left-most label (i.e., the ID must start with "*.")
+ */
+ is_wildcard = (*id[i] == '*' && *(id[i]+1) == '.') ? TRUE : FALSE;
+
+ /*
+ * If the ID includes a wildcard character (and the caller is
+ * allowing wildcards), check if it matches for the left-most
+ * DNS label - i.e., the wildcard character is not allowed
+ * to match a dot. Otherwise, try a simple string compare.
+ */
+ if ((allow_wildcard == TRUE && is_wildcard == TRUE &&
+ (cp = ap_strchr_c(name, '.')) && !strcasecmp(id[i]+1, cp)) ||
+ !strcasecmp(id[i], name)) {
+ matched = TRUE;
+ }
+
+ if (s) {
+ ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s,
+ "[%s] modssl_X509_match_name: expecting name '%s', "
+ "%smatched by ID '%s'",
+ (mySrvConfig(s))->vhost_id, name,
+ matched == TRUE ? "" : "NOT ", id[i]);
+ }
+
+ if (matched == TRUE) {
+ break;
+ }
+ }
+
+ }
+
+ if (s) {
+ ssl_log_xerror(SSLLOG_MARK, APLOG_DEBUG, 0, p, s, x509,
+ APLOGNO(02412) "[%s] Cert %s for name '%s'",
+ (mySrvConfig(s))->vhost_id,
+ matched == TRUE ? "matches" : "does not match",
+ name);
+ }
+
+ return matched;
+}
+
+/* _________________________________________________________________
+**
+** Custom (EC)DH parameter support
+** _________________________________________________________________
+*/
+
+DH *ssl_dh_GetParamFromFile(const char *file)
+{
+ DH *dh = NULL;
+ BIO *bio;
+
+ if ((bio = BIO_new_file(file, "r")) == NULL)
+ return NULL;
+ dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ return (dh);
+}
+
+#ifdef HAVE_ECC
+EC_GROUP *ssl_ec_GetParamFromFile(const char *file)
+{
+ EC_GROUP *group = NULL;
+ BIO *bio;
+
+ if ((bio = BIO_new_file(file, "r")) == NULL)
+ return NULL;
+ group = PEM_read_bio_ECPKParameters(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ return (group);
+}
+#endif
+
+/* _________________________________________________________________
+**
+** Session Stuff
+** _________________________________________________________________
+*/
+
+char *modssl_SSL_SESSION_id2sz(IDCONST unsigned char *id, int idlen,
+ char *str, int strsize)
+{
+ if (idlen > SSL_MAX_SSL_SESSION_ID_LENGTH)
+ idlen = SSL_MAX_SSL_SESSION_ID_LENGTH;
+
+ /* We must ensure not to process more than what would fit in the
+ * destination buffer, including terminating NULL */
+ if (idlen > (strsize-1) / 2)
+ idlen = (strsize-1) / 2;
+
+ ap_bin2hex(id, idlen, str);
+
+ return str;
+}
+
+/* _________________________________________________________________
+**
+** Certificate/Key Stuff
+** _________________________________________________________________
+*/
+
+apr_status_t modssl_read_cert(apr_pool_t *p,
+ const char *cert_pem, const char *key_pem,
+ pem_password_cb *cb, void *ud,
+ X509 **pcert, EVP_PKEY **pkey)
+{
+ BIO *in;
+ X509 *x = NULL;
+ EVP_PKEY *key = NULL;
+ apr_status_t rv = APR_SUCCESS;
+
+ in = BIO_new_mem_buf(cert_pem, -1);
+ if (in == NULL) {
+ rv = APR_ENOMEM;
+ goto cleanup;
+ }
+
+ x = PEM_read_bio_X509(in, NULL, cb, ud);
+ if (x == NULL) {
+ rv = APR_ENOENT;
+ goto cleanup;
+ }
+
+ BIO_free(in);
+ in = BIO_new_mem_buf(key_pem? key_pem : cert_pem, -1);
+ if (in == NULL) {
+ rv = APR_ENOMEM;
+ goto cleanup;
+ }
+ key = PEM_read_bio_PrivateKey(in, NULL, cb, ud);
+ if (key == NULL) {
+ rv = APR_ENOENT;
+ goto cleanup;
+ }
+
+cleanup:
+ if (rv == APR_SUCCESS) {
+ *pcert = x;
+ *pkey = key;
+ }
+ else {
+ *pcert = NULL;
+ *pkey = NULL;
+ if (x) X509_free(x);
+ if (key) EVP_PKEY_free(key);
+ }
+ if (in != NULL) BIO_free(in);
+ return rv;
+}
+
+apr_status_t modssl_cert_get_pem(apr_pool_t *p,
+ X509 *cert1, X509 *cert2,
+ const char **ppem)
+{
+ apr_status_t rv = APR_ENOMEM;
+ BIO *bio;
+
+ if ((bio = BIO_new(BIO_s_mem())) == NULL) goto cleanup;
+ if (PEM_write_bio_X509(bio, cert1) != 1) goto cleanup;
+ if (cert2 && PEM_write_bio_X509(bio, cert2) != 1) goto cleanup;
+ rv = APR_SUCCESS;
+
+cleanup:
+ if (rv != APR_SUCCESS) {
+ *ppem = NULL;
+ if (bio) BIO_free(bio);
+ }
+ else {
+ *ppem = modssl_bio_free_read(p, bio);
+ }
+ return rv;
+}
diff --git a/modules/ssl/ssl_util_ssl.h b/modules/ssl/ssl_util_ssl.h
new file mode 100644
index 0000000..443c1b7
--- /dev/null
+++ b/modules/ssl/ssl_util_ssl.h
@@ -0,0 +1,107 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+/**
+ * @verbatim
+ _ _
+ _ __ ___ ___ __| | ___ ___| | mod_ssl
+ | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+ | | | | | | (_) | (_| | \__ \__ \ |
+ |_| |_| |_|\___/ \__,_|___|___/___/_|
+ |_____|
+ @endverbatim
+ * @file ssl_util_ssl.h
+ * @brief Additional Utility Functions for OpenSSL
+ *
+ * @defgroup MOD_SSL_UTIL Utilities
+ * @ingroup MOD_SSL
+ * @{
+ */
+
+#ifndef __SSL_UTIL_SSL_H__
+#define __SSL_UTIL_SSL_H__
+
+/**
+ * SSL library version number
+ */
+
+#define MODSSL_LIBRARY_VERSION OPENSSL_VERSION_NUMBER
+#define MODSSL_LIBRARY_NAME "OpenSSL"
+#define MODSSL_LIBRARY_TEXT OPENSSL_VERSION_TEXT
+#if MODSSL_USE_OPENSSL_PRE_1_1_API
+#define MODSSL_LIBRARY_DYNTEXT SSLeay_version(SSLEAY_VERSION)
+#else
+#define MODSSL_LIBRARY_DYNTEXT OpenSSL_version(OPENSSL_VERSION)
+#endif
+
+/**
+ * Maximum length of a DER encoded session.
+ * FIXME: There is no define in OpenSSL, but OpenSSL uses 1024*10,
+ * so this value should be ok. Although we have no warm feeling.
+ */
+#define MODSSL_SESSION_MAX_DER 1024*10
+
+/** max length for modssl_SSL_SESSION_id2sz */
+#define MODSSL_SESSION_ID_STRING_LEN \
+ ((SSL_MAX_SSL_SESSION_ID_LENGTH + 1) * 2)
+
+/**
+ * Additional Functions
+ */
+void modssl_init_app_data2_idx(void);
+void *modssl_get_app_data2(SSL *);
+void modssl_set_app_data2(SSL *, void *);
+
+/* Read private key from filename in either PEM or raw base64(DER)
+ * format, using password entry callback cb and userdata. */
+EVP_PKEY *modssl_read_privatekey(const char *filename, pem_password_cb *cb, void *ud);
+
+int modssl_smart_shutdown(SSL *ssl);
+BOOL modssl_X509_getBC(X509 *, int *, int *);
+char *modssl_X509_NAME_ENTRY_to_string(apr_pool_t *p, X509_NAME_ENTRY *xsne,
+ int raw);
+char *modssl_X509_NAME_to_string(apr_pool_t *, X509_NAME *, int);
+BOOL modssl_X509_getSAN(apr_pool_t *, X509 *, int, const char *, int, apr_array_header_t **);
+BOOL modssl_X509_match_name(apr_pool_t *, X509 *, const char *, BOOL, server_rec *);
+char *modssl_SSL_SESSION_id2sz(IDCONST unsigned char *, int, char *, int);
+
+/* Reads the remaining data in BIO, if not empty, and copies it into a
+ * pool-allocated string. If empty, returns NULL. BIO_free(bio) is
+ * called for both cases. */
+char *modssl_bio_free_read(apr_pool_t *p, BIO *bio);
+
+/* Read a single certificate and its private key from the given string in PEM format.
+ * If `key_pem` is NULL, it will expect the key in `cert_pem`.
+ */
+apr_status_t modssl_read_cert(apr_pool_t *p,
+ const char *cert_pem, const char *key_pem,
+ pem_password_cb *cb, void *ud,
+ X509 **pcert, EVP_PKEY **pkey);
+
+/* Convert a certificate (and optionally a second) into a PEM string.
+ * @param p pool for allocations
+ * @param cert1 the certificate to convert
+ * @param cert2 a second cert to add to the PEM afterwards or NULL.
+ * @param ppem the certificate(s) in PEM format, NUL-terminated.
+ * @return APR_SUCCESS if ppem is valid.
+ */
+apr_status_t modssl_cert_get_pem(apr_pool_t *p,
+ X509 *cert1, X509 *cert2,
+ const char **ppem);
+
+#endif /* __SSL_UTIL_SSL_H__ */
+/** @} */
+
diff --git a/modules/ssl/ssl_util_stapling.c b/modules/ssl/ssl_util_stapling.c
new file mode 100644
index 0000000..ab77e4a
--- /dev/null
+++ b/modules/ssl/ssl_util_stapling.c
@@ -0,0 +1,975 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+/* _ _
+ * _ __ ___ ___ __| | ___ ___| | mod_ssl
+ * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| | \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ * |_____|
+ * ssl_stapling.c
+ * OCSP Stapling Support
+ */
+ /* ``Where's the spoons?
+ Where's the spoons?
+ Where's the bloody spoons?''
+ -- Alexei Sayle */
+
+#include "ssl_private.h"
+#include "ap_mpm.h"
+#include "apr_thread_mutex.h"
+#include "mod_ssl_openssl.h"
+
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, init_stapling_status,
+ (server_rec *s, apr_pool_t *p,
+ X509 *cert, X509 *issuer),
+ (s, p, cert, issuer),
+ DECLINED, DECLINED)
+
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, get_stapling_status,
+ (unsigned char **pder, int *pderlen,
+ conn_rec *c, server_rec *s, X509 *cert),
+ (pder, pderlen, c, s, cert),
+ DECLINED, DECLINED)
+
+
+#ifdef HAVE_OCSP_STAPLING
+
+static int stapling_cache_mutex_on(server_rec *s);
+static int stapling_cache_mutex_off(server_rec *s);
+
+static int stapling_cb(SSL *ssl, void *arg);
+
+/**
+ * Maximum OCSP stapling response size. This should be the response for a
+ * single certificate and will typically include the responder certificate chain
+ * so 10K should be more than enough.
+ *
+ */
+
+#define MAX_STAPLING_DER 10240
+
+/* Cached info stored in the global stapling_certinfo hash. */
+typedef struct {
+ /* Index in session cache (SHA-1 digest of DER encoded certificate) */
+ UCHAR idx[SHA_DIGEST_LENGTH];
+ /* Certificate ID for OCSP request */
+ OCSP_CERTID *cid;
+ /* URI of the OCSP responder */
+ char *uri;
+} certinfo;
+
+static apr_status_t ssl_stapling_certid_free(void *data)
+{
+ OCSP_CERTID *cid = data;
+
+ if (cid) {
+ OCSP_CERTID_free(cid);
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_hash_t *stapling_certinfo;
+
+void ssl_stapling_certinfo_hash_init(apr_pool_t *p)
+{
+ stapling_certinfo = apr_hash_make(p);
+}
+
+static X509 *stapling_get_issuer(modssl_ctx_t *mctx, X509 *x)
+{
+ X509 *issuer = NULL;
+ int i;
+ X509_STORE *st = SSL_CTX_get_cert_store(mctx->ssl_ctx);
+ X509_STORE_CTX *inctx;
+ STACK_OF(X509) *extra_certs = NULL;
+
+#ifdef OPENSSL_NO_SSL_INTERN
+ SSL_CTX_get_extra_chain_certs(mctx->ssl_ctx, &extra_certs);
+#else
+ extra_certs = mctx->ssl_ctx->extra_certs;
+#endif
+
+ for (i = 0; i < sk_X509_num(extra_certs); i++) {
+ issuer = sk_X509_value(extra_certs, i);
+ if (X509_check_issued(issuer, x) == X509_V_OK) {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ CRYPTO_add(&issuer->references, 1, CRYPTO_LOCK_X509);
+#else
+ X509_up_ref(issuer);
+#endif
+ return issuer;
+ }
+ }
+
+ inctx = X509_STORE_CTX_new();
+ if (!X509_STORE_CTX_init(inctx, st, NULL, NULL))
+ return 0;
+ if (X509_STORE_CTX_get1_issuer(&issuer, inctx, x) <= 0)
+ issuer = NULL;
+ X509_STORE_CTX_cleanup(inctx);
+ X509_STORE_CTX_free(inctx);
+ return issuer;
+}
+
+int ssl_stapling_init_cert(server_rec *s, apr_pool_t *p, apr_pool_t *ptemp,
+ modssl_ctx_t *mctx, X509 *x)
+{
+ UCHAR idx[SHA_DIGEST_LENGTH];
+ certinfo *cinf = NULL;
+ X509 *issuer = NULL;
+ OCSP_CERTID *cid = NULL;
+ STACK_OF(OPENSSL_STRING) *aia = NULL;
+ const char *pem = NULL;
+ int rv = 1; /* until further notice */
+
+ if (x == NULL)
+ return 0;
+
+ if (!(issuer = stapling_get_issuer(mctx, x))) {
+ /* In Apache pre 2.4.40, we use to come here only when mod_ssl stapling
+ * was enabled. With the new hooks, we give other modules the chance
+ * to provide stapling status. However, we do not want to log ssl errors
+ * where we did not do so in the past. */
+ if (mctx->stapling_enabled == TRUE) {
+ ssl_log_xerror(SSLLOG_MARK, APLOG_ERR, 0, ptemp, s, x, APLOGNO(02217)
+ "ssl_stapling_init_cert: can't retrieve issuer "
+ "certificate!");
+ return 0;
+ }
+ return 1;
+ }
+
+ if (X509_digest(x, EVP_sha1(), idx, NULL) != 1) {
+ rv = 0;
+ goto cleanup;
+ }
+
+ if (modssl_cert_get_pem(ptemp, x, issuer, &pem) != APR_SUCCESS) {
+ rv = 0;
+ goto cleanup;
+ }
+
+ if (ap_ssl_ocsp_prime(s, p, (const char*)idx, sizeof(idx), pem) == APR_SUCCESS
+ || ssl_run_init_stapling_status(s, p, x, issuer) == OK) {
+ /* Someone's taken over or mod_ssl's own implementation is not enabled */
+ if (mctx->stapling_enabled != TRUE) {
+ SSL_CTX_set_tlsext_status_cb(mctx->ssl_ctx, stapling_cb);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10177) "OCSP stapling added via hook");
+ }
+ goto cleanup;
+ }
+
+ if (mctx->stapling_enabled != TRUE) {
+ /* mod_ssl's own implementation is not enabled */
+ goto cleanup;
+ }
+
+ cinf = apr_hash_get(stapling_certinfo, idx, sizeof(idx));
+ if (cinf) {
+ /*
+ * We already parsed the certificate, and no OCSP URI was found.
+ * The certificate might be used for multiple vhosts, though,
+ * so we check for a ForceURL for this vhost.
+ */
+ if (!cinf->uri && !mctx->stapling_force_url) {
+ ssl_log_xerror(SSLLOG_MARK, APLOG_ERR, 0, ptemp, s, x,
+ APLOGNO(02814) "ssl_stapling_init_cert: no OCSP URI "
+ "in certificate and no SSLStaplingForceURL "
+ "configured for server %s", mctx->sc->vhost_id);
+ rv = 0;
+ }
+ goto cleanup;
+ }
+
+ cid = OCSP_cert_to_id(NULL, x, issuer);
+ if (!cid) {
+ ssl_log_xerror(SSLLOG_MARK, APLOG_ERR, 0, ptemp, s, x, APLOGNO(02815)
+ "ssl_stapling_init_cert: can't create CertID "
+ "for OCSP request");
+ rv = 0;
+ goto cleanup;
+ }
+
+ aia = X509_get1_ocsp(x);
+ if (!aia && !mctx->stapling_force_url) {
+ OCSP_CERTID_free(cid);
+ ssl_log_xerror(SSLLOG_MARK, APLOG_ERR, 0, ptemp, s, x,
+ APLOGNO(02218) "ssl_stapling_init_cert: no OCSP URI "
+ "in certificate and no SSLStaplingForceURL set");
+ rv = 0;
+ goto cleanup;
+ }
+
+ /* At this point, we have determined that there's something to store */
+ cinf = apr_pcalloc(p, sizeof(certinfo));
+ memcpy (cinf->idx, idx, sizeof(idx));
+ cinf->cid = cid;
+ /* make sure cid is also freed at pool cleanup */
+ apr_pool_cleanup_register(p, cid, ssl_stapling_certid_free,
+ apr_pool_cleanup_null);
+ if (aia) {
+ /* allocate uri from the pconf pool */
+ cinf->uri = apr_pstrdup(p, sk_OPENSSL_STRING_value(aia, 0));
+ X509_email_free(aia);
+ }
+
+ ssl_log_xerror(SSLLOG_MARK, APLOG_TRACE1, 0, ptemp, s, x,
+ "ssl_stapling_init_cert: storing certinfo for server %s",
+ mctx->sc->vhost_id);
+
+ apr_hash_set(stapling_certinfo, cinf->idx, sizeof(cinf->idx), cinf);
+
+cleanup:
+ X509_free(issuer);
+ return rv;
+}
+
+static certinfo *stapling_get_certinfo(server_rec *s, UCHAR *idx, apr_size_t idx_len,
+ modssl_ctx_t *mctx, SSL *ssl)
+{
+ certinfo *cinf;
+ cinf = apr_hash_get(stapling_certinfo, idx, idx_len);
+ if (cinf && cinf->cid)
+ return cinf;
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01926)
+ "stapling_get_certinfo: stapling not supported for certificate");
+ return NULL;
+}
+
+/*
+ * OCSP response caching code. The response is preceded by a flag value
+ * which indicates whether the response was invalid when it was stored.
+ * the purpose of this flag is to avoid repeated queries to a server
+ * which has given an invalid response while allowing a response which
+ * has subsequently become invalid to be retried immediately.
+ *
+ * The key for the cache is the hash of the certificate the response
+ * is for.
+ */
+static BOOL stapling_cache_response(server_rec *s, modssl_ctx_t *mctx,
+ OCSP_RESPONSE *rsp, certinfo *cinf,
+ BOOL ok, apr_pool_t *pool)
+{
+ SSLModConfigRec *mc = myModConfig(s);
+ unsigned char resp_der[MAX_STAPLING_DER]; /* includes one-byte flag + response */
+ unsigned char *p;
+ int resp_derlen, stored_len;
+ BOOL rv;
+ apr_time_t expiry;
+
+ resp_derlen = i2d_OCSP_RESPONSE(rsp, NULL);
+
+ if (resp_derlen <= 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01927)
+ "OCSP stapling response encode error??");
+ return FALSE;
+ }
+
+ stored_len = resp_derlen + 1; /* response + ok flag */
+ if (stored_len > sizeof resp_der) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01928)
+ "OCSP stapling response too big (%u bytes)", resp_derlen);
+ return FALSE;
+ }
+
+ p = resp_der;
+
+ /* TODO: potential optimization; _timeout members as apr_interval_time_t */
+ if (ok == TRUE) {
+ *p++ = 1;
+ expiry = apr_time_from_sec(mctx->stapling_cache_timeout);
+ }
+ else {
+ *p++ = 0;
+ expiry = apr_time_from_sec(mctx->stapling_errcache_timeout);
+ }
+
+ expiry += apr_time_now();
+
+ i2d_OCSP_RESPONSE(rsp, &p);
+
+ if (mc->stapling_cache->flags & AP_SOCACHE_FLAG_NOTMPSAFE)
+ stapling_cache_mutex_on(s);
+ rv = mc->stapling_cache->store(mc->stapling_cache_context, s,
+ cinf->idx, sizeof(cinf->idx),
+ expiry, resp_der, stored_len, pool);
+ if (mc->stapling_cache->flags & AP_SOCACHE_FLAG_NOTMPSAFE)
+ stapling_cache_mutex_off(s);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01929)
+ "stapling_cache_response: OCSP response session store error!");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void stapling_get_cached_response(server_rec *s, OCSP_RESPONSE **prsp,
+ BOOL *pok, certinfo *cinf,
+ apr_pool_t *pool)
+{
+ SSLModConfigRec *mc = myModConfig(s);
+ apr_status_t rv;
+ OCSP_RESPONSE *rsp;
+ unsigned char resp_der[MAX_STAPLING_DER];
+ const unsigned char *p;
+ unsigned int resp_derlen = MAX_STAPLING_DER;
+
+ if (mc->stapling_cache->flags & AP_SOCACHE_FLAG_NOTMPSAFE)
+ stapling_cache_mutex_on(s);
+ rv = mc->stapling_cache->retrieve(mc->stapling_cache_context, s,
+ cinf->idx, sizeof(cinf->idx),
+ resp_der, &resp_derlen, pool);
+ if (mc->stapling_cache->flags & AP_SOCACHE_FLAG_NOTMPSAFE)
+ stapling_cache_mutex_off(s);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01930)
+ "stapling_get_cached_response: cache miss");
+ return;
+ }
+ if (resp_derlen <= 1) {
+ /* should-not-occur; must have at least valid-when-stored flag +
+ * OCSPResponseStatus
+ */
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01931)
+ "stapling_get_cached_response: response length invalid??");
+ return;
+ }
+ p = resp_der;
+ if (*p) /* valid when stored */
+ *pok = TRUE;
+ else
+ *pok = FALSE;
+ p++;
+ resp_derlen--;
+ rsp = d2i_OCSP_RESPONSE(NULL, &p, resp_derlen);
+ if (!rsp) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01932)
+ "stapling_get_cached_response: response parse error??");
+ return;
+ }
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01933)
+ "stapling_get_cached_response: cache hit");
+
+ *prsp = rsp;
+}
+
+static int stapling_set_response(SSL *ssl, OCSP_RESPONSE *rsp)
+{
+ int rspderlen;
+ unsigned char *rspder = NULL;
+
+ rspderlen = i2d_OCSP_RESPONSE(rsp, &rspder);
+ if (rspderlen <= 0)
+ return 0;
+ SSL_set_tlsext_status_ocsp_resp(ssl, rspder, rspderlen);
+ return 1;
+}
+
+static int stapling_check_response(server_rec *s, modssl_ctx_t *mctx,
+ certinfo *cinf, OCSP_RESPONSE *rsp,
+ BOOL *pok)
+{
+ int status = V_OCSP_CERTSTATUS_UNKNOWN;
+ int reason = OCSP_REVOKED_STATUS_NOSTATUS;
+ OCSP_BASICRESP *bs = NULL;
+ ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
+ int response_status = OCSP_response_status(rsp);
+ int rv = SSL_TLSEXT_ERR_OK;
+
+ if (pok)
+ *pok = FALSE;
+ /* Check to see if response is an error. If so we automatically accept
+ * it because it would have expired from the cache if it was time to
+ * retry.
+ */
+ if (response_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+ if (mctx->stapling_return_errors)
+ return SSL_TLSEXT_ERR_OK;
+ else
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ bs = OCSP_response_get1_basic(rsp);
+ if (bs == NULL) {
+ /* If we can't parse response just pass it to client */
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01934)
+ "stapling_check_response: Error Parsing Response!");
+ return SSL_TLSEXT_ERR_OK;
+ }
+
+ if (!OCSP_resp_find_status(bs, cinf->cid, &status, &reason, &rev,
+ &thisupd, &nextupd)) {
+ /* If ID not present pass back to client (if configured so) */
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01935)
+ "stapling_check_response: certificate ID not present in response!");
+ if (mctx->stapling_return_errors == FALSE)
+ rv = SSL_TLSEXT_ERR_NOACK;
+ }
+ else {
+ if (OCSP_check_validity(thisupd, nextupd,
+ mctx->stapling_resptime_skew,
+ mctx->stapling_resp_maxage)) {
+ if (pok)
+ *pok = TRUE;
+ }
+ else {
+ /* If pok is not NULL response was direct from a responder and
+ * the times should be valide. If pok is NULL the response was
+ * retrieved from cache and it is expected to subsequently expire
+ */
+ if (pok) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01936)
+ "stapling_check_response: response times invalid");
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01937)
+ "stapling_check_response: cached response expired");
+ }
+
+ rv = SSL_TLSEXT_ERR_NOACK;
+ }
+
+ if (status != V_OCSP_CERTSTATUS_GOOD) {
+ char snum[MAX_STRING_LEN] = { '\0' };
+ BIO *bio = BIO_new(BIO_s_mem());
+
+ if (bio) {
+ int n;
+ ASN1_INTEGER *pserial;
+ OCSP_id_get0_info(NULL, NULL, NULL, &pserial, cinf->cid);
+ if ((i2a_ASN1_INTEGER(bio, pserial) != -1) &&
+ ((n = BIO_read(bio, snum, sizeof snum - 1)) > 0))
+ snum[n] = '\0';
+ BIO_free(bio);
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02969)
+ "stapling_check_response: response has certificate "
+ "status %s (reason: %s) for serial number %s",
+ OCSP_cert_status_str(status),
+ (reason != OCSP_REVOKED_STATUS_NOSTATUS) ?
+ OCSP_crl_reason_str(reason) : "n/a",
+ snum[0] ? snum : "[n/a]");
+
+ if (mctx->stapling_return_errors == FALSE) {
+ if (pok)
+ *pok = FALSE;
+ rv = SSL_TLSEXT_ERR_NOACK;
+ }
+ }
+ }
+
+ OCSP_BASICRESP_free(bs);
+
+ return rv;
+}
+
+static BOOL stapling_renew_response(server_rec *s, modssl_ctx_t *mctx, SSL *ssl,
+ certinfo *cinf, OCSP_RESPONSE **prsp,
+ BOOL *pok, apr_pool_t *pool)
+{
+ conn_rec *conn = (conn_rec *)SSL_get_app_data(ssl);
+ apr_pool_t *vpool;
+ OCSP_REQUEST *req = NULL;
+ OCSP_CERTID *id = NULL;
+ STACK_OF(X509_EXTENSION) *exts;
+ int i;
+ BOOL rv = TRUE;
+ const char *ocspuri;
+ apr_uri_t uri;
+
+ *prsp = NULL;
+ /* Build up OCSP query from server certificate info */
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01938)
+ "stapling_renew_response: querying responder");
+
+ req = OCSP_REQUEST_new();
+ if (!req)
+ goto err;
+ id = OCSP_CERTID_dup(cinf->cid);
+ if (!id)
+ goto err;
+ if (!OCSP_request_add0_id(req, id))
+ goto err;
+ id = NULL;
+ /* Add any extensions to the request */
+ SSL_get_tlsext_status_exts(ssl, &exts);
+ for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) {
+ X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i);
+ if (!OCSP_REQUEST_add_ext(req, ext, -1))
+ goto err;
+ }
+
+ if (mctx->stapling_force_url)
+ ocspuri = mctx->stapling_force_url;
+ else
+ ocspuri = cinf->uri;
+
+ if (!ocspuri) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02621)
+ "stapling_renew_response: no uri for responder");
+ rv = FALSE;
+ goto done;
+ }
+
+ /* Create a temporary pool to constrain memory use */
+ apr_pool_create(&vpool, conn->pool);
+ apr_pool_tag(vpool, "modssl_stapling_renew");
+
+ if (apr_uri_parse(vpool, ocspuri, &uri) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01939)
+ "stapling_renew_response: Error parsing uri %s",
+ ocspuri);
+ rv = FALSE;
+ goto done;
+ }
+ else if (strcmp(uri.scheme, "http")) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01940)
+ "stapling_renew_response: Unsupported uri %s", ocspuri);
+ rv = FALSE;
+ goto done;
+ }
+
+ if (!uri.port) {
+ uri.port = apr_uri_port_of_scheme(uri.scheme);
+ }
+
+ *prsp = modssl_dispatch_ocsp_request(&uri, mctx->stapling_responder_timeout,
+ req, conn, vpool);
+
+ apr_pool_destroy(vpool);
+
+ if (!*prsp) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01941)
+ "stapling_renew_response: responder error");
+ if (mctx->stapling_fake_trylater) {
+ *prsp = OCSP_response_create(OCSP_RESPONSE_STATUS_TRYLATER, NULL);
+ }
+ else {
+ goto done;
+ }
+ }
+ else {
+ int response_status = OCSP_response_status(*prsp);
+
+ if (response_status == OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01942)
+ "stapling_renew_response: query response received");
+ stapling_check_response(s, mctx, cinf, *prsp, pok);
+ if (*pok == FALSE) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01943)
+ "stapling_renew_response: error in retrieved response!");
+ }
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01944)
+ "stapling_renew_response: responder error %s",
+ OCSP_response_status_str(response_status));
+ *pok = FALSE;
+ }
+ }
+ if (stapling_cache_response(s, mctx, *prsp, cinf, *pok, pool) == FALSE) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01945)
+ "stapling_renew_response: error caching response!");
+ }
+
+done:
+ if (id)
+ OCSP_CERTID_free(id);
+ if (req)
+ OCSP_REQUEST_free(req);
+ return rv;
+err:
+ rv = FALSE;
+ goto done;
+}
+
+/*
+ * SSL stapling mutex operations. Similar to SSL mutex except mutexes are
+ * mandatory if stapling is enabled.
+ */
+static int ssl_stapling_mutex_init(server_rec *s, apr_pool_t *p)
+{
+ SSLModConfigRec *mc = myModConfig(s);
+ SSLSrvConfigRec *sc = mySrvConfig(s);
+ apr_status_t rv;
+
+ /* already init or stapling not enabled? */
+ if (mc->stapling_refresh_mutex || sc->server->stapling_enabled != TRUE) {
+ return TRUE;
+ }
+
+ /* need a cache mutex? */
+ if (mc->stapling_cache->flags & AP_SOCACHE_FLAG_NOTMPSAFE) {
+ if ((rv = ap_global_mutex_create(&mc->stapling_cache_mutex, NULL,
+ SSL_STAPLING_CACHE_MUTEX_TYPE, NULL, s,
+ s->process->pool, 0)) != APR_SUCCESS) {
+ return FALSE;
+ }
+ }
+
+ /* always need stapling_refresh_mutex */
+ if ((rv = ap_global_mutex_create(&mc->stapling_refresh_mutex, NULL,
+ SSL_STAPLING_REFRESH_MUTEX_TYPE, NULL, s,
+ s->process->pool, 0)) != APR_SUCCESS) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int stapling_mutex_reinit_helper(server_rec *s, apr_pool_t *p,
+ apr_global_mutex_t **mutex,
+ const char *type)
+{
+ apr_status_t rv;
+ const char *lockfile;
+
+ lockfile = apr_global_mutex_lockfile(*mutex);
+ if ((rv = apr_global_mutex_child_init(mutex,
+ lockfile, p)) != APR_SUCCESS) {
+ if (lockfile) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(01946)
+ "Cannot reinit %s mutex with file `%s'",
+ type, lockfile);
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, rv, s, APLOGNO(01947)
+ "Cannot reinit %s mutex", type);
+ }
+ return FALSE;
+ }
+ return TRUE;
+}
+
+int ssl_stapling_mutex_reinit(server_rec *s, apr_pool_t *p)
+{
+ SSLModConfigRec *mc = myModConfig(s);
+
+ if (mc->stapling_cache_mutex != NULL
+ && stapling_mutex_reinit_helper(s, p, &mc->stapling_cache_mutex,
+ SSL_STAPLING_CACHE_MUTEX_TYPE) == FALSE) {
+ return FALSE;
+ }
+
+ if (mc->stapling_refresh_mutex != NULL
+ && stapling_mutex_reinit_helper(s, p, &mc->stapling_refresh_mutex,
+ SSL_STAPLING_REFRESH_MUTEX_TYPE) == FALSE) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int stapling_mutex_on(server_rec *s, apr_global_mutex_t *mutex,
+ const char *name)
+{
+ apr_status_t rv;
+
+ if ((rv = apr_global_mutex_lock(mutex)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, rv, s, APLOGNO(01948)
+ "Failed to acquire OCSP %s lock", name);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static int stapling_mutex_off(server_rec *s, apr_global_mutex_t *mutex,
+ const char *name)
+{
+ apr_status_t rv;
+
+ if ((rv = apr_global_mutex_unlock(mutex)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, rv, s, APLOGNO(01949)
+ "Failed to release OCSP %s lock", name);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static int stapling_cache_mutex_on(server_rec *s)
+{
+ SSLModConfigRec *mc = myModConfig(s);
+
+ return stapling_mutex_on(s, mc->stapling_cache_mutex,
+ SSL_STAPLING_CACHE_MUTEX_TYPE);
+}
+
+static int stapling_cache_mutex_off(server_rec *s)
+{
+ SSLModConfigRec *mc = myModConfig(s);
+
+ return stapling_mutex_off(s, mc->stapling_cache_mutex,
+ SSL_STAPLING_CACHE_MUTEX_TYPE);
+}
+
+static int stapling_refresh_mutex_on(server_rec *s)
+{
+ SSLModConfigRec *mc = myModConfig(s);
+
+ return stapling_mutex_on(s, mc->stapling_refresh_mutex,
+ SSL_STAPLING_REFRESH_MUTEX_TYPE);
+}
+
+static int stapling_refresh_mutex_off(server_rec *s)
+{
+ SSLModConfigRec *mc = myModConfig(s);
+
+ return stapling_mutex_off(s, mc->stapling_refresh_mutex,
+ SSL_STAPLING_REFRESH_MUTEX_TYPE);
+}
+
+static int get_and_check_cached_response(server_rec *s, modssl_ctx_t *mctx,
+ OCSP_RESPONSE **rsp, BOOL *pok,
+ certinfo *cinf, apr_pool_t *p)
+{
+ BOOL ok = FALSE;
+ int rv;
+
+ AP_DEBUG_ASSERT(*rsp == NULL);
+
+ /* Check to see if we already have a response for this certificate */
+ stapling_get_cached_response(s, rsp, &ok, cinf, p);
+
+ if (*rsp) {
+ /* see if response is acceptable */
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01953)
+ "stapling_cb: retrieved cached response");
+ rv = stapling_check_response(s, mctx, cinf, *rsp, NULL);
+ if (rv == SSL_TLSEXT_ERR_ALERT_FATAL) {
+ OCSP_RESPONSE_free(*rsp);
+ *rsp = NULL;
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+ else if (rv == SSL_TLSEXT_ERR_NOACK) {
+ /* Error in response. If this error was not present when it was
+ * stored (i.e. response no longer valid) then it can be
+ * renewed straight away.
+ *
+ * If the error *was* present at the time it was stored then we
+ * don't renew the response straight away; we just wait for the
+ * cached response to expire.
+ */
+ if (ok) {
+ OCSP_RESPONSE_free(*rsp);
+ *rsp = NULL;
+ }
+ else if (!mctx->stapling_return_errors) {
+ OCSP_RESPONSE_free(*rsp);
+ *rsp = NULL;
+ *pok = FALSE;
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+ }
+ }
+ return 0;
+}
+
+typedef struct {
+ unsigned char *data;
+ apr_size_t len;
+} ocsp_resp;
+
+static void copy_ocsp_resp(const unsigned char *der, apr_size_t der_len, void *userdata)
+{
+ ocsp_resp *resp = userdata;
+
+ resp->len = 0;
+ resp->data = der? OPENSSL_malloc(der_len) : NULL;
+ if (resp->data) {
+ memcpy(resp->data, der, der_len);
+ resp->len = der_len;
+ }
+}
+
+/* Certificate Status callback. This is called when a client includes a
+ * certificate status request extension.
+ *
+ * Check for cached responses in session cache. If valid send back to
+ * client. If absent or no longer valid, query responder and update
+ * cache.
+ */
+static int stapling_cb(SSL *ssl, void *arg)
+{
+ conn_rec *conn = (conn_rec *)SSL_get_app_data(ssl);
+ server_rec *s = mySrvFromConn(conn);
+ SSLSrvConfigRec *sc = mySrvConfig(s);
+ modssl_ctx_t *mctx = myConnCtxConfig(conn, sc);
+ UCHAR idx[SHA_DIGEST_LENGTH];
+ ocsp_resp resp;
+ certinfo *cinf = NULL;
+ OCSP_RESPONSE *rsp = NULL;
+ int rv;
+ BOOL ok = TRUE;
+ X509 *x;
+ int rspderlen, provided = 0;
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01951)
+ "stapling_cb: OCSP Stapling callback called");
+
+ x = SSL_get_certificate(ssl);
+ if (x == NULL) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ if (X509_digest(x, EVP_sha1(), idx, NULL) != 1) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ if (ap_ssl_ocsp_get_resp(s, conn, (const char*)idx, sizeof(idx),
+ copy_ocsp_resp, &resp) == APR_SUCCESS) {
+ provided = 1;
+ }
+ else if (ssl_run_get_stapling_status(&resp.data, &rspderlen, conn, s, x) == APR_SUCCESS) {
+ resp.len = (apr_size_t)rspderlen;
+ provided = 1;
+ }
+
+ if (provided) {
+ /* a hook handles stapling for this certificate and determines the response */
+ if (resp.data == NULL || resp.len == 0) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+ SSL_set_tlsext_status_ocsp_resp(ssl, resp.data, (int)resp.len);
+ return SSL_TLSEXT_ERR_OK;
+ }
+
+ if (sc->server->stapling_enabled != TRUE) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01950)
+ "stapling_cb: OCSP Stapling disabled");
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ if ((cinf = stapling_get_certinfo(s, idx, sizeof(idx), mctx, ssl)) == NULL) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01952)
+ "stapling_cb: retrieved cached certificate data");
+
+ rv = get_and_check_cached_response(s, mctx, &rsp, &ok, cinf, conn->pool);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (rsp == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01954)
+ "stapling_cb: renewing cached response");
+ stapling_refresh_mutex_on(s);
+ /* Maybe another request refreshed the OCSP response while this
+ * thread waited for the mutex. Check again.
+ */
+ rv = get_and_check_cached_response(s, mctx, &rsp, &ok, cinf,
+ conn->pool);
+ if (rv != 0) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03236)
+ "stapling_cb: error checking for cached response "
+ "after obtaining refresh mutex");
+ stapling_refresh_mutex_off(s);
+ return rv;
+ }
+ else if (rsp) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03237)
+ "stapling_cb: don't need to refresh cached response "
+ "after obtaining refresh mutex");
+ stapling_refresh_mutex_off(s);
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03238)
+ "stapling_cb: still must refresh cached response "
+ "after obtaining refresh mutex");
+ rv = stapling_renew_response(s, mctx, ssl, cinf, &rsp, &ok,
+ conn->pool);
+ stapling_refresh_mutex_off(s);
+
+ if (rv == TRUE) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03040)
+ "stapling_cb: success renewing response");
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01955)
+ "stapling_cb: fatal error renewing response");
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+ }
+ }
+
+ if (rsp && ((ok == TRUE) || (mctx->stapling_return_errors == TRUE))) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01956)
+ "stapling_cb: setting response");
+ if (!stapling_set_response(ssl, rsp)) {
+ rv = SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+ else {
+ rv = SSL_TLSEXT_ERR_OK;
+ }
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01957)
+ "stapling_cb: no suitable response available");
+ rv = SSL_TLSEXT_ERR_NOACK;
+ }
+ OCSP_RESPONSE_free(rsp); /* NULL safe */
+
+ return rv;
+}
+
+apr_status_t modssl_init_stapling(server_rec *s, apr_pool_t *p,
+ apr_pool_t *ptemp, modssl_ctx_t *mctx)
+{
+ SSL_CTX *ctx = mctx->ssl_ctx;
+ SSLModConfigRec *mc = myModConfig(s);
+
+ if (mc->stapling_cache == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01958)
+ "SSLStapling: no stapling cache available");
+ return ssl_die(s);
+ }
+ if (ssl_stapling_mutex_init(s, ptemp) == FALSE) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01959)
+ "SSLStapling: cannot initialise stapling mutex");
+ return ssl_die(s);
+ }
+ /* Set some default values for parameters if they are not set */
+ if (mctx->stapling_resptime_skew == UNSET) {
+ mctx->stapling_resptime_skew = 60 * 5;
+ }
+ if (mctx->stapling_cache_timeout == UNSET) {
+ mctx->stapling_cache_timeout = 3600;
+ }
+ if (mctx->stapling_return_errors == UNSET) {
+ mctx->stapling_return_errors = TRUE;
+ }
+ if (mctx->stapling_fake_trylater == UNSET) {
+ mctx->stapling_fake_trylater = TRUE;
+ }
+ if (mctx->stapling_errcache_timeout == UNSET) {
+ mctx->stapling_errcache_timeout = 600;
+ }
+ if (mctx->stapling_responder_timeout == UNSET) {
+ mctx->stapling_responder_timeout = 10 * APR_USEC_PER_SEC;
+ }
+
+ SSL_CTX_set_tlsext_status_cb(ctx, stapling_cb);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01960) "OCSP stapling initialized");
+
+ return APR_SUCCESS;
+}
+
+#endif