OpenSSL fixes and backwards compat break.
[exim.git] / src / src / tls-openssl.c
index 78b28f5e84cabb9fe6210c92e68f235d4458d0d0..5e8c804e5789be729b892f0c6f8e5d7f4389104e 100644 (file)
@@ -1,5 +1,3 @@
-/* $Cambridge: exim/src/src/tls-openssl.c,v 1.26 2010/06/05 10:34:29 pdp Exp $ */
-
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
@@ -349,6 +347,9 @@ level. */
 
 SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
 
+/* Automatically re-try reads/writes after renegotiation. */
+(void) SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
+
 /* Apply administrator-supplied work-arounds.
 Historically we applied just one requested option,
 SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS, but when bug 994 requested a second, we
@@ -360,7 +361,7 @@ availability of the option value macros from OpenSSL.  */
 
 okay = tls_openssl_options_parse(openssl_options, &init_options);
 if (!okay)
-  return tls_error("openssl_options parsing failed", host, NULL);
+  return tls_error(US"openssl_options parsing failed", host, NULL);
 
 if (init_options)
   {
@@ -438,9 +439,11 @@ static void
 construct_cipher_name(SSL *ssl)
 {
 static uschar cipherbuf[256];
-SSL_CIPHER *c;
+/* With OpenSSL 1.0.0a, this 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 SSL_CIPHER *c;
 uschar *ver;
-int bits;
 
 switch (ssl->session->ssl_version)
   {
@@ -456,15 +459,27 @@ switch (ssl->session->ssl_version)
   ver = US"TLSv1";
   break;
 
+#ifdef TLS1_1_VERSION
+  case TLS1_1_VERSION:
+  ver = US"TLSv1.1";
+  break;
+#endif
+
+#ifdef TLS1_2_VERSION
+  case TLS1_2_VERSION:
+  ver = US"TLSv1.2";
+  break;
+#endif
+
   default:
   ver = US"UNKNOWN";
   }
 
-c = SSL_get_current_cipher(ssl);
-SSL_CIPHER_get_bits(c, &bits);
+c = (const SSL_CIPHER *) SSL_get_current_cipher(ssl);
+SSL_CIPHER_get_bits(c, &tls_bits);
 
 string_format(cipherbuf, sizeof(cipherbuf), "%s:%s:%u", ver,
-  SSL_CIPHER_get_name(c), bits);
+  SSL_CIPHER_get_name(c), tls_bits);
 tls_cipher = cipherbuf;
 
 DEBUG(D_tls) debug_printf("Cipher: %s\n", cipherbuf);
@@ -680,7 +695,19 @@ else if (verify_check_host(&tls_try_verify_hosts) == OK)
 /* Prepare for new connection */
 
 if ((ssl = SSL_new(ctx)) == NULL) return tls_error(US"SSL_new", NULL, NULL);
-SSL_clear(ssl);
+
+/* Warning: we used to SSL_clear(ssl) here, it was removed.
+ *
+ * With the SSL_clear(), we get strange interoperability bugs with
+ * OpenSSL 1.0.1b and TLS1.1/1.2.  It looks as though this may be a bug in
+ * OpenSSL itself, as a clear should not lead to inability to follow protocols.
+ *
+ * The SSL_clear() call is to let an existing SSL* be reused, typically after
+ * session shutdown.  In this case, we have a brand new object and there's no
+ * obvious reason to immediately clear it.  I'm guessing that this was
+ * originally added because of incomplete initialisation which the clear fixed,
+ * in some historic release.
+ */
 
 /* Set context and tell client to go ahead, except in the case of TLS startup
 on connection, where outputting anything now upsets the clients and tends to
@@ -714,7 +741,7 @@ if (rc <= 0)
   tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : NULL);
   if (ERR_get_error() == 0)
     log_write(0, LOG_MAIN,
-        "  => client disconnected cleanly (rejected our certificate?)\n");
+        "TLS client disconnected cleanly (rejected our certificate?)");
   return FAIL;
   }
 
@@ -873,8 +900,8 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm)
   int error;
   int inbytes;
 
-  DEBUG(D_tls) debug_printf("Calling SSL_read(%lx, %lx, %u)\n", (long)ssl,
-    (long)ssl_xfer_buffer, ssl_xfer_buffer_size);
+  DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", ssl,
+    ssl_xfer_buffer, ssl_xfer_buffer_size);
 
   if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout);
   inbytes = SSL_read(ssl, CS ssl_xfer_buffer, ssl_xfer_buffer_size);
@@ -920,6 +947,7 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm)
     ssl_xfer_error = 1;
     return EOF;
     }
+
 #ifndef DISABLE_DKIM
   dkim_exim_verify_feed(ssl_xfer_buffer, inbytes);
 #endif
@@ -953,8 +981,8 @@ tls_read(uschar *buff, size_t len)
 int inbytes;
 int error;
 
-DEBUG(D_tls) debug_printf("Calling SSL_read(%lx, %lx, %u)\n", (long)ssl,
-  (long)buff, (unsigned int)len);
+DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", ssl,
+  buff, (unsigned int)len);
 
 inbytes = SSL_read(ssl, CS buff, len);
 error = SSL_get_error(ssl, inbytes);
@@ -996,10 +1024,10 @@ int outbytes;
 int error;
 int left = len;
 
-DEBUG(D_tls) debug_printf("tls_do_write(%lx, %d)\n", (long)buff, left);
+DEBUG(D_tls) debug_printf("tls_do_write(%p, %d)\n", buff, left);
 while (left > 0)
   {
-  DEBUG(D_tls) debug_printf("SSL_write(SSL, %lx, %d)\n", (long)buff, left);
+  DEBUG(D_tls) debug_printf("SSL_write(SSL, %p, %d)\n", buff, left);
   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);
@@ -1078,8 +1106,10 @@ Returns:     nothing
 void
 tls_version_report(FILE *f)
 {
-fprintf(f, "OpenSSL compile-time version: %s\n", OPENSSL_VERSION_TEXT);
-fprintf(f, "OpenSSL runtime version: %s\n", SSLeay_version(SSLEAY_VERSION));
+fprintf(f, "Library version: OpenSSL: Compile: %s\n"
+           "                          Runtime: %s\n",
+           OPENSSL_VERSION_TEXT,
+           SSLeay_version(SSLEAY_VERSION));
 }
 
 
@@ -1175,70 +1205,97 @@ all options unless explicitly for DTLS, let the administrator choose which
 to apply.
 
 This list is current as of:
-  ==>  0.9.8n  <==  */
+  ==>  1.0.1b  <==  */
 static struct exim_openssl_option exim_openssl_options[] = {
 /* KEEP SORTED ALPHABETICALLY! */
 #ifdef SSL_OP_ALL
-  { "all", SSL_OP_ALL },
+  { US"all", SSL_OP_ALL },
 #endif
 #ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
-  { "allow_unsafe_legacy_renegotiation", SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION },
+  { US"allow_unsafe_legacy_renegotiation", SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION },
 #endif
 #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
-  { "cipher_server_preference", SSL_OP_CIPHER_SERVER_PREFERENCE },
+  { US"cipher_server_preference", SSL_OP_CIPHER_SERVER_PREFERENCE },
 #endif
 #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
-  { "dont_insert_empty_fragments", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS },
+  { US"dont_insert_empty_fragments", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS },
 #endif
 #ifdef SSL_OP_EPHEMERAL_RSA
-  { "ephemeral_rsa", SSL_OP_EPHEMERAL_RSA },
+  { US"ephemeral_rsa", SSL_OP_EPHEMERAL_RSA },
 #endif
 #ifdef SSL_OP_LEGACY_SERVER_CONNECT
-  { "legacy_server_connect", SSL_OP_LEGACY_SERVER_CONNECT },
+  { US"legacy_server_connect", SSL_OP_LEGACY_SERVER_CONNECT },
 #endif
 #ifdef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
-  { "microsoft_big_sslv3_buffer", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER },
+  { US"microsoft_big_sslv3_buffer", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER },
 #endif
 #ifdef SSL_OP_MICROSOFT_SESS_ID_BUG
-  { "microsoft_sess_id_bug", SSL_OP_MICROSOFT_SESS_ID_BUG },
+  { US"microsoft_sess_id_bug", SSL_OP_MICROSOFT_SESS_ID_BUG },
 #endif
 #ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING
-  { "msie_sslv2_rsa_padding", SSL_OP_MSIE_SSLV2_RSA_PADDING },
+  { US"msie_sslv2_rsa_padding", SSL_OP_MSIE_SSLV2_RSA_PADDING },
 #endif
 #ifdef SSL_OP_NETSCAPE_CHALLENGE_BUG
-  { "netscape_challenge_bug", SSL_OP_NETSCAPE_CHALLENGE_BUG },
+  { US"netscape_challenge_bug", SSL_OP_NETSCAPE_CHALLENGE_BUG },
 #endif
 #ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
-  { "netscape_reuse_cipher_change_bug", 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_COMPRESSION
+  { US"no_compression", SSL_OP_NO_COMPRESSION },
 #endif
 #ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
-  { "no_session_resumption_on_renegotiation", SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION },
+  { US"no_session_resumption_on_renegotiation", SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION },
+#endif
+#ifdef SSL_OP_NO_SSLv2
+  { US"no_sslv2", SSL_OP_NO_SSLv2 },
+#endif
+#ifdef SSL_OP_NO_SSLv3
+  { US"no_sslv3", SSL_OP_NO_SSLv3 },
+#endif
+#ifdef SSL_OP_NO_TICKET
+  { US"no_ticket", SSL_OP_NO_TICKET },
+#endif
+#ifdef SSL_OP_NO_TLSv1
+  { US"no_tlsv1", SSL_OP_NO_TLSv1 },
+#endif
+#ifdef SSL_OP_NO_TLSv1_1
+#if SSL_OP_NO_TLSv1_1 == 0x00000400L
+  /* Error in chosen value in 1.0.1a; see first item in CHANGES for 1.0.1b */
+#warning OpenSSL 1.0.1a uses a bad value for SSL_OP_NO_TLSv1_1, ignoring
+#else
+  { US"no_tlsv1_1", SSL_OP_NO_TLSv1_1 },
+#endif
+#endif
+#ifdef SSL_OP_NO_TLSv1_2
+  { US"no_tlsv1_2", SSL_OP_NO_TLSv1_2 },
 #endif
 #ifdef SSL_OP_SINGLE_DH_USE
-  { "single_dh_use", SSL_OP_SINGLE_DH_USE },
+  { US"single_dh_use", SSL_OP_SINGLE_DH_USE },
 #endif
 #ifdef SSL_OP_SINGLE_ECDH_USE
-  { "single_ecdh_use", SSL_OP_SINGLE_ECDH_USE },
+  { US"single_ecdh_use", SSL_OP_SINGLE_ECDH_USE },
 #endif
 #ifdef SSL_OP_SSLEAY_080_CLIENT_DH_BUG
-  { "ssleay_080_client_dh_bug", SSL_OP_SSLEAY_080_CLIENT_DH_BUG },
+  { US"ssleay_080_client_dh_bug", SSL_OP_SSLEAY_080_CLIENT_DH_BUG },
 #endif
 #ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
-  { "sslref2_reuse_cert_type_bug", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG },
+  { US"sslref2_reuse_cert_type_bug", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG },
 #endif
 #ifdef SSL_OP_TLS_BLOCK_PADDING_BUG
-  { "tls_block_padding_bug", SSL_OP_TLS_BLOCK_PADDING_BUG },
+  { US"tls_block_padding_bug", SSL_OP_TLS_BLOCK_PADDING_BUG },
 #endif
 #ifdef SSL_OP_TLS_D5_BUG
-  { "tls_d5_bug", SSL_OP_TLS_D5_BUG },
+  { US"tls_d5_bug", SSL_OP_TLS_D5_BUG },
 #endif
 #ifdef SSL_OP_TLS_ROLLBACK_BUG
-  { "tls_rollback_bug", SSL_OP_TLS_ROLLBACK_BUG },
+  { US"tls_rollback_bug", SSL_OP_TLS_ROLLBACK_BUG },
 #endif
 };
 static int exim_openssl_options_size =
   sizeof(exim_openssl_options)/sizeof(struct exim_openssl_option);
 
+
 static BOOL
 tls_openssl_one_option_parse(uschar *name, long *value)
 {
@@ -1286,12 +1343,9 @@ uschar *s, *end;
 uschar keep_c;
 BOOL adding, item_parsed;
 
-/* We grandfather in as default the one option which we used to set always. */
-#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
-result = SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
-#else
 result = 0L;
-#endif
+/* Prior to 4.78 we or'd in SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; removed
+ * from default because it increases BEAST susceptibility. */
 
 if (option_spec == NULL)
   {
@@ -1307,7 +1361,7 @@ for (s=option_spec; *s != '\0'; /**/)
   if (*s != '+' && *s != '-')
     {
     DEBUG(D_tls) debug_printf("malformed openssl option setting: "
-        "+ or - expected but found \"%s\"", s);
+        "+ or - expected but found \"%s\"\n", s);
     return FALSE;
     }
   adding = *s++ == '+';
@@ -1317,7 +1371,7 @@ for (s=option_spec; *s != '\0'; /**/)
   item_parsed = tls_openssl_one_option_parse(s, &item);
   if (!item_parsed)
     {
-    DEBUG(D_tls) debug_printf("openssl option setting unrecognised: \"%s\"", s);
+    DEBUG(D_tls) debug_printf("openssl option setting unrecognised: \"%s\"\n", s);
     return FALSE;
     }
   DEBUG(D_tls) debug_printf("openssl option, %s from %lx: %lx (%s)\n",