From ee87746075c597385bc552ce4f0365476a919d32 Mon Sep 17 00:00:00 2001 From: MadAlexUK <44322503+MadAlexUK@users.noreply.github.com> Date: Sat, 3 Nov 2018 17:54:20 +0000 Subject: [PATCH] Tls server name indication support (#310) * Added a mailstream_ssl_set_server_name() function This allows clients to enable Server Name Indication on a TLS session by supplying the server name to be used, via a call within the session open callback. --- doc/API.sgml | 16 ++++++++ src/data-types/mailstream_ssl.c | 65 ++++++++++++++++++++++++++++++++- src/data-types/mailstream_ssl.h | 4 ++ 3 files changed, 83 insertions(+), 2 deletions(-) diff --git a/doc/API.sgml b/doc/API.sgml index d5c37a09..0f68d908 100644 --- a/doc/API.sgml +++ b/doc/API.sgml @@ -1213,6 +1213,22 @@ mailstream * mailstream_ssl_open(int fd); mailstream_ssl_open() will open a TLS/SSL socket. + + +int mailstream_ssl_set_server_name(struct mailstream_ssl_context * ssl_context, + char * hostname) + + + + mailstream_ssl_set_server_name() allows the client + to enable the use of the Server Name Indication TLS extension upon + opening a TLS stream, by providing the domain name to be indicated + to server as the desired destination. ssl_context + is the context pointer passed to the client-supplied callback + function by mailstream_ssl_open_with_callback() + etc. Note that hostname must be a domain name, not + a string representation of an IP address. + diff --git a/src/data-types/mailstream_ssl.c b/src/data-types/mailstream_ssl.c index cb0a17eb..2c1a7441 100644 --- a/src/data-types/mailstream_ssl.c +++ b/src/data-types/mailstream_ssl.c @@ -113,6 +113,9 @@ struct mailstream_ssl_context SSL_CTX * openssl_ssl_ctx; X509* client_x509; EVP_PKEY *client_pkey; +# if (OPENSSL_VERSION_NUMBER >= 0x10000000L) + char * server_name; +# endif /* (OPENSSL_VERSION_NUMBER >= 0x10000000L) */ #else gnutls_session session; gnutls_x509_crt client_x509; @@ -463,7 +466,15 @@ static struct mailstream_ssl_data * ssl_data_new_full(int fd, time_t timeout, if (ssl_conn == NULL) goto free_ctx; - + +#if (OPENSSL_VERSION_NUMBER >= 0x10000000L) + if (ssl_context->server_name != NULL) { + SSL_set_tlsext_host_name(ssl_conn, ssl_context->server_name); + free(ssl_context->server_name); + ssl_context->server_name = NULL; + } +#endif /* (OPENSSL_VERSION_NUMBER >= 0x10000000L) */ + if (SSL_set_fd(ssl_conn, fd) == 0) goto free_ssl_conn; @@ -1361,6 +1372,47 @@ int mailstream_ssl_set_server_certicate(struct mailstream_ssl_context * ssl_cont #endif /* USE_SSL */ } +LIBETPAN_EXPORT +int mailstream_ssl_set_server_name(struct mailstream_ssl_context * ssl_context, + char * hostname) +{ + int r = -1; + +#ifdef USE_SSL +# ifdef USE_GNUTLS + if (hostname != NULL) { + r = gnutls_server_name_set(ssl_context->session, GNUTLS_NAME_DNS, hostname, strlen(hostname)); + } + else { + r = gnutls_server_name_set(ssl_context->session, GNUTLS_NAME_DNS, "", 0U); + } +# else /* !USE_GNUTLS */ +# if (OPENSSL_VERSION_NUMBER >= 0x10000000L) + if (hostname != NULL) { + /* Unfortunately we can't set this in the openssl session yet since it + * hasn't been created yet; we only have the openssl context at this point. + * We will set it in the openssl session when we create it, soon after the + * client callback that we expect to be calling us (since it is the way the + * client gets our mailstream_ssl_context) returns (see + * ssl_data_new_full()) but we cannot rely on the client persisting it. We + * must therefore take a temporary copy here, which we free once we've set + * it in the openssl session. */ + ssl_context->server_name = strdup(hostname); + } + else { + if (ssl_context->server_name != NULL) { + free(ssl_context->server_name); + } + ssl_context->server_name = NULL; + } + r = 0; +# endif /* (OPENSSL_VERSION_NUMBER >= 0x10000000L) */ +# endif /* !USE_GNUTLS */ +#endif /* USE_SSL */ + + return r; +} + #ifdef USE_SSL #ifndef USE_GNUTLS static struct mailstream_ssl_context * mailstream_ssl_context_new(SSL_CTX * open_ssl_ctx, int fd) @@ -1374,6 +1426,9 @@ static struct mailstream_ssl_context * mailstream_ssl_context_new(SSL_CTX * open ssl_ctx->openssl_ssl_ctx = open_ssl_ctx; ssl_ctx->client_x509 = NULL; ssl_ctx->client_pkey = NULL; +#if (OPENSSL_VERSION_NUMBER >= 0x10000000L) + ssl_ctx->server_name = NULL; +#endif /* (OPENSSL_VERSION_NUMBER >= 0x10000000L) */ ssl_ctx->fd = fd; return ssl_ctx; @@ -1381,8 +1436,14 @@ static struct mailstream_ssl_context * mailstream_ssl_context_new(SSL_CTX * open static void mailstream_ssl_context_free(struct mailstream_ssl_context * ssl_ctx) { - if (ssl_ctx) + if (ssl_ctx != NULL) { +#if (OPENSSL_VERSION_NUMBER >= 0x10000000L) + if (ssl_ctx->server_name != NULL) { + free(ssl_ctx->server_name); + } +#endif /* (OPENSSL_VERSION_NUMBER >= 0x10000000L) */ free(ssl_ctx); + } } #else static struct mailstream_ssl_context * mailstream_ssl_context_new(gnutls_session session, int fd) diff --git a/src/data-types/mailstream_ssl.h b/src/data-types/mailstream_ssl.h index 885dc7e7..fbf2642a 100644 --- a/src/data-types/mailstream_ssl.h +++ b/src/data-types/mailstream_ssl.h @@ -123,6 +123,10 @@ LIBETPAN_EXPORT int mailstream_ssl_set_server_certicate(struct mailstream_ssl_context * ssl_context, char * CAfile, char * CApath); +LIBETPAN_EXPORT +int mailstream_ssl_set_server_name(struct mailstream_ssl_context * ssl_context, + char * hostname); + LIBETPAN_EXPORT void * mailstream_ssl_get_openssl_ssl_ctx(struct mailstream_ssl_context * ssl_context);