OpenSSL: support authenticator channel-binding. Bug 2467
authorJeremy Harris <jgh146exb@wizmail.org>
Sun, 17 Nov 2019 19:30:42 +0000 (19:30 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Sun, 17 Nov 2019 21:23:05 +0000 (21:23 +0000)
doc/doc-txt/NewStuff
src/src/auths/gsasl_exim.c
src/src/base64.c
src/src/functions.h
src/src/globals.h
src/src/tls-gnu.c
src/src/tls-openssl.c
src/src/tls.c

index fbd1a5e4e5be9949c4aa55b2c13df0ea965d3a8b..18c3d30243f2d365e39b82a371474f45b8cc58d7 100644 (file)
@@ -14,6 +14,9 @@ Version 4.next
 
  2. Variables $tls_in_ver, $tls_out_ver.
 
+ 3. Channel-binding for authenticators is now supported under OpenSSL.
+    Previously it was GnuTLS-only.
+
 
 Version 4.93
 ------------
index 06c91ea3f41840e00f195639038f05ad5fab12a6..78a63cd0ee4f60538dcc02bac8691c52ce531b0c 100644 (file)
@@ -292,7 +292,7 @@ if (ob->server_realm)
 gsasl_property_set(sctx, GSASL_QOPS, "qop-auth");
 
 #ifndef DISABLE_TLS
-if (tls_channelbinding_b64)
+if (tls_in.channelbinding)
   {
   /* Some auth mechanisms can ensure that both sides are talking withing the
   same security context; for TLS, this means that even if a bad certificate
@@ -317,7 +317,7 @@ if (tls_channelbinding_b64)
     HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
        ablock->name);
     gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE,
-       CCS  tls_channelbinding_b64);
+       CCS  tls_in.channelbinding);
     }
   else
     HDEBUG(D_auth)
index 6c8191462a9c8cd63c9c88c744b8590c6b638315..aa46c2b32d7ae57b853df7bd7fc30b3805f10ad1 100644 (file)
@@ -242,9 +242,9 @@ static uschar *enc64table =
   US"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
 uschar *
-b64encode(const uschar * clear, int len)
+b64encode_taint(const uschar * clear, int len, BOOL tainted)
 {
-uschar *code = store_get(4*((len+2)/3) + 1, is_tainted(clear));
+uschar *code = store_get(4*((len+2)/3) + 1, tainted);
 uschar *p = code;
 
 while (len-- >0)
@@ -283,6 +283,12 @@ while (len-- >0)
 return code;
 }
 
+uschar *
+b64encode(const uschar * clear, int len)
+{
+return b64encode_taint(clear, len, is_tainted(clear));
+}
+
 
 /* End of base64.c */
 /* vi: sw ai sw=2
index 187bdafa6947ee2502ed8e0c933c68c8bb0f38c6..da21b87795be02b3a5c761e74b33d78092eb1d8e 100644 (file)
@@ -136,6 +136,7 @@ extern gstring *authres_spf(gstring *);
 #endif
 
 extern uschar *b64encode(const uschar *, int);
+extern uschar *b64encode_taint(const uschar *, int, BOOL);
 extern int     b64decode(const uschar *, uschar **);
 extern int     bdat_getc(unsigned);
 extern uschar *bdat_getbuf(unsigned *);
index 1754d3e89594995ccbf35f00277c301c7cc97108..8a3d4c56f077ec4013c474326695841380aecae1 100644 (file)
@@ -97,6 +97,7 @@ typedef struct {
   void  *peercert;           /* Certificate of peer, binary */
   uschar *peerdn;             /* DN from peer */
   uschar *sni;                /* Server Name Indication */
+  uschar *channelbinding;     /* b64'd data identifying channel, for authenticators */
   enum {
     OCSP_NOT_REQ=0,            /* not requested */
     OCSP_NOT_RESP,             /* no response to request */
@@ -120,7 +121,6 @@ extern BOOL    gnutls_allow_auto_pkcs11; /* Let GnuTLS autoload PKCS11 modules *
 extern uschar *openssl_options;        /* OpenSSL compatibility options */
 extern const pcre *regex_STARTTLS;     /* For recognizing STARTTLS settings */
 extern uschar *tls_certificate;        /* Certificate file */
-extern uschar *tls_channelbinding_b64; /* string of base64 channel binding */
 extern uschar *tls_crl;                /* CRL File */
 extern int     tls_dh_max_bits;        /* don't accept higher lib suggestions */
 extern uschar *tls_dhparam;            /* DH param file */
index 7d7f61dd888e8e46937403d5499fb56170d8eae3..f3c3835fecd6b6a57d773d3933ac717ccf8a67f4 100644 (file)
@@ -155,7 +155,7 @@ Some of these correspond to variables in globals.c; those variables will
 be set to point to content in one of these instances, as appropriate for
 the stage of the process lifetime.
 
-Not handled here: global tls_channelbinding_b64.
+Not handled here: global tlsp->tls_channelbinding.
 */
 
 typedef struct exim_gnutls_state {
@@ -467,7 +467,7 @@ Sets:
   tls_active                fd
   tls_bits                  strength indicator
   tls_certificate_verified  bool indicator
-  tls_channelbinding_b64    for some SASL mechanisms
+  tls_channelbinding        for some SASL mechanisms
   tls_ver                   a string
   tls_cipher                a string
   tls_peercert              pointer to library internal
@@ -499,10 +499,10 @@ tlsp->certificate_verified = state->peer_cert_verified;
 tlsp->dane_verified = state->peer_dane_verified;
 #endif
 
-/* note that tls_channelbinding_b64 is not saved to the spool file, since it's
+/* note that tls_channelbinding is not saved to the spool file, since it's
 only available for use for authenticators while this TLS session is running. */
 
-tls_channelbinding_b64 = NULL;
+tlsp->channelbinding = NULL;
 #ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING
 channel.data = NULL;
 channel.size = 0;
@@ -510,11 +510,15 @@ if ((rc = gnutls_session_channel_binding(state->session, GNUTLS_CB_TLS_UNIQUE, &
   { DEBUG(D_tls) debug_printf("Channel binding error: %s\n", gnutls_strerror(rc)); }
 else
   {
+  /* Declare the taintedness of the binding info.  On server, untainted; on
+  client, tainted - being the Finish msg from the server. */
+
   old_pool = store_pool;
   store_pool = POOL_PERM;
-  tls_channelbinding_b64 = b64encode(CUS channel.data, (int)channel.size);
+  tlsp->channelbinding = b64encode_taint(CUS channel.data, (int)channel.size,
+                                         !!state->host);
   store_pool = old_pool;
-  DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage.\n");
+  DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage\n");
   }
 #endif
 
@@ -3093,7 +3097,7 @@ gnutls_certificate_free_credentials(state->x509_cred);
 tlsp->active.sock = -1;
 tlsp->active.tls_ctx = NULL;
 /* Leave bits, peercert, cipher, peerdn, certificate_verified set, for logging */
-tls_channelbinding_b64 = NULL;
+tlsp->channelbinding = NULL;
 
 
 if (state->xfer_buffer) store_free(state->xfer_buffer);
index 7a82e1d554176ca1f8adb03c4c15348aefdd208f..5ea4d964e8b02a39d809c4f65a516d1145d7eb26 100644 (file)
@@ -2741,6 +2741,20 @@ DEBUG(D_tls)
   tls_in.ourcert = crt ? X509_dup(crt) : NULL;
   }
 
+/* Channel-binding info for authenticators
+See description in https://paquier.xyz/postgresql-2/channel-binding-openssl/ */
+  {
+  uschar c, * s;
+  size_t len = SSL_get_peer_finished(server_ssl, &c, 0);
+  int old_pool = store_pool;
+  
+  SSL_get_peer_finished(server_ssl, s = store_get((int)len, FALSE), len);
+  store_pool = POOL_PERM;
+    tls_in.channelbinding = b64encode_taint(CUS s, (int)len, FALSE);
+  store_pool = old_pool;
+  DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage\n");
+  }
+
 /* Only used by the server-side tls (tls_in), including tls_getc.
    Client-side (tls_out) reads (seem to?) go via
    smtp_read_response()/ip_recv().
@@ -3303,6 +3317,20 @@ tlsp->cipher_stdname = cipher_stdname_ssl(exim_client_ctx->ssl);
   tlsp->ourcert = crt ? X509_dup(crt) : NULL;
   }
 
+/*XXX will this work with continued-TLS? */
+/* Channel-binding info for authenticators */
+  {
+  uschar c, * s;
+  size_t len = SSL_get_finished(exim_client_ctx->ssl, &c, 0);
+  int old_pool = store_pool;
+  
+  SSL_get_finished(exim_client_ctx->ssl, s = store_get((int)len, TRUE), len);
+  store_pool = POOL_PERM;
+    tlsp->channelbinding = b64encode_taint(CUS s, (int)len, TRUE);
+  store_pool = old_pool;
+  DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage\n");
+  }
+
 tlsp->active.sock = cctx->sock;
 tlsp->active.tls_ctx = exim_client_ctx;
 cctx->tls_ctx = exim_client_ctx;
index 531d679503c2a0bea50187b9c87c079b3da13383..d47156cdc5ea555778ce3a5e4ea15378a775aa57 100644 (file)
@@ -61,8 +61,6 @@ static int ssl_xfer_eof = FALSE;
 static BOOL ssl_xfer_error = FALSE;
 #endif
 
-uschar *tls_channelbinding_b64 = NULL;
-
 
 /*************************************************
 *       Expand string; give error on failure     *