Add TLS servername indication (SNI) support (fixes #386, thx Peter Colberg <peter@colberg.org>)
* This patch may "break" some configs, if they do stupid things. Like setting ssl.pemfile to a not existing file in a "non-socket/non-ssl" block. Fix them! :) From: Peter Colberg <peter@colberg.org> git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@2648 152afb58-edef-0310-8abb-c4023f1b3aa9
This commit is contained in:
parent
83145e8ba6
commit
8b6dae4139
1
NEWS
1
NEWS
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"))) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
202
src/network.c
202
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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue