Plüm, Rüdiger, VF-Group wrote: > I reviewed your patch and found some issues with it.
Thanks for your review and testing, Rüdiger. I assume you used and adapted version of the sni_sslverifyclient-v5.diff patch, is that correct? > (All cases below use Name based virtual hosting and a non SNI client): > > 1. Renegotiation due to more restrictive cipher requirements: > > Lets say the first virtual host allows cipher A and B. > The handshake with the client decided to use A. > The virtual host the client switches to later also allows A and B. > But /restricted in this host only allows B. > In this case a request to /restricted does not cause a renegotiation > but it should. Right. It also applies to SNI clients, actually, and the problem is that the logic of this code (added in sni_sslverifyclient-v4.diff) is flawed: if ((dc->szCipherSuite && !modssl_set_cipher_list(ssl, dc->szCipherSuite)) || (sc->server->auth.cipher_suite && !modssl_set_cipher_list(ssl, sc->server->auth.cipher_suite))) { - it will override the per-dir setting for the cipher suite with that from the vhost level, if the latter is also set. Changing these lines to if ((dc->szCipherSuite || sc->server->auth.cipher_suite) && !modssl_set_cipher_list(ssl, dc->szCipherSuite ? dc->szCipherSuite : sc->server->auth.cipher_suite)) { resolves this issue for me. > 2. The verification depth check causes unneeded renegotiations which > break the ssl v2 tests in the perl framework (No dicussion here please > whether we should still support SSL v2 :-)) This is an issue I already addressed in the patch for 2.2.x (http://sni.velox.ch/httpd-2.2.x-sni.20080928.patch), but I guess you were testing a trunk version without these changes, is that correct? > There might be further issues but I currently have no time to check. > > I think we both agree that without this patch from you name based virtual > hosting with SSL is definitely unsafe. > I haven't analyzed any further if the above issues are fixable or not > and I admit that I currently have no resources to do so. I'm attaching a new patch (against r763127, i.e. current trunk), which addresses both issues. Would very much appreciate if you could have a look at it / give it a try, as it would definitely improve the situation regarding SNI support in mod_ssl. Kaspar
Index: httpd-trunk/modules/ssl/ssl_engine_kernel.c =================================================================== --- httpd-trunk/modules/ssl/ssl_engine_kernel.c (revision 763127) +++ httpd-trunk/modules/ssl/ssl_engine_kernel.c (working copy) @@ -186,16 +186,6 @@ int ssl_hook_ReadReq(request_rec *r) return HTTP_BAD_REQUEST; } } - else if (r->connection->vhost_lookup_data) { - /* - * We are using a name based configuration here, but no hostname was - * provided via SNI. Don't allow that. - */ - 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); @@ -362,7 +352,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 != r->connection->base_server)) { /* remember old state */ if (dc->nOptions & SSL_OPT_OPTRENEGOTIATE) { @@ -377,7 +367,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_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Unable to reconfigure (per-directory) " "permitted SSL ciphers"); @@ -443,8 +436,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_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Reconfigured cipher suite will force renegotiation"); } @@ -462,26 +460,17 @@ int ssl_hook_Access(request_rec *r) * currently active/remembered verify depth (because this means more * restriction on the certificate chain). */ - if ((sc->server->auth.verify_depth != UNSET) && - (dc->nVerifyDepth == UNSET)) { - /* apply per-vhost setting, if per-directory config is not set */ - dc->nVerifyDepth = sc->server->auth.verify_depth; + n = sslconn->verify_depth; + sslconn->verify_depth = (dc->nVerifyDepth != UNSET) ? + dc->nVerifyDepth : sc->server->auth.verify_depth; + if ((sslconn->verify_depth < n) || + ((n == 0) && (sc->server->auth.verify_depth == 0))) { + renegotiate = TRUE; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + "Reduced client verification depth will force " + "renegotiation"); } - 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_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - "Reduced client verification depth will force " - "renegotiation"); - } - } - /* * override of SSLVerifyClient * @@ -496,23 +485,22 @@ int ssl_hook_Access(request_rec *r) * verification but at least skip the I/O-intensive renegotation * handshake. */ - if ((sc->server->auth.verify_mode != SSL_CVERIFY_UNSET) && - (dc->nVerifyClient == SSL_CVERIFY_UNSET)) { - /* apply per-vhost setting, if per-directory config is not set */ - dc->nVerifyClient = sc->server->auth.verify_mode; - } - 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; } @@ -550,6 +538,39 @@ int ssl_hook_Access(request_rec *r) } } +#ifndef OPENSSL_NO_TLSEXT + /* 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. 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 (SSL_set_cert_store() would be required + * for this). + */ +#define MODSSL_CFG_CA_NE(f, sc1, sc2) \ + (sc1->server->auth.f && \ + (!sc2->server->auth.f || \ + sc2->server->auth.f && strNE(sc1->server->auth.f, sc2->server->auth.f))) + + if ((r->server != r->connection->base_server) && + (verify & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) && + renegotiate && + !(SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) { + SSLSrvConfigRec *bssc = mySrvConfig(r->connection->base_server); + + if (MODSSL_CFG_CA_NE(ca_cert_file, sc, bssc) || + MODSSL_CFG_CA_NE(ca_cert_path, sc, bssc)) { + 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 " + "supported for clients with TLS server name indication " + "(SNI) support"); + return HTTP_FORBIDDEN; + } + } +#endif /* OPENSSL_NO_TLSEXT */ + /* If a renegotiation is now required for this location, and the * request includes a message body (and the client has not * requested a "100 Continue" response), then the client will be @@ -732,8 +753,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_rerror(APLOG_MARK, APLOG_ERR, 0, r, @@ -1195,8 +1218,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 = mySrvFromConn(conn); 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; @@ -1326,7 +1349,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 = mySrvFromConn(c); + 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);