source: server/common/patches/httpd-2.2.x-sni.patch @ 848

Last change on this file since 848 was 836, checked in by andersk, 16 years ago
Update the SNI patch as per <http://www.mail-archive.com/dev@httpd.apache.org/msg41417.html>. This is claimed to fix unnecessary SSL renegotiations, so maybe it has something to do with the repeated Firefox certificate popups.
File size: 19.5 KB
RevLine 
[816]1# httpd-2.2.x-sni.patch - server name indication support for Apache 2.2
2# (see RFC 4366, "Transport Layer Security (TLS) Extensions")
[683]3
[816]4# based on a patch from the EdelKey project
5# (http://www.edelweb.fr/EdelKey/files/apache-2.2.0+0.9.9+servername.patch)
[683]6
[816]7# Needs openssl-SNAP-20060330 / OpenSSL 0.9.8f or later
8# to work properly (ftp://ftp.openssl.org/snapshot/). The 0.9.8 versions
9# must be configured explicitly for TLS extension support at compile time
10# ("./config enable-tlsext").
[683]11
12Index: httpd-2.2.x/modules/ssl/ssl_private.h
13===================================================================
[816]14--- httpd-2.2.x/modules/ssl/ssl_private.h       (revision 663014)
[683]15+++ httpd-2.2.x/modules/ssl/ssl_private.h       (working copy)
16@@ -35,6 +35,7 @@
17 #include "http_connection.h"
18 #include "http_request.h"
19 #include "http_protocol.h"
20+#include "http_vhost.h"
21 #include "util_script.h"
22 #include "util_filter.h"
23 #include "util_ebcdic.h"
[816]24@@ -555,6 +556,9 @@ int          ssl_callback_NewSessionCach
[683]25 SSL_SESSION *ssl_callback_GetSessionCacheEntry(SSL *, unsigned char *, int, int *);
26 void         ssl_callback_DelSessionCacheEntry(SSL_CTX *, SSL_SESSION *);
27 void         ssl_callback_LogTracingState(MODSSL_INFO_CB_ARG_TYPE, int, int);
28+#ifndef OPENSSL_NO_TLSEXT
29+int          ssl_callback_ServerNameIndication(SSL *, int *, modssl_ctx_t *);
30+#endif
31 
32 /**  Session Cache Support  */
33 void         ssl_scache_init(server_rec *, apr_pool_t *);
34Index: httpd-2.2.x/modules/ssl/ssl_engine_init.c
35===================================================================
[816]36--- httpd-2.2.x/modules/ssl/ssl_engine_init.c   (revision 663014)
[683]37+++ httpd-2.2.x/modules/ssl/ssl_engine_init.c   (working copy)
[816]38@@ -355,6 +355,33 @@ static void ssl_init_server_check(server
[683]39     }
40 }
41 
42+#ifndef OPENSSL_NO_TLSEXT
43+static void ssl_init_ctx_tls_extensions(server_rec *s,
44+                                        apr_pool_t *p,
45+                                        apr_pool_t *ptemp,
46+                                        modssl_ctx_t *mctx)
47+{
48+    /*
49+     * Configure TLS extensions support
50+     */
51+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
52+                 "Configuring TLS extension handling");
53+
54+    /*
55+     * Server name indication (SNI)
56+     */
57+    if (!SSL_CTX_set_tlsext_servername_callback(mctx->ssl_ctx,
58+                          ssl_callback_ServerNameIndication) ||
59+        !SSL_CTX_set_tlsext_servername_arg(mctx->ssl_ctx, mctx)) {
60+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
61+                     "Unable to initialize TLS servername extension "
62+                     "callback (incompatible OpenSSL version?)");
63+        ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s);
64+        ssl_die();
65+    }
66+}
67+#endif
68+
69 static void ssl_init_ctx_protocol(server_rec *s,
70                                   apr_pool_t *p,
71                                   apr_pool_t *ptemp,
[816]72@@ -687,6 +714,9 @@ static void ssl_init_ctx(server_rec *s,
[683]73     if (mctx->pks) {
74         /* XXX: proxy support? */
75         ssl_init_ctx_cert_chain(s, p, ptemp, mctx);
76+#ifndef OPENSSL_NO_TLSEXT
77+        ssl_init_ctx_tls_extensions(s, p, ptemp, mctx);
78+#endif
79     }
80 }
81 
[816]82@@ -1036,9 +1066,19 @@ void ssl_init_CheckServers(server_rec *b
83         klen = strlen(key);
84 
[683]85         if ((ps = (server_rec *)apr_hash_get(table, key, klen))) {
[816]86-            ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
87+            ap_log_error(APLOG_MARK,
88+#ifdef OPENSSL_NO_TLSEXT
89+                         APLOG_WARNING,
90+#else
91+                         APLOG_DEBUG,
92+#endif
93+                         0,
[683]94                          base_server,
95+#ifdef OPENSSL_NO_TLSEXT
96                          "Init: SSL server IP/port conflict: "
97+#else
98+                         "Init: SSL server IP/port overlap: "
99+#endif
100                          "%s (%s:%d) vs. %s (%s:%d)",
101                          ssl_util_vhostid(p, s),
102                          (s->defn_name ? s->defn_name : "unknown"),
[816]103@@ -1055,8 +1095,14 @@ void ssl_init_CheckServers(server_rec *b
[683]104 
105     if (conflict) {
106         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server,
107+#ifdef OPENSSL_NO_TLSEXT
108                      "Init: You should not use name-based "
109                      "virtual hosts in conjunction with SSL!!");
110+#else
111+                     "Init: Name-based SSL virtual hosts only "
112+                     "work for clients with TLS server name indication "
113+                     "support (RFC 4366)");
114+#endif
115     }
116 }
117 
118Index: httpd-2.2.x/modules/ssl/ssl_engine_vars.c
119===================================================================
[816]120--- httpd-2.2.x/modules/ssl/ssl_engine_vars.c   (revision 663014)
[683]121+++ httpd-2.2.x/modules/ssl/ssl_engine_vars.c   (working copy)
[816]122@@ -320,6 +320,12 @@ static char *ssl_var_lookup_ssl(apr_pool
[683]123     else if (ssl != NULL && strcEQ(var, "COMPRESS_METHOD")) {
124         result = ssl_var_lookup_ssl_compress_meth(ssl);
125     }
126+#ifndef OPENSSL_NO_TLSEXT
127+    else if (ssl != NULL && strcEQ(var, "TLS_SNI")) {
128+        result = apr_pstrdup(p, SSL_get_servername(ssl,
129+                                                   TLSEXT_NAMETYPE_host_name));
130+    }
131+#endif
132     return result;
133 }
134 
135Index: httpd-2.2.x/modules/ssl/ssl_engine_kernel.c
136===================================================================
[816]137--- httpd-2.2.x/modules/ssl/ssl_engine_kernel.c (revision 663014)
[683]138+++ httpd-2.2.x/modules/ssl/ssl_engine_kernel.c (working copy)
139@@ -31,6 +31,9 @@
140 #include "ssl_private.h"
141 
142 static void ssl_configure_env(request_rec *r, SSLConnRec *sslconn);
143+#ifndef OPENSSL_NO_TLSEXT
144+static int ssl_find_vhost(void *servername, conn_rec *c, server_rec *s);
145+#endif
146 
147 /*
148  *  Post Read Request Handler
[816]149@@ -39,6 +42,9 @@ int ssl_hook_ReadReq(request_rec *r)
[683]150 {
151     SSLConnRec *sslconn = myConnConfig(r->connection);
152     SSL *ssl;
153+#ifndef OPENSSL_NO_TLSEXT
154+    const char *servername;
155+#endif
156 
157     if (!sslconn) {
158         return DECLINED;
[816]159@@ -87,6 +93,14 @@ int ssl_hook_ReadReq(request_rec *r)
[683]160     if (!ssl) {
161         return DECLINED;
162     }
163+#ifndef OPENSSL_NO_TLSEXT
164+    if (!r->hostname &&
165+        (servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) {
166+        /* Use the SNI extension as the hostname if no Host: header was sent */
167+        r->hostname = apr_pstrdup(r->pool, servername);
168+        ap_update_vhost_from_headers(r);
169+    }
170+#endif
171     SSL_set_app_data2(ssl, r);
172 
173     /*
[816]174@@ -252,7 +266,7 @@ int ssl_hook_Access(request_rec *r)
175      *   has to enable this via ``SSLOptions +OptRenegotiate''. So we do no
176      *   implicit optimizations.
177      */
178-    if (dc->szCipherSuite) {
179+    if (dc->szCipherSuite || (r->server != r->connection->base_server)) {
180         /* remember old state */
181 
182         if (dc->nOptions & SSL_OPT_OPTRENEGOTIATE) {
183@@ -267,7 +281,10 @@ int ssl_hook_Access(request_rec *r)
184         }
185 
186         /* configure new state */
187-        if (!modssl_set_cipher_list(ssl, dc->szCipherSuite)) {
188+        if ((dc->szCipherSuite &&
189+             !modssl_set_cipher_list(ssl, dc->szCipherSuite)) ||
190+            (sc->server->auth.cipher_suite &&
191+             !modssl_set_cipher_list(ssl, sc->server->auth.cipher_suite))) {
192             ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
193                          r->server,
194                          "Unable to reconfigure (per-directory) "
195@@ -334,8 +351,13 @@ int ssl_hook_Access(request_rec *r)
196             sk_SSL_CIPHER_free(cipher_list_old);
197         }
198 
199-        /* tracing */
200         if (renegotiate) {
201+#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
202+            if (sc->cipher_server_pref == TRUE) {
203+                SSL_set_options(ssl, SSL_OP_CIPHER_SERVER_PREFERENCE);
204+            }
205+#endif
206+            /* tracing */
207             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
208                          "Reconfigured cipher suite will force renegotiation");
209         }
210@@ -353,14 +375,16 @@ int ssl_hook_Access(request_rec *r)
211      * currently active/remembered verify depth (because this means more
212      * restriction on the certificate chain).
213      */
214-    if (dc->nVerifyDepth != UNSET) {
215+    if ((dc->nVerifyDepth != UNSET) ||
[836]216+        (sc->server->auth.verify_depth != 1)) {
[816]217         /* XXX: doesnt look like sslconn->verify_depth is actually used */
218         if (!(n = sslconn->verify_depth)) {
219             sslconn->verify_depth = n = sc->server->auth.verify_depth;
220         }
221 
222         /* determine whether a renegotiation has to be forced */
223-        if (dc->nVerifyDepth < n) {
224+        if ((dc->nVerifyDepth < n) ||
225+            (sc->server->auth.verify_depth < n)) {
226             renegotiate = TRUE;
227             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
228                          "Reduced client verification depth will force "
229@@ -382,18 +406,22 @@ int ssl_hook_Access(request_rec *r)
230      * verification but at least skip the I/O-intensive renegotation
231      * handshake.
232      */
233-    if (dc->nVerifyClient != SSL_CVERIFY_UNSET) {
234+    if ((dc->nVerifyClient != SSL_CVERIFY_UNSET) ||
235+        (sc->server->auth.verify_mode != SSL_CVERIFY_UNSET)) {
236         /* remember old state */
237         verify_old = SSL_get_verify_mode(ssl);
238         /* configure new state */
239         verify = SSL_VERIFY_NONE;
240 
241-        if (dc->nVerifyClient == SSL_CVERIFY_REQUIRE) {
242+        if ((dc->nVerifyClient == SSL_CVERIFY_REQUIRE) ||
243+            (sc->server->auth.verify_mode == SSL_CVERIFY_REQUIRE)) {
244             verify |= SSL_VERIFY_PEER_STRICT;
245         }
246 
247         if ((dc->nVerifyClient == SSL_CVERIFY_OPTIONAL) ||
248-            (dc->nVerifyClient == SSL_CVERIFY_OPTIONAL_NO_CA))
249+            (dc->nVerifyClient == SSL_CVERIFY_OPTIONAL_NO_CA) ||
250+            (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL) ||
251+            (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA))
252         {
253             verify |= SSL_VERIFY_PEER;
254         }
255@@ -491,6 +519,40 @@ int ssl_hook_Access(request_rec *r)
256                      "Changed client verification locations will force "
257                      "renegotiation");
258     }
259+#else
260+#ifndef OPENSSL_NO_TLSEXT
261+#define MODSSL_CFG_CA_NE(f, sc1, sc2) \
262+    (sc1->server->auth.f && \
263+     (!sc2->server->auth.f || \
264+      sc2->server->auth.f && strNE(sc1->server->auth.f, sc2->server->auth.f)))
265+
266+    /* If we're handling a request for a vhost other than the default one,
267+     * then we need to make sure that client authentication is properly
268+     * enforced. For clients supplying an SNI extension, the peer certificate
269+     * verification has happened in the handshake already (and r->server
270+     * has been set to r->connection->base_server). For non-SNI requests,
271+     * an additional check is needed here. If client authentication is
272+     * configured as mandatory, then we can only proceed if the CA list
273+     * doesn't have to be changed (SSL_set_cert_store() would be required
274+     * for this).
275+     */
276+    if ((r->server != r->connection->base_server) &&
277+        (verify & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) &&
278+        renegotiate &&
279+        !(SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) {
280+        SSLSrvConfigRec *bssc = mySrvConfig(r->connection->base_server);
281+
282+        if (MODSSL_CFG_CA_NE(ca_cert_file, sc, bssc) ||
283+            MODSSL_CFG_CA_NE(ca_cert_path, sc, bssc)) {
284+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
285+                 "Non-default virtual host with SSLVerify set to 'require' "
286+                 "and VirtualHost-specific CA certificate list is only "
287+                 "supported for clients with TLS server name indication "
288+                 "(SNI) support");
289+            return HTTP_FORBIDDEN;
290+        }
291+    }
292+#endif /* OPENSSL_NO_TLSEXT */
293 #endif /* HAVE_SSL_SET_CERT_STORE */
294 
295     /* If a renegotiation is now required for this location, and the
296@@ -666,8 +728,10 @@ int ssl_hook_Access(request_rec *r)
297         /*
298          * Finally check for acceptable renegotiation results
299          */
300-        if (dc->nVerifyClient != SSL_CVERIFY_NONE) {
301-            BOOL do_verify = (dc->nVerifyClient == SSL_CVERIFY_REQUIRE);
302+        if ((dc->nVerifyClient != SSL_CVERIFY_NONE) ||
303+            (sc->server->auth.verify_mode != SSL_CVERIFY_NONE)) {
304+            BOOL do_verify = ((dc->nVerifyClient == SSL_CVERIFY_REQUIRE) ||
305+                              (sc->server->auth.verify_mode == SSL_CVERIFY_REQUIRE));
306 
307             if (do_verify && (SSL_get_verify_result(ssl) != X509_V_OK)) {
308                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
309@@ -997,6 +1061,9 @@ int ssl_hook_Fixup(request_rec *r)
[683]310     SSLDirConfigRec *dc = myDirConfig(r);
311     apr_table_t *env = r->subprocess_env;
312     char *var, *val = "";
313+#ifndef OPENSSL_NO_TLSEXT
314+    const char *servername;
315+#endif
316     STACK_OF(X509) *peer_certs;
317     SSL *ssl;
318     int i;
[816]319@@ -1018,6 +1085,13 @@ int ssl_hook_Fixup(request_rec *r)
[683]320     /* the always present HTTPS (=HTTP over SSL) flag! */
321     apr_table_setn(env, "HTTPS", "on");
322 
323+#ifndef OPENSSL_NO_TLSEXT
324+    /* add content of SNI TLS extension (if supplied with ClientHello) */
325+    if ((servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) {
326+        apr_table_set(env, "SSL_TLS_SNI", servername);
327+    }
328+#endif
329+
330     /* standard SSL environment variables */
331     if (dc->nOptions & SSL_OPT_STDENVVARS) {
332         for (i = 0; ssl_hook_Fixup_vars[i]; i++) {
[816]333@@ -1166,8 +1240,8 @@ int ssl_callback_SSLVerify(int ok, X509_
334     SSL *ssl = X509_STORE_CTX_get_ex_data(ctx,
335                                           SSL_get_ex_data_X509_STORE_CTX_idx());
336     conn_rec *conn      = (conn_rec *)SSL_get_app_data(ssl);
337-    server_rec *s       = conn->base_server;
338     request_rec *r      = (request_rec *)SSL_get_app_data2(ssl);
339+    server_rec *s       = r ? r->server : conn->base_server;
340 
341     SSLSrvConfigRec *sc = mySrvConfig(s);
342     SSLDirConfigRec *dc = r ? myDirConfig(r) : NULL;
343@@ -1290,7 +1364,10 @@ int ssl_callback_SSLVerify(int ok, X509_
344 
345 int ssl_callback_SSLVerify_CRL(int ok, X509_STORE_CTX *ctx, conn_rec *c)
346 {
347-    server_rec *s       = c->base_server;
348+    SSL *ssl = X509_STORE_CTX_get_ex_data(ctx,
349+                                          SSL_get_ex_data_X509_STORE_CTX_idx());
350+    request_rec *r      = (request_rec *)SSL_get_app_data2(ssl);
351+    server_rec *s       = r ? r->server : c->base_server;
352     SSLSrvConfigRec *sc = mySrvConfig(s);
353     SSLConnRec *sslconn = myConnConfig(c);
354     modssl_ctx_t *mctx  = myCtxConfig(sslconn, sc);
355@@ -1810,3 +1887,141 @@ void ssl_callback_LogTracingState(MODSSL
[683]356     }
357 }
358 
359+#ifndef OPENSSL_NO_TLSEXT
360+/*
361+ * This callback function is executed when OpenSSL encounters an extended
362+ * client hello with a server name indication extension ("SNI", cf. RFC 4366).
363+ */
364+int ssl_callback_ServerNameIndication(SSL *ssl, int *al, modssl_ctx_t *mctx)
365+{
366+    const char *servername =
367+                SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
368+
369+    if (servername) {
370+        conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
371+        if (c) {
372+            if (ap_vhost_iterate_given_conn(c, ssl_find_vhost,
373+                                            (void *)servername)) {
374+                ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
375+                              "SSL virtual host for servername %s found",
376+                              servername);
377+                return SSL_TLSEXT_ERR_OK;
378+            }
379+            else {
380+                ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
381+                              "No matching SSL virtual host for servername "
382+                              "%s found (using default/first virtual host)",
383+                              servername);
384+                return SSL_TLSEXT_ERR_ALERT_WARNING;
385+            }
386+        }
387+    }
388+
389+    return SSL_TLSEXT_ERR_NOACK;
390+}
391+
392+/*
393+ * Find a (name-based) SSL virtual host where either the ServerName
394+ * or one of the ServerAliases matches the supplied name (to be used
395+ * with ap_vhost_iterate_given_conn())
396+ */
397+static int ssl_find_vhost(void *servername, conn_rec *c, server_rec *s)
398+{
399+    SSLSrvConfigRec *sc;
400+    SSL *ssl;
401+    BOOL found = FALSE;
402+    apr_array_header_t *names;
403+    int i;
404+
405+    /* check ServerName */
406+    if (!strcasecmp(servername, s->server_hostname)) {
407+        found = TRUE;
408+    }
409+
410+    /*
411+     * if not matched yet, check ServerAlias entries
412+     * (adapted from vhost.c:matches_aliases())
413+     */
414+    if (!found) {
415+        names = s->names;
416+        if (names) {
417+            char **name = (char **)names->elts;
418+            for (i = 0; i < names->nelts; ++i) {
419+                if (!name[i])
420+                    continue;
421+                if (!strcasecmp(servername, name[i])) {
422+                    found = TRUE;
423+                    break;
424+                }
425+            }
426+        }
427+    }
428+
429+    /* if still no match, check ServerAlias entries with wildcards */
430+    if (!found) {
431+        names = s->wild_names;
432+        if (names) {
433+            char **name = (char **)names->elts;
434+            for (i = 0; i < names->nelts; ++i) {
435+                if (!name[i])
436+                    continue;
437+                if (!ap_strcasecmp_match(servername, name[i])) {
438+                    found = TRUE;
439+                    break;
440+                }
441+            }
442+        }
443+    }
444+
445+    /* set SSL_CTX (if matched) */
446+    if (found && (ssl = ((SSLConnRec *)myConnConfig(c))->ssl) &&
447+        (sc = mySrvConfig(s))) {
448+        SSL_set_SSL_CTX(ssl, sc->server->ssl_ctx);
449+        /*
450+         * SSL_set_SSL_CTX() only deals with the server cert,
451+         * so we need to duplicate a few additional settings
452+         * from the ctx by hand
453+         */
454+        SSL_set_options(ssl, SSL_CTX_get_options(ssl->ctx));
455+        if ((SSL_get_verify_mode(ssl) == SSL_VERIFY_NONE) ||
456+            (SSL_num_renegotiations(ssl) == 0)) {
457+           /*
458+            * Only initialize the verification settings from the ctx
459+            * if they are not yet set, or if we're called when a new
460+            * SSL connection is set up (num_renegotiations == 0).
461+            * Otherwise, we would possibly reset a per-directory
462+            * configuration which was put into effect by ssl_hook_Access.
463+            */
464+            SSL_set_verify(ssl, SSL_CTX_get_verify_mode(ssl->ctx),
465+                           SSL_CTX_get_verify_callback(ssl->ctx));
466+        }
467+
468+        /*
[816]469+         * We also need to make sure that the correct mctx
470+         * (accessed through the c->base_server->module_config vector)
471+         * is assigned to the connection - the CRL callback e.g.
[683]472+         * makes use of it for retrieving its store (mctx->crl).
473+         * Since logging in callbacks uses c->base_server in many
474+         * cases, it also ensures that these messages are routed
[816]475+         * to the proper log.
[683]476+         */
477+        c->base_server = s;
[816]478+
479+        /*
480+         * There is one special filter callback, which is set
481+         * very early depending on the base_server's log level.
482+         * If this is not the first vhost we're now selecting
483+         * (and the first vhost doesn't use APLOG_DEBUG), then
484+         * we need to set that callback here.
485+         */
[683]486+        if (c->base_server->loglevel >= APLOG_DEBUG) {
487+            BIO_set_callback(SSL_get_rbio(ssl), ssl_io_data_cb);
488+            BIO_set_callback_arg(SSL_get_rbio(ssl), (void *)ssl);
489+        }
490+
491+        return 1;
492+    }
493+
494+    return 0;
495+}
496+#endif
497Index: httpd-2.2.x/modules/ssl/ssl_toolkit_compat.h
498===================================================================
[816]499--- httpd-2.2.x/modules/ssl/ssl_toolkit_compat.h        (revision 663014)
[683]500+++ httpd-2.2.x/modules/ssl/ssl_toolkit_compat.h        (working copy)
[816]501@@ -264,6 +264,12 @@ typedef void (*modssl_popfree_fn)(char *
[683]502 #define SSL_SESS_CACHE_NO_INTERNAL  SSL_SESS_CACHE_NO_INTERNAL_LOOKUP
503 #endif
504 
505+#ifndef OPENSSL_NO_TLSEXT
506+#ifndef SSL_CTRL_SET_TLSEXT_HOSTNAME
507+#define OPENSSL_NO_TLSEXT
508+#endif
509+#endif
510+
511 #endif /* SSL_TOOLKIT_COMPAT_H */
512 
513 /** @} */
Note: See TracBrowser for help on using the repository browser.