summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2018-07-27 17:56:39 +0100
committerJeremy Harris <jgh146exb@wizmail.org>2018-07-27 17:56:39 +0100
commit8ac90765750f87c573300b9e953af3d8090cab8b (patch)
tree47278e6cdac764dc65ebcf14bea7ec1781a1fb21 /src
parent8d3dc2397dd769bf4654b0678be8d2acf0956ddd (diff)
Support REQUIRETLS
Diffstat (limited to 'src')
-rw-r--r--src/src/EDITME5
-rw-r--r--src/src/acl.c20
-rw-r--r--src/src/child.c21
-rw-r--r--src/src/config.h.defaults1
-rw-r--r--src/src/deliver.c5
-rw-r--r--src/src/exim.c15
-rw-r--r--src/src/expand.c3
-rw-r--r--src/src/globals.c5
-rw-r--r--src/src/globals.h5
-rw-r--r--src/src/host.c2
-rw-r--r--src/src/macro_predef.c3
-rw-r--r--src/src/macros.h8
-rw-r--r--src/src/readconf.c3
-rw-r--r--src/src/routers/dnslookup.c4
-rw-r--r--src/src/smtp_in.c86
-rw-r--r--src/src/spool_in.c39
-rw-r--r--src/src/spool_out.c6
-rw-r--r--src/src/tls-openssl.c2
-rw-r--r--src/src/transports/smtp.c74
-rw-r--r--src/src/transports/smtp.h4
-rw-r--r--src/src/verify.c14
21 files changed, 280 insertions, 45 deletions
diff --git a/src/src/EDITME b/src/src/EDITME
index bd5151db1..cbb080545 100644
--- a/src/src/EDITME
+++ b/src/src/EDITME
@@ -511,6 +511,11 @@ DISABLE_MAL_MKS=yes
# Uncomment the following line to add queuefile transport support
# EXPERIMENTAL_QUEUEFILE=yes
+# Uncomment the following to add REQUIRETLS support.
+# You must also have SUPPORT_TLS enabled.
+# Ref: https://datatracker.ietf.org/doc/draft-fenton-smtp-require-tls
+# EXPERIMENTAL_REQUIRETLS=yes
+
###############################################################################
# THESE ARE THINGS YOU MIGHT WANT TO SPECIFY #
###############################################################################
diff --git a/src/src/acl.c b/src/src/acl.c
index 1fa5c1f63..4f335df36 100644
--- a/src/src/acl.c
+++ b/src/src/acl.c
@@ -367,6 +367,9 @@ enum {
CONTROL_NO_PIPELINING,
CONTROL_QUEUE_ONLY,
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+ CONTROL_REQUIRETLS,
+#endif
CONTROL_SUBMISSION,
CONTROL_SUPPRESS_LOCAL_FIXUPS,
#ifdef SUPPORT_I18N
@@ -510,6 +513,18 @@ static control_def controls_list[] = {
// ACL_BIT_PRDR| /* Not allow one user to freeze for all */
ACL_BIT_NOTSMTP | ACL_BIT_MIME)
},
+
+
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+[CONTROL_REQUIRETLS] =
+ { US"requiretls", FALSE,
+ (unsigned)
+ ~(ACL_BIT_MAIL | ACL_BIT_RCPT | ACL_BIT_PREDATA |
+ ACL_BIT_DATA | ACL_BIT_MIME |
+ ACL_BIT_NOTSMTP)
+ },
+#endif
+
[CONTROL_SUBMISSION] =
{ US"submission", TRUE,
(unsigned)
@@ -3163,6 +3178,11 @@ for (; cb; cb = cb->next)
cancel_cutthrough_connection(TRUE, US"queueing forced");
break;
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+ case CONTROL_REQUIRETLS:
+ tls_requiretls |= REQUIRETLS_MSG;
+ break;
+#endif
case CONTROL_SUBMISSION:
originator_name = US"";
submission_mode = TRUE;
diff --git a/src/src/child.c b/src/src/child.c
index de12c44b5..ad625aeca 100644
--- a/src/src/child.c
+++ b/src/src/child.c
@@ -10,6 +10,10 @@
static void (*oldsignal)(int);
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+static uschar tls_requiretls_copy = 0;
+#endif
+
/*************************************************
* Ensure an fd has a given value *
@@ -73,8 +77,13 @@ child_exec_exim(int exec_type, BOOL kill_v, int *pcount, BOOL minimal,
int first_special = -1;
int n = 0;
int extra = pcount ? *pcount : 0;
-uschar **argv =
- store_get((extra + acount + MAX_CLMACROS + 18) * sizeof(char *));
+uschar **argv;
+
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+if (tls_requiretls) extra++;
+#endif
+
+argv = store_get((extra + acount + MAX_CLMACROS + 18) * sizeof(char *));
/* In all case, the list starts out with the path, any macros, and a changed
config file. */
@@ -120,6 +129,11 @@ if (!minimal)
}
}
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+if (tls_requiretls_copy & REQUIRETLS_MSG)
+ argv[n++] = US"-MS";
+#endif
+
/* Now add in any others that are in the call. Remember which they were,
for more helpful diagnosis on failure. */
@@ -229,6 +243,9 @@ occur. */
if (pid == 0)
{
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+ tls_requiretls_copy = tls_requiretls;
+#endif
force_fd(pfd[pipe_read], 0);
(void)close(pfd[pipe_write]);
if (debug_fd > 0) force_fd(debug_fd, 2);
diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults
index 0f348fa40..4fb30dd5f 100644
--- a/src/src/config.h.defaults
+++ b/src/src/config.h.defaults
@@ -198,6 +198,7 @@ Do not put spaces between # and the 'define'.
#define EXPERIMENTAL_DMARC
#define DMARC_TLD_FILE "/etc/exim/opendmarc.tlds"
#define EXPERIMENTAL_LMDB
+#define EXPERIMENTAL_REQUIRETLS
#define EXPERIMENTAL_QUEUEFILE
#define EXPERIMENTAL_SRS
diff --git a/src/src/deliver.c b/src/src/deliver.c
index 68152d505..0b9e55141 100644
--- a/src/src/deliver.c
+++ b/src/src/deliver.c
@@ -8454,6 +8454,11 @@ if (!regex_AUTH) regex_AUTH =
#ifdef SUPPORT_TLS
if (!regex_STARTTLS) regex_STARTTLS =
regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE);
+
+# ifdef EXPERIMENTAL_REQUIRETLS
+if (!regex_REQUIRETLS) regex_REQUIRETLS =
+ regex_must_compile(US"\\n250[\\s\\-]REQUIRETLS(\\s|\\n|$)", FALSE, TRUE);
+# endif
#endif
if (!regex_CHUNKING) regex_CHUNKING =
diff --git a/src/src/exim.c b/src/src/exim.c
index 2b4ecbc66..1410359bc 100644
--- a/src/src/exim.c
+++ b/src/src/exim.c
@@ -898,6 +898,9 @@ fprintf(f, "Support for:");
#ifdef EXPERIMENTAL_DSN_INFO
fprintf(f, " Experimental_DSN_info");
#endif
+#ifdef EXPERIMENTAL_REQUIRETLS
+ fprintf(f, " Experimental_REQUIRETLS");
+#endif
fprintf(f, "\n");
fprintf(f, "Lookups (built-in):");
@@ -2783,9 +2786,19 @@ for (i = 1; i < argc; i++)
default: badarg = TRUE; break;
}
- break;
+ break;
}
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+ /* -MS set REQUIRETLS on (new) message */
+
+ else if (*argrest == 'S')
+ {
+ tls_requiretls |= REQUIRETLS_MSG;
+ break;
+ }
+#endif
+
/* -M[x]: various operations on the following list of message ids:
-M deliver the messages, ignoring next retry times and thawing
-Mc deliver the messages, checking next retry times, no thawing
diff --git a/src/src/expand.c b/src/src/expand.c
index 2feaf957b..b6ff96aee 100644
--- a/src/src/expand.c
+++ b/src/src/expand.c
@@ -660,6 +660,9 @@ static var_entry var_table[] = {
{ "regex_match_string", vtype_stringptr, &regex_match_string },
#endif
{ "reply_address", vtype_reply, NULL },
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+ { "requiretls", vtype_bool, &tls_requiretls },
+#endif
{ "return_path", vtype_stringptr, &return_path },
{ "return_size_limit", vtype_int, &bounce_return_size_limit },
{ "router_name", vtype_stringptr, &router_name },
diff --git a/src/src/globals.c b/src/src/globals.c
index 3fa0e3e3f..10045f482 100644
--- a/src/src/globals.c
+++ b/src/src/globals.c
@@ -161,6 +161,11 @@ uschar *tls_ocsp_file = NULL;
uschar *tls_privatekey = NULL;
BOOL tls_remember_esmtp = FALSE;
uschar *tls_require_ciphers = NULL;
+# ifdef EXPERIMENTAL_REQUIRETLS
+uschar tls_requiretls = 0; /* REQUIRETLS_MSG etc. bit #defines */
+uschar *tls_advertise_requiretls = US"*";
+const pcre *regex_REQUIRETLS = NULL;
+# endif
uschar *tls_try_verify_hosts = NULL;
uschar *tls_verify_certificates= US"system";
uschar *tls_verify_hosts = NULL;
diff --git a/src/src/globals.h b/src/src/globals.h
index ef5b3a560..b28aa5e6e 100644
--- a/src/src/globals.h
+++ b/src/src/globals.h
@@ -120,6 +120,11 @@ extern uschar *tls_eccurve; /* EC curve */
extern uschar *tls_ocsp_file; /* OCSP stapling proof file */
# endif
extern uschar *tls_privatekey; /* Private key file */
+# ifdef EXPERIMENTAL_REQUIRETLS
+extern uschar tls_requiretls; /* REQUIRETLS active for this message */
+extern uschar *tls_advertise_requiretls; /* hosts for which REQUIRETLS adv */
+extern const pcre *regex_REQUIRETLS; /* for recognising the command */
+# endif
extern BOOL tls_remember_esmtp; /* For YAEB */
extern uschar *tls_require_ciphers; /* So some can be avoided */
extern uschar *tls_try_verify_hosts; /* Optional client verification */
diff --git a/src/src/host.c b/src/src/host.c
index d4267429b..711822177 100644
--- a/src/src/host.c
+++ b/src/src/host.c
@@ -2859,7 +2859,7 @@ for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
block. Otherwise, add a new block in the correct place; if it has to be
before the first block, copy the first block's data to a new second block. */
- if (last == NULL)
+ if (!last)
{
host->name = string_copy_dnsdomain(data);
host->address = NULL;
diff --git a/src/src/macro_predef.c b/src/src/macro_predef.c
index 32c05a807..1073e45a6 100644
--- a/src/src/macro_predef.c
+++ b/src/src/macro_predef.c
@@ -198,6 +198,9 @@ due to conflicts with other common macros. */
#ifdef EXPERIMENTAL_DSN_INFO
builtin_macro_create(US"_HAVE_DSN_INFO");
#endif
+#ifdef EXPERIMENTAL_REQUIRETLS
+ builtin_macro_create(US"_HAVE_REQTLS");
+#endif
#ifdef LOOKUP_LSEARCH
builtin_macro_create(US"_HAVE_LOOKUP_LSEARCH");
diff --git a/src/src/macros.h b/src/src/macros.h
index f22fe8c9a..e7550346c 100644
--- a/src/src/macros.h
+++ b/src/src/macros.h
@@ -550,6 +550,9 @@ table exim_errstrings[] in log.c */
#ifdef SUPPORT_I18N
# define ERRNO_UTF8_FWD (-49) /* target not supporting SMTPUTF8 */
#endif
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+# define ERRNO_REQUIRETLS (-50) /* REQUIRETLS session not started */
+#endif
/* These must be last, so all retry deferments can easily be identified */
@@ -1014,6 +1017,11 @@ enum { FILTER_UNSET, FILTER_FORWARD, FILTER_EXIM, FILTER_SIEVE };
#define OPTION_PIPE BIT(5)
#define OPTION_SIZE BIT(6)
#define OPTION_CHUNKING BIT(7)
+#define OPTION_REQUIRETLS BIT(8)
+
+/* Codes for tls_requiretls requests (usually by sender) */
+
+#define REQUIRETLS_MSG BIT(0) /* REQUIRETLS onward use */
/* Argument for *_getc */
diff --git a/src/src/readconf.c b/src/src/readconf.c
index 3f307fd5c..fbf6d6b49 100644
--- a/src/src/readconf.c
+++ b/src/src/readconf.c
@@ -348,6 +348,9 @@ static optionlist optionlist_config[] = {
{ "timezone", opt_stringptr, &timezone_string },
{ "tls_advertise_hosts", opt_stringptr, &tls_advertise_hosts },
#ifdef SUPPORT_TLS
+# ifdef EXPERIMENTAL_REQUIRETLS
+ { "tls_advertise_requiretls", opt_stringptr, &tls_advertise_requiretls },
+# endif
{ "tls_certificate", opt_stringptr, &tls_certificate },
{ "tls_crl", opt_stringptr, &tls_crl },
{ "tls_dh_max_bits", opt_int, &tls_dh_max_bits },
diff --git a/src/src/routers/dnslookup.c b/src/src/routers/dnslookup.c
index 6ab08d7ba..a3b0d352c 100644
--- a/src/src/routers/dnslookup.c
+++ b/src/src/routers/dnslookup.c
@@ -300,7 +300,9 @@ for (;;)
rc = host_find_bydns(&h, CUS rblock->ignore_target_hosts, flags,
srv_service, ob->srv_fail_domains, ob->mx_fail_domains,
- &rblock->dnssec, &fully_qualified_name, &removed);
+ &rblock->dnssec,
+ &fully_qualified_name, &removed);
+
if (removed) setflag(addr, af_local_host_removed);
/* If host found with only address records, test for the domain's being in
diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
index ff0f6acaa..d1c19ea0d 100644
--- a/src/src/smtp_in.c
+++ b/src/src/smtp_in.c
@@ -135,6 +135,9 @@ static auth_instance *authenticated_by;
static BOOL auth_advertised;
#ifdef SUPPORT_TLS
static BOOL tls_advertised;
+# ifdef EXPERIMENTAL_REQUIRETLS
+static BOOL requiretls_advertised;
+# endif
#endif
static BOOL dsn_advertised;
static BOOL esmtp;
@@ -256,6 +259,9 @@ enum {
#ifdef SUPPORT_I18N
ENV_MAIL_OPT_UTF8,
#endif
+#ifdef EXPERIMENTAL_REQUIRETLS
+ ENV_MAIL_OPT_REQTLS,
+#endif
};
typedef struct {
uschar * name; /* option requested during MAIL cmd */
@@ -275,6 +281,10 @@ static env_mail_type_t env_mail_type_list[] = {
#ifdef SUPPORT_I18N
{ US"SMTPUTF8",ENV_MAIL_OPT_UTF8, FALSE }, /* rfc6531 */
#endif
+#ifdef EXPERIMENTAL_REQUIRETLS
+ /* https://tools.ietf.org/html/draft-ietf-uta-smtp-require-tls-03 */
+ { US"REQUIRETLS",ENV_MAIL_OPT_REQTLS, FALSE },
+#endif
/* keep this the last entry */
{ US"NULL", ENV_MAIL_OPT_NULL, FALSE },
};
@@ -2437,6 +2447,9 @@ tls_in.ourcert = tls_in.peercert = NULL;
tls_in.sni = NULL;
tls_in.ocsp = OCSP_NOT_REQ;
tls_advertised = FALSE;
+# ifdef EXPERIMENTAL_REQUIRETLS
+requiretls_advertised = FALSE;
+# endif
#endif
dsn_advertised = FALSE;
#ifdef SUPPORT_I18N
@@ -4172,6 +4185,9 @@ while (done <= 0)
pipelining_advertised = FALSE;
#ifdef SUPPORT_TLS
tls_advertised = FALSE;
+# ifdef EXPERIMENTAL_REQUIRETLS
+ requiretls_advertised = FALSE;
+# endif
#endif
dsn_advertised = FALSE;
#ifdef SUPPORT_I18N
@@ -4371,6 +4387,17 @@ while (done <= 0)
g = string_catn(g, US"-STARTTLS\r\n", 11);
tls_advertised = TRUE;
}
+
+# ifdef EXPERIMENTAL_REQUIRETLS
+ /* Advertise REQUIRETLS only once we are in a secure connection */
+ if ( tls_in.active.sock >= 0
+ && verify_check_host(&tls_advertise_requiretls) != FAIL)
+ {
+ g = string_catn(g, smtp_code, 3);
+ g = string_catn(g, US"-REQUIRETLS\r\n", 13);
+ requiretls_advertised = TRUE;
+ }
+# endif
#endif
#ifndef DISABLE_PRDR
@@ -4453,14 +4480,14 @@ while (done <= 0)
break;
}
- if (sender_address != NULL)
+ if (sender_address)
{
done = synprot_error(L_smtp_protocol_error, 503, NULL,
US"sender already given");
break;
}
- if (smtp_cmd_data[0] == 0)
+ if (!*smtp_cmd_data)
{
done = synprot_error(L_smtp_protocol_error, 501, NULL,
US"MAIL must have an address operand");
@@ -4557,7 +4584,7 @@ while (done <= 0)
/* Check if RET has already been set */
if (dsn_ret > 0)
{
- synprot_error(L_smtp_syntax_error, 501, NULL,
+ done = synprot_error(L_smtp_syntax_error, 501, NULL,
US"RET can be specified once only");
goto COMMAND_LOOP;
}
@@ -4570,7 +4597,7 @@ while (done <= 0)
/* Check for invalid invalid value, and exit with error */
if (dsn_ret == 0)
{
- synprot_error(L_smtp_syntax_error, 501, NULL,
+ done = synprot_error(L_smtp_syntax_error, 501, NULL,
US"Value for RET is invalid");
goto COMMAND_LOOP;
}
@@ -4582,7 +4609,7 @@ while (done <= 0)
/* Check if the dsn envid has been already set */
if (dsn_envid)
{
- synprot_error(L_smtp_syntax_error, 501, NULL,
+ done = synprot_error(L_smtp_syntax_error, 501, NULL,
US"ENVID can be specified once only");
goto COMMAND_LOOP;
}
@@ -4671,7 +4698,7 @@ while (done <= 0)
case ENV_MAIL_OPT_UTF8:
if (!smtputf8_advertised)
{
- synprot_error(L_smtp_syntax_error, 501, NULL,
+ done = synprot_error(L_smtp_syntax_error, 501, NULL,
US"SMTPUTF8 used when not advertised");
goto COMMAND_LOOP;
}
@@ -4687,6 +4714,32 @@ while (done <= 0)
}
break;
#endif
+
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+ case ENV_MAIL_OPT_REQTLS:
+ {
+ const uschar * list = value;
+ int sep = ',';
+ const uschar * opt;
+ uschar * r, * t;
+
+ if (!requiretls_advertised)
+ {
+ done = synprot_error(L_smtp_syntax_error, 555, NULL,
+ US"unadvertised MAIL option: REQUIRETLS");
+ goto COMMAND_LOOP;
+ }
+
+ DEBUG(D_receive) debug_printf("requiretls requested\n");
+ tls_requiretls = REQUIRETLS_MSG;
+
+ r = string_copy_malloc(received_protocol);
+ if ((t = Ustrrchr(r, 's'))) *t = 'S';
+ received_protocol = r;
+ }
+ break;
+#endif
+
/* No valid option. Stick back the terminator characters and break
the loop. Do the name-terminator second as extract_option sets
value==name when it found no equal-sign.
@@ -4704,6 +4757,17 @@ while (done <= 0)
if (arg_error) break;
}
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+ if (tls_requiretls & REQUIRETLS_MSG)
+ {
+ /* Ensure headers-only bounces whether a RET option was given or not. */
+
+ DEBUG(D_receive) if (dsn_ret == dsn_ret_full)
+ debug_printf("requiretls override: dsn_ret_full -> dsn_ret_hdrs\n");
+ dsn_ret = dsn_ret_hdrs;
+ }
+#endif
+
/* If we have passed the threshold for rate limiting, apply the current
delay, and update it for next time, provided this is a limited host. */
@@ -4780,8 +4844,7 @@ while (done <= 0)
in which case just qualify the address. The flag is set above at the start
of the SMTP connection. */
- if (sender_domain == 0 && sender_address[0] != 0)
- {
+ if (!sender_domain && *sender_address)
if (allow_unqualified_sender)
{
sender_domain = Ustrlen(sender_address) + 1;
@@ -4802,7 +4865,6 @@ while (done <= 0)
sender_address = NULL;
break;
}
- }
/* Apply an ACL check if one is defined, before responding. Afterwards,
when pipelining is not advertised, do another sync check in case the ACL
@@ -4907,7 +4969,7 @@ while (done <= 0)
/* Check whether orcpt has been already set */
if (orcpt)
{
- synprot_error(L_smtp_syntax_error, 501, NULL,
+ done = synprot_error(L_smtp_syntax_error, 501, NULL,
US"ORCPT can be specified once only");
goto COMMAND_LOOP;
}
@@ -4920,7 +4982,7 @@ while (done <= 0)
/* Check if the notify flags have been already set */
if (flags > 0)
{
- synprot_error(L_smtp_syntax_error, 501, NULL,
+ done = synprot_error(L_smtp_syntax_error, 501, NULL,
US"NOTIFY can be specified once only");
goto COMMAND_LOOP;
}
@@ -4952,7 +5014,7 @@ while (done <= 0)
else
{
/* Catch any strange values */
- synprot_error(L_smtp_syntax_error, 501, NULL,
+ done = synprot_error(L_smtp_syntax_error, 501, NULL,
US"Invalid value for NOTIFY parameter");
goto COMMAND_LOOP;
}
diff --git a/src/src/spool_in.c b/src/src/spool_in.c
index cd74d1ee7..0b3490b22 100644
--- a/src/src/spool_in.c
+++ b/src/src/spool_in.c
@@ -287,6 +287,9 @@ tls_free_cert(&tls_in.peercert);
tls_in.peerdn = NULL;
tls_in.sni = NULL;
tls_in.ocsp = OCSP_NOT_REQ;
+# if defined(EXPERIMENTAL_REQUIRETLS) && !defined(COMPILE_UTILITY)
+tls_requiretls = 0;
+# endif
#endif
#ifdef WITH_CONTENT_SCAN
@@ -649,22 +652,30 @@ for (;;)
#ifdef SUPPORT_TLS
case 't':
- if (Ustrncmp(p, "ls_certificate_verified", 23) == 0)
- tls_in.certificate_verified = TRUE;
- else if (Ustrncmp(p, "ls_cipher", 9) == 0)
- tls_in.cipher = string_copy(big_buffer + 12);
+ if (Ustrncmp(p, "ls_", 3) == 0)
+ {
+ uschar * q = p + 3;
+ if (Ustrncmp(q, "certificate_verified", 20) == 0)
+ tls_in.certificate_verified = TRUE;
+ else if (Ustrncmp(q, "cipher", 6) == 0)
+ tls_in.cipher = string_copy(big_buffer + 12);
# ifndef COMPILE_UTILITY /* tls support fns not built in */
- else if (Ustrncmp(p, "ls_ourcert", 10) == 0)
- (void) tls_import_cert(big_buffer + 13, &tls_in.ourcert);
- else if (Ustrncmp(p, "ls_peercert", 11) == 0)
- (void) tls_import_cert(big_buffer + 14, &tls_in.peercert);
+ else if (Ustrncmp(q, "ourcert", 7) == 0)
+ (void) tls_import_cert(big_buffer + 13, &tls_in.ourcert);
+ else if (Ustrncmp(q, "peercert", 8) == 0)
+ (void) tls_import_cert(big_buffer + 14, &tls_in.peercert);
# endif
- else if (Ustrncmp(p, "ls_peerdn", 9) == 0)
- tls_in.peerdn = string_unprinting(string_copy(big_buffer + 12));
- else if (Ustrncmp(p, "ls_sni", 6) == 0)
- tls_in.sni = string_unprinting(string_copy(big_buffer + 9));
- else if (Ustrncmp(p, "ls_ocsp", 7) == 0)
- tls_in.ocsp = big_buffer[10] - '0';
+ else if (Ustrncmp(q, "peerdn", 6) == 0)
+ tls_in.peerdn = string_unprinting(string_copy(big_buffer + 12));
+ else if (Ustrncmp(q, "sni", 3) == 0)
+ tls_in.sni = string_unprinting(string_copy(big_buffer + 9));
+ else if (Ustrncmp(q, "ocsp", 4) == 0)
+ tls_in.ocsp = big_buffer[10] - '0';
+# if defined(EXPERIMENTAL_REQUIRETLS) && !defined(COMPILE_UTILITY)
+ else if (Ustrncmp(q, "requiretls", 10) == 0)
+ tls_requiretls = strtol(CS big_buffer+16, NULL, 0);
+# endif
+ }
break;
#endif
diff --git a/src/src/spool_out.c b/src/src/spool_out.c
index a6ab3754e..52f079c4e 100644
--- a/src/src/spool_out.c
+++ b/src/src/spool_out.c
@@ -252,6 +252,10 @@ if (tls_in.ourcert)
fprintf(f, "-tls_ourcert %s\n", CS big_buffer);
}
if (tls_in.ocsp) fprintf(f, "-tls_ocsp %d\n", tls_in.ocsp);
+
+# ifdef EXPERIMENTAL_REQUIRETLS
+if (tls_requiretls) fprintf(f, "-tls_requiretls 0x%x\n", tls_requiretls);
+# endif
#endif
#ifdef SUPPORT_I18N
@@ -267,7 +271,7 @@ if (message_smtputf8)
DEBUG(D_deliver) debug_printf("DSN: Write SPOOL :-dsn_envid %s\n", dsn_envid);
if (dsn_envid) fprintf(f, "-dsn_envid %s\n", dsn_envid);
DEBUG(D_deliver) debug_printf("DSN: Write SPOOL :-dsn_ret %d\n", dsn_ret);
-if (dsn_ret != 0) fprintf(f, "-dsn_ret %d\n", dsn_ret);
+if (dsn_ret) fprintf(f, "-dsn_ret %d\n", dsn_ret);
/* To complete the envelope, write out the tree of non-recipients, followed by
the list of recipients. These won't be disjoint the first time, when no
diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
index d8c8101cc..9e1fc910c 100644
--- a/src/src/tls-openssl.c
+++ b/src/src/tls-openssl.c
@@ -2156,7 +2156,7 @@ int rc;
if ( ( !ob->tls_verify_hosts
&& (!ob->tls_try_verify_hosts || !*ob->tls_try_verify_hosts)
)
- || (verify_check_given_host(&ob->tls_verify_hosts, host) == OK)
+ || verify_check_given_host(&ob->tls_verify_hosts, host) == OK
)
client_verify_optional = FALSE;
else if (verify_check_given_host(&ob->tls_try_verify_hosts, host) == OK)
diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
index ae4385a05..08d1810d6 100644
--- a/src/src/transports/smtp.c
+++ b/src/src/transports/smtp.c
@@ -1336,8 +1336,8 @@ return Ustrcmp(current_local_identity, message_local_identity) == 0;
-static uschar
-ehlo_response(uschar * buf, uschar checks)
+static unsigned
+ehlo_response(uschar * buf, unsigned checks)
{
size_t bsize = Ustrlen(buf);
@@ -1345,6 +1345,12 @@ 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
@@ -1533,7 +1539,8 @@ sx->utf8_needed = FALSE;
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;
@@ -2078,10 +2085,17 @@ 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");
@@ -2122,6 +2136,9 @@ if (continue_hostname == NULL
| 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
@@ -2166,6 +2183,16 @@ if (continue_hostname == NULL
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
@@ -2206,6 +2233,22 @@ if (sx->utf8_needed && !(sx->peer_offered & OPTION_UTF8))
}
#endif
+#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;
@@ -2216,6 +2259,7 @@ 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:
@@ -2223,6 +2267,7 @@ return OK;
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:
@@ -2230,6 +2275,7 @@ return OK;
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,
@@ -2239,7 +2285,13 @@ return OK;
#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
@@ -2272,7 +2324,6 @@ FAILED:
, sx->smtp_greeting, sx->helo_response
#endif
);
- yield = DEFER;
}
@@ -2378,6 +2429,11 @@ if ( sx->peer_offered & OPTION_UTF8
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;
@@ -3862,6 +3918,12 @@ same one in order to be passed to a single transport - or if the transport has
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)
diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h
index a33ef437f..34c49d930 100644
--- a/src/src/transports/smtp.h
+++ b/src/src/transports/smtp.h
@@ -133,8 +133,8 @@ typedef struct {
int max_rcpt;
int cmd_count;
- uschar peer_offered;
- uschar avoid_option;
+ unsigned peer_offered;
+ unsigned avoid_option;
uschar * igquotstr;
uschar * helo_data;
#ifdef EXPERIMENTAL_DSN_INFO
diff --git a/src/src/verify.c b/src/src/verify.c
index a892b058b..35b21a54d 100644
--- a/src/src/verify.c
+++ b/src/src/verify.c
@@ -172,7 +172,6 @@ else
if ( cache_record->result == ccache_reject
|| *from_address == 0 && cache_record->result == ccache_reject_mfnull)
{
- setflag(addr, af_verify_nsfail);
HDEBUG(D_verify)
debug_printf("callout cache: domain gave initial rejection, or "
"does not accept HELO or MAIL FROM:<>\n");
@@ -991,6 +990,13 @@ no_conn:
}
break;
#endif
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+ case ERRNO_REQUIRETLS:
+ addr->user_message = US"530 5.7.4 REQUIRETLS support required";
+ yield = FAIL;
+ done = TRUE;
+ break;
+#endif
case ECONNREFUSED:
sx.send_quit = FALSE;
break;
@@ -1901,16 +1907,16 @@ while (addr_new)
(void)host_find_byname(host, NULL, flags, NULL, TRUE);
else
{
- dnssec_domains * dnssec_domains = NULL;
+ const dnssec_domains * dsp = NULL;
if (Ustrcmp(tp->driver_name, "smtp") == 0)
{
smtp_transport_options_block * ob =
(smtp_transport_options_block *) tp->options_block;
- dnssec_domains = &ob->dnssec;
+ dsp = &ob->dnssec;
}
(void) host_find_bydns(host, NULL, flags, NULL, NULL, NULL,
- dnssec_domains, NULL, NULL);
+ dsp, NULL, NULL);
}
}
}