From: Timo Sirainen Date: Wed, 1 Nov 2017 23:05:01 +0000 (+0200) Subject: lib-ssl-iostream: Add TLS SNI callback and a way to change SSL context X-Git-Tag: 2.3.0.rc1~524 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=14a07d2bb34f1d52fce3e3218799f271f118d501;p=thirdparty%2Fdovecot%2Fcore.git lib-ssl-iostream: Add TLS SNI callback and a way to change SSL context --- diff --git a/src/lib-ssl-iostream/iostream-openssl-context.c b/src/lib-ssl-iostream/iostream-openssl-context.c index 940aebb44e..d05e0c208a 100644 --- a/src/lib-ssl-iostream/iostream-openssl-context.c +++ b/src/lib-ssl-iostream/iostream-openssl-context.c @@ -296,7 +296,7 @@ static int ssl_servername_callback(SSL *ssl, int *al ATTR_UNUSED, void *context ATTR_UNUSED) { struct ssl_iostream *ssl_io; - const char *host; + const char *host, *error; ssl_io = SSL_get_ex_data(ssl, dovecot_ssl_extdata_index); host = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); @@ -306,6 +306,14 @@ static int ssl_servername_callback(SSL *ssl, int *al ATTR_UNUSED, } else if (ssl_io->verbose) { i_debug("SSL_get_servername() failed"); } + + if (ssl_io->sni_callback != NULL) { + if (ssl_io->sni_callback(ssl_io->sni_host, &error, + ssl_io->sni_context) < 0) { + openssl_iostream_set_error(ssl_io, error); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + } return SSL_TLSEXT_ERR_OK; } #endif diff --git a/src/lib-ssl-iostream/iostream-openssl.c b/src/lib-ssl-iostream/iostream-openssl.c index 221ae0acd6..e960f485ef 100644 --- a/src/lib-ssl-iostream/iostream-openssl.c +++ b/src/lib-ssl-iostream/iostream-openssl.c @@ -10,8 +10,7 @@ static void openssl_iostream_free(struct ssl_iostream *ssl_io); -static void -openssl_iostream_set_error(struct ssl_iostream *ssl_io, const char *str) +void openssl_iostream_set_error(struct ssl_iostream *ssl_io, const char *str) { if (ssl_io->verbose) { /* This error should normally be logged by lib-ssl-iostream's @@ -661,6 +660,27 @@ openssl_iostream_set_handshake_callback(struct ssl_iostream *ssl_io, ssl_io->handshake_context = context; } +static void +openssl_iostream_set_sni_callback(struct ssl_iostream *ssl_io, + ssl_iostream_sni_callback_t *callback, + void *context) +{ + ssl_io->sni_callback = callback; + ssl_io->sni_context = context; +} + +static void +openssl_iostream_change_context(struct ssl_iostream *ssl_io, + struct ssl_iostream_context *ctx) +{ + if (ctx != ssl_io->ctx) { + SSL_set_SSL_CTX(ssl_io->ssl, ctx->ssl_ctx); + ssl_iostream_context_ref(ctx); + ssl_iostream_context_unref(&ssl_io->ctx); + ssl_io->ctx = ctx; + } +} + static void openssl_iostream_set_log_prefix(struct ssl_iostream *ssl_io, const char *prefix) { @@ -789,6 +809,8 @@ static const struct iostream_ssl_vfuncs ssl_vfuncs = { .handshake = openssl_iostream_handshake, .set_handshake_callback = openssl_iostream_set_handshake_callback, + .set_sni_callback = openssl_iostream_set_sni_callback, + .change_context = openssl_iostream_change_context, .set_log_prefix = openssl_iostream_set_log_prefix, .is_handshaked = openssl_iostream_is_handshaked, diff --git a/src/lib-ssl-iostream/iostream-openssl.h b/src/lib-ssl-iostream/iostream-openssl.h index d67de5207b..caa34ed1f2 100644 --- a/src/lib-ssl-iostream/iostream-openssl.h +++ b/src/lib-ssl-iostream/iostream-openssl.h @@ -56,6 +56,9 @@ struct ssl_iostream { ssl_iostream_handshake_callback_t *handshake_callback; void *handshake_context; + ssl_iostream_sni_callback_t *sni_callback; + void *sni_context; + bool handshaked:1; bool handshake_failed:1; bool cert_received:1; @@ -105,6 +108,7 @@ int openssl_iostream_handle_error(struct ssl_iostream *ssl_io, int ret, enum openssl_iostream_sync_type type, const char *func_name); +void openssl_iostream_set_error(struct ssl_iostream *ssl_io, const char *str); const char *openssl_iostream_error(void); const char *openssl_iostream_key_load_error(void); const char * diff --git a/src/lib-ssl-iostream/iostream-ssl-private.h b/src/lib-ssl-iostream/iostream-ssl-private.h index b5f16112e7..19b581e28a 100644 --- a/src/lib-ssl-iostream/iostream-ssl-private.h +++ b/src/lib-ssl-iostream/iostream-ssl-private.h @@ -24,6 +24,11 @@ struct iostream_ssl_vfuncs { void (*set_handshake_callback)(struct ssl_iostream *ssl_io, ssl_iostream_handshake_callback_t *callback, void *context); + void (*set_sni_callback)(struct ssl_iostream *ssl_io, + ssl_iostream_sni_callback_t *callback, + void *context); + void (*change_context)(struct ssl_iostream *ssl_io, + struct ssl_iostream_context *ctx); void (*set_log_prefix)(struct ssl_iostream *ssl_io, const char *prefix); bool (*is_handshaked)(const struct ssl_iostream *ssl_io); diff --git a/src/lib-ssl-iostream/iostream-ssl.c b/src/lib-ssl-iostream/iostream-ssl.c index 6656698c80..228c982eaf 100644 --- a/src/lib-ssl-iostream/iostream-ssl.c +++ b/src/lib-ssl-iostream/iostream-ssl.c @@ -167,6 +167,19 @@ void ssl_iostream_set_handshake_callback(struct ssl_iostream *ssl_io, ssl_vfuncs->set_handshake_callback(ssl_io, callback, context); } +void ssl_iostream_set_sni_callback(struct ssl_iostream *ssl_io, + ssl_iostream_sni_callback_t *callback, + void *context) +{ + ssl_vfuncs->set_sni_callback(ssl_io, callback, context); +} + +void ssl_iostream_change_context(struct ssl_iostream *ssl_io, + struct ssl_iostream_context *ctx) +{ + ssl_vfuncs->change_context(ssl_io, ctx); +} + bool ssl_iostream_is_handshaked(const struct ssl_iostream *ssl_io) { return ssl_vfuncs->is_handshaked(ssl_io); diff --git a/src/lib-ssl-iostream/iostream-ssl.h b/src/lib-ssl-iostream/iostream-ssl.h index 86c569a5a7..cee3810a10 100644 --- a/src/lib-ssl-iostream/iostream-ssl.h +++ b/src/lib-ssl-iostream/iostream-ssl.h @@ -43,6 +43,9 @@ int ssl_module_load(const char **error_r); likely should be calling ssl_iostream_check_cert_validity(). */ typedef int ssl_iostream_handshake_callback_t(const char **error_r, void *context); +/* Called when TLS SNI becomes available. */ +typedef int ssl_iostream_sni_callback_t(const char *name, const char **error_r, + void *context); int io_stream_create_ssl_client(struct ssl_iostream_context *ctx, const char *host, const struct ssl_iostream_settings *set, @@ -71,6 +74,14 @@ int ssl_iostream_handshake(struct ssl_iostream *ssl_io); void ssl_iostream_set_handshake_callback(struct ssl_iostream *ssl_io, ssl_iostream_handshake_callback_t *callback, void *context); +/* Call the given callback when client sends SNI. The callback can change the + ssl_iostream's context (with different certificates) by using + ssl_iostream_change_context(). */ +void ssl_iostream_set_sni_callback(struct ssl_iostream *ssl_io, + ssl_iostream_sni_callback_t *callback, + void *context); +void ssl_iostream_change_context(struct ssl_iostream *ssl_io, + struct ssl_iostream_context *ctx); bool ssl_iostream_is_handshaked(const struct ssl_iostream *ssl_io); /* Returns TRUE if the remote cert is invalid, or handshake callback returned