Add an openssl_options main configuration option, to allow administrators to
authorPhil Pennock <pdp@exim.org>
Sat, 5 Jun 2010 09:10:08 +0000 (09:10 +0000)
committerPhil Pennock <pdp@exim.org>
Sat, 5 Jun 2010 09:10:08 +0000 (09:10 +0000)
shoot themselves in each foot in turn.  The default value is chosen to avoid
a change in behaviour, but since it is disabling a security countermeasure,
I'd like to change the default to be "no options".  Fixes: #994

doc/doc-docbook/spec.xfpt
doc/doc-txt/ChangeLog
doc/doc-txt/NewStuff
src/src/functions.h
src/src/globals.c
src/src/globals.h
src/src/readconf.c
src/src/tls-openssl.c

index b33edeb..493c554 100644 (file)
@@ -1,4 +1,4 @@
-. $Cambridge: exim/doc/doc-docbook/spec.xfpt,v 1.74 2010/05/29 19:26:31 nm4 Exp $
+. $Cambridge: exim/doc/doc-docbook/spec.xfpt,v 1.75 2010/06/05 09:10:08 pdp Exp $
 .
 . /////////////////////////////////////////////////////////////////////////////
 . This is the primary source of the Exim Manual. It is an xfpt document that is
@@ -12392,6 +12392,7 @@ listed in more than one group.
 .row &%gnutls_require_mac%&          "control GnuTLS MAC algorithms"
 .row &%gnutls_require_protocols%&    "control GnuTLS protocols"
 .row &%gnutls_compat_mode%&          "use GnuTLS compatibility mode"
+.row &%openssl_options%&             "adjust OpenSSL compatibility options"
 .row &%tls_advertise_hosts%&         "advertise TLS to these hosts"
 .row &%tls_certificate%&             "location of server certificate"
 .row &%tls_crl%&                     "certificate revocation list"
@@ -14003,6 +14004,36 @@ harm. This option overrides the &%pipe_as_creator%& option of the &(pipe)&
 transport driver.
 
 
+.option openssl_options main "string list" +dont_insert_empty_fragments
+.cindex "OpenSSL "compatibility options"
+This option allows an administrator to adjust the SSL options applied
+by OpenSSL to connections.  It is given as a space-separated list of items,
+each one to be +added or -subtracted from the current value.  The default
+value is one option which happens to have been set historically.  You can
+remove all options with:
+.code
+openssl_options = -all
+.endd
+This option is only available if Exim is built against OpenSSL.  The values
+available for this option vary according to the age of your OpenSSL install.
+The &"all"& value controls a subset of flags which are available, typically
+the bug workaround options.  The &'SSL_CTX_set_options'& man page will
+list the values known on your system and Exim should support all the
+&"bug workaround"& options and many of the &"modifying"& options.  The Exim
+names lose the leading &"SSL_OP_"& and are lower-cased.
+
+Note that adjusting the options can have severe impact upon the security of
+SSL as used by Exim.  It is possible to disable safety checks and shoot
+yourself in the foot in various unpleasant ways.  This option should not be
+adjusted lightly.  An unrecognised item will be detected at by invoking Exim
+with the &%-bV%& flag.
+
+An example:
+.code
+openssl_options = -all +microsoft_big_sslv3_buffer
+.endd
+
+
 .option oracle_servers main "string list" unset
 .cindex "Oracle" "server list"
 This option provides a list of Oracle servers and associated connection data,
index 7b560e5..3ed3aa8 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.616 2010/06/03 15:20:41 jetmore Exp $
+$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.617 2010/06/05 09:10:09 pdp Exp $
 
 Change log file for Exim from version 4.21
 -------------------------------------------
@@ -14,6 +14,8 @@ PP/02 Include check_rfc2047_length in configure.default because we're seeing
 
 JJ/01 Added DISABLE_DKIM and comment to src/EDITME
 
+PP/03 Bugzilla 994: added openssl_options main configuration option.
+
 Exim version 4.72
 -----------------
 
index b42c0d0..589bc53 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-txt/NewStuff,v 1.165 2010/06/03 15:20:41 jetmore Exp $
+$Cambridge: exim/doc/doc-txt/NewStuff,v 1.166 2010/06/05 09:10:09 pdp Exp $
 
 New Features in Exim
 --------------------
@@ -9,6 +9,18 @@ test from the snapshots or the CVS before the documentation is updated. Once
 the documentation is updated, this file is reduced to a short list.
 
 
+Version 4.73
+------------
+
+ 1. A new main configuration option, "openssl_options", is available if Exim
+    is built with SSL support provided by OpenSSL.  The option allows
+    administrators to specify OpenSSL options to be used on connections;
+    typically this is to set bug compatibility features which the OpenSSL
+    developers have not enabled by default.  There may be security
+    consequences for certain options, so these should not be changed
+    frivolously.
+
+
 Version 4.72
 ------------
 
index 49bdbf3..612d1e7 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/functions.h,v 1.47 2009/11/16 19:50:37 nm4 Exp $ */
+/* $Cambridge: exim/src/src/functions.h,v 1.48 2010/06/05 09:10:10 pdp Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -36,6 +36,9 @@ extern BOOL    tls_smtp_buffered(void);
 extern int     tls_ungetc(int);
 extern int     tls_write(const uschar *, size_t);
 extern void    tls_version_report(FILE *);
+#ifndef USE_GNUTLS
+extern BOOL    tls_openssl_options_parse(uschar *, long *);
+#endif
 #endif
 
 
index 3e1e76c..b4e2448 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/globals.c,v 1.86 2009/11/16 19:50:37 nm4 Exp $ */
+/* $Cambridge: exim/src/src/globals.c,v 1.87 2010/06/05 09:10:10 pdp Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -115,6 +115,7 @@ BOOL    gnutls_compat_mode     = FALSE;
 uschar *gnutls_require_mac     = NULL;
 uschar *gnutls_require_kx      = NULL;
 uschar *gnutls_require_proto   = NULL;
+uschar *openssl_options        = NULL;
 const pcre *regex_STARTTLS     = NULL;
 uschar *tls_advertise_hosts    = NULL;    /* This is deliberate */
 uschar *tls_certificate        = NULL;
index cc5d601..e3991dc 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/globals.h,v 1.67 2009/11/16 19:50:37 nm4 Exp $ */
+/* $Cambridge: exim/src/src/globals.h,v 1.68 2010/06/05 09:10:10 pdp Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -75,6 +75,7 @@ extern BOOL    gnutls_compat_mode;     /* Less security, more compatibility */
 extern uschar *gnutls_require_mac;     /* So some can be avoided */
 extern uschar *gnutls_require_kx;      /* So some can be avoided */
 extern uschar *gnutls_require_proto;   /* So some can be avoided */
+extern uschar *openssl_options;        /* OpenSSL compatibility options */
 extern const pcre *regex_STARTTLS;     /* For recognizing STARTTLS settings */
 extern uschar *tls_advertise_hosts;    /* host for which TLS is advertised */
 extern uschar *tls_certificate;        /* Certificate file */
index 41fb9f7..adb6220 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/readconf.c,v 1.39 2009/11/16 19:50:37 nm4 Exp $ */
+/* $Cambridge: exim/src/src/readconf.c,v 1.40 2010/06/05 09:10:10 pdp Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -291,6 +291,9 @@ static optionlist optionlist_config[] = {
   { "mysql_servers",            opt_stringptr,   &mysql_servers },
 #endif
   { "never_users",              opt_uidlist,     &never_users },
+#ifdef SUPPORT_TLS
+  { "openssl_options",          opt_stringptr,   &openssl_options },
+#endif
 #ifdef LOOKUP_ORACLE
   { "oracle_servers",           opt_stringptr,   &oracle_servers },
 #endif
@@ -2748,6 +2751,7 @@ void
 readconf_main(void)
 {
 int sep = 0;
+long dummy_l;
 struct stat statbuf;
 uschar *s, *filename;
 uschar *list = config_main_filelist;
@@ -3156,6 +3160,20 @@ if ((tls_verify_hosts != NULL || tls_try_verify_hosts != NULL) &&
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
     "tls_%sverify_hosts is set, but tls_verify_certificates is not set",
     (tls_verify_hosts != NULL)? "" : "try_");
+
+/* If openssl_options is set, validate it */
+if (openssl_options != NULL)
+  {
+# ifdef USE_GNUTLS
+  log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
+    "openssl_options is set but we're using GnuTLS\n");
+# else
+  long dummy;
+  if (!(tls_openssl_options_parse(openssl_options, &dummy)))
+    log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
+      "openssl_options parse error: %s\n", openssl_options);
+# endif
+  }
 #endif
 }
 
index a9251c7..a7dad08 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/tls-openssl.c,v 1.22 2009/11/16 19:50:37 nm4 Exp $ */
+/* $Cambridge: exim/src/src/tls-openssl.c,v 1.23 2010/06/05 09:10:10 pdp Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -302,11 +302,14 @@ static int
 tls_init(host_item *host, uschar *dhparam, uschar *certificate,
   uschar *privatekey, address_item *addr)
 {
+long init_options;
+BOOL okay;
+
 SSL_load_error_strings();          /* basic set up */
 OpenSSL_add_ssl_algorithms();
 
 #if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
-/* SHA256 is becoming ever moar popular. This makes sure it gets added to the
+/* SHA256 is becoming ever more popular. This makes sure it gets added to the
 list of available digests. */
 EVP_add_digest(EVP_sha256());
 #endif
@@ -346,21 +349,28 @@ level. */
 
 SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
 
-/* The following patch was supplied by Robert Roselius */
+/* 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
+moved to an administrator-controlled list of options to specify and
+grandfathered in the first one as the default value for "openssl_options".
 
-#if OPENSSL_VERSION_NUMBER > 0x00906040L
-/* Enable client-bug workaround.
-   Versions of OpenSSL as of 0.9.6d include a "CBC countermeasure" feature,
-   which causes problems with some clients (such as the Certicom SSL Plus
-   library used by Eudora).  This option, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS,
-   disables the coutermeasure allowing Eudora to connect.
-   Some poppers and MTAs use SSL_OP_ALL, which enables all such bug
-   workarounds. */
-/* XXX (Silently?) ignore failure here? XXX*/
+No OpenSSL version number checks: the options we accept depend upon the
+availability of the option value macros from OpenSSL.  */
 
-if (!(SSL_CTX_set_options(ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS)))
-  return tls_error(US"SSL_CTX_set_option", host, NULL);
-#endif
+okay = tls_openssl_options_parse(openssl_options, &init_options);
+if (!okay)
+  return tls_error("openssl_options parsing failed", host, NULL);
+
+if (init_options)
+  {
+  DEBUG(D_tls) debug_printf("setting SSL CTX options: %#lx\n", init_options);
+  if (!(SSL_CTX_set_options(ctx, init_options)))
+    return tls_error(string_sprintf(
+          "SSL_CTX_set_option(%#lx)", init_options), host, NULL);
+  }
+else
+  DEBUG(D_tls) debug_printf("no SSL CTX options to set\n");
 
 /* Initialize with DH parameters if supplied */
 
@@ -702,6 +712,9 @@ alarm(0);
 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");
   return FAIL;
   }
 
@@ -1123,4 +1136,188 @@ smooth distribution and cares enough then they should submit a patch then. */
 return r % max;
 }
 
+
+
+
+/*************************************************
+*        OpenSSL option parse                    *
+*************************************************/
+
+/* Parse one option for tls_openssl_options_parse below
+
+Arguments:
+  name    one option name
+  value   place to store a value for it
+Returns   success or failure in parsing
+*/
+
+struct exim_openssl_option {
+  uschar *name;
+  long    value;
+};
+/* We could use a macro to expand, but we need the ifdef and not all the
+options document which version they were introduced in.  Policylet: include
+all options unless explicitly for DTLS, let the administrator choose which
+to apply.
+
+This list is current as of:
+  ==>  0.9.8n  <==  */
+static struct exim_openssl_option exim_openssl_options[] = {
+/* KEEP SORTED ALPHABETICALLY! */
+#ifdef SSL_OP_ALL
+  { "all", SSL_OP_ALL },
+#endif
+#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
+  { "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 },
+#endif
+#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
+  { "dont_insert_empty_fragments", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS },
+#endif
+#ifdef SSL_OP_EPHEMERAL_RSA
+  { "ephemeral_rsa", SSL_OP_EPHEMERAL_RSA },
+#endif
+#ifdef SSL_OP_LEGACY_SERVER_CONNECT
+  { "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 },
+#endif
+#ifdef SSL_OP_MICROSOFT_SESS_ID_BUG
+  { "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 },
+#endif
+#ifdef SSL_OP_NETSCAPE_CHALLENGE_BUG
+  { "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 },
+#endif
+#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
+  { "no_session_resumption_on_renegotiation", SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION },
+#endif
+#ifdef SSL_OP_SINGLE_DH_USE
+  { "single_dh_use", SSL_OP_SINGLE_DH_USE },
+#endif
+#ifdef SSL_OP_SINGLE_ECDH_USE
+  { "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 },
+#endif
+#ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
+  { "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 },
+#endif
+#ifdef SSL_OP_TLS_D5_BUG
+  { "tls_d5_bug", SSL_OP_TLS_D5_BUG },
+#endif
+#ifdef SSL_OP_TLS_ROLLBACK_BUG
+  { "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)
+{
+int first = 0;
+int last = exim_openssl_options_size;
+while (last > first)
+  {
+  int middle = (first + last)/2;
+  int c = Ustrcmp(name, exim_openssl_options[middle].name);
+  if (c == 0)
+    {
+    *value = exim_openssl_options[middle].value;
+    return TRUE;
+    }
+  else if (c > 0)
+    first = middle + 1;
+  else
+    last = middle;
+  }
+return FALSE;
+}
+
+
+
+
+/*************************************************
+*        OpenSSL option parsing logic            *
+*************************************************/
+
+/* OpenSSL has a number of compatibility options which an administrator might
+reasonably wish to set.  Interpret a list similarly to decode_bits(), so that
+we look like log_selector.
+
+Arguments:
+  option_spec  the administrator-supplied string of options
+  results      ptr to long storage for the options bitmap
+Returns        success or failure
+*/
+
+BOOL
+tls_openssl_options_parse(uschar *option_spec, long *results)
+{
+long result, item;
+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
+
+if (option_spec == NULL)
+  {
+  *results = result;
+  return TRUE;
+  }
+
+for (s=option_spec; *s != '\0'; /**/)
+  {
+  while (isspace(*s)) ++s;
+  if (*s == '\0')
+    break;
+  if (*s != '+' && *s != '-')
+    {
+    DEBUG(D_tls) debug_printf("malformed openssl option setting: "
+        "+ or - expected but found \"%s\"", s);
+    return FALSE;
+    }
+  adding = *s++ == '+';
+  for (end = s; (*end != '\0') && !isspace(*end); ++end) /**/ ;
+  keep_c = *end;
+  *end = '\0';
+  item_parsed = tls_openssl_one_option_parse(s, &item);
+  if (!item_parsed)
+    {
+    DEBUG(D_tls) debug_printf("openssl option setting unrecognised: \"%s\"", s);
+    return FALSE;
+    }
+  DEBUG(D_tls) debug_printf("openssl option, %s from %lx: %lx (%s)\n",
+      adding ? "adding" : "removing", result, item, s);
+  if (adding)
+    result |= item;
+  else
+    result &= ~item;
+  *end = keep_c;
+  s = end;
+  }
+
+*results = result;
+return TRUE;
+}
+
 /* End of tls-openssl.c */