X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/eea58ada63dde3265728daccc037d3a376b25f45..32d07012f108f56aaf8f03468fa7ef2e8635325c:/src/src/transports/smtp.c diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 9e0ab1556..33c91a67a 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -69,7 +69,7 @@ optionlist smtp_transport_options[] = { (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; a later release will warn, and a later-still release will remove + 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) }, @@ -102,10 +102,14 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, hosts_override) }, { "hosts_randomize", opt_bool, (void *)offsetof(smtp_transport_options_block, hosts_randomize) }, +#if defined(SUPPORT_TLS) && !defined(DISABLE_OCSP) + { "hosts_request_ocsp", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, hosts_request_ocsp) }, +#endif { "hosts_require_auth", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_require_auth) }, #ifdef SUPPORT_TLS -# if defined EXPERIMENTAL_OCSP +# ifndef DISABLE_OCSP { "hosts_require_ocsp", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_require_ocsp) }, # endif @@ -114,7 +118,7 @@ optionlist smtp_transport_options[] = { #endif { "hosts_try_auth", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_try_auth) }, -#ifdef EXPERIMENTAL_PRDR +#ifndef DISABLE_PRDR { "hosts_try_prdr", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_try_prdr) }, #endif @@ -159,6 +163,10 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, tls_tempfail_tryclear) }, { "tls_try_verify_hosts", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_try_verify_hosts) }, +#ifdef EXPERIMENTAL_CERTNAMES + { "tls_verify_cert_hostnames", opt_stringptr, + (void *)offsetof(smtp_transport_options_block,tls_verify_cert_hostnames)}, +#endif { "tls_verify_certificates", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_verify_certificates) }, { "tls_verify_hosts", opt_stringptr, @@ -192,10 +200,11 @@ smtp_transport_options_block smtp_transport_option_defaults = { NULL, /* serialize_hosts */ NULL, /* hosts_try_auth */ NULL, /* hosts_require_auth */ -#ifdef EXPERIMENTAL_PRDR +#ifndef DISABLE_PRDR NULL, /* hosts_try_prdr */ #endif -#ifdef EXPERIMENTAL_OCSP +#ifndef DISABLE_OCSP + US"*", /* hosts_request_ocsp */ NULL, /* hosts_require_ocsp */ #endif NULL, /* hosts_require_tls */ @@ -240,6 +249,9 @@ smtp_transport_options_block smtp_transport_option_defaults = { TRUE, /* tls_tempfail_tryclear */ NULL, /* tls_verify_hosts */ NULL /* tls_try_verify_hosts */ +# ifdef EXPERIMENTAL_CERTNAMES + ,NULL /* tls_verify_cert_hostnames */ +# endif #endif #ifndef DISABLE_DKIM ,NULL, /* dkim_canon */ @@ -254,6 +266,16 @@ smtp_transport_options_block smtp_transport_option_defaults = { #endif }; +#ifdef EXPERIMENTAL_DSN +/* some DSN flags for use later */ + +static int rf_list[] = {rf_notify_never, rf_notify_success, + rf_notify_failure, rf_notify_delay }; + +static uschar *rf_names[] = { "NEVER", "SUCCESS", "FAILURE", "DELAY" }; +#endif + + /* Local statics */ @@ -378,6 +400,15 @@ 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 } @@ -631,7 +662,7 @@ tpda_defer_errstr = addr->message ? string_sprintf("%s: %s", addr->message, strerror(addr->basic_errno)) : string_copy(addr->message) : addr->basic_errno > 0 - ? string_copy(strerror(addr->basic_errno)) + ? string_copy(US strerror(addr->basic_errno)) : NULL; DEBUG(D_transport) @@ -1180,10 +1211,13 @@ BOOL completed_address = FALSE; BOOL esmtp = TRUE; BOOL pending_MAIL; BOOL pass_message = FALSE; -#ifdef EXPERIMENTAL_PRDR +#ifndef DISABLE_PRDR BOOL prdr_offered = FALSE; BOOL prdr_active; #endif +#ifdef EXPERIMENTAL_DSN +BOOL dsn_all_lasthop = TRUE; +#endif smtp_inblock inblock; smtp_outblock outblock; int max_rcpt = tblock->max_addresses; @@ -1221,10 +1255,13 @@ outblock.authenticating = FALSE; tls_out.bits = 0; tls_out.cipher = NULL; /* the one we may use for this transport */ +tls_out.ourcert = NULL; +tls_out.peercert = NULL; tls_out.peerdn = NULL; #if defined(SUPPORT_TLS) && !defined(USE_GNUTLS) tls_out.sni = NULL; #endif +tls_out.ocsp = OCSP_NOT_REQ; /* Flip the legacy TLS-related variables over to the outbound set in case they're used in the context of the transport. Don't bother resetting @@ -1235,8 +1272,8 @@ tls_modify_variables(&tls_out); #ifndef SUPPORT_TLS if (smtps) { - set_errno(addrlist, 0, US"TLS support not available", DEFER, FALSE); - return ERROR; + set_errno(addrlist, 0, US"TLS support not available", DEFER, FALSE); + return ERROR; } #endif @@ -1378,7 +1415,7 @@ goto SEND_QUIT; PCRE_EOPT, NULL, 0) >= 0; #endif - #ifdef EXPERIMENTAL_PRDR + #ifndef DISABLE_PRDR prdr_offered = esmtp && (pcre_exec(regex_PRDR, NULL, CS buffer, Ustrlen(buffer), 0, PCRE_EOPT, NULL, 0) >= 0) && @@ -1444,22 +1481,7 @@ if (tls_offered && !suppress_tls && else TLS_NEGOTIATE: { - int rc = tls_client_start(inblock.sock, - host, - addrlist, - 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, -#endif - ob->tls_dh_min_bits, - ob->command_timeout, - ob->tls_verify_hosts, - ob->tls_try_verify_hosts); + int rc = tls_client_start(inblock.sock, host, addrlist, ob); /* TLS negotiation failed; give an error. From outside, this function may be called again to try in clear on a new connection, if the options permit @@ -1480,7 +1502,10 @@ if (tls_offered && !suppress_tls && if (addr->transport_return == PENDING_DEFER) { addr->cipher = tls_out.cipher; + addr->ourcert = tls_out.ourcert; + addr->peercert = tls_out.peercert; addr->peerdn = tls_out.peerdn; + addr->ocsp = tls_out.ocsp; } } } @@ -1589,7 +1614,7 @@ if (continue_hostname == NULL DEBUG(D_transport) debug_printf("%susing PIPELINING\n", smtp_use_pipelining? "" : "not "); -#ifdef EXPERIMENTAL_PRDR +#ifndef DISABLE_PRDR prdr_offered = esmtp && pcre_exec(regex_PRDR, NULL, CS buffer, Ustrlen(CS buffer), 0, PCRE_EOPT, NULL, 0) >= 0 && @@ -1600,6 +1625,13 @@ if (continue_hostname == NULL {DEBUG(D_transport) debug_printf("PRDR usable\n");} #endif +#ifdef EXPERIMENTAL_DSN + /* Note if the server supports DSN */ + smtp_use_dsn = esmtp && pcre_exec(regex_DSN, NULL, CS buffer, (int)Ustrlen(CS buffer), 0, + PCRE_EOPT, NULL, 0) >= 0; + DEBUG(D_transport) debug_printf("use_dsn=%d\n", smtp_use_dsn); +#endif + /* Note if the response to EHLO specifies support for the AUTH extension. If it has, check that this host is one we want to authenticate to, and do the business. The host name and address must be available when the @@ -1677,7 +1709,7 @@ if (smtp_use_size) while (*p) p++; } -#ifdef EXPERIMENTAL_PRDR +#ifndef DISABLE_PRDR prdr_active = FALSE; if (prdr_offered) { @@ -1697,6 +1729,38 @@ if (prdr_offered) prdr_is_active: #endif +#ifdef EXPERIMENTAL_DSN +/* check if all addresses have lasthop flag */ +/* do not send RET and ENVID if true */ +dsn_all_lasthop = TRUE; +for (addr = first_addr; + address_count < max_rcpt && addr != NULL; + addr = addr->next) + if ((addr->dsn_flags & rf_dsnlasthop) != 1) + dsn_all_lasthop = FALSE; + +/* Add any DSN flags to the mail command */ + +if ((smtp_use_dsn) && (dsn_all_lasthop == FALSE)) + { + if (dsn_ret == dsn_ret_hdrs) + { + strcpy(p, " RET=HDRS"); + while (*p) p++; + } + else if (dsn_ret == dsn_ret_full) + { + strcpy(p, " RET=FULL"); + while (*p) p++; + } + if (dsn_envid != NULL) + { + string_format(p, sizeof(buffer) - (p-buffer), " ENVID=%s", dsn_envid); + while (*p) p++; + } + } +#endif + /* If an authenticated_sender override has been specified for this transport instance, expand it. If the expansion is forced to fail, and there was already an authenticated_sender for this message, the original value will be used. @@ -1759,18 +1823,66 @@ for (addr = first_addr; int count; BOOL no_flush; + #ifdef EXPERIMENTAL_DSN + if(smtp_use_dsn) + addr->dsn_aware = dsn_support_yes; + else + addr->dsn_aware = dsn_support_no; + #endif + if (addr->transport_return != PENDING_DEFER) continue; address_count++; no_flush = smtp_use_pipelining && (!mua_wrapper || addr->next != NULL); + #ifdef EXPERIMENTAL_DSN + /* 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 ((addr->dsn_flags & rf_dsnflags) != 0) + { + int i; + BOOL first = TRUE; + strcpy(p, " NOTIFY="); + while (*p) p++; + for (i = 0; i < 4; i++) + { + if ((addr->dsn_flags & rf_list[i]) != 0) + { + if (!first) *p++ = ','; + first = FALSE; + strcpy(p, rf_names[i]); + while (*p) p++; + } + } + } + + if (addr->dsn_orcpt != NULL) { + string_format(p, sizeof(buffer) - (p-buffer), " ORCPT=%s", + addr->dsn_orcpt); + while (*p) p++; + } + } + #endif + + /* Now send the RCPT command, and process outstanding responses when necessary. After a timeout on RCPT, we just end the function, leaving the yield as OK, because this error can often mean that there is a problem with just one address, so we don't want to delay the host. */ + #ifdef EXPERIMENTAL_DSN + count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>%s%s\r\n", + transport_rcpt_address(addr, tblock->rcpt_include_affixes), igquotstr, buffer); + #else count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>%s\r\n", transport_rcpt_address(addr, tblock->rcpt_include_affixes), igquotstr); + #endif + if (count < 0) goto SEND_FAILED; if (count > 0) { @@ -1913,7 +2025,7 @@ if (!ok) ok = TRUE; else smtp_command = US"end of data"; -#ifdef EXPERIMENTAL_PRDR +#ifndef DISABLE_PRDR /* For PRDR we optionally get a partial-responses warning * followed by the individual responses, before going on with * the overall response. If we don't get the warning then deal @@ -2008,7 +2120,7 @@ if (!ok) ok = TRUE; else address. For temporary errors, add a retry item for the address so that it doesn't get tried again too soon. */ -#ifdef EXPERIMENTAL_PRDR +#ifndef DISABLE_PRDR if (lmtp || prdr_active) #else if (lmtp) @@ -2019,7 +2131,7 @@ if (!ok) ok = TRUE; else { if (errno != 0 || buffer[0] == 0) goto RESPONSE_FAILED; addr->message = string_sprintf( -#ifdef EXPERIMENTAL_PRDR +#ifndef DISABLE_PRDR "%s error after %s: %s", prdr_active ? "PRDR":"LMTP", #else "LMTP error after %s: %s", @@ -2033,7 +2145,7 @@ if (!ok) ok = TRUE; else errno = ERRNO_DATA4XX; addr->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8; addr->transport_return = DEFER; -#ifdef EXPERIMENTAL_PRDR +#ifndef DISABLE_PRDR if (!prdr_active) #endif retry_add_item(addr, addr->address_retry_key, 0); @@ -2056,12 +2168,12 @@ if (!ok) ok = TRUE; else addr->host_used = thost; addr->special_action = flag; addr->message = conf; -#ifdef EXPERIMENTAL_PRDR +#ifndef DISABLE_PRDR if (prdr_active) addr->flags |= af_prdr_used; #endif flag = '-'; -#ifdef EXPERIMENTAL_PRDR +#ifndef DISABLE_PRDR if (!prdr_active) #endif { @@ -2083,7 +2195,7 @@ if (!ok) ok = TRUE; else } } -#ifdef EXPERIMENTAL_PRDR +#ifndef DISABLE_PRDR if (prdr_active) { /* PRDR - get the final, overall response. For any non-success @@ -2417,8 +2529,6 @@ tls_close(FALSE, TRUE); #endif /* Close the socket, and return the appropriate value, first setting -continue_transport and continue_hostname NULL to prevent any other addresses -that may include the host from trying to re-use a continuation socket. This works because the NULL setting is passed back to the calling process, and remote_max_parallel is forced to 1 when delivering over an existing connection, @@ -2519,7 +2629,10 @@ for (addr = addrlist; addr != NULL; addr = addr->next) addr->message = NULL; #ifdef SUPPORT_TLS addr->cipher = NULL; + addr->ourcert = NULL; + addr->peercert = NULL; addr->peerdn = NULL; + addr->ocsp = OCSP_NOT_REQ; #endif } return first_addr; @@ -2922,6 +3035,9 @@ for (cutoff_retry = 0; expired && deliver_host = host->name; deliver_host_address = host->address; + lookup_dnssec_authenticated = host->dnssec == DS_YES ? US"yes" + : host->dnssec == DS_NO ? US"no" + : US""; /* Set up a string for adding to the retry key if the port number is not the standard SMTP port. A host may have its own port setting that overrides