-/* $Cambridge: exim/src/src/tls-gnu.c,v 1.20 2008/09/03 18:53:29 fanf2 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
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 */
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 };
} 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 }
};
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;
dn_string = string_copy_malloc(buff);
}
- verify = gnutls_certificate_verify_peers(session);
+ rc = gnutls_certificate_verify_peers2(session, &verify);
}
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)?
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;
}
{
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
}
ssl_xfer_error = 1;
return EOF;
}
-
+#ifndef DISABLE_DKIM
+ dkim_exim_verify_feed(ssl_xfer_buffer, inbytes);
+#endif
ssl_xfer_buffer_hwm = inbytes;
ssl_xfer_buffer_lwm = 0;
}
tls_active = -1;
}
+
+
+
+/*************************************************
+* Report the library versions. *
+*************************************************/
+
+/* See a description in tls-openssl.c for an explanation of why this exists.
+
+Arguments: a FILE* to print the results to
+Returns: nothing
+*/
+
+void
+tls_version_report(FILE *f)
+{
+fprintf(f, "Library version: GnuTLS: Compile: %s\n"
+ " Runtime: %s\n",
+ LIBGNUTLS_VERSION,
+ gnutls_check_version(NULL));
+}
+
/* End of tls-gnu.c */