X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/36f12725ebda2bfd6ed4fe98b0eeaf1ce01f2604..bbaa92096a0720af1625982d024befac65d5909e:/src/src/tls-gnu.c diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index c26a9bac6..7e87dded0 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -1,10 +1,8 @@ -/* $Cambridge: exim/src/src/tls-gnu.c,v 1.22 2009/10/14 13:52:48 nm4 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2007 */ +/* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ /* This module provides TLS (aka SSL) support for Exim using the GnuTLS @@ -14,6 +12,13 @@ is based on a patch that was contributed by Nikos Mavroyanopoulos. No cryptographic code is included in Exim. All this module does is to call functions from the GnuTLS library. */ +/* Note: This appears to be using an old API from compat.h; it is likely that +someone familiary with GnuTLS programming could rework a lot of this to a +modern API and perhaps remove the explicit knowledge of crypto algorithms from +Exim. Such a re-work would be most welcome and we'd sacrifice support for +older GnuTLS releases without too many qualms -- maturity and experience +in crypto libraries tends to improve their robustness against attack. +Frankly, if you maintain it, you decide what's supported and what isn't. */ /* Heading stuff for GnuTLS */ @@ -48,6 +53,13 @@ static int verify_requirement; and space into which it can be copied and altered. */ static const int default_proto_priority[16] = { + /* These are gnutls_protocol_t enum values */ +#if GNUTLS_VERSION_MAJOR > 1 || GNUTLS_VERSION_MINOR >= 7 + GNUTLS_TLS1_2, +#endif +#if GNUTLS_VERSION_MAJOR > 1 || GNUTLS_VERSION_MINOR >= 2 + GNUTLS_TLS1_1, +#endif GNUTLS_TLS1, GNUTLS_SSL3, 0 }; @@ -91,11 +103,27 @@ typedef struct pri_item { } pri_item; -static int tls1_codes[] = { GNUTLS_TLS1, 0 }; +#if GNUTLS_VERSION_MAJOR > 1 || GNUTLS_VERSION_MINOR >= 7 +static int tls1_2_codes[] = { GNUTLS_TLS1_2, 0 }; +#endif +#if GNUTLS_VERSION_MAJOR > 1 || GNUTLS_VERSION_MINOR >= 2 +static int tls1_1_codes[] = { GNUTLS_TLS1_1, 0 }; +#endif +/* more recent libraries define this as an equivalent value to the +canonical GNUTLS_TLS1_0; since they're the same, we stick to the +older name. */ +static int tls1_0_codes[] = { GNUTLS_TLS1, 0 }; static int ssl3_codes[] = { GNUTLS_SSL3, 0 }; static pri_item proto_index[] = { - { US"TLS1", tls1_codes }, +#if GNUTLS_VERSION_MAJOR > 1 || GNUTLS_VERSION_MINOR >= 7 + { US"TLS1.2", tls1_2_codes }, +#endif +#if GNUTLS_VERSION_MAJOR > 1 || GNUTLS_VERSION_MINOR >= 2 + { US"TLS1.1", tls1_1_codes }, +#endif + { US"TLS1.0", tls1_0_codes }, + { US"TLS1", tls1_0_codes }, { US"SSL3", ssl3_codes } }; @@ -207,10 +235,10 @@ Returns: TRUE/FALSE static BOOL verify_certificate(gnutls_session session, const char **error) { -int verify; +int rc = -1; uschar *dn_string = US""; const gnutls_datum *cert; -unsigned int cert_size = 0; +unsigned int verify, cert_size = 0; *error = NULL; @@ -234,7 +262,7 @@ if (cert != NULL) dn_string = string_copy_malloc(buff); } - verify = gnutls_certificate_verify_peers(session); + rc = gnutls_certificate_verify_peers2(session, &verify); } else { @@ -246,7 +274,7 @@ else /* Handle the result of verification. INVALID seems to be set as well as REVOKED, but leave the test for both. */ -if ((verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED)) != 0) +if ((rc < 0) || (verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED)) != 0) { tls_certificate_verified = FALSE; if (*error == NULL) *error = ((verify & GNUTLS_CERT_REVOKED) != 0)? @@ -792,6 +820,18 @@ if (verify_requirement != VERIFY_NONE) gnutls_db_set_cache_expiration(session, ssl_session_timeout); +/* Reduce security in favour of increased compatibility, if the admin +decides to make that trade-off. */ +if (gnutls_compat_mode) + { +#if LIBGNUTLS_VERSION_NUMBER >= 0x020104 + DEBUG(D_tls) debug_printf("lowering GnuTLS security, compatibility mode\n"); + gnutls_session_enable_compatibility_mode(session); +#else + DEBUG(D_tls) debug_printf("Unable to set gnutls_compat_mode - GnuTLS version too old\n"); +#endif + } + DEBUG(D_tls) debug_printf("initialized GnuTLS session\n"); return session; } @@ -814,23 +854,43 @@ construct_cipher_name(gnutls_session session) { static uschar cipherbuf[256]; uschar *ver; -int bits, c, kx, mac; +int c, kx, mac; +#ifdef GNUTLS_CB_TLS_UNIQUE +int rc; +gnutls_datum_t channel; +#endif ver = string_copy( US gnutls_protocol_get_name(gnutls_protocol_get_version(session))); if (Ustrncmp(ver, "TLS ", 4) == 0) ver[3] = '-'; /* Don't want space */ c = gnutls_cipher_get(session); -bits = gnutls_cipher_get_key_size(c); +/* returns size in "bytes" */ +tls_bits = gnutls_cipher_get_key_size(c) * 8; mac = gnutls_mac_get(session); kx = gnutls_kx_get(session); string_format(cipherbuf, sizeof(cipherbuf), "%s:%s:%u", ver, - gnutls_cipher_suite_get_name(kx, c, mac), bits); + gnutls_cipher_suite_get_name(kx, c, mac), tls_bits); tls_cipher = cipherbuf; DEBUG(D_tls) debug_printf("cipher: %s\n", cipherbuf); + +if (tls_channelbinding_b64) + free(tls_channelbinding_b64); +tls_channelbinding_b64 = NULL; + +#ifdef GNUTLS_CB_TLS_UNIQUE +channel = { NULL, 0 }; +rc = gnutls_session_channel_binding(session, GNUTLS_CB_TLS_UNIQUE, &channel); +if (rc) { + DEBUG(D_tls) debug_printf("Channel binding error: %s\n", gnutls_strerror(rc)); +} else { + tls_channelbinding_b64 = auth_b64encode(channel.data, (int)channel.size); + DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage.\n"); +} +#endif } @@ -995,6 +1055,7 @@ Arguments: dhparam DH parameter file certificate certificate file privatekey private key file + sni TLS SNI to send to remote host verify_certs file for certificate verify verify_crl CRL for verify require_ciphers list of allowed ciphers or NULL @@ -1009,8 +1070,9 @@ 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, uschar *require_mac, + uschar *certificate, uschar *privatekey, uschar *sni ARG_UNUSED, + uschar *verify_certs, uschar *verify_crl, + uschar *require_ciphers, uschar *require_mac, uschar *require_kx, uschar *require_proto, int timeout) { const gnutls_datum *server_certs; @@ -1314,8 +1376,10 @@ Returns: nothing void tls_version_report(FILE *f) { -fprintf(f, "GnuTLS compile-time version: %s\n", LIBGNUTLS_VERSION); -fprintf(f, "GnuTLS runtime version: %s\n", gnutls_check_version(NULL)); +fprintf(f, "Library version: GnuTLS: Compile: %s\n" + " Runtime: %s\n", + LIBGNUTLS_VERSION, + gnutls_check_version(NULL)); } /* End of tls-gnu.c */