http://sni.velox.ch/httpd-2.2.11-sni.20090427.patch - server name indication support for mod_ssl / Apache 2.2.11 (RFC 4366, section 3.1) Last updated 2009-04-27, by Kaspar Brand. Provided AS IS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND. Based on a patch from the EdelKey project (http://www.edelweb.fr/EdelKey/files/), includes further improvements by Ruediger Pluem (from httpd trunk). Needs openssl-SNAP-20060330 / OpenSSL 0.9.8f or later to work properly (ftp://ftp.openssl.org/snapshot/). OpenSSL versions prior to 0.9.8j must be configured explicitly for TLS extension support at compile time ("./config enable-tlsext"). Index: httpd-2.2.11/modules/ssl/ssl_private.h =================================================================== --- httpd-2.2.11/modules/ssl/ssl_private.h (revision 768863) +++ httpd-2.2.11/modules/ssl/ssl_private.h (working copy) @@ -35,6 +35,7 @@ #include "http_connection.h" #include "http_request.h" #include "http_protocol.h" +#include "http_vhost.h" #include "util_script.h" #include "util_filter.h" #include "util_ebcdic.h" @@ -129,6 +130,9 @@ ap_set_module_config(c->conn_config, &ssl_module, #define mySrvConfig(srv) (SSLSrvConfigRec *)ap_get_module_config(srv->module_config, &ssl_module) #define myDirConfig(req) (SSLDirConfigRec *)ap_get_module_config(req->per_dir_config, &ssl_module) #define myModConfig(srv) (mySrvConfig((srv)))->mc +#define mySrvFromConn(c) (myConnConfig(c))->server +#define mySrvConfigFromConn(c) mySrvConfig(mySrvFromConn(c)) +#define myModConfigFromConn(c) myModConfig(mySrvFromConn(c)) #define myCtxVarSet(mc,num,val) mc->rCtx.pV##num = val #define myCtxVarGet(mc,num,type) (type)(mc->rCtx.pV##num) @@ -347,6 +351,7 @@ typedef struct { int is_proxy; int disabled; int non_ssl_request; + server_rec *server; } SSLConnRec; typedef struct { @@ -449,6 +454,9 @@ struct SSLSrvConfigRec { BOOL cipher_server_pref; modssl_ctx_t *server; modssl_ctx_t *proxy; +#ifndef OPENSSL_NO_TLSEXT + ssl_enabled_t strict_sni_vhost_check; +#endif }; /** @@ -513,6 +521,9 @@ const char *ssl_cmd_SSLOptions(cmd_parms *, void const char *ssl_cmd_SSLRequireSSL(cmd_parms *, void *); const char *ssl_cmd_SSLRequire(cmd_parms *, void *, const char *); const char *ssl_cmd_SSLUserName(cmd_parms *, void *, const char *); +#ifndef OPENSSL_NO_TLSEXT +const char *ssl_cmd_SSLStrictSNIVHostCheck(cmd_parms *cmd, void *dcfg, int flag); +#endif const char *ssl_cmd_SSLProxyEngine(cmd_parms *cmd, void *dcfg, int flag); const char *ssl_cmd_SSLProxyProtocol(cmd_parms *, void *, const char *); @@ -555,6 +566,9 @@ int ssl_callback_NewSessionCacheEntry(SSL SSL_SESSION *ssl_callback_GetSessionCacheEntry(SSL *, unsigned char *, int, int *); void ssl_callback_DelSessionCacheEntry(SSL_CTX *, SSL_SESSION *); void ssl_callback_LogTracingState(MODSSL_INFO_CB_ARG_TYPE, int, int); +#ifndef OPENSSL_NO_TLSEXT +int ssl_callback_ServerNameIndication(SSL *, int *, modssl_ctx_t *); +#endif /** Session Cache Support */ void ssl_scache_init(server_rec *, apr_pool_t *); Index: httpd-2.2.11/modules/ssl/ssl_engine_init.c =================================================================== --- httpd-2.2.11/modules/ssl/ssl_engine_init.c (revision 768863) +++ httpd-2.2.11/modules/ssl/ssl_engine_init.c (working copy) @@ -358,6 +358,33 @@ static void ssl_init_server_check(server_rec *s, } } +#ifndef OPENSSL_NO_TLSEXT +static void ssl_init_ctx_tls_extensions(server_rec *s, + apr_pool_t *p, + apr_pool_t *ptemp, + modssl_ctx_t *mctx) +{ + /* + * Configure TLS extensions support + */ + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, + "Configuring TLS extension handling"); + + /* + * Server name indication (SNI) + */ + if (!SSL_CTX_set_tlsext_servername_callback(mctx->ssl_ctx, + ssl_callback_ServerNameIndication) || + !SSL_CTX_set_tlsext_servername_arg(mctx->ssl_ctx, mctx)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "Unable to initialize TLS servername extension " + "callback (incompatible OpenSSL version?)"); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + ssl_die(); + } +} +#endif + static void ssl_init_ctx_protocol(server_rec *s, apr_pool_t *p, apr_pool_t *ptemp, @@ -690,6 +717,9 @@ static void ssl_init_ctx(server_rec *s, if (mctx->pks) { /* XXX: proxy support? */ ssl_init_ctx_cert_chain(s, p, ptemp, mctx); +#ifndef OPENSSL_NO_TLSEXT + ssl_init_ctx_tls_extensions(s, p, ptemp, mctx); +#endif } } @@ -1039,9 +1069,19 @@ void ssl_init_CheckServers(server_rec *base_server klen = strlen(key); if ((ps = (server_rec *)apr_hash_get(table, key, klen))) { - ap_log_error(APLOG_MARK, APLOG_WARNING, 0, + ap_log_error(APLOG_MARK, +#ifdef OPENSSL_NO_TLSEXT + APLOG_WARNING, +#else + APLOG_DEBUG, +#endif + 0, base_server, +#ifdef OPENSSL_NO_TLSEXT "Init: SSL server IP/port conflict: " +#else + "Init: SSL server IP/port overlap: " +#endif "%s (%s:%d) vs. %s (%s:%d)", ssl_util_vhostid(p, s), (s->defn_name ? s->defn_name : "unknown"), @@ -1058,8 +1098,14 @@ void ssl_init_CheckServers(server_rec *base_server if (conflict) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server, +#ifdef OPENSSL_NO_TLSEXT "Init: You should not use name-based " "virtual hosts in conjunction with SSL!!"); +#else + "Init: Name-based SSL virtual hosts only " + "work for clients with TLS server name indication " + "support (RFC 4366)"); +#endif } } Index: httpd-2.2.11/modules/ssl/ssl_engine_config.c =================================================================== --- httpd-2.2.11/modules/ssl/ssl_engine_config.c (revision 768863) +++ httpd-2.2.11/modules/ssl/ssl_engine_config.c (working copy) @@ -169,6 +169,9 @@ static SSLSrvConfigRec *ssl_config_server_new(apr_ sc->vhost_id_len = 0; /* set during module init */ sc->session_cache_timeout = UNSET; sc->cipher_server_pref = UNSET; +#ifndef OPENSSL_NO_TLSEXT + sc->strict_sni_vhost_check = SSL_ENABLED_UNSET; +#endif modssl_ctx_init_proxy(sc, p); @@ -257,6 +260,9 @@ void *ssl_config_server_merge(apr_pool_t *p, void cfgMergeBool(proxy_enabled); cfgMergeInt(session_cache_timeout); cfgMergeBool(cipher_server_pref); +#ifndef OPENSSL_NO_TLSEXT + cfgMerge(strict_sni_vhost_check, SSL_ENABLED_UNSET); +#endif modssl_ctx_cfg_merge_proxy(base->proxy, add->proxy, mrg->proxy); @@ -1411,6 +1417,17 @@ const char *ssl_cmd_SSLUserName(cmd_parms *cmd, vo return NULL; } +#ifndef OPENSSL_NO_TLSEXT +const char *ssl_cmd_SSLStrictSNIVHostCheck(cmd_parms *cmd, void *dcfg, int flag) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + sc->strict_sni_vhost_check = flag ? SSL_ENABLED_TRUE : SSL_ENABLED_FALSE; + + return NULL; +} +#endif + void ssl_hook_ConfigTest(apr_pool_t *pconf, server_rec *s) { if (!ap_exists_config_define("DUMP_CERTS")) { Index: httpd-2.2.11/modules/ssl/ssl_engine_io.c =================================================================== --- httpd-2.2.11/modules/ssl/ssl_engine_io.c (revision 768863) +++ httpd-2.2.11/modules/ssl/ssl_engine_io.c (working copy) @@ -695,7 +695,7 @@ static apr_status_t ssl_io_input_read(bio_filter_i */ ap_log_cerror(APLOG_MARK, APLOG_INFO, inctx->rc, c, "SSL library error %d reading data", ssl_err); - ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, c->base_server); + ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, mySrvFromConn(c)); } if (inctx->rc == APR_SUCCESS) { @@ -799,7 +799,7 @@ static apr_status_t ssl_filter_write(ap_filter_t * */ ap_log_cerror(APLOG_MARK, APLOG_INFO, outctx->rc, c, "SSL library error %d writing data", ssl_err); - ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, c->base_server); + ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, mySrvFromConn(c)); } if (outctx->rc == APR_SUCCESS) { outctx->rc = APR_EGENERAL; @@ -861,7 +861,7 @@ static apr_status_t ssl_io_filter_error(ap_filter_ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, f->c, "SSL handshake failed: HTTP spoken on HTTPS port; " "trying to send HTML error page"); - ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, f->c->base_server); + ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, sslconn->server); sslconn->non_ssl_request = 1; ssl_io_filter_disable(sslconn, f); @@ -971,11 +971,11 @@ static apr_status_t ssl_filter_io_shutdown(ssl_fil SSL_smart_shutdown(ssl); /* and finally log the fact that we've closed the connection */ - if (c->base_server->loglevel >= APLOG_INFO) { + if (mySrvFromConn(c)->loglevel >= APLOG_INFO) { ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, "Connection closed to child %ld with %s shutdown " "(server %s)", - c->id, type, ssl_util_vhostid(c->pool, c->base_server)); + c->id, type, ssl_util_vhostid(c->pool, mySrvFromConn(c))); } /* deallocate the SSL connection */ @@ -1021,21 +1021,23 @@ static int ssl_io_filter_connect(ssl_filter_ctx_t { conn_rec *c = (conn_rec *)SSL_get_app_data(filter_ctx->pssl); SSLConnRec *sslconn = myConnConfig(c); - SSLSrvConfigRec *sc = mySrvConfig(c->base_server); + SSLSrvConfigRec *sc; X509 *cert; int n; int ssl_err; long verify_result; + server_rec *server; if (SSL_is_init_finished(filter_ctx->pssl)) { return APR_SUCCESS; } + server = mySrvFromConn(c); if (sslconn->is_proxy) { if ((n = SSL_connect(filter_ctx->pssl)) <= 0) { ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, "SSL Proxy connect failed"); - ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, c->base_server); + ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, server); /* ensure that the SSL structures etc are freed, etc: */ ssl_filter_io_shutdown(filter_ctx, c, 1); return HTTP_BAD_GATEWAY; @@ -1092,8 +1094,8 @@ static int ssl_io_filter_connect(ssl_filter_ctx_t ap_log_cerror(APLOG_MARK, APLOG_INFO, rc, c, "SSL library error %d in handshake " "(server %s)", ssl_err, - ssl_util_vhostid(c->pool, c->base_server)); - ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, c->base_server); + ssl_util_vhostid(c->pool, server)); + ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, server); } if (inctx->rc == APR_SUCCESS) { @@ -1102,6 +1104,7 @@ static int ssl_io_filter_connect(ssl_filter_ctx_t return ssl_filter_io_shutdown(filter_ctx, c, 1); } + sc = mySrvConfig(sslconn->server); /* * Check for failed client authentication @@ -1127,7 +1130,7 @@ static int ssl_io_filter_connect(ssl_filter_ctx_t "accepting certificate based on " "\"SSLVerifyClient optional_no_ca\" " "configuration"); - ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, c->base_server); + ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, server); } else { const char *error = sslconn->verify_error ? @@ -1137,7 +1140,7 @@ static int ssl_io_filter_connect(ssl_filter_ctx_t ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, "SSL client authentication failed: %s", error ? error : "unknown"); - ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, c->base_server); + ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, server); return ssl_filter_io_shutdown(filter_ctx, c, 1); } @@ -1809,7 +1812,7 @@ long ssl_io_data_cb(BIO *bio, int cmd, return rc; if ((c = (conn_rec *)SSL_get_app_data(ssl)) == NULL) return rc; - s = c->base_server; + s = mySrvFromConn(c); if ( cmd == (BIO_CB_WRITE|BIO_CB_RETURN) || cmd == (BIO_CB_READ |BIO_CB_RETURN) ) { Index: httpd-2.2.11/modules/ssl/ssl_engine_vars.c =================================================================== --- httpd-2.2.11/modules/ssl/ssl_engine_vars.c (revision 768863) +++ httpd-2.2.11/modules/ssl/ssl_engine_vars.c (working copy) @@ -320,6 +320,12 @@ static char *ssl_var_lookup_ssl(apr_pool_t *p, con else if (ssl != NULL && strcEQ(var, "COMPRESS_METHOD")) { result = ssl_var_lookup_ssl_compress_meth(ssl); } +#ifndef OPENSSL_NO_TLSEXT + else if (ssl != NULL && strcEQ(var, "TLS_SNI")) { + result = apr_pstrdup(p, SSL_get_servername(ssl, + TLSEXT_NAMETYPE_host_name)); + } +#endif return result; } @@ -589,7 +595,7 @@ static char *ssl_var_lookup_ssl_cert_verify(apr_po vrc = SSL_get_verify_result(ssl); xs = SSL_get_peer_certificate(ssl); - if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs == NULL) + if (vrc == X509_V_OK && verr == NULL && xs == NULL) /* no client verification done at all */ result = "NONE"; else if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs != NULL) Index: httpd-2.2.11/modules/ssl/ssl_engine_kernel.c =================================================================== --- httpd-2.2.11/modules/ssl/ssl_engine_kernel.c (revision 768863) +++ httpd-2.2.11/modules/ssl/ssl_engine_kernel.c (working copy) @@ -31,6 +31,9 @@ #include "ssl_private.h" static void ssl_configure_env(request_rec *r, SSLConnRec *sslconn); +#ifndef OPENSSL_NO_TLSEXT +static int ssl_find_vhost(void *servername, conn_rec *c, server_rec *s); +#endif /* * Post Read Request Handler @@ -39,6 +42,9 @@ int ssl_hook_ReadReq(request_rec *r) { SSLConnRec *sslconn = myConnConfig(r->connection); SSL *ssl; +#ifndef OPENSSL_NO_TLSEXT + const char *servername; +#endif if (!sslconn) { return DECLINED; @@ -87,6 +93,51 @@ int ssl_hook_ReadReq(request_rec *r) if (!ssl) { return DECLINED; } +#ifndef OPENSSL_NO_TLSEXT + if ((servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) { + char *host, *scope_id; + apr_port_t port; + apr_status_t rv; + + /* + * The SNI extension supplied a hostname. So don't accept requests + * with either no hostname or a different hostname. + */ + if (!r->hostname) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Hostname %s provided via SNI, but no hostname" + " provided in HTTP request", servername); + return HTTP_BAD_REQUEST; + } + rv = apr_parse_addr_port(&host, &scope_id, &port, r->hostname, r->pool); + if (rv != APR_SUCCESS || scope_id) { + return HTTP_BAD_REQUEST; + } + if (strcmp(host, servername)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Hostname %s provided via SNI and hostname %s provided" + " via HTTP are different", servername, host); + return HTTP_BAD_REQUEST; + } + } + else if ((((mySrvConfig(r->server))->strict_sni_vhost_check + == SSL_ENABLED_TRUE) + || (mySrvConfig(sslconn->server))->strict_sni_vhost_check + == SSL_ENABLED_TRUE) + && r->connection->vhost_lookup_data) { + /* + * We are using a name based configuration here, but no hostname was + * provided via SNI. Don't allow that if are requested to do strict + * checking. Check whether this strict checking was setup either in the + * server config we used for handshaking or in our current server. + * This should avoid insecure configuration by accident. + */ + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "No hostname was provided via SNI for a name based" + " virtual host"); + return HTTP_FORBIDDEN; + } +#endif SSL_set_app_data2(ssl, r); /* @@ -155,10 +206,11 @@ static void ssl_configure_env(request_rec *r, SSLC */ int ssl_hook_Access(request_rec *r) { - SSLDirConfigRec *dc = myDirConfig(r); - SSLSrvConfigRec *sc = mySrvConfig(r->server); - SSLConnRec *sslconn = myConnConfig(r->connection); - SSL *ssl = sslconn ? sslconn->ssl : NULL; + SSLDirConfigRec *dc = myDirConfig(r); + SSLSrvConfigRec *sc = mySrvConfig(r->server); + SSLConnRec *sslconn = myConnConfig(r->connection); + SSL *ssl = sslconn ? sslconn->ssl : NULL; + server_rec *handshakeserver = sslconn ? sslconn->server : NULL; SSL_CTX *ctx = NULL; apr_array_header_t *requires; ssl_require_t *ssl_requires; @@ -252,7 +304,7 @@ int ssl_hook_Access(request_rec *r) * has to enable this via ``SSLOptions +OptRenegotiate''. So we do no * implicit optimizations. */ - if (dc->szCipherSuite) { + if (dc->szCipherSuite || (r->server != handshakeserver)) { /* remember old state */ if (dc->nOptions & SSL_OPT_OPTRENEGOTIATE) { @@ -267,7 +319,10 @@ int ssl_hook_Access(request_rec *r) } /* configure new state */ - if (!modssl_set_cipher_list(ssl, dc->szCipherSuite)) { + if ((dc->szCipherSuite || sc->server->auth.cipher_suite) && + !modssl_set_cipher_list(ssl, dc->szCipherSuite ? + dc->szCipherSuite : + sc->server->auth.cipher_suite)) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, "Unable to reconfigure (per-directory) " @@ -334,8 +389,13 @@ int ssl_hook_Access(request_rec *r) sk_SSL_CIPHER_free(cipher_list_old); } - /* tracing */ if (renegotiate) { +#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE + if (sc->cipher_server_pref == TRUE) { + SSL_set_options(ssl, SSL_OP_CIPHER_SERVER_PREFERENCE); + } +#endif + /* tracing */ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "Reconfigured cipher suite will force renegotiation"); } @@ -348,24 +408,22 @@ int ssl_hook_Access(request_rec *r) * function and not by OpenSSL internally (and our function is aware of * both the per-server and per-directory contexts). So we cannot ask * OpenSSL about the currently verify depth. Instead we remember it in our - * ap_ctx attached to the SSL* of OpenSSL. We've to force the + * SSLConnRec attached to the SSL* of OpenSSL. We've to force the * renegotiation if the reconfigured/new verify depth is less than the * currently active/remembered verify depth (because this means more * restriction on the certificate chain). */ - if (dc->nVerifyDepth != UNSET) { - /* XXX: doesnt look like sslconn->verify_depth is actually used */ - if (!(n = sslconn->verify_depth)) { - sslconn->verify_depth = n = sc->server->auth.verify_depth; - } - - /* determine whether a renegotiation has to be forced */ - if (dc->nVerifyDepth < n) { - renegotiate = TRUE; - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, - "Reduced client verification depth will force " - "renegotiation"); - } + n = sslconn->verify_depth ? + sslconn->verify_depth : + (mySrvConfig(handshakeserver))->server->auth.verify_depth; + /* determine the new depth */ + sslconn->verify_depth = (dc->nVerifyDepth != UNSET) ? + dc->nVerifyDepth : sc->server->auth.verify_depth; + if (sslconn->verify_depth < n) { + renegotiate = TRUE; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "Reduced client verification depth will force " + "renegotiation"); } /* @@ -382,18 +440,22 @@ int ssl_hook_Access(request_rec *r) * verification but at least skip the I/O-intensive renegotation * handshake. */ - if (dc->nVerifyClient != SSL_CVERIFY_UNSET) { + if ((dc->nVerifyClient != SSL_CVERIFY_UNSET) || + (sc->server->auth.verify_mode != SSL_CVERIFY_UNSET)) { /* remember old state */ verify_old = SSL_get_verify_mode(ssl); /* configure new state */ verify = SSL_VERIFY_NONE; - if (dc->nVerifyClient == SSL_CVERIFY_REQUIRE) { + if ((dc->nVerifyClient == SSL_CVERIFY_REQUIRE) || + (sc->server->auth.verify_mode == SSL_CVERIFY_REQUIRE)) { verify |= SSL_VERIFY_PEER_STRICT; } if ((dc->nVerifyClient == SSL_CVERIFY_OPTIONAL) || - (dc->nVerifyClient == SSL_CVERIFY_OPTIONAL_NO_CA)) + (dc->nVerifyClient == SSL_CVERIFY_OPTIONAL_NO_CA) || + (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL) || + (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA)) { verify |= SSL_VERIFY_PEER; } @@ -430,6 +492,45 @@ int ssl_hook_Access(request_rec *r) renegotiate_quick ? "quick " : ""); } } + /* If we're handling a request for a vhost other than the default one, + * then we need to make sure that client authentication is properly + * enforced. For clients supplying an SNI extension, the peer + * certificate verification has happened in the handshake already + * (and r->server == handshakeserver). For non-SNI requests, + * an additional check is needed here. If client authentication + * is configured as mandatory, then we can only proceed if the + * CA list doesn't have to be changed (OpenSSL doesn't provide + * an option to change the list for an existing session). + */ + if ((r->server != handshakeserver) + && renegotiate + && ((verify & SSL_VERIFY_PEER) || + (verify & SSL_VERIFY_FAIL_IF_NO_PEER_CERT))) { + SSLSrvConfigRec *hssc = mySrvConfig(handshakeserver); + +#define MODSSL_CFG_CA_NE(f, sc1, sc2) \ + (sc1->server->auth.f && \ + (!sc2->server->auth.f || \ + strNE(sc1->server->auth.f, sc2->server->auth.f))) + + if (MODSSL_CFG_CA_NE(ca_cert_file, sc, hssc) || + MODSSL_CFG_CA_NE(ca_cert_path, sc, hssc)) { + if (verify & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, + "Non-default virtual host with SSLVerify set to " + "'require' and VirtualHost-specific CA certificate " + "list is only available to clients with TLS server " + "name indication (SNI) support"); + modssl_set_verify(ssl, verify_old, NULL); + return HTTP_FORBIDDEN; + } else + /* let it pass, possibly with an "incorrect" peer cert, + * so make sure the SSL_CLIENT_VERIFY environment variable + * will indicate partial success only, later on. + */ + sslconn->verify_info = "GENEROUS"; + } + } } /* @@ -666,8 +767,10 @@ int ssl_hook_Access(request_rec *r) /* * Finally check for acceptable renegotiation results */ - if (dc->nVerifyClient != SSL_CVERIFY_NONE) { - BOOL do_verify = (dc->nVerifyClient == SSL_CVERIFY_REQUIRE); + if ((dc->nVerifyClient != SSL_CVERIFY_NONE) || + (sc->server->auth.verify_mode != SSL_CVERIFY_NONE)) { + BOOL do_verify = ((dc->nVerifyClient == SSL_CVERIFY_REQUIRE) || + (sc->server->auth.verify_mode == SSL_CVERIFY_REQUIRE)); if (do_verify && (SSL_get_verify_result(ssl) != X509_V_OK)) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, @@ -997,6 +1100,9 @@ int ssl_hook_Fixup(request_rec *r) SSLDirConfigRec *dc = myDirConfig(r); apr_table_t *env = r->subprocess_env; char *var, *val = ""; +#ifndef OPENSSL_NO_TLSEXT + const char *servername; +#endif STACK_OF(X509) *peer_certs; SSL *ssl; int i; @@ -1018,6 +1124,13 @@ int ssl_hook_Fixup(request_rec *r) /* the always present HTTPS (=HTTP over SSL) flag! */ apr_table_setn(env, "HTTPS", "on"); +#ifndef OPENSSL_NO_TLSEXT + /* add content of SNI TLS extension (if supplied with ClientHello) */ + if ((servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) { + apr_table_set(env, "SSL_TLS_SNI", servername); + } +#endif + /* standard SSL environment variables */ if (dc->nOptions & SSL_OPT_STDENVVARS) { for (i = 0; ssl_hook_Fixup_vars[i]; i++) { @@ -1105,7 +1218,7 @@ int ssl_hook_Fixup(request_rec *r) RSA *ssl_callback_TmpRSA(SSL *ssl, int export, int keylen) { conn_rec *c = (conn_rec *)SSL_get_app_data(ssl); - SSLModConfigRec *mc = myModConfig(c->base_server); + SSLModConfigRec *mc = myModConfigFromConn(c); int idx; ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, @@ -1137,7 +1250,7 @@ RSA *ssl_callback_TmpRSA(SSL *ssl, int export, int DH *ssl_callback_TmpDH(SSL *ssl, int export, int keylen) { conn_rec *c = (conn_rec *)SSL_get_app_data(ssl); - SSLModConfigRec *mc = myModConfig(c->base_server); + SSLModConfigRec *mc = myModConfigFromConn(c); int idx; ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, @@ -1166,8 +1279,8 @@ int ssl_callback_SSLVerify(int ok, X509_STORE_CTX SSL *ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); conn_rec *conn = (conn_rec *)SSL_get_app_data(ssl); - server_rec *s = conn->base_server; request_rec *r = (request_rec *)SSL_get_app_data2(ssl); + server_rec *s = r ? r->server : mySrvFromConn(conn); SSLSrvConfigRec *sc = mySrvConfig(s); SSLDirConfigRec *dc = r ? myDirConfig(r) : NULL; @@ -1290,7 +1403,10 @@ int ssl_callback_SSLVerify(int ok, X509_STORE_CTX int ssl_callback_SSLVerify_CRL(int ok, X509_STORE_CTX *ctx, conn_rec *c) { - server_rec *s = c->base_server; + SSL *ssl = X509_STORE_CTX_get_ex_data(ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + request_rec *r = (request_rec *)SSL_get_app_data2(ssl); + server_rec *s = r ? r->server : mySrvFromConn(c); SSLSrvConfigRec *sc = mySrvConfig(s); SSLConnRec *sslconn = myConnConfig(c); modssl_ctx_t *mctx = myCtxConfig(sslconn, sc); @@ -1515,7 +1631,7 @@ static void modssl_proxy_info_log(server_rec *s, int ssl_callback_proxy_cert(SSL *ssl, MODSSL_CLIENT_CERT_CB_ARG_TYPE **x509, EVP_PKEY **pkey) { conn_rec *c = (conn_rec *)SSL_get_app_data(ssl); - server_rec *s = c->base_server; + server_rec *s = mySrvFromConn(c); SSLSrvConfigRec *sc = mySrvConfig(s); X509_NAME *ca_name, *issuer; X509_INFO *info; @@ -1613,7 +1729,7 @@ int ssl_callback_NewSessionCacheEntry(SSL *ssl, SS { /* Get Apache context back through OpenSSL context */ conn_rec *conn = (conn_rec *)SSL_get_app_data(ssl); - server_rec *s = conn->base_server; + server_rec *s = mySrvFromConn(conn); SSLSrvConfigRec *sc = mySrvConfig(s); long timeout = sc->session_cache_timeout; BOOL rc; @@ -1661,7 +1777,7 @@ SSL_SESSION *ssl_callback_GetSessionCacheEntry(SSL { /* Get Apache context back through OpenSSL context */ conn_rec *conn = (conn_rec *)SSL_get_app_data(ssl); - server_rec *s = conn->base_server; + server_rec *s = mySrvFromConn(conn); SSL_SESSION *session; /* @@ -1739,7 +1855,7 @@ void ssl_callback_LogTracingState(MODSSL_INFO_CB_A return; } - s = c->base_server; + s = mySrvFromConn(c); if (!(sc = mySrvConfig(s))) { return; } @@ -1810,3 +1926,138 @@ void ssl_callback_LogTracingState(MODSSL_INFO_CB_A } } +#ifndef OPENSSL_NO_TLSEXT +/* + * This callback function is executed when OpenSSL encounters an extended + * client hello with a server name indication extension ("SNI", cf. RFC 4366). + */ +int ssl_callback_ServerNameIndication(SSL *ssl, int *al, modssl_ctx_t *mctx) +{ + const char *servername = + SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + + if (servername) { + conn_rec *c = (conn_rec *)SSL_get_app_data(ssl); + if (c) { + if (ap_vhost_iterate_given_conn(c, ssl_find_vhost, + (void *)servername)) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, + "SSL virtual host for servername %s found", + servername); + return SSL_TLSEXT_ERR_OK; + } + else { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, + "No matching SSL virtual host for servername " + "%s found (using default/first virtual host)", + servername); + return SSL_TLSEXT_ERR_ALERT_WARNING; + } + } + } + + return SSL_TLSEXT_ERR_NOACK; +} + +/* + * Find a (name-based) SSL virtual host where either the ServerName + * or one of the ServerAliases matches the supplied name (to be used + * with ap_vhost_iterate_given_conn()) + */ +static int ssl_find_vhost(void *servername, conn_rec *c, server_rec *s) +{ + SSLSrvConfigRec *sc; + SSL *ssl; + BOOL found = FALSE; + apr_array_header_t *names; + int i; + SSLConnRec *sslcon; + + /* check ServerName */ + if (!strcasecmp(servername, s->server_hostname)) { + found = TRUE; + } + + /* + * if not matched yet, check ServerAlias entries + * (adapted from vhost.c:matches_aliases()) + */ + if (!found) { + names = s->names; + if (names) { + char **name = (char **)names->elts; + for (i = 0; i < names->nelts; ++i) { + if (!name[i]) + continue; + if (!strcasecmp(servername, name[i])) { + found = TRUE; + break; + } + } + } + } + + /* if still no match, check ServerAlias entries with wildcards */ + if (!found) { + names = s->wild_names; + if (names) { + char **name = (char **)names->elts; + for (i = 0; i < names->nelts; ++i) { + if (!name[i]) + continue; + if (!ap_strcasecmp_match(servername, name[i])) { + found = TRUE; + break; + } + } + } + } + + /* set SSL_CTX (if matched) */ + sslcon = myConnConfig(c); + if (found && (ssl = sslcon->ssl) && + (sc = mySrvConfig(s))) { + SSL_set_SSL_CTX(ssl, sc->server->ssl_ctx); + /* + * SSL_set_SSL_CTX() only deals with the server cert, + * so we need to duplicate a few additional settings + * from the ctx by hand + */ + SSL_set_options(ssl, SSL_CTX_get_options(ssl->ctx)); + if ((SSL_get_verify_mode(ssl) == SSL_VERIFY_NONE) || + (SSL_num_renegotiations(ssl) == 0)) { + /* + * Only initialize the verification settings from the ctx + * if they are not yet set, or if we're called when a new + * SSL connection is set up (num_renegotiations == 0). + * Otherwise, we would possibly reset a per-directory + * configuration which was put into effect by ssl_hook_Access. + */ + SSL_set_verify(ssl, SSL_CTX_get_verify_mode(ssl->ctx), + SSL_CTX_get_verify_callback(ssl->ctx)); + } + + /* + * Save the found server into our SSLConnRec for later + * retrieval + */ + sslcon->server = s; + + /* + * There is one special filter callback, which is set + * very early depending on the base_server's log level. + * If this is not the first vhost we're now selecting + * (and the first vhost doesn't use APLOG_DEBUG), then + * we need to set that callback here. + */ + if (s->loglevel >= APLOG_DEBUG) { + BIO_set_callback(SSL_get_rbio(ssl), ssl_io_data_cb); + BIO_set_callback_arg(SSL_get_rbio(ssl), (void *)ssl); + } + + return 1; + } + + return 0; +} +#endif Index: httpd-2.2.11/modules/ssl/mod_ssl.c =================================================================== --- httpd-2.2.11/modules/ssl/mod_ssl.c (revision 768863) +++ httpd-2.2.11/modules/ssl/mod_ssl.c (working copy) @@ -145,6 +145,10 @@ static const command_rec ssl_config_cmds[] = { "Use the server's cipher ordering preference") SSL_CMD_ALL(UserName, TAKE1, "Set user name to SSL variable value") +#ifndef OPENSSL_NO_TLSEXT + SSL_CMD_SRV(StrictSNIVHostCheck, FLAG, + "Strict SNI virtual host checking") +#endif /* * Proxy configuration for remote SSL connections @@ -295,6 +299,8 @@ static SSLConnRec *ssl_init_connection_ctx(conn_re sslconn = apr_pcalloc(c->pool, sizeof(*sslconn)); + sslconn->server = c->base_server; + myConnConfigSet(c, sslconn); return sslconn; @@ -302,9 +308,10 @@ static SSLConnRec *ssl_init_connection_ctx(conn_re int ssl_proxy_enable(conn_rec *c) { - SSLSrvConfigRec *sc = mySrvConfig(c->base_server); + SSLSrvConfigRec *sc; SSLConnRec *sslconn = ssl_init_connection_ctx(c); + sc = mySrvConfig(sslconn->server); if (!sc->proxy_enabled) { ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, @@ -322,10 +329,16 @@ int ssl_proxy_enable(conn_rec *c) int ssl_engine_disable(conn_rec *c) { - SSLSrvConfigRec *sc = mySrvConfig(c->base_server); + SSLSrvConfigRec *sc; - SSLConnRec *sslconn; + SSLConnRec *sslconn = myConnConfig(c); + if (sslconn) { + sc = mySrvConfig(sslconn->server); + } + else { + sc = mySrvConfig(c->base_server); + } if (sc->enabled == SSL_ENABLED_FALSE) { return 0; } @@ -339,21 +352,24 @@ int ssl_engine_disable(conn_rec *c) int ssl_init_ssl_connection(conn_rec *c) { - SSLSrvConfigRec *sc = mySrvConfig(c->base_server); + SSLSrvConfigRec *sc; SSL *ssl; SSLConnRec *sslconn = myConnConfig(c); char *vhost_md5; modssl_ctx_t *mctx; + server_rec *server; + if (!sslconn) { + sslconn = ssl_init_connection_ctx(c); + } + server = sslconn->server; + sc = mySrvConfig(server); + /* * Seed the Pseudo Random Number Generator (PRNG) */ - ssl_rand_seed(c->base_server, c->pool, SSL_RSCTX_CONNECT, ""); + ssl_rand_seed(server, c->pool, SSL_RSCTX_CONNECT, ""); - if (!sslconn) { - sslconn = ssl_init_connection_ctx(c); - } - mctx = sslconn->is_proxy ? sc->proxy : sc->server; /* @@ -365,7 +381,7 @@ int ssl_init_ssl_connection(conn_rec *c) ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, "Unable to create a new SSL connection from the SSL " "context"); - ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, c->base_server); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, server); c->aborted = 1; @@ -380,7 +396,7 @@ int ssl_init_ssl_connection(conn_rec *c) { ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, "Unable to set session id context to `%s'", vhost_md5); - ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, c->base_server); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, server); c->aborted = 1; @@ -429,9 +445,15 @@ static apr_port_t ssl_hook_default_port(const requ static int ssl_hook_pre_connection(conn_rec *c, void *csd) { - SSLSrvConfigRec *sc = mySrvConfig(c->base_server); + SSLSrvConfigRec *sc; SSLConnRec *sslconn = myConnConfig(c); + if (sslconn) { + sc = mySrvConfig(sslconn->server); + } + else { + sc = mySrvConfig(c->base_server); + } /* * Immediately stop processing if SSL is disabled for this connection */ Index: httpd-2.2.11/modules/ssl/ssl_toolkit_compat.h =================================================================== --- httpd-2.2.11/modules/ssl/ssl_toolkit_compat.h (revision 768863) +++ httpd-2.2.11/modules/ssl/ssl_toolkit_compat.h (working copy) @@ -264,6 +264,12 @@ typedef void (*modssl_popfree_fn)(char *data); #define SSL_SESS_CACHE_NO_INTERNAL SSL_SESS_CACHE_NO_INTERNAL_LOOKUP #endif +#ifndef OPENSSL_NO_TLSEXT +#ifndef SSL_CTRL_SET_TLSEXT_HOSTNAME +#define OPENSSL_NO_TLSEXT +#endif +#endif + #endif /* SSL_TOOLKIT_COMPAT_H */ /** @} */ Index: httpd-2.2.11/docs/manual/mod/mod_ssl.html.en =================================================================== --- httpd-2.2.11/docs/manual/mod/mod_ssl.html.en (revision 768863) +++ httpd-2.2.11/docs/manual/mod/mod_ssl.html.en (working copy) @@ -75,6 +75,7 @@ to provide the cryptography engine.

  • SSLRequireSSL
  • SSLSessionCache
  • SSLSessionCacheTimeout
  • +
  • SSLStrictSNIVHostCheck
  • SSLUserName
  • SSLVerifyClient
  • SSLVerifyDepth
  • @@ -1613,6 +1614,37 @@ SSLSessionCacheTimeout 600
    top
    +

    SSLStrictSNIVHostCheck Directive

    + + + + + + + +
    Description:Whether to allow non SNI clients to access a name based virtual +host. +
    Syntax:SSLStrictSNIVHostCheck on|off
    Default:SSLStrictSNIVHostCheck off
    Context:server config, virtual host
    Status:Extension
    Module:mod_ssl
    +

    +This directive sets whether a non SNI client is allowed to access a name based +virtual host. If set to on in the non default name based virtual +host, non SNI clients are not allowed to access this particular virtual host. +If set to on in the default name based virtual host, non SNI +clients are not allowed to access any name based virtual host belonging to +this IP / port combination. +

    + +

    +This option is only available if httpd was compiled against an SNI capable +version of OpenSSL. +

    + +

    Example

    +SSLStrictSNIVHostCheck on +

    + +
    +
    top

    SSLUserName Directive

    @@ -1717,6 +1749,6 @@ SSLVerifyDepth 10

    Available Languages:  en 

    Description:Variable name to determine user name