GSASL channel-binding: TLS resumption checks
authorJeremy Harris <jgh146exb@wizmail.org>
Sat, 28 Dec 2019 17:00:30 +0000 (17:00 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Sat, 28 Dec 2019 17:04:18 +0000 (17:04 +0000)
doc/doc-docbook/spec.xfpt
src/src/auths/gsasl_exim.c
src/src/auths/gsasl_exim.h
src/src/globals.h
src/src/tls-gnu.c
src/src/tls-openssl.c

index 9043685c55e44a3b1cb7e5c6c538e99b57ed9ed5..1d4c39c6d6a898b9e71ea616826b45c028fbdc55 100644 (file)
@@ -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
 .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.
 
 If unset or (after expansion) empty it is not used,
 which is the common case.
 
index db14a40e0ad2fe6b8117a6d23034d760f58909d9..f527e130af9bc065befa4f8f5e200f69efe69d17 100644 (file)
@@ -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;}
 /* 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) {}
 
   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
       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. */
 
 # 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
   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)
 #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);
     {
     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
     }
     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 */
 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 */
   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
 *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)
 #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);
     {
     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
     }
     gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
 # endif
     }
index 93d20784fa364248bdd50a00ef4c3ef952cdd6ac..7afec7050c067dc018c173099f9083cf859d32ec 100644 (file)
@@ -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 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);
 
                                int, uschar *, int);
 extern void auth_gsasl_version_report(FILE *f);
 
index 03a56f0f2a36e8df766ecfbc1406a8687d7af275..bb66cb2392adf2f79a180368067c08133c094fd0 100644 (file)
@@ -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   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;
 } tls_support;
 extern tls_support tls_in;
 extern tls_support tls_out;
index 837b991df47e906092f51ef9af9845df962ab046..69a8bd6f41fb510106e42c60b57b7f2c202d21ef 100644 (file)
@@ -2529,6 +2529,9 @@ if (rc != GNUTLS_E_SUCCESS)
   return FAIL;
   }
 
   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
 #ifdef EXPERIMENTAL_TLS_RESUME
 tls_server_resume_posthandshake(state);
 #endif
@@ -2998,6 +3001,9 @@ if (!verify_certificate(state, errstr))
   return FALSE;
   }
 
   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)
   {
 #ifndef DISABLE_OCSP
 if (request_ocsp)
   {
index bee5a4256e7ff57b11388c7b95e7a922ff678bd1..d16479e5806a3627a0b611cbdab5d748628d48f0 100644 (file)
@@ -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 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);
 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
 
 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);
 peer_cert(exim_client_ctx->ssl, tlsp, peerdn, sizeof(peerdn));
 
 tlsp->ver = tlsver_name(exim_client_ctx->ssl);