X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Ftls-openssl.c;h=e37b1add5001ad6ba8b6a2f02f07dee08f8b34df;hb=e5cccda9bbf169ea7dc97fa3859735523dd4cec0;hp=9609d62526682e3647e5476e1393bb8de3d6f7db;hpb=d8e7834aeddc637bd49730444f4d257eb8267135;p=exim.git diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 9609d6252..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 * @@ -436,14 +458,11 @@ const char *pem; if (!expand_check(dhparam, US"tls_dhparam", &dhexpanded)) return FALSE; -if (dhexpanded == NULL || *dhexpanded == '\0') - { +if (!dhexpanded || !*dhexpanded) bio = BIO_new_mem_buf(CS std_dh_prime_default(), -1); - } else if (dhexpanded[0] == '/') { - bio = BIO_new_file(CS dhexpanded, "r"); - if (bio == NULL) + if (!(bio = BIO_new_file(CS dhexpanded, "r"))) { tls_error(string_sprintf("could not read dhparams file %s", dhexpanded), host, US strerror(errno)); @@ -458,8 +477,7 @@ else return TRUE; } - pem = std_dh_prime_named(dhexpanded); - if (!pem) + if (!(pem = std_dh_prime_named(dhexpanded))) { tls_error(string_sprintf("Unknown standard DH prime \"%s\"", dhexpanded), host, US strerror(errno)); @@ -468,8 +486,7 @@ else bio = BIO_new_mem_buf(CS pem, -1); } -dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); -if (dh == NULL) +if (!(dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL))) { BIO_free(bio); tls_error(string_sprintf("Could not read tls_dhparams \"%s\"", dhexpanded), @@ -770,8 +787,7 @@ if (!reexpand_tls_files_for_sni) not confident that memcpy wouldn't break some internal reference counting. Especially since there's a references struct member, which would be off. */ -server_sni = SSL_CTX_new(SSLv23_server_method()); -if (!server_sni) +if (!(server_sni = SSL_CTX_new(SSLv23_server_method()))) { ERR_error_string(ERR_get_error(), ssl_errstring); DEBUG(D_tls) debug_printf("SSL_CTX_new() failed: %s\n", ssl_errstring); @@ -805,8 +821,8 @@ OCSP information. */ rc = tls_expand_session_files(server_sni, cbinfo); if (rc != OK) return SSL_TLSEXT_ERR_NOACK; -rc = init_dh(server_sni, cbinfo->dhparam, NULL); -if (rc != OK) return SSL_TLSEXT_ERR_NOACK; +if (!init_dh(server_sni, cbinfo->dhparam, NULL)) + return SSL_TLSEXT_ERR_NOACK; DEBUG(D_tls) debug_printf("Switching SSL context.\n"); SSL_set_SSL_CTX(s, server_sni); @@ -1005,7 +1021,6 @@ return i; #endif /*!DISABLE_OCSP*/ - /************************************************* * Initialize for TLS * *************************************************/ @@ -1014,13 +1029,14 @@ return i; of the library. We allocate and return a context structure. Arguments: + ctxp returned SSL context host connected host, if client; NULL if server dhparam DH parameter file certificate certificate file privatekey private key ocsp_file file of stapling info (server); flag for require ocsp (client) addr address if client; NULL if server (for some randomness) - cbp place to put allocated context + cbp place to put allocated callback context Returns: OK/DEFER/FAIL */ @@ -1052,6 +1068,7 @@ else cbinfo->u_ocsp.client.verify_store = NULL; #endif cbinfo->dhparam = dhparam; +cbinfo->server_cipher_list = NULL; cbinfo->host = host; SSL_load_error_strings(); /* basic set up */ @@ -1538,6 +1555,50 @@ 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 + ) +{ +int rc; +/* stick to the old behaviour for compatibility if tls_verify_certificates is + 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; + client_verify_optional = FALSE; + +#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 + } +else if (verify_check_host(&ob->tls_try_verify_hosts) == OK) + { + if ((rc = setup_certs(ctx, ob->tls_verify_certificates, + ob->tls_crl, host, TRUE, verify_callback_client)) != OK) + return rc; + client_verify_optional = TRUE; + } + +return OK; +} /************************************************* * Start a TLS session in a client * @@ -1562,16 +1623,84 @@ 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]; + #ifndef DISABLE_OCSP -BOOL require_ocsp = verify_check_this_host(&ob->hosts_require_ocsp, - NULL, host->name, host->address, NULL) == OK; -BOOL request_ocsp = require_ocsp ? TRUE - : verify_check_this_host(&ob->hosts_request_ocsp, - NULL, host->name, host->address, NULL) == OK; +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; + +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 */ + + 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 + { + 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; + } #endif rc = tls_init(&client_ctx, host, NULL, @@ -1602,38 +1731,26 @@ if (expciphers != NULL) return tls_error(US"SSL_CTX_set_cipher_list", host, NULL); } -/* stick to the old behaviour for compatibility if tls_verify_certificates is - 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)) +#ifdef EXPERIMENTAL_DANE +if (dane) { - if ((rc = setup_certs(client_ctx, ob->tls_verify_certificates, - ob->tls_crl, host, FALSE, verify_callback_client)) != OK) - return rc; - client_verify_optional = FALSE; + SSL_CTX_set_verify(client_ctx, SSL_VERIFY_PEER, verify_callback_client_dane); + + if (!DANESSL_library_init()) + return tls_error(US"library init", host, NULL); + if (DANESSL_CTX_init(client_ctx) <= 0) + return tls_error(US"context init", host, NULL); + } +else + +#endif + if ((rc = tls_client_basic_ctx_init(client_ctx, host, ob #ifdef EXPERIMENTAL_CERTNAMES - if (ob->tls_verify_cert_hostnames) - { - if (!expand_check(ob->tls_verify_cert_hostnames, - US"tls_verify_cert_hostnames", - &client_static_cbinfo->verify_cert_hostnames)) - return FAIL; - if (client_static_cbinfo->verify_cert_hostnames) - DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n", - client_static_cbinfo->verify_cert_hostnames); - } + , client_static_cbinfo #endif - } -else if (verify_check_host(&ob->tls_try_verify_hosts) == OK) - { - if ((rc = setup_certs(client_ctx, ob->tls_verify_certificates, - ob->tls_crl, host, TRUE, verify_callback_client)) != OK) + )) != OK) return rc; - client_verify_optional = TRUE; - } if ((client_ssl = SSL_new(client_ctx)) == NULL) return tls_error(US"SSL_new", host, NULL); @@ -1675,6 +1792,51 @@ 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, 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); + + 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 + + /* There doesn't seem to be a built-in timeout on connection. */ DEBUG(D_tls) debug_printf("Calling SSL_connect\n"); @@ -1683,6 +1845,11 @@ alarm(ob->command_timeout); rc = SSL_connect(client_ssl); alarm(0); +#ifdef EXPERIMENTAL_DANE +if (dane) + DANESSL_cleanup(client_ssl); /*XXX earliest possible callpoint. Too early? */ +#endif + if (rc <= 0) return tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL);