From 94e0b16c011b7b88708b8a8505fac6bfbe2e3cca Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 15 Nov 2024 15:46:05 +0000 Subject: [PATCH] Use RFC9266 'tls-exporter' channel bindings for Cisco STRAP with TLSv1.3 Fixes #659 Signed-off-by: David Woodhouse --- gnutls.c | 20 +++++++++++++++++++- openconnect-internal.h | 5 +++++ openssl.c | 30 +++++++++++++++++++++++------- www/changelog.xml | 1 + 4 files changed, 48 insertions(+), 8 deletions(-) diff --git a/gnutls.c b/gnutls.c index 9fc010b9..6c2e3aec 100644 --- a/gnutls.c +++ b/gnutls.c @@ -3176,7 +3176,25 @@ void append_strap_verify(struct openconnect_info *vpninfo, /* Concatenate our Finished message with our pubkey to be signed */ struct oc_text_buf *nonce = buf_alloc(); - buf_append_bytes(nonce, vpninfo->finished, vpninfo->finished_len); + if (gnutls_protocol_get_version(vpninfo->https_sess) <= GNUTLS_TLS1_2) { + /* For TLSv1.2 and earlier, use RFC5929 'tls-unique' channel binding */ + buf_append_bytes(nonce, vpninfo->finished, vpninfo->finished_len); + } else { + /* For TLSv1.3 use RFC9266 'tls-exporter' channel binding */ + char channel_binding_buf[TLS_EXPORTER_KEY_SIZE]; + err = gnutls_prf(vpninfo->https_sess, TLS_EXPORTER_LABEL_SIZE, TLS_EXPORTER_LABEL, + 0, 0, 0, TLS_EXPORTER_KEY_SIZE, channel_binding_buf); + if (err) { + vpn_progress(vpninfo, PRG_ERR, + _("Failed to generate channel bindings for STRAP key: %s\n"), + gnutls_strerror(err)); + if (!buf_error(buf)) + buf->error = -EIO; + buf_free(nonce); + return; + } + buf_append_bytes(nonce, channel_binding_buf, TLS_EXPORTER_KEY_SIZE); + } if (rekey) { /* We have a copy and we don't want it freed just yet */ diff --git a/openconnect-internal.h b/openconnect-internal.h index 5abfe98d..600b43b3 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -1060,6 +1060,11 @@ static inline void __monitor_fd_new(struct openconnect_info *vpninfo, #define PSK_LABEL_SIZE (sizeof(PSK_LABEL) - 1) #define PSK_KEY_SIZE 32 +/* Key material for RFC9266 tls-exporter channel binding */ +#define TLS_EXPORTER_LABEL "EXPORTER-Channel-Binding" +#define TLS_EXPORTER_LABEL_SIZE (sizeof(TLS_EXPORTER_LABEL) - 1) +#define TLS_EXPORTER_KEY_SIZE 32 + /* Packet types */ #define AC_PKT_DATA 0 /* Uncompressed data */ diff --git a/openssl.c b/openssl.c index 3f204d0f..b354cf74 100644 --- a/openssl.c +++ b/openssl.c @@ -2518,14 +2518,30 @@ void append_strap_verify(struct openconnect_info *vpninfo, struct oc_text_buf *buf, int rekey) { unsigned char finished[64]; - size_t flen = SSL_get_finished(vpninfo->https_ssl, finished, sizeof(finished)); + size_t flen; - if (flen > sizeof(finished)) { - vpn_progress(vpninfo, PRG_ERR, - _("SSL Finished message too large (%zu bytes)\n"), flen); - if (!buf_error(buf)) - buf->error = -EIO; - return; + if (SSL_SESSION_get_protocol_version(SSL_get_session(vpninfo->https_ssl)) <= TLS1_2_VERSION) { + /* For TLSv1.2 and earlier, use RFC5929 'tls-unique' channel binding */ + flen = SSL_get_finished(vpninfo->https_ssl, finished, sizeof(finished)); + if (flen > sizeof(finished)) { + vpn_progress(vpninfo, PRG_ERR, + _("SSL Finished message too large (%zu bytes)\n"), flen); + if (!buf_error(buf)) + buf->error = -EIO; + return; + } + } else { + /* For TLSv1.3 use RFC9266 'tls-exporter' channel binding */ + if (!SSL_export_keying_material(vpninfo->https_ssl, + finished, TLS_EXPORTER_KEY_SIZE, + TLS_EXPORTER_LABEL, TLS_EXPORTER_LABEL_SIZE, + NULL, 0, 0)) { + vpn_progress(vpninfo, PRG_ERR, + _("Failed to generate channel bindings for STRAP key\n")); + openconnect_report_ssl_errors(vpninfo); + return; + } + flen = TLS_EXPORTER_KEY_SIZE; } /* If we're rekeying, we need to sign the Verify header with the *old* key. */ diff --git a/www/changelog.xml b/www/changelog.xml index 04b088f2..f4f46363 100644 --- a/www/changelog.xml +++ b/www/changelog.xml @@ -15,6 +15,7 @@