X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Ftls-openssl.c;h=e37b1add5001ad6ba8b6a2f02f07dee08f8b34df;hb=e5cccda9bbf169ea7dc97fa3859735523dd4cec0;hp=fa29f4ed9eaa21922ab7ea47335dfc8fd2a9d4f9;hpb=6634ac8dc1c8fa3f429835a4735adfeb1bcc4390;p=exim.git diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index fa29f4ed9..e37b1add5 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -361,7 +361,7 @@ else return 0; /* reject */ } # endif -#endif +#endif /*EXPERIMENTAL_CERTNAMES*/ DEBUG(D_tls) debug_printf("SSL%s verify ok: depth=0 SN=%s\n", *calledp ? "" : " authenticated", txt); @@ -385,6 +385,28 @@ 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]; + +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); + +if (state == 1) + tls_out.certificate_verified = TRUE; +return 1; +} +#endif + /************************************************* * Information callback * @@ -999,7 +1021,6 @@ return i; #endif /*!DISABLE_OCSP*/ - /************************************************* * Initialize for TLS * *************************************************/ @@ -1602,8 +1623,8 @@ tls_client_start(int fd, host_item *host, address_item *addr, { smtp_transport_options_block * ob = v_ob; static uschar txt[256]; -uschar *expciphers; -X509* server_cert; +uschar * expciphers; +X509 * server_cert; int rc; static uschar cipherbuf[256]; @@ -1612,14 +1633,64 @@ BOOL request_ocsp = FALSE; BOOL require_ocsp = FALSE; #endif #ifdef EXPERIMENTAL_DANE -BOOL dane_in_use = FALSE; +dns_answer tlsa_dnsa; +BOOL dane = FALSE; +BOOL dane_required; #endif #ifdef EXPERIMENTAL_DANE -/*XXX TBD: test for transport options, and for TLSA records */ -/*dane_in_use = TRUE;*/ +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 + ) + { + /* 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 */ -if (!dane_in_use) + 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; + } + } + } +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; + } + +if (!dane) /*XXX todo: enable ocsp with dane */ #endif #ifndef DISABLE_OCSP @@ -1661,12 +1732,14 @@ if (expciphers != NULL) } #ifdef EXPERIMENTAL_DANE -if (dane_in_use) +if (dane) { + SSL_CTX_set_verify(client_ctx, SSL_VERIFY_PEER, 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 @@ -1720,18 +1793,46 @@ if (request_ocsp) #endif #ifdef EXPERIMENTAL_DANE -if (dane_in_use) +if (dane) { - if (DANESSL_init(client_ssl, NULL, NULL /*??? hostnames*/) != 1) - return tls_error(US"hostnames load", host, US"DANE library error"); + 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, NULL); + + 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); - /* - foreach TLSA record - - DANESSL_add_tlsa(client_ssl, uint8_t usage, uint8_t selector, - const char *mdname, - unsigned const char *data, size_t dlen) - */ + switch (mtype) + { + default: /* log bad */ return FAIL; + case 0: mdname = NULL; break; + case 1: mdname = "sha256"; break; + case 2: mdname = "sha512"; 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 */ + return tls_error(US"tlsa load", host, NULL); + case 1: break; + } + } } #endif @@ -1745,7 +1846,8 @@ rc = SSL_connect(client_ssl); alarm(0); #ifdef EXPERIMENTAL_DANE -DANESSL_cleanup(client_ssl); /*XXX earliest possible callpoint. Too early? */ +if (dane) + DANESSL_cleanup(client_ssl); /*XXX earliest possible callpoint. Too early? */ #endif if (rc <= 0)