#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
# define EXIM_HAVE_OPENSSL_TLSEXT
#endif
+#if OPENSSL_VERSION_NUMBER >= 0x010100000L
+# define EXIM_HAVE_OPENSSL_CHECKHOST
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x010000000L \
+ && (OPENSSL_VERSION_NUMBER & 0x0000ff000L) >= 0x000002000L
+# define EXIM_HAVE_OPENSSL_CHECKHOST
+#endif
#if !defined(EXIM_HAVE_OPENSSL_TLSEXT) && !defined(DISABLE_OCSP)
# warning "OpenSSL library version too old; define DISABLE_OCSP in Makefile"
#ifdef EXPERIMENTAL_CERTNAMES
uschar * verify_cert_hostnames;
#endif
-#ifdef EXPERIMENTAL_TPDA
+#ifdef EXPERIMENTAL_EVENT
uschar * event_action;
#endif
} tls_ext_ctx_cb;
static int
tls_error(uschar *prefix, host_item *host, uschar *msg)
{
-if (msg == NULL)
+if (!msg)
{
ERR_error_string(ERR_get_error(), ssl_errstring);
msg = (uschar *)ssl_errstring;
}
-if (host == NULL)
+if (host)
+ {
+ log_write(0, LOG_MAIN, "H=%s [%s] TLS error on connection (%s): %s",
+ host->name, host->address, prefix, msg);
+ return FAIL;
+ }
+else
{
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",
conn_info, prefix, msg);
return DEFER;
}
-else
- {
- log_write(0, LOG_MAIN, "TLS error on connection to %s [%s] (%s): %s",
- host->name, host->address, prefix, msg);
- return FAIL;
- }
}
X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx);
int depth = X509_STORE_CTX_get_error_depth(x509ctx);
static uschar txt[256];
+#ifdef EXPERIMENTAL_EVENT
+uschar * ev;
+uschar * yield;
+#endif
X509_NAME_oneline(X509_get_subject_name(cert), CS txt, sizeof(txt));
depth,
X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509ctx)),
txt);
- tlsp->certificate_verified = FALSE;
*calledp = TRUE;
if (!*optionalp)
{
ERR_clear_error();
}
#endif
-#ifdef EXPERIMENTAL_TPDA
- if (tlsp == &tls_out && client_static_cbinfo->event_action)
+#ifdef EXPERIMENTAL_EVENT
+ ev = tlsp == &tls_out ? client_static_cbinfo->event_action : event_action;
+ if (ev)
{
tlsp->peercert = X509_dup(cert);
- if (tpda_raise_event(client_static_cbinfo->event_action,
- US"tls:cert", string_sprintf("%d", depth)) == DEFER)
+ if ((yield = event_raise(ev, US"tls:cert", string_sprintf("%d", depth))))
{
log_write(0, LOG_MAIN, "SSL verify denied by event-action: "
- "depth=%d cert=%s", depth, txt);
- tlsp->certificate_verified = FALSE;
+ "depth=%d cert=%s: %s", depth, txt, yield);
*calledp = TRUE;
- return 0; /* reject */
+ if (!*optionalp)
+ return 0; /* reject */
+ DEBUG(D_tls) debug_printf("Event-action verify failure overridden "
+ "(host in tls_try_verify_hosts)\n");
}
X509_free(tlsp->peercert);
tlsp->peercert = NULL;
&& ((verify_cert_hostnames = client_static_cbinfo->verify_cert_hostnames)))
/* client, wanting hostname check */
-# if OPENSSL_VERSION_NUMBER >= 0x010100000L || OPENSSL_VERSION_NUMBER >= 0x010002000L
+# if EXIM_HAVE_OPENSSL_CHECKHOST
# ifndef X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS
# define X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS 0
# endif
{
log_write(0, LOG_MAIN,
"SSL verify error: certificate name mismatch: \"%s\"\n", txt);
- return 0; /* reject */
+ *calledp = TRUE;
+ if (!*optionalp)
+ return 0; /* reject */
+ DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in "
+ "tls_try_verify_hosts)\n");
}
}
# else
{
log_write(0, LOG_MAIN,
"SSL verify error: certificate name mismatch: \"%s\"\n", txt);
- return 0; /* reject */
+ *calledp = TRUE;
+ if (!*optionalp)
+ return 0; /* reject */
+ DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in "
+ "tls_try_verify_hosts)\n");
}
# endif
#endif /*EXPERIMENTAL_CERTNAMES*/
-#ifdef EXPERIMENTAL_TPDA
- if (tlsp == &tls_out)
- {
- if (tpda_raise_event(client_static_cbinfo->event_action,
- US"tls:cert", US"0") == DEFER)
+#ifdef EXPERIMENTAL_EVENT
+ ev = tlsp == &tls_out ? client_static_cbinfo->event_action : event_action;
+ if (ev)
+ if ((yield = event_raise(ev, US"tls:cert", US"0")))
{
log_write(0, LOG_MAIN, "SSL verify denied by event-action: "
- "depth=0 cert=%s", txt);
- tlsp->certificate_verified = FALSE;
+ "depth=0 cert=%s: %s", txt, yield);
*calledp = TRUE;
- return 0; /* reject */
+ if (!*optionalp)
+ return 0; /* reject */
+ DEBUG(D_tls) debug_printf("Event-action verify failure overridden "
+ "(host in tls_try_verify_hosts)\n");
}
- }
#endif
DEBUG(D_tls) debug_printf("SSL%s verify ok: depth=0 SN=%s\n",
{
X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx);
static uschar txt[256];
-#ifdef EXPERIMENTAL_TPDA
+#ifdef EXPERIMENTAL_EVENT
int depth = X509_STORE_CTX_get_error_depth(x509ctx);
+uschar * yield;
#endif
X509_NAME_oneline(X509_get_subject_name(cert), CS txt, sizeof(txt));
tls_out.peerdn = txt;
tls_out.peercert = X509_dup(cert);
-#ifdef EXPERIMENTAL_TPDA
+#ifdef EXPERIMENTAL_EVENT
if (client_static_cbinfo->event_action)
{
- if (tpda_raise_event(client_static_cbinfo->event_action,
- US"tls:cert", string_sprintf("%d", depth)) == DEFER)
+ if ((yield = event_raise(client_static_cbinfo->event_action,
+ US"tls:cert", string_sprintf("%d", depth))))
{
log_write(0, LOG_MAIN, "DANE verify denied by event-action: "
- "depth=%d cert=%s", depth, txt);
+ "depth=%d cert=%s: %s", depth, txt, yield);
tls_out.certificate_verified = FALSE;
return 0; /* reject */
}
cbinfo->dhparam = dhparam;
cbinfo->server_cipher_list = NULL;
cbinfo->host = host;
-#ifdef EXPERIMENTAL_TPDA
+#ifdef EXPERIMENTAL_EVENT
cbinfo->event_action = NULL;
#endif
!SSL_CTX_load_verify_locations(sctx, CS file, CS dir))
return tls_error(US"SSL_CTX_load_verify_locations", host, NULL);
+ /* Load the list of CAs for which we will accept certs, for sending
+ to the client. This is only for the one-file tls_verify_certificates
+ variant.
+ If a list isn't loaded into the server, but
+ some verify locations are set, the server end appears to make
+ a wildcard reqest for client certs.
+ Meanwhile, the client library as deafult behaviour *ignores* the list
+ we send over the wire - see man SSL_CTX_set_client_cert_cb.
+ Because of this, and that the dir variant is likely only used for
+ the public-CA bundle (not for a private CA), not worth fixing.
+ */
if (file != NULL)
{
- SSL_CTX_set_client_CA_list(sctx, SSL_load_client_CA_file(CS file));
+ STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file);
+DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n",
+ sk_X509_NAME_num(names));
+ SSL_CTX_set_client_CA_list(sctx, names);
}
}
#ifdef EXPERIMENTAL_DANE
-static int
-tlsa_lookup(host_item * host, dns_answer * dnsa,
- BOOL dane_required, BOOL * dane)
-{
-/* move this out to host.c given the similarity to dns_lookup() ? */
-uschar buffer[300];
-uschar * fullname = buffer;
-
-/* TLSA lookup string */
-(void)sprintf(CS buffer, "_%d._tcp.%.256s", host->port, host->name);
-
-switch (dns_lookup(dnsa, buffer, T_TLSA, &fullname))
- {
- case DNS_AGAIN:
- return DEFER; /* just defer this TLS'd conn */
-
- default:
- case DNS_FAIL:
- if (dane_required)
- {
- log_write(0, LOG_MAIN, "DANE error: TLSA lookup failed");
- return FAIL;
- }
- break;
-
- case DNS_SUCCEED:
- if (!dns_is_secure(dnsa))
- {
- log_write(0, LOG_MAIN, "DANE error: TLSA lookup not DNSSEC");
- return DEFER;
- }
- *dane = TRUE;
- break;
- }
-return OK;
-}
-
-
static int
dane_tlsa_load(SSL * ssl, host_item * host, dns_answer * dnsa)
{
uint8_t usage, selector, mtype;
const char * mdname;
- found++;
usage = *p++;
+
+ /* Only DANE-TA(2) and DANE-EE(3) are supported */
+ if (usage != 2 && usage != 3) continue;
+
selector = *p++;
mtype = *p++;
switch (mtype)
{
- default:
- log_write(0, LOG_MAIN,
- "DANE error: TLSA record w/bad mtype 0x%x", mtype);
- return FAIL;
- case 0: mdname = NULL; break;
- case 1: mdname = "sha256"; break;
- case 2: mdname = "sha512"; break;
+ default: continue; /* Only match-types 0, 1, 2 are supported */
+ case 0: mdname = NULL; break;
+ case 1: mdname = "sha256"; break;
+ case 2: mdname = "sha512"; break;
}
+ found++;
switch (DANESSL_add_tlsa(ssl, usage, selector, mdname, p, rr->size - 3))
{
default:
if (found)
return OK;
-log_write(0, LOG_MAIN, "DANE error: No TLSA records");
+log_write(0, LOG_MAIN, "DANE error: No usable TLSA records");
return FAIL;
}
#endif /*EXPERIMENTAL_DANE*/
host connected host (for messages)
addr the first address
tb transport (always smtp)
+ tlsa_dnsa tlsa lookup, if DANE, else null
Returns: OK on success
FAIL otherwise - note that tls_error() will not give DEFER
int
tls_client_start(int fd, host_item *host, address_item *addr,
- transport_instance *tb)
+ transport_instance *tb
+#ifdef EXPERIMENTAL_DANE
+ , dns_answer * tlsa_dnsa
+#endif
+ )
{
smtp_transport_options_block * ob =
(smtp_transport_options_block *)tb->options_block;
BOOL request_ocsp = FALSE;
BOOL require_ocsp = FALSE;
#endif
-#ifdef EXPERIMENTAL_DANE
-dns_answer tlsa_dnsa;
-BOOL dane = FALSE;
-BOOL dane_required;
-#endif
#ifdef EXPERIMENTAL_DANE
-tls_out.dane_verified = FALSE;
tls_out.tlsa_usage = 0;
-dane_required = verify_check_this_host(&ob->hosts_require_dane, NULL,
- host->name, host->address, NULL) == OK;
-
-if (host->dnssec == DS_YES)
- {
- if( dane_required
- || verify_check_this_host(&ob->hosts_try_dane, NULL,
- host->name, host->address, NULL) == OK
- )
- if ((rc = tlsa_lookup(host, &tlsa_dnsa, dane_required, &dane)) != OK)
- return rc;
- }
-else if (dane_required)
- {
- /*XXX a shame we only find this after making tcp & smtp connection */
- /* move the test earlier? */
- log_write(0, LOG_MAIN, "DANE error: previous lookup not DNSSEC");
- return FAIL;
- }
#endif
#ifndef DISABLE_OCSP
{
+# ifdef EXPERIMENTAL_DANE
+ if ( tlsa_dnsa
+ && ob->hosts_request_ocsp[0] == '*'
+ && ob->hosts_request_ocsp[1] == '\0'
+ )
+ {
+ /* Unchanged from default. Use a safer one under DANE */
+ request_ocsp = TRUE;
+ ob->hosts_request_ocsp = US"${if or { {= {0}{$tls_out_tlsa_usage}} "
+ " {= {4}{$tls_out_tlsa_usage}} } "
+ " {*}{}}";
+ }
+# endif
+
if ((require_ocsp = verify_check_this_host(&ob->hosts_require_ocsp,
NULL, host->name, host->address, NULL) == OK))
request_ocsp = TRUE;
else
- {
# ifdef EXPERIMENTAL_DANE
- if ( dane
- && ob->hosts_request_ocsp[0] == '*'
- && ob->hosts_request_ocsp[1] == '\0'
- )
- {
- /* Unchanged from default. Use a safer one under DANE */
- request_ocsp = TRUE;
- ob->hosts_request_ocsp = US"${if or { {= {0}{$tls_out_tlsa_usage}} "
- " {= {4}{$tls_out_tlsa_usage}} } "
- " {*}{}}";
- }
- else
+ if (!request_ocsp)
# endif
request_ocsp = verify_check_this_host(&ob->hosts_request_ocsp,
NULL, host->name, host->address, NULL) == OK;
- }
}
#endif
}
#ifdef EXPERIMENTAL_DANE
-if (dane)
+if (tlsa_dnsa)
{
SSL_CTX_set_verify(client_ctx, SSL_VERIFY_PEER, verify_callback_client_dane);
}
#ifdef EXPERIMENTAL_DANE
-if (dane)
- if ((rc = dane_tlsa_load(client_ssl, host, &tlsa_dnsa)) != OK)
+if (tlsa_dnsa)
+ if ((rc = dane_tlsa_load(client_ssl, host, tlsa_dnsa)) != OK)
return rc;
#endif
if (request_ocsp)
{
const uschar * s;
- if ( (s = ob->hosts_require_ocsp) && Ustrstr(s, US"tls_out_tlsa_usage")
- || (s = ob->hosts_request_ocsp) && Ustrstr(s, US"tls_out_tlsa_usage")
+ if ( ((s = ob->hosts_require_ocsp) && Ustrstr(s, US"tls_out_tlsa_usage"))
+ || ((s = ob->hosts_request_ocsp) && Ustrstr(s, US"tls_out_tlsa_usage"))
)
{ /* Re-eval now $tls_out_tlsa_usage is populated. If
this means we avoid the OCSP request, we wasted the setup
}
#endif
-#ifdef EXPERIMENTAL_TPDA
-client_static_cbinfo->event_action = tb->tpda_event_action;
-#endif
-
-#ifdef EXPERIMENTAL_TPDA
-client_static_cbinfo->event_action = tb->tpda_event_action;
+#ifdef EXPERIMENTAL_EVENT
+client_static_cbinfo->event_action = tb->event_action;
#endif
/* There doesn't seem to be a built-in timeout on connection. */
alarm(0);
#ifdef EXPERIMENTAL_DANE
-if (dane)
+if (tlsa_dnsa)
DANESSL_cleanup(client_ssl);
#endif