GnuTLS: fix $tls_X_ver generation
[exim.git] / src / src / tls-openssl.c
index 057a0e006cdb3ac1cc8a0172d03b7ad93109e0e7..7a82e1d554176ca1f8adb03c4c15348aefdd208f 100644 (file)
@@ -46,6 +46,7 @@ functions from the OpenSSL library. */
 #endif
 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
 # define EXIM_HAVE_OCSP_RESP_COUNT
+# define OPENSSL_AUTO_SHA256
 #else
 # define EXIM_HAVE_EPHEM_RSA_KEX
 # define EXIM_HAVE_RAND_PSEUDO
@@ -74,6 +75,7 @@ change this guard and punt the issue for a while longer. */
 #  define EXIM_HAVE_OPENSSL_CIPHER_GET_ID
 #  define EXIM_HAVE_SESSION_TICKET
 #  define EXIM_HAVE_OPESSL_TRACE
+#  define EXIM_HAVE_OPESSL_GET0_SERIAL
 # else
 #  define EXIM_NEED_OPENSSL_INIT
 # endif
@@ -1714,6 +1716,7 @@ tls_in.ocsp = OCSP_NOT_RESP;
 if (!olist)
   return SSL_TLSEXT_ERR_NOACK;
 
+#ifdef EXIM_HAVE_OPESSL_GET0_SERIAL
  {
   const X509 * cert_sent = SSL_get_certificate(s);
   const ASN1_INTEGER * cert_serial = X509_get0_serialNumber(cert_sent);
@@ -1761,6 +1764,13 @@ if (!olist)
     return SSL_TLSEXT_ERR_NOACK;
     }
  }
+#else
+if (olist->next)
+  {
+  DEBUG(D_tls) debug_printf("OpenSSL version too early to support multi-leaf OCSP\n");
+  return SSL_TLSEXT_ERR_NOACK;
+  }
+#endif
 
 /*XXX could we do the i2d earlier, rather than during the callback? */
 response_der = NULL;
@@ -1936,6 +1946,23 @@ return i;
 *            Initialize for TLS                  *
 *************************************************/
 
+static void
+tls_openssl_init(void)
+{
+#ifdef EXIM_NEED_OPENSSL_INIT
+SSL_load_error_strings();          /* basic set up */
+OpenSSL_add_ssl_algorithms();
+#endif
+
+#if defined(EXIM_HAVE_SHA256) && !defined(OPENSSL_AUTO_SHA256)
+/* SHA256 is becoming ever more popular. This makes sure it gets added to the
+list of available digests. */
+EVP_add_digest(EVP_sha256());
+#endif
+}
+
+
+
 /* Called from both server and client code, to do preliminary initialization
 of the library.  We allocate and return a context structure.
 
@@ -1991,16 +2018,7 @@ cbinfo->host = host;
 cbinfo->event_action = NULL;
 #endif
 
-#ifdef EXIM_NEED_OPENSSL_INIT
-SSL_load_error_strings();          /* basic set up */
-OpenSSL_add_ssl_algorithms();
-#endif
-
-#ifdef EXIM_HAVE_SHA256
-/* SHA256 is becoming ever more popular. This makes sure it gets added to the
-list of available digests. */
-EVP_add_digest(EVP_sha256());
-#endif
+tls_openssl_init();
 
 /* Create a context.
 The OpenSSL docs in 1.0.1b have not been updated to clarify TLS variant
@@ -2194,14 +2212,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;
 
@@ -2232,6 +2249,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)
 {
@@ -2621,8 +2653,41 @@ ALARM_CLR(0);
 
 if (rc <= 0)
   {
-  (void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : NULL, errstr);
-  return FAIL;
+  int error = SSL_get_error(server_ssl, rc);
+  switch(error)
+    {
+    case SSL_ERROR_NONE:
+      break;
+
+    case SSL_ERROR_ZERO_RETURN:
+      DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n");
+      (void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : NULL, errstr);
+
+      if (SSL_get_shutdown(server_ssl) == SSL_RECEIVED_SHUTDOWN)
+           SSL_shutdown(server_ssl);
+
+      tls_close(NULL, TLS_NO_SHUTDOWN);
+      return FAIL;
+
+    /* Handle genuine errors */
+    case SSL_ERROR_SSL:
+      (void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : NULL, errstr);
+      return FAIL;
+
+    default:
+      DEBUG(D_tls) debug_printf("Got SSL error %d\n", error);
+      if (error == SSL_ERROR_SYSCALL)
+       {
+       if (!errno)
+         {
+         *errstr = US"SSL_accept: TCP connection closed by peer";
+         return FAIL;
+         }
+       DEBUG(D_tls) debug_printf(" - syscall %s\n", strerror(errno));
+       }
+      (void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : NULL, errstr);
+      return FAIL;
+    }
   }
 
 DEBUG(D_tls) debug_printf("SSL_accept was successful\n");
@@ -2637,12 +2702,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)
@@ -3227,7 +3293,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 */
@@ -3466,14 +3533,14 @@ a store reset there, so use POOL_PERM. */
 
 if ((more || corked))
   {
-#ifdef SUPPORT_PIPE_CONNECT
+#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
 
@@ -3619,18 +3686,7 @@ tls_validate_require_cipher(void)
 SSL_CTX *ctx;
 uschar *s, *expciphers, *err;
 
-/* this duplicates from tls_init(), we need a better "init just global
-state, for no specific purpose" singleton function of our own */
-
-#ifdef EXIM_NEED_OPENSSL_INIT
-SSL_load_error_strings();
-OpenSSL_add_ssl_algorithms();
-#endif
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
-/* SHA256 is becoming ever more popular. This makes sure it gets added to the
-list of available digests. */
-EVP_add_digest(EVP_sha256());
-#endif
+tls_openssl_init();
 
 if (!(tls_require_ciphers && *tls_require_ciphers))
   return NULL;