X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/60788a1cfdd64787ed6513986af2335b51d89766..bba74fc65f77dc6678b3d33eef0acf43efe8f653:/src/src/tls-gnu.c diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index b2659d7a7..c8bf634bc 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -358,9 +358,6 @@ file is never present. If two processes both compute some new parameters, you 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 */ @@ -370,8 +367,12 @@ init_server_dh(void) 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"); @@ -379,6 +380,46 @@ 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. */ @@ -404,9 +445,13 @@ if (dh_bits > tls_dh_max_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. */ @@ -483,6 +528,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 +540,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, @@ -512,18 +576,15 @@ if (rc < 0) m.data = malloc(m.size); if (m.data == NULL) return tls_error(US"memory allocation failed", strerror(errno), NULL); - /* this will return a size 1 less than the allocation size above; I - originally used sz so as to avoid type compatibility errors, as gnutls_datum - uses "unsigned int" for the size field, but this call takes separate data - and size fields, with the latter being a size_t*. For now, we live with - the error as being safer than throwing away type information. */ + /* this will return a size 1 less than the allocation size above */ rc = gnutls_dh_params_export_pkcs3(dh_server_params, GNUTLS_X509_FMT_PEM, - m.data, &m.size); + m.data, &sz); if (rc != GNUTLS_E_SUCCESS) { free(m.data); exim_gnutls_err_check(US"gnutls_dh_params_export_pkcs3() real"); } + m.size = sz; /* shrink by 1, probably */ sz = write_to_fd_buf(fd, m.data, (size_t) m.size); if (sz != m.size) @@ -1170,7 +1231,7 @@ if ((rc < 0) || (verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED)) != 0) 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 {