summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhil Pennock <pdp@exim.org>2012-05-27 01:17:04 -0400
committerPhil Pennock <pdp@exim.org>2012-05-27 01:17:04 -0400
commit201f5254b5bbba620893cd607ea182bc25c123d2 (patch)
treed4148a04a91984b38b56e334abc976c16aa3037c
parent51fb80db26ea90194e91bfb4b9676715f1466dfc (diff)
Deal with GnuTLS DH generation overshoot
-rw-r--r--doc/doc-docbook/spec.xfpt18
-rw-r--r--doc/doc-txt/GnuTLS-FAQ.txt31
-rw-r--r--src/src/tls-gnu.c23
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 &<<SECTopenvsgnu>>& 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,