]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-ssl-iostream: Add support for channel binding
authorStephan Bosch <stephan.bosch@open-xchange.com>
Thu, 5 Nov 2020 01:31:07 +0000 (02:31 +0100)
committerStephan Bosch <stephan.bosch@open-xchange.com>
Fri, 17 Jan 2025 17:36:39 +0000 (18:36 +0100)
src/lib-ssl-iostream/iostream-openssl.c
src/lib-ssl-iostream/iostream-ssl-private.h
src/lib-ssl-iostream/iostream-ssl.c
src/lib-ssl-iostream/iostream-ssl.h

index cdbd45c1716b558a26828c2695a17dde8bfc5316..77999216444b77f99a1a4b940d19a15a3811c3c1 100644 (file)
@@ -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)
index 1f587d12a1252276f46336c4dd4cef83bfff4dcb..33941481dbd35abcfe648e2f88e2aaf9e093e73c 100644 (file)
@@ -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);
index 8cece98df6280a0dad7d9fd97ccc3ecbc73f923a..59acac4f2642085f4d8f4326731dbaa20ff12a88 100644 (file)
@@ -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);
+}
index 1825037ba62fadbc47035dd917a8fad987b9019b..379a0bc9a731c6fa42856483ce3f1facc4ba15a1 100644 (file)
@@ -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);