* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2017 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions for handling an incoming SMTP call. */
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;
{ "auth", sizeof("auth")-1, AUTH_CMD, TRUE, TRUE },
#ifdef SUPPORT_TLS
{ "starttls", sizeof("starttls")-1, STARTTLS_CMD, FALSE, FALSE },
- { "tls_auth", 0, TLS_AUTH_CMD, FALSE, TRUE },
+ { "tls_auth", 0, TLS_AUTH_CMD, FALSE, FALSE },
#endif
/* If you change anything above here, also fix the definitions below. */
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
if (rc < 0) return TRUE; /* End of file or error */
smtp_ungetc(rc);
-rc = smtp_inend - smtp_inptr;
-if (rc > 150) rc = 150;
-smtp_inptr[rc] = 0;
return FALSE;
}
static BOOL
check_sync(void)
{
-if (!smtp_enforce_sync || sender_host_address == NULL || sender_host_notsocket)
+if (!smtp_enforce_sync || !sender_host_address || sender_host_notsocket)
return TRUE;
return wouldblock_reading();
+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.
*/
fflush(smtp_out);
if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout);
-/* Limit amount read, so non-message data is not fed to DKIM */
+/* Limit amount read, so non-message data is not fed to DKIM.
+Take care to not touch the safety NUL at the end of the buffer. */
-rc = read(fileno(smtp_in), smtp_inbuffer, MIN(IN_BUFFER_SIZE, lim));
+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
}
receive_getc = bdat_getc;
- receive_getbuf = bdat_getbuf;
+ receive_getbuf = bdat_getbuf; /* r~getbuf is never actually used */
receive_ungetc = bdat_ungetc;
#ifndef DISABLE_DKIM
dkim_collect_input = dkim_save;
void
bdat_flush_data(void)
{
-unsigned n = chunking_data_left;
-(void) bdat_getbuf(&n);
+while (chunking_data_left)
+ {
+ unsigned n = chunking_data_left;
+ if (!bdat_getbuf(&n)) break;
+ }
receive_getc = lwr_receive_getc;
receive_getbuf = lwr_receive_getbuf;
{
log_write(0, LOG_MAIN|LOG_PANIC, "string too large in smtp_printf()");
smtp_closedown(US"Unexpected error");
- exim_exit(EXIT_FAILURE);
+ exim_exit(EXIT_FAILURE, NULL);
}
/* If this is the first output for a (non-batch) RCPT command, see if all RCPTs
/* 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);
+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);
+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)
/* Enforce synchronization for unknown commands */
-if (smtp_inptr < smtp_inend && /* Outstanding input */
- check_sync && /* Local flag set */
- smtp_enforce_sync && /* Global flag set */
- sender_host_address != NULL && /* Not local input */
- !sender_host_notsocket) /* Really is a socket */
+if ( smtp_inptr < smtp_inend /* Outstanding input */
+ && check_sync /* Local flag set */
+ && smtp_enforce_sync /* Global flag set */
+ && sender_host_address /* Not local input */
+ && !sender_host_notsocket) /* Really is a socket */
return BADSYN_CMD;
return OTHER_CMD;
void
smtp_closedown(uschar *message)
{
-if (smtp_in == NULL || smtp_batched_input) return;
+if (!smtp_in || smtp_batched_input) return;
receive_swallow_smtp();
smtp_printf("421 %s\r\n", FALSE, message);
for (;;) switch(smtp_read_command(FALSE, GETC_BUFFER_UNLIMITED))
{
case EOF_CMD:
- return;
+ return;
case QUIT_CMD:
- smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname);
- mac_smtp_fflush();
- return;
+ smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname);
+ mac_smtp_fflush();
+ return;
case RSET_CMD:
- smtp_printf("250 Reset OK\r\n", FALSE);
- break;
+ smtp_printf("250 Reset OK\r\n", FALSE);
+ break;
default:
- smtp_printf("421 %s\r\n", FALSE, message);
- break;
+ smtp_printf("421 %s\r\n", FALSE, message);
+ break;
}
}
if (smtp_mailcmd_count > 0 || !LOGGING(smtp_no_mail))
return;
-if (sender_host_authenticated != NULL)
+if (sender_host_authenticated)
{
g = string_append(g, 2, US" A=", sender_host_authenticated);
if (authenticated_id) g = string_append(g, 2, US":", authenticated_id);
/* Discard any previous helo name */
-if (sender_helo_name != NULL)
+if (sender_helo_name)
{
store_free(sender_helo_name);
sender_helo_name = NULL;
/* Skip tests if junk is permitted. */
if (!yield)
- {
+
/* Allow the new standard form for IPv6 address literals, namely,
[IPv6:....], and because someone is bound to use it, allow an equivalent
IPv4 form. Allow plain addresses as well. */
/* Non-literals must be alpha, dot, hyphen, plus any non-valid chars
that have been configured (usually underscore - sigh). */
- else if (*s != 0)
- {
- yield = TRUE;
- while (*s != 0)
- {
+ else if (*s)
+ for (yield = TRUE; *s; s++)
if (!isalnum(*s) && *s != '.' && *s != '-' &&
Ustrchr(helo_allow_chars, *s) == NULL)
{
yield = FALSE;
break;
}
- s++;
- }
- }
- }
/* Save argument if OK */
*************************************************/
/* This function is called whenever the SMTP session is reset from
-within either of the setup functions.
+within either of the setup functions; also from the daemon loop.
Argument: the stacking pool storage reset point
Returns: nothing
*/
-static void
+void
smtp_reset(void *reset_point)
{
recipients_list = NULL;
sending_ip_address = NULL;
return_path = sender_address = NULL;
sender_data = NULL; /* Can be set by ACL */
-deliver_localpart_orig = NULL;
-deliver_domain_orig = NULL;
+deliver_localpart_parent = deliver_localpart_orig = NULL;
+deliver_domain_parent = deliver_domain_orig = NULL;
callout_address = NULL;
submission_name = NULL; /* Can be set by ACL */
raw_sender = NULL; /* After SMTP rewrite, before qualifying */
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_signers = NULL;
+dkim_cur_signer = dkim_signers =
+dkim_signing_domain = dkim_signing_selector = dkim_signatures = NULL;
+dkim_cur_signer = dkim_signers = dkim_signing_domain = dkim_signing_selector = NULL;
dkim_disable_verify = FALSE;
-dkim_collect_input = 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
+#ifdef EXPERIMENTAL_DMARC
+dmarc_has_been_checked = dmarc_disable_verify = dmarc_enable_forensic = FALSE;
+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;
#ifndef DISABLE_PRDR
prdr_requested = FALSE;
#endif
-#ifdef EXPERIMENTAL_SPF
-spf_header_comment = NULL;
-spf_received = NULL;
-spf_result = NULL;
-spf_smtp_comment = NULL;
-#endif
#ifdef SUPPORT_I18N
message_smtputf8 = FALSE;
#endif
case HELO_CMD:
case EHLO_CMD:
- check_helo(smtp_cmd_data);
- /* Fall through */
+ check_helo(smtp_cmd_data);
+ /* Fall through */
case RSET_CMD:
- cancel_cutthrough_connection(TRUE, US"RSET received");
- smtp_reset(reset_point);
- bsmtp_transaction_linecount = receive_linecount;
- break;
+ cancel_cutthrough_connection(TRUE, US"RSET received");
+ smtp_reset(reset_point);
+ bsmtp_transaction_linecount = receive_linecount;
+ break;
/* The MAIL FROM command requires an address as an operand. All we
it is the canonical extracted address which is all that is kept. */
case MAIL_CMD:
- smtp_mailcmd_count++; /* Count for no-mail log */
- if (sender_address != NULL)
- /* The function moan_smtp_batch() does not return. */
- moan_smtp_batch(smtp_cmd_buffer, "503 Sender already given");
+ smtp_mailcmd_count++; /* Count for no-mail log */
+ if (sender_address != NULL)
+ /* The function moan_smtp_batch() does not return. */
+ moan_smtp_batch(smtp_cmd_buffer, "503 Sender already given");
- if (smtp_cmd_data[0] == 0)
- /* The function moan_smtp_batch() does not return. */
- moan_smtp_batch(smtp_cmd_buffer, "501 MAIL FROM must have an address operand");
+ if (smtp_cmd_data[0] == 0)
+ /* The function moan_smtp_batch() does not return. */
+ moan_smtp_batch(smtp_cmd_buffer, "501 MAIL FROM must have an address operand");
- /* Reset to start of message */
+ /* Reset to start of message */
- cancel_cutthrough_connection(TRUE, US"MAIL received");
- smtp_reset(reset_point);
+ cancel_cutthrough_connection(TRUE, US"MAIL received");
+ smtp_reset(reset_point);
- /* Apply SMTP rewrite */
+ /* Apply SMTP rewrite */
- raw_sender = ((rewrite_existflags & rewrite_smtp) != 0)?
- rewrite_one(smtp_cmd_data, rewrite_smtp|rewrite_smtp_sender, NULL, FALSE,
- US"", global_rewrite_rules) : smtp_cmd_data;
+ raw_sender = ((rewrite_existflags & rewrite_smtp) != 0)?
+ rewrite_one(smtp_cmd_data, rewrite_smtp|rewrite_smtp_sender, NULL, FALSE,
+ US"", global_rewrite_rules) : smtp_cmd_data;
- /* Extract the address; the TRUE flag allows <> as valid */
+ /* Extract the address; the TRUE flag allows <> as valid */
- raw_sender =
- parse_extract_address(raw_sender, &errmess, &start, &end, &sender_domain,
- TRUE);
+ raw_sender =
+ parse_extract_address(raw_sender, &errmess, &start, &end, &sender_domain,
+ TRUE);
- if (raw_sender == NULL)
- /* The function moan_smtp_batch() does not return. */
- moan_smtp_batch(smtp_cmd_buffer, "501 %s", errmess);
+ if (!raw_sender)
+ /* The function moan_smtp_batch() does not return. */
+ moan_smtp_batch(smtp_cmd_buffer, "501 %s", errmess);
- sender_address = string_copy(raw_sender);
+ sender_address = string_copy(raw_sender);
- /* Qualify unqualified sender addresses if permitted to do so. */
+ /* Qualify unqualified sender addresses if permitted to do so. */
- if (sender_domain == 0 && sender_address[0] != 0 && sender_address[0] != '@')
- {
- if (allow_unqualified_sender)
- {
- sender_address = rewrite_address_qualify(sender_address, FALSE);
- DEBUG(D_receive) debug_printf("unqualified address %s accepted "
- "and rewritten\n", raw_sender);
- }
- /* The function moan_smtp_batch() does not return. */
- else moan_smtp_batch(smtp_cmd_buffer, "501 sender address must contain "
- "a domain");
- }
- break;
+ if ( !sender_domain
+ && sender_address[0] != 0 && sender_address[0] != '@')
+ if (allow_unqualified_sender)
+ {
+ sender_address = rewrite_address_qualify(sender_address, FALSE);
+ DEBUG(D_receive) debug_printf("unqualified address %s accepted "
+ "and rewritten\n", raw_sender);
+ }
+ /* The function moan_smtp_batch() does not return. */
+ else
+ moan_smtp_batch(smtp_cmd_buffer, "501 sender address must contain "
+ "a domain");
+ break;
/* The RCPT TO command requires an address as an operand. All we do
extracted address. */
case RCPT_CMD:
- if (sender_address == NULL)
- /* The function moan_smtp_batch() does not return. */
- moan_smtp_batch(smtp_cmd_buffer, "503 No sender yet given");
+ if (!sender_address)
+ /* The function moan_smtp_batch() does not return. */
+ moan_smtp_batch(smtp_cmd_buffer, "503 No sender yet given");
- if (smtp_cmd_data[0] == 0)
- /* The function moan_smtp_batch() does not return. */
- moan_smtp_batch(smtp_cmd_buffer, "501 RCPT TO must have an address operand");
+ if (smtp_cmd_data[0] == 0)
+ /* The function moan_smtp_batch() does not return. */
+ moan_smtp_batch(smtp_cmd_buffer,
+ "501 RCPT TO must have an address operand");
- /* Check maximum number allowed */
+ /* Check maximum number allowed */
- if (recipients_max > 0 && recipients_count + 1 > recipients_max)
- /* The function moan_smtp_batch() does not return. */
- moan_smtp_batch(smtp_cmd_buffer, "%s too many recipients",
- recipients_max_reject? "552": "452");
+ if (recipients_max > 0 && recipients_count + 1 > recipients_max)
+ /* The function moan_smtp_batch() does not return. */
+ moan_smtp_batch(smtp_cmd_buffer, "%s too many recipients",
+ recipients_max_reject? "552": "452");
- /* Apply SMTP rewrite, then extract address. Don't allow "<>" as a
- recipient address */
+ /* Apply SMTP rewrite, then extract address. Don't allow "<>" as a
+ recipient address */
- recipient = rewrite_existflags & rewrite_smtp
- ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
- global_rewrite_rules)
- : smtp_cmd_data;
+ recipient = rewrite_existflags & rewrite_smtp
+ ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
+ global_rewrite_rules)
+ : smtp_cmd_data;
- recipient = parse_extract_address(recipient, &errmess, &start, &end,
- &recipient_domain, FALSE);
+ recipient = parse_extract_address(recipient, &errmess, &start, &end,
+ &recipient_domain, FALSE);
- if (!recipient)
- /* The function moan_smtp_batch() does not return. */
- moan_smtp_batch(smtp_cmd_buffer, "501 %s", errmess);
+ if (!recipient)
+ /* The function moan_smtp_batch() does not return. */
+ moan_smtp_batch(smtp_cmd_buffer, "501 %s", errmess);
- /* If the recipient address is unqualified, qualify it if permitted. Then
- add it to the list of recipients. */
+ /* If the recipient address is unqualified, qualify it if permitted. Then
+ add it to the list of recipients. */
- if (recipient_domain == 0)
- {
- if (allow_unqualified_recipient)
- {
- DEBUG(D_receive) debug_printf("unqualified address %s accepted\n",
- recipient);
- recipient = rewrite_address_qualify(recipient, TRUE);
- }
- /* The function moan_smtp_batch() does not return. */
- else moan_smtp_batch(smtp_cmd_buffer, "501 recipient address must contain "
- "a domain");
- }
- receive_add_recipient(recipient, -1);
- break;
+ if (!recipient_domain)
+ if (allow_unqualified_recipient)
+ {
+ DEBUG(D_receive) debug_printf("unqualified address %s accepted\n",
+ recipient);
+ recipient = rewrite_address_qualify(recipient, TRUE);
+ }
+ /* The function moan_smtp_batch() does not return. */
+ else
+ moan_smtp_batch(smtp_cmd_buffer,
+ "501 recipient address must contain a domain");
+
+ receive_add_recipient(recipient, -1);
+ break;
/* The DATA command is legal only if it follows successful MAIL FROM
command is encountered. */
case DATA_CMD:
- if (sender_address == NULL || recipients_count <= 0)
- {
- /* The function moan_smtp_batch() does not return. */
- if (sender_address == NULL)
- moan_smtp_batch(smtp_cmd_buffer,
- "503 MAIL FROM:<sender> command must precede DATA");
+ if (!sender_address || recipients_count <= 0)
+ /* The function moan_smtp_batch() does not return. */
+ if (!sender_address)
+ moan_smtp_batch(smtp_cmd_buffer,
+ "503 MAIL FROM:<sender> command must precede DATA");
+ else
+ moan_smtp_batch(smtp_cmd_buffer,
+ "503 RCPT TO:<recipient> must precede DATA");
else
- moan_smtp_batch(smtp_cmd_buffer,
- "503 RCPT TO:<recipient> must precede DATA");
- }
- else
- {
- done = 3; /* DATA successfully achieved */
- message_ended = END_NOTENDED; /* Indicate in middle of message */
- }
- break;
+ {
+ done = 3; /* DATA successfully achieved */
+ message_ended = END_NOTENDED; /* Indicate in middle of message */
+ }
+ break;
/* The VRFY, EXPN, HELP, ETRN, and NOOP commands are ignored. */
case HELP_CMD:
case NOOP_CMD:
case ETRN_CMD:
- bsmtp_transaction_linecount = receive_linecount;
- break;
+ bsmtp_transaction_linecount = receive_linecount;
+ break;
case EOF_CMD:
case QUIT_CMD:
- done = 2;
- break;
+ done = 2;
+ break;
case BADARG_CMD:
- /* The function moan_smtp_batch() does not return. */
- moan_smtp_batch(smtp_cmd_buffer, "501 Unexpected argument data");
- break;
+ /* The function moan_smtp_batch() does not return. */
+ moan_smtp_batch(smtp_cmd_buffer, "501 Unexpected argument data");
+ break;
case BADCHAR_CMD:
- /* The function moan_smtp_batch() does not return. */
- moan_smtp_batch(smtp_cmd_buffer, "501 Unexpected NULL in SMTP command");
- break;
+ /* The function moan_smtp_batch() does not return. */
+ moan_smtp_batch(smtp_cmd_buffer, "501 Unexpected NULL in SMTP command");
+ break;
default:
- /* The function moan_smtp_batch() does not return. */
- moan_smtp_batch(smtp_cmd_buffer, "500 Command unrecognized");
- break;
+ /* The function moan_smtp_batch() does not return. */
+ moan_smtp_batch(smtp_cmd_buffer, "500 Command unrecognized");
+ break;
}
}
/* If receiving by -bs from a trusted user, or testing with -bh, we allow
authentication settings from -oMaa to remain in force. */
-if (!host_checking && !sender_host_notsocket) sender_host_authenticated = NULL;
+if (!host_checking && !sender_host_notsocket)
+ sender_host_auth_pubname = sender_host_authenticated = NULL;
authenticated_by = NULL;
#ifdef SUPPORT_TLS
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
(sender_host_address ? protocols : protocols_local) [pnormal];
/* Set up the buffer for inputting using direct read() calls, and arrange to
-call the local functions instead of the standard C ones. */
+call the local functions instead of the standard C ones. Place a NUL at the
+end of the buffer to safety-stop C-string reads from it. */
if (!(smtp_inbuffer = US malloc(IN_BUFFER_SIZE)))
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "malloc() failed for SMTP input buffer");
+smtp_inbuffer[IN_BUFFER_SIZE-1] = '\0';
receive_getc = smtp_getc;
receive_getbuf = smtp_getbuf;
/* Set up the message size limit; this may be host-specific */
thismessage_size_limit = expand_string_integer(message_size_limit, TRUE);
-if (expand_string_message != NULL)
+if (expand_string_message)
{
if (thismessage_size_limit == -1)
log_write(0, LOG_MAIN|LOG_PANIC, "unable to expand message_size_limit: "
smtps port for use with older style SSL MTAs. */
#ifdef SUPPORT_TLS
- if (tls_in.on_connect && tls_server_start(tls_require_ciphers, &user_msg) != OK)
- return smtp_log_tls_fail(user_msg);
+ if (tls_in.on_connect)
+ {
+ if (tls_server_start(tls_require_ciphers, &user_msg) != OK)
+ return smtp_log_tls_fail(user_msg);
+ cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE;
+ }
#endif
/* Run the connect ACL if it exists */
int len;
uschar *linebreak = Ustrchr(p, '\n');
ss = string_catn(ss, code, 3);
- if (linebreak == NULL)
+ if (!linebreak)
{
len = Ustrlen(p);
ss = string_catn(ss, US" ", 1);
ss = string_catn(ss, p, len);
ss = string_catn(ss, US"\r\n", 2);
p += len;
- if (linebreak != NULL) p++;
+ if (linebreak) p++;
}
-while (*p != 0);
+while (*p);
/* Before we write the banner, check that there is no input pending, unless
this synchronisation check is disabled. */
/* This function is called when acl_check() fails. As well as calls from within
this module, it is called from receive.c for an ACL after DATA. It sorts out
-logging the incident, and sets up the error response. A message containing
+logging the incident, and sends the error response. A message containing
newlines is turned into a multiline SMTP response, but for logging, only the
first line is used.
HDEBUG(D_receive) debug_printf("getting IP address for %s\n",
sender_helo_name);
- rc = host_find_bydns(&h, NULL, HOST_FIND_BY_A,
+ rc = host_find_bydns(&h, NULL, HOST_FIND_BY_A | HOST_FIND_BY_AAAA,
NULL, NULL, NULL, &d, NULL, NULL);
if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL)
for (hh = &h; hh; hh = hh->next)
{
if (set_id) authenticated_id = string_copy_malloc(set_id);
sender_host_authenticated = au->name;
+ sender_host_auth_pubname = au->public_name;
authentication_failed = FALSE;
authenticated_fail_id = NULL; /* Impossible to already be set? */
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, TRUE);
+tls_close(NULL, TLS_SHUTDOWN_NOWAIT);
#endif
log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT",
cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE;
#ifdef SUPPORT_TLS
cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = TRUE;
-cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE;
#endif
/* 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
)
{
cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = FALSE;
- if ( acl_smtp_auth
- && (rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth,
- &user_msg, &log_msg)) != OK
- )
- {
- done = smtp_handle_acl_fail(ACL_WHERE_AUTH, rc, user_msg, log_msg);
- continue;
- }
for (au = auths; au; au = au->next)
if (strcmpic(US"tls", au->driver_name) == 0)
{
- smtp_cmd_data = NULL;
-
- if (smtp_in_auth(au, &s, &ss) == OK)
- { DEBUG(D_auth) debug_printf("tls auth succeeded\n"); }
+ if ( acl_smtp_auth
+ && (rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth,
+ &user_msg, &log_msg)) != OK
+ )
+ done = smtp_handle_acl_fail(ACL_WHERE_AUTH, rc, user_msg, log_msg);
else
- { DEBUG(D_auth) debug_printf("tls auth not succeeded\n"); }
+ {
+ smtp_cmd_data = NULL;
+
+ if (smtp_in_auth(au, &s, &ss) == OK)
+ { DEBUG(D_auth) debug_printf("tls auth succeeded\n"); }
+ else
+ { DEBUG(D_auth) debug_printf("tls auth not succeeded\n"); }
+ }
break;
}
}
/* Force a reverse lookup if HELO quoted something in helo_lookup_domains
because otherwise the log can be confusing. */
- if (sender_host_name == NULL &&
- (deliver_domain = sender_helo_name, /* set $domain */
- match_isinlist(sender_helo_name, CUSS &helo_lookup_domains, 0,
+ if ( !sender_host_name
+ && (deliver_domain = sender_helo_name, /* set $domain */
+ match_isinlist(sender_helo_name, CUSS &helo_lookup_domains, 0,
&domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL)) == OK)
(void)host_name_lookup();
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,
}
}
-#ifdef EXPERIMENTAL_SPF
+#ifdef SUPPORT_SPF
/* set up SPF context */
spf_init(sender_helo_name, sender_host_address);
#endif
&user_msg, &log_msg)) != OK)
{
done = smtp_handle_acl_fail(ACL_WHERE_HELO, rc, user_msg, log_msg);
- sender_helo_name = NULL;
+ if (sender_helo_name)
+ {
+ store_free(sender_helo_name);
+ sender_helo_name = NULL;
+ }
host_build_sender_fullhost(); /* Rebuild */
break;
}
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;
}
US"invalid data for AUTH");
goto COMMAND_LOOP;
}
- if (acl_smtp_mailauth == NULL)
+ if (!acl_smtp_mailauth)
{
ignore_msg = US"client not authenticated";
- rc = (sender_host_authenticated != NULL)? OK : FAIL;
+ rc = sender_host_authenticated ? OK : FAIL;
}
else
{
#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;
}
DEBUG(D_receive) debug_printf("chunking state %d, %d bytes\n",
(int)chunking_state, chunking_data_left);
+ /* push the current receive_* function on the "stack", and
+ replace them by bdat_getc(), which in turn will use the lwr_receive_*
+ functions to do the dirty work. */
lwr_receive_getc = receive_getc;
lwr_receive_getbuf = receive_getbuf;
lwr_receive_ungetc = receive_ungetc;
+
receive_getc = bdat_getc;
receive_ungetc = bdat_ungetc;
+ dot_ends = FALSE;
+
goto DATA_BDAT;
}
case DATA_CMD:
HAD(SCH_DATA);
+ dot_ends = TRUE;
DATA_BDAT: /* Common code for DATA and BDAT */
if (!discarded && recipients_count <= 0)
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 */
}
cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE;
cmd_list[CMD_LIST_AUTH].is_mail_cmd = TRUE;
cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE;
- if (sender_helo_name != NULL)
+ if (sender_helo_name)
{
store_free(sender_helo_name);
sender_helo_name = NULL;
[ (esmtp
? pextend + (sender_host_authenticated ? pauthed : 0)
: pnormal)
- + (tls_in.active >= 0 ? pcrpted : 0)
+ + (tls_in.active.sock >= 0 ? pcrpted : 0)
];
- sender_host_authenticated = NULL;
+ sender_host_auth_pubname = sender_host_authenticated = NULL;
authenticated_id = NULL;
sync_cmd_limit = NON_SYNC_CMD_NON_PIPELINING;
DEBUG(D_tls) debug_printf("TLS active\n");
smtp_printf("554 Security failure\r\n", FALSE);
break;
}
- tls_close(TRUE, TRUE);
+ 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
case ETRN_CMD:
HAD(SCH_ETRN);
- if (sender_address != NULL)
+ if (sender_address)
{
done = synprot_error(L_smtp_protocol_error, 503, NULL,
US"ETRN is not permitted inside a transaction");
since that is strictly the only kind of ETRN that can be implemented
according to the RFC. */
- if (smtp_etrn_command != NULL)
+ if (smtp_etrn_command)
{
uschar *error;
BOOL rc;
case BADCHAR_CMD:
done = synprot_error(L_smtp_syntax_error, 0, NULL, /* Just logs */
- US"NULL character(s) present (shown as '?')");
- smtp_printf("501 NULL characters are not allowed in SMTP commands\r\n", FALSE);
+ US"NUL character(s) present (shown as '?')");
+ smtp_printf("501 NUL characters are not allowed in SMTP commands\r\n", FALSE);
break;
if (smtp_inend >= smtp_inbuffer + IN_BUFFER_SIZE)
smtp_inend = smtp_inbuffer + IN_BUFFER_SIZE - 1;
c = smtp_inend - smtp_inptr;
- if (c > 150) c = 150;
+ if (c > 150) c = 150; /* limit logged amount */
smtp_inptr[c] = 0;
incomplete_transaction_log(US"sync failure");
log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol synchronization error "
return done - 2; /* Convert yield values */
}
+
+
+gstring *
+authres_smtpauth(gstring * g)
+{
+if (!sender_host_authenticated)
+ return g;
+
+g = string_append(g, 2, US";\n\tauth=pass (", sender_host_auth_pubname);
+
+if (Ustrcmp(sender_host_auth_pubname, "tls") != 0)
+ g = string_append(g, 2, US") smtp.auth=", authenticated_id);
+else if (authenticated_id)
+ g = string_append(g, 2, US") x509.auth=", authenticated_id);
+else
+ g = string_catn(g, US") reason=x509.auth", 17);
+
+if (authenticated_sender)
+ g = string_append(g, 2, US" smtp.mailfrom=", authenticated_sender);
+return g;
+}
+
+
+
/* vi: aw ai sw=2
*/
/* End of smtp_in.c */