CVE-2020-28018: Use-after-free in tls-openssl.c
[exim.git] / src / src / tls-openssl.c
index c97dc1bff2f48a19d4bd1ebe7ce3c85c9d68db03..499384b50e34c6e6159c79a9858abe8bcc4bb133 100644 (file)
@@ -3,6 +3,7 @@
 *************************************************/
 
 /* Copyright (c) University of Cambridge 1995 - 2019 */
+/* Copyright (c) The Exim Maintainers 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Portions Copyright (c) The OpenSSL Project 1999 */
@@ -148,13 +149,10 @@ all options unless explicitly for DTLS, let the administrator choose which
 to apply.
 
 This list is current as of:
-  ==>  1.0.1b  <==
-Plus SSL_OP_SAFARI_ECDHE_ECDSA_BUG from 2013-June patch/discussion on openssl-dev
-Plus SSL_OP_NO_TLSv1_3 for 1.1.2-dev
-Plus SSL_OP_NO_RENEGOTIATION for 1.1.1
+  ==>  1.1.1c  <==
 
 XXX could we autobuild this list, as with predefined-macros?
-Seems just parsing ssl.h for SSL_OP_.* would be enough.
+Seems just parsing ssl.h for SSL_OP_.* would be enough (except to exclude DTLS).
 Also allow a numeric literal?
 */
 static exim_openssl_option exim_openssl_options[] = {
@@ -162,15 +160,24 @@ static exim_openssl_option exim_openssl_options[] = {
 #ifdef SSL_OP_ALL
   { US"all", (long) SSL_OP_ALL },
 #endif
+#ifdef SSL_OP_ALLOW_NO_DHE_KEX
+  { US"allow_no_dhe_kex", SSL_OP_ALLOW_NO_DHE_KEX },
+#endif
 #ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
   { US"allow_unsafe_legacy_renegotiation", SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION },
 #endif
 #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
   { US"cipher_server_preference", SSL_OP_CIPHER_SERVER_PREFERENCE },
 #endif
+#ifdef SSL_OP_CRYPTOPRO_TLSEXT_BUG
+  { US"cryptopro_tlsext_bug", SSL_OP_CRYPTOPRO_TLSEXT_BUG },
+#endif
 #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
   { US"dont_insert_empty_fragments", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS },
 #endif
+#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT
+  { US"enable_middlebox_compat", SSL_OP_ENABLE_MIDDLEBOX_COMPAT },
+#endif
 #ifdef SSL_OP_EPHEMERAL_RSA
   { US"ephemeral_rsa", SSL_OP_EPHEMERAL_RSA },
 #endif
@@ -192,9 +199,15 @@ static exim_openssl_option exim_openssl_options[] = {
 #ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
   { US"netscape_reuse_cipher_change_bug", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG },
 #endif
+#ifdef SSL_OP_NO_ANTI_REPLAY
+  { US"no_anti_replay", SSL_OP_NO_ANTI_REPLAY },
+#endif
 #ifdef SSL_OP_NO_COMPRESSION
   { US"no_compression", SSL_OP_NO_COMPRESSION },
 #endif
+#ifdef SSL_OP_NO_ENCRYPT_THEN_MAC
+  { US"no_encrypt_then_mac", SSL_OP_NO_ENCRYPT_THEN_MAC },
+#endif
 #ifdef SSL_OP_NO_RENEGOTIATION
   { US"no_renegotiation", SSL_OP_NO_RENEGOTIATION },
 #endif
@@ -227,6 +240,9 @@ static exim_openssl_option exim_openssl_options[] = {
 #ifdef SSL_OP_NO_TLSv1_3
   { US"no_tlsv1_3", SSL_OP_NO_TLSv1_3 },
 #endif
+#ifdef SSL_OP_PRIORITIZE_CHACHA
+  { US"prioritize_chacha", SSL_OP_PRIORITIZE_CHACHA },
+#endif
 #ifdef SSL_OP_SAFARI_ECDHE_ECDSA_BUG
   { US"safari_ecdhe_ecdsa_bug", SSL_OP_SAFARI_ECDHE_ECDSA_BUG },
 #endif
@@ -251,6 +267,9 @@ static exim_openssl_option exim_openssl_options[] = {
 #ifdef SSL_OP_TLS_ROLLBACK_BUG
   { US"tls_rollback_bug", SSL_OP_TLS_ROLLBACK_BUG },
 #endif
+#ifdef SSL_OP_TLSEXT_PADDING
+  { US"tlsext_padding", SSL_OP_TLSEXT_PADDING },
+#endif
 };
 
 #ifndef MACRO_PREDEF
@@ -353,10 +372,10 @@ typedef struct ocsp_resp {
 } ocsp_resplist;
 
 typedef struct tls_ext_ctx_cb {
-  tls_support * tlsp;
-  uschar *certificate;
-  uschar *privatekey;
-  BOOL is_server;
+  tls_support *        tlsp;
+  uschar *     certificate;
+  uschar *     privatekey;
+  BOOL         is_server;
 #ifndef DISABLE_OCSP
   STACK_OF(X509) *verify_stack;                /* chain for verifying the proof */
   union {
@@ -371,14 +390,14 @@ typedef struct tls_ext_ctx_cb {
     } client;
   } u_ocsp;
 #endif
-  uschar *dhparam;
+  uschar *     dhparam;
   /* these are cached from first expand */
-  uschar *server_cipher_list;
+  uschar *     server_cipher_list;
   /* only passed down to tls_error: */
-  host_item *host;
+  host_item *  host;
   const uschar * verify_cert_hostnames;
 #ifndef DISABLE_EVENT
-  uschar * event_action;
+  uschar *     event_action;
 #endif
 } tls_ext_ctx_cb;
 
@@ -2480,7 +2499,7 @@ This is inconsistent with the need to verify the OCSP proof of the server cert.
 #endif
        }
 
-      /* If a certificate file is empty, the next function fails with an
+      /* If a certificate file is empty, the load function fails with an
       unhelpful error message. If we skip it, we get the correct behaviour (no
       certificates are recognized, but the error message is still misleading (it
       says no certificate was supplied).  But this is better. */
@@ -2489,9 +2508,9 @@ This is inconsistent with the need to verify the OCSP proof of the server cert.
          && !SSL_CTX_load_verify_locations(sctx, CS file, CS dir))
        return tls_error(US"SSL_CTX_load_verify_locations", host, NULL, errstr);
 
-      /* Load the list of CAs for which we will accept certs, for sending
-      to the client.  This is only for the one-file tls_verify_certificates
-      variant.
+      /* On the server load the list of CAs for which we will accept certs, for
+      sending to the client.  This is only for the one-file
+      tls_verify_certificates variant.
       If a list isn't loaded into the server, but some verify locations are set,
       the server end appears to make a wildcard request for client certs.
       Meanwhile, the client library as default behaviour *ignores* the list
@@ -2503,7 +2522,7 @@ This is inconsistent with the need to verify the OCSP proof of the server cert.
        {
        STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file);
 
-       SSL_CTX_set_client_CA_list(sctx, names);
+       if (!host) SSL_CTX_set_client_CA_list(sctx, names);
        DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n",
                                    sk_X509_NAME_num(names));
        }
@@ -2746,8 +2765,12 @@ if (rc <= 0)
     case SSL_ERROR_SSL:
       {
       uschar * s = US"SSL_accept";
-      unsigned long e = ERR_peek_error();
-      if (ERR_GET_REASON(e) == SSL_R_WRONG_VERSION_NUMBER)
+      int r = ERR_GET_REASON(ERR_peek_error());
+      if (  r == SSL_R_WRONG_VERSION_NUMBER
+#ifdef SSL_R_VERSION_TOO_LOW
+         || r == SSL_R_VERSION_TOO_LOW
+#endif
+         || r == SSL_R_UNKNOWN_PROTOCOL || r == SSL_R_UNSUPPORTED_PROTOCOL)
        s = string_sprintf("%s (%s)", s, SSL_get_version(server_ssl));
       (void) tls_error(s, NULL, sigalrm_seen ? US"timed out" : NULL, errstr);
       return FAIL;
@@ -2829,7 +2852,7 @@ 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);
@@ -2892,9 +2915,9 @@ if (verify_check_given_host(CUSS &ob->tls_verify_cert_hostnames, host) == OK)
   {
   cbinfo->verify_cert_hostnames =
 #ifdef SUPPORT_I18N
-    string_domain_utf8_to_alabel(host->name, NULL);
+    string_domain_utf8_to_alabel(host->certname, NULL);
 #else
-    host->name;
+    host->certname;
 #endif
   DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n",
                    cbinfo->verify_cert_hostnames);
@@ -2976,7 +2999,7 @@ if (tlsp->host_resumable)
 
   tlsp->resumption |= RESUME_CLIENT_REQUESTED;
   DEBUG(D_tls) debug_printf("checking for resumable session for %s\n", key);
-  if ((dbm_file = dbfn_open(US"tls", O_RDONLY, &dbblock, FALSE, FALSE)))
+  if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
     {
     /* key for the db is the IP */
     if ((dt = dbfn_read_with_length(dbm_file, key, &len)))
@@ -3174,6 +3197,7 @@ tlsp->tlsa_usage = 0;
 #ifndef DISABLE_OCSP
   {
 # ifdef SUPPORT_DANE
+  /*XXX this should be moved to caller, to be common across gnutls/openssl */
   if (  conn_args->dane
      && ob->hosts_request_ocsp[0] == '*'
      && ob->hosts_request_ocsp[1] == '\0'
@@ -3408,7 +3432,7 @@ tlsp->cipher_stdname = cipher_stdname_ssl(exim_client_ctx->ssl);
   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);
@@ -3435,6 +3459,7 @@ int inbytes;
 DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", server_ssl,
   ssl_xfer_buffer, ssl_xfer_buffer_size);
 
+ERR_clear_error();
 if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout);
 inbytes = SSL_read(server_ssl, CS ssl_xfer_buffer,
                  MIN(ssl_xfer_buffer_size, lim));
@@ -3584,6 +3609,7 @@ int error;
 DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", ssl,
   buff, (unsigned int)len);
 
+ERR_clear_error();
 inbytes = SSL_read(ssl, CS buff, len);
 error = SSL_get_error(ssl, inbytes);
 
@@ -3649,16 +3675,12 @@ if ((more || corked))
   {
   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);
 
-#ifndef DISABLE_PIPE_CONNECT
   store_pool = save_pool;
-#endif
 
   if (more)
     {
@@ -3673,6 +3695,7 @@ if ((more || corked))
 for (int left = len; left > 0;)
   {
   DEBUG(D_tls) debug_printf("SSL_write(%p, %p, %d)\n", ssl, buff, left);
+  ERR_clear_error();
   outbytes = SSL_write(ssl, CS buff, left);
   error = SSL_get_error(ssl, outbytes);
   DEBUG(D_tls) debug_printf("outbytes=%d error=%d\n", outbytes, error);