diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:01:30 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:01:30 +0000 |
commit | 6beeb1b708550be0d4a53b272283e17e5e35fe17 (patch) | |
tree | 1ce8673d4aaa948e5554000101f46536a1e4cc29 /modules/ssl | |
parent | Initial commit. (diff) | |
download | apache2-6beeb1b708550be0d4a53b272283e17e5e35fe17.tar.xz apache2-6beeb1b708550be0d4a53b272283e17e5e35fe17.zip |
Adding upstream version 2.4.57.upstream/2.4.57upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
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, ¶ms, 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 <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 |