X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/67791ce4a0ae39a79aab22157d587d774190ac61..cf2b569e3a2f8956b7045191e96bc5edfd366c78:/src/src/tls-openssl.c diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index bcca506e0..eb74605da 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -327,13 +327,25 @@ else /* client, wanting hostname check */ # if OPENSSL_VERSION_NUMBER >= 0x010100000L || OPENSSL_VERSION_NUMBER >= 0x010002000L +# ifndef X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS +# define X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS 0 +# endif { int sep = 0; uschar * list = verify_cert_hostnames; uschar * name; - while (name = string_nextinlist(&list, &sep, NULL, 0)) - if (X509_check_host(cert, name, 0, 0)) + int rc; + while ((name = string_nextinlist(&list, &sep, NULL, 0))) + if ((rc = X509_check_host(cert, name, 0, + X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS))) + { + if (rc < 0) + { + log_write(0, LOG_MAIN, "SSL verify error: internal error\n"); + name = NULL; + } break; + } if (!name) { log_write(0, LOG_MAIN, @@ -424,14 +436,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)); @@ -446,8 +455,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)); @@ -456,8 +464,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), @@ -758,8 +765,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); @@ -793,8 +799,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); @@ -1002,13 +1008,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 */ @@ -1040,6 +1047,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 */ @@ -1526,6 +1534,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 * @@ -1550,16 +1602,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) + { + /* 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 + +#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, @@ -1590,38 +1710,24 @@ 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; + if (!DANESSL_library_init()) + return tls_error(US"library init", host, US"DANE library error"); + if (DANESSL_CTX_init(client_ctx) <= 0) + return tls_error(US"context init", host, US"DANE library error"); + } +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); @@ -1663,6 +1769,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, 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; + } + } + } +#endif + + /* There doesn't seem to be a built-in timeout on connection. */ DEBUG(D_tls) debug_printf("Calling SSL_connect\n"); @@ -1671,6 +1822,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);