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;
ENV_MAIL_OPT_RET, ENV_MAIL_OPT_ENVID,
#ifdef SUPPORT_I18N
ENV_MAIL_OPT_UTF8,
+#endif
+#ifdef EXPERIMENTAL_REQUIRETLS
+ ENV_MAIL_OPT_REQTLS,
#endif
};
typedef struct {
{ US"ENVID", ENV_MAIL_OPT_ENVID, TRUE },
#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 },
struct timeval tzero;
#ifdef SUPPORT_TLS
-if (tls_in.active >= 0)
+if (tls_in.active.sock >= 0)
return !tls_could_read();
#endif
+void
+smtp_command_timeout_exit(void)
+{
+log_write(L_lost_incoming_connection,
+ LOG_MAIN, "SMTP command timeout on%s connection from %s",
+ tls_in.active.sock >= 0 ? " TLS" : "", host_and_ident(FALSE));
+if (smtp_batched_input)
+ moan_smtp_batch(NULL, "421 SMTP command timeout"); /* Does not return */
+smtp_notquit_exit(US"command-timeout", US"421",
+ US"%s: SMTP command timeout - closing connection",
+ smtp_active_hostname);
+exim_exit(EXIT_FAILURE, US"receiving");
+}
+
+void
+smtp_command_sigterm_exit(void)
+{
+log_write(0, LOG_MAIN, "%s closed after SIGTERM", smtp_get_connection_info());
+if (smtp_batched_input)
+ moan_smtp_batch(NULL, "421 SIGTERM received"); /* Does not return */
+smtp_notquit_exit(US"signal-exit", US"421",
+ US"%s: Service not available - closing connection", smtp_active_hostname);
+exim_exit(EXIT_FAILURE, US"receiving");
+}
+
+void
+smtp_data_timeout_exit(void)
+{
+log_write(L_lost_incoming_connection,
+ LOG_MAIN, "SMTP data timeout (message abandoned) on connection from %s F=<%s>",
+ sender_fullhost ? sender_fullhost : US"local process", sender_address);
+receive_bomb_out(US"data-timeout", US"SMTP incoming data timeout");
+/* Does not return */
+}
+
+void
+smtp_data_sigint_exit(void)
+{
+log_write(0, LOG_MAIN, "%s closed after %s",
+ smtp_get_connection_info(), had_data_sigint == SIGTERM ? "SIGTERM":"SIGINT");
+receive_bomb_out(US"signal-exit",
+ US"Service not available - SIGTERM or SIGINT received");
+/* Does not return */
+}
+
+
+
/* Refill the buffer, and notify DKIM verification code.
Return false for error or EOF.
*/
rc = read(fileno(smtp_in), smtp_inbuffer, MIN(IN_BUFFER_SIZE-1, lim));
save_errno = errno;
-alarm(0);
+if (smtp_receive_timeout > 0) alarm(0);
if (rc <= 0)
{
/* Must put the error text in fixed store, because this might be during
header reading, where it releases unused store above the header. */
if (rc < 0)
{
+ if (had_command_timeout) /* set by signal handler */
+ smtp_command_timeout_exit(); /* does not return */
+ if (had_command_sigterm)
+ smtp_command_sigterm_exit();
+ if (had_data_timeout)
+ smtp_data_timeout_exit();
+ if (had_data_sigint)
+ smtp_data_sigint_exit();
+
smtp_had_error = save_errno;
smtp_read_error = string_copy_malloc(
string_sprintf(" (error: %s)", strerror(save_errno)));
}
- else smtp_had_eof = 1;
+ else
+ smtp_had_eof = 1;
return FALSE;
}
#ifndef DISABLE_DKIM
for(;;)
{
#ifndef DISABLE_DKIM
- BOOL dkim_save;
+ unsigned dkim_save;
#endif
if (chunking_data_left > 0)
receive_ungetc = lwr_receive_ungetc;
#ifndef DISABLE_DKIM
dkim_save = dkim_collect_input;
- dkim_collect_input = FALSE;
+ dkim_collect_input = 0;
#endif
/* Unless PIPELINING was offered, there should be no next command
/* Now write the string */
#ifdef SUPPORT_TLS
-if (tls_in.active >= 0)
+if (tls_in.active.sock >= 0)
{
- if (tls_write(TRUE, big_buffer, Ustrlen(big_buffer), more) < 0)
+ if (tls_write(NULL, big_buffer, Ustrlen(big_buffer), more) < 0)
smtp_write_error = -1;
}
else
int
smtp_fflush(void)
{
-if (tls_in.active < 0 && fflush(smtp_out) != 0) smtp_write_error = -1;
+if (tls_in.active.sock < 0 && fflush(smtp_out) != 0) smtp_write_error = -1;
return smtp_write_error;
}
static void
command_timeout_handler(int sig)
{
-sig = sig; /* Keep picky compilers happy */
-log_write(L_lost_incoming_connection,
- LOG_MAIN, "SMTP command timeout on%s connection from %s",
- (tls_in.active >= 0)? " TLS" : "",
- host_and_ident(FALSE));
-if (smtp_batched_input)
- moan_smtp_batch(NULL, "421 SMTP command timeout"); /* Does not return */
-smtp_notquit_exit(US"command-timeout", US"421",
- US"%s: SMTP command timeout - closing connection", smtp_active_hostname);
-exim_exit(EXIT_FAILURE, US"receiving");
+had_command_timeout = sig;
}
static void
command_sigterm_handler(int sig)
{
-sig = sig; /* Keep picky compilers happy */
-log_write(0, LOG_MAIN, "%s closed after SIGTERM", smtp_get_connection_info());
-if (smtp_batched_input)
- moan_smtp_batch(NULL, "421 SIGTERM received"); /* Does not return */
-smtp_notquit_exit(US"signal-exit", US"421",
- US"%s: Service not available - closing connection", smtp_active_hostname);
-exim_exit(EXIT_FAILURE, US"receiving");
+had_command_sigterm = sig;
}
smtp_cmd_list *p;
BOOL hadnull = FALSE;
+had_command_timeout = 0;
os_non_restarting_signal(SIGALRM, command_timeout_handler);
while ((c = (receive_getc)(buffer_lim)) != '\n' && c != EOF)
bmi_verdicts = NULL;
#endif
dnslist_domain = dnslist_matched = NULL;
+#ifdef SUPPORT_SPF
+spf_header_comment = spf_received = spf_result = spf_smtp_comment = NULL;
+spf_result_guessed = FALSE;
+#endif
#ifndef DISABLE_DKIM
dkim_cur_signer = dkim_signers =
-dkim_signing_domain = dkim_signing_selector = NULL;
+dkim_signing_domain = dkim_signing_selector = dkim_signatures = NULL;
dkim_cur_signer = dkim_signers = dkim_signing_domain = dkim_signing_selector = NULL;
-dkim_disable_verify = dkim_collect_input = FALSE;
+dkim_disable_verify = FALSE;
+dkim_collect_input = 0;
dkim_verify_overall = dkim_verify_status = dkim_verify_reason = NULL;
dkim_key_length = 0;
dkim_verify_signers = US"$dkim_signers";
#endif
-dsn_ret = 0;
-dsn_envid = NULL;
-deliver_host = deliver_host_address = NULL; /* Can be set by ACL */
-#ifndef DISABLE_PRDR
-prdr_requested = FALSE;
-#endif
-#ifdef SUPPORT_SPF
-spf_header_comment = spf_received = spf_result = spf_smtp_comment = NULL;
-spf_result_guessed = FALSE;
-#endif
#ifdef EXPERIMENTAL_DMARC
dmarc_has_been_checked = dmarc_disable_verify = dmarc_enable_forensic = FALSE;
-dmarc_domain_policy = dmarc_forensic_sender =
-dmarc_history_file = dmarc_status = dmarc_status_text =
+dmarc_domain_policy = dmarc_status = dmarc_status_text =
dmarc_used_domain = NULL;
#endif
#ifdef EXPERIMENTAL_ARC
arc_state = arc_state_reason = NULL;
#endif
+dsn_ret = 0;
+dsn_envid = NULL;
+deliver_host = deliver_host_address = NULL; /* Can be set by ACL */
+#ifndef DISABLE_PRDR
+prdr_requested = FALSE;
+#endif
#ifdef SUPPORT_I18N
message_smtputf8 = FALSE;
#endif
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
received_protocol =
(sender_host_address ? protocols : protocols_local)
- [pextend + pauthed + (tls_in.active >= 0 ? pcrpted:0)];
+ [pextend + pauthed + (tls_in.active.sock >= 0 ? pcrpted:0)];
*s = *ss = US"235 Authentication succeeded";
authenticated_by = au;
break;
smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname);
#ifdef SUPPORT_TLS
-tls_close(TRUE, TLS_SHUTDOWN_NOWAIT);
+tls_close(NULL, TLS_SHUTDOWN_NOWAIT);
#endif
log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT",
/* Set the local signal handler for SIGTERM - it tries to end off tidily */
+had_command_sigterm = 0;
os_non_restarting_signal(SIGTERM, command_sigterm_handler);
/* Batched SMTP is handled in a different function. */
#ifdef AUTH_TLS
/* Check once per STARTTLS or SSL-on-connect for a TLS AUTH */
- if ( tls_in.active >= 0
+ if ( tls_in.active.sock >= 0
&& tls_in.peercert
&& tls_in.certificate_verified
&& cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd
host_build_sender_fullhost(); /* Rebuild */
set_process_info("handling%s incoming connection from %s",
- (tls_in.active >= 0)? " TLS" : "", host_and_ident(FALSE));
+ (tls_in.active.sock >= 0)? " TLS" : "", host_and_ident(FALSE));
/* Verify if configured. This doesn't give much security, but it does
make some people happy to be able to do it. If helo_required is set,
pipelining_advertised = FALSE;
#ifdef SUPPORT_TLS
tls_advertised = FALSE;
+# ifdef EXPERIMENTAL_REQUIRETLS
+ requiretls_advertised = FALSE;
+# endif
#endif
dsn_advertised = FALSE;
#ifdef SUPPORT_I18N
secure connection. */
#ifdef SUPPORT_TLS
- if (tls_in.active < 0 &&
+ if (tls_in.active.sock < 0 &&
verify_check_host(&tls_advertise_hosts) != FAIL)
{
g = string_catn(g, smtp_code, 3);
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
has been seen. */
#ifdef SUPPORT_TLS
- if (tls_in.active >= 0) (void)tls_write(TRUE, g->s, g->ptr, FALSE); else
+ if (tls_in.active.sock >= 0) (void)tls_write(NULL, g->s, g->ptr, FALSE); else
#endif
{
[ (esmtp
? pextend + (sender_host_authenticated ? pauthed : 0)
: pnormal)
- + (tls_in.active >= 0 ? pcrpted : 0)
+ + (tls_in.active.sock >= 0 ? pcrpted : 0)
];
cancel_cutthrough_connection(TRUE, US"sent EHLO response");
smtp_reset(reset_point);
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");
/* 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;
}
/* 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;
}
if (dsn_advertised)
{
/* Check if the dsn envid has been already set */
- if (dsn_envid != NULL)
+ 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;
}
#ifdef SUPPORT_I18N
case ENV_MAIL_OPT_UTF8:
- if (smtputf8_advertised)
+ if (!smtputf8_advertised)
{
- int old_pool = store_pool;
+ done = synprot_error(L_smtp_syntax_error, 501, NULL,
+ US"SMTPUTF8 used when not advertised");
+ goto COMMAND_LOOP;
+ }
- DEBUG(D_receive) debug_printf("smtputf8 requested\n");
- message_smtputf8 = allow_utf8_domains = TRUE;
+ DEBUG(D_receive) debug_printf("smtputf8 requested\n");
+ message_smtputf8 = allow_utf8_domains = TRUE;
+ if (Ustrncmp(received_protocol, US"utf8", 4) != 0)
+ {
+ int old_pool = store_pool;
store_pool = POOL_PERM;
received_protocol = string_sprintf("utf8%s", received_protocol);
store_pool = old_pool;
}
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.
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. */
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;
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
/* 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;
}
/* 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;
}
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;
}
ACL may have delayed. To handle cutthrough delivery enforce a dummy call
to get the DATA command sent. */
- if (acl_smtp_predata == NULL && cutthrough.fd < 0)
+ if (acl_smtp_predata == NULL && cutthrough.cctx.sock < 0)
rc = OK;
else
{
{
DEBUG(D_any)
debug_printf("Non-empty input buffer after STARTTLS; naive attack?\n");
- if (tls_in.active < 0)
+ if (tls_in.active.sock < 0)
smtp_inend = smtp_inptr = smtp_inbuffer;
/* and if TLS is already active, tls_server_start() should fail */
}
[ (esmtp
? pextend + (sender_host_authenticated ? pauthed : 0)
: pnormal)
- + (tls_in.active >= 0 ? pcrpted : 0)
+ + (tls_in.active.sock >= 0 ? pcrpted : 0)
];
sender_host_auth_pubname = sender_host_authenticated = NULL;
smtp_printf("554 Security failure\r\n", FALSE);
break;
}
- tls_close(TRUE, TLS_SHUTDOWN_NOWAIT);
+ tls_close(NULL, TLS_SHUTDOWN_NOWAIT);
break;
#endif
buffer[0] = 0;
Ustrcat(buffer, " AUTH");
#ifdef SUPPORT_TLS
- if (tls_in.active < 0 &&
+ if (tls_in.active.sock < 0 &&
verify_check_host(&tls_advertise_hosts) != FAIL)
Ustrcat(buffer, " STARTTLS");
#endif