X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Fverify.c;h=82dc5cc722f322a8e933b6470600ba22d9ec1b7b;hb=c1cc0506c3069a9d93d71321f9578150662ede91;hp=39f546ed6a7ab072020c410cf4ce9c25d86dc26c;hpb=511a6c14924b5e931d67c4257ee7592dcc6ef49e;p=exim.git diff --git a/src/src/verify.c b/src/src/verify.c index 39f546ed6..82dc5cc72 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -373,10 +373,13 @@ if (!addr->transport) { HDEBUG(D_verify) debug_printf("cannot callout via null transport\n"); } +else if (Ustrcmp(addr->transport->driver_name, "smtp") != 0) + log_write(0, LOG_MAIN|LOG_PANIC|LOG_CONFIG_FOR, "callout transport '%s': %s is non-smtp", + addr->transport->name, addr->transport->driver_name); else { smtp_transport_options_block *ob = - (smtp_transport_options_block *)(addr->transport->options_block); + (smtp_transport_options_block *)addr->transport->options_block; /* The information wasn't available in the cache, so we have to do a real callout and save the result in the cache for next time, unless no_cache is set, @@ -423,6 +426,10 @@ else BOOL esmtp; BOOL suppress_tls = FALSE; uschar *interface = NULL; /* Outgoing interface to use; NULL => any */ +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE) + BOOL dane = FALSE; + dns_answer tlsa_dnsa; +#endif uschar inbuffer[4096]; uschar outbuffer[1024]; uschar responsebuffer[4096]; @@ -459,7 +466,9 @@ else deliver_host = host->name; deliver_host_address = host->address; + deliver_host_port = host->port; deliver_domain = addr->domain; + transport_name = addr->transport->name; if (!smtp_get_interface(tf->interface, host_af, addr, NULL, &interface, US"callout") || @@ -474,6 +483,37 @@ else HDEBUG(D_verify) debug_printf("interface=%s port=%d\n", interface, port); +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE) + { + BOOL dane_required; + int rc; + + 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) + { + log_write(0, LOG_MAIN, "DANE error: %s lookup not DNSSEC", host->name); + return FAIL; + } + + if (dane) + ob->tls_tempfail_tryclear = FALSE; + } +#endif /*DANE*/ + /* Set up the buffer for reading SMTP response packets. */ inblock.buffer = inbuffer; @@ -498,12 +538,18 @@ else tls_retry_connection: inblock.sock = outblock.sock = - smtp_connect(host, host_af, port, interface, callout_connect, TRUE, NULL); + smtp_connect(host, host_af, port, interface, callout_connect, TRUE, NULL +#ifdef EXPERIMENTAL_EVENT + /*XXX event action? NULL for now. */ + , NULL +#endif + ); /* reconsider DSCP here */ if (inblock.sock < 0) { addr->message = string_sprintf("could not connect to %s [%s]: %s", host->name, host->address, strerror(errno)); + transport_name = NULL; deliver_host = deliver_host_address = NULL; deliver_domain = save_deliver_domain; continue; @@ -521,9 +567,6 @@ else else active_hostname = s; } - deliver_host = deliver_host_address = NULL; - deliver_domain = save_deliver_domain; - /* Wait for initial response, and send HELO. The smtp_write_command() function leaves its command in big_buffer. This is used in error responses. Initialize it in case the connection is rejected. */ @@ -533,12 +576,23 @@ else /* Unless ssl-on-connect, wait for the initial greeting */ smtps_redo_greeting: - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (!smtps || (smtps && tls_out.active >= 0)) - #endif +#endif + { if (!(done= smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout))) goto RESPONSE_FAILED; +#ifdef EXPERIMENTAL_EVENT + if (event_raise(addr->transport->event_action, + US"smtp:connect", responsebuffer)) + { + /* Logging? Debug? */ + goto RESPONSE_FAILED; + } +#endif + } + /* Not worth checking greeting line for ESMTP support */ if (!(esmtp = verify_check_this_host(&(ob->hosts_avoid_esmtp), NULL, host->name, host->address, NULL) != OK)) @@ -547,14 +601,14 @@ else tls_redo_helo: - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (smtps && tls_out.active < 0) /* ssl-on-connect, first pass */ { tls_offered = TRUE; ob->tls_tempfail_tryclear = FALSE; } - else /* all other cases */ - #endif + else /* all other cases */ +#endif { esmtp_retry: @@ -568,26 +622,26 @@ else done= FALSE; goto RESPONSE_FAILED; } - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS tls_offered = FALSE; - #endif +#endif esmtp = FALSE; goto esmtp_retry; /* fallback to HELO */ } /* Set tls_offered if the response to EHLO specifies support for STARTTLS. */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (esmtp && !suppress_tls && tls_out.active < 0) - { - if (regex_STARTTLS == NULL) regex_STARTTLS = - regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE); + { + if (regex_STARTTLS == NULL) regex_STARTTLS = + regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE); - tls_offered = pcre_exec(regex_STARTTLS, NULL, CS responsebuffer, - Ustrlen(responsebuffer), 0, PCRE_EOPT, NULL, 0) >= 0; + tls_offered = pcre_exec(regex_STARTTLS, NULL, CS responsebuffer, + Ustrlen(responsebuffer), 0, PCRE_EOPT, NULL, 0) >= 0; } else tls_offered = FALSE; - #endif +#endif } /* If TLS is available on this connection attempt to @@ -598,7 +652,7 @@ else the client not be required to use TLS. If the response is bad, copy the buffer for error analysis. */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (tls_offered && verify_check_this_host(&(ob->hosts_avoid_tls), NULL, host->name, host->address, NULL) != OK && @@ -623,47 +677,54 @@ else { if (errno != 0 || buffer2[0] == 0 || (buffer2[0] == '4' && !ob->tls_tempfail_tryclear)) - { - Ustrncpy(responsebuffer, buffer2, sizeof(responsebuffer)); - done= FALSE; - goto RESPONSE_FAILED; - } + { + Ustrncpy(responsebuffer, buffer2, sizeof(responsebuffer)); + done= FALSE; + goto RESPONSE_FAILED; + } } /* STARTTLS accepted or ssl-on-connect: try to negotiate a TLS session. */ else { - int rc = tls_client_start(inblock.sock, host, addr, - ob->tls_certificate, ob->tls_privatekey, - ob->tls_sni, - ob->tls_verify_certificates, ob->tls_crl, - ob->tls_require_ciphers, -#ifdef EXPERIMENTAL_OCSP - ob->hosts_require_ocsp, + int oldtimeout = ob->command_timeout; + int rc; + + ob->command_timeout = callout; + rc = tls_client_start(inblock.sock, host, addr, addr->transport +#ifdef EXPERIMENTAL_DANE + , dane ? &tlsa_dnsa : NULL #endif - ob->tls_dh_min_bits, callout, - ob->tls_verify_hosts, ob->tls_try_verify_hosts); + ); + ob->command_timeout = oldtimeout; /* TLS negotiation failed; give an error. Try in clear on a new connection, if the options permit it for this host. */ if (rc != OK) { - if (rc == DEFER && ob->tls_tempfail_tryclear && !smtps && - verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name, - host->address, NULL) != OK) - { - (void)close(inblock.sock); - log_write(0, LOG_MAIN, "TLS session failure: delivering unencrypted " - "to %s [%s] (not in hosts_require_tls)", host->name, host->address); - suppress_tls = TRUE; - goto tls_retry_connection; - } - /*save_errno = ERRNO_TLSFAILURE;*/ - /*message = US"failure while setting up TLS session";*/ - send_quit = FALSE; - done= FALSE; - goto TLS_FAILED; - } + if ( rc == DEFER + && ob->tls_tempfail_tryclear + && !smtps + && verify_check_this_host(&(ob->hosts_require_tls), NULL, + host->name, host->address, NULL) != OK + ) + { + (void)close(inblock.sock); +#ifdef EXPERIMENTAL_EVENT + (void) event_raise(addr->transport->event_action, + US"tcp:close", NULL); +#endif + log_write(0, LOG_MAIN, "TLS session failure: delivering unencrypted " + "to %s [%s] (not in hosts_require_tls)", host->name, host->address); + suppress_tls = TRUE; + goto tls_retry_connection; + } + /*save_errno = ERRNO_TLSFAILURE;*/ + /*message = US"failure while setting up TLS session";*/ + send_quit = FALSE; + done= FALSE; + goto TLS_FAILED; + } /* TLS session is set up. Copy info for logging. */ addr->cipher = tls_out.cipher; @@ -671,7 +732,7 @@ else /* For SMTPS we need to wait for the initial OK response, then do HELO. */ if (smtps) - goto smtps_redo_greeting; + goto smtps_redo_greeting; /* For STARTTLS we need to redo EHLO */ goto tls_redo_helo; @@ -680,13 +741,20 @@ else /* If the host is required to use a secure channel, ensure that we have one. */ if (tls_out.active < 0) - if (verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name, - host->address, NULL) == OK) + if ( +#ifdef EXPERIMENTAL_DANE + dane || +#endif + verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name, + host->address, NULL) == OK + ) { /*save_errno = ERRNO_TLSREQUIRED;*/ - log_write(0, LOG_MAIN, "a TLS session is required for %s [%s], but %s", + log_write(0, LOG_MAIN, + "H=%s [%s]: a TLS session is required for this host, but %s", host->name, host->address, - tls_offered? "an attempt to start TLS failed" : "the server did not offer TLS support"); + tls_offered ? "an attempt to start TLS failed" + : "the server did not offer TLS support"); done= FALSE; goto TLS_FAILED; } @@ -706,13 +774,13 @@ else cutthrough_delivery= FALSE; HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of transport filter\n"); } - #ifndef DISABLE_DKIM +#ifndef DISABLE_DKIM if (ob->dkim_domain) { cutthrough_delivery= FALSE; HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of DKIM signing\n"); } - #endif +#endif } SEND_FAILED: @@ -721,7 +789,6 @@ else ; /* Clear down of the TLS, SMTP and TCP layers on error is handled below. */ - /* Failure to accept HELO is cached; this blocks the whole domain for all senders. I/O errors and defer responses are not cached. */ @@ -759,6 +826,9 @@ else smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout); + deliver_host = deliver_host_address = NULL; + deliver_domain = save_deliver_domain; + /* If the host does not accept MAIL FROM:<>, arrange to cache this information, but again, don't record anything for an I/O error or a defer. Do not cache rejections of MAIL when a non-empty sender has been used, because @@ -811,9 +881,7 @@ else /* If accepted, we aren't going to do any further tests below. */ if (random_ok) - { new_domain_record.random_result = ccache_accept; - } /* Otherwise, cache a real negative response, and get back to the right state to send RCPT. Unless there's some problem such as a dropped @@ -978,9 +1046,7 @@ else cutthrough_addr = *addr; /* Save the address_item for later logging */ cutthrough_addr.next = NULL; cutthrough_addr.host_used = store_get(sizeof(host_item)); - cutthrough_addr.host_used->name = host->name; - cutthrough_addr.host_used->address = host->address; - cutthrough_addr.host_used->port = port; + *(cutthrough_addr.host_used) = *host; if (addr->parent) *(cutthrough_addr.parent = store_get(sizeof(address_item)))= *addr->parent; ctblock.buffer = ctbuffer; @@ -996,10 +1062,14 @@ else cancel_cutthrough_connection("multiple verify calls"); if (send_quit) (void)smtp_write_command(&outblock, FALSE, "QUIT\r\n"); - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS tls_close(FALSE, TRUE); - #endif +#endif (void)close(inblock.sock); +#ifdef EXPERIMENTAL_EVENT + (void) event_raise(addr->transport->event_action, + US"tcp:close", NULL); +#endif } } /* Loop through all hosts, while !done */ @@ -1573,13 +1643,7 @@ if (address[0] == 0) return OK; they're used in the context of a transport used by verification. Reset them at exit from this routine. */ -modify_variable(US"tls_bits", &tls_out.bits); -modify_variable(US"tls_certificate_verified", &tls_out.certificate_verified); -modify_variable(US"tls_cipher", &tls_out.cipher); -modify_variable(US"tls_peerdn", &tls_out.peerdn); -#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS) -modify_variable(US"tls_sni", &tls_out.sni); -#endif +tls_modify_variables(&tls_out); /* Save a copy of the sender address for re-instating if we change it to <> while verifying a sender address (a nice bit of self-reference there). */ @@ -1752,8 +1816,20 @@ while (addr_new != NULL) string_is_ip_address(host->name, NULL) != 0) (void)host_find_byname(host, NULL, flags, &canonical_name, TRUE); else + { + uschar * d_request = NULL, * d_require = NULL; + if (Ustrcmp(addr->transport->driver_name, "smtp") == 0) + { + smtp_transport_options_block * ob = + (smtp_transport_options_block *) + addr->transport->options_block; + d_request = ob->dnssec_request_domains; + d_require = ob->dnssec_require_domains; + } + (void)host_find_bydns(host, NULL, flags, NULL, NULL, NULL, - &canonical_name, NULL); + d_request, d_require, &canonical_name, NULL); + } } } } @@ -1776,8 +1852,10 @@ while (addr_new != NULL) #ifdef SUPPORT_TLS deliver_set_expansions(addr); #endif + verify_mode = is_recipient ? US"R" : US"S"; rc = do_callout(addr, host_list, &tf, callout, callout_overall, callout_connect, options, se_mailfrom, pm_mailfrom); + verify_mode = NULL; } } else @@ -2038,14 +2116,7 @@ for (addr_list = addr_local, i = 0; i < 2; addr_list = addr_remote, i++) the -bv or -bt case). */ out: - -modify_variable(US"tls_bits", &tls_in.bits); -modify_variable(US"tls_certificate_verified", &tls_in.certificate_verified); -modify_variable(US"tls_cipher", &tls_in.cipher); -modify_variable(US"tls_peerdn", &tls_in.peerdn); -#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS) -modify_variable(US"tls_sni", &tls_in.sni); -#endif +tls_modify_variables(&tls_in); return yield; } @@ -3556,7 +3627,7 @@ revadd[0] = 0; /* In case this is the first time the DNS resolver is being used. */ -dns_init(FALSE, FALSE); +dns_init(FALSE, FALSE, FALSE); /*XXX dnssec? */ /* Loop through all the domains supplied, until something matches */