X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/0f06b4f296802e4e13188c740ea09419931a3020..8d69247061599b35b60308ca4b75ba529b8dcba2:/src/src/tls-openssl.c diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 2e95a467a..afc898ca7 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -38,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" @@ -120,7 +127,7 @@ typedef struct tls_ext_ctx_cb { #ifdef EXPERIMENTAL_CERTNAMES uschar * verify_cert_hostnames; #endif -#ifdef EXPERIMENTAL_TPDA +#ifdef EXPERIMENTAL_EVENT uschar * event_action; #endif } tls_ext_ctx_cb; @@ -167,27 +174,28 @@ Returns: OK/DEFER/FAIL 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; - } } @@ -286,6 +294,7 @@ verify_callback(int state, X509_STORE_CTX *x509ctx, { X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx); int depth = X509_STORE_CTX_get_error_depth(x509ctx); +uschar * ev; static uschar txt[256]; X509_NAME_oneline(X509_get_subject_name(cert), CS txt, sizeof(txt)); @@ -321,12 +330,12 @@ else if (depth != 0) 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 (event_raise(ev, US"tls:cert", string_sprintf("%d", depth)) == DEFER) { log_write(0, LOG_MAIN, "SSL verify denied by event-action: " "depth=%d cert=%s", depth, txt); @@ -353,9 +362,12 @@ else && ((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; @@ -364,7 +376,8 @@ else 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) { @@ -390,11 +403,10 @@ else # 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 (event_raise(ev, US"tls:cert", US"0") == DEFER) { log_write(0, LOG_MAIN, "SSL verify denied by event-action: " "depth=0 cert=%s", txt); @@ -402,7 +414,6 @@ else *calledp = TRUE; return 0; /* reject */ } - } #endif DEBUG(D_tls) debug_printf("SSL%s verify ok: depth=0 SN=%s\n", @@ -437,7 +448,7 @@ 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_TPDA +#ifdef EXPERIMENTAL_EVENT int depth = X509_STORE_CTX_get_error_depth(x509ctx); #endif @@ -447,10 +458,10 @@ DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s\n", 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, + if (event_raise(client_static_cbinfo->event_action, US"tls:cert", string_sprintf("%d", depth)) == DEFER) { log_write(0, LOG_MAIN, "DANE verify denied by event-action: " @@ -1139,7 +1150,7 @@ else cbinfo->dhparam = dhparam; cbinfo->server_cipher_list = NULL; cbinfo->host = host; -#ifdef EXPERIMENTAL_TPDA +#ifdef EXPERIMENTAL_EVENT cbinfo->event_action = NULL; #endif @@ -1376,9 +1387,23 @@ if (expcerts != NULL && *expcerts != '\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) { - 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); } } @@ -1677,44 +1702,6 @@ return OK; #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) { @@ -1735,22 +1722,23 @@ for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); 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: @@ -1765,7 +1753,7 @@ for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); 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*/ @@ -1783,6 +1771,7 @@ Argument: 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 @@ -1791,7 +1780,11 @@ Returns: OK on success 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; @@ -1805,60 +1798,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 -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 @@ -1891,7 +1860,7 @@ if (expciphers != NULL) } #ifdef EXPERIMENTAL_DANE -if (dane) +if (tlsa_dnsa) { SSL_CTX_set_verify(client_ctx, SSL_VERIFY_PEER, verify_callback_client_dane); @@ -1941,8 +1910,8 @@ if (ob->tls_sni) } #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 @@ -1953,8 +1922,8 @@ does OCSP stapling we will get the callback (set in tls_init()) */ 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 @@ -1976,12 +1945,8 @@ if (request_ocsp) } #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. */ @@ -1993,7 +1958,7 @@ rc = SSL_connect(client_ssl); alarm(0); #ifdef EXPERIMENTAL_DANE -if (dane) +if (tlsa_dnsa) DANESSL_cleanup(client_ssl); #endif