06f80e
commit 72c678024d5f7b97bae8c20cc3fb2e0299778d5b
06f80e
Author: Tomas Korbar <tkorbar@redhat.com>
06f80e
Date:   Mon Sep 7 12:41:05 2020 +0200
06f80e
06f80e
    Backport confTLS_FALLBACK_TO_CLEAR Configuration option
06f80e
06f80e
diff --git a/cf/README b/cf/README
06f80e
index 91e69a9..e8941ad 100644
06f80e
--- a/cf/README
06f80e
+++ b/cf/README
06f80e
@@ -4011,6 +4011,10 @@ confUSERDB_SPEC		UserDatabaseSpec
06f80e
 confFALLBACK_MX		FallbackMXhost	[undefined] Fallback MX host.
06f80e
 confFALLBACK_SMARTHOST	FallbackSmartHost
06f80e
 					[undefined] Fallback smart host.
06f80e
+confTLS_FALLBACK_TO_CLEAR	TLSFallbacktoClear
06f80e
+					[undefined] If set, immediately try
06f80e
+					a connection again without STARTTLS
06f80e
+					after a TLS handshake failure.
06f80e
 confTRY_NULL_MX_LIST	TryNullMXList	[False] If this host is the best MX
06f80e
 					for a host and other arrangements
06f80e
 					haven't been made, try connecting
06f80e
diff --git a/cf/m4/proto.m4 b/cf/m4/proto.m4
06f80e
index 0df3416..a741d97 100644
06f80e
--- a/cf/m4/proto.m4
06f80e
+++ b/cf/m4/proto.m4
06f80e
@@ -656,6 +656,8 @@ _OPTION(CipherList, `confCIPHER_LIST', `')
06f80e
 _OPTION(ServerSSLOptions, `confSERVER_SSL_OPTIONS', `')
06f80e
 # client side SSL options
06f80e
 _OPTION(ClientSSLOptions, `confCLIENT_SSL_OPTIONS', `')
06f80e
+# TLS: fall back to clear text after handshake failure?
06f80e
+_OPTION(TLSFallbacktoClear, `confTLS_FALLBACK_TO_CLEAR', `')
06f80e
 
06f80e
 # Input mail filters
06f80e
 _OPTION(InputMailFilters, `confINPUT_MAIL_FILTERS', `')
06f80e
@@ -2856,6 +2858,7 @@ R<$-:$+><VERIFY $*> <$*> FAIL	$#error $@ $2 $: $1 " authentication failed"
06f80e
 R<$-:$+><VERIFY $*> <$*> NO	$#error $@ $2 $: $1 " not authenticated"
06f80e
 R<$-:$+><VERIFY $*> <$*> NOT	$#error $@ $2 $: $1 " no authentication requested"
06f80e
 R<$-:$+><VERIFY $*> <$*> NONE	$#error $@ $2 $: $1 " other side does not support STARTTLS"
06f80e
+R<$-:$+><VERIFY $*> <$*> CLEAR	$#error $@ $2 $: $1 " STARTTLS disabled locally"
06f80e
 dnl some other value for ${verify}
06f80e
 R<$-:$+><VERIFY $*> <$*> $+	$#error $@ $2 $: $1 " authentication failure " $4
06f80e
 dnl some level of encryption required: get the maximum level (case 2.)
06f80e
diff --git a/doc/op/op.me b/doc/op/op.me
06f80e
index 57e25cd..97d3b9c 100644
06f80e
--- a/doc/op/op.me
06f80e
+++ b/doc/op/op.me
06f80e
@@ -8340,6 +8340,22 @@ PostMilter is useful only when
06f80e
 .i sendmail
06f80e
 is running as an SMTP server; in all other situations it
06f80e
 acts the same as True.
06f80e
+.ip TLSFallbacktoClear
06f80e
+[no short name]
06f80e
+If set,
06f80e
+.i sendmail
06f80e
+immediately tries an outbound connection again without STARTTLS
06f80e
+after a TLS handshake failure.
06f80e
+Note:
06f80e
+this applies to all connections even if TLS specific requirements are set
06f80e
+(see rulesets
06f80e
+.i tls_rcpt
06f80e
+and
06f80e
+.i tls_client
06f80e
+).
06f80e
+Hence such requirements will cause an error on a retry without STARTTLS.
06f80e
+Therefore they should only trigger a temporary failure so the connection
06f80e
+is later on tried again.
06f80e
 .ip TLSSrvOptions
06f80e
 [no short name]
06f80e
 List of options for SMTP STARTTLS for the server
06f80e
diff --git a/sendmail/deliver.c b/sendmail/deliver.c
06f80e
index 8027a50..af42e8f 100644
06f80e
--- a/sendmail/deliver.c
06f80e
+++ b/sendmail/deliver.c
06f80e
@@ -1334,6 +1334,10 @@ deliver(e, firstto)
06f80e
 	char *pv[MAXPV + 1];
06f80e
 	char buf[MAXNAME + 1];
06f80e
 	char cbuf[MAXPATHLEN];
06f80e
+#if STARTTLS
06f80e
+	/* 0: try TLS, 1: try without TLS again, >1: don't try again */
06f80e
+	int tlsstate;
06f80e
+#endif
06f80e
 
06f80e
 	errno = 0;
06f80e
 	SM_REQUIRE(firstto != NULL);	/* same as to */
06f80e
@@ -1349,7 +1353,9 @@ deliver(e, firstto)
06f80e
 	e->e_statmsg = NULL;
06f80e
 	SmtpError[0] = '\0';
06f80e
 	xstart = curtime();
06f80e
-
06f80e
+#if STARTTLS
06f80e
+	tlsstate = 0;
06f80e
+#endif
06f80e
 	if (tTd(10, 1))
06f80e
 		sm_dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n",
06f80e
 			e->e_id, m->m_name, host, to->q_user);
06f80e
@@ -2073,6 +2079,9 @@ tryhost:
06f80e
 			hostnum++;
06f80e
 			if (endp != NULL)
06f80e
 				*endp = sep;
06f80e
+#if STARTTLS
06f80e
+			tlsstate = 0;
06f80e
+#endif
06f80e
 
06f80e
   one_last_try:
06f80e
 			/* see if we already know that this host is fried */
06f80e
@@ -2960,6 +2969,8 @@ reconnect:	/* after switching to an encrypted connection */
06f80e
 			usetls = bitset(MCIF_TLS, mci->mci_flags);
06f80e
 			if (usetls)
06f80e
 				usetls = !iscltflgset(e, D_NOTLS);
06f80e
+			if (usetls)
06f80e
+				usetls = tlsstate == 0;
06f80e
 
06f80e
 			host = macvalue(macid("{server_name}"), e);
06f80e
 			if (usetls)
06f80e
@@ -3025,8 +3036,11 @@ reconnect:	/* after switching to an encrypted connection */
06f80e
 				}
06f80e
 			}
06f80e
 			else
06f80e
+			{
06f80e
+				p = tlsstate == 0 ? "NONE": "CLEAR";
06f80e
 				macdefine(&e->e_macro, A_PERM,
06f80e
-					  macid("{verify}"), "NONE");
06f80e
+					  macid("{verify}"), p);
06f80e
+			}
06f80e
 			olderrors = Errors;
06f80e
 			QuickAbort = false;
06f80e
 			SuprErrs = true;
06f80e
@@ -3077,6 +3091,10 @@ reconnect:	/* after switching to an encrypted connection */
06f80e
 					}
06f80e
 					mci->mci_flags &= ~MCIF_TLSACT;
06f80e
 					(void) endmailer(mci, e, pv);
06f80e
+					if (TLSFallbacktoClear)
06f80e
+					{
06f80e
+						++tlsstate;
06f80e
+					}
06f80e
 				}
06f80e
 				else
06f80e
 				{
06f80e
@@ -3119,6 +3137,27 @@ reconnect:	/* after switching to an encrypted connection */
06f80e
 				mci_clr_extensions(mci);
06f80e
 				goto reconnect;
06f80e
 			}
06f80e
+			if (tlsstate == 1)
06f80e
+			{
06f80e
+				if (tTd(11, 1))
06f80e
+				{
06f80e
+					sm_syslog(LOG_DEBUG, NOQID,
06f80e
+						"STARTTLS=client, relay=%.100s, tlsstate=%d, status=trying_again",
06f80e
+						mci->mci_host, tlsstate);
06f80e
+					mci_dump(NULL, mci, true);
06f80e
+				}
06f80e
+				++tlsstate;
06f80e
+				/*
06f80e
+				**  Fake the status so a new connection is
06f80e
+				**  tried, otherwise the TLS error will
06f80e
+				**  "persist" during this delivery attempt.
06f80e
+				*/
06f80e
+
06f80e
+				mci->mci_errno = 0;
06f80e
+				rcode = EX_OK;
06f80e
+				mci_setstat(mci, rcode, NULL, NULL);
06f80e
+				goto one_last_try;
06f80e
+			}
06f80e
 		}
06f80e
 # endif /* STARTTLS */
06f80e
 # if SASL
06f80e
diff --git a/sendmail/readcf.c b/sendmail/readcf.c
06f80e
index 86892f5..82660f4 100644
06f80e
--- a/sendmail/readcf.c
06f80e
+++ b/sendmail/readcf.c
06f80e
@@ -2911,7 +2911,10 @@ static struct optioninfo
06f80e
 #endif
06f80e
 #define O_USECOMPRESSEDIPV6ADDRESSES 0xec
06f80e
 	{ "UseCompressedIPv6Addresses",	O_USECOMPRESSEDIPV6ADDRESSES, OI_NONE },
06f80e
-
06f80e
+#if STARTTLS
06f80e
+# define O_TLSFB2CLEAR		0xef
06f80e
+	{ "TLSFallbacktoClear",	O_TLSFB2CLEAR,	OI_NONE	},
06f80e
+#endif
06f80e
 	{ NULL,				'\0',		OI_NONE	}
06f80e
 };
06f80e
 
06f80e
@@ -4305,6 +4308,9 @@ setoption(opt, val, safe, sticky, e)
06f80e
 #endif /* SASL */
06f80e
 
06f80e
 #if STARTTLS
06f80e
+	  case O_TLSFB2CLEAR:
06f80e
+		TLSFallbacktoClear = atobool(val);
06f80e
+		break;
06f80e
 	  case O_SRVCERTFILE:
06f80e
 		SET_STRING_EXP(SrvCertFile);
06f80e
 	  case O_SRVKEYFILE:
06f80e
diff --git a/sendmail/sendmail.h b/sendmail/sendmail.h
06f80e
index 441399c..9be1e76 100644
06f80e
--- a/sendmail/sendmail.h
06f80e
+++ b/sendmail/sendmail.h
06f80e
@@ -2032,6 +2032,7 @@ EXTERN char	*CRLPath;	/* path to CRLs (dir. with hashes) */
06f80e
 #endif /* _FFR_CRLPATH */
06f80e
 EXTERN unsigned long	TLS_Srv_Opts;	/* TLS server options */
06f80e
 EXTERN unsigned long	Srv_SSL_Options, Clt_SSL_Options; /* SSL options */
06f80e
+EXTERN bool	TLSFallbacktoClear;
06f80e
 #endif /* STARTTLS */
06f80e
 
06f80e
 /*