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 */
#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_debugreturn0(Label) do { \
- if (rc != GNUTLS_E_SUCCESS) { \
- DEBUG(D_tls) debug_printf("TLS failure: %s: %s", (Label), gnutls_strerror(rc)); \
- return 0; } } while (0)
-
#define expand_check_tlsvar(Varname) expand_check(state->Varname, US #Varname, &state->exp_##Varname)
#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 */
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 PK, 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
if ((Ustrcmp(state->exp_tls_certificate, saved_tls_certificate) == 0) &&
(Ustrcmp(state->exp_tls_privatekey, saved_tls_privatekey) == 0))
{
- DEBUG(D_tls) debug_printf("cert and key unchanged with SNI.\n");
+ DEBUG(D_tls) debug_printf("TLS SNI: cert and key unchanged\n");
setit = FALSE;
}
else
{
- DEBUG(D_tls) debug_printf("SNI changed cert/key pair.\n");
+ DEBUG(D_tls) debug_printf("TLS SNI: have a changed cert/key pair.\n");
}
}
exim_gnutls_err_check(
string_sprintf("cert/key setup: cert=%s key=%s",
state->exp_tls_certificate, state->exp_tls_privatekey));
+ DEBUG(D_tls) debug_printf("TLS: cert/key registered\n");
}
- }
+ } /* tls_certificate */
/* Set the trusted CAs file if one is provided, and then add the CRL if one is
provided. Experiment shows that, if the certificate file is empty, an unhelpful
if (state->received_sni)
{
- if (Ustrcmp(state->exp_tls_verify_certificates, saved_tls_verify_certificates) == 0)
- setit_vc = FALSE;
- if (Ustrcmp(state->exp_tls_crl, saved_tls_crl) == 0)
- setit_crl = FALSE;
+ state->exp_tls_verify_certificates, state->exp_tls_verify_certificates,
+ saved_tls_verify_certificates, saved_tls_verify_certificates);
+ if (!(state->exp_tls_verify_certificates || saved_tls_verify_certificates))
+ setit_vc = FALSE; /* never was set */
+ else if (!state->exp_tls_verify_certificates || !saved_tls_verify_certificates)
+ setit_vc = TRUE; /* changed whether set */
+ else if (Ustrcmp(state->exp_tls_verify_certificates, saved_tls_verify_certificates) == 0)
+ setit_vc = FALSE; /* not changed value */
+
+ state->exp_tls_crl, state->exp_tls_crl,
+ saved_tls_crl, saved_tls_crl);
+ if (!(state->exp_tls_crl || saved_tls_crl))
+ setit_crl = FALSE; /* never was set */
+ else if (!state->exp_tls_crl || !saved_tls_crl)
+ setit_crl = TRUE; /* changed whether set */
+ else if (Ustrcmp(state->exp_tls_crl, saved_tls_crl) == 0)
+ setit_crl = FALSE; /* not changed value */
}
/* nb: early exit; change if add more expansions to this function */
if (!(setit_vc || setit_crl))
+ {
+ DEBUG(D_tls)
+ debug_printf("TLS SNI: no change to tls_crl or tls_verify_certificates\n");
return OK;
+ }
if (Ustat(state->exp_tls_verify_certificates, &statbuf) < 0)
{
}
DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n", cert_count);
}
+ else
+ {
+ DEBUG(D_tls) debug_printf("TLS SNI: tls_verify_certificates unchanged\n");
+ }
if (setit_crl && state->tls_crl && *state->tls_crl)
{
exim_gnutls_err_check(US"gnutls_certificate_set_x509_crl_file");
}
}
+ DEBUG(D_tls)
+ if (!setit_crl) debug_printf("TLS SNI: tls_crl unchanged\n");
} /* statbuf.st_size */
} /* tls_verify_certificates */
* 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.
int rc, old_pool;
rc = gnutls_server_name_get(session, sni_name, &data_len, &sni_type, 0);
-exim_gnutls_err_debugreturn0("gnutls_server_name_get()");
+if (rc != GNUTLS_E_SUCCESS)
+ {
+ DEBUG(D_tls) {
+ if (rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ debug_printf("TLS: no SNI presented in handshake.\n");
+ else
+ debug_printf("TLS failure: gnutls_server_name_get(): %s [%d]\n",
+ gnutls_strerror(rc), rc);
+ };
+ return 0;
+ }
+
if (sni_type != GNUTLS_NAME_DNS)
{
DEBUG(D_tls) debug_printf("TLS: ignoring SNI of unhandled type %u\n", sni_type);
Returns a random number in range [0, max-1]
*/
+#ifdef HAVE_GNUTLS_RND
int
vaguely_random_number(int 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 */