X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Ftransports%2Fsmtp.c;h=757a837dbc3fe2499a4242b8ab948bfe589a40aa;hp=a8a78d94546211a70f2a4c4445bff31ca2747bb4;hb=ff5aac2be82177a7c96c71ce7dd0939e70436402;hpb=93a680e4c9c899d86ff3fde0933fb5367b34af50 diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index a8a78d945..757a837db 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -44,17 +44,17 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, delay_after_cutoff) }, #ifndef DISABLE_DKIM { "dkim_canon", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, dkim_canon) }, + (void *)offsetof(smtp_transport_options_block, dkim.dkim_canon) }, { "dkim_domain", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, dkim_domain) }, + (void *)offsetof(smtp_transport_options_block, dkim.dkim_domain) }, { "dkim_private_key", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, dkim_private_key) }, + (void *)offsetof(smtp_transport_options_block, dkim.dkim_private_key) }, { "dkim_selector", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, dkim_selector) }, + (void *)offsetof(smtp_transport_options_block, dkim.dkim_selector) }, { "dkim_sign_headers", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, dkim_sign_headers) }, + (void *)offsetof(smtp_transport_options_block, dkim.dkim_sign_headers) }, { "dkim_strict", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, dkim_strict) }, + (void *)offsetof(smtp_transport_options_block, dkim.dkim_strict) }, #endif { "dns_qualify_single", opt_bool, (void *)offsetof(smtp_transport_options_block, dns_qualify_single) }, @@ -72,17 +72,6 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, final_timeout) }, { "gethostbyname", opt_bool, (void *)offsetof(smtp_transport_options_block, gethostbyname) }, -#ifdef SUPPORT_TLS - /* These are no longer honoured, as of Exim 4.80; for now, we silently - ignore; 4.83 will warn, and a later-still release will remove - these options, so that using them becomes an error. */ - { "gnutls_require_kx", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, gnutls_require_kx) }, - { "gnutls_require_mac", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, gnutls_require_mac) }, - { "gnutls_require_protocols", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, gnutls_require_proto) }, -#endif { "helo_data", opt_stringptr, (void *)offsetof(smtp_transport_options_block, helo_data) }, { "hosts", opt_stringptr, @@ -257,9 +246,6 @@ smtp_transport_options_block smtp_transport_option_defaults = { NULL, /* tls_crl */ NULL, /* tls_privatekey */ NULL, /* tls_require_ciphers */ - NULL, /* gnutls_require_kx */ - NULL, /* gnutls_require_mac */ - NULL, /* gnutls_require_proto */ NULL, /* tls_sni */ US"system", /* tls_verify_certificates */ EXIM_CLIENT_DH_DEFAULT_MIN_BITS, @@ -270,12 +256,12 @@ smtp_transport_options_block smtp_transport_option_defaults = { US"*" /* tls_verify_cert_hostnames */ #endif #ifndef DISABLE_DKIM - ,NULL, /* dkim_canon */ - NULL, /* dkim_domain */ - NULL, /* dkim_private_key */ - NULL, /* dkim_selector */ - NULL, /* dkim_sign_headers */ - NULL /* dkim_strict */ + , {NULL, /* dkim_canon */ + NULL, /* dkim_domain */ + NULL, /* dkim_private_key */ + NULL, /* dkim_selector */ + NULL, /* dkim_sign_headers */ + NULL} /* dkim_strict */ #endif }; @@ -411,15 +397,6 @@ if (ob->hosts_override && ob->hosts != NULL) tblock->overrides_hosts = TRUE; for them, but do not do any lookups at this time. */ host_build_hostlist(&(ob->fallback_hostlist), ob->fallback_hosts, FALSE); - -#ifdef SUPPORT_TLS -if ( ob->gnutls_require_kx - || ob->gnutls_require_mac - || ob->gnutls_require_proto) - log_write(0, LOG_MAIN, "WARNING: smtp transport options" - " gnutls_require_kx, gnutls_require_mac and gnutls_require_protocols" - " are obsolete\n"); -#endif } @@ -1215,9 +1192,15 @@ return FALSE; #ifdef EXPERIMENTAL_DANE +/* Lookup TLSA record for host/port. +Return: OK success with dnssec; DANE mode + DEFER Do not use this host now, may retry later + FAIL_FORCED No TLSA record; DANE not usable + FAIL Do not use this connection +*/ + int -tlsa_lookup(const host_item * host, dns_answer * dnsa, - BOOL dane_required, BOOL * dane) +tlsa_lookup(const host_item * host, dns_answer * dnsa, BOOL dane_required) { /* move this out to host.c given the similarity to dns_lookup() ? */ uschar buffer[300]; @@ -1228,25 +1211,24 @@ const uschar * fullname = buffer; switch (dns_lookup(dnsa, buffer, T_TLSA, &fullname)) { - case DNS_AGAIN: - return DEFER; /* just defer this TLS'd conn */ - - default: - case DNS_FAIL: - if (dane_required) - return FAIL; - break; - case DNS_SUCCEED: if (!dns_is_secure(dnsa)) { log_write(0, LOG_MAIN, "DANE error: TLSA lookup not DNSSEC"); return DEFER; } - *dane = TRUE; - break; + return OK; + + case DNS_AGAIN: + return DEFER; /* just defer this TLS'd conn */ + + case DNS_NOMATCH: + return dane_required ? FAIL : FAIL_FORCED; + + default: + case DNS_FAIL: + return dane_required ? FAIL : DEFER; } -return OK; } #endif @@ -1311,7 +1293,6 @@ we will veto this new message. */ static BOOL smtp_are_same_identities(uschar * message_id, smtp_compare_t * s_compare) { - uschar * message_local_identity, * current_local_identity, * new_sender_address; @@ -1334,40 +1315,40 @@ uschar ehlo_response(uschar * buf, size_t bsize, uschar checks) { #ifdef SUPPORT_TLS -if (checks & PEER_OFFERED_TLS) - if (pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) - checks &= ~PEER_OFFERED_TLS; +if ( checks & PEER_OFFERED_TLS + && pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) + checks &= ~PEER_OFFERED_TLS; #endif - if ( checks & PEER_OFFERED_IGNQ - && pcre_exec(regex_IGNOREQUOTA, NULL, CS buf, bsize, 0, - PCRE_EOPT, NULL, 0) < 0) - checks &= ~PEER_OFFERED_IGNQ; +if ( checks & PEER_OFFERED_IGNQ + && pcre_exec(regex_IGNOREQUOTA, NULL, CS buf, bsize, 0, + PCRE_EOPT, NULL, 0) < 0) + checks &= ~PEER_OFFERED_IGNQ; #ifndef DISABLE_PRDR - if ( checks & PEER_OFFERED_PRDR - && pcre_exec(regex_PRDR, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) - checks &= ~PEER_OFFERED_PRDR; +if ( checks & PEER_OFFERED_PRDR + && pcre_exec(regex_PRDR, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) + checks &= ~PEER_OFFERED_PRDR; #endif #ifdef SUPPORT_I18N - if ( checks & PEER_OFFERED_UTF8 - && pcre_exec(regex_UTF8, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) - checks &= ~PEER_OFFERED_UTF8; +if ( checks & PEER_OFFERED_UTF8 + && pcre_exec(regex_UTF8, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) + checks &= ~PEER_OFFERED_UTF8; #endif - if ( checks & PEER_OFFERED_DSN - && pcre_exec(regex_DSN, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) - checks &= ~PEER_OFFERED_DSN; +if ( checks & PEER_OFFERED_DSN + && pcre_exec(regex_DSN, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) + checks &= ~PEER_OFFERED_DSN; - if ( checks & PEER_OFFERED_PIPE - && pcre_exec(regex_PIPELINING, NULL, CS buf, bsize, 0, - PCRE_EOPT, NULL, 0) < 0) - checks &= ~PEER_OFFERED_PIPE; +if ( checks & PEER_OFFERED_PIPE + && pcre_exec(regex_PIPELINING, NULL, CS buf, bsize, 0, + PCRE_EOPT, NULL, 0) < 0) + checks &= ~PEER_OFFERED_PIPE; - if ( checks & PEER_OFFERED_SIZE - && pcre_exec(regex_SIZE, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) - checks &= ~PEER_OFFERED_SIZE; +if ( checks & PEER_OFFERED_SIZE + && pcre_exec(regex_SIZE, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) + checks &= ~PEER_OFFERED_SIZE; return checks; } @@ -1545,26 +1526,26 @@ if (continue_hostname == NULL) 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 - && dane_required /* do not error on only dane-requested */ + if( dane_required + || verify_check_given_host(&ob->hosts_try_dane, host) == OK ) - { - set_errno_nohost(addrlist, ERRNO_DNSDEFER, - string_sprintf("DANE error: tlsa lookup %s", - rc == DEFER ? "DEFER" : "FAIL"), - rc, FALSE); - return rc; - } + switch (rc = tlsa_lookup(host, &tlsa_dnsa, dane_required)) + { + case OK: dane = TRUE; break; + case FAIL_FORCED: break; + default: set_errno_nohost(addrlist, ERRNO_DNSDEFER, + string_sprintf("DANE error: tlsa lookup %s", + rc == DEFER ? "DEFER" : "FAIL"), + rc, FALSE); + return rc; + } } else if (dane_required) { set_errno_nohost(addrlist, ERRNO_DNSDEFER, string_sprintf("DANE error: %s lookup not DNSSEC", host->name), FAIL, FALSE); - return FAIL; + return FAIL; } if (dane) @@ -1728,13 +1709,7 @@ goto SEND_QUIT; if (esmtp || lmtp) peer_offered = ehlo_response(buffer, Ustrlen(buffer), - PEER_OFFERED_TLS - | 0 /* IGNQ checked later */ - | 0 /* PRDR checked later */ - | 0 /* UTF8 checked later */ - | 0 /* DSN checked later */ - | 0 /* PIPE checked later */ - | 0 /* SIZE checked later */ + PEER_OFFERED_TLS /* others checked later */ ); /* Set tls_offered if the response to EHLO specifies support for STARTTLS. */ @@ -1790,8 +1765,10 @@ if ( tls_offered if (!smtp_read_response(&inblock, buffer2, sizeof(buffer2), '2', ob->command_timeout)) { - if (errno != 0 || buffer2[0] == 0 || - (buffer2[0] == '4' && !ob->tls_tempfail_tryclear)) + if ( errno != 0 + || buffer2[0] == 0 + || (buffer2[0] == '4' && !ob->tls_tempfail_tryclear) + ) { Ustrncpy(buffer, buffer2, sizeof(buffer)); goto RESPONSE_FAILED; @@ -1816,13 +1793,11 @@ if ( tls_offered if (rc != OK) { # ifdef EXPERIMENTAL_DANE - if (rc == DEFER && dane && !dane_required) + if (rc == DEFER && dane) { - log_write(0, LOG_MAIN, "DANE attempt failed;" - " trying CA-root TLS to %s [%s] (not in hosts_require_dane)", + log_write(0, LOG_MAIN, + "DANE attempt failed; no TLS connection to %s [%s]", host->name, host->address); - dane = FALSE; - goto TLS_NEGOTIATE; } # endif @@ -1908,17 +1883,17 @@ if (tls_out.active >= 0) /* If the host is required to use a secure channel, ensure that we have one. */ -else if ( +else if ( smtps # ifdef EXPERIMENTAL_DANE - dane || + || dane # endif - verify_check_given_host(&ob->hosts_require_tls, host) == OK + || verify_check_given_host(&ob->hosts_require_tls, host) == OK ) { save_errno = ERRNO_TLSREQUIRED; message = string_sprintf("a TLS session is required, but %s", - 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"); goto TLS_FAILED; } #endif /*SUPPORT_TLS*/ @@ -2118,13 +2093,11 @@ if (smtp_use_dsn && !dsn_all_lasthop) { if (dsn_ret == dsn_ret_hdrs) { - Ustrcpy(p, " RET=HDRS"); - while (*p) p++; + Ustrcpy(p, " RET=HDRS"); p += 9; } else if (dsn_ret == dsn_ret_full) { - Ustrcpy(p, " RET=FULL"); - while (*p) p++; + Ustrcpy(p, " RET=FULL"); p += 9; } if (dsn_envid != NULL) { @@ -2217,7 +2190,7 @@ the PENDING_DEFER status, because only one attempt is ever made, and we know that max_rcpt will be large, so all addresses will be done at once. */ for (addr = first_addr; - address_count < max_rcpt && addr != NULL; + addr && address_count < max_rcpt; addr = addr->next) { int count; @@ -2229,14 +2202,14 @@ for (addr = first_addr; if (addr->transport_return != PENDING_DEFER) continue; address_count++; - no_flush = smtp_use_pipelining && (!mua_wrapper || addr->next != NULL); + no_flush = smtp_use_pipelining && (!mua_wrapper || addr->next); /* Add any DSN flags to the rcpt command and add to the sent string */ p = buffer; *p = 0; - if (smtp_use_dsn && (addr->dsn_flags & rf_dsnlasthop) != 1) + if (smtp_use_dsn && !(addr->dsn_flags & rf_dsnlasthop)) { if ((addr->dsn_flags & rf_dsnflags) != 0) { @@ -2254,7 +2227,7 @@ for (addr = first_addr; } } - if (addr->dsn_orcpt != NULL) + if (addr->dsn_orcpt) { string_format(p, sizeof(buffer) - (p-buffer), " ORCPT=%s", addr->dsn_orcpt); @@ -2366,7 +2339,9 @@ for handling the SMTP dot-handling protocol, flagging to apply to headers as well as body. Set the appropriate timeout value to be used for each chunk. (Haven't been able to make it work using select() for writing yet.) */ -if (!ok) ok = TRUE; else +if (!ok) + ok = TRUE; +else { sigalrm_seen = FALSE; transport_write_timeout = ob->data_timeout; @@ -2383,12 +2358,10 @@ if (!ok) ok = TRUE; else (tblock->return_path_add? topt_add_return_path : 0) | (tblock->delivery_date_add? topt_add_delivery_date : 0) | (tblock->envelope_to_add? topt_add_envelope_to : 0), - 0, /* No size limit */ tblock->add_headers, tblock->remove_headers, US".", US"..", /* Escaping strings */ tblock->rewrite_rules, tblock->rewrite_existflags, - ob->dkim_private_key, ob->dkim_domain, ob->dkim_selector, - ob->dkim_canon, ob->dkim_strict, ob->dkim_sign_headers + &ob->dkim ); #else ok = transport_write_message(addrlist, inblock.sock, @@ -3917,7 +3890,7 @@ If queue_smtp is set, or this transport was called to send a subsequent message down an existing TCP/IP connection, and something caused the host not to be found, we end up here, but can detect these cases and handle them specially. */ -for (addr = addrlist; addr != NULL; addr = addr->next) +for (addr = addrlist; addr; addr = addr->next) { /* If host is not NULL, it means that we stopped processing the host list because of hosts_max_try or hosts_max_try_hardlimit. In the former case, this @@ -3926,8 +3899,7 @@ for (addr = addrlist; addr != NULL; addr = addr->next) However, if we have hit hosts_max_try_hardlimit, we want to behave as if all hosts were tried. */ - if (host != NULL) - { + if (host) if (total_hosts_tried >= ob->hosts_max_try_hardlimit) { DEBUG(D_transport) @@ -3940,7 +3912,6 @@ for (addr = addrlist; addr != NULL; addr = addr->next) debug_printf("hosts_max_try limit caused some hosts to be skipped\n"); setflag(addr, af_retry_skipped); } - } if (queue_smtp) /* no deliveries attempted */ { @@ -3949,28 +3920,28 @@ for (addr = addrlist; addr != NULL; addr = addr->next) addr->message = US"SMTP delivery explicitly queued"; } - else if (addr->transport_return == DEFER && - (addr->basic_errno == ERRNO_UNKNOWNERROR || addr->basic_errno == 0) && - addr->message == NULL) + else if ( addr->transport_return == DEFER + && (addr->basic_errno == ERRNO_UNKNOWNERROR || addr->basic_errno == 0) + && !addr->message + ) { addr->basic_errno = ERRNO_HRETRY; - if (continue_hostname != NULL) - { + if (continue_hostname) addr->message = US"no host found for existing SMTP connection"; - } else if (expired) { setflag(addr, af_pass_message); /* This is not a security risk */ - addr->message = ob->delay_after_cutoff - ? US"retry time not reached for any host after a long failure period" - : US"all hosts have been failing for a long time and were last tried " - "after this message arrived"; + addr->message = string_sprintf( + "all hosts%s have been failing for a long time %s", + addr->domain ? string_sprintf(" for '%s'", addr->domain) : US"", + ob->delay_after_cutoff + ? US"(and retry time not reached)" + : US"and were last tried after this message arrived"); /* If we are already using fallback hosts, or there are no fallback hosts defined, convert the result to FAIL to cause a bounce. */ - if (addr->host_list == addr->fallback_hosts || - addr->fallback_hosts == NULL) + if (addr->host_list == addr->fallback_hosts || !addr->fallback_hosts) addr->transport_return = FAIL; } else