From 201f5254b5bbba620893cd607ea182bc25c123d2 Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Sun, 27 May 2012 01:17:04 -0400 Subject: [PATCH 1/1] Deal with GnuTLS DH generation overshoot --- doc/doc-docbook/spec.xfpt | 18 ++++++++++++++++-- doc/doc-txt/GnuTLS-FAQ.txt | 31 +++++++++++++++++++++++++++++++ src/src/tls-gnu.c | 23 +++++++++++++++++++++-- 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 9eaf9e804..9c2bf199f 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -15697,6 +15697,10 @@ by Thunderbird, while GnuTLS was suggesting 2432 bits as normal. If you prefer more security and are willing to break some clients, raise this number. + +Note that the value passed to GnuTLS for *generating* a new prime may be a +little less than this figure, because GnuTLS is inexact and may produce a +larger prime than requested. .wen @@ -15708,8 +15712,8 @@ This is used only for OpenSSL. When Exim is linked with GnuTLS, this option is ignored. See section &<>& for further details. .new -If the DH bit-count from loading the file is greater than tls_dh_max_bits then -it will be ignored. +If the DH bit-count from loading the file is greater than &%tls_dh_max_bits$& +then it will be ignored. .wen @@ -25070,6 +25074,10 @@ renaming. The relevant commands are something like this: # chown exim:exim new-params # chmod 0600 new-params # certtool --generate-dh-params --bits 2236 >>new-params +# openssl dhparam -noout -text -in new-params | head +[ check the first line, make sure it's not more than 2236; + if it is, then go back to the start ("rm") and repeat + until the size generated is at most the size requested ] # chmod 0400 new-params # mv new-params gnutls-params-2236 .endd @@ -25092,6 +25100,12 @@ The filename and bits used will change as the GnuTLS maintainers change the value for their parameter &`GNUTLS_SEC_PARAM_NORMAL`&, as clamped by &%tls_dh_max_bits%&. At the time of writing (mid 2012), GnuTLS 2.12 recommends 2432 bits, while NSS is limited to 2236 bits. + +In fact, the requested value will be *lower* than &%tls_dh_max_bits%&, to +increase the chance of the generated prime actually being within acceptable +bounds, as GnuTLS has been observed to overshoot. Note the check step in the +procedure above. There is no sane procedure available to Exim to double-check +the size of the generated prime, so it might still be too large. .wen diff --git a/doc/doc-txt/GnuTLS-FAQ.txt b/doc/doc-txt/GnuTLS-FAQ.txt index 60f402004..4339becac 100644 --- a/doc/doc-txt/GnuTLS-FAQ.txt +++ b/doc/doc-txt/GnuTLS-FAQ.txt @@ -232,6 +232,37 @@ security versus compatibility by raising it. A future release of Exim may even let the administrator tell GnuTLS to ask for more or less than "NORMAL". +To add to the fun, the size of the prime returned by GnuTLS when we call +gnutls_dh_params_generate2() is not limited to be the requested size. GnuTLS +has a tendency to overshoot. 2237 bit primes are common when 2236 is +requested, and higher still have been observed. Further, there is no API to +ask how large the prime bundled up inside the parameter is; the most we can do +is ask how large the DH prime used in an active TLS session is. Since we're +not able to use GnuTLS API calls (and exporting to PKCS3 and then calling +OpenSSL routines would be undiplomatic, plus add a library dependency), we're +left with no way to actually know the size of the freshly generated DH prime. + +Thus we check if the the value returned is at least 10 more than the minimum +we'll accept as a client (EXIM_CLIENT_DH_MIN_BITS, see below, defaults to +1024) and if it is, we subtract 10. Then we reluctantly deploy a strategy +called "hope". This is not guaranteed to be successful; in the first code +pass on this logic, we subtracted 3, asked for 2233 bits and got 2240 in the +first test. + +If you see Thunderbird clients still failing, then as a user who can see into +Exim's spool directory, run: + +$ openssl dhparam -noout -text -in /path/to/spool/gnutls-params-2236 | head + +Ideally, the first line will read "PKCS#3 DH Parameters: (2236 bit)". If the +count is more than 2236, then remove the file and let Exim regenerate it, or +generate one yourself and move it into place. Ideally use "openssl dhparam" +to generate it, and then wait a very long time; at least this way, the size +will be correct. (This developer is now convinced that Exim 4.81 should +bundle the suggested primes from a few RFCs and let the administrator choose +those.) + + A TLS client does not get to choose the DH prime used, but can choose a minimum acceptable value. For Exim, this is a compile-time constant called "EXIM_CLIENT_DH_MIN_BITS" of 1024, which can be overruled in "Local/Makefile". diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index aa2f92514..214007e5f 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -483,6 +483,7 @@ case. */ if (rc < 0) { uschar *temp_fn; + unsigned int dh_bits_gen = dh_bits; if ((PATH_MAX - Ustrlen(filename)) < 10) return tls_error(US"Filename too long to generate replacement", @@ -494,8 +495,26 @@ if (rc < 0) return tls_error(US"Unable to open temp file", strerror(errno), NULL); (void)fchown(fd, exim_uid, exim_gid); /* Probably not necessary */ - DEBUG(D_tls) debug_printf("generating %d bits Diffie-Hellman key ...\n", dh_bits); - rc = gnutls_dh_params_generate2(dh_server_params, dh_bits); + /* GnuTLS overshoots! + * If we ask for 2236, we might get 2237 or more. + * But there's no way to ask GnuTLS how many bits there really are. + * We can ask how many bits were used in a TLS session, but that's it! + * The prime itself is hidden behind too much abstraction. + * So we ask for less, and proceed on a wing and a prayer. + * First attempt, subtracted 3 for 2233 and got 2240. + */ + if (dh_bits > EXIM_CLIENT_DH_MIN_BITS + 10) + { + dh_bits_gen = dh_bits - 10; + DEBUG(D_tls) + debug_printf("being paranoid about DH generation, make it '%d' bits'\n", + dh_bits_gen); + } + + DEBUG(D_tls) + debug_printf("requesting generation of %d bit Diffie-Hellman prime ...\n", + dh_bits_gen); + rc = gnutls_dh_params_generate2(dh_server_params, dh_bits_gen); exim_gnutls_err_check(US"gnutls_dh_params_generate2"); /* gnutls_dh_params_export_pkcs3() will tell us the exact size, every time, -- 2.30.2