{ "tls_verify_certificates", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_verify_certificates) },
{ "tls_verify_hosts", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, tls_verify_hosts) }
+ (void *)offsetof(smtp_transport_options_block, tls_verify_hosts) },
+#endif
+#ifdef SUPPORT_I18N
+ { "utf8_downconvert", opt_stringptr,
+ (void *)offsetof(smtp_transport_options_block, utf8_downconvert) },
#endif
};
/* Size of the options list. An extern variable has to be used so that its
address can appear in the tables drtables.c. */
-int smtp_transport_options_count =
- sizeof(smtp_transport_options)/sizeof(optionlist);
+int smtp_transport_options_count = nelem(smtp_transport_options);
#ifdef MACRO_PREDEF
.tls_try_verify_hosts = US"*",
.tls_verify_cert_hostnames = US"*",
#endif
+#ifdef SUPPORT_I18N
+ .utf8_downconvert = NULL,
+#endif
#ifndef DISABLE_DKIM
.dkim =
{.dkim_domain = NULL,
-static uschar
-ehlo_response(uschar * buf, uschar checks)
+static unsigned
+ehlo_response(uschar * buf, unsigned checks)
{
size_t bsize = Ustrlen(buf);
if ( checks & OPTION_TLS
&& pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
checks &= ~OPTION_TLS;
+
+# ifdef EXPERIMENTAL_REQUIRETLS
+if ( checks & OPTION_REQUIRETLS
+ && pcre_exec(regex_REQUIRETLS, NULL, CS buf,bsize, 0, PCRE_EOPT, NULL,0) < 0)
+ checks &= ~OPTION_REQUIRETLS;
+# endif
#endif
if ( checks & OPTION_IGNQ
sx->dsn_all_lasthop = TRUE;
#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
sx->dane = FALSE;
-sx->dane_required = verify_check_given_host(&sx->ob->hosts_require_dane, sx->host) == OK;
+sx->dane_required =
+ verify_check_given_host(&sx->ob->hosts_require_dane, sx->host) == OK;
#endif
if ((sx->max_rcpt = sx->tblock->max_addresses) == 0) sx->max_rcpt = 999999;
else if ( sx->smtps
# ifdef SUPPORT_DANE
|| sx->dane
+# endif
+# ifdef EXPERIMENTAL_REQUIRETLS
+ || tls_requiretls & REQUIRETLS_MSG
# endif
|| verify_check_given_host(&sx->ob->hosts_require_tls, sx->host) == OK
)
{
- errno = ERRNO_TLSREQUIRED;
+ errno =
+# ifdef EXPERIMENTAL_REQUIRETLS
+ tls_requiretls & REQUIRETLS_MSG ? ERRNO_REQUIRETLS :
+# endif
+ ERRNO_TLSREQUIRED;
message = string_sprintf("a TLS session is required, but %s",
smtp_peer_options & OPTION_TLS
? "an attempt to start TLS failed" : "the server did not offer TLS support");
| OPTION_DSN
| OPTION_PIPE
| (sx->ob->size_addition >= 0 ? OPTION_SIZE : 0)
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+ | (tls_requiretls & REQUIRETLS_MSG ? OPTION_REQUIRETLS : 0)
+#endif
);
/* Set for IGNOREQUOTA if the response to LHLO specifies support and the
DEBUG(D_transport) debug_printf("%susing DSN\n",
sx->peer_offered & OPTION_DSN ? "" : "not ");
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+ if (sx->peer_offered & OPTION_REQUIRETLS)
+ {
+ smtp_peer_options |= OPTION_REQUIRETLS;
+ DEBUG(D_transport) debug_printf(
+ tls_requiretls & REQUIRETLS_MSG
+ ? "using REQUIRETLS\n" : "REQUIRETLS offered\n");
+ }
+#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
#ifdef SUPPORT_I18N
if (sx->addrlist->prop.utf8_msg)
{
+ uschar * s;
+
+ /* If the transport sets a downconversion mode it overrides any set by ACL
+ for the message. */
+
+ if ((s = sx->ob->utf8_downconvert))
+ {
+ if (!(s = expand_string(s)))
+ {
+ message = string_sprintf("failed to expand utf8_downconvert: %s",
+ expand_string_message);
+ set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
+ yield = DEFER;
+ goto SEND_QUIT;
+ }
+ switch (*s)
+ {
+ case '1': sx->addrlist->prop.utf8_downcvt = TRUE;
+ sx->addrlist->prop.utf8_downcvt_maybe = FALSE;
+ break;
+ case '0': sx->addrlist->prop.utf8_downcvt = FALSE;
+ sx->addrlist->prop.utf8_downcvt_maybe = FALSE;
+ break;
+ case '-': if (s[1] == '1')
+ {
+ sx->addrlist->prop.utf8_downcvt = FALSE;
+ sx->addrlist->prop.utf8_downcvt_maybe = TRUE;
+ }
+ break;
+ }
+ }
+
sx->utf8_needed = !sx->addrlist->prop.utf8_downcvt
&& !sx->addrlist->prop.utf8_downcvt_maybe;
DEBUG(D_transport) if (!sx->utf8_needed)
errno = ERRNO_UTF8_FWD;
goto RESPONSE_FAILED;
}
+#endif /*SUPPORT_I18N*/
+
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+ /*XXX should tls_requiretls actually be per-addr? */
+
+if ( tls_requiretls & REQUIRETLS_MSG
+ && !(sx->peer_offered & OPTION_REQUIRETLS)
+ )
+ {
+ sx->setting_up = TRUE;
+ errno = ERRNO_REQUIRETLS;
+ message = US"REQUIRETLS support is required from the server"
+ " but it was not offered";
+ DEBUG(D_transport) debug_printf("%s\n", message);
+ goto TLS_FAILED;
+ }
#endif
return OK;
message = NULL;
sx->send_quit = check_response(sx->host, &errno, sx->addrlist->more_errno,
sx->buffer, &code, &message, &pass_message);
+ yield = DEFER;
goto FAILED;
SEND_FAILED:
message = US string_sprintf("send() to %s [%s] failed: %s",
sx->host->name, sx->host->address, strerror(errno));
sx->send_quit = FALSE;
+ yield = DEFER;
goto FAILED;
EHLOHELO_FAILED:
message = string_sprintf("Remote host closed connection in response to %s"
" (EHLO response was: %s)", smtp_command, sx->buffer);
sx->send_quit = FALSE;
+ yield = DEFER;
goto FAILED;
/* This label is jumped to directly when a TLS negotiation has failed,
#ifdef SUPPORT_TLS
TLS_FAILED:
- code = '4';
+# ifdef EXPERIMENTAL_REQUIRETLS
+ if (errno == ERRNO_REQUIRETLS)
+ code = '5', yield = FAIL;
+ /*XXX DSN will be labelled 500; prefer 530 5.7.4 */
+ else
+# endif
+ code = '4', yield = DEFER;
goto FAILED;
#endif
, sx->smtp_greeting, sx->helo_response
#endif
);
- yield = DEFER;
}
Ustrcpy(p, " SMTPUTF8"), p += 9;
#endif
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+if (tls_requiretls & REQUIRETLS_MSG)
+ Ustrcpy(p, " REQUIRETLS") , p += 11;
+#endif
+
/* check if all addresses have DSN-lasthop flag; do not send RET and ENVID if so */
for (sx->dsn_all_lasthop = TRUE, addr = addrlist, address_count = 0;
addr && address_count < sx->max_rcpt;
a host list with hosts_override set, use the host list supplied with the
transport. It is an error for this not to exist. */
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+if (tls_requiretls & REQUIRETLS_MSG)
+ ob->tls_tempfail_tryclear = FALSE; /*XXX surely we should have a local for this
+ rather than modifying the transport? */
+#endif
+
if (!hostlist || (ob->hosts_override && ob->hosts))
{
if (!ob->hosts)