diff options
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | src/base.h | 6 | ||||
-rw-r--r-- | src/configfile-glue.c | 4 | ||||
-rw-r--r-- | src/configfile.c | 2 | ||||
-rw-r--r-- | src/connections.c | 9 | ||||
-rw-r--r-- | src/network.c | 202 | ||||
-rw-r--r-- | tests/bug-06.conf | 2 | ||||
-rw-r--r-- | tests/bug-12.conf | 2 | ||||
-rw-r--r-- | tests/fastcgi-10.conf | 2 | ||||
-rw-r--r-- | tests/fastcgi-13.conf | 2 | ||||
-rw-r--r-- | tests/fastcgi-auth.conf | 2 | ||||
-rw-r--r-- | tests/fastcgi-responder.conf | 2 | ||||
-rw-r--r-- | tests/lighttpd.conf | 2 | ||||
-rw-r--r-- | tests/proxy.conf | 2 |
14 files changed, 163 insertions, 77 deletions
@@ -54,6 +54,7 @@ NEWS * Add some iterators for mod_magnet (fixes #1307) * Fix close_timeout_ts trigger (should finally fix lingering close) * mod_rewrite: add url.rewrite-[repeat-]if-not-file to rewrite if file doesn't exist or is not a regular file (fixes #985, thx lucas aerbeydt) + * Add TLS servername indication (SNI) support (fixes #386, thx Peter Colberg <peter@colberg.org>) - 1.4.23 - 2009-06-19 * Added some extra warning options in cmake and fix the resulting warnings (unused/static functions) @@ -33,6 +33,9 @@ #if defined HAVE_LIBSSL && defined HAVE_OPENSSL_SSL_H # define USE_OPENSSL # include <openssl/ssl.h> +# if ! defined OPENSSL_NO_TLSEXT && ! defined SSL_CTRL_SET_TLSEXT_HOSTNAME +# define OPENSSL_NO_TLSEXT +# endif #endif #ifdef HAVE_FAM_H @@ -424,6 +427,9 @@ typedef struct { #ifdef USE_OPENSSL SSL *ssl; buffer *ssl_error_want_reuse_buffer; +# ifndef OPENSSL_NO_TLSEXT + buffer *tlsext_server_name; +# endif #endif /* etag handling */ etag_flags_t etag_flags; diff --git a/src/configfile-glue.c b/src/configfile-glue.c index 2904f711..782eb815 100644 --- a/src/configfile-glue.c +++ b/src/configfile-glue.c @@ -318,6 +318,10 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat default: break; } +#if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT + } else if (!buffer_is_empty(con->tlsext_server_name)) { + l = con->tlsext_server_name; +#endif } else { l = srv->empty_string; } diff --git a/src/configfile.c b/src/configfile.c index 360b642d..14c37f39 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -297,6 +297,7 @@ int config_setup_connection(server *srv, connection *con) { PATCH(is_ssl); PATCH(ssl_pemfile); + PATCH(ssl_ctx); PATCH(ssl_ca_file); PATCH(ssl_cipher_list); PATCH(ssl_use_sslv2); @@ -352,6 +353,7 @@ int config_patch_connection(server *srv, connection *con, comp_key_t comp) { PATCH(etag_use_size); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.pemfile"))) { PATCH(ssl_pemfile); + PATCH(ssl_ctx); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.ca-file"))) { PATCH(ssl_ca_file); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.use-sslv2"))) { diff --git a/src/connections.c b/src/connections.c index 6b765cf1..daec12a6 100644 --- a/src/connections.c +++ b/src/connections.c @@ -667,6 +667,9 @@ connection *connection_init(server *srv) { CLEAN(server_name); CLEAN(error_handler); CLEAN(dst_addr_buf); +#if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT + CLEAN(tlsext_server_name); +#endif #undef CLEAN con->write_queue = chunkqueue_init(); @@ -731,6 +734,9 @@ void connections_free(server *srv) { CLEAN(server_name); CLEAN(error_handler); CLEAN(dst_addr_buf); +#if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT + CLEAN(tlsext_server_name); +#endif #undef CLEAN free(con->plugin_ctx); free(con->cond_cache); @@ -1343,6 +1349,9 @@ connection *connection_accept(server *srv, server_socket *srv_socket) { return NULL; } +#ifndef OPENSSL_NO_TLSEXT + SSL_set_app_data(con->ssl, con); +#endif SSL_set_accept_state(con->ssl); con->conf.is_ssl=1; diff --git a/src/network.c b/src/network.c index 5b49a749..a7a71584 100644 --- a/src/network.c +++ b/src/network.c @@ -62,6 +62,45 @@ static handler_t network_server_handle_fdevent(void *s, void *context, int reven return HANDLER_GO_ON; } +#if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT +int network_ssl_servername_callback(SSL *ssl, int *al, server *srv) { + const char *servername; + connection *con = (connection *) SSL_get_app_data(ssl); + + buffer_copy_string(con->uri.scheme, "https"); + + if (NULL == (servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + "failed to get TLS server name"); + return SSL_TLSEXT_ERR_NOACK; + } + buffer_copy_string(con->tlsext_server_name, servername); + buffer_to_lower(con->tlsext_server_name); + + config_cond_cache_reset(srv, con); + config_setup_connection(srv, con); + + config_patch_connection(srv, con, COMP_SERVER_SOCKET); + config_patch_connection(srv, con, COMP_HTTP_SCHEME); + config_patch_connection(srv, con, COMP_HTTP_HOST); + + if (NULL == con->conf.ssl_ctx) { + log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", + "null SSL_CTX for TLS server name", con->tlsext_server_name); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + /* switch to new SSL_CTX in reaction to a client's server_name extension */ + if (con->conf.ssl_ctx != SSL_set_SSL_CTX(ssl, con->conf.ssl_ctx)) { + log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", + "failed to set SSL_CTX for TLS server name", con->tlsext_server_name); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + return SSL_TLSEXT_ERR_OK; +} +#endif + static int network_server_init(server *srv, buffer *host_token, specific_config *s) { int val; socklen_t addr_len; @@ -312,78 +351,10 @@ static int network_server_init(server *srv, buffer *host_token, specific_config if (s->is_ssl) { #ifdef USE_OPENSSL - if (srv->ssl_is_init == 0) { - SSL_load_error_strings(); - SSL_library_init(); - srv->ssl_is_init = 1; - - if (0 == RAND_status()) { - log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", - "not enough entropy in the pool"); - goto error_free_socket; - } - } - - if (NULL == (s->ssl_ctx = SSL_CTX_new(SSLv23_server_method()))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", - ERR_error_string(ERR_get_error(), NULL)); - goto error_free_socket; - } - - if (!s->ssl_use_sslv2) { - /* disable SSLv2 */ - if (SSL_OP_NO_SSLv2 != SSL_CTX_set_options(s->ssl_ctx, SSL_OP_NO_SSLv2)) { - log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", - ERR_error_string(ERR_get_error(), NULL)); - goto error_free_socket; - } - } - - if (!buffer_is_empty(s->ssl_cipher_list)) { - /* Disable support for low encryption ciphers */ - if (SSL_CTX_set_cipher_list(s->ssl_ctx, s->ssl_cipher_list->ptr) != 1) { - log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", - ERR_error_string(ERR_get_error(), NULL)); - goto error_free_socket; - } - } - - if (buffer_is_empty(s->ssl_pemfile)) { + if (NULL == (srv_socket->ssl_ctx = s->ssl_ctx)) { log_error_write(srv, __FILE__, __LINE__, "s", "ssl.pemfile has to be set"); goto error_free_socket; } - - if (!buffer_is_empty(s->ssl_ca_file)) { - if (1 != SSL_CTX_load_verify_locations(s->ssl_ctx, s->ssl_ca_file->ptr, NULL)) { - log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", - ERR_error_string(ERR_get_error(), NULL), s->ssl_ca_file); - goto error_free_socket; - } - } - - if (SSL_CTX_use_certificate_file(s->ssl_ctx, s->ssl_pemfile->ptr, SSL_FILETYPE_PEM) < 0) { - log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", - ERR_error_string(ERR_get_error(), NULL), s->ssl_pemfile); - goto error_free_socket; - } - - if (SSL_CTX_use_PrivateKey_file (s->ssl_ctx, s->ssl_pemfile->ptr, SSL_FILETYPE_PEM) < 0) { - log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", - ERR_error_string(ERR_get_error(), NULL), s->ssl_pemfile); - goto error_free_socket; - } - - if (SSL_CTX_check_private_key(s->ssl_ctx) != 1) { - log_error_write(srv, __FILE__, __LINE__, "sssb", "SSL:", - "Private key does not match the certificate public key, reason:", - ERR_error_string(ERR_get_error(), NULL), - s->ssl_pemfile); - goto error_free_socket; - } - SSL_CTX_set_default_read_ahead(s->ssl_ctx, 1); - SSL_CTX_set_mode(s->ssl_ctx, SSL_CTX_get_mode(s->ssl_ctx) | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - - srv_socket->ssl_ctx = s->ssl_ctx; #else buffer_free(srv_socket->srv_token); @@ -510,6 +481,99 @@ int network_init(server *srv) { { NETWORK_BACKEND_UNSET, NULL } }; +#ifdef USE_OPENSSL + /* load SSL certificates */ + for (i = 0; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + specific_config *s = srv->config_storage[i]; + + if (buffer_is_empty(s->ssl_pemfile)) continue; + +#ifdef OPENSSL_NO_TLSEXT + if (COMP_HTTP_HOST == dc->comp) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + "can't use ssl.pemfile with $HTTP[\"host\"], openssl version does not support TLS extensions"); + return -1; + } +#endif + + if (srv->ssl_is_init == 0) { + SSL_load_error_strings(); + SSL_library_init(); + srv->ssl_is_init = 1; + + if (0 == RAND_status()) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + "not enough entropy in the pool"); + return -1; + } + } + + if (NULL == (s->ssl_ctx = SSL_CTX_new(SSLv23_server_method()))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + if (!s->ssl_use_sslv2) { + /* disable SSLv2 */ + if (SSL_OP_NO_SSLv2 != SSL_CTX_set_options(s->ssl_ctx, SSL_OP_NO_SSLv2)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + } + + if (!buffer_is_empty(s->ssl_cipher_list)) { + /* Disable support for low encryption ciphers */ + if (SSL_CTX_set_cipher_list(s->ssl_ctx, s->ssl_cipher_list->ptr) != 1) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + } + + if (!buffer_is_empty(s->ssl_ca_file)) { + if (1 != SSL_CTX_load_verify_locations(s->ssl_ctx, s->ssl_ca_file->ptr, NULL)) { + log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", + ERR_error_string(ERR_get_error(), NULL), s->ssl_ca_file); + return -1; + } + } + + if (SSL_CTX_use_certificate_file(s->ssl_ctx, s->ssl_pemfile->ptr, SSL_FILETYPE_PEM) < 0) { + log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", + ERR_error_string(ERR_get_error(), NULL), s->ssl_pemfile); + return -1; + } + + if (SSL_CTX_use_PrivateKey_file (s->ssl_ctx, s->ssl_pemfile->ptr, SSL_FILETYPE_PEM) < 0) { + log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", + ERR_error_string(ERR_get_error(), NULL), s->ssl_pemfile); + return -1; + } + + if (SSL_CTX_check_private_key(s->ssl_ctx) != 1) { + log_error_write(srv, __FILE__, __LINE__, "sssb", "SSL:", + "Private key does not match the certificate public key, reason:", + ERR_error_string(ERR_get_error(), NULL), + s->ssl_pemfile); + return -1; + } + SSL_CTX_set_default_read_ahead(s->ssl_ctx, 1); + SSL_CTX_set_mode(s->ssl_ctx, SSL_CTX_get_mode(s->ssl_ctx) | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + +# ifndef OPENSSL_NO_TLSEXT + if (!SSL_CTX_set_tlsext_servername_callback(s->ssl_ctx, network_ssl_servername_callback) || + !SSL_CTX_set_tlsext_servername_arg(s->ssl_ctx, srv)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + "failed to initialize TLS servername callback, openssl library does not support TLS servername extension"); + return -1; + } +# endif + } +#endif + b = buffer_init(); buffer_copy_string_buffer(b, srv->srvconf.bindhost); diff --git a/tests/bug-06.conf b/tests/bug-06.conf index 34db0e0f..8835535e 100644 --- a/tests/bug-06.conf +++ b/tests/bug-06.conf @@ -103,7 +103,7 @@ cgi.assign = ( ".pl" => "/usr/bin/perl", ssl.engine = "disable" -ssl.pemfile = "server.pem" +# ssl.pemfile = "server.pem" auth.backend = "plain" auth.backend.plain.userfile = env.SRCDIR + "/tmp/lighttpd/lighttpd.user" diff --git a/tests/bug-12.conf b/tests/bug-12.conf index b23ae53d..61cba0e6 100644 --- a/tests/bug-12.conf +++ b/tests/bug-12.conf @@ -105,7 +105,7 @@ cgi.assign = ( ".pl" => "/usr/bin/perl", ssl.engine = "disable" -ssl.pemfile = "server.pem" +# ssl.pemfile = "server.pem" auth.backend = "plain" auth.backend.plain.userfile = env.SRCDIR + "/tmp/lighttpd/lighttpd.user" diff --git a/tests/fastcgi-10.conf b/tests/fastcgi-10.conf index 087c17d4..1fa50fbe 100644 --- a/tests/fastcgi-10.conf +++ b/tests/fastcgi-10.conf @@ -82,7 +82,7 @@ cgi.assign = ( ".pl" => "/usr/bin/perl", ssl.engine = "disable" -ssl.pemfile = "server.pem" +# ssl.pemfile = "server.pem" auth.backend = "plain" auth.backend.plain.userfile = env.SRCDIR + "/tmp/lighttpd/lighttpd.user" diff --git a/tests/fastcgi-13.conf b/tests/fastcgi-13.conf index 19d0b856..499a7f55 100644 --- a/tests/fastcgi-13.conf +++ b/tests/fastcgi-13.conf @@ -99,7 +99,7 @@ cgi.assign = ( ".pl" => "/usr/bin/perl", ssl.engine = "disable" -ssl.pemfile = "server.pem" +# ssl.pemfile = "server.pem" auth.backend = "plain" auth.backend.plain.userfile = env.SRCDIR + "/tmp/lighttpd/lighttpd.user" diff --git a/tests/fastcgi-auth.conf b/tests/fastcgi-auth.conf index cac14535..47a9268d 100644 --- a/tests/fastcgi-auth.conf +++ b/tests/fastcgi-auth.conf @@ -104,7 +104,7 @@ cgi.assign = ( ".pl" => "/usr/bin/perl", ssl.engine = "disable" -ssl.pemfile = "server.pem" +# ssl.pemfile = "server.pem" auth.backend = "plain" auth.backend.plain.userfile = env.SRCDIR + "/tmp/lighttpd/lighttpd.user" diff --git a/tests/fastcgi-responder.conf b/tests/fastcgi-responder.conf index 91553423..0f6c1dc8 100644 --- a/tests/fastcgi-responder.conf +++ b/tests/fastcgi-responder.conf @@ -106,7 +106,7 @@ cgi.assign = ( ".pl" => "/usr/bin/perl", ssl.engine = "disable" -ssl.pemfile = "server.pem" +# ssl.pemfile = "server.pem" auth.backend = "plain" auth.backend.plain.userfile = env.SRCDIR + "/tmp/lighttpd/lighttpd.user" diff --git a/tests/lighttpd.conf b/tests/lighttpd.conf index 4fe516d2..2202100f 100644 --- a/tests/lighttpd.conf +++ b/tests/lighttpd.conf @@ -96,7 +96,7 @@ userdir.include-user = ( "jan" ) userdir.path = "/" ssl.engine = "disable" -ssl.pemfile = "server.pem" +# ssl.pemfile = "server.pem" $HTTP["host"] == "auth-htpasswd.example.org" { auth.backend = "htpasswd" diff --git a/tests/proxy.conf b/tests/proxy.conf index e130d4a6..2e959acc 100644 --- a/tests/proxy.conf +++ b/tests/proxy.conf @@ -85,7 +85,7 @@ userdir.include-user = ( "jan" ) userdir.path = "/" ssl.engine = "disable" -ssl.pemfile = "server.pem" +# ssl.pemfile = "server.pem" auth.backend = "plain" auth.backend.plain.userfile = env.SRCDIR + "/tmp/lighttpd/lighttpd.user" |