X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Fverify.c;h=e00e7b9eba828f4dbfc5512496a8d971041f1e85;hp=d39e103ac99f45ca8f26cd0bd0a702f04f65c929;hb=b77f4a148c955fd18e7437dfed5fa25ed06a983a;hpb=55414b25bee9f0195ccd1e47f3d3b5cba766e099 diff --git a/src/src/verify.c b/src/src/verify.c index d39e103ac..e00e7b9eb 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions concerned with verifying things. The original code for callout @@ -70,7 +70,7 @@ cache_record = dbfn_read_with_length(dbm_file, key, &length); if (cache_record == NULL) { - HDEBUG(D_verify) debug_printf("callout cache: no %s record found\n", type); + HDEBUG(D_verify) debug_printf("callout cache: no %s record found for %s\n", type, key); return NULL; } @@ -84,7 +84,7 @@ now = time(NULL); if (now - cache_record->time_stamp > expire) { - HDEBUG(D_verify) debug_printf("callout cache: %s record expired\n", type); + HDEBUG(D_verify) debug_printf("callout cache: %s record expired for %s\n", type, key); return NULL; } @@ -111,7 +111,7 @@ if (type[0] == 'd' && cache_record->result != ccache_reject) cache_record->random_result = ccache_unknown; } -HDEBUG(D_verify) debug_printf("callout cache: found %s record\n", type); +HDEBUG(D_verify) debug_printf("callout cache: found %s record for %s\n", type, key); return cache_record; } @@ -173,6 +173,9 @@ dbdata_callout_cache new_domain_record; dbdata_callout_cache_address new_address_record; host_item *host; time_t callout_start_time; +#ifdef EXPERIMENTAL_INTERNATIONAL +BOOL utf8_offered = FALSE; +#endif new_domain_record.result = ccache_unknown; new_domain_record.postmaster_result = ccache_unknown; @@ -388,12 +391,9 @@ else log the fact, but carry on without randomming. */ if (callout_random && callout_random_local_part != NULL) - { - random_local_part = expand_string(callout_random_local_part); - if (random_local_part == NULL) + if (!(random_local_part = expand_string(callout_random_local_part))) log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " "callout_random_local_part: %s", expand_string_message); - } /* Default the connect and overall callout timeouts if not set, and record the time we are starting so that we can enforce it. */ @@ -578,9 +578,10 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount. deliver_domain = addr->domain; transport_name = addr->transport->name; - if (!smtp_get_interface(tf->interface, host_af, addr, NULL, &interface, - US"callout") || - !smtp_get_port(tf->port, addr, &port, US"callout")) + if ( !smtp_get_interface(tf->interface, host_af, addr, NULL, &interface, + US"callout") + || !smtp_get_port(tf->port, addr, &port, US"callout") + ) log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address, addr->message); @@ -591,35 +592,6 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount. HDEBUG(D_verify) debug_printf("interface=%s port=%d\n", interface, port); -#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE) - { - int rc; - - tls_out.dane_verified = FALSE; - tls_out.tlsa_usage = 0; - - dane_required = - verify_check_given_host(&ob->hosts_require_dane, host) == OK; - - if (host->dnssec == DS_YES) - { - if( dane_required - || verify_check_given_host(&ob->hosts_try_dane, host) == 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; @@ -635,22 +607,17 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount. outblock.cmd_count = 0; outblock.authenticating = FALSE; - /* Reset the parameters of a TLS session */ - tls_out.cipher = tls_out.peerdn = NULL; - /* Connect to the host; on failure, just loop for the next one, but we set the error for the last one. Use the callout_connect timeout. */ tls_retry_connection: + /* Reset the parameters of a TLS session */ + tls_out.cipher = tls_out.peerdn = tls_out.peercert = NULL; + inblock.sock = outblock.sock = - 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 */ + smtp_connect(host, host_af, port, interface, callout_connect, + addr->transport); if (inblock.sock < 0) { addr->message = string_sprintf("could not connect to %s [%s]: %s", @@ -661,6 +628,36 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount. continue; } +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE) + { + int rc; + + tls_out.dane_verified = FALSE; + tls_out.tlsa_usage = 0; + + dane_required = + verify_check_given_host(&ob->hosts_require_dane, host) == OK; + + if (host->dnssec == DS_YES) + { + if( ( dane_required + || verify_check_given_host(&ob->hosts_try_dane, host) == OK + ) + && (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*/ + /* Expand the helo_data string to find the host name to use. */ if (tf->helo_data != NULL) @@ -926,6 +923,40 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount. } } +#ifdef EXPERIMENTAL_INTERNATIONAL + else if ( addr->prop.utf8_msg + && !addr->prop.utf8_downcvt + && !( esmtp + && ( regex_UTF8 + || ( (regex_UTF8 = regex_must_compile( + US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)", FALSE, TRUE)), + TRUE + ) ) + && ( (utf8_offered = pcre_exec(regex_UTF8, NULL, + CS responsebuffer, Ustrlen(responsebuffer), + 0, PCRE_EOPT, NULL, 0) >= 0) + || addr->prop.utf8_downcvt_maybe + ) ) ) + { + HDEBUG(D_acl|D_v) debug_printf("utf8 required but not offered\n"); + errno = ERRNO_UTF8_FWD; + setflag(addr, af_verify_nsfail); + done = FALSE; + } + else if ( addr->prop.utf8_msg + && (addr->prop.utf8_downcvt || !utf8_offered) + && (setflag(addr, af_utf8_downcvt), + from_address = string_address_utf8_to_alabel(from_address, + &addr->message), + addr->message + ) ) + { + errno = ERRNO_EXPANDFAIL; + setflag(addr, af_verify_nsfail); + done = FALSE; + } +#endif + /* If we haven't authenticated, but are required to, give up. */ /* Try to AUTH */ @@ -943,7 +974,13 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount. ( (addr->auth_sndr = client_authenticated_sender), /* Send the MAIL command */ - (smtp_write_command(&outblock, FALSE, "MAIL FROM:<%s>%s\r\n", + (smtp_write_command(&outblock, FALSE, +#ifdef EXPERIMENTAL_INTERNATIONAL + addr->prop.utf8_msg && !addr->prop.utf8_downcvt + ? "MAIL FROM:<%s>%s SMTPUTF8\r\n" + : +#endif + "MAIL FROM:<%s>%s\r\n", from_address, responsebuffer) >= 0) ) && @@ -982,6 +1019,23 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount. else { + const uschar * rcpt_domain = addr->domain; + +#ifdef EXPERIMENTAL_INTERNATIONAL + uschar * errstr = NULL; + if ( testflag(addr, af_utf8_downcvt) + && (rcpt_domain = string_domain_utf8_to_alabel(rcpt_domain, + &errstr), errstr) + ) + { + addr->message = errstr; + errno = ERRNO_EXPANDFAIL; + setflag(addr, af_verify_nsfail); + done = FALSE; + rcpt_domain = US""; /*XXX errorhandling! */ + } +#endif + new_domain_record.result = (old_domain_cache_result == ccache_reject_mfnull)? ccache_reject_mfnull: ccache_accept; @@ -994,7 +1048,7 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount. BOOL random_ok = smtp_write_command(&outblock, FALSE, "RCPT TO:<%.1000s@%.1000s>\r\n", random_local_part, - addr->domain) >= 0 && + rcpt_domain) >= 0 && smtp_read_response(&inblock, randombuffer, sizeof(randombuffer), '2', callout); @@ -1009,10 +1063,15 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount. /* 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 - connection, we expect to succeed, because the commands succeeded above. */ + connection, we expect to succeed, because the commands succeeded above. + However, some servers drop the connection after responding to an + invalid recipient, so on (any) error we drop and remake the connection. + */ else if (errno == 0) { + /* This would be ok for 1st rcpt a cutthrough, but no way to + handle a subsequent. So refuse to support any */ cancel_cutthrough_connection("random-recipient"); if (randombuffer[0] == '5') @@ -1023,10 +1082,32 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount. smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout) && - smtp_write_command(&outblock, FALSE, "MAIL FROM:<%s>\r\n", + smtp_write_command(&outblock, FALSE, +#ifdef EXPERIMENTAL_INTERNATIONAL + addr->prop.utf8_msg && !addr->prop.utf8_downcvt + ? "MAIL FROM:<%s> SMTPUTF8\r\n" + : +#endif + "MAIL FROM:<%s>\r\n", from_address) >= 0 && smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout); + + if (!done) + { + HDEBUG(D_acl|D_v) + debug_printf("problem after random/rset/mfrom; reopen conn\n"); + random_local_part = NULL; +#ifdef SUPPORT_TLS + tls_close(FALSE, TRUE); +#endif + (void)close(inblock.sock); +#ifdef EXPERIMENTAL_EVENT + (void) event_raise(addr->transport->event_action, + US"tcp:close", NULL); +#endif + goto tls_retry_connection; + } } else done = FALSE; /* Some timeout/connection problem */ } /* Random check */ @@ -1039,11 +1120,27 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount. /* Get the rcpt_include_affixes flag from the transport if there is one, but assume FALSE if there is not. */ + uschar * rcpt = transport_rcpt_address(addr, + addr->transport ? addr->transport->rcpt_include_affixes : FALSE); + +#ifdef EXPERIMENTAL_INTERNATIONAL + /*XXX should the conversion be moved into transport_rcpt_address() ? */ + uschar * dummy_errstr = NULL; + if ( testflag(addr, af_utf8_downcvt) + && (rcpt = string_address_utf8_to_alabel(rcpt, &dummy_errstr), + dummy_errstr + ) ) + { + errno = ERRNO_EXPANDFAIL; + *failure_ptr = US"recipient"; + done = FALSE; + } + else +#endif + done = smtp_write_command(&outblock, FALSE, "RCPT TO:<%.1000s>\r\n", - transport_rcpt_address(addr, - (addr->transport == NULL)? FALSE : - addr->transport->rcpt_include_affixes)) >= 0 && + rcpt) >= 0 && smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout); @@ -1060,8 +1157,9 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount. if (done && pm_mailfrom != NULL) { - /*XXX not suitable for cutthrough - we cannot afford to do an RSET - and lose the original mail-from */ + /* Could possibly shift before main verify, just above, and be ok + for cutthrough. But no way to handle a subsequent rcpt, so just + refuse any */ cancel_cutthrough_connection("postmaster verify"); HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of postmaster verify\n"); @@ -1079,7 +1177,7 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount. (( smtp_write_command(&outblock, FALSE, - "RCPT TO:\r\n", addr->domain) >= 0 && + "RCPT TO:\r\n", rcpt_domain) >= 0 && smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout) ) @@ -1130,6 +1228,21 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount. HDEBUG(D_verify) debug_printf("SMTP timeout\n"); send_quit = FALSE; } +#ifdef EXPERIMENTAL_INTERNATIONAL + else if (errno == ERRNO_UTF8_FWD) + { + extern int acl_where; /* src/acl.c */ + errno = 0; + addr->message = string_sprintf( + "response to \"%s\" from %s [%s] did not include SMTPUTF8", + big_buffer, host->name, host->address); + addr->user_message = acl_where == ACL_WHERE_RCPT + ? US"533 mailbox name not allowed" + : US"550 mailbox unavailable"; + yield = FAIL; + done = TRUE; + } +#endif else if (errno == 0) { if (*responsebuffer == 0) Ustrcpy(responsebuffer, US"connection dropped"); @@ -1600,7 +1713,7 @@ if (addr != vaddr) vaddr->user_message = addr->user_message; vaddr->basic_errno = addr->basic_errno; vaddr->more_errno = addr->more_errno; - vaddr->p.address_data = addr->p.address_data; + vaddr->prop.address_data = addr->prop.address_data; copyflag(vaddr, addr, af_pass_message); } return yield; @@ -1861,8 +1974,8 @@ while (addr_new != NULL) /* Just in case some router parameter refers to it. */ - return_path = (addr->p.errors_address != NULL)? - addr->p.errors_address : sender_address; + return_path = (addr->prop.errors_address != NULL)? + addr->prop.errors_address : sender_address; /* Split the address into domain and local part, handling the %-hack if necessary, and then route it. While routing a sender address, set @@ -1963,18 +2076,17 @@ while (addr_new != NULL) (void)host_find_byname(host, NULL, flags, NULL, TRUE); else { - uschar * d_request = NULL, * d_require = NULL; + dnssec_domains * dnssec_domains = 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; + dnssec_domains = &ob->dnssec; } (void)host_find_bydns(host, NULL, flags, NULL, NULL, NULL, - d_request, d_require, NULL, NULL); + dnssec_domains, NULL, NULL); } } } @@ -2155,7 +2267,7 @@ while (addr_new != NULL) /* If we have carried on to verify a child address, we want the value of $address_data to be that of the child */ - vaddr->p.address_data = addr->p.address_data; + vaddr->prop.address_data = addr->prop.address_data; yield = OK; goto out; } @@ -2187,8 +2299,8 @@ for (addr_list = addr_local, i = 0; i < 2; addr_list = addr_remote, i++) fprintf(f, "%s", CS addr->address); #ifdef EXPERIMENTAL_SRS - if(addr->p.srs_sender) - fprintf(f, " [srs = %s]", addr->p.srs_sender); + if(addr->prop.srs_sender) + fprintf(f, " [srs = %s]", addr->prop.srs_sender); #endif /* If the address is a duplicate, show something about it. */ @@ -2251,6 +2363,12 @@ for (addr_list = addr_local, i = 0; i < 2; addr_list = addr_remote, i++) while (len++ < maxaddlen) fprintf(f," "); if (h->mx >= 0) fprintf(f, "MX=%d", h->mx); if (h->port != PORT_NONE) fprintf(f, " port=%d", h->port); + if (running_in_test_harness) +#ifndef DISABLE_DNSSEC + fprintf(f, " ad=%s", h->dnssec==DS_YES ? "yes" : "no"); +#else + fprintf(f, " ad=no"); +#endif if (h->status == hstatus_unusable) fprintf(f, " ** unusable **"); fprintf(f, "\n"); } @@ -3128,6 +3246,10 @@ if (*t == 0) h.address = NULL; h.mx = MX_NONE; + /* Using byname rather than bydns here means we cannot determine dnssec + status. On the other hand it is unclear how that could be either + propagated up or enforced. */ + rc = host_find_byname(&h, NULL, HOST_FIND_QUALIFY_SINGLE, NULL, FALSE); if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL) { @@ -3501,13 +3623,13 @@ if (t == NULL) dns_record *rr; dns_address **addrp = &(cb->rhs); for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); - rr != NULL; + rr; rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) { if (rr->type == T_A) { dns_address *da = dns_address_from_rr(&dnsa, rr); - if (da != NULL) + if (da) { *addrp = da; while (da->next != NULL) da = da->next;