Guard SNI usage better (client-side)
[exim.git] / src / src / tls-openssl.c
index 9ead7945d6d55e7898a09016a2debc802f5361eb..ebc5a62557a589c03ef6d2a1717ad756031042e3 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2009 */
+/* Copyright (c) University of Cambridge 1995 - 2012 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* This module provides the TLS (aka SSL) support for Exim using the OpenSSL
@@ -29,6 +29,10 @@ functions from the OpenSSL library. */
 #define EXIM_OCSP_MAX_AGE (-1L)
 #endif
 
+#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
+#define EXIM_HAVE_OPENSSL_TLSEXT
+#endif
+
 /* Structure for collecting random data for seeding. */
 
 typedef struct randstuff {
@@ -42,7 +46,9 @@ static BOOL verify_callback_called = FALSE;
 static const uschar *sid_ctx = US"exim";
 
 static SSL_CTX *ctx = NULL;
+#ifdef EXIM_HAVE_OPENSSL_TLSEXT
 static SSL_CTX *ctx_sni = NULL;
+#endif
 static SSL *ssl = NULL;
 
 static char ssl_errstring[256];
@@ -77,7 +83,9 @@ static int
 setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, BOOL optional);
 
 /* Callbacks */
+#ifdef EXIM_HAVE_OPENSSL_TLSEXT
 static int tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg);
+#endif
 #ifdef EXPERIMENTAL_OCSP
 static int tls_stapling_cb(SSL *s, void *arg);
 #endif
@@ -302,10 +310,19 @@ else
     }
   else
     {
-    SSL_CTX_set_tmp_dh(ctx, dh);
-    DEBUG(D_tls)
-      debug_printf("Diffie-Hellman initialized from %s with %d-bit key\n",
-        dhexpanded, 8*DH_size(dh));
+    if ((8*DH_size(dh)) > tls_dh_max_bits)
+      {
+      DEBUG(D_tls)
+        debug_printf("dhparams file %d bits, is > tls_dh_max_bits limit of %d",
+            8*DH_size(dh), tls_dh_max_bits);
+      }
+    else
+      {
+      SSL_CTX_set_tmp_dh(ctx, dh);
+      DEBUG(D_tls)
+        debug_printf("Diffie-Hellman initialized from %s with %d-bit key\n",
+          dhexpanded, 8*DH_size(dh));
+      }
     DH_free(dh);
     }
   BIO_free(bio);
@@ -540,6 +557,7 @@ Arguments:
 Returns:          SSL_TLSEXT_ERR_{OK,ALERT_WARNING,ALERT_FATAL,NOACK}
 */
 
+#ifdef EXIM_HAVE_OPENSSL_TLSEXT
 static int
 tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg)
 {
@@ -606,6 +624,7 @@ SSL_set_SSL_CTX(s, ctx_sni);
 
 return SSL_TLSEXT_ERR_OK;
 }
+#endif /* EXIM_HAVE_OPENSSL_TLSEXT */
 
 
 
@@ -768,7 +787,7 @@ rc = tls_expand_session_files(ctx, cbinfo);
 if (rc != OK) return rc;
 
 /* If we need to handle SNI, do so */
-#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
+#ifdef EXIM_HAVE_OPENSSL_TLSEXT
 if (host == NULL)
   {
 #ifdef EXPERIMENTAL_OCSP
@@ -1006,11 +1025,6 @@ a TLS session.
 
 Arguments:
   require_ciphers   allowed ciphers
-  ------------------------------------------------------
-  require_mac      list of allowed MACs                 ) Not used
-  require_kx       list of allowed key_exchange methods )   for
-  require_proto    list of allowed protocols            ) OpenSSL
-  ------------------------------------------------------
 
 Returns:            OK on success
                     DEFER for errors before the start of the negotiation
@@ -1019,8 +1033,7 @@ Returns:            OK on success
 */
 
 int
-tls_server_start(uschar *require_ciphers, uschar *require_mac,
-  uschar *require_kx, uschar *require_proto)
+tls_server_start(const uschar *require_ciphers)
 {
 int rc;
 uschar *expciphers;
@@ -1050,8 +1063,9 @@ if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers))
   return FAIL;
 
 /* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they
-are separated by underscores. So that I can use either form in my tests, and
-also for general convenience, we turn underscores into hyphens here. */
+were historically separated by underscores. So that I can use either form in my
+tests, and also for general convenience, we turn underscores into hyphens here.
+*/
 
 if (expciphers != NULL)
   {
@@ -1185,11 +1199,6 @@ Argument:
   verify_certs     file for certificate verify
   crl              file containing CRL
   require_ciphers  list of allowed ciphers
-  ------------------------------------------------------
-  require_mac      list of allowed MACs                 ) Not used
-  require_kx       list of allowed key_exchange methods )   for
-  require_proto    list of allowed protocols            ) OpenSSL
-  ------------------------------------------------------
   timeout          startup timeout
 
 Returns:           OK on success
@@ -1201,8 +1210,7 @@ int
 tls_client_start(int fd, host_item *host, address_item *addr, uschar *dhparam,
   uschar *certificate, uschar *privatekey, uschar *sni,
   uschar *verify_certs, uschar *crl,
-  uschar *require_ciphers, uschar *require_mac, uschar *require_kx,
-  uschar *require_proto, int timeout)
+  uschar *require_ciphers, int timeout)
 {
 static uschar txt[256];
 uschar *expciphers;
@@ -1251,8 +1259,14 @@ if (sni)
     tls_sni = NULL;
   else
     {
+#ifdef EXIM_HAVE_OPENSSL_TLSEXT
     DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tls_sni);
     SSL_set_tlsext_host_name(ssl, tls_sni);
+#else
+    DEBUG(D_tls)
+      debug_printf("OpenSSL at build-time lacked SNI support, ignoring \"%s\"\n",
+          tls_sni);
+#endif
     }
   }
 
@@ -1500,6 +1514,72 @@ tls_active = -1;
 
 
 
+/*************************************************
+*  Let tls_require_ciphers be checked at startup *
+*************************************************/
+
+/* The tls_require_ciphers option, if set, must be something which the
+library can parse.
+
+Returns:     NULL on success, or error message
+*/
+
+uschar *
+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 */
+
+SSL_load_error_strings();
+OpenSSL_add_ssl_algorithms();
+#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
+
+if (!(tls_require_ciphers && *tls_require_ciphers))
+  return NULL;
+
+if (!expand_check(tls_require_ciphers, US"tls_require_ciphers", &expciphers))
+  return US"failed to expand tls_require_ciphers";
+
+if (!(expciphers && *expciphers))
+  return NULL;
+
+/* normalisation ripped from above */
+s = expciphers;
+while (*s != 0) { if (*s == '_') *s = '-'; s++; }
+
+err = NULL;
+
+ctx = SSL_CTX_new(SSLv23_server_method());
+if (!ctx)
+  {
+  ERR_error_string(ERR_get_error(), ssl_errstring);
+  return string_sprintf("SSL_CTX_new() failed: %s", ssl_errstring);
+  }
+
+DEBUG(D_tls)
+  debug_printf("tls_require_ciphers expands to \"%s\"\n", expciphers);
+
+if (!SSL_CTX_set_cipher_list(ctx, CS expciphers))
+  {
+  ERR_error_string(ERR_get_error(), ssl_errstring);
+  err = string_sprintf("SSL_CTX_set_cipher_list(%s) failed", expciphers);
+  }
+
+SSL_CTX_free(ctx);
+
+return err;
+}
+
+
+
+
 /*************************************************
 *         Report the library versions.           *
 *************************************************/
@@ -1527,7 +1607,7 @@ fprintf(f, "Library version: OpenSSL: Compile: %s\n"
 
 
 /*************************************************
-*        Pseudo-random number generation         *
+*            Random number generation            *
 *************************************************/
 
 /* Pseudo-random number generation.  The result is not expected to be
@@ -1542,7 +1622,7 @@ Returns     a random number in range [0, max-1]
 */
 
 int
-pseudo_random_number(int max)
+vaguely_random_number(int max)
 {
 unsigned int r;
 int i, needed_len;
@@ -1578,7 +1658,14 @@ if (i < needed_len)
   needed_len = i;
 
 /* We do not care if crypto-strong */
-(void) RAND_pseudo_bytes(smallbuf, needed_len);
+i = RAND_pseudo_bytes(smallbuf, needed_len);
+if (i < 0)
+  {
+  DEBUG(D_all)
+    debug_printf("OpenSSL RAND_pseudo_bytes() not supported by RAND method, using fallback.\n");
+  return vaguely_random_number_fallback(max);
+  }
+
 r = 0;
 for (p = smallbuf; needed_len; --needed_len, ++p)
   {
@@ -1755,7 +1842,7 @@ uschar keep_c;
 BOOL adding, item_parsed;
 
 result = 0L;
-/* Prior to 4.78 we or'd in SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; removed
+/* Prior to 4.80 we or'd in SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; removed
  * from default because it increases BEAST susceptibility. */
 
 if (option_spec == NULL)