Merge branch '4.next'
[exim.git] / src / src / tls-openssl.c
index bef3fb4f1652a27b0c726f7f4faf9328debbcb55..a236bc0c68c0028497f80157c75750c2d150b3b5 100644 (file)
@@ -2281,14 +2281,13 @@ Returns:    pointer to allocated string in perm-pool
 */
 
 static uschar *
-construct_cipher_name(SSL * ssl, int * bits)
+construct_cipher_name(SSL * ssl, const uschar * ver, int * bits)
 {
 int pool = store_pool;
 /* With OpenSSL 1.0.0a, 'c' needs to be const but the documentation doesn't
 yet reflect that.  It should be a safe change anyway, even 0.9.8 versions have
 the accessor functions use const in the prototype. */
 
-const uschar * ver = CUS SSL_get_version(ssl);
 const SSL_CIPHER * c = (const SSL_CIPHER *) SSL_get_current_cipher(ssl);
 uschar * s;
 
@@ -2319,6 +2318,21 @@ return cipher_stdname(id >> 8, id & 0xff);
 }
 
 
+static const uschar *
+tlsver_name(SSL * ssl)
+{
+uschar * s, * p;
+int pool = store_pool;
+
+store_pool = POOL_PERM;
+s = string_copy(US SSL_get_version(ssl));
+store_pool = pool;
+if ((p = Ustrchr(s, 'v')))     /* TLSv1.2 -> TLS1.2 */
+  for (;; p++) if (!(*p = p[1])) break;
+return CUS s;
+}
+
+
 static void
 peer_cert(SSL * ssl, tls_support * tlsp, uschar * peerdn, unsigned siz)
 {
@@ -2767,12 +2781,13 @@ if (SSL_session_reused(server_ssl))
   }
 #endif
 
-/* TLS has been set up. 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. */
 
 peer_cert(server_ssl, &tls_in, peerdn, sizeof(peerdn));
 
-tls_in.cipher = construct_cipher_name(server_ssl, &tls_in.bits);
+tls_in.ver = tlsver_name(server_ssl);
+tls_in.cipher = construct_cipher_name(server_ssl, tls_in.ver, &tls_in.bits);
 tls_in.cipher_stdname = cipher_stdname_ssl(server_ssl);
 
 DEBUG(D_tls)
@@ -2805,6 +2820,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().
@@ -3357,7 +3386,8 @@ tls_client_resume_posthandshake(exim_client_ctx, tlsp);
 
 peer_cert(exim_client_ctx->ssl, tlsp, peerdn, sizeof(peerdn));
 
-tlsp->cipher = construct_cipher_name(exim_client_ctx->ssl, &tlsp->bits);
+tlsp->ver = tlsver_name(exim_client_ctx->ssl);
+tlsp->cipher = construct_cipher_name(exim_client_ctx->ssl, tlsp->ver, &tlsp->bits);
 tlsp->cipher_stdname = cipher_stdname_ssl(exim_client_ctx->ssl);
 
 /* Record the certificate we presented */
@@ -3366,6 +3396,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;
@@ -3566,11 +3610,12 @@ Arguments:
 Returns:    the number of bytes after a successful write,
             -1 after a failed write
 
-Used by both server-side and client-side TLS.
+Used by both server-side and client-side TLS.  Calling with len zero and more unset
+will flush buffered writes; buff can be null for this case.
 */
 
 int
-tls_write(void * ct_ctx, const uschar *buff, size_t len, BOOL more)
+tls_write(void * ct_ctx, const uschar * buff, size_t len, BOOL more)
 {
 size_t olen = len;
 int outbytes, error;
@@ -3596,14 +3641,16 @@ a store reset there, so use POOL_PERM. */
 
 if ((more || corked))
   {
-#ifdef SUPPORT_PIPE_CONNECT
+  if (!len) buff = US &error;  /* dummy just so that string_catn is ok */
+
+#ifndef DISABLE_PIPE_CONNECT
   int save_pool = store_pool;
   store_pool = POOL_PERM;
 #endif
 
   corked = string_catn(corked, buff, len);
 
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
   store_pool = save_pool;
 #endif
 
@@ -3625,16 +3672,16 @@ for (int left = len; left > 0;)
   DEBUG(D_tls) debug_printf("outbytes=%d error=%d\n", outbytes, error);
   switch (error)
     {
+    case SSL_ERROR_NONE:       /* the usual case */
+      left -= outbytes;
+      buff += outbytes;
+      break;
+
     case SSL_ERROR_SSL:
       ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
       log_write(0, LOG_MAIN, "TLS error (SSL_write): %s", ssl_errstring);
       return -1;
 
-    case SSL_ERROR_NONE:
-      left -= outbytes;
-      buff += outbytes;
-      break;
-
     case SSL_ERROR_ZERO_RETURN:
       log_write(0, LOG_MAIN, "SSL channel closed on write");
       return -1;