From 5fbcf1a914507b4c73d83387fdc5e8f612d83558 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Thu, 22 Dec 2016 21:58:38 +0100 Subject: [PATCH 16/19] BUG/MEDIUM: ssl: for a handshake when server-side SNI changes Calling SSL_set_tlsext_host_name() on the current SSL ctx has no effect if the session is being resumed because the hostname is already stored in the session and is not advertised again in subsequent connections. It's visible when enabling SNI and health checks at the same time because checks do not send an SNI and regular traffic reuses the same connection, resulting in no SNI being sent. The only short-term solution is to reset the reused session when the SNI changes compared to the previous one. It can make the server-side performance suffer when SNIs are interleaved but it will work. A better long-term solution would be to keep a small cache of a few contexts for a few SNIs. Now with SSL_set_session(ctx, NULL) it works. This needs to be double- checked though. The man says that SSL_set_session() frees any previously existing context. Some people report a bit of breakage when calling SSL_set_session(NULL) on openssl 1.1.0a (freed session not reusable at all though it's not an issue for now). This needs to be backported to 1.7 and 1.6. (cherry picked from commit 119a4084bf88418bce74d8af686576e371700c20) --- src/ssl_sock.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 55eaa28..77fb4b3 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -4143,12 +4143,27 @@ char *ssl_sock_get_version(struct connection *conn) return (char *)SSL_get_version(conn->xprt_ctx); } +/* Sets advertised SNI for outgoing connections. Please set to NULL + * to disable SNI. + */ void ssl_sock_set_servername(struct connection *conn, const char *hostname) { #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + char *prev_name; + if (!ssl_sock_is_ssl(conn)) return; + /* if the SNI changes, we must destroy the reusable context so that a + * new connection will present a new SNI. As an optimization we could + * later imagine having a small cache of ssl_ctx to hold a few SNI per + * server. + */ + prev_name = (char *)SSL_get_servername(conn->xprt_ctx, TLSEXT_NAMETYPE_host_name); + if ((!prev_name && hostname) || + (prev_name && (!hostname || strcmp(hostname, prev_name) != 0))) + SSL_set_session(conn->xprt_ctx, NULL); + SSL_set_tlsext_host_name(conn->xprt_ctx, hostname); #endif } -- 2.10.2