diff options
author | Graham Leggett <minfrin@apache.org> | 2016-07-10 17:27:03 +0000 |
---|---|---|
committer | Graham Leggett <minfrin@apache.org> | 2016-07-10 17:27:03 +0000 |
commit | 45b089f7acb62ac7f456285404a9551cbd98a1bf (patch) | |
tree | c88dc506bd68881f59d9cc2428e26056cbccda0b | |
parent | 91b7139dba05562838a843853eeb5e0e9839e1a6 (diff) | |
download | httpd-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.dsw | 21 | ||||
-rw-r--r-- | Apache.dsw | 24 | ||||
-rw-r--r-- | CHANGES | 3 | ||||
-rw-r--r-- | build/rpm/httpd.spec.in | 3 | ||||
-rw-r--r-- | docs/log-message-tags/next-number | 2 | ||||
-rw-r--r-- | docs/manual/expr.xml | 2 | ||||
-rw-r--r-- | docs/manual/mod/allmodules.xml | 1 | ||||
-rw-r--r-- | docs/manual/mod/mod_crypto.xml | 278 | ||||
-rw-r--r-- | docs/manual/mod/mod_crypto.xml.meta | 12 | ||||
-rw-r--r-- | modules/filters/NWGNUmakefile | 1 | ||||
-rw-r--r-- | modules/filters/NWGNUmod_crypto | 248 | ||||
-rw-r--r-- | modules/filters/config.m4 | 1 | ||||
-rw-r--r-- | modules/filters/mod_crypto.c | 1313 | ||||
-rw-r--r-- | modules/filters/mod_crypto.dsp | 111 | ||||
-rw-r--r-- | modules/filters/mod_crypto.h | 95 |
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> @@ -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> + <Location /key><br /> + <indent> + SetHandler crypto-key<br /> + CryptoCipher aes128<br /> + CryptoKey file:/path/to/file.key<br /> + AuthType basic<br /> + ...<br /> + </indent> + </Location><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> + <LocationMatch (?<SEQUENCE>[\d]+)[^\d^/]+$><br /> + <indent> + SetOutputFilter ENCRYPT<br /> + CryptoCipher aes128<br /> + CryptoKey file:/path/to/file.key<br /> + CryptoIV decimal:%{env:MATCH_SEQUENCE}<br /> + </indent> + </LocationMatch><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 |