summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGraham Leggett <minfrin@apache.org>2016-07-10 17:27:03 +0000
committerGraham Leggett <minfrin@apache.org>2016-07-10 17:27:03 +0000
commit45b089f7acb62ac7f456285404a9551cbd98a1bf (patch)
treec88dc506bd68881f59d9cc2428e26056cbccda0b
parent91b7139dba05562838a843853eeb5e0e9839e1a6 (diff)
downloadhttpd-45b089f7acb62ac7f456285404a9551cbd98a1bf.tar.gz
mod_crypto: Add the all purpose crypto filters with support for HLS.
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1752099 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--Apache-apr2.dsw21
-rw-r--r--Apache.dsw24
-rw-r--r--CHANGES3
-rw-r--r--build/rpm/httpd.spec.in3
-rw-r--r--docs/log-message-tags/next-number2
-rw-r--r--docs/manual/expr.xml2
-rw-r--r--docs/manual/mod/allmodules.xml1
-rw-r--r--docs/manual/mod/mod_crypto.xml278
-rw-r--r--docs/manual/mod/mod_crypto.xml.meta12
-rw-r--r--modules/filters/NWGNUmakefile1
-rw-r--r--modules/filters/NWGNUmod_crypto248
-rw-r--r--modules/filters/config.m41
-rw-r--r--modules/filters/mod_crypto.c1313
-rw-r--r--modules/filters/mod_crypto.dsp111
-rw-r--r--modules/filters/mod_crypto.h95
15 files changed, 2113 insertions, 2 deletions
diff --git a/Apache-apr2.dsw b/Apache-apr2.dsw
index 25b6823710..9c089ca6c5 100644
--- a/Apache-apr2.dsw
+++ b/Apache-apr2.dsw
@@ -180,6 +180,9 @@ Package=<4>
Project_Dep_Name mod_charset_lite
End Project Dependency
Begin Project Dependency
+ Project_Dep_Name mod_crypto
+ End Project Dependency
+ Begin Project Dependency
Project_Dep_Name mod_data
End Project Dependency
Begin Project Dependency
@@ -1408,6 +1411,24 @@ Package=<4>
###############################################################################
+Project: "mod_crypto"=.\modules\filters\mod_crypto.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name libapr
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name libhttpd
+ End Project Dependency
+}}}
+
+###############################################################################
+
Project: "mod_data"=.\modules\filters\mod_data.dsp - Package Owner=<4>
Package=<5>
diff --git a/Apache.dsw b/Apache.dsw
index 9dcb224115..6a11fa04d6 100644
--- a/Apache.dsw
+++ b/Apache.dsw
@@ -195,6 +195,9 @@ Package=<4>
Project_Dep_Name mod_charset_lite
End Project Dependency
Begin Project Dependency
+ Project_Dep_Name mod_crypto
+ End Project Dependency
+ Begin Project Dependency
Project_Dep_Name mod_data
End Project Dependency
Begin Project Dependency
@@ -1675,6 +1678,27 @@ Package=<4>
###############################################################################
+Project: "mod_crypto"=.\modules\filters\mod_crypto.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name libapr
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name libaprutil
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name libhttpd
+ End Project Dependency
+}}}
+
+###############################################################################
+
Project: "mod_data"=.\modules\filters\mod_data.dsp - Package Owner=<4>
Package=<5>
diff --git a/CHANGES b/CHANGES
index 6c0f632803..5c3ffb230f 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,9 @@
-*- coding: utf-8 -*-
Changes with Apache 2.5.0
+ *) mod_crypto: Add the all purpose crypto filters with support for HLS.
+ [Graham Leggett]
+
*) ab: Add option -I to use the Server Name Indication (SNI) extension on
outgoing TLS connections, according to the Host header (if any) or the
requested URL's hostname otherwise. [Yann Ylavic]
diff --git a/build/rpm/httpd.spec.in b/build/rpm/httpd.spec.in
index dd6fe6c6e6..4d58f6169d 100644
--- a/build/rpm/httpd.spec.in
+++ b/build/rpm/httpd.spec.in
@@ -158,7 +158,7 @@ rm -rf srclib/{apr,apr-util,pcre}
--enable-mods-shared=all \
--enable-ssl --with-ssl --enable-socache-dc --enable-bucketeer \
--enable-case-filter --enable-case-filter-in \
- --enable-policy \
+ --enable-policy --enable-crypto \
--disable-imagemap
make %{?_smp_mflags}
@@ -343,6 +343,7 @@ rm -rf $RPM_BUILD_ROOT
%{_libdir}/httpd/modules/mod_case_filter_in.so
%{_libdir}/httpd/modules/mod_cgid.so
%{_libdir}/httpd/modules/mod_charset_lite.so
+%{_libdir}/httpd/modules/mod_crypto.so
%{_libdir}/httpd/modules/mod_data.so
%{_libdir}/httpd/modules/mod_dav_fs.so
%{_libdir}/httpd/modules/mod_dav_lock.so
diff --git a/docs/log-message-tags/next-number b/docs/log-message-tags/next-number
index 46cff15d5b..b3d97ff871 100644
--- a/docs/log-message-tags/next-number
+++ b/docs/log-message-tags/next-number
@@ -1 +1 @@
-3409
+3442
diff --git a/docs/manual/expr.xml b/docs/manual/expr.xml
index 40ad217f28..10b4fd72ea 100644
--- a/docs/manual/expr.xml
+++ b/docs/manual/expr.xml
@@ -55,6 +55,8 @@
<seealso><directive module="mod_headers">Header</directive></seealso>
<seealso><directive module="mod_headers">RequestHeader</directive></seealso>
<seealso><directive module="mod_filter">FilterProvider</directive></seealso>
+<seealso><directive module="mod_crypto">CryptoKey</directive></seealso>
+<seealso><directive module="mod_crypto">CryptoIV</directive></seealso>
<seealso><a href="mod/mod_authz_core.html#reqexpr">Require expr</a></seealso>
<seealso><a href="mod/mod_authnz_ldap.html#requser">Require ldap-user</a></seealso>
<seealso><a href="mod/mod_authnz_ldap.html#reqgroup">Require ldap-group</a></seealso>
diff --git a/docs/manual/mod/allmodules.xml b/docs/manual/mod/allmodules.xml
index 32232fc4bf..44a7dfd403 100644
--- a/docs/manual/mod/allmodules.xml
+++ b/docs/manual/mod/allmodules.xml
@@ -36,6 +36,7 @@
<modulefile>mod_cgi.xml</modulefile>
<modulefile>mod_cgid.xml</modulefile>
<modulefile>mod_charset_lite.xml</modulefile>
+ <modulefile>mod_crypto.xml</modulefile>
<modulefile>mod_data.xml</modulefile>
<modulefile>mod_dav.xml</modulefile>
<modulefile>mod_dav_fs.xml</modulefile>
diff --git a/docs/manual/mod/mod_crypto.xml b/docs/manual/mod/mod_crypto.xml
new file mode 100644
index 0000000000..a1847bad59
--- /dev/null
+++ b/docs/manual/mod/mod_crypto.xml
@@ -0,0 +1,278 @@
+<?xml version="1.0"?>
+<!DOCTYPE modulesynopsis SYSTEM "../style/modulesynopsis.dtd">
+<?xml-stylesheet type="text/xsl" href="../style/manual.en.xsl"?>
+<!-- $LastChangedRevision: 1330964 $ -->
+
+<!--
+ 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.
+-->
+
+<modulesynopsis metafile="mod_crypto.xml.meta">
+
+<name>mod_crypto</name>
+<description>Support for symmetrical encryption and decryption</description>
+<status>Extension</status>
+<sourcefile>mod_crypto.c</sourcefile>
+<identifier>crypto_module</identifier>
+<compatibility>Available in Apache 2.5 and later</compatibility>
+
+<summary>
+ <p>This module provides the ability to <strong>encrypt and decrypt</strong>
+ data on the input and output filter stacks.</p>
+
+ <p>Most specifically, it can be used to implement <strong>on-the-fly HLS
+ encryption</strong> as described by
+ <a href="http://www.ietf.org/id/draft-pantos-http-live-streaming-19.txt">
+ draft-pantos-http-live-streaming-19</a>.</p>
+
+ <p>Alternatively, it can be used to deliver secure data over insecure CDN
+ to suitable clients.</p>
+
+ <p>The crypto filter may be added to either the input or the
+ output filter stacks, as appropriate, using the
+ <directive module="core">SetInputFilter</directive>,
+ <directive module="core">SetOutputFilter</directive>,
+ <directive module="mod_mime">AddOutputFilter</directive> or
+ <directive module="mod_filter">AddOutputFilterByType</directive> directives.</p>
+
+</summary>
+<seealso><a href="../filter.html">Filters</a></seealso>
+
+<section id="format">
+ <title>Stream Format</title>
+
+ <p>The encrypted stream consists of an optional IV block, followed by encrypted
+ blocks using the chosen cipher. The final block is padded before being written. The
+ size of the blocks is governed by the choice of cipher in use.</p>
+
+ <p>When the IV is specified with the <directive module="mod_crypto">CryptoIV</directive>
+ directive, that IV is used, and is not written to or read from the stream.</p>
+
+</section>
+
+<section id="config">
+ <title>Keys and IVs</title>
+
+ <p>
+ The
+ <directive module="mod_crypto">CryptoKey</directive>
+ and
+ <directive module="mod_crypto">CryptoIV</directive>
+ directives expect binary
+ values which can be specified in a number of ways as below. The most significant bits of the relevant values
+ are used, and if the values are too short, they are padded on the left
+ with zero bits.
+ </p>
+
+ <dl>
+ <dt>file:</dt><dd>Read the value directly from the given file.</dd>
+ <dt>hex:</dt><dd>Parse the expression as a hex value, which may contain colon separators.</dd>
+ <dt>decimal:</dt><dd>Parse the expression as a decimal value.</dd>
+ <dt>base64:</dt><dd>Parse the expression as a base64 value.</dd>
+ <dt>none</dt><dd>No value is specified.</dd>
+ </dl>
+
+ <p>If the IV is unspecified a random IV will be generated during
+ encryption and written
+ as the first block. During decryption, the first block will be
+ interpreted as the IV.
+ </p>
+
+ <p>With the exception of file:, the <directive module="mod_crypto">CryptoKey</directive>
+ and <directive module="mod_crypto">CryptoIV</directive> directives allow
+ <a href="../expr.html">expression syntax</a> to provide flexibility when setting the values.
+ This allows both keys and IVs to be salted with values available to the webserver, such
+ as REMOTE_USER, or the URL.
+ </p>
+
+</section>
+
+<section id="handler">
+ <title>Crypto Key Handler</title>
+
+ <p>For convenience, the <strong>crypto-key</strong> handler can be used to serve the key
+ to suitably authorized clients, removing the need to store the key within the directory
+ space of the webserver. This also allows the same <a href="../expr.html">expression
+ syntax</a> to be used to derive the key for both the clients and the encrypted content.</p>
+
+ <example><title>Crypto Key Handler with a File</title>
+ &lt;Location /key&gt;<br />
+ <indent>
+ SetHandler crypto-key<br />
+ CryptoCipher aes128<br />
+ CryptoKey file:/path/to/file.key<br />
+ AuthType basic<br />
+ ...<br />
+ </indent>
+ &lt;/Location&gt;<br />
+ </example>
+
+</section>
+
+<section id="hls">
+ <title>HTTP Live Streaming</title>
+
+ <p>The HLS protocol supports encrypted streams using the AES-128 cipher and a
+ corresponding key. Access is granted to the stream by sharing the key with
+ the HLS client, typically over a secured connection.</p>
+
+ <p>The IV used for encrypting each media segment is specified within
+ HLS in one of two ways:</p>
+
+ <ul>
+ <li>
+ Explicitly specified within an IV attribute inside the EXT-X-KEY
+ tag as a <strong>hexadecimal</strong> value.
+ </li>
+ <li>
+ Implicitly specified by interpreting the <strong>decimal</strong>
+ value of the EXT-X-MEDIA-SEQUENCE tag.
+ </li>
+ </ul>
+
+ <p>The media sequence value is usually embedded within the media
+ segment filenames, and can be matched by using named regular
+ expressions as per the example below.
+ </p>
+
+ <example><title>HLS Example - IV from media sequence</title>
+ &lt;LocationMatch (?&lt;SEQUENCE&gt;[\d]+)[^\d^/]+$&gt;<br />
+ <indent>
+ SetOutputFilter ENCRYPT<br />
+ CryptoCipher aes128<br />
+ CryptoKey file:/path/to/file.key<br />
+ CryptoIV decimal:%{env:MATCH_SEQUENCE}<br />
+ </indent>
+ &lt;/LocationMatch&gt;<br />
+ </example>
+
+</section>
+
+<directivesynopsis>
+<name>CryptoDriver</name>
+<description>Name of the crypto driver to use</description>
+<syntax>CryptoDriver name</syntax>
+<default>CryptoDriver openssl</default>
+<contextlist><context>server config</context>
+</contextlist>
+
+<usage>
+ <p>The <directive module="mod_crypto">CryptoDriver</directive>
+ directive specifies the name of the crypto driver to use. There is usually
+ a recommended default driver on each platform. Possible values include
+ <strong>openssl</strong>, <strong>commoncrypto</strong> and
+ <strong>nss</strong>.</p>
+</usage>
+</directivesynopsis>
+
+<directivesynopsis>
+<name>CryptoCipher</name>
+<description>Cipher to be used by the crypto filter</description>
+<syntax>CryptoCipher name</syntax>
+<default>CryptoCipher aes256</default>
+<contextlist><context>server config</context>
+<context>virtual host</context>
+<context>directory</context>
+<context>.htaccess</context>
+</contextlist>
+
+<usage>
+ <p>The <directive>CryptoCipher</directive> directive allows specification of
+ the cipher to be used during encryption or decryption. If not specified, the
+ cipher defaults to <code>aes256</code>.</p>
+
+ <p>Possible values depend on the crypto driver in use, and could be one of:</p>
+
+ <ul><li>3des192</li><li>aes128</li><li>aes192</li><li>aes256</li></ul>
+
+</usage>
+</directivesynopsis>
+
+<directivesynopsis>
+<name>CryptoIV</name>
+<description>IV (Initialisation Vector) to be used by the crypto filter</description>
+<syntax>CryptoIV value</syntax>
+<default>CryptoIV none</default>
+<contextlist><context>server config</context>
+<context>virtual host</context>
+<context>directory</context>
+<context>.htaccess</context>
+</contextlist>
+
+<usage>
+ <p>The <directive>CryptoIV</directive> directive allows the IV (initialisation
+ vector) to be specified for the particular URL space. The IV can be read from
+ a file, or can be set based on the <a href="../expr.html">expression parser</a>,
+ allowing for flexible scenarios for the setting of keys.</p>
+
+ <p>Values can be specified as read from a file, or as a hexadecimal, decimal or
+ base64 value based on the following prefixes:</p>
+
+ <ul><li>file:</li><li>hex:</li><li>decimal:</li><li>base64:</li></ul>
+
+ <p>The value 'none' disables setting of the IV. In this case, during encryption
+ a random IV will be generated and inserted as the first block, and during
+ decryption the first block will interpreted as the IV.</p>
+</usage>
+</directivesynopsis>
+
+<directivesynopsis>
+<name>CryptoKey</name>
+<description>Key to be used by the crypto filter</description>
+<syntax>CryptoKey value</syntax>
+<default>CryptoKey none</default>
+<contextlist><context>server config</context>
+<context>virtual host</context>
+<context>directory</context>
+<context>.htaccess</context>
+</contextlist>
+
+<usage>
+ <p>The <directive>CryptoKey</directive> directive allows the encryption / decryption
+ key to be specified for the particular URL space. The key can be read from a file, or
+ can be set based on the <a href="../expr.html">expression parser</a>, allowing for
+ flexible scenarios for the setting of keys.</p>
+
+ <p>Values can be specified as read from a file, or as a hexadecimal, decimal or
+ base64 value based on the following prefixes:</p>
+
+ <ul><li>file:</li><li>hex:</li><li>decimal:</li><li>base64:</li></ul>
+
+ <p>The value 'none' disables the key. An attempt to serve a file through the ENCRYPT
+ or DECRYPT filters without a key will cause the request to fail.</p>
+</usage>
+</directivesynopsis>
+
+<directivesynopsis>
+<name>CryptoSize</name>
+<description>Maximum size in bytes to buffer by the crypto filter</description>
+<syntax>CryptoSize integer</syntax>
+<default>CryptoSize 131072</default>
+<contextlist><context>server config</context>
+<context>virtual host</context>
+<context>directory</context>
+<context>.htaccess</context>
+</contextlist>
+
+<usage>
+ <p>The <directive module="mod_crypto">CryptoSize</directive>
+ directive specifies the amount of data in bytes that will be
+ buffered before being encrypted or decrypted during each request.
+ The default is 128 kilobytes.</p>
+</usage>
+</directivesynopsis>
+
+</modulesynopsis>
diff --git a/docs/manual/mod/mod_crypto.xml.meta b/docs/manual/mod/mod_crypto.xml.meta
new file mode 100644
index 0000000000..6e247337bd
--- /dev/null
+++ b/docs/manual/mod/mod_crypto.xml.meta
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- GENERATED FROM XML: DO NOT EDIT -->
+
+<metafile reference="mod_crypto.xml">
+ <basename>mod_crypto</basename>
+ <path>/mod/</path>
+ <relpath>..</relpath>
+
+ <variants>
+ <variant>en</variant>
+ </variants>
+</metafile>
diff --git a/modules/filters/NWGNUmakefile b/modules/filters/NWGNUmakefile
index 922abac9a8..1c4a9bf949 100644
--- a/modules/filters/NWGNUmakefile
+++ b/modules/filters/NWGNUmakefile
@@ -154,6 +154,7 @@ XDCDATA =
TARGET_nlm = \
$(OBJDIR)/extfiltr.nlm \
$(OBJDIR)/charsetl.nlm \
+ $(OBJDIR)/mod_crypto.nlm \
$(OBJDIR)/mod_data.nlm \
$(OBJDIR)/mod_filter.nlm \
$(OBJDIR)/mod_request.nlm \
diff --git a/modules/filters/NWGNUmod_crypto b/modules/filters/NWGNUmod_crypto
new file mode 100644
index 0000000000..1114151c78
--- /dev/null
+++ b/modules/filters/NWGNUmod_crypto
@@ -0,0 +1,248 @@
+#
+# Make sure all needed macro's are defined
+#
+
+#
+# Get the 'head' of the build environment if necessary. This includes default
+# targets and paths to tools
+#
+
+ifndef EnvironmentDefined
+include $(AP_WORK)/build/NWGNUhead.inc
+endif
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS += \
+ $(APR)/include \
+ $(APRUTIL)/include \
+ $(SRC)/include \
+ $(NWOS) \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS += \
+ $(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_crypto
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = Apache $(VERSION_STR) Data Module
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = Filter Module
+
+#
+# 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 these are 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 =
+
+#
+# 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 = \
+ $(OBJDIR)/$(NLM_NAME).o \
+ $(EOLIST)
+
+#
+# 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)
+
+#
+# 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 = \
+ aprlib \
+ libc \
+ $(EOLIST)
+
+#
+# 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 = \
+ @aprlib.imp \
+ @httpd.imp \
+ @libc.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ crypto_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
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APBUILD)/NWGNUtail.inc
+
+
diff --git a/modules/filters/config.m4 b/modules/filters/config.m4
index 60917edadb..db3533bc58 100644
--- a/modules/filters/config.m4
+++ b/modules/filters/config.m4
@@ -5,6 +5,7 @@ dnl APACHE_MODULE(name, helptext[, objects[, structname[, default[, config]]]])
APACHE_MODPATH_INIT(filters)
APACHE_MODULE(buffer, Filter Buffering, , , most)
+APACHE_MODULE(crypto, Symmetrical encryption / decryption, , , no)
APACHE_MODULE(data, RFC2397 data encoder, , , )
APACHE_MODULE(ratelimit, Output Bandwidth Limiting, , , most)
APACHE_MODULE(reqtimeout, Limit time waiting for request from client, , , yes)
diff --git a/modules/filters/mod_crypto.c b/modules/filters/mod_crypto.c
new file mode 100644
index 0000000000..5ae3f066ce
--- /dev/null
+++ b/modules/filters/mod_crypto.c
@@ -0,0 +1,1313 @@
+/* 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_crypto.c --- Encrypt / decrypt data in the input and output filter
+ * stacks.
+ */
+
+#include "mod_crypto.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include "apr_crypto.h"
+#include "apr_base64.h"
+#include "apr_escape.h"
+#include "apr_version.h"
+#if !APR_VERSION_AT_LEAST(2,0,0)
+#include "apu_version.h"
+#endif
+#include "util_filter.h"
+#include "http_log.h"
+#include "http_request.h"
+#include "http_protocol.h"
+#include "ap_expr.h"
+
+#if APR_VERSION_AT_LEAST(2,0,0) || \
+ (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 6)
+
+APR_HOOK_STRUCT(APR_HOOK_LINK(crypto_key)
+ APR_HOOK_LINK(crypto_iv))
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(ap, CRYPTO, apr_status_t, crypto_key,
+ (request_rec *r,
+ apr_crypto_block_key_type_t * cipher,
+ apr_crypto_block_key_mode_t * mode,
+ int pad,
+ const apr_crypto_key_rec_t ** rec),
+ (r, cipher, mode, pad, rec), DECLINED)
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(ap, CRYPTO, apr_status_t, crypto_iv,
+ (request_rec *r,
+ apr_crypto_block_key_type_t * cipher,
+ const unsigned char **iv), (r, cipher,
+ iv), DECLINED)
+module AP_MODULE_DECLARE_DATA crypto_module;
+
+#define DEFAULT_BUFFER_SIZE 128*1024
+#define DEFAULT_CIPHER "aes256"
+#define DEFAULT_MODE "cbc"
+#define CRYPTO_KEY "crypto_context"
+
+typedef struct pass_conf
+{
+ const char *scheme;
+ const ap_expr_info_t *expr;
+ unsigned char *raw;
+ apr_size_t size;
+} pass_conf;
+
+/**
+ * Structure to carry the server wide session config.
+ */
+typedef struct
+{
+ const char *library;
+ const char *params;
+ apr_crypto_t **crypto;
+ int library_set;
+} crypto_conf;
+
+typedef struct crypto_dir_conf
+{
+ apr_off_t size; /* size of the buffer */
+ int size_set; /* has the size been set */
+ const char *cipher;
+ const char *mode;
+ int cipher_set;
+ pass_conf *key;
+ int key_set;
+ pass_conf *iv;
+ int iv_set;
+} crypto_dir_conf;
+
+typedef struct crypto_ctx
+{
+ apr_bucket_brigade *bb;
+ apr_bucket_brigade *tmp;
+ crypto_dir_conf *conf;
+ unsigned char *out;
+ apr_crypto_key_t *key;
+ apr_crypto_block_key_type_t *cipher;
+ apr_crypto_block_key_mode_t *mode;
+ apr_crypto_block_t *block;
+ const unsigned char *iv;
+ apr_off_t remaining;
+ apr_off_t written;
+ apr_size_t osize;
+ int seen_eos:1;
+ int encrypt:1;
+ int clength:1;
+} crypto_ctx;
+
+static const char *parse_pass_conf_binary(cmd_parms *cmd,
+ pass_conf * pass,
+ const char *arg)
+{
+ apr_status_t rv;
+ char ps = *arg;
+
+ if ('f' == ps && !strncmp(arg, "file:", 5)) {
+ arg += 5;
+
+ if (!*arg) {
+ return apr_pstrcat(cmd->pool, "No filename specified", NULL);
+ }
+
+ const char *name = ap_server_root_relative(cmd->temp_pool, arg);
+ if (name) {
+ apr_file_t *file;
+
+ rv = apr_file_open(&file, name, APR_FOPEN_READ,
+ APR_FPROT_OS_DEFAULT, cmd->temp_pool);
+ if (APR_SUCCESS == rv) {
+ apr_finfo_t finfo;
+
+ rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, file);
+ if (rv == APR_SUCCESS) {
+ apr_size_t size;
+
+ pass->scheme = "file";
+ pass->raw = apr_palloc(cmd->pool, finfo.size);
+ pass->size = finfo.size;
+ apr_crypto_clear(cmd->pool, pass->raw, pass->size);
+
+ rv = apr_file_read_full(file, pass->raw, pass->size,
+ &size);
+ if (APR_SUCCESS == rv && size != pass->size) {
+ rv = APR_EGENERAL;
+ }
+
+ }
+ }
+ if (APR_SUCCESS != rv) {
+ char buf[120];
+ return apr_pstrcat(cmd->pool, "Unable to load from file '",
+ arg, "': ", apr_strerror(rv, buf,
+ sizeof(buf)),
+ NULL);
+ }
+ }
+ else {
+ return apr_pstrcat(cmd->pool, "Unable to locate file from name ",
+ arg, NULL);
+ }
+ }
+
+ else if ('h' == ps && (!strncmp(arg, "hex:", 4))) {
+ const char *expr_err = NULL;
+ arg += 4;
+
+ if (!*arg) {
+ return apr_pstrcat(cmd->temp_pool,
+ "Cannot parse expression, it is blank", NULL);
+ }
+
+ pass->scheme = "hex";
+ pass->expr = ap_expr_parse_cmd(cmd, arg, AP_EXPR_FLAG_STRING_RESULT,
+ &expr_err, NULL);
+
+ if (expr_err) {
+ return apr_pstrcat(cmd->temp_pool, "Cannot parse ", pass->scheme,
+ " expression '", arg, "' in: ", expr_err,
+ NULL);
+ }
+
+ }
+
+ else if ('b' == ps && !strncmp(arg, "base64:", 7)) {
+ const char *expr_err = NULL;
+ arg += 7;
+
+ if (!*arg) {
+ return apr_pstrcat(cmd->temp_pool,
+ "Cannot parse expression, it is blank", NULL);
+ }
+
+ pass->scheme = "base64";
+ pass->expr = ap_expr_parse_cmd(cmd, arg, AP_EXPR_FLAG_STRING_RESULT,
+ &expr_err, NULL);
+
+ if (expr_err) {
+ return apr_pstrcat(cmd->temp_pool, "Cannot parse ", pass->scheme,
+ " expression '", arg, "' in: ", expr_err,
+ NULL);
+ }
+
+ }
+
+ else if ('d' == ps && !strncmp(arg, "decimal:", 8)) {
+ const char *expr_err = NULL;
+ arg += 8;
+
+ if (!*arg) {
+ return apr_pstrcat(cmd->temp_pool,
+ "Cannot parse expression, it is blank", NULL);
+ }
+
+ pass->scheme = "decimal";
+ pass->expr = ap_expr_parse_cmd(cmd, arg, AP_EXPR_FLAG_STRING_RESULT,
+ &expr_err, NULL);
+
+ if (expr_err) {
+ return apr_pstrcat(cmd->temp_pool, "Cannot parse ", pass->scheme,
+ " expression '", arg, "' in: ", expr_err,
+ NULL);
+ }
+
+ }
+
+ else if ('n' == ps && !strcmp(arg, "none")) {
+ pass->scheme = arg;
+ }
+
+ else {
+ return apr_pstrcat(cmd->pool,
+ "Scheme must be 'file:', 'hex:', 'base64:', 'decimal:' or 'none': ",
+ arg, NULL);
+ }
+
+ return NULL;
+}
+
+static apr_status_t
+exec_pass_conf_binary(request_rec *r, pass_conf * pass,
+ const char *description, apr_size_t size,
+ const unsigned char **k)
+{
+
+ if (pass) {
+
+ if (pass->raw) {
+ *k = pass->raw;
+
+ if (size != pass->size) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r,
+ APLOGNO(03409) "%s has wrong size (was %"
+ APR_SIZE_T_FMT ", must be %" APR_SIZE_T_FMT ")",
+ description, pass->size, size);
+ return APR_EGENERAL;
+ }
+
+ return APR_SUCCESS;
+ }
+
+ else if (pass->expr) {
+ char ps = *pass->scheme;
+ const char *err = NULL;
+
+ const char *arg = ap_expr_str_exec(r, pass->expr, &err);
+ if (err) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r,
+ APLOGNO(03410) "%s could not be parsed: %s",
+ description, err);
+ return APR_EGENERAL;
+ }
+
+ /* hex */
+ if ('h' == ps) {
+ apr_size_t len;
+ unsigned char *b;
+
+ apr_unescape_hex(NULL, arg, strlen(arg), 1, &len);
+ if (len < size) {
+ b = apr_palloc(r->pool, size);
+ memset(b, 0, size - len);
+ apr_unescape_hex(b + size - len, arg, strlen(arg), 1,
+ &len);
+ }
+ else {
+ b = apr_palloc(r->pool, len);
+ apr_unescape_hex(b, arg, strlen(arg), 1, NULL);
+ b += len - size;
+ }
+ *k = b;
+
+ }
+
+ /* base64 */
+ else if ('b' == ps) {
+ apr_size_t len;
+ unsigned char *b;
+
+ len = apr_base64_decode_len(arg);
+ if (len < size) {
+ b = apr_palloc(r->pool, size);
+ memset(b, 0, size - len);
+ apr_base64_decode_binary(b + size - len, arg);
+ }
+ else {
+ b = apr_palloc(r->pool, len);
+ apr_base64_decode_binary(b, arg);
+ b += len - size;
+ }
+ *k = b;
+
+ }
+
+ /* decimal */
+ else if ('d' == ps) {
+ apr_size_t len;
+ unsigned char *b;
+ char n[8];
+ apr_uint64_t t;
+ int i;
+
+ t = (apr_uint64_t) apr_atoi64(arg);
+
+ for (i = 7; i >= 0; i--) {
+ n[i] = t & 0xFF;
+ t = t >> 8;
+ }
+
+ len = sizeof(n);
+ if (len < size) {
+ b = apr_palloc(r->pool, size);
+ memset(b, 0, size - len);
+ memcpy(b + size - len, n, len);
+ }
+ else {
+ b = apr_palloc(r->pool, len);
+ memcpy(b, n, len);
+ b += len - size;
+ }
+ *k = b;
+
+ }
+
+ }
+
+ }
+
+ return DECLINED;
+}
+
+static apr_status_t
+init_cipher(request_rec *r,
+ apr_crypto_block_key_type_t ** cipher,
+ apr_crypto_block_key_mode_t ** mode)
+{
+ apr_status_t rv;
+ apr_hash_t *ciphers;
+ apr_hash_t *modes;
+
+ crypto_conf *conf = ap_get_module_config(r->server->module_config,
+ &crypto_module);
+ crypto_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
+ &crypto_module);
+
+ if (cipher) {
+
+ rv = apr_crypto_get_block_key_types(&ciphers, *conf->crypto);
+ if (APR_SUCCESS != rv) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+ APLOGNO(03411) "no ciphers returned by APR");
+ return rv;
+ }
+
+ *cipher = apr_hash_get(ciphers, dconf->cipher, APR_HASH_KEY_STRING);
+ if (!*cipher) {
+ apr_hash_index_t *hi;
+ const void *key;
+ apr_ssize_t klen;
+ int sum = 0;
+ int offset = 0;
+ char *options = NULL;
+
+ for (hi = apr_hash_first(r->pool, ciphers); hi;
+ hi = apr_hash_next(hi)) {
+ apr_hash_this(hi, NULL, &klen, NULL);
+ sum += klen + 2;
+ }
+ for (hi = apr_hash_first(r->pool, ciphers); hi;
+ hi = apr_hash_next(hi)) {
+ apr_hash_this(hi, &key, &klen, NULL);
+ if (!options) {
+ options = apr_palloc(r->pool, sum + 1);
+ }
+ else {
+ options[offset++] = ',';
+ options[offset++] = ' ';
+ }
+ strncpy(options + offset, key, klen);
+ offset += klen;
+ }
+ options[offset] = 0;
+
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+ APLOGNO(03428) "cipher '%s' not recognised by crypto driver. "
+ "Options: %s", dconf->cipher, options);
+
+ return rv;
+ }
+
+ }
+
+ if (mode) {
+
+ rv = apr_crypto_get_block_key_modes(&modes, *conf->crypto);
+ if (APR_SUCCESS != rv) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+ APLOGNO(03412) "no cipher modes returned by APR");
+ return rv;
+ }
+
+ *mode = apr_hash_get(modes, dconf->mode, APR_HASH_KEY_STRING);
+ if (!*mode) {
+ apr_hash_index_t *hi;
+ const void *key;
+ apr_ssize_t klen;
+ int sum = 0;
+ int offset = 0;
+ char *options = NULL;
+
+ for (hi = apr_hash_first(r->pool, modes); hi;
+ hi = apr_hash_next(hi)) {
+ apr_hash_this(hi, NULL, &klen, NULL);
+ sum += klen + 2;
+ }
+ for (hi = apr_hash_first(r->pool, modes); hi;
+ hi = apr_hash_next(hi)) {
+ apr_hash_this(hi, &key, &klen, NULL);
+ if (!options) {
+ options = apr_palloc(r->pool, sum + 1);
+ }
+ else {
+ options[offset++] = ',';
+ options[offset++] = ' ';
+ }
+ strncpy(options + offset, key, klen);
+ offset += klen;
+ }
+ options[offset] = 0;
+
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+ APLOGNO(03429) "cipher mode '%s' not recognised by crypto driver. "
+ "Options: %s", dconf->mode, options);
+
+ return rv;
+ }
+
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t init_crypt(ap_filter_t * f)
+{
+ apr_status_t rv;
+ crypto_ctx *ctx = f->ctx;
+ const apr_crypto_key_rec_t *rec;
+
+ crypto_conf *conf = ap_get_module_config(f->r->server->module_config,
+ &crypto_module);
+ crypto_dir_conf *dconf =
+ ap_get_module_config(f->r->per_dir_config, &crypto_module);
+
+ /* sanity check - has crypto been switched on? */
+ if (!conf->crypto || !*conf->crypto) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, f->r,
+ APLOGNO(03430) "crypto driver has not been enabled for this server");
+ return APR_EGENERAL;
+ }
+
+ /* initial setup of the context */
+ ctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+ ctx->conf = dconf;
+ ctx->remaining = ctx->conf->size;
+ ctx->written = 0;
+ ctx->osize = ctx->conf->size;
+
+ /* fetch the cipher for this location */
+ rv = init_cipher(f->r, &ctx->cipher, &ctx->mode);
+ if (APR_SUCCESS != rv) {
+ return rv;
+ }
+
+ /* sanity check - buffer size multiple of block size? */
+ if (ctx->conf->size % ctx->cipher->blocksize) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, f->r,
+ APLOGNO(03413) "Buffer size %" APR_OFF_T_FMT
+ " is not a multiple of the block size %d of cipher '%s'",
+ ctx->conf->size, ctx->cipher->blocksize, dconf->cipher);
+ return APR_EGENERAL;
+ }
+
+ /* fetch the key we'll be using for decryption */
+ rv = ap_run_crypto_key(f->r, ctx->cipher, ctx->mode, 1, &rec);
+ if (DECLINED == rv) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
+ APLOGNO(03414) "no key specified for this URL");
+ return APR_ENOKEY;
+ }
+ if (APR_SUCCESS != rv || !rec) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r,
+ APLOGNO(03415) "key could not be retrieved");
+ return APR_ENOKEY;
+ }
+ if (rec->ktype != APR_CRYPTO_KTYPE_SECRET) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r,
+ APLOGNO(03416) "key is not a symmetrical key");
+ return APR_ENOKEY;
+ }
+
+ /* attempt to import the key */
+ rv = apr_crypto_key(&ctx->key, rec, *conf->crypto, f->r->pool);
+ if (APR_STATUS_IS_ENOKEY(rv)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r,
+ APLOGNO(03417) "key could not be loaded");
+ }
+ if (APR_STATUS_IS_EPADDING(rv)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r,
+ APLOGNO(03418) "padding is not supported for cipher");
+ }
+ if (APR_STATUS_IS_EKEYTYPE(rv)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r,
+ APLOGNO(03419) "the key type is not known");
+ }
+ if (APR_SUCCESS != rv) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r,
+ APLOGNO(03420) "encryption could not be configured.");
+ return rv;
+ }
+
+ /* fetch the optional iv */
+ rv = ap_run_crypto_iv(f->r, ctx->cipher, &ctx->iv);
+ if (DECLINED != rv && APR_SUCCESS != rv) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r,
+ APLOGNO(03431) "initialisation vector could not be retrieved");
+ return rv;
+ }
+
+ return APR_SUCCESS;
+}
+
+static int init_encrypt(ap_filter_t * f)
+{
+ apr_status_t rv;
+ crypto_ctx *ctx;
+
+ ctx = f->ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
+ ctx->encrypt = 1;
+
+ rv = init_crypt(f);
+ if (APR_SUCCESS != rv) {
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ return OK;
+}
+
+static int init_decrypt(ap_filter_t * f)
+{
+ apr_status_t rv;
+ crypto_ctx *ctx;
+
+ ctx = f->ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
+ ctx->encrypt = 0;
+
+ rv = init_crypt(f);
+ if (APR_SUCCESS != rv) {
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ return OK;
+}
+
+/**
+ * Run the crypto algorithm, write to ctx->out
+ */
+static apr_status_t
+do_crypto(ap_filter_t * f, unsigned char *in, apr_off_t size, int finish)
+{
+ apr_status_t rv;
+ crypto_ctx *ctx = f->ctx;
+ apr_off_t extra = 0;
+ apr_size_t blockSize = 0;
+ int need_iv = (ctx->iv == NULL);
+ unsigned char *out;
+ apr_size_t written;
+
+ /* encrypt the given buffer */
+ if (ctx->encrypt) {
+
+ if (!ctx->block) {
+ rv = apr_crypto_block_encrypt_init(&ctx->block, &ctx->iv,
+ ctx->key, &blockSize,
+ f->r->pool);
+ if (APR_SUCCESS != rv) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r,
+ APLOGNO(03421) "could not initialise encryption");
+ return rv;
+ }
+ }
+
+ if (!ctx->out) {
+
+ if (need_iv && ctx->iv) {
+ ctx->osize += blockSize;
+ }
+
+ out = ctx->out = apr_palloc(f->r->pool,
+ ctx->osize + ctx->cipher->blocksize);
+ apr_crypto_clear(f->r->pool, ctx->out,
+ ctx->osize + ctx->cipher->blocksize);
+
+ /* no precomputed iv? write the generated iv as the first block of the stream */
+ if (need_iv && ctx->iv) {
+ memcpy(out, ctx->iv, blockSize);
+ ctx->remaining += blockSize;
+ out += blockSize;
+ extra = blockSize;
+ }
+
+ }
+ else {
+ out = ctx->out + (ctx->osize - ctx->remaining);
+ }
+
+ if (!finish) {
+ rv = apr_crypto_block_encrypt(&out, &written, in, size,
+ ctx->block);
+ if (APR_SUCCESS != rv) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r,
+ APLOGNO(03422) "crypto: attempt to encrypt failed");
+ return rv;
+ }
+ }
+
+ else {
+ rv = apr_crypto_block_encrypt_finish(out, &written, ctx->block);
+ if (APR_SUCCESS != rv) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r,
+ APLOGNO(03432) "crypto: attempt to finish encrypt failed");
+ return rv;
+ }
+ }
+ }
+
+ /* decrypt the given buffer */
+ else {
+
+ if (!ctx->out) {
+ out = ctx->out = apr_palloc(f->r->pool,
+ ctx->osize + ctx->cipher->blocksize);
+ apr_crypto_clear(f->r->pool, ctx->out,
+ ctx->osize + ctx->cipher->blocksize);
+ }
+ else {
+ out = ctx->out + (ctx->osize - ctx->remaining);
+ }
+
+ /* no precomputed iv? assume the first block in the stream is the iv */
+ if (need_iv) {
+ apr_off_t isize =
+ ctx->cipher->blocksize - (ctx->osize - ctx->remaining);
+ if (size < isize) {
+ memcpy(out, in, size);
+ ctx->remaining -= size;
+ return APR_SUCCESS;
+ }
+ else {
+ memcpy(out, in, isize);
+ ctx->remaining -= isize;
+ out += isize;
+ ctx->iv = ctx->out;
+ }
+ }
+
+ if (!ctx->block) {
+ rv = apr_crypto_block_decrypt_init(&ctx->block, &blockSize,
+ ctx->iv, ctx->key, f->r->pool);
+ if (APR_SUCCESS != rv) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r,
+ APLOGNO(03423) "could not initialise decryption");
+ return rv;
+ }
+ }
+
+ if (!finish) {
+ rv = apr_crypto_block_decrypt(&out, &written, in, size,
+ ctx->block);
+ if (APR_SUCCESS != rv) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r,
+ APLOGNO(03433) "crypto: attempt to decrypt failed (key/iv incorrect?)");
+ return rv;
+ }
+ }
+ else {
+ rv = apr_crypto_block_decrypt_finish(out, &written, ctx->block);
+ if (APR_SUCCESS != rv) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r,
+ APLOGNO(03434) "crypto: attempt to finish decrypt failed (key/iv incorrect?)");
+ return rv;
+ }
+ }
+ }
+
+ ctx->remaining -= written;
+ ctx->written += written;
+ ctx->written += extra;
+
+ return rv;
+}
+
+/**
+ * Encrypt/decrypt buckets being written to the output filter stack.
+ */
+static apr_status_t
+crypto_out_filter(ap_filter_t * f, apr_bucket_brigade * bb)
+{
+ apr_bucket *e, *after;
+ crypto_ctx *ctx = f->ctx;
+ apr_status_t rv = APR_SUCCESS;
+
+ /* Do nothing if asked to filter nothing. */
+ if (APR_BRIGADE_EMPTY(bb)) {
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ /* clear the content length */
+ if (!ctx->clength) {
+ ctx->clength = 1;
+ apr_table_unset(f->r->headers_out, "Content-Length");
+ }
+
+ /* make sure we fit in the buffer snugly */
+ if (APR_BRIGADE_EMPTY(ctx->bb)) {
+ apr_brigade_partition(bb, ctx->remaining, &after);
+ }
+
+ while (APR_SUCCESS == rv && !APR_BRIGADE_EMPTY(bb)) {
+ const char *data;
+ apr_size_t size;
+
+ e = APR_BRIGADE_FIRST(bb);
+
+ /* EOS means we are done. */
+ if (APR_BUCKET_IS_EOS(e)) {
+
+ /* handle any leftovers */
+ do_crypto(f, NULL, 0, 1);
+ apr_brigade_write(ctx->bb, NULL, NULL, (const char *) ctx->out,
+ ctx->conf->size - ctx->remaining);
+ ctx->remaining = ctx->osize;
+ ctx->written = 0;
+ apr_brigade_partition(bb, ctx->remaining, &after);
+
+ /* pass the EOS across */
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+
+ /* pass what we have down the chain */
+ rv = ap_pass_brigade(f->next, ctx->bb);
+
+ ap_remove_output_filter(f);
+ continue;
+ }
+
+ /* handle flush */
+ if (APR_BUCKET_IS_FLUSH(e)) {
+
+ /* we cannot change the laws of physics: crypto can only happen
+ * on a block boundary. As a result, just pass the flush bucket
+ * through as is, we'll send the rest of the block when it
+ * arrives in full.
+ */
+
+ /* pass the flush bucket across */
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+
+ /* pass what we have down the chain */
+ rv = ap_pass_brigade(f->next, ctx->bb);
+ continue;
+ }
+
+ /* metadata buckets are preserved as is */
+ if (APR_BUCKET_IS_METADATA(e)) {
+ /*
+ * Remove meta data bucket from old brigade and insert into the
+ * new.
+ */
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+ continue;
+ }
+
+ if (APR_SUCCESS
+ == (rv = apr_bucket_read(e, &data, &size, APR_BLOCK_READ))) {
+
+ do_crypto(f, (unsigned char *) data, size, 0);
+ apr_bucket_delete(e);
+
+ if (!ctx->remaining) {
+ apr_brigade_write(ctx->bb, NULL, NULL,
+ (const char *) ctx->out, ctx->written);
+ ctx->remaining = ctx->osize;
+ ctx->written = 0;
+ apr_brigade_partition(bb, ctx->remaining, &after);
+ rv = ap_pass_brigade(f->next, ctx->bb);
+ }
+
+ }
+
+ }
+
+ return rv;
+
+}
+
+/**
+ * Decrypt/encrypt buckets being read from the input filter stack.
+ */
+static apr_status_t
+crypto_in_filter(ap_filter_t * f, apr_bucket_brigade * bb,
+ ap_input_mode_t mode, apr_read_type_e block,
+ apr_off_t readbytes)
+{
+ apr_bucket *e, *after;
+ apr_status_t rv = APR_SUCCESS;
+ crypto_ctx *ctx = f->ctx;
+
+ if (!ctx->tmp) {
+ ctx->tmp = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+ }
+
+ /* just get out of the way of things we don't want. */
+ if (mode != AP_MODE_READBYTES) {
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ }
+
+ /* if our buffer is empty, read off the network until the buffer is full */
+ if (APR_BRIGADE_EMPTY(ctx->bb)) {
+ ctx->remaining = ctx->osize;
+ ctx->written = 0;
+
+ while (!ctx->seen_eos && ctx->remaining > 0) {
+ const char *data;
+ apr_size_t size = 0;
+
+ if (APR_BRIGADE_EMPTY(ctx->tmp)) {
+ rv = ap_get_brigade(f->next, ctx->tmp, mode, block,
+ ctx->remaining);
+ }
+
+ /* if an error was received, bail out now. If the error is
+ * EAGAIN and we have not yet seen an EOS, we will definitely
+ * be called again, at which point we will send our buffered
+ * data. Instead of sending EAGAIN, some filters return an
+ * empty brigade instead when data is not yet available. In
+ * this case, we drop through and pass buffered data, if any.
+ */
+ if (APR_STATUS_IS_EAGAIN(rv)
+ || (rv == APR_SUCCESS
+ && block == APR_NONBLOCK_READ
+ && APR_BRIGADE_EMPTY(ctx->tmp))) {
+ if (APR_BRIGADE_EMPTY(ctx->bb)) {
+ return rv;
+ }
+ break;
+ }
+ if (APR_SUCCESS != rv) {
+ return rv;
+ }
+
+ while (!APR_BRIGADE_EMPTY(ctx->tmp)) {
+ e = APR_BRIGADE_FIRST(ctx->tmp);
+
+ /* if we see an EOS, we are done */
+ if (APR_BUCKET_IS_EOS(e)) {
+
+ /* handle any leftovers */
+ do_crypto(f, NULL, 0, 1);
+ apr_brigade_write(ctx->bb, NULL, NULL,
+ (const char *) ctx->out, ctx->written);
+
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+ ctx->seen_eos = 1;
+ break;
+ }
+
+ /* flush buckets clear the buffer */
+ if (APR_BUCKET_IS_FLUSH(e)) {
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+ break;
+ }
+
+ /* pass metadata buckets through */
+ if (APR_BUCKET_IS_METADATA(e)) {
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+ continue;
+ }
+
+ /* read the bucket in, pack it into the buffer */
+ rv = apr_bucket_read(e, &data, &size, block);
+ if (APR_STATUS_IS_EAGAIN(rv)) {
+ if (APR_BRIGADE_EMPTY(ctx->bb)) {
+ return rv;
+ }
+ break;
+ }
+ if (APR_SUCCESS != rv) {
+ return rv;
+ }
+
+ do_crypto(f, (unsigned char *) data, size, 0);
+ if (!ctx->remaining || APR_STATUS_IS_EAGAIN(rv)) {
+ apr_brigade_write(ctx->bb, NULL, NULL,
+ (const char *) ctx->out, ctx->written);
+ }
+
+ apr_bucket_delete(e);
+
+ }
+ }
+ }
+
+ /* give the caller the data they asked for from the buffer */
+ apr_brigade_partition(ctx->bb, readbytes, &after);
+ e = APR_BRIGADE_FIRST(ctx->bb);
+ while (e != after) {
+ if (APR_BUCKET_IS_EOS(e)) {
+ /* last bucket read, step out of the way */
+ ap_remove_input_filter(f);
+ }
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(bb, e);
+ e = APR_BRIGADE_FIRST(ctx->bb);
+ }
+
+ /* clear the content length */
+ if (!ctx->clength) {
+ ctx->clength = 1;
+ apr_table_unset(f->r->headers_in, "Content-Length");
+ }
+
+ return APR_SUCCESS;
+}
+
+static int crypto_handler(request_rec *r)
+{
+ crypto_conf *conf;
+ crypto_dir_conf *dconf;
+ apr_status_t rv;
+
+ if (*r->handler != 'c' || strcmp(r->handler, "crypto-key")) {
+ return DECLINED;
+ }
+
+ conf = ap_get_module_config(r->server->module_config, &crypto_module);
+ dconf = ap_get_module_config(r->per_dir_config, &crypto_module);
+
+ /* sanity check - has crypto been switched on? */
+ if (!conf->crypto || !*conf->crypto) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r,
+ APLOGNO(03435) "crypto driver has not been enabled for this server");
+ return APR_EGENERAL;
+ }
+
+ if (dconf->key_set) {
+ const apr_crypto_key_rec_t *rec;
+ apr_crypto_block_key_type_t *cipher;
+ apr_crypto_block_key_mode_t *mode;
+
+ /* fetch the cipher for this location */
+ rv = init_cipher(r, &cipher, &mode);
+ if (APR_SUCCESS != rv) {
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ /* fetch the key we'll be using for encryption / decryption */
+ rv = ap_run_crypto_key(r, cipher, mode, 1, &rec);
+ if (DECLINED == rv) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ APLOGNO(03424) "no key specified for this URL");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ if (APR_SUCCESS != rv || !rec) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+ APLOGNO(03425) "key could not be retrieved");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ if (rec->ktype != APR_CRYPTO_KTYPE_SECRET) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+ APLOGNO(03426) "key is not a symmetrical key");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ ap_set_content_type(r, "application/octet-stream");
+ ap_set_content_length(r, rec->k.secret.secretLen);
+ ap_rwrite(rec->k.secret.secret, rec->k.secret.secretLen, r);
+
+ return OK;
+
+ }
+ else {
+
+ return HTTP_NOT_FOUND;
+
+ }
+
+}
+
+static void *create_crypto_config(apr_pool_t * p, server_rec *s)
+{
+ crypto_conf *new = (crypto_conf *) apr_pcalloc(p, sizeof(crypto_conf));
+
+ /* if no library has been configured, set the recommended library
+ * as a sensible default.
+ */
+#ifdef APU_CRYPTO_RECOMMENDED_DRIVER
+ new->library = APU_CRYPTO_RECOMMENDED_DRIVER;
+#endif
+ new->crypto = apr_pcalloc(p, sizeof(apr_crypto_t *));
+
+ return (void *) new;
+}
+
+static void *merge_crypto_config(apr_pool_t * p, void *basev, void *addv)
+{
+ crypto_conf *new = (crypto_conf *) apr_pcalloc(p, sizeof(crypto_conf));
+ crypto_conf *add = (crypto_conf *) addv;
+ crypto_conf *base = (crypto_conf *) basev;
+
+ new->library = (add->library_set == 0) ? base->library : add->library;
+ new->params = (add->library_set == 0) ? base->params : add->params;
+ new->library_set = add->library_set || base->library_set;
+
+ new->crypto = base->crypto;
+
+ return (void *) new;
+}
+
+static void *create_crypto_dir_config(apr_pool_t * p, char *dummy)
+{
+ crypto_dir_conf *new =
+ (crypto_dir_conf *) apr_pcalloc(p, sizeof(crypto_dir_conf));
+
+ new->size_set = 0; /* unset */
+ new->size = DEFAULT_BUFFER_SIZE; /* default size */
+ new->cipher = DEFAULT_CIPHER;
+ new->cipher = DEFAULT_MODE;
+
+ return (void *) new;
+}
+
+static void *merge_crypto_dir_config(apr_pool_t * p, void *basev, void *addv)
+{
+ crypto_dir_conf *new =
+ (crypto_dir_conf *) apr_pcalloc(p, sizeof(crypto_dir_conf));
+ crypto_dir_conf *add = (crypto_dir_conf *) addv;
+ crypto_dir_conf *base = (crypto_dir_conf *) basev;
+
+ new->size = (add->size_set == 0) ? base->size : add->size;
+ new->size_set = add->size_set || base->size_set;
+
+ new->cipher = (add->cipher_set == 0) ? base->cipher : add->cipher;
+ new->mode = (add->cipher_set == 0) ? base->mode : add->mode;
+ new->cipher_set = add->cipher_set || base->cipher_set;
+
+ new->key = (add->key_set == 0) ? base->key : add->key;
+ new->key_set = add->key_set || base->key_set;
+
+ new->iv = (add->iv_set == 0) ? base->iv : add->iv;
+ new->iv_set = add->iv_set || base->iv_set;
+
+ return new;
+}
+
+static const char *set_crypto_size(cmd_parms *cmd, void *dconf,
+ const char *arg)
+{
+ crypto_dir_conf *conf = dconf;
+
+ if (APR_SUCCESS != apr_strtoff(&(conf->size), arg, NULL, 10)
+ || conf->size <= 0) {
+ return "CryptoSize must be a size in bytes, and greater than zero";
+ }
+ conf->size_set = 1;
+
+ return NULL;
+}
+
+static const char *set_crypto_driver(cmd_parms *cmd, void *config,
+ const char *arg)
+{
+ crypto_conf *conf =
+ (crypto_conf *) ap_get_module_config(cmd->server->module_config,
+ &crypto_module);
+
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+
+ if (err != NULL) {
+ return err;
+ }
+
+ conf->library = ap_getword_conf(cmd->pool, &arg);
+ conf->params = arg;
+ conf->crypto = apr_pcalloc(cmd->pool, sizeof(apr_crypto_t *));
+ conf->library_set = 1;
+
+ return NULL;
+}
+
+static const char *set_crypto_cipher(cmd_parms *cmd, void *config,
+ const char *cipher, const char *mode)
+{
+ crypto_dir_conf *dconf = (crypto_dir_conf *) config;
+
+ dconf->cipher = cipher;
+ dconf->mode = mode ? mode : DEFAULT_MODE;
+ dconf->cipher_set = 1;
+
+ return NULL;
+}
+
+static const char *set_crypto_key(cmd_parms *cmd, void *config,
+ const char *arg)
+{
+ crypto_dir_conf *dconf = (crypto_dir_conf *) config;
+
+ pass_conf *key = dconf->key = apr_pcalloc(cmd->pool, sizeof(pass_conf));
+ dconf->key_set = 1;
+
+ return parse_pass_conf_binary(cmd, key, arg);
+}
+
+static const char *set_crypto_iv(cmd_parms *cmd, void *config,
+ const char *arg)
+{
+ crypto_dir_conf *dconf = (crypto_dir_conf *) config;
+
+ pass_conf *iv = dconf->iv = apr_pcalloc(cmd->pool, sizeof(pass_conf));
+ dconf->iv_set = 1;
+
+ return parse_pass_conf_binary(cmd, iv, arg);
+}
+
+static const command_rec crypto_cmds[] = {
+ AP_INIT_TAKE1("CryptoSize", set_crypto_size, NULL, ACCESS_CONF,
+ "Maximum size of the buffer used by the crypto filters"),
+ AP_INIT_RAW_ARGS("CryptoDriver", set_crypto_driver, NULL, RSRC_CONF,
+ "The underlying crypto library driver to use"),
+ AP_INIT_TAKE12("CryptoCipher", set_crypto_cipher, NULL,
+ RSRC_CONF | OR_AUTHCFG,
+ "The underlying crypto cipher and mode to use. If unspecified, the mode defaults to 'cbc'"),
+ AP_INIT_TAKE1("CryptoKey", set_crypto_key, NULL, RSRC_CONF | OR_AUTHCFG,
+ "The crypto key scheme and value to use. Scheme is one of 'none', 'file:', 'hex:', 'base64:' or 'decimal:'"),
+ AP_INIT_TAKE1("CryptoIV", set_crypto_iv, NULL, RSRC_CONF | OR_AUTHCFG,
+ "The crypto IV scheme and value to use. Scheme is one of 'none', 'file:', 'hex:', 'base64:' or 'decimal:'"),
+ {NULL}
+};
+
+/**
+ * Initialise the SSL in the post_config hook.
+ */
+static int
+crypto_init(apr_pool_t * p, apr_pool_t * plog,
+ apr_pool_t * ptemp, server_rec *s)
+{
+ const apr_crypto_driver_t *driver = NULL;
+
+ while (s) {
+
+ crypto_conf *conf = ap_get_module_config(s->module_config,
+ &crypto_module);
+
+ if (conf->library_set && !*conf->crypto) {
+
+ const apu_err_t *err = NULL;
+ apr_status_t rv;
+
+ rv = apr_crypto_init(p);
+ if (APR_SUCCESS != rv) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+ APLOGNO(03427) "APR crypto could not be initialised");
+ return rv;
+ }
+
+ rv = apr_crypto_get_driver(&driver, conf->library, conf->params,
+ &err, p);
+ if (APR_EREINIT == rv) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, rv, s,
+ APLOGNO(03436) "warning: crypto for '%s' was already initialised, "
+ "using existing configuration", conf->library);
+ rv = APR_SUCCESS;
+ }
+ if (APR_SUCCESS != rv && err) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+ APLOGNO(03437) "The crypto library '%s' could not be loaded: %s (%s: %d)",
+ conf->library, err->msg, err->reason, err->rc);
+ return rv;
+ }
+ if (APR_ENOTIMPL == rv) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+ APLOGNO(03438) "The crypto library '%s' could not be found",
+ conf->library);
+ return rv;
+ }
+ if (APR_SUCCESS != rv || !driver) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+ APLOGNO(03439) "The crypto library '%s' could not be loaded",
+ conf->library);
+ return rv;
+ }
+
+ rv = apr_crypto_make(conf->crypto, driver, conf->params, p);
+ if (APR_SUCCESS != rv) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+ APLOGNO(03440) "The crypto library '%s' could not be initialised",
+ conf->library);
+ return rv;
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_INFO, rv, s,
+ APLOGNO(03441) "The crypto library '%s' was loaded successfully",
+ conf->library);
+
+ }
+ s = s->next;
+ }
+
+ return OK;
+}
+
+static apr_status_t
+crypto_key(request_rec *r,
+ apr_crypto_block_key_type_t * cipher,
+ apr_crypto_block_key_mode_t * mode, int pad,
+ const apr_crypto_key_rec_t ** recptr)
+{
+ apr_crypto_key_rec_t *rec;
+
+ crypto_dir_conf *conf =
+ ap_get_module_config(r->per_dir_config, &crypto_module);
+
+ pass_conf *key = conf->key;
+
+ *recptr = rec = apr_palloc(r->pool, sizeof(apr_crypto_key_rec_t));
+ rec->ktype = APR_CRYPTO_KTYPE_SECRET;
+ rec->type = cipher->type;
+ rec->mode = mode->mode;
+ rec->pad = pad;
+ rec->k.secret.secretLen = cipher->keysize;
+
+ return exec_pass_conf_binary(r, key, "key", cipher->keysize,
+ &(rec->k.secret.secret));
+}
+
+static apr_status_t
+crypto_iv(request_rec *r,
+ apr_crypto_block_key_type_t * cipher, const unsigned char **v)
+{
+ crypto_dir_conf *conf =
+ ap_get_module_config(r->per_dir_config, &crypto_module);
+
+ pass_conf *iv = conf->iv;
+
+ return exec_pass_conf_binary(r, iv, "iv", cipher->ivsize, v);
+}
+
+static void register_hooks(apr_pool_t * p)
+{
+ ap_hook_crypto_key(crypto_key, NULL, NULL, APR_HOOK_REALLY_LAST);
+ ap_hook_crypto_iv(crypto_iv, NULL, NULL, APR_HOOK_REALLY_LAST);
+ ap_hook_post_config(crypto_init, NULL, NULL, APR_HOOK_LAST);
+ ap_hook_handler(crypto_handler, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_register_output_filter("ENCRYPT", crypto_out_filter, init_encrypt,
+ AP_FTYPE_RESOURCE);
+ ap_register_input_filter("ENCRYPT", crypto_in_filter, init_encrypt,
+ AP_FTYPE_RESOURCE);
+ ap_register_output_filter("DECRYPT", crypto_out_filter, init_decrypt,
+ AP_FTYPE_RESOURCE);
+ ap_register_input_filter("DECRYPT", crypto_in_filter, init_decrypt,
+ AP_FTYPE_RESOURCE);
+}
+
+AP_DECLARE_MODULE(crypto) = {
+ STANDARD20_MODULE_STUFF,
+ create_crypto_dir_config, /* create per-directory config structure */
+ merge_crypto_dir_config, /* merge per-directory config structures */
+ create_crypto_config, /* create per-server config structure */
+ merge_crypto_config, /* merge per-server config structures */
+ crypto_cmds, /* command apr_table_t */
+ register_hooks /* register hooks */
+};
+
+#else
+#error This module requires at least v1.6.0 of apr-util.
+#endif
diff --git a/modules/filters/mod_crypto.dsp b/modules/filters/mod_crypto.dsp
new file mode 100644
index 0000000000..51c05e9056
--- /dev/null
+++ b/modules/filters/mod_crypto.dsp
@@ -0,0 +1,111 @@
+# Microsoft Developer Studio Project File - Name="mod_crypto" - 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_crypto - 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_crypto.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_crypto.mak" CFG="mod_crypto - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_crypto - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_crypto - 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_crypto - 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" /D "HAVE_ZUTIL_H" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_crypto_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_crypto.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_crypto.so" /d LONG_NAME="crypto_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_crypto.so" /base:@..\..\os\win32\BaseAddr.ref,mod_crypto.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_crypto.so" /base:@..\..\os\win32\BaseAddr.ref,mod_crypto.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_crypto.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_crypto - 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 "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "HAVE_ZUTIL_H" /Fd"Debug\mod_crypto_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_crypto.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_crypto.so" /d LONG_NAME="crypto_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_crypto.so" /base:@..\..\os\win32\BaseAddr.ref,mod_crypto.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_crypto.so" /base:@..\..\os\win32\BaseAddr.ref,mod_crypto.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_crypto.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_crypto - Win32 Release"
+# Name "mod_crypto - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\mod_crypto.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project
diff --git a/modules/filters/mod_crypto.h b/modules/filters/mod_crypto.h
new file mode 100644
index 0000000000..e7e66fa187
--- /dev/null
+++ b/modules/filters/mod_crypto.h
@@ -0,0 +1,95 @@
+/* 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 _MOD_CRYPTO_H_
+#define _MOD_CRYPTO_H_
+
+/* Create a set of CRYPTO_DECLARE(type), CRYPTO_DECLARE_NONSTD(type) and
+ * CRYPTO_DECLARE_DATA with appropriate export and import tags for the platform
+ */
+#if !defined(WIN32)
+#define CRYPTO_DECLARE(type) type
+#define CRYPTO_DECLARE_NONSTD(type) type
+#define CRYPTO_DECLARE_DATA
+#elif defined(CRYPTO_DECLARE_STATIC)
+#define CRYPTO_DECLARE(type) type __stdcall
+#define CRYPTO_DECLARE_NONSTD(type) type
+#define CRYPTO_DECLARE_DATA
+#elif defined(CRYPTO_DECLARE_EXPORT)
+#define CRYPTO_DECLARE(type) __declspec(dllexport) type __stdcall
+#define CRYPTO_DECLARE_NONSTD(type) __declspec(dllexport) type
+#define CRYPTO_DECLARE_DATA __declspec(dllexport)
+#else
+#define CRYPTO_DECLARE(type) __declspec(dllimport) type __stdcall
+#define CRYPTO_DECLARE_NONSTD(type) __declspec(dllimport) type
+#define CRYPTO_DECLARE_DATA __declspec(dllimport)
+#endif
+
+/**
+ * @file mod_crypto.h
+ * @brief Crypto Module for Apache
+ *
+ * @defgroup MOD_CRYPTO mod_crypto
+ * @ingroup APACHE_MODS
+ * @{
+ */
+
+#include "apr.h"
+#include "apr_hooks.h"
+#include "apr_optional.h"
+#include "apr_tables.h"
+#include "apr_uuid.h"
+#include "apr_pools.h"
+#include "apr_time.h"
+#include "apr_crypto.h"
+
+#include "httpd.h"
+#include "http_config.h"
+#include "ap_config.h"
+
+/**
+ * Hook to provide a key.
+ *
+ * @param r The request
+ * @param cipher The cipher to use with this key
+ * @param rec Pointer to the key record from which to derive the key
+ */
+APR_DECLARE_EXTERNAL_HOOK(ap, CRYPTO, apr_status_t, crypto_key,
+ (request_rec *r,
+ apr_crypto_block_key_type_t * cipher,
+ apr_crypto_block_key_mode_t * mode, int pad,
+ const apr_crypto_key_rec_t ** rec));
+
+/**
+ * Hook to provide an initialization vector (IV).
+ *
+ * @param r The request
+ * @param size The block size of the expected IV.
+ * @param iv A pointer to where the iv will be returned
+ */
+APR_DECLARE_EXTERNAL_HOOK(ap, CRYPTO, apr_status_t, crypto_iv,
+ (request_rec *r,
+ apr_crypto_block_key_type_t * cipher,
+ const unsigned char **iv));
+
+/**
+ * The name of the module.
+ */
+extern module AP_MODULE_DECLARE_DATA crypto_module;
+
+/** @} */
+
+#endif