From: Ruediger Pluem Date: Sat, 25 Apr 2009 09:50:27 +0000 (+0000) Subject: Backport of r760866: X-Git-Tag: 2.2.12~157 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1cda0128f7590e2eca6a852d05e19af99c4b0977;p=thirdparty%2Fapache%2Fhttpd.git Backport of r760866: * Add SSLProxyCheckPeerExpire and SSLProxyCheckPeerCN directives to enable stricter checking of remote server certificates. (docs/manual/mod/mod_ssl.xml) Documentation of SSLProxyCheckPeerExpire and SSLProxyCheckPeerCN. (modules/proxy/mod_proxy_http.c) Set the hostname of the request URL as note on the connection. (modules/ssl/ssl_private.h) Add proxy_ssl_check_peer_expire and proxy_ssl_check_peer_cn fields to the SSLSrvConfigRec. (modules/ssl/ssl_engine_config.c) Directives stuff for SSLProxyCheckPeerExpire and SSLProxyCheckPeerCN. (modules/ssl/ssl_engine_io.c) Check whether the remote servers certificate is expired / if there is a mismatch between the requested hostanme and the remote server certificates CN field. Be able to parse ASN1 times. (modules/ssl/mod_ssl.c) Directives stuff for SSLProxyCheckPeerExpire and SSLProxyCheckPeerCN. Submitted by: rpluem Reviewed by: rpluem, jim, jfclere git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.2.x@768504 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index fdd5836818c..2697cd93441 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,10 @@ -*- coding: utf-8 -*- Changes with Apache 2.2.12 + *) mod_ssl: Add SSLProxyCheckPeerExpire and SSLProxyCheckPeerCN directives + to enable stricter checking of remote server certificates. + [Ruediger Pluem] + *) mod_substitute: Fix a memory leak. PR 44948 [Dan Poirier ] diff --git a/STATUS b/STATUS index 246fea3e400..b3ed5a88a96 100644 --- a/STATUS +++ b/STATUS @@ -97,14 +97,6 @@ PATCHES ACCEPTED TO BACKPORT FROM TRUNK: Trunk version of patch works +1: rpluem, wrowe, jfclere - * mod_ssl: Add SSLProxyCheckPeerExpire and SSLProxyCheckPeerCN directives - to enable stricter checking of remote server certificates. - Trunk version of patch: - http://svn.apache.org/viewvc?rev=760866&view=rev - Backport version for 2.2.x of patch: - http://people.apache.org/~rpluem/patches/SSLProxyCheckPeer.diff - +1: rpluem, jim, jfclere - * mod_proxy_ajp: Check more strictly that the backend follows the AJP protocol. Trunk version of patch: http://svn.apache.org/viewvc?rev=764239&view=rev diff --git a/docs/manual/mod/mod_ssl.xml b/docs/manual/mod/mod_ssl.xml index 2f4b0531642..1a93b791ff9 100644 --- a/docs/manual/mod/mod_ssl.xml +++ b/docs/manual/mod/mod_ssl.xml @@ -1502,6 +1502,48 @@ SSLProxyVerifyDepth 10 + +SSLProxyCheckPeerExpire +Whether to check if remote server certificate is expired + +SSLProxyCheckPeerExpire on|off|optional +SSLProxyCheckPeerExpire off +server config +virtual host + + +

+This directive sets whether it is checked if the remote server certificate +is expired or not. If the check fails a 502 status code (Bad Gateway) is +sent. +

+Example +SSLProxyCheckPeerExpire on + +
+
+ + +SSLProxyCheckPeerCN +Whether to check the remote server certificates CN field + +SSLProxyCheckPeerCN on|off|optional +SSLProxyCheckPeerCN off +server config +virtual host + + +

+This directive sets whether the remote server certificates CN field is +compared against the hostname of the request URL. If both are not equal +a 502 status code (Bad Gateway) is sent. +

+Example +SSLProxyCheckPeerCN on + +
+
+ SSLProxyEngine SSL Proxy Engine Operation Switch diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c index cf695fda924..846504f1af8 100644 --- a/modules/proxy/mod_proxy_http.c +++ b/modules/proxy/mod_proxy_http.c @@ -1966,6 +1966,15 @@ static int proxy_http_handler(request_rec *r, proxy_worker *worker, if ((status = ap_proxy_connection_create(proxy_function, backend, c, r->server)) != OK) goto cleanup; + /* + * On SSL connections set a note on the connection what CN is + * requested, such that mod_ssl can check if it is requested to do + * so. + */ + if (is_ssl) { + apr_table_set(backend->connection->notes, "proxy-request-hostname", + uri->hostname); + } } /* Step Four: Send the Request */ diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c index f8a88a2adb5..f90585de2dd 100644 --- a/modules/ssl/mod_ssl.c +++ b/modules/ssl/mod_ssl.c @@ -182,6 +182,10 @@ static const command_rec ssl_config_cmds[] = { SSL_CMD_SRV(ProxyMachineCertificatePath, TAKE1, "SSL Proxy: directory containing client certificates " "(`/path/to/dir' - contains PEM encoded certificates)") + SSL_CMD_SRV(ProxyCheckPeerExpire, FLAG, + "SSL Proxy: check the peers certificate expiration date") + SSL_CMD_SRV(ProxyCheckPeerCN, FLAG, + "SSL Proxy: check the peers certificate CN") /* * Per-directory context configuration directives diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c index 3b22c4a10b6..48ef535bd8d 100644 --- a/modules/ssl/ssl_engine_config.c +++ b/modules/ssl/ssl_engine_config.c @@ -169,6 +169,8 @@ static SSLSrvConfigRec *ssl_config_server_new(apr_pool_t *p) sc->vhost_id_len = 0; /* set during module init */ sc->session_cache_timeout = UNSET; sc->cipher_server_pref = UNSET; + sc->proxy_ssl_check_peer_expire = SSL_ENABLED_UNSET; + sc->proxy_ssl_check_peer_cn = SSL_ENABLED_UNSET; modssl_ctx_init_proxy(sc, p); @@ -257,6 +259,8 @@ void *ssl_config_server_merge(apr_pool_t *p, void *basev, void *addv) cfgMergeBool(proxy_enabled); cfgMergeInt(session_cache_timeout); cfgMergeBool(cipher_server_pref); + cfgMerge(proxy_ssl_check_peer_expire, SSL_ENABLED_UNSET); + cfgMerge(proxy_ssl_check_peer_cn, SSL_ENABLED_UNSET); modssl_ctx_cfg_merge_proxy(base->proxy, add->proxy, mrg->proxy); @@ -1428,6 +1432,24 @@ const char *ssl_cmd_SSLUserName(cmd_parms *cmd, void *dcfg, return NULL; } +const char *ssl_cmd_SSLProxyCheckPeerExpire(cmd_parms *cmd, void *dcfg, int flag) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + sc->proxy_ssl_check_peer_expire = flag ? SSL_ENABLED_TRUE : SSL_ENABLED_FALSE; + + return NULL; +} + +const char *ssl_cmd_SSLProxyCheckPeerCN(cmd_parms *cmd, void *dcfg, int flag) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + sc->proxy_ssl_check_peer_cn = flag ? SSL_ENABLED_TRUE : SSL_ENABLED_FALSE; + + return NULL; +} + void ssl_hook_ConfigTest(apr_pool_t *pconf, server_rec *s) { if (!ap_exists_config_define("DUMP_CERTS")) { diff --git a/modules/ssl/ssl_engine_io.c b/modules/ssl/ssl_engine_io.c index c46e9eec9be..a4b21584653 100644 --- a/modules/ssl/ssl_engine_io.c +++ b/modules/ssl/ssl_engine_io.c @@ -28,6 +28,7 @@ core keeps dumping.'' -- Unknown */ #include "ssl_private.h" +#include "apr_date.h" /* _________________________________________________________________ ** @@ -1010,6 +1011,31 @@ static apr_status_t ssl_io_filter_cleanup(void *data) return APR_SUCCESS; } +/* + * Parse an ASN1time string as returned by ASN1_UTCTIME_print into an + * apr_time_t. + */ +static apr_time_t parseASN1time(apr_pool_t *p, const char *asn1time) +{ + char *asctime; + + /* + * Little bit ugly hack: + * The ASN1time looks very similar to the asctime format which can be + * parsed by apr_date_parse_rfc: + * It misses the weekday at the beginning (which is ignored by + * apr_date_parse_rfc anyway) and it has a GMT at the end which + * does not into the asctime pattern. So add a dummy "Sun " before + * the ASN1time and remove the GMT string at the end. + */ + asctime = apr_pstrcat(p, "Sun ", asn1time, NULL); + if (strlen(asctime) < 25) { + return APR_DATE_BAD; + } + asctime[24] = '\0'; + return apr_date_parse_rfc(asctime); +} + /* * The hook is NOT registered with ap_hook_process_connection. Instead, it is * called manually from the churn () before it tries to read any data. @@ -1032,6 +1058,8 @@ static int ssl_io_filter_connect(ssl_filter_ctx_t *filter_ctx) } if (sslconn->is_proxy) { + const char *hostname_note; + if ((n = SSL_connect(filter_ctx->pssl)) <= 0) { ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, "SSL Proxy connect failed"); @@ -1041,6 +1069,47 @@ static int ssl_io_filter_connect(ssl_filter_ctx_t *filter_ctx) return HTTP_BAD_GATEWAY; } + if (sc->proxy_ssl_check_peer_expire == SSL_ENABLED_TRUE) { + apr_time_t start_time; + apr_time_t end_time; + apr_time_t now; + + start_time = parseASN1time(c->pool, + ssl_var_lookup(NULL, c->base_server, + c, NULL, + "SSL_CLIENT_V_START")); + end_time = parseASN1time(c->pool, + ssl_var_lookup(NULL, c->base_server, + c, NULL, + "SSL_CLIENT_V_END")); + now = apr_time_now(); + if ((now > end_time) || (now < start_time)) { + ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, + "SSL Proxy: Peer certificate is expired"); + /* ensure that the SSL structures etc are freed, etc: */ + ssl_filter_io_shutdown(filter_ctx, c, 1); + return HTTP_BAD_GATEWAY; + } + } + if ((sc->proxy_ssl_check_peer_cn == SSL_ENABLED_TRUE) + && ((hostname_note = + apr_table_get(c->notes, "proxy-request-hostname")) != NULL)) { + const char *hostname; + + hostname = ssl_var_lookup(NULL, c->base_server, c, NULL, + "SSL_CLIENT_S_DN_CN"); + apr_table_unset(c->notes, "proxy-request-hostname"); + if (strcasecmp(hostname, hostname_note)) { + ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, + "SSL Proxy: Peer certificate CN mismatch:" + " Certificate CN: %s Requested hostname: %s", + hostname, hostname_note); + /* ensure that the SSL structures etc are freed, etc: */ + ssl_filter_io_shutdown(filter_ctx, c, 1); + return HTTP_BAD_GATEWAY; + } + } + return APR_SUCCESS; } diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h index 2dfa16f6540..54ca9d274df 100644 --- a/modules/ssl/ssl_private.h +++ b/modules/ssl/ssl_private.h @@ -454,6 +454,8 @@ struct SSLSrvConfigRec { BOOL cipher_server_pref; modssl_ctx_t *server; modssl_ctx_t *proxy; + ssl_enabled_t proxy_ssl_check_peer_expire; + ssl_enabled_t proxy_ssl_check_peer_cn; }; /** @@ -532,6 +534,8 @@ const char *ssl_cmd_SSLProxyCARevocationPath(cmd_parms *, void *, const char *) const char *ssl_cmd_SSLProxyCARevocationFile(cmd_parms *, void *, const char *); const char *ssl_cmd_SSLProxyMachineCertificatePath(cmd_parms *, void *, const char *); const char *ssl_cmd_SSLProxyMachineCertificateFile(cmd_parms *, void *, const char *); +const char *ssl_cmd_SSLProxyCheckPeerExpire(cmd_parms *cmd, void *dcfg, int flag); +const char *ssl_cmd_SSLProxyCheckPeerCN(cmd_parms *cmd, void *dcfg, int flag); /** module initialization */ int ssl_init_Module(apr_pool_t *, apr_pool_t *, apr_pool_t *, server_rec *);