Index: docs/manual/mod/mod_proxy_ajp.xml =================================================================== --- docs/manual/mod/mod_proxy_ajp.xml (revision 943568) +++ docs/manual/mod/mod_proxy_ajp.xml (working copy) @@ -50,6 +50,43 @@ mod_proxy + +ProxyAJPForwardSSLCertChain +Whether to send the client's SSL certificate chain +ProxyAJPForwardSSLCertChain on|off +ProxyAJPForwardSSLCertChain off +server config +virtual host +Available in Apache ? and later + + +

This directive controls whether the client's SSL certificate chain (the +client's certificate and all intermediate certificates) or only the client's +certificate (without any intermediate certificates) will be sent to the AJP +server when proxying an incoming SSL connection. If on, the SSL +certificate chain will be sent.

+

This is disabled by default because the default AJP message buffers in both +mod_proxy_ajp and Tomcat are not large enough to hold a long certificate chain +(more than 1 or 2 intermediate certificates), so clients having a long chain +will get errors if this is enabled without first adjusting the AJP message +buffer sizes appropriately.

+

To adjust the AJP message buffer size in mod_proxy_ajp, use the +ProxyIOBufferSize directive. The +specified size should be large enough to hold the client's (PEM-encoded) +certificate and all (PEM-encoded) intermediate certificates, plus 400 bytes or +so for other AJP request data. The minimum size is 8192, the maximum is 65536, +and the default is 8192.

+

To adjust the AJP message buffer size in Tomcat, specify the +packetSize attribute in the AJP connector declaration. The same +buffer size should be used for both mod_proxy_ajp and Tomcat.

+

Note that support for processing of intermediate certificates is only +available in Tomcat 5.5.28+ and 6.0.21+ (earlier versions will simply ignore +the intermediate certificates in the AJP request), and the +packetSize attribute is only available in Tomcat 5.5.20+ and +6.0.2+ (earlier versions had a fixed buffer size of 8192 bytes).

+
+
+
Overview of the protocol

The AJP13 protocol is packet-oriented. A binary format was presumably chosen over the more readable plain text for reasons of Index: modules/proxy/ajp_config.h =================================================================== --- modules/proxy/ajp_config.h (revision 0) +++ modules/proxy/ajp_config.h (revision 0) @@ -0,0 +1,47 @@ +/* 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 AJP_CONFIG_H +#define AJP_CONFIG_H + +/** + * @file ajp_config.h + * @brief AJP internal configuration management. + * + * @defgroup AJP_config AJP configuration + * @ingroup MOD_PROXY + * @{ + */ + +#ifndef BOOL +#define BOOL unsigned char +#endif +#ifndef FALSE +#define FALSE (0) +#endif +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +typedef struct { + BOOL send_cert_chain; + BOOL send_cert_chain_set; +} proxy_ajp_conf; + +extern module AP_MODULE_DECLARE_DATA proxy_ajp_module; + +#endif /*AJP_CONFIG_H*/ +/** @} */ Index: modules/proxy/ajp.h =================================================================== --- modules/proxy/ajp.h (revision 943568) +++ modules/proxy/ajp.h (working copy) @@ -61,6 +61,7 @@ /* The following environment variables match mod_ssl! */ #define AJP13_HTTPS_INDICATOR "HTTPS" #define AJP13_SSL_CLIENT_CERT_INDICATOR "SSL_CLIENT_CERT" +#define AJP13_SSL_CLIENT_CHAIN_PREFIX "SSL_CLIENT_CERT_CHAIN_" #define AJP13_SSL_CIPHER_INDICATOR "SSL_CIPHER" #define AJP13_SSL_SESSION_INDICATOR "SSL_SESSION_ID" #define AJP13_SSL_KEY_SIZE_INDICATOR "SSL_CIPHER_USEKEYSIZE" Index: modules/proxy/mod_proxy_ajp.c =================================================================== --- modules/proxy/mod_proxy_ajp.c (revision 943568) +++ modules/proxy/mod_proxy_ajp.c (working copy) @@ -18,9 +18,47 @@ #include "mod_proxy.h" #include "ajp.h" +#include "ajp_config.h" module AP_MODULE_DECLARE_DATA proxy_ajp_module; +static void *create_proxy_ajp_config(apr_pool_t *p, server_rec *s) +{ + proxy_ajp_conf *c = apr_pcalloc(p, sizeof(proxy_ajp_conf)); + c->send_cert_chain = FALSE; + c->send_cert_chain_set = FALSE; + return c; +} + +static void *merge_proxy_ajp_config(apr_pool_t *p, void *basev, void *overridesv) +{ + proxy_ajp_conf *c = apr_pcalloc(p, sizeof(proxy_ajp_conf)); + proxy_ajp_conf *base = (proxy_ajp_conf *) basev; + proxy_ajp_conf *overrides = (proxy_ajp_conf *) overridesv; + c->send_cert_chain = (overrides->send_cert_chain_set ? + overrides->send_cert_chain : base->send_cert_chain); + c->send_cert_chain_set = (base->send_cert_chain_set || + overrides->send_cert_chain_set); + return c; +} + +static const char * + set_forward_cert_chain(cmd_parms *params, void *dummy, const char *arg) +{ + proxy_ajp_conf *c = + ap_get_module_config(params->server->module_config, &proxy_ajp_module); + + if (strcasecmp(arg, "Off") == 0) + c->send_cert_chain = FALSE; + else if (strcasecmp(arg, "On") == 0) + c->send_cert_chain = TRUE; + else + return "ProxyAJPForwardSSLCertChain must be one of: On | Off"; + + c->send_cert_chain_set = TRUE; + return NULL; +} + /* * Canonicalise http-like URLs. * scheme is the scheme for the URL @@ -724,13 +762,21 @@ proxy_hook_canon_handler(proxy_ajp_canon, NULL, NULL, APR_HOOK_FIRST); } +static const command_rec proxy_ajp_cmds[] = +{ + AP_INIT_TAKE1("ProxyAJPForwardSSLCertChain", set_forward_cert_chain, NULL, + RSRC_CONF, "Forward the entire SSL Certificate Chain " + "('on', 'off')"), + {NULL} +}; + module AP_MODULE_DECLARE_DATA proxy_ajp_module = { STANDARD20_MODULE_STUFF, NULL, /* create per-directory config structure */ NULL, /* merge per-directory config structures */ - NULL, /* create per-server config structure */ - NULL, /* merge per-server config structures */ - NULL, /* command apr_table_t */ + create_proxy_ajp_config, /* create per-server config structure */ + merge_proxy_ajp_config, /* merge per-server config structures */ + proxy_ajp_cmds, /* command apr_table_t */ ap_proxy_http_register_hook /* register hooks */ }; Index: modules/proxy/ajp_header.c =================================================================== --- modules/proxy/ajp_header.c (revision 943568) +++ modules/proxy/ajp_header.c (working copy) @@ -16,6 +16,7 @@ #include "ajp_header.h" #include "ajp.h" +#include "ajp_config.h" static const char *response_trans_headers[] = { "Content-Type", @@ -200,7 +201,7 @@ ?auth_type (byte)(string) ?query_string (byte)(string) ?jvm_route (byte)(string) - ?ssl_cert (byte)(string) + ?ssl_certs (byte)(string) ?ssl_cipher (byte)(string) ?ssl_session (byte)(string) ?ssl_key_size (byte)(int) via JkOptions +ForwardKeySize @@ -220,6 +221,8 @@ const char *session_route, *envvar; const apr_array_header_t *arr = apr_table_elts(r->subprocess_env); const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts; + proxy_ajp_conf *sc = + ap_get_module_config(r->server->module_config, &proxy_ajp_module); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "Into ajp_marshal_into_msgb"); @@ -356,6 +359,24 @@ if ((envvar = ap_proxy_ssl_val(r->pool, r->server, r->connection, r, AJP13_SSL_CLIENT_CERT_INDICATOR)) && envvar[0]) { + /* If ProxyAJPForwardSSLCertChain is set, send the entire chain */ + if (sc->send_cert_chain) { + apr_array_header_t *certs = apr_array_make(r->pool, 1, sizeof(char *)); + int i = 0; + char *envvar_name; + do { + *(const char **)apr_array_push(certs) = envvar; + envvar_name = apr_psprintf(r->pool, "%s%d", + AJP13_SSL_CLIENT_CHAIN_PREFIX, i); + envvar = ap_proxy_ssl_val(r->pool, r->server, r->connection, r, + envvar_name); + i++; + } while (envvar && envvar[0]); + envvar = apr_array_pstrcat(r->pool, certs, '\0'); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "ajp_marshal_into_msgb: SSL Client Cert Chain " + "(%d certs)", i-1); + } if (ajp_msg_append_uint8(msg, SC_A_SSL_CERT) || ajp_msg_append_string(msg, envvar)) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, @@ -363,6 +384,8 @@ "Error appending the SSL certificates"); return AJP_EOVERFLOW; } + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "ajp_marshal_into_msgb: SSL Client Cert"); } if ((envvar = ap_proxy_ssl_val(r->pool, r->server, r->connection, r,