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 9043685..1d4c39c 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
-by $%client_username%& option.
+by &%client_username%& option.
 If unset or (after expansion) empty it is not used,
 which is the common case.
 
index db14a40..f527e13 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;}
-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
     }
index 93d2078..7afec70 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 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);
 
index 03a56f0..bb66cb2 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   ext_master_secret:1;  /* extended-master-secret was used */
 } tls_support;
 extern tls_support tls_in;
 extern tls_support tls_out;
index 837b991..69a8bd6 100644 (file)
@@ -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)
   {
index bee5a42..d16479e 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_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);