X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/cf2b569e3a2f8956b7045191e96bc5edfd366c78..55414b25bee9f0195ccd1e47f3d3b5cba766e099:/src/src/tls-openssl.c diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index eb74605da..f28b2fbf5 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -25,6 +25,10 @@ functions from the OpenSSL library. */ #ifndef DISABLE_OCSP # include #endif +#ifdef EXPERIMENTAL_DANE +# include +#endif + #ifndef DISABLE_OCSP # define EXIM_OCSP_SKEW_SECONDS (300L) @@ -34,6 +38,13 @@ functions from the OpenSSL library. */ #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" @@ -112,9 +123,9 @@ typedef struct tls_ext_ctx_cb { uschar *server_cipher_list; /* only passed down to tls_error: */ host_item *host; - -#ifdef EXPERIMENTAL_CERTNAMES - uschar * verify_cert_hostnames; + const uschar * verify_cert_hostnames; +#ifdef EXPERIMENTAL_EVENT + uschar * event_action; #endif } tls_ext_ctx_cb; @@ -158,29 +169,30 @@ Returns: OK/DEFER/FAIL */ static int -tls_error(uschar *prefix, host_item *host, uschar *msg) +tls_error(uschar * prefix, const 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; - } } @@ -262,6 +274,9 @@ when asked. We get here only if a certificate has been received. Handling of optional verification for this case is done when requesting SSL to verify, by setting SSL_VERIFY_FAIL_IF_NO_PEER_CERT in the non-optional case. +May be called multiple times for different issues with a certificate, even +for a given "depth" in the certificate chain. + Arguments: state current yes/no state as 1/0 x509ctx certificate information. @@ -275,17 +290,21 @@ verify_callback(int state, X509_STORE_CTX *x509ctx, tls_support *tlsp, BOOL *calledp, BOOL *optionalp) { 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)); if (state == 0) { log_write(0, LOG_MAIN, "SSL verify error: depth=%d error=%s cert=%s", - X509_STORE_CTX_get_error_depth(x509ctx), + depth, X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509ctx)), txt); - tlsp->certificate_verified = FALSE; *calledp = TRUE; if (!*optionalp) { @@ -296,10 +315,9 @@ if (state == 0) "tls_try_verify_hosts)\n"); } -else if (X509_STORE_CTX_get_error_depth(x509ctx) != 0) +else if (depth != 0) { - DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d SN=%s\n", - X509_STORE_CTX_get_error_depth(x509ctx), txt); + DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d SN=%s\n", depth, txt); #ifndef DISABLE_OCSP if (tlsp == &tls_out && client_static_cbinfo->u_ocsp.client.verify_store) { /* client, wanting stapling */ @@ -310,34 +328,54 @@ else if (X509_STORE_CTX_get_error_depth(x509ctx) != 0) cert)) ERR_clear_error(); } +#endif +#ifdef EXPERIMENTAL_EVENT + ev = tlsp == &tls_out ? client_static_cbinfo->event_action : event_action; + if (ev) + { + tlsp->peercert = X509_dup(cert); + 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: %s", depth, txt, yield); + *calledp = TRUE; + 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; + } #endif } else { -#ifdef EXPERIMENTAL_CERTNAMES - uschar * verify_cert_hostnames; -#endif + const uschar * verify_cert_hostnames; tlsp->peerdn = txt; tlsp->peercert = X509_dup(cert); -#ifdef EXPERIMENTAL_CERTNAMES if ( tlsp == &tls_out && ((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 +# ifndef X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS +# define X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS 0 # endif { int sep = 0; - uschar * list = verify_cert_hostnames; + const uschar * list = verify_cert_hostnames; uschar * name; int rc; while ((name = string_nextinlist(&list, &sep, NULL, 0))) if ((rc = X509_check_host(cert, name, 0, - X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS))) + X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS + | X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS))) { if (rc < 0) { @@ -350,7 +388,11 @@ 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"); } } # else @@ -358,9 +400,27 @@ 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 + +#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: %s", txt, yield); + *calledp = TRUE; + 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", @@ -369,7 +429,7 @@ else *calledp = TRUE; } -return 1; /* accept */ +return 1; /* accept, at least for this level */ } static int @@ -385,6 +445,54 @@ return verify_callback(state, x509ctx, &tls_in, &server_verify_callback_called, } +#ifdef EXPERIMENTAL_DANE + +/* This gets called *by* the dane library verify callback, which interposes +itself. +*/ +static int +verify_callback_client_dane(int state, X509_STORE_CTX * x509ctx) +{ +X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx); +static uschar txt[256]; +#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)); + +DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s\n", txt); +tls_out.peerdn = txt; +tls_out.peercert = X509_dup(cert); + +#ifdef EXPERIMENTAL_EVENT + if (client_static_cbinfo->event_action) + { + 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: %s", depth, txt, yield); + tls_out.certificate_verified = FALSE; + return 0; /* reject */ + } + if (depth != 0) + { + X509_free(tls_out.peercert); + tls_out.peercert = NULL; + } + } +#endif + +if (state == 1) + tls_out.dane_verified = + tls_out.certificate_verified = TRUE; +return 1; +} + +#endif /*EXPERIMENTAL_DANE*/ + /************************************************* * Information callback * @@ -426,7 +534,7 @@ Returns: TRUE if OK (nothing to set up, or setup worked) */ static BOOL -init_dh(SSL_CTX *sctx, uschar *dhparam, host_item *host) +init_dh(SSL_CTX *sctx, uschar *dhparam, const host_item *host) { BIO *bio; DH *dh; @@ -888,7 +996,7 @@ if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len))) { tls_out.ocsp = OCSP_FAILED; if (log_extra_selector & LX_tls_cipher) - log_write(0, LOG_MAIN, "Received TLS status response, parse error"); + log_write(0, LOG_MAIN, "Received TLS cert status response, parse error"); else DEBUG(D_tls) debug_printf(" parse error\n"); return 0; @@ -898,7 +1006,7 @@ if(!(bs = OCSP_response_get1_basic(rsp))) { tls_out.ocsp = OCSP_FAILED; if (log_extra_selector & LX_tls_cipher) - log_write(0, LOG_MAIN, "Received TLS status response, error parsing response"); + log_write(0, LOG_MAIN, "Received TLS cert status response, error parsing response"); else DEBUG(D_tls) debug_printf(" error parsing response\n"); OCSP_RESPONSE_free(rsp); @@ -928,6 +1036,8 @@ if(!(bs = OCSP_response_get1_basic(rsp))) cbinfo->u_ocsp.client.verify_store, 0)) <= 0) { tls_out.ocsp = OCSP_FAILED; + if (log_extra_selector & LX_tls_cipher) + log_write(0, LOG_MAIN, "Received TLS cert status response, itself unverifiable"); BIO_printf(bp, "OCSP response verify failure\n"); ERR_print_errors(bp); i = cbinfo->u_ocsp.client.verify_required ? 0 : 1; @@ -999,7 +1109,6 @@ return i; #endif /*!DISABLE_OCSP*/ - /************************************************* * Initialize for TLS * *************************************************/ @@ -1031,7 +1140,7 @@ tls_init(SSL_CTX **ctxp, host_item *host, uschar *dhparam, uschar *certificate, long init_options; int rc; BOOL okay; -tls_ext_ctx_cb *cbinfo; +tls_ext_ctx_cb * cbinfo; cbinfo = store_malloc(sizeof(tls_ext_ctx_cb)); cbinfo->certificate = certificate; @@ -1049,6 +1158,9 @@ else cbinfo->dhparam = dhparam; cbinfo->server_cipher_list = NULL; cbinfo->host = host; +#ifdef EXPERIMENTAL_EVENT +cbinfo->event_action = NULL; +#endif SSL_load_error_strings(); /* basic set up */ OpenSSL_add_ssl_algorithms(); @@ -1170,9 +1282,7 @@ else /* client */ # endif #endif -#ifdef EXPERIMENTAL_CERTNAMES cbinfo->verify_cert_hostnames = NULL; -#endif /* Set up the RSA callback */ @@ -1256,36 +1366,65 @@ if (!expand_check(certs, US"tls_verify_certificates", &expcerts)) if (expcerts != NULL && *expcerts != '\0') { - struct stat statbuf; - if (!SSL_CTX_set_default_verify_paths(sctx)) - return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL); - - if (Ustat(expcerts, &statbuf) < 0) + if (Ustrcmp(expcerts, "system") == 0) { - log_write(0, LOG_MAIN|LOG_PANIC, - "failed to stat %s for certificates", expcerts); - return DEFER; + /* Tell the library to use its compiled-in location for the system default + CA bundle, only */ + + if (!SSL_CTX_set_default_verify_paths(sctx)) + return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL); } else { - uschar *file, *dir; - if ((statbuf.st_mode & S_IFMT) == S_IFDIR) - { file = NULL; dir = expcerts; } - else - { file = expcerts; dir = NULL; } + struct stat statbuf; - /* If a certificate file is empty, the next function fails with an - unhelpful error message. If we skip it, we get the correct behaviour (no - certificates are recognized, but the error message is still misleading (it - says no certificate was supplied.) But this is better. */ + /* Tell the library to use its compiled-in location for the system default + CA bundle. Those given by the exim config are additional to these */ - if ((file == NULL || statbuf.st_size > 0) && - !SSL_CTX_load_verify_locations(sctx, CS file, CS dir)) - return tls_error(US"SSL_CTX_load_verify_locations", host, NULL); + if (!SSL_CTX_set_default_verify_paths(sctx)) + return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL); - if (file != NULL) + if (Ustat(expcerts, &statbuf) < 0) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "failed to stat %s for certificates", expcerts); + return DEFER; + } + else { - SSL_CTX_set_client_CA_list(sctx, SSL_load_client_CA_file(CS file)); + uschar *file, *dir; + if ((statbuf.st_mode & S_IFMT) == S_IFDIR) + { file = NULL; dir = expcerts; } + else + { file = expcerts; dir = NULL; } + + /* If a certificate file is empty, the next function fails with an + unhelpful error message. If we skip it, we get the correct behaviour (no + certificates are recognized, but the error message is still misleading (it + says no certificate was supplied.) But this is better. */ + + if ((file == NULL || statbuf.st_size > 0) && + !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) + { + 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); + } } } @@ -1421,6 +1560,9 @@ if (expciphers != NULL) optional, set up appropriately. */ tls_in.certificate_verified = FALSE; +#ifdef EXPERIMENTAL_DANE +tls_in.dane_verified = FALSE; +#endif server_verify_callback_called = FALSE; if (verify_check_host(&tls_verify_hosts) == OK) @@ -1536,10 +1678,7 @@ return OK; static int tls_client_basic_ctx_init(SSL_CTX * ctx, - host_item * host, smtp_transport_options_block * ob -#ifdef EXPERIMENTAL_CERTNAMES - , tls_ext_ctx_cb * cbinfo -#endif + host_item * host, smtp_transport_options_block * ob, tls_ext_ctx_cb * cbinfo ) { int rc; @@ -1547,37 +1686,89 @@ int rc; set but both tls_verify_hosts and tls_try_verify_hosts is not set. Check only the specified host patterns if one of them is defined */ -if ((!ob->tls_verify_hosts && !ob->tls_try_verify_hosts) || - (verify_check_host(&ob->tls_verify_hosts) == OK)) - { - if ((rc = setup_certs(ctx, ob->tls_verify_certificates, - ob->tls_crl, host, FALSE, verify_callback_client)) != OK) - return rc; +if ( ( !ob->tls_verify_hosts + && (!ob->tls_try_verify_hosts || !*ob->tls_try_verify_hosts) + ) + || (verify_check_given_host(&ob->tls_verify_hosts, host) == OK) + ) client_verify_optional = FALSE; +else if (verify_check_given_host(&ob->tls_try_verify_hosts, host) == OK) + client_verify_optional = TRUE; +else + return OK; -#ifdef EXPERIMENTAL_CERTNAMES - if (ob->tls_verify_cert_hostnames) - { - if (!expand_check(ob->tls_verify_cert_hostnames, - US"tls_verify_cert_hostnames", - &cbinfo->verify_cert_hostnames)) - return FAIL; - if (cbinfo->verify_cert_hostnames) - DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n", - cbinfo->verify_cert_hostnames); - } -#endif +if ((rc = setup_certs(ctx, ob->tls_verify_certificates, + ob->tls_crl, host, client_verify_optional, verify_callback_client)) != OK) + return rc; + +if (verify_check_given_host(&ob->tls_verify_cert_hostnames, host) == OK) + { + cbinfo->verify_cert_hostnames = host->name; + DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n", + cbinfo->verify_cert_hostnames); } -else if (verify_check_host(&ob->tls_try_verify_hosts) == OK) +return OK; +} + + +#ifdef EXPERIMENTAL_DANE +static int +dane_tlsa_load(SSL * ssl, host_item * host, dns_answer * dnsa) +{ +dns_record * rr; +dns_scan dnss; +const char * hostnames[2] = { CS host->name, NULL }; +int found = 0; + +if (DANESSL_init(ssl, NULL, hostnames) != 1) + return tls_error(US"hostnames load", host, NULL); + +for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); + rr; + rr = dns_next_rr(dnsa, &dnss, RESET_NEXT) + ) if (rr->type == T_TLSA) { - if ((rc = setup_certs(ctx, ob->tls_verify_certificates, - ob->tls_crl, host, TRUE, verify_callback_client)) != OK) - return rc; - client_verify_optional = TRUE; + uschar * p = rr->data; + uint8_t usage, selector, mtype; + const char * mdname; + + 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: 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: + case 0: /* action not taken */ + return tls_error(US"tlsa load", host, NULL); + case 1: break; + } + + tls_out.tlsa_usage |= 1<options_block; static uschar txt[256]; uschar * expciphers; X509 * server_cert; @@ -1611,74 +1808,36 @@ static uschar cipherbuf[256]; 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 -dane_required = verify_check_this_host(&ob->hosts_require_dane, NULL, - host->name, host->address, NULL) == OK; +tls_out.tlsa_usage = 0; +#endif -if (host->dnssec == DS_YES) +#ifndef DISABLE_OCSP { - if( dane_required - || verify_check_this_host(&ob->hosts_try_dane, NULL, - host->name, host->address, NULL) == OK - ) +# ifdef EXPERIMENTAL_DANE + if ( tlsa_dnsa + && ob->hosts_request_ocsp[0] == '*' + && ob->hosts_request_ocsp[1] == '\0' + ) { - /* 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 (rc = dns_lookup(&tlsa_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(&tlsa_dnsa)) - { - log_write(0, LOG_MAIN, "DANE error: TLSA lookup not DNSSEC"); - return DEFER; - } - dane = TRUE; - break; - } + /* 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 (dane_required) - { - /* Hmm - what lookup, precisely? */ - /*XXX a shame we only find this after making tcp & smtp connection */ - log_write(0, LOG_MAIN, "DANE error: previous lookup not DNSSEC"); - return FAIL; - } - -if (!dane) /*XXX todo: enable ocsp with dane */ -#endif +# endif -#ifndef DISABLE_OCSP - { - require_ocsp = verify_check_this_host(&ob->hosts_require_ocsp, - NULL, host->name, host->address, NULL) == OK; - request_ocsp = require_ocsp ? TRUE - : verify_check_this_host(&ob->hosts_request_ocsp, - NULL, host->name, host->address, NULL) == OK; + if ((require_ocsp = + verify_check_given_host(&ob->hosts_require_ocsp, host) == OK)) + request_ocsp = TRUE; + else +# ifdef EXPERIMENTAL_DANE + if (!request_ocsp) +# endif + request_ocsp = + verify_check_given_host(&ob->hosts_request_ocsp, host) == OK; } #endif @@ -1711,22 +1870,23 @@ if (expciphers != NULL) } #ifdef EXPERIMENTAL_DANE -if (dane) +if (tlsa_dnsa) { + SSL_CTX_set_verify(client_ctx, + SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + verify_callback_client_dane); + if (!DANESSL_library_init()) - return tls_error(US"library init", host, US"DANE library error"); + return tls_error(US"library init", host, NULL); if (DANESSL_CTX_init(client_ctx) <= 0) - return tls_error(US"context init", host, US"DANE library error"); + return tls_error(US"context init", host, NULL); } else #endif - if ((rc = tls_client_basic_ctx_init(client_ctx, host, ob -#ifdef EXPERIMENTAL_CERTNAMES - , client_static_cbinfo -#endif - )) != OK) + if ((rc = tls_client_basic_ctx_init(client_ctx, host, ob, client_static_cbinfo)) + != OK) return rc; if ((client_ssl = SSL_new(client_ctx)) == NULL) @@ -1758,9 +1918,32 @@ if (ob->tls_sni) } } +#ifdef EXPERIMENTAL_DANE +if (tlsa_dnsa) + if ((rc = dane_tlsa_load(client_ssl, host, tlsa_dnsa)) != OK) + return rc; +#endif + #ifndef DISABLE_OCSP /* Request certificate status at connection-time. If the server does OCSP stapling we will get the callback (set in tls_init()) */ +# ifdef EXPERIMENTAL_DANE +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")) + ) + { /* Re-eval now $tls_out_tlsa_usage is populated. If + this means we avoid the OCSP request, we wasted the setup + cost in tls_init(). */ + require_ocsp = verify_check_given_host(&ob->hosts_require_ocsp, host) == OK; + request_ocsp = require_ocsp + || verify_check_given_host(&ob->hosts_request_ocsp, host) == OK; + } + } +# endif + if (request_ocsp) { SSL_set_tlsext_status_type(client_ssl, TLSEXT_STATUSTYPE_ocsp); @@ -1769,51 +1952,10 @@ if (request_ocsp) } #endif -#ifdef EXPERIMENTAL_DANE -if (dane) - { - dns_record * rr; - dns_scan dnss; - uschar * hostnames[2] = { host->name, NULL }; - - if (DANESSL_init(client_ssl, NULL, hostnames) != 1) - return tls_error(US"hostnames load", host, US"DANE library error"); - - for (rr = dns_next_rr(&tlsa_dnsa, &dnss, RESET_ANSWERS); - rr; - rr = dns_next_rr(&tlsa_dnsa, &dnss, RESET_NEXT) - ) if (rr->type == T_TLSA) - { - uschar * p = rr->data; - int usage, selector, mtype; - const char * mdname; - - GETSHORT(usage, p); - GETSHORT(selector, p); - GETSHORT(mtype, p); - - switch (mtype) - { - default: /* log bad */ return FAIL; - case 0: mdname = NULL; break; - case 1: mdname = "SHA2-256"; break; - case 2: mdname = "SHA2-512"; break; - } - - switch (DANESSL_add_tlsa(client_ssl, - (uint8_t) usage, (uint8_t) selector, - mdname, p, rr->size - (p - rr->data))) - { - default: - case 0: /* action not taken; log error */ - return FAIL; - case 1: break; - } - } - } +#ifdef EXPERIMENTAL_EVENT +client_static_cbinfo->event_action = tb->event_action; #endif - /* There doesn't seem to be a built-in timeout on connection. */ DEBUG(D_tls) debug_printf("Calling SSL_connect\n"); @@ -1823,8 +1965,8 @@ rc = SSL_connect(client_ssl); alarm(0); #ifdef EXPERIMENTAL_DANE -if (dane) - DANESSL_cleanup(client_ssl); /*XXX earliest possible callpoint. Too early? */ +if (tlsa_dnsa) + DANESSL_cleanup(client_ssl); #endif if (rc <= 0)