From 77bb000fa965b786ddb1085dd5af6c80c7d425b0 Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Sat, 5 Jun 2010 09:10:08 +0000 Subject: [PATCH] Add an openssl_options main configuration option, to allow administrators to 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 | 33 +++++- doc/doc-txt/ChangeLog | 4 +- doc/doc-txt/NewStuff | 14 ++- src/src/functions.h | 5 +- src/src/globals.c | 3 +- src/src/globals.h | 3 +- src/src/readconf.c | 20 +++- src/src/tls-openssl.c | 227 +++++++++++++++++++++++++++++++++++--- 8 files changed, 287 insertions(+), 22 deletions(-) diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index b33edeb97..493c55445 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -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, diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 7b560e527..3ed3aa8c7 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -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 ----------------- diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index b42c0d07c..589bc5397 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -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 ------------ diff --git a/src/src/functions.h b/src/src/functions.h index 49bdbf3d8..612d1e708 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -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 diff --git a/src/src/globals.c b/src/src/globals.c index 3e1e76cff..b4e24485c 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -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; diff --git a/src/src/globals.h b/src/src/globals.h index cc5d601a5..e3991dcbd 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -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 */ diff --git a/src/src/readconf.c b/src/src/readconf.c index 41fb9f796..adb62205d 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -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 } diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index a9251c7f4..a7dad0805 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -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 */ -- 2.30.2