From 83da1223921fe30362e8374951360dcc8f21c4e7 Mon Sep 17 00:00:00 2001 From: Philip Hazel Date: Thu, 18 Jan 2007 15:35:42 +0000 Subject: [PATCH] Add gnutls_require_{kx,mac,protocols}. --- doc/doc-txt/ChangeLog | 5 +- doc/doc-txt/NewStuff | 71 ++++++++- doc/doc-txt/OptionLists.txt | 10 +- src/src/functions.h | 7 +- src/src/globals.c | 5 +- src/src/globals.h | 5 +- src/src/readconf.c | 7 +- src/src/smtp_in.c | 9 +- src/src/tls-gnu.c | 273 ++++++++++++++++++++++++---------- src/src/tls-openssl.c | 20 ++- src/src/transports/smtp.c | 20 ++- src/src/transports/smtp.h | 5 +- test/confs/2011 | 11 +- test/log/2011 | 43 +++++- test/scripts/2000-GnuTLS/2011 | 30 +++- 15 files changed, 422 insertions(+), 99 deletions(-) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 240c815eb..238621e9b 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -1,4 +1,4 @@ -$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.454 2007/01/17 11:17:58 ph10 Exp $ +$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.455 2007/01/18 15:35:42 ph10 Exp $ Change log file for Exim from version 4.21 ------------------------------------------- @@ -26,6 +26,9 @@ PH/02 In an ACL statement such as PH/03 Added additional dnslists conditions == and =& which are different from = and & when the dns lookup returns more than one IP address. +PH/04 Added gnutls_require_{kx,mac,protocols} to give more control over the + cipher suites used by GnuTLS. These options are ignored by OpenSSL. + Exim version 4.66 ----------------- diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index 960f93ce8..9cc8f81cc 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -1,4 +1,4 @@ -$Cambridge: exim/doc/doc-txt/NewStuff,v 1.127 2007/01/17 11:17:58 ph10 Exp $ +$Cambridge: exim/doc/doc-txt/NewStuff,v 1.128 2007/01/18 15:35:42 ph10 Exp $ New Features in Exim -------------------- @@ -106,6 +106,75 @@ Version 4.67 When the DNS lookup yields only a single IP address, there is no difference between = and == and between & and =&. + 3. Up till now, the only control over which cipher suites GnuTLS uses has been + for the cipher algorithms. New options have been added to allow some of the + other parameters to be varied. Here is complete documentation for the + available features: + + GnuTLS allows the caller to specify separate lists of permitted key + exchange methods, main cipher algorithms, and MAC algorithms. These may be + used in any combination to form a specific cipher suite. This is unlike + OpenSSL, where complete cipher names can be passed to its control function. + GnuTLS also allows a list of acceptable protocols to be supplied. + + For compatibility with OpenSSL, the tls_require_ciphers option can be set + to complete cipher suite names such as RSA_ARCFOUR_SHA, but for GnuTLS this + option controls only the cipher algorithms. Exim searches each item in the + list for the name of an available algorithm. For example, if the list + contains RSA_AES_SHA, then AES is recognized, and the behaviour is exactly + the same as if just AES were given. + + There are additional options called gnutls_require_kx, gnutls_require_mac, + and gnutls_require_protocols that can be used to restrict the key exchange + methods, MAC algorithms, and protocols, respectively. These options are + ignored if OpenSSL is in use. + + All four options are available as global options, controlling how Exim + behaves as a server, and also as options of the smtp transport, controlling + how Exim behaves as a client. All the values are string expanded. After + expansion, the values must be colon-separated lists, though the separator + can be changed in the usual way. + + Each of the four lists starts out with a default set of algorithms. If the + first item in one of the "require" options does _not_ start with an + exclamation mark, all the default items are deleted. In this case, only + those that are explicitly specified can be used. If the first item in one + of the "require" items _does_ start with an exclamation mark, the defaults + are left on the list. + + Then, any item that starts with an exclamation mark causes the relevant + entry to be removed from the list, and any item that does not start with an + exclamation mark causes a new entry to be added to the list. Unrecognized + items in the list are ignored. Thus: + + tls_require_ciphers = !ARCFOUR + + allows all the defaults except ARCFOUR, whereas + + tls_require_ciphers = AES : 3DES + + allows only cipher suites that use AES or 3DES. For tls_require_ciphers + the recognized names are AES_256, AES_128, AES (both of the preceding), + 3DES, ARCFOUR_128, ARCFOUR_40, and ARCFOUR (both of the preceding). The + default list does not contain all of these; it just has AES_256, AES_128, + 3DES, and ARCFOUR_128. + + For gnutls_require_kx, the recognized names are DHE_RSA, RSA (which + includes DHE_RSA), DHE_DSS, and DHE (which includes both DHE_RSA and + DHE_DSS). The default list contains RSA, DHE_DSS, DHE_RSA. + + For gnutls_require_mac, the recognized names are SHA (synonym SHA1), and + MD5. The default list contains SHA, MD5. + + For gnutls_require_protocols, the recognized names are TLS1 and SSL3. + The default list contains TLS1, SSL3. + + In a server, the order of items in these lists is unimportant. The server + will advertise the availability of all the relevant cipher suites. However, + in a client, the order in the tls_require_ciphers list specifies a + preference order for the cipher algorithms. The first one in the client's + list that is also advertised by the server is tried first. + Version 4.66 ------------ diff --git a/doc/doc-txt/OptionLists.txt b/doc/doc-txt/OptionLists.txt index e484f98e9..67b03c793 100644 --- a/doc/doc-txt/OptionLists.txt +++ b/doc/doc-txt/OptionLists.txt @@ -1,4 +1,4 @@ -$Cambridge: exim/doc/doc-txt/OptionLists.txt,v 1.28 2006/12/05 11:35:28 ph10 Exp $ +$Cambridge: exim/doc/doc-txt/OptionLists.txt,v 1.29 2007/01/18 15:35:42 ph10 Exp $ LISTS OF EXIM OPTIONS --------------------- @@ -11,7 +11,7 @@ This file contains complete lists of four kinds of Exim option: 4. Those that can appear in the build time configuration for the Exim monitor (Local/eximon.conf). -This file was last updated for Exim release 4.64. +This file was last updated for Exim release 4.67. 1. RUN TIME OPTIONS @@ -231,6 +231,12 @@ from string* unset autoreply gecos_name string* unset main gecos_pattern string unset main gethostbyname boolean false smtp +gnutls_require_kx string* unset main 4.67 + string* unset smtp 4.67 +gnutls_require_mac string* unset main 4.67 + string* unset smtp 4.67 +gnutls_require_protocols string* unset main 4.67 + string* unset smtp 4.67 group string + routers 4.00 unset transports 4.00 replaces local option in some transports header_line_maxsize integer 0 (unset) main 4.14 diff --git a/src/src/functions.h b/src/src/functions.h index f15142336..5e3f9d580 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/functions.h,v 1.33 2007/01/15 15:59:22 ph10 Exp $ */ +/* $Cambridge: exim/src/src/functions.h,v 1.34 2007/01/18 15:35:42 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -24,13 +24,14 @@ extern uschar *init_perl(uschar *); #ifdef SUPPORT_TLS extern int tls_client_start(int, host_item *, address_item *, uschar *, - uschar *, uschar *, uschar *, uschar *, uschar *, int); + uschar *, uschar *, uschar *, uschar *, uschar *, uschar *, + uschar *, uschar *, int); extern void tls_close(BOOL); extern int tls_feof(void); extern int tls_ferror(void); extern int tls_getc(void); extern int tls_read(uschar *, size_t); -extern int tls_server_start(uschar *); +extern int tls_server_start(uschar *, uschar *, uschar *, uschar *); extern int tls_ungetc(int); extern int tls_write(const uschar *, size_t); #endif diff --git a/src/src/globals.c b/src/src/globals.c index 796104bc0..c60ef864f 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/globals.c,v 1.62 2007/01/15 15:59:22 ph10 Exp $ */ +/* $Cambridge: exim/src/src/globals.c,v 1.63 2007/01/18 15:35:42 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -109,6 +109,9 @@ uschar *tls_on_connect_ports = NULL; uschar *tls_peerdn = NULL; #ifdef SUPPORT_TLS +uschar *gnutls_require_mac = NULL; +uschar *gnutls_require_kx = NULL; +uschar *gnutls_require_proto = 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 2ea06dad9..090e1b483 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/globals.h,v 1.43 2007/01/15 15:59:22 ph10 Exp $ */ +/* $Cambridge: exim/src/src/globals.h,v 1.44 2007/01/18 15:35:42 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -71,6 +71,9 @@ extern uschar *tls_on_connect_ports; /* Ports always tls-on-connect */ extern uschar *tls_peerdn; /* DN from peer */ #ifdef SUPPORT_TLS +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 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 593da8a57..c3514aba5 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/readconf.c,v 1.26 2007/01/08 10:50:18 ph10 Exp $ */ +/* $Cambridge: exim/src/src/readconf.c,v 1.27 2007/01/18 15:35:42 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -218,6 +218,11 @@ static optionlist optionlist_config[] = { { "freeze_tell", opt_stringptr, &freeze_tell }, { "gecos_name", opt_stringptr, &gecos_name }, { "gecos_pattern", opt_stringptr, &gecos_pattern }, +#ifdef SUPPORT_TLS + { "gnutls_require_kx", opt_stringptr, &gnutls_require_kx }, + { "gnutls_require_mac", opt_stringptr, &gnutls_require_mac }, + { "gnutls_require_protocols", opt_stringptr, &gnutls_require_proto }, +#endif { "header_line_maxsize", opt_int, &header_line_maxsize }, { "header_maxsize", opt_int, &header_maxsize }, { "headers_charset", opt_stringptr, &headers_charset }, diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index b1a1eba3d..ea7039935 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/smtp_in.c,v 1.50 2007/01/15 15:59:22 ph10 Exp $ */ +/* $Cambridge: exim/src/src/smtp_in.c,v 1.51 2007/01/18 15:35:42 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -1540,7 +1540,9 @@ if (!sender_host_unknown) smtps port for use with older style SSL MTAs. */ #ifdef SUPPORT_TLS - if (tls_on_connect && tls_server_start(tls_require_ciphers) != OK) + if (tls_on_connect && + tls_server_start(tls_require_ciphers, + gnutls_require_mac, gnutls_require_kx, gnutls_require_proto) != OK) return FALSE; #endif @@ -3593,7 +3595,8 @@ while (done <= 0) We must allow for an extra EHLO command and an extra AUTH command after STARTTLS that don't add to the nonmail command count. */ - if ((rc = tls_server_start(tls_require_ciphers)) == OK) + if ((rc = tls_server_start(tls_require_ciphers, gnutls_require_mac, + gnutls_require_kx, gnutls_require_proto)) == OK) { if (!tls_remember_esmtp) helo_seen = esmtp = auth_advertised = pipelining_advertised = FALSE; diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 891d1ea00..ee57659fa 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/tls-gnu.c,v 1.17 2007/01/08 10:50:18 ph10 Exp $ */ +/* $Cambridge: exim/src/src/tls-gnu.c,v 1.18 2007/01/18 15:35:42 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -46,17 +46,24 @@ static char ssl_errstring[256]; static int ssl_session_timeout = 200; static int verify_requirement; -/* Priorities for TLS algorithms to use. At present, only the cipher priority -vector can be altered. */ +/* Priorities for TLS algorithms to use. In each case there's a default table, +and space into which it can be copied and altered. */ -static const int protocol_priority[16] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 }; +static const int default_proto_priority[16] = { + GNUTLS_TLS1, + GNUTLS_SSL3, + 0 }; + +static int proto_priority[16]; -static const int kx_priority[16] = { +static const int default_kx_priority[16] = { GNUTLS_KX_RSA, GNUTLS_KX_DHE_DSS, GNUTLS_KX_DHE_RSA, 0 }; +static int kx_priority[16]; + static int default_cipher_priority[16] = { GNUTLS_CIPHER_AES_256_CBC, GNUTLS_CIPHER_AES_128_CBC, @@ -66,21 +73,50 @@ static int default_cipher_priority[16] = { static int cipher_priority[16]; -static const int mac_priority[16] = { +static const int default_mac_priority[16] = { GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0 }; +static int mac_priority[16]; + +/* These two are currently not changeable. */ + static const int comp_priority[16] = { GNUTLS_COMP_NULL, 0 }; static const int cert_type_priority[16] = { GNUTLS_CRT_X509, 0 }; -/* Tables of cipher names and equivalent numbers */ +/* Tables of priority names and equivalent numbers */ typedef struct pri_item { uschar *name; int *values; } pri_item; + +static int tls1_codes[] = { GNUTLS_TLS1, 0 }; +static int ssl3_codes[] = { GNUTLS_SSL3, 0 }; + +static pri_item proto_index[] = { + { US"TLS1", tls1_codes }, + { US"SSL3", ssl3_codes } +}; + + +static int kx_rsa_codes[] = { GNUTLS_KX_RSA, + GNUTLS_KX_DHE_RSA, 0 }; +static int kx_dhe_codes[] = { GNUTLS_KX_DHE_DSS, + GNUTLS_KX_DHE_RSA, 0 }; +static int kx_dhe_dss_codes[] = { GNUTLS_KX_DHE_DSS, 0 }; +static int kx_dhe_rsa_codes[] = { GNUTLS_KX_DHE_RSA, 0 }; + +static pri_item kx_index[] = { + { US"DHE_DSS", kx_dhe_dss_codes }, + { US"DHE_RSA", kx_dhe_rsa_codes }, + { US"RSA", kx_rsa_codes }, + { US"DHE", kx_dhe_codes } +}; + + static int arcfour_128_codes[] = { GNUTLS_CIPHER_ARCFOUR_128, 0 }; static int arcfour_40_codes[] = { GNUTLS_CIPHER_ARCFOUR_40, 0 }; static int arcfour_codes[] = { GNUTLS_CIPHER_ARCFOUR_128, @@ -102,6 +138,16 @@ static pri_item cipher_index[] = { }; +static int mac_sha_codes[] = { GNUTLS_MAC_SHA, 0 }; +static int mac_md5_codes[] = { GNUTLS_MAC_MD5, 0 }; + +static pri_item mac_index[] = { + { US"SHA", mac_sha_codes }, + { US"SHA1", mac_sha_codes }, + { US"MD5", mac_md5_codes } +}; + + /************************************************* * Handle TLS error * @@ -512,7 +558,7 @@ return OK; /************************************************* -* Remove ciphers from priority list * +* Remove from a priority list * *************************************************/ /* Cautiously written so that it will remove duplicates if present. @@ -525,7 +571,7 @@ Returns: nothing */ static void -remove_ciphers(int *list, int *remove_list) +remove_priority(int *list, int *remove_list) { for (; *remove_list != 0; remove_list++) { @@ -545,7 +591,7 @@ for (; *remove_list != 0; remove_list++) /************************************************* -* Add ciphers to priority list * +* Add to a priority list * *************************************************/ /* Cautiously written to check the list size @@ -559,7 +605,7 @@ Returns: TRUE if OK; FALSE if list overflows */ static BOOL -add_ciphers(int *list, int list_max, int *add_list) +add_priority(int *list, int list_max, int *add_list) { int next = 0; while (list[next] != 0) next++; @@ -574,6 +620,78 @@ return TRUE; +/************************************************* +* Adjust a priority list * +*************************************************/ + +/* This function is called to adjust the lists of cipher algorithms, MAC +algorithms, key-exchange methods, and protocols. + +Arguments: + plist the appropriate priority list + psize the length of the list + s the configuation string + index the index of recognized strings + isize the length of the index + + + which text for an error message + +Returns: FALSE if the table overflows, else TRUE +*/ + +static BOOL +set_priority(int *plist, int psize, uschar *s, pri_item *index, int isize, + uschar *which) +{ +int sep = 0; +BOOL first = TRUE; +uschar *t; + +while ((t = string_nextinlist(&s, &sep, big_buffer, big_buffer_size)) != NULL) + { + int i; + BOOL exclude = t[0] == '!'; + if (first && !exclude) plist[0] = 0; + first = FALSE; + for (i = 0; i < isize; i++) + { + uschar *ss = strstric(t, index[i].name, FALSE); + if (ss != NULL) + { + uschar *endss = ss + Ustrlen(index[i].name); + if ((ss == t || !isalnum(ss[-1])) && !isalnum(*endss)) + { + if (exclude) + remove_priority(plist, index[i].values); + else + { + if (!add_priority(plist, psize, index[i].values)) + { + log_write(0, LOG_MAIN|LOG_PANIC, "GnuTLS init failed: %s " + "priority table overflow", which); + return FALSE; + } + } + } + } + } + } + +DEBUG(D_tls) + { + int *ptr = plist; + debug_printf("adjusted %s priorities:", which); + while (*ptr != 0) debug_printf(" %d", *ptr++); + debug_printf("\n"); + } + +return TRUE; +} + + + + /************************************************* * Initialize a single GNUTLS session * *************************************************/ @@ -591,78 +709,60 @@ cipher. Arguments: side one of GNUTLS_SERVER, GNUTLS_CLIENT - expciphers expanded ciphers list + expciphers expanded ciphers list or NULL + expmac expanded MAC list or NULL + expkx expanded key-exchange list or NULL + expproto expanded protocol list or NULL Returns: a gnutls_session, or NULL if there is a problem */ static gnutls_session -tls_session_init(int side, uschar *expciphers) +tls_session_init(int side, uschar *expciphers, uschar *expmac, uschar *expkx, + uschar *expproto) { gnutls_session session; gnutls_init(&session, side); -/* Handle the list of permitted ciphers */ +/* Initialize the lists of permitted protocols, key-exchange methods, ciphers, +and MACs. */ memcpy(cipher_priority, default_cipher_priority, sizeof(cipher_priority)); +memcpy(mac_priority, default_mac_priority, sizeof(mac_priority)); +memcpy(kx_priority, default_kx_priority, sizeof(kx_priority)); +memcpy(proto_priority, default_proto_priority, sizeof(proto_priority)); + +/* The names OpenSSL uses in tls_require_ciphers are of the form DES-CBC3-SHA, +using hyphen separators. GnuTLS uses underscore separators. So that I can use +either form for tls_require_ciphers in my tests, and also for general +convenience, we turn hyphens into underscores before scanning the list. */ if (expciphers != NULL) { - int sep = 0; - BOOL first = TRUE; - uschar *cipher; - - /* The names OpenSSL uses are of the form DES-CBC3-SHA, using hyphen - separators. GnuTLS uses underscore separators. So that I can use either form - in my tests, and also for general convenience, we turn hyphens into - underscores before scanning the list. */ - uschar *s = expciphers; while (*s != 0) { if (*s == '-') *s = '_'; s++; } + } - while ((cipher = string_nextinlist(&expciphers, &sep, big_buffer, - big_buffer_size)) != NULL) - { - int i; - BOOL exclude = cipher[0] == '!'; - if (first && !exclude) cipher_priority[0] = 0; - first = FALSE; - - for (i = 0; i < sizeof(cipher_index)/sizeof(pri_item); i++) - { - uschar *ss = strstric(cipher, cipher_index[i].name, FALSE); - if (ss != NULL) - { - uschar *endss = ss + Ustrlen(cipher_index[i].name); - if ((ss == cipher || !isalnum(ss[-1])) && !isalnum(*endss)) - { - if (exclude) - remove_ciphers(cipher_priority, cipher_index[i].values); - else - { - if (!add_ciphers(cipher_priority, - sizeof(cipher_priority)/sizeof(pri_item), - cipher_index[i].values)) - { - log_write(0, LOG_MAIN|LOG_PANIC, "GnuTLS init failed: cipher " - "priority table overflow"); - gnutls_deinit(session); - return NULL; - } - } - } - } - } - } - - DEBUG(D_tls) - { - int *ptr = cipher_priority; - debug_printf("adjusted cipher priorities:"); - while (*ptr != 0) debug_printf(" %d", *ptr++); - debug_printf("\n"); - } +if ((expciphers != NULL && + !set_priority(cipher_priority, sizeof(cipher_priority)/sizeof(int), + expciphers, cipher_index, sizeof(cipher_index)/sizeof(pri_item), + US"cipher")) || + (expmac != NULL && + !set_priority(mac_priority, sizeof(mac_priority)/sizeof(int), + expmac, mac_index, sizeof(mac_index)/sizeof(pri_item), + US"MAC")) || + (expkx != NULL && + !set_priority(kx_priority, sizeof(kx_priority)/sizeof(int), + expkx, kx_index, sizeof(kx_index)/sizeof(pri_item), + US"key-exchange")) || + (expproto != NULL && + !set_priority(proto_priority, sizeof(proto_priority)/sizeof(int), + expproto, proto_index, sizeof(proto_index)/sizeof(pri_item), + US"protocol"))) + { + gnutls_deinit(session); + return NULL; } /* Define the various priorities */ @@ -670,7 +770,7 @@ if (expciphers != NULL) gnutls_cipher_set_priority(session, cipher_priority); gnutls_compression_set_priority(session, comp_priority); gnutls_kx_set_priority(session, kx_priority); -gnutls_protocol_set_priority(session, protocol_priority); +gnutls_protocol_set_priority(session, proto_priority); gnutls_mac_set_priority(session, mac_priority); gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred); @@ -739,7 +839,10 @@ the STARTTLS command. It must respond to that command, and then negotiate a TLS session. Arguments: - require_ciphers list of allowed ciphers + require_ciphers list of allowed ciphers or NULL + require_mac list of allowed MACs or NULL + require_kx list of allowed key_exchange methods or NULL + require_proto list of allowed protocols or NULL Returns: OK on success DEFER for errors before the start of the negotiation @@ -748,11 +851,15 @@ Returns: OK on success */ int -tls_server_start(uschar *require_ciphers) +tls_server_start(uschar *require_ciphers, uschar *require_mac, + uschar *require_kx, uschar *require_proto) { int rc; uschar *error; uschar *expciphers = NULL; +uschar *expmac = NULL; +uschar *expkx = NULL; +uschar *expproto = NULL; /* Check for previous activation */ @@ -774,7 +881,10 @@ rc = tls_init(NULL, tls_certificate, tls_privatekey, tls_verify_certificates, tls_crl); if (rc != OK) return rc; -if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers)) +if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers) || + !expand_check(require_mac, US"gnutls_require_mac", &expmac) || + !expand_check(require_kx, US"gnutls_require_kx", &expkx) || + !expand_check(require_proto, US"gnutls_require_proto", &expproto)) return FAIL; /* If this is a host for which certificate verification is mandatory or @@ -790,7 +900,8 @@ else if (verify_check_host(&tls_try_verify_hosts) == OK) /* Prepare for new connection */ -tls_session = tls_session_init(GNUTLS_SERVER, expciphers); +tls_session = tls_session_init(GNUTLS_SERVER, expciphers, expmac, expkx, + expproto); if (tls_session == NULL) return tls_error(US"tls_session_init", NULL, GNUTLS_E_MEMORY_ERROR); @@ -883,13 +994,16 @@ return OK; Arguments: fd the fd of the connection host connected host (for messages) - addr + addr the first address (not used) dhparam DH parameter file certificate certificate file privatekey private key file verify_certs file for certificate verify verify_crl CRL for verify - require_ciphers list of allowed ciphers + require_ciphers list of allowed ciphers or NULL + require_mac list of allowed MACs or NULL + require_kx list of allowed key_exchange methods or NULL + require_proto list of allowed protocols or NULL timeout startup timeout Returns: OK/DEFER/FAIL (because using common functions), @@ -899,10 +1013,14 @@ Returns: OK/DEFER/FAIL (because using common functions), int tls_client_start(int fd, host_item *host, address_item *addr, uschar *dhparam, uschar *certificate, uschar *privatekey, uschar *verify_certs, - uschar *verify_crl, uschar *require_ciphers, int timeout) + uschar *verify_crl, uschar *require_ciphers, uschar *require_mac, + uschar *require_kx, uschar *require_proto, int timeout) { const gnutls_datum *server_certs; uschar *expciphers = NULL; +uschar *expmac = NULL; +uschar *expkx = NULL; +uschar *expproto = NULL; uschar *error; unsigned int server_certs_size; int rc; @@ -914,10 +1032,15 @@ verify_requirement = (verify_certs == NULL)? VERIFY_NONE : VERIFY_REQUIRED; rc = tls_init(host, certificate, privatekey, verify_certs, verify_crl); if (rc != OK) return rc; -if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers)) +if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers) || + !expand_check(require_mac, US"gnutls_require_mac", &expmac) || + !expand_check(require_kx, US"gnutls_require_kx", &expkx) || + !expand_check(require_proto, US"gnutls_require_proto", &expproto)) return FAIL; -tls_session = tls_session_init(GNUTLS_CLIENT, expciphers); +tls_session = tls_session_init(GNUTLS_CLIENT, expciphers, expmac, expkx, + expproto); + if (tls_session == NULL) return tls_error(US "tls_session_init", host, GNUTLS_E_MEMORY_ERROR); diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 297b68b0d..1ae725b86 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.9 2007/01/08 10:50:18 ph10 Exp $ */ +/* $Cambridge: exim/src/src/tls-openssl.c,v 1.10 2007/01/18 15:35:42 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -598,6 +598,11 @@ 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 @@ -606,7 +611,8 @@ Returns: OK on success */ int -tls_server_start(uschar *require_ciphers) +tls_server_start(uschar *require_ciphers, uschar *require_mac, + uschar *require_kx, uschar *require_proto) { int rc; uschar *expciphers; @@ -746,12 +752,19 @@ return OK; Argument: fd the fd of the connection host connected host (for messages) + addr the first address dhparam DH parameter file certificate certificate file privatekey private key file 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 FAIL otherwise - note that tls_error() will not give DEFER @@ -761,7 +774,8 @@ Returns: OK on success int tls_client_start(int fd, host_item *host, address_item *addr, uschar *dhparam, uschar *certificate, uschar *privatekey, uschar *verify_certs, uschar *crl, - uschar *require_ciphers, int timeout) + uschar *require_ciphers, uschar *require_mac, uschar *require_kx, + uschar *require_proto, int timeout) { static uschar txt[256]; uschar *expciphers; diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index a5a96aeed..887b1ff82 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/transports/smtp.c,v 1.30 2007/01/08 10:50:20 ph10 Exp $ */ +/* $Cambridge: exim/src/src/transports/smtp.c,v 1.31 2007/01/18 15:35:43 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -63,6 +63,14 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, final_timeout) }, { "gethostbyname", opt_bool, (void *)offsetof(smtp_transport_options_block, gethostbyname) }, + #ifdef SUPPORT_TLS + { "gnutls_require_kx", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, gnutls_require_kx) }, + { "gnutls_require_mac", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, gnutls_require_mac) }, + { "gnutls_require_protocols", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, gnutls_require_proto) }, + #endif { "helo_data", opt_stringptr, (void *)offsetof(smtp_transport_options_block, helo_data) }, { "hosts", opt_stringptr, @@ -178,6 +186,9 @@ smtp_transport_options_block smtp_transport_option_defaults = { NULL, /* tls_crl */ NULL, /* tls_privatekey */ NULL, /* tls_require_ciphers */ + NULL, /* gnutls_require_kx */ + NULL, /* gnutls_require_mac */ + NULL, /* gnutls_require_proto */ NULL, /* tls_verify_certificates */ TRUE /* tls_tempfail_tryclear */ #endif @@ -1053,13 +1064,18 @@ if (tls_offered && !suppress_tls && else { - int rc = tls_client_start(inblock.sock, host, addrlist, + int rc = tls_client_start(inblock.sock, + host, + addrlist, NULL, /* No DH param */ ob->tls_certificate, ob->tls_privatekey, ob->tls_verify_certificates, ob->tls_crl, ob->tls_require_ciphers, + ob->gnutls_require_mac, + ob->gnutls_require_kx, + ob->gnutls_require_proto, ob->command_timeout); /* TLS negotiation failed; give an error. From outside, this function may diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index e759286bc..4f5dec707 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/transports/smtp.h,v 1.10 2007/01/08 10:50:20 ph10 Exp $ */ +/* $Cambridge: exim/src/src/transports/smtp.h,v 1.11 2007/01/18 15:35:43 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -50,6 +50,9 @@ typedef struct { uschar *tls_crl; uschar *tls_privatekey; uschar *tls_require_ciphers; + uschar *gnutls_require_kx; + uschar *gnutls_require_mac; + uschar *gnutls_require_proto; uschar *tls_verify_certificates; BOOL tls_tempfail_tryclear; #endif diff --git a/test/confs/2011 b/test/confs/2011 index f5f707eb1..334ca894f 100644 --- a/test/confs/2011 +++ b/test/confs/2011 @@ -1,6 +1,10 @@ # Exim test configuration 2011 SERVER = +CREQCIP = +CREQMAC = +SREQCIP = +SREQMAC = exim_path = EXIM_PATH host_lookup_order = bydns @@ -28,6 +32,9 @@ tls_privatekey = ${if eq {SERVER}{server}{DIR/aux-fixed/cert1}fail} tls_verify_hosts = * tls_verify_certificates = ${if eq {SERVER}{server}{DIR/aux-fixed/cert2}fail} +SREQCIP +SREQMAC + # ----- Routers ----- @@ -52,7 +59,7 @@ send_to_server: port = PORT_D tls_certificate = DIR/aux-fixed/cert2 tls_privatekey = DIR/aux-fixed/cert2 - tls_require_ciphers = IDEA-CBC-MD5 \ - ${if eq{$host_address}{127.0.0.1}{:DES-CBC3-SHA:RSA_ARCFOUR_SHA}} + CREQCIP + CREQMAC # End diff --git a/test/log/2011 b/test/log/2011 index 477754e37..7fc966b95 100644 --- a/test/log/2011 +++ b/test/log/2011 @@ -1,11 +1,50 @@ 1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss 1999-03-02 09:44:33 Start queue run: pid=pppp -qf 1999-03-02 09:44:33 10HmaX-0005vi-00 TLS error on connection to ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] (gnutls_handshake): No supported cipher suites have been found. -1999-03-02 09:44:33 10HmaX-0005vi-00 => userx@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] X=TLS-1.0:RSA_ARCFOUR_SHA1:16 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" +1999-03-02 09:44:33 10HmaX-0005vi-00 TLS error on connection to 127.0.0.1 [127.0.0.1] (gnutls_handshake): No supported cipher suites have been found. +1999-03-02 09:44:33 10HmaX-0005vi-00 TLS session failure: delivering unencrypted to 127.0.0.1 [127.0.0.1] (not in hosts_require_tls) +1999-03-02 09:44:33 10HmaX-0005vi-00 => userx@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed 1999-03-02 09:44:33 End queue run: pid=pppp -qf +1999-03-02 09:44:33 Start queue run: pid=pppp -qf +1999-03-02 09:44:33 10HmaY-0005vi-00 => userx@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS-1.0:RSA_ARCFOUR_SHA1:16 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" +1999-03-02 09:44:33 10HmaY-0005vi-00 Completed +1999-03-02 09:44:33 End queue run: pid=pppp -qf +1999-03-02 09:44:33 Start queue run: pid=pppp -qf +1999-03-02 09:44:33 10HmaZ-0005vi-00 => userx@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS-1.0:RSA_ARCFOUR_MD5:16 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" +1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed +1999-03-02 09:44:33 End queue run: pid=pppp -qf +1999-03-02 09:44:33 Start queue run: pid=pppp -qf +1999-03-02 09:44:33 10HmbA-0005vi-00 => userx@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS-1.0:RSA_ARCFOUR_MD5:16 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" +1999-03-02 09:44:33 10HmbA-0005vi-00 Completed +1999-03-02 09:44:33 End queue run: pid=pppp -qf +1999-03-02 09:44:33 Start queue run: pid=pppp -qf +1999-03-02 09:44:33 10HmbB-0005vi-00 => userx@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS-1.0:RSA_AES_256_CBC_SHA1:32 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" +1999-03-02 09:44:33 10HmbB-0005vi-00 Completed +1999-03-02 09:44:33 End queue run: pid=pppp -qf +1999-03-02 09:44:33 Start queue run: pid=pppp -qf +1999-03-02 09:44:33 10HmbC-0005vi-00 => userx@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS-1.0:RSA_AES_256_CBC_SHA1:32 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" +1999-03-02 09:44:33 10HmbC-0005vi-00 Completed +1999-03-02 09:44:33 End queue run: pid=pppp -qf +1999-03-02 09:44:33 Start queue run: pid=pppp -qf +1999-03-02 09:44:33 10HmbD-0005vi-00 => userx@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=SSL 3.0:RSA_AES_256_CBC_SHA1:32 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" +1999-03-02 09:44:33 10HmbD-0005vi-00 Completed +1999-03-02 09:44:33 End queue run: pid=pppp -qf +1999-03-02 09:44:33 Start queue run: pid=pppp -qf +1999-03-02 09:44:33 10HmbE-0005vi-00 => userx@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS-1.0:RSA_ARCFOUR_MD5:16 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" +1999-03-02 09:44:33 10HmbE-0005vi-00 Completed +1999-03-02 09:44:33 End queue run: pid=pppp -qf ******** SERVER ******** 1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225 1999-03-02 09:44:33 TLS error on connection from the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] (gnutls_handshake): A TLS packet with unexpected length was received. -1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLS-1.0:RSA_ARCFOUR_SHA1:16 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" S=sss id=E10HmaX-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 TLS error on connection from localhost (myhost.test.ex) [127.0.0.1] (gnutls_handshake): A TLS packet with unexpected length was received. +1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex H=localhost (myhost.test.ex) [127.0.0.1] P=esmtp S=sss id=E10HmaX-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS-1.0:RSA_ARCFOUR_SHA1:16 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" S=sss id=E10HmaX-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS-1.0:RSA_ARCFOUR_MD5:16 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" S=sss id=E10HmaX-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS-1.0:RSA_ARCFOUR_MD5:16 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" S=sss id=E10HmaX-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS-1.0:RSA_AES_256_CBC_SHA1:32 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" S=sss id=E10HmaX-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 10HmbD-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS-1.0:RSA_AES_256_CBC_SHA1:32 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" S=sss id=E10HmaX-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 10HmbE-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=SSL 3.0:RSA_AES_256_CBC_SHA1:32 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" S=sss id=E10HmaX-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225 +1999-03-02 09:44:33 10HmbF-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS-1.0:RSA_ARCFOUR_MD5:16 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" S=sss id=E10HmaX-0005vi-00@myhost.test.ex diff --git a/test/scripts/2000-GnuTLS/2011 b/test/scripts/2000-GnuTLS/2011 index 4e48ebce6..6f72fba80 100644 --- a/test/scripts/2000-GnuTLS/2011 +++ b/test/scripts/2000-GnuTLS/2011 @@ -1,10 +1,38 @@ -# TLS client: require_ciphers +# TLS client & server: (gnu)tls_require_xxx gnutls +# Start up the server exim -DSERVER=server -bd -oX PORT_D **** +# This puts a message on the queue (queue_only is set). exim userx@test.ex Testing **** +# This will fail to deliver encrypted because there are no acceptable +# ciphers, so it will deliver in clear. +exim -qf -DCREQCIP=tls_require_ciphers=IDEA-CBC-MD5 +**** +# This delivers the message to the server, where it will remain +# on the queue because queue_only is set. +exim -qf -DCREQCIP=tls_require_ciphers=IDEA-CBC-MD5:DES-CBC3-SHA:RSA_ARCFOUR_SHA +**** +# So we can deliver it again and again, with different parameters. +exim -qf -DCREQMAC=gnutls_require_mac=MD5 +**** +exim -qf -DCREQMAC=gnutls_require_mac=!SHA1 +**** +exim -qf -DCREQMAC=gnutls_require_mac=MD5:SHA +**** +exim -qf -DCREQMAC=gnutls_require_kx=!DHE +**** +exim -qf -DCREQMAC=gnutls_require_protocols=SSL3 +**** +# Restart the server with a cipher restriction +killdaemon +exim -DSERVER=server \ + -DSREQCIP=tls_require_ciphers=ARCFOUR \ + -DSREQMAC=gnutls_require_mac=MD5 \ + -bd -oX PORT_D +**** exim -qf **** killdaemon -- 2.30.2