From 1c519e07b908a314ce7bdfceb6baa9e18e302dfc Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Sat, 28 Dec 2019 17:00:30 +0000 Subject: [PATCH] GSASL channel-binding: TLS resumption checks --- doc/doc-docbook/spec.xfpt | 2 +- src/src/auths/gsasl_exim.c | 43 ++++++++++++++++++++++++++++---------- src/src/auths/gsasl_exim.h | 2 +- src/src/globals.h | 1 + src/src/tls-gnu.c | 6 ++++++ src/src/tls-openssl.c | 2 ++ 6 files changed, 43 insertions(+), 13 deletions(-) diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 9043685c5..1d4c39c6d 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -27447,7 +27447,7 @@ without code changes in Exim. .option client_authz gsasl string&!! unset This option can be used to supply an &'authorization id'& which is different to the &'authentication_id'& provided -by $%client_username%& option. +by &%client_username%& option. If unset or (after expansion) empty it is not used, which is the common case. diff --git a/src/src/auths/gsasl_exim.c b/src/src/auths/gsasl_exim.c index db14a40e0..f527e130a 100644 --- a/src/src/auths/gsasl_exim.c +++ b/src/src/auths/gsasl_exim.c @@ -96,7 +96,7 @@ auth_gsasl_options_block auth_gsasl_option_defaults = { /* Dummy values */ void auth_gsasl_init(auth_instance *ablock) {} int auth_gsasl_server(auth_instance *ablock, uschar *data) {return 0;} -int auth_gsasl_client(auth_instance *ablock, smtp_inblock * sx, +int auth_gsasl_client(auth_instance *ablock, void * sx, int timeout, uschar *buffer, int buffsize) {return 0;} void auth_gsasl_version_report(FILE *f) {} @@ -301,14 +301,24 @@ HDEBUG(D_auth) ablock->name, ob->server_mech); #ifndef DISABLE_TLS +if (tls_in.channelbinding && ob->server_channelbinding) + { +# ifdef EXPERIMENTAL_TLS_RESUME + if (!tls_in.ext_master_secret && tls_in.resumption == RESUME_USED) + { /* per RFC 7677 section 4 */ + HDEBUG(D_auth) debug_printf( + "channel binding not usable on resumed TLS without extended-master-secret"); + return FAIL; + } +# endif # ifdef CHANNELBIND_HACK /* This is a gross hack to get around the library a) requiring that c-b was already set, at the _start() call, and b) caching a b64'd version of the binding then which it never updates. */ -if (tls_in.channelbinding && ob->server_channelbinding) gsasl_callback_hook_set(gsasl_ctx, tls_in.channelbinding); # endif + } #endif if ((rc = gsasl_server_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK) @@ -362,7 +372,7 @@ if (tls_in.channelbinding) { HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n", ablock->name); -# ifdef CHANNELBIND_HACK +# ifndef CHANNELBIND_HACK gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_in.channelbinding); # endif } @@ -720,7 +730,7 @@ return TRUE; int auth_gsasl_client( auth_instance *ablock, /* authenticator block */ - smtp_inblock * sx, /* connection */ + void * sx, /* connection */ int timeout, /* command timeout */ uschar *buffer, /* buffer for reading response */ int buffsize) /* size of buffer */ @@ -740,13 +750,24 @@ HDEBUG(D_auth) *buffer = 0; #ifndef DISABLE_TLS -/* This is a gross hack to get around the library a) requiring that -c-b was already set, at the _start() call, and b) caching a b64'd -version of the binding then which it never updates. */ +if (tls_out.channelbinding && ob->client_channelbinding) + { +# ifdef EXPERIMENTAL_TLS_RESUME + if (!tls_out.ext_master_secret && tls_out.resumption == RESUME_USED) + { /* per RFC 7677 section 4 */ + string_format(buffer, buffsize, "%s", + "channel binding not usable on resumed TLS without extended-master-secret"); + return FAIL; + } +# endif +# ifdef CHANNELBIND_HACK + /* This is a gross hack to get around the library a) requiring that + c-b was already set, at the _start() call, and b) caching a b64'd + version of the binding then which it never updates. */ -if (tls_out.channelbinding) - if (ob->client_channelbinding) - gsasl_callback_hook_set(gsasl_ctx, tls_out.channelbinding); + gsasl_callback_hook_set(gsasl_ctx, tls_out.channelbinding); +# endif + } #endif if ((rc = gsasl_client_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK) @@ -780,7 +801,7 @@ if (tls_out.channelbinding) { HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n", ablock->name); -# ifdef CHANNELBIND_HACK +# ifndef CHANNELBIND_HACK gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding); # endif } diff --git a/src/src/auths/gsasl_exim.h b/src/src/auths/gsasl_exim.h index 93d20784f..7afec7050 100644 --- a/src/src/auths/gsasl_exim.h +++ b/src/src/auths/gsasl_exim.h @@ -42,7 +42,7 @@ extern auth_gsasl_options_block auth_gsasl_option_defaults; extern void auth_gsasl_init(auth_instance *); extern int auth_gsasl_server(auth_instance *, uschar *); -extern int auth_gsasl_client(auth_instance *, smtp_inblock *, +extern int auth_gsasl_client(auth_instance *, void *, int, uschar *, int); extern void auth_gsasl_version_report(FILE *f); diff --git a/src/src/globals.h b/src/src/globals.h index 03a56f0f2..bb66cb239 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -111,6 +111,7 @@ typedef struct { BOOL ticket_received:1; #endif BOOL verify_override:1; /* certificate_verified only due to tls_try_verify_hosts */ + BOOL ext_master_secret:1; /* extended-master-secret was used */ } tls_support; extern tls_support tls_in; extern tls_support tls_out; diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 837b991df..69a8bd6f4 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -2529,6 +2529,9 @@ if (rc != GNUTLS_E_SUCCESS) return FAIL; } +if (gnutls_session_get_flags(state->session) & GNUTLS_SFLAGS_EXT_MASTER_SECRET) + tls_in.ext_master_secret = TRUE; + #ifdef EXPERIMENTAL_TLS_RESUME tls_server_resume_posthandshake(state); #endif @@ -2998,6 +3001,9 @@ if (!verify_certificate(state, errstr)) return FALSE; } +if (gnutls_session_get_flags(state->session) & GNUTLS_SFLAGS_EXT_MASTER_SECRET) + tlsp->ext_master_secret = TRUE; + #ifndef DISABLE_OCSP if (request_ocsp) { diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index bee5a4256..d16479e58 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -2784,6 +2784,7 @@ if (SSL_session_reused(server_ssl)) /* TLS has been set up. Record data for the connection, adjust the input functions to read via TLS, and initialize things. */ +tls_in.ext_master_secret = SSL_get_extms_support(server_ssl) == 1; peer_cert(server_ssl, &tls_in, peerdn, sizeof(peerdn)); tls_in.ver = tlsver_name(server_ssl); @@ -3384,6 +3385,7 @@ DEBUG(D_tls) tls_client_resume_posthandshake(exim_client_ctx, tlsp); #endif +tlsp->ext_master_secret = SSL_get_extms_support(exim_client_ctx->ssl) == 1; peer_cert(exim_client_ctx->ssl, tlsp, peerdn, sizeof(peerdn)); tlsp->ver = tlsver_name(exim_client_ctx->ssl); -- 2.30.2