Guards for older releases of GnuTLS.
authorPhil Pennock <pdp@exim.org>
Thu, 17 May 2012 05:32:13 +0000 (01:32 -0400)
committerPhil Pennock <pdp@exim.org>
Thu, 17 May 2012 05:32:13 +0000 (01:32 -0400)
gnutls_sec_param_to_pk_bits() and gnutls_rnd() are both new as of
GnuTLS 2.12.x.  Guard their usage on 2.12.0+ at compile time.

In older versions, the vaguely_random_number() function just immediately
calls the fallback, so it's the same as before this change (just one
extra indirection in the code-path).

Define a constant of 1024 for dh-bits for use in those old releases
where GnuTLS won't tell us how many we should use.

Change the on-disk filename for generated D-H params again, replacing
the -normal with -<bitcount>, so that it's 1024 or whatever, and as
the value changes, Exim will automatically start using the new value.

doc/doc-docbook/spec.xfpt
doc/doc-txt/ChangeLog
doc/doc-txt/NewStuff
src/src/tls-gnu.c

index 22b805c18cd2e41ea3ce5a41bf769435ef8bcc6b..6d1802b6b4097132eab3bf02ad29c7f33b197d1c 100644 (file)
@@ -9772,7 +9772,8 @@ supplied number and is at least 0.  The quality of this randomness depends
 on how Exim was built; the values are not suitable for keying material.
 If Exim is linked against OpenSSL then RAND_pseudo_bytes() is used.
 .new
 on how Exim was built; the values are not suitable for keying material.
 If Exim is linked against OpenSSL then RAND_pseudo_bytes() is used.
 .new
-if Exim is linked against GnuTLS then gnutls_rnd(GNUTLS_RND_NONCE) is used.
+If Exim is linked against GnuTLS then gnutls_rnd(GNUTLS_RND_NONCE) is used,
+for versions of GnuTLS with that function.
 .wen
 Otherwise, the implementation may be arc4random(), random() seeded by
 srandomdev() or srandom(), or a custom implementation even weaker than
 .wen
 Otherwise, the implementation may be arc4random(), random() seeded by
 srandomdev() or srandom(), or a custom implementation even weaker than
@@ -24964,7 +24965,8 @@ implementation, then patches are welcome.
 GnuTLS uses D-H parameters that may take a substantial amount of time
 to compute. It is unreasonable to re-compute them for every TLS session.
 Therefore, Exim keeps this data in a file in its spool directory, called
 GnuTLS uses D-H parameters that may take a substantial amount of time
 to compute. It is unreasonable to re-compute them for every TLS session.
 Therefore, Exim keeps this data in a file in its spool directory, called
-&_gnutls-params-normal_&.
+&_gnutls-params-NNNN_& for some value of NNNN, corresponding to the number
+of bits requested.
 The file is owned by the Exim user and is readable only by
 its owner. Every Exim process that start up GnuTLS reads the D-H
 parameters from this file. If the file does not exist, the first Exim process
 The file is owned by the Exim user and is readable only by
 its owner. Every Exim process that start up GnuTLS reads the D-H
 parameters from this file. If the file does not exist, the first Exim process
@@ -24983,7 +24985,7 @@ until enough randomness (entropy) is available. This may cause Exim to hang for
 a substantial amount of time, causing timeouts on incoming connections.
 
 The solution is to generate the parameters externally to Exim. They are stored
 a substantial amount of time, causing timeouts on incoming connections.
 
 The solution is to generate the parameters externally to Exim. They are stored
-in &_gnutls-params-normal_& in PEM format, which means that they can be
+in &_gnutls-params-N_& in PEM format, which means that they can be
 generated externally using the &(certtool)& command that is part of GnuTLS.
 
 To replace the parameters with new ones, instead of deleting the file
 generated externally using the &(certtool)& command that is part of GnuTLS.
 
 To replace the parameters with new ones, instead of deleting the file
@@ -24991,20 +24993,27 @@ and letting Exim re-create it, you can generate new parameters using
 &(certtool)& and, when this has been done, replace Exim's cache file by
 renaming. The relevant commands are something like this:
 .code
 &(certtool)& and, when this has been done, replace Exim's cache file by
 renaming. The relevant commands are something like this:
 .code
+# ls
+[ look for file; assume gnutls-params-1024 is the most recent ]
 # rm -f new-params
 # touch new-params
 # chown exim:exim new-params
 # chmod 0600 new-params
 # rm -f new-params
 # touch new-params
 # chown exim:exim new-params
 # chmod 0600 new-params
-# certtool --generate-dh-params >>new-params
+# certtool --generate-dh-params --bits 1024 >>new-params
 # chmod 0400 new-params
 # chmod 0400 new-params
-# mv new-params gnutls-params-normal
+# mv new-params gnutls-params-1024
 .endd
 If Exim never has to generate the parameters itself, the possibility of
 stalling is removed.
 
 .endd
 If Exim never has to generate the parameters itself, the possibility of
 stalling is removed.
 
-The filename changed in Exim 4.78, to gain the -normal suffix, corresponding
-to the GnuTLS constant &`GNUTLS_SEC_PARAM_NORMAL`&, defining the number of
-bits to include.  At time of writing, NORMAL corresponds to 2432 bits for D-H.
+The filename changed in Exim 4.78, to gain the -bits suffix.  The value which
+Exim will choose depends upon the version of GnuTLS in use.  For older GnuTLS,
+the value remains hard-coded in Exim as 1024.  As of GnuTLS 2.12.x, there is
+a way for Exim to ask for the "normal" number of bits for D-H public-key usage,
+and Exim does so.  Exim thus removes itself from the policy decision, and the
+filename and bits used change as the GnuTLS maintainers change the value for
+their parameter &`GNUTLS_SEC_PARAM_NORMAL`&.  At the time of writing, this
+gives 2432 bits.
 .wen
 
 
 .wen
 
 
index ff463b1a44ea1f729e7e62d2c9c54042ba86831d..a93041e62889eaa8488e630c0e4600dc6c99f376 100644 (file)
@@ -106,6 +106,7 @@ PP/25 Revamped GnuTLS support, passing tls_require_ciphers to
       gnutls_priority_init, ignoring Exim options gnutls_require_kx,
       gnutls_require_mac & gnutls_require_protocols (no longer supported).
       Added SNI support via GnuTLS too.
       gnutls_priority_init, ignoring Exim options gnutls_require_kx,
       gnutls_require_mac & gnutls_require_protocols (no longer supported).
       Added SNI support via GnuTLS too.
+      Made ${randint:..} supplier available, if using not-too-old GnuTLS.
 
 PP/26 Added EXPERIMENTAL_OCSP for OpenSSL.
 
 
 PP/26 Added EXPERIMENTAL_OCSP for OpenSSL.
 
index 82eaeb73bd607fffa3641cd8391776b82f4fe02c..7b3b5aff018148e944c5f467c6e777a94059dd93 100644 (file)
@@ -80,6 +80,9 @@ Version 4.78
 
     SNI support has been added to Exim's GnuTLS integration too.
 
 
     SNI support has been added to Exim's GnuTLS integration too.
 
+    For sufficiently recent GnuTLS libraries, ${randint:..} will now use
+    gnutls_rnd(), asking for GNUTLS_RND_NONCE level randomness.
+
 12. With OpenSSL, if built with EXPERIMENTAL_OCSP, a new option tls_ocsp_file
     is now available.  If the contents of the file are valid, then Exim will
     send that back in response to a TLS status request; this is OCSP Stapling.
 12. With OpenSSL, if built with EXPERIMENTAL_OCSP, a new option tls_ocsp_file
     is now available.  If the contents of the file are valid, then Exim will
     send that back in response to a TLS status request; this is OCSP Stapling.
index f0e391f9713ef53c7edd7940138a411e3a4ea16c..6dd76cd40feca8f7b4d9508aa15dd120e5b5e686 100644 (file)
@@ -140,12 +140,6 @@ static const char * const exim_default_gnutls_priority = "NORMAL";
 static BOOL exim_gnutls_base_init_done = FALSE;
 
 
 static BOOL exim_gnutls_base_init_done = FALSE;
 
 
-/* ------------------------------------------------------------------------ */
-/* Callback declarations */
-
-static void exim_gnutls_logger_cb(int level, const char *message);
-static int exim_sni_handling_cb(gnutls_session_t session);
-
 /* ------------------------------------------------------------------------ */
 /* macros */
 
 /* ------------------------------------------------------------------------ */
 /* macros */
 
@@ -158,6 +152,11 @@ callbacks. */
 
 #define EXIM_CLIENT_DH_MIN_BITS 1024
 
 
 #define EXIM_CLIENT_DH_MIN_BITS 1024
 
+/* With GnuTLS 2.12.x+ we have gnutls_sec_param_to_pk_bits() with which we
+can ask for a bit-strength.  Without that, we stick to the constant we had
+before, for now. */
+#define EXIM_SERVER_DH_BITS_PRE2_12 1024
+
 #define exim_gnutls_err_check(Label) do { \
   if (rc != GNUTLS_E_SUCCESS) { return tls_error((Label), gnutls_strerror(rc), host); } } while (0)
 
 #define exim_gnutls_err_check(Label) do { \
   if (rc != GNUTLS_E_SUCCESS) { return tls_error((Label), gnutls_strerror(rc), host); } } while (0)
 
@@ -170,8 +169,25 @@ callbacks. */
 
 #if GNUTLS_VERSION_NUMBER >= 0x020c00
 #define HAVE_GNUTLS_SESSION_CHANNEL_BINDING
 
 #if GNUTLS_VERSION_NUMBER >= 0x020c00
 #define HAVE_GNUTLS_SESSION_CHANNEL_BINDING
+#define HAVE_GNUTLS_SEC_PARAM_CONSTANTS
+#define HAVE_GNUTLS_RND
 #endif
 
 #endif
 
+
+
+
+/* ------------------------------------------------------------------------ */
+/* Callback declarations */
+
+#if EXIM_GNUTLS_LIBRARY_LOG_LEVEL >= 0
+static void exim_gnutls_logger_cb(int level, const char *message);
+#endif
+
+static int exim_sni_handling_cb(gnutls_session_t session);
+
+
+
+
 /* ------------------------------------------------------------------------ */
 /* Static functions */
 
 /* ------------------------------------------------------------------------ */
 /* Static functions */
 
@@ -380,21 +396,30 @@ gnutls_datum m;
 uschar filename[PATH_MAX];
 size_t sz;
 host_item *host = NULL; /* dummy for macros */
 uschar filename[PATH_MAX];
 size_t sz;
 host_item *host = NULL; /* dummy for macros */
-const char * const dh_param_fn_ext = "normal"; /* change as dh_bits changes */
 
 DEBUG(D_tls) debug_printf("Initialising GnuTLS server params.\n");
 
 rc = gnutls_dh_params_init(&dh_server_params);
 exim_gnutls_err_check(US"gnutls_dh_params_init");
 
 
 DEBUG(D_tls) debug_printf("Initialising GnuTLS server params.\n");
 
 rc = gnutls_dh_params_init(&dh_server_params);
 exim_gnutls_err_check(US"gnutls_dh_params_init");
 
-/* If you change this, also change dh_param_fn_ext so that we can use a
+#ifdef HAVE_GNUTLS_SEC_PARAM_CONSTANTS
+/* If you change this constant, also change dh_param_fn_ext so that we can use a
 different filename and ensure we have sufficient bits. */
 dh_bits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, GNUTLS_SEC_PARAM_NORMAL);
 if (!dh_bits)
   return tls_error(US"gnutls_sec_param_to_pk_bits() failed", NULL, NULL);
 different filename and ensure we have sufficient bits. */
 dh_bits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, GNUTLS_SEC_PARAM_NORMAL);
 if (!dh_bits)
   return tls_error(US"gnutls_sec_param_to_pk_bits() failed", NULL, NULL);
+DEBUG(D_tls)
+  debug_printf("GnuTLS tells us that for D-H PL, NORMAL is %d bits.\n",
+      dh_bits);
+#else
+dh_bits = EXIM_SERVER_DH_BITS_PRE2_12;
+DEBUG(D_tls)
+  debug_printf("GnuTLS lacks gnutls_sec_param_to_pk_bits(), using %d bits.\n",
+      dh_bits);
+#endif
 
 if (!string_format(filename, sizeof(filename),
 
 if (!string_format(filename, sizeof(filename),
-      "%s/gnutls-params-%s", spool_directory, dh_param_fn_ext))
+      "%s/gnutls-params-%d", spool_directory, dh_bits))
   return tls_error(US"overlong filename", NULL, NULL);
 
 /* Open the cache file for reading and if successful, read it and set up the
   return tls_error(US"overlong filename", NULL, NULL);
 
 /* Open the cache file for reading and if successful, read it and set up the
@@ -1095,11 +1120,13 @@ return TRUE;
  *   gnutls_global_set_log_function()
  *   gnutls_global_set_log_level() 0..9
  */
  *   gnutls_global_set_log_function()
  *   gnutls_global_set_log_level() 0..9
  */
+#if EXIM_GNUTLS_LIBRARY_LOG_LEVEL >= 0
 static void
 exim_gnutls_logger_cb(int level, const char *message)
 {
   DEBUG(D_tls) debug_printf("GnuTLS<%d>: %s\n", level, message);
 }
 static void
 exim_gnutls_logger_cb(int level, const char *message)
 {
   DEBUG(D_tls) debug_printf("GnuTLS<%d>: %s\n", level, message);
 }
+#endif
 
 
 /* Called after client hello, should handle SNI work.
 
 
 /* Called after client hello, should handle SNI work.
@@ -1667,6 +1694,7 @@ Arguments:
 Returns     a random number in range [0, max-1]
 */
 
 Returns     a random number in range [0, max-1]
 */
 
+#ifdef HAVE_GNUTLS_RND
 int
 vaguely_random_number(int max)
 {
 int
 vaguely_random_number(int max)
 {
@@ -1704,6 +1732,13 @@ for (p = smallbuf; needed_len; --needed_len, ++p)
  * smooth distribution and cares enough then they should submit a patch then. */
 return r % max;
 }
  * smooth distribution and cares enough then they should submit a patch then. */
 return r % max;
 }
+#else /* HAVE_GNUTLS_RND */
+int
+vaguely_random_number(int max)
+{
+  return vaguely_random_number_fallback(max);
+}
+#endif /* HAVE_GNUTLS_RND */