waste a bit of effort, but it doesn't seem worth messing around with locking to
prevent this.
-Argument:
- host NULL for server, server for client (for error handling)
-
Returns: OK/DEFER/FAIL
*/
int fd, rc;
unsigned int dh_bits;
gnutls_datum m;
-uschar filename[PATH_MAX];
+uschar filename_buf[PATH_MAX];
+uschar *filename = NULL;
size_t sz;
+uschar *exp_tls_dhparam;
+BOOL use_file_in_spool = FALSE;
+BOOL use_fixed_file = FALSE;
host_item *host = NULL; /* dummy for macros */
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");
+m.data = NULL;
+m.size = 0;
+
+if (!expand_check(tls_dhparam, US"tls_dhparam", &exp_tls_dhparam))
+ return DEFER;
+
+if (!exp_tls_dhparam)
+ {
+ DEBUG(D_tls) debug_printf("Loading default hard-coded DH params\n");
+ m.data = US std_dh_prime_default();
+ m.size = Ustrlen(m.data);
+ }
+else if (Ustrcmp(exp_tls_dhparam, "historic") == 0)
+ use_file_in_spool = TRUE;
+else if (Ustrcmp(exp_tls_dhparam, "none") == 0)
+ {
+ DEBUG(D_tls) debug_printf("Requested no DH parameters.\n");
+ return OK;
+ }
+else if (exp_tls_dhparam[0] != '/')
+ {
+ m.data = US std_dh_prime_named(exp_tls_dhparam);
+ if (m.data == NULL)
+ return tls_error(US"No standard prime named", CS exp_tls_dhparam, NULL);
+ m.size = Ustrlen(m.data);
+ }
+else
+ {
+ use_fixed_file = TRUE;
+ filename = exp_tls_dhparam;
+ }
+
+if (m.data)
+ {
+ rc = gnutls_dh_params_import_pkcs3(dh_server_params, &m, GNUTLS_X509_FMT_PEM);
+ exim_gnutls_err_check(US"gnutls_dh_params_import_pkcs3");
+ DEBUG(D_tls) debug_printf("Loaded fixed standard D-H parameters\n");
+ return OK;
+ }
+
#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 = tls_dh_max_bits;
}
-if (!string_format(filename, sizeof(filename),
- "%s/gnutls-params-%d", spool_directory, dh_bits))
- return tls_error(US"overlong filename", NULL, NULL);
+if (use_file_in_spool)
+ {
+ if (!string_format(filename_buf, sizeof(filename_buf),
+ "%s/gnutls-params-%d", spool_directory, dh_bits))
+ return tls_error(US"overlong filename", NULL, NULL);
+ filename = filename_buf;
+ }
/* Open the cache file for reading and if successful, read it and set up the
parameters. */
* 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)
+ if (dh_bits >= EXIM_CLIENT_DH_MIN_BITS + 10)
{
dh_bits_gen = dh_bits - 10;
DEBUG(D_tls)
return FALSE;
}
DEBUG(D_tls)
- debug_printf("TLS verify failure overriden (host in tls_try_verify_hosts)\n");
+ debug_printf("TLS verify failure overridden (host in tls_try_verify_hosts)\n");
}
else
{
verify_certs file for certificate verify
verify_crl CRL for verify
require_ciphers list of allowed ciphers or NULL
+ dh_min_bits minimum number of bits acceptable in server's DH prime
timeout startup timeout
Returns: OK/DEFER/FAIL (because using common functions),
address_item *addr ARG_UNUSED, uschar *dhparam ARG_UNUSED,
uschar *certificate, uschar *privatekey, uschar *sni,
uschar *verify_certs, uschar *verify_crl,
- uschar *require_ciphers, int timeout)
+ uschar *require_ciphers, int dh_min_bits, int timeout)
{
int rc;
const char *error;
sni, verify_certs, verify_crl, require_ciphers, &state);
if (rc != OK) return rc;
-gnutls_dh_set_prime_bits(state->session, EXIM_CLIENT_DH_MIN_BITS);
+if (dh_min_bits < EXIM_CLIENT_DH_MIN_MIN_BITS)
+ {
+ DEBUG(D_tls)
+ debug_printf("WARNING: tls_dh_min_bits far too low, clamping %d up to %d\n",
+ dh_min_bits, EXIM_CLIENT_DH_MIN_MIN_BITS);
+ dh_min_bits = EXIM_CLIENT_DH_MIN_MIN_BITS;
+ }
+
+DEBUG(D_tls) debug_printf("Setting D-H prime minimum acceptable bits to %d\n",
+ dh_min_bits);
+gnutls_dh_set_prime_bits(state->session, dh_min_bits);
if (verify_certs == NULL)
{