diff --git a/CHANGES b/CHANGES index f58752434d..e5067d56f5 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,10 @@ Changes with Apache 2.3.3 + *) mod_ssl: Add SSLProxyCheckPeerExpire and SSLProxyCheckPeerCN directives + to enable stricter checking of remote server certificates. + [Ruediger Pluem] + *) ab: Fix a 100% CPU loop on platforms where a failed non-blocking connect returns EINPROGRESS and a subsequent poll() returns only POLLERR. Observed on HP-UX. [Eric Covener] diff --git a/docs/manual/mod/mod_ssl.xml b/docs/manual/mod/mod_ssl.xml index 61c1e46159..aff648de16 100644 --- a/docs/manual/mod/mod_ssl.xml +++ b/docs/manual/mod/mod_ssl.xml @@ -1501,6 +1501,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 990e736939..3f709c4fab 100644 --- a/modules/proxy/mod_proxy_http.c +++ b/modules/proxy/mod_proxy_http.c @@ -1964,6 +1964,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 9483619237..98dfd4d545 100644 --- a/modules/ssl/mod_ssl.c +++ b/modules/ssl/mod_ssl.c @@ -166,6 +166,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 23bc8f9b70..41118c5da9 100644 --- a/modules/ssl/ssl_engine_config.c +++ b/modules/ssl/ssl_engine_config.c @@ -173,6 +173,8 @@ static SSLSrvConfigRec *ssl_config_server_new(apr_pool_t *p) sc->session_cache_timeout = UNSET; sc->cipher_server_pref = UNSET; sc->ssl_log_level = SSL_LOG_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); @@ -266,6 +268,8 @@ void *ssl_config_server_merge(apr_pool_t *p, void *basev, void *addv) cfgMergeInt(session_cache_timeout); cfgMergeBool(cipher_server_pref); cfgMerge(ssl_log_level, SSL_LOG_UNSET); + 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); @@ -1418,6 +1422,24 @@ const char *ssl_cmd_SSLOCSPDefaultResponder(cmd_parms *cmd, void *dcfg, const ch 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 8e2754f345..2db613e179 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" /* _________________________________________________________________ ** @@ -1033,6 +1034,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. @@ -1060,6 +1086,9 @@ static apr_status_t ssl_io_filter_handshake(ssl_filter_ctx_t *filter_ctx) server = mySrvFromConn(c); if (sslconn->is_proxy) { + const char *hostname_note; + + sc = mySrvConfig(sslconn->server); if ((n = SSL_connect(filter_ctx->pssl)) <= 0) { ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, "SSL Proxy connect failed"); @@ -1069,6 +1098,47 @@ static apr_status_t ssl_io_filter_handshake(ssl_filter_ctx_t *filter_ctx) return MODSSL_ERROR_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 66b265c226..f9f82b82d1 100644 --- a/modules/ssl/ssl_private.h +++ b/modules/ssl/ssl_private.h @@ -477,6 +477,8 @@ struct SSLSrvConfigRec { modssl_ctx_t *server; modssl_ctx_t *proxy; ssl_log_level_e ssl_log_level; + ssl_enabled_t proxy_ssl_check_peer_expire; + ssl_enabled_t proxy_ssl_check_peer_cn; }; /** @@ -554,6 +556,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); const char *ssl_cmd_SSLOCSPOverrideResponder(cmd_parms *cmd, void *dcfg, int flag); const char *ssl_cmd_SSLOCSPDefaultResponder(cmd_parms *cmd, void *dcfg, const char *arg);