From 8e119a72f0396038aa6b5dba68e018fd00004f33 Mon Sep 17 00:00:00 2001 From: George Joseph Date: Tue, 23 Apr 2024 14:15:20 -0600 Subject: [PATCH] tcptls/iostream: Add support for setting SNI on client TLS connections If the hostname field of the ast_tcptls_session_args structure is set (which it is for websocket client connections), that hostname will now automatically be used in an SNI TLS extension in the client hello. Resolves: #713 UserNote: Secure websocket client connections now send SNI in the TLS client hello. --- include/asterisk/iostream.h | 11 +++++++++++ main/iostream.c | 26 +++++++++++++++++++++----- main/tcptls.c | 7 +++++++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/include/asterisk/iostream.h b/include/asterisk/iostream.h index 402bd5b1fe..9600e969f1 100644 --- a/include/asterisk/iostream.h +++ b/include/asterisk/iostream.h @@ -106,6 +106,17 @@ void ast_iostream_set_timeout_sequence(struct ast_iostream *stream, struct timev */ void ast_iostream_set_exclusive_input(struct ast_iostream *stream, int exclusive_input); +/*! + * \brief Set the iostream's SNI hostname for TLS client connections + * + * \param stream A pointer to an iostream + * \param sni_hostname The hostname to use for SNI when in client mode + * + * \retval 0 if the hostname was set successfully. + * \retval -1 if memory could not be allocated for the hostname. + */ +int ast_iostream_set_sni_hostname(struct ast_iostream *stream, const char *sni_hostname); + /*! * \brief Get an iostream's file descriptor. * diff --git a/main/iostream.c b/main/iostream.c index 7727983423..e165abe410 100644 --- a/main/iostream.c +++ b/main/iostream.c @@ -46,6 +46,7 @@ struct ast_iostream { int rbuflen; char *rbufhead; char rbuf[2048]; + char *sni_hostname; }; #if defined(DO_SSL) @@ -152,6 +153,16 @@ void ast_iostream_set_exclusive_input(struct ast_iostream *stream, int exclusive stream->exclusive_input = exclusive_input; } +int ast_iostream_set_sni_hostname(struct ast_iostream *stream, const char *sni_hostname) +{ + ast_assert(stream != NULL); + + ast_free(stream->sni_hostname); + stream->sni_hostname = ast_strdup(sni_hostname); + + return stream->sni_hostname ? 0 : -1; +} + static ssize_t iostream_read(struct ast_iostream *stream, void *buf, size_t size) { struct timeval start; @@ -591,13 +602,9 @@ int ast_iostream_close(struct ast_iostream *stream) static void iostream_dtor(void *cookie) { -#ifdef AST_DEVMODE - /* Since the ast_assert below is the only one using stream, - * and ast_assert is only available with AST_DEVMODE, we - * put this in a conditional to avoid compiler warnings. */ struct ast_iostream *stream = cookie; -#endif + ast_free(stream->sni_hostname); ast_assert(stream->fd == -1); } @@ -639,6 +646,15 @@ int ast_iostream_start_tls(struct ast_iostream **pstream, SSL_CTX *ssl_ctx, int */ SSL_set_fd(stream->ssl, stream->fd); + if (client && !ast_strlen_zero(stream->sni_hostname)) { + if (!SSL_set_tlsext_host_name(stream->ssl, stream->sni_hostname)) { + ast_log(LOG_ERROR, "Unable to set SNI hostname '%s'\n", + stream->sni_hostname); + errno = EIO; + return -1; + } + } + res = ssl_setup(stream->ssl); if (res <= 0) { int sslerr = SSL_get_error(stream->ssl, res); diff --git a/main/tcptls.c b/main/tcptls.c index 73a165726c..c20bcab5d9 100644 --- a/main/tcptls.c +++ b/main/tcptls.c @@ -741,6 +741,13 @@ struct ast_tcptls_session_instance *ast_tcptls_client_create(struct ast_tcptls_s /* Set current info */ ast_sockaddr_copy(&desc->old_address, &desc->remote_address); + if (!ast_strlen_zero(desc->hostname)) { + if (ast_iostream_set_sni_hostname(tcptls_session->stream, desc->hostname) != 0) { + ast_log(LOG_WARNING, "Unable to set SNI hostname '%s' on connection '%s'\n", + desc->hostname, desc->name); + } + } + return tcptls_session; error: