[exim-cvs] Deal with GnuTLS DH generation overshoot

Top Page
Delete this message
Reply to this message
Author: Exim Git Commits Mailing List
Date:  
To: exim-cvs
Subject: [exim-cvs] Deal with GnuTLS DH generation overshoot
Gitweb: http://git.exim.org/exim.git/commitdiff/201f5254b5bbba620893cd607ea182bc25c123d2
Commit:     201f5254b5bbba620893cd607ea182bc25c123d2
Parent:     51fb80db26ea90194e91bfb4b9676715f1466dfc
Author:     Phil Pennock <pdp@???>
AuthorDate: Sun May 27 01:17:04 2012 -0400
Committer:  Phil Pennock <pdp@???>
CommitDate: Sun May 27 01:17:04 2012 -0400


    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 9eaf9e8..9c2bf19 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 60f4020..4339bec 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 aa2f925..214007e 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,