From: Phil Pennock Date: Thu, 17 May 2012 05:32:13 +0000 (-0400) Subject: Guards for older releases of GnuTLS. X-Git-Tag: exim-4_80_RC1~16 X-Git-Url: https://git.exim.org/exim.git/commitdiff_plain/af3498d60d7cae92d50e56353ae19f304b84e6ca?hp=eae0036b2dfac1547351908f77a6154b898c45d6 Guards for older releases of GnuTLS. 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 -, so that it's 1024 or whatever, and as the value changes, Exim will automatically start using the new value. --- diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 22b805c18..6d1802b6b 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -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 -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 @@ -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-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 @@ -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 -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 @@ -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 +# 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 -# certtool --generate-dh-params >>new-params +# certtool --generate-dh-params --bits 1024 >>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. -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 diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index ff463b1a4..a93041e62 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -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. + Made ${randint:..} supplier available, if using not-too-old GnuTLS. PP/26 Added EXPERIMENTAL_OCSP for OpenSSL. diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index 82eaeb73b..7b3b5aff0 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -80,6 +80,9 @@ Version 4.78 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. diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index f0e391f97..6dd76cd40 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -140,12 +140,6 @@ static const char * const exim_default_gnutls_priority = "NORMAL"; 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 */ @@ -158,6 +152,11 @@ callbacks. */ #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) @@ -170,8 +169,25 @@ callbacks. */ #if GNUTLS_VERSION_NUMBER >= 0x020c00 #define HAVE_GNUTLS_SESSION_CHANNEL_BINDING +#define HAVE_GNUTLS_SEC_PARAM_CONSTANTS +#define HAVE_GNUTLS_RND #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 */ @@ -380,21 +396,30 @@ gnutls_datum m; 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"); -/* 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); +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), - "%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 @@ -1095,11 +1120,13 @@ return TRUE; * 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); } +#endif /* Called after client hello, should handle SNI work. @@ -1667,6 +1694,7 @@ Arguments: Returns a random number in range [0, max-1] */ +#ifdef HAVE_GNUTLS_RND 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; } +#else /* HAVE_GNUTLS_RND */ +int +vaguely_random_number(int max) +{ + return vaguely_random_number_fallback(max); +} +#endif /* HAVE_GNUTLS_RND */