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	2008-08-20 19:02:48.000000000 -0400
+++ httpd-2.2.11/docs/manual/mod/mod_ssl.html.en	2009-06-03 05:33:23.000000000 -0400
@@ -75,6 +75,7 @@ to provide the cryptography engine.</p>
 <li><img alt="" src="../images/down.gif" /> <a href="#sslrequiressl">SSLRequireSSL</a></li>
 <li><img alt="" src="../images/down.gif" /> <a href="#sslsessioncache">SSLSessionCache</a></li>
 <li><img alt="" src="../images/down.gif" /> <a href="#sslsessioncachetimeout">SSLSessionCacheTimeout</a></li>
+<li><img alt="" src="../images/down.gif" /> <a href="#sslstrictsnivhostcheck">SSLStrictSNIVHostCheck</a></li>
 <li><img alt="" src="../images/down.gif" /> <a href="#sslusername">SSLUserName</a></li>
 <li><img alt="" src="../images/down.gif" /> <a href="#sslverifyclient">SSLVerifyClient</a></li>
 <li><img alt="" src="../images/down.gif" /> <a href="#sslverifydepth">SSLVerifyDepth</a></li>
@@ -1613,6 +1614,37 @@ SSLSessionCacheTimeout 600
 
 </div>
 <div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif" /></a></div>
+<div class="directive-section"><h2><a name="SSLStrictSNIVHostCheck" id="SSLStrictSNIVHostCheck">SSLStrictSNIVHostCheck</a> <a name="sslstrictsnivhostcheck" id="sslstrictsnivhostcheck">Directive</a></h2>
+<table class="directive">
+<tr><th><a href="directive-dict.html#Description">Description:</a></th><td>Whether to allow non SNI clients to access a name based virtual
+host.
+</td></tr>
+<tr><th><a href="directive-dict.html#Syntax">Syntax:</a></th><td><code>SSLStrictSNIVHostCheck on|off</code></td></tr>
+<tr><th><a href="directive-dict.html#Default">Default:</a></th><td><code>SSLStrictSNIVHostCheck off</code></td></tr>
+<tr><th><a href="directive-dict.html#Context">Context:</a></th><td>server config, virtual host</td></tr>
+<tr><th><a href="directive-dict.html#Status">Status:</a></th><td>Extension</td></tr>
+<tr><th><a href="directive-dict.html#Module">Module:</a></th><td>mod_ssl</td></tr>
+</table>
+<p>
+This directive sets whether a non SNI client is allowed to access a name based
+virtual host. If set to <code>on</code> in the non default name based virtual
+host, non SNI clients are not allowed to access this particular virtual host.
+If set to <code>on</code> 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.
+</p>
+
+<div class="warning"><p>
+This option is only available if httpd was compiled against an SNI capable
+version of OpenSSL.
+</p></div>
+
+<div class="example"><h3>Example</h3><p><code>
+SSLStrictSNIVHostCheck on
+</code></p></div>
+
+</div>
+<div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif" /></a></div>
 <div class="directive-section"><h2><a name="SSLUserName" id="SSLUserName">SSLUserName</a> <a name="sslusername" id="sslusername">Directive</a></h2>
 <table class="directive">
 <tr><th><a href="directive-dict.html#Description">Description:</a></th><td>Variable name to determine user name</td></tr>
@@ -1717,6 +1749,6 @@ SSLVerifyDepth 10
 <div class="bottomlang">
 <p><span>Available Languages: </span><a href="../en/mod/mod_ssl.html" title="English">&nbsp;en&nbsp;</a></p>
 </div><div id="footer">
-<p class="apache">Copyright 2008 The Apache Software Foundation.<br />Licensed under the <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License, Version 2.0</a>.</p>
+<p class="apache">Copyright 2009 The Apache Software Foundation.<br />Licensed under the <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License, Version 2.0</a>.</p>
 <p class="menu"><a href="../mod/">Modules</a> | <a href="../mod/directives.html">Directives</a> | <a href="../faq/">FAQ</a> | <a href="../glossary.html">Glossary</a> | <a href="../sitemap.html">Sitemap</a></p></div>
-</body></html>
\ No newline at end of file
+</body></html>
