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.
 
 
  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
 ------------
 
 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
 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
   {
   /* 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,
     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)
     }
   else
     HDEBUG(D_auth)
index 6c8191462a9c8cd63c9c88c744b8590c6b638315..aa46c2b32d7ae57b853df7bd7fc30b3805f10ad1 100644 (file)
@@ -242,9 +242,9 @@ static uschar *enc64table =
   US"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
 uschar *
   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)
 uschar *p = code;
 
 while (len-- >0)
@@ -283,6 +283,12 @@ while (len-- >0)
 return code;
 }
 
 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
 
 /* 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);
 #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 *);
 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 */
   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 */
   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 *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 */
 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.
 
 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 {
 */
 
 typedef struct exim_gnutls_state {
@@ -467,7 +467,7 @@ Sets:
   tls_active                fd
   tls_bits                  strength indicator
   tls_certificate_verified  bool indicator
   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
   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
 
 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. */
 
 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;
 #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
   {
   { 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;
   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;
   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
 
   }
 #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 */
 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);
 
 
 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;
   }
 
   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().
 /* 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;
   }
 
   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;
 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
 
 static BOOL ssl_xfer_error = FALSE;
 #endif
 
-uschar *tls_channelbinding_b64 = NULL;
-
 
 /*************************************************
 *       Expand string; give error on failure     *
 
 /*************************************************
 *       Expand string; give error on failure     *