# warning "GnuTLS library version too old; define DISABLE_OCSP in Makefile"
# define DISABLE_OCSP
#endif
-#if GNUTLS_VERSION_NUMBER < 0x020a00 && defined(EXPERIMENTAL_TPDA)
-# warning "GnuTLS library version too old; TPDA tls:cert event unsupported"
-# undef EXPERIMENTAL_TPDA
+#if GNUTLS_VERSION_NUMBER < 0x020a00 && defined(EXPERIMENTAL_EVENT)
+# warning "GnuTLS library version too old; tls:cert event unsupported"
+# undef EXPERIMENTAL_EVENT
#endif
#if GNUTLS_VERSION_NUMBER >= 0x030306
# define SUPPORT_CA_DIR
/* Values for verify_requirement */
enum peer_verify_requirement
- { VERIFY_NONE, VERIFY_OPTIONAL, VERIFY_REQUIRED
-#ifdef EXPERIMENTAL_CERTNAMES
- ,VERIFY_WITHHOST
-#endif
- };
+ { VERIFY_NONE, VERIFY_OPTIONAL, VERIFY_REQUIRED };
/* This holds most state for server or client; with this, we can set up an
outbound TLS-enabled connection in an ACL callout, while not stomping all
#ifdef EXPERIMENTAL_CERTNAMES
uschar *exp_tls_verify_cert_hostnames;
#endif
-#ifdef EXPERIMENTAL_TPDA
+#ifdef EXPERIMENTAL_EVENT
uschar *event_action;
#endif
#ifdef EXPERIMENTAL_CERTNAMES
NULL,
#endif
-#ifdef EXPERIMENTAL_TPDA
+#ifdef EXPERIMENTAL_EVENT
NULL,
#endif
NULL,
{
if (host)
{
- log_write(0, LOG_MAIN, "TLS error on connection to %s [%s] (%s)%s%s",
+ log_write(0, LOG_MAIN, "H=%s [%s] TLS error on connection (%s)%s%s",
host->name, host->address, prefix, msg ? ": " : "", msg ? msg : "");
return FAIL;
}
uschar *conn_info = smtp_get_connection_info();
if (Ustrncmp(conn_info, US"SMTP ", 5) == 0)
conn_info += 5;
+ /* I'd like to get separated H= here, but too hard for now */
log_write(0, LOG_MAIN, "TLS error on %s (%s)%s%s",
conn_info, prefix, msg ? ": " : "", msg ? msg : "");
return DEFER;
else
{
#ifdef EXPERIMENTAL_CERTNAMES
- if (state->verify_requirement == VERIFY_WITHHOST)
+ if (state->exp_tls_verify_cert_hostnames)
{
int sep = 0;
uschar * list = state->exp_tls_verify_cert_hostnames;
{
DEBUG(D_tls)
debug_printf("TLS certificate verification failed: cert name mismatch\n");
- gnutls_alert_send(state->session,
- GNUTLS_AL_FATAL, GNUTLS_A_BAD_CERTIFICATE);
- return FALSE;
+ if (state->verify_requirement >= VERIFY_REQUIRED)
+ {
+ gnutls_alert_send(state->session,
+ GNUTLS_AL_FATAL, GNUTLS_A_BAD_CERTIFICATE);
+ return FALSE;
+ }
+ return TRUE;
}
}
#endif
#endif
-#ifdef EXPERIMENTAL_TPDA
+#ifdef EXPERIMENTAL_EVENT
/*
We use this callback to get observability and detail-level control
-for an exim client TLS connection, raising a TPDA tls:cert event
-for each cert in the chain presented by the server. Any event
+for an exim TLS connection (either direction), raising a tls:cert event
+for each cert in the chain presented by the peer. Any event
can deny verification.
Return 0 for the handshake to continue or non-zero to terminate.
*/
static int
-client_verify_cb(gnutls_session_t session)
+verify_cb(gnutls_session_t session)
{
const gnutls_datum * cert_list;
unsigned int cert_list_size = 0;
gnutls_x509_crt_t crt;
int rc;
+uschar * yield;
exim_gnutls_state_st * state = gnutls_session_get_ptr(session);
cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
}
state->tlsp->peercert = crt;
- if (tpda_raise_event(state->event_action,
- US"tls:cert", string_sprintf("%d", cert_list_size)) == DEFER)
+ if ((yield = event_raise(state->event_action,
+ US"tls:cert", string_sprintf("%d", cert_list_size))))
{
log_write(0, LOG_MAIN,
- "SSL verify denied by event-action: depth=%d", cert_list_size);
+ "SSL verify denied by event-action: depth=%d: %s",
+ cert_list_size, yield);
return 1; /* reject */
}
state->tlsp->peercert = NULL;
gnutls_certificate_server_set_request(state->session, GNUTLS_CERT_IGNORE);
}
+#ifdef EXPERIMENTAL_EVENT
+if (event_action)
+ {
+ state->event_action = event_action;
+ gnutls_session_set_ptr(state->session, state);
+ gnutls_certificate_set_verify_function(state->x509_cred, verify_cb);
+ }
+#endif
+
/* Register SNI handling; always, even if not in tls_certificate, so that the
expansion variable $tls_sni is always available. */
+#ifdef EXPERIMENTAL_CERTNAMES
+static void
+tls_client_setup_hostname_checks(host_item * host, exim_gnutls_state_st * state,
+ smtp_transport_options_block * ob)
+{
+if (verify_check_given_host(&ob->tls_verify_cert_hostnames, host) == OK)
+ {
+ state->exp_tls_verify_cert_hostnames = host->name;
+ DEBUG(D_tls)
+ debug_printf("TLS: server cert verification includes hostname: \"%s\".\n",
+ state->exp_tls_verify_cert_hostnames);
+ }
+}
+#endif
+
+
/*************************************************
* Start a TLS session in a client *
*************************************************/
const char *error;
exim_gnutls_state_st *state = NULL;
#ifndef DISABLE_OCSP
-BOOL require_ocsp = verify_check_this_host(&ob->hosts_require_ocsp,
- NULL, host->name, host->address, NULL) == OK;
+BOOL require_ocsp =
+ verify_check_given_host(&ob->hosts_require_ocsp, host) == OK;
BOOL request_ocsp = require_ocsp ? TRUE
- : verify_check_this_host(&ob->hosts_request_ocsp,
- NULL, host->name, host->address, NULL) == OK;
+ : verify_check_given_host(&ob->hosts_request_ocsp, host) == OK;
#endif
DEBUG(D_tls) debug_printf("initialising GnuTLS as a client on fd %d\n", fd);
set but both tls_verify_hosts and tls_try_verify_hosts are unset. Check only
the specified host patterns if one of them is defined */
-if (( state->exp_tls_verify_certificates
- && !ob->tls_verify_hosts
- && !ob->tls_try_verify_hosts
- )
- ||
- verify_check_host(&ob->tls_verify_hosts) == OK
+if ( ( state->exp_tls_verify_certificates
+ && !ob->tls_verify_hosts
+ && !ob->tls_try_verify_hosts
+ )
+ || verify_check_given_host(&ob->tls_verify_hosts, host) == OK
)
{
#ifdef EXPERIMENTAL_CERTNAMES
- if (ob->tls_verify_cert_hostnames)
- {
- DEBUG(D_tls)
- debug_printf("TLS: server cert incl. hostname verification required.\n");
- state->verify_requirement = VERIFY_WITHHOST;
- if (!expand_check(ob->tls_verify_cert_hostnames,
- US"tls_verify_cert_hostnames",
- &state->exp_tls_verify_cert_hostnames))
- return FAIL;
- if (state->exp_tls_verify_cert_hostnames)
- DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n",
- state->exp_tls_verify_cert_hostnames);
- }
- else
+ tls_client_setup_hostname_checks(host, state, ob);
#endif
- {
- DEBUG(D_tls)
- debug_printf("TLS: server certificate verification required.\n");
- state->verify_requirement = VERIFY_REQUIRED;
- }
+ DEBUG(D_tls)
+ debug_printf("TLS: server certificate verification required.\n");
+ state->verify_requirement = VERIFY_REQUIRED;
gnutls_certificate_server_set_request(state->session, GNUTLS_CERT_REQUIRE);
}
-else if (verify_check_host(&ob->tls_try_verify_hosts) == OK)
+else if (verify_check_given_host(&ob->tls_try_verify_hosts, host) == OK)
{
+#ifdef EXPERIMENTAL_CERTNAMES
+ tls_client_setup_hostname_checks(host, state, ob);
+#endif
DEBUG(D_tls)
debug_printf("TLS: server certificate verification optional.\n");
state->verify_requirement = VERIFY_OPTIONAL;
}
#endif
-#ifdef EXPERIMENTAL_TPDA
-if (tb->tpda_event_action)
+#ifdef EXPERIMENTAL_EVENT
+if (tb->event_action)
{
- state->event_action = tb->tpda_event_action;
+ state->event_action = tb->event_action;
gnutls_session_set_ptr(state->session, state);
- gnutls_certificate_set_verify_function(state->x509_cred, client_verify_cb);
+ gnutls_certificate_set_verify_function(state->x509_cred, verify_cb);
}
#endif