From 07990884125a2146c1f7d954c6d568ab5d729f9a Mon Sep 17 00:00:00 2001 From: Guido Kiener Date: Tue, 12 Dec 2023 23:49:15 +0000 Subject: [PATCH] RFC 9266: Channel Bindings for TLS 1.3 #4191 TLS connections of the IMAPD service provide channel binding data for the SASL authentication layer. The current implementation sets the correct "tls-unique" channel binding data for TLS versions 1.2 and lower, however not for TLS version 1.3. TLS version 1.3 requires using specific exporter keying material (EKM) according to RFC 9266 Section 2: Label: "EXPORTER-Channel-Binding" Context: Zero-length string Key Length: 32 bytes Signed-off-by: Guido Kiener --- imap/tls.c | 48 ++++++++++++++++++++++++++++++++++++++---------- imtest/imtest.c | 44 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 73 insertions(+), 19 deletions(-) diff --git a/imap/tls.c b/imap/tls.c index cee70d9b92..daae41fca2 100644 --- a/imap/tls.c +++ b/imap/tls.c @@ -1314,19 +1314,47 @@ EXPORTED int tls_start_servertls(int readfd, int writefd, int timeout, saslprops->ssf = (sasl_ssf_t) tls_cipher_usebits; #if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) - if (SSL_session_reused(tls_conn)) { - saslprops->cbinding.len = SSL_get_finished(tls_conn, - saslprops->tls_finished, - MAX_FINISHED_LEN); + if (SSL_version(tls_conn) <= TLS1_2_VERSION) { + if (SSL_session_reused(tls_conn)) { + saslprops->cbinding.len = + SSL_get_finished(tls_conn, + saslprops->tls_finished, + MAX_FINISHED_LEN); + } + else { + saslprops->cbinding.len = + SSL_get_peer_finished(tls_conn, + saslprops->tls_finished, + MAX_FINISHED_LEN); + } + saslprops->cbinding.name = "tls-unique"; + saslprops->cbinding.data = saslprops->tls_finished; + } +#if (OPENSSL_VERSION_NUMBER >= 0x10100001L) + else { + /* see RFC 9266 Section 2, Definition of label, context, length */ + static const char label[] = "EXPORTER-Channel-Binding"; + static const size_t key_len = 32; + + assert(key_len <= sizeof(saslprops->tls_finished)); + if (1 != SSL_export_keying_material(tls_conn, + saslprops->tls_finished, key_len, + label, sizeof(label)-1, + 0, 0, 0)) { + syslog(LOG_ERR, "Error reading EKM for channel binding"); + } + else { + saslprops->cbinding.name = "tls-exporter"; + saslprops->cbinding.data = saslprops->tls_finished; + saslprops->cbinding.len = key_len; + } } +#else else { - saslprops->cbinding.len = SSL_get_peer_finished(tls_conn, - saslprops->tls_finished, - MAX_FINISHED_LEN); + /* This case should not happen. It's a strange OpenSSL variant */ + syslog(LOG_ERR, "No EKM (channel binding) for TLS 1.3 or higher"); } - - saslprops->cbinding.name = "tls-unique"; - saslprops->cbinding.data = saslprops->tls_finished; +#endif /* (OPENSSL_VERSION_NUMBER >= 0x10100001L) */ #endif /* (OPENSSL_VERSION_NUMBER >= 0x0090800fL) */ #if OPENSSL_VERSION_NUMBER >= 0x10002000L diff --git a/imtest/imtest.c b/imtest/imtest.c index c3bfe3125b..c7b40963fe 100644 --- a/imtest/imtest.c +++ b/imtest/imtest.c @@ -791,20 +791,46 @@ static void do_starttls(int ssl, char *keyfile, unsigned *ssf) imtest_fatal("Error setting SASL property (external auth_id)"); #if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) - static unsigned char finished[EVP_MAX_MD_SIZE]; static struct sasl_channel_binding cbinding; - if (SSL_session_reused(tls_conn)) { - cbinding.len = SSL_get_peer_finished(tls_conn, - finished, sizeof(finished)); + if (SSL_version(tls_conn) <= TLS1_2_VERSION) { + static unsigned char finished[EVP_MAX_MD_SIZE]; + + if (SSL_session_reused(tls_conn)) { + cbinding.len = + SSL_get_peer_finished(tls_conn, finished, sizeof(finished)); + } + else { + cbinding.len = + SSL_get_finished(tls_conn, finished, sizeof(finished)); + } + + cbinding.name = "tls-unique"; + cbinding.critical = 0; + cbinding.data = finished; } +#if (OPENSSL_VERSION_NUMBER >= 0x10100001L) else { - cbinding.len = SSL_get_finished(tls_conn, finished, sizeof(finished)); - } + /* see RFC 9266 Section 2, Definition of label, context, length */ + static const char label[] = "EXPORTER-Channel-Binding"; + static unsigned char exported_key[32]; + static const size_t key_len = sizeof(exported_key); - cbinding.name = "tls-unique"; - cbinding.critical = 0; - cbinding.data = finished; + if (1 != SSL_export_keying_material(tls_conn, exported_key, key_len, + label, sizeof(label)-1, 0, 0, 0)) { + imtest_fatal("Error reading EKM for channel binding"); + } + cbinding.name = "tls-exporter"; + cbinding.critical = 0; + cbinding.len = key_len; + cbinding.data = exported_key; + } +#else + else { + /* This case should not happen. It's a strange OpenSSL variant */ + imtest_fatal("No EKM (channel binding) for TLS 1.3 or higher"); + } +#endif /* (OPENSSL_VERSION_NUMBER >= 0x10100001L) */ result = sasl_setprop(conn, SASL_CHANNEL_BINDING, &cbinding); if (result!=SASL_OK)