From: Stephan Bosch Date: Thu, 5 Nov 2020 01:31:07 +0000 (+0100) Subject: lib-ssl-iostream: Add support for channel binding X-Git-Tag: 2.4.0~23 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=77d50a6b5e75796896e8e5b437783a99497908d9;p=thirdparty%2Fdovecot%2Fcore.git lib-ssl-iostream: Add support for channel binding --- diff --git a/src/lib-ssl-iostream/iostream-openssl.c b/src/lib-ssl-iostream/iostream-openssl.c index cdbd45c171..7799921644 100644 --- a/src/lib-ssl-iostream/iostream-openssl.c +++ b/src/lib-ssl-iostream/iostream-openssl.c @@ -1,6 +1,7 @@ /* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ #include "lib.h" +#include "buffer.h" #include "istream-private.h" #include "ostream-private.h" #include "iostream-openssl.h" @@ -876,6 +877,115 @@ openssl_iostream_get_application_protocol(struct ssl_iostream *ssl_io) return NULL; } +static int +openssl_iostream_get_cb_tls_exporter(struct ssl_iostream *ssl_io, + const buffer_t **data_r, + const char **error_r) +{ + /* RFC 9266, Section 4.2: + + When TLS renegotiation is enabled on a connection, the "tls-exporter" + channel binding type is not defined for that connection, and + implementations MUST NOT support it. + */ + if (SSL_version(ssl_io->ssl) < TLS1_3_VERSION && + HAS_NO_BITS(SSL_get_options(ssl_io->ssl), + SSL_OP_NO_RENEGOTIATION)) { + *error_r = t_strdup_printf( + "Channel binding type 'tls-exporter' not available: " + "TLS renegotiation is enabled for %s", + SSL_get_version(ssl_io->ssl)); + return -1; + } + + static const char literal[] = "EXPORTER-Channel-Binding"; + static const size_t size = 32; + buffer_t *buf = t_buffer_create(size); + void *data = buffer_get_space_unsafe(buf, 0, size); + + if (SSL_export_keying_material(ssl_io->ssl, data, size, + literal, sizeof(literal) - 1, + NULL, 0, 0) != 1) { + *error_r = t_strdup_printf( + "Failed to compose channel binding 'tls-exporter': %s", + openssl_iostream_error()); + return -1; + } + + *data_r = buf; + return 0; +} + +static int +openssl_iostream_get_cb_tls_unique(struct ssl_iostream *ssl_io, + const buffer_t **data_r, + const char **error_r) +{ + /* RFC 9266, Section 3: + + The specifications for Salted Challenge Response Authentication + Mechanism (SCRAM) [RFC5802] [RFC7677] and Generic Security Service + Application Program Interface (GSS-API) over Simple Authentication + and Security Layer (SASL) [RFC5801] define "tls-unique" as the + default channel binding to use over TLS. As "tls-unique" is not + defined for TLS 1.3 (and greater), this document updates [RFC5801], + [RFC5802], and [RFC7677] to use "tls-exporter" as the default channel + binding over TLS 1.3 (and greater). + */ + if (SSL_version(ssl_io->ssl) >= TLS1_3_VERSION) { + *error_r = t_strdup_printf( + "Channel binding type 'tls-unique' not defined: " + "TLS version is %s", SSL_get_version(ssl_io->ssl)); + return -1; + } + + static const size_t max_size = EVP_MAX_MD_SIZE; + buffer_t *buf = t_buffer_create(max_size); + void *data = buffer_get_space_unsafe(buf, 0, max_size); + size_t size; + bool peer_finished; + + /* Roles are reversed when session reuse is in effect */ + peer_finished = !ssl_io->ctx->client_ctx; + if (SSL_session_reused(ssl_io->ssl) != 0) + peer_finished = !peer_finished; + if (peer_finished) + size = SSL_get_peer_finished(ssl_io->ssl, data, max_size); + else + size = SSL_get_finished(ssl_io->ssl, data, max_size); + + buffer_set_used_size(buf, size); + + *data_r = buf; + return 0; +} + +static int +openssl_iostream_get_channel_binding(struct ssl_iostream *ssl_io, + const char *type, const buffer_t **data_r, + const char **error_r) +{ + *error_r = NULL; + *data_r = NULL; + + if (!ssl_io->handshaked) { + *error_r = "Channel binding not available before handshake"; + return -1; + } + + if (strcmp(type, SSL_CHANNEL_BIND_TYPE_TLS_UNIQUE) == 0) { + return openssl_iostream_get_cb_tls_unique( + ssl_io, data_r, error_r); + } else if (strcmp(type, SSL_CHANNEL_BIND_TYPE_TLS_EXPORTER) == 0) { + return openssl_iostream_get_cb_tls_exporter( + ssl_io, data_r, error_r); + } + + *error_r = t_strdup_printf( + "Unsupported channel binding type '%s'", type); + return -1; +} + static const struct iostream_ssl_vfuncs ssl_vfuncs = { .global_init = openssl_iostream_global_init, .context_init_client = openssl_iostream_context_init_client, @@ -912,6 +1022,8 @@ static const struct iostream_ssl_vfuncs ssl_vfuncs = { .get_application_protocol = openssl_iostream_get_application_protocol, .set_application_protocols = openssl_iostream_context_set_application_protocols, + + .get_channel_binding = openssl_iostream_get_channel_binding, }; void ssl_iostream_openssl_init(void) diff --git a/src/lib-ssl-iostream/iostream-ssl-private.h b/src/lib-ssl-iostream/iostream-ssl-private.h index 1f587d12a1..33941481db 100644 --- a/src/lib-ssl-iostream/iostream-ssl-private.h +++ b/src/lib-ssl-iostream/iostream-ssl-private.h @@ -58,6 +58,10 @@ struct iostream_ssl_vfuncs { const char *(*get_application_protocol)(struct ssl_iostream *ssl_io); void (*set_application_protocols)(struct ssl_iostream_context *ctx, const char *const *names); + + int (*get_channel_binding)(struct ssl_iostream *ssl_io, + const char *type, const buffer_t **data_r, + const char **error_r); }; void iostream_ssl_module_init(const struct iostream_ssl_vfuncs *vfuncs); diff --git a/src/lib-ssl-iostream/iostream-ssl.c b/src/lib-ssl-iostream/iostream-ssl.c index 8cece98df6..59acac4f26 100644 --- a/src/lib-ssl-iostream/iostream-ssl.c +++ b/src/lib-ssl-iostream/iostream-ssl.c @@ -425,3 +425,22 @@ void ssl_iostream_context_set_application_protocols(struct ssl_iostream_context { ssl_vfuncs->set_application_protocols(ssl_ctx, names); } + +int ssl_iostream_get_channel_binding(struct ssl_iostream *ssl_io, + const char *type, const buffer_t **data_r, + const char **error_r) +{ + *data_r = NULL; + *error_r = NULL; + + if (ssl_io == NULL) { + *error_r = "Channel binding not available for insecure channel"; + return -1; + } + if (ssl_vfuncs->get_channel_binding == NULL) { + *error_r = "Channel binding not supported"; + return -1; + } + + return ssl_vfuncs->get_channel_binding(ssl_io, type, data_r, error_r); +} diff --git a/src/lib-ssl-iostream/iostream-ssl.h b/src/lib-ssl-iostream/iostream-ssl.h index 1825037ba6..379a0bc9a7 100644 --- a/src/lib-ssl-iostream/iostream-ssl.h +++ b/src/lib-ssl-iostream/iostream-ssl.h @@ -7,6 +7,9 @@ struct ssl_iostream; struct ssl_iostream_context; +#define SSL_CHANNEL_BIND_TYPE_TLS_UNIQUE "tls-unique" +#define SSL_CHANNEL_BIND_TYPE_TLS_EXPORTER "tls-exporter" + enum ssl_iostream_protocol_version { /* Version not yet known at this protocol stage */ SSL_IOSTREAM_PROTOCOL_VERSION_UNKNOWN = 0, @@ -237,6 +240,14 @@ const char *ssl_iostream_get_protocol_name(struct ssl_iostream *ssl_io); /* Returns currently used SSL protocol version. */ enum ssl_iostream_protocol_version ssl_iostream_get_protocol_version(struct ssl_iostream *ssl_io); +/* Returns 0 if channel binding type is supported with channel binding data of + requested type in data_r. Returns -1 if channel binding (of that type) is not + supported and error message is returned in error_r. The ssl_io parameter + may be NULL, in which case -1 is returned along with a generic error + applicable to an insecure channel. */ +int ssl_iostream_get_channel_binding(struct ssl_iostream *ssl_io, + const char *type, const buffer_t **data_r, + const char **error_r); const char *ssl_iostream_get_last_error(struct ssl_iostream *ssl_io);