* Exim - an Internet mail transport agent *
*************************************************/
+/* Copyright (c) The Exim Maintainers 2020 - 2022 */
/* Copyright (c) University of Cambridge 1995 - 2018 */
-/* Copyright (c) The Exim Maintainers 2020 - 2021 */
/* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Functions for handling an incoming SMTP call. */
tls_auth is a pseudo-command, never expected in input. It is activated
on TLS startup and looks for a tls authenticator. */
+enum { CL_RSET, CL_HELO, CL_EHLO, CL_AUTH,
+#ifndef DISABLE_TLS
+ CL_STLS, CL_TLAU,
+#endif
+};
+
static smtp_cmd_list cmd_list[] = {
/* name len cmd has_arg is_mail_cmd */
- { "rset", sizeof("rset")-1, RSET_CMD, FALSE, FALSE }, /* First */
- { "helo", sizeof("helo")-1, HELO_CMD, TRUE, FALSE },
- { "ehlo", sizeof("ehlo")-1, EHLO_CMD, TRUE, FALSE },
- { "auth", sizeof("auth")-1, AUTH_CMD, TRUE, TRUE },
+ [CL_RSET] = { "rset", sizeof("rset")-1, RSET_CMD, FALSE, FALSE }, /* First */
+ [CL_HELO] = { "helo", sizeof("helo")-1, HELO_CMD, TRUE, FALSE },
+ [CL_EHLO] = { "ehlo", sizeof("ehlo")-1, EHLO_CMD, TRUE, FALSE },
+ [CL_AUTH] = { "auth", sizeof("auth")-1, AUTH_CMD, TRUE, TRUE },
#ifndef DISABLE_TLS
- { "starttls", sizeof("starttls")-1, STARTTLS_CMD, FALSE, FALSE },
- { "tls_auth", 0, TLS_AUTH_CMD, FALSE, FALSE },
+ [CL_STLS] = { "starttls", sizeof("starttls")-1, STARTTLS_CMD, FALSE, FALSE },
+ [CL_TLAU] = { "tls_auth", 0, TLS_AUTH_CMD, FALSE, FALSE },
#endif
/* If you change anything above here, also fix the definitions below. */
{ "help", sizeof("help")-1, HELP_CMD, TRUE, FALSE }
};
-static smtp_cmd_list *cmd_list_end =
- cmd_list + sizeof(cmd_list)/sizeof(smtp_cmd_list);
-
-#define CMD_LIST_RSET 0
-#define CMD_LIST_HELO 1
-#define CMD_LIST_EHLO 2
-#define CMD_LIST_AUTH 3
-#define CMD_LIST_STARTTLS 4
-#define CMD_LIST_TLS_AUTH 5
-
-/* This list of names is used for performing the smtp_no_mail logging action.
-It must be kept in step with the SCH_xxx enumerations. */
+/* This list of names is used for performing the smtp_no_mail logging action. */
uschar * smtp_names[] =
{
- US"NONE", US"AUTH", US"DATA", US"BDAT", US"EHLO", US"ETRN", US"EXPN",
- US"HELO", US"HELP", US"MAIL", US"NOOP", US"QUIT", US"RCPT", US"RSET",
- US"STARTTLS", US"VRFY" };
+ [SCH_NONE] = US"NONE",
+ [SCH_AUTH] = US"AUTH",
+ [SCH_DATA] = US"DATA",
+ [SCH_BDAT] = US"BDAT",
+ [SCH_EHLO] = US"EHLO",
+ [SCH_ETRN] = US"ETRN",
+ [SCH_EXPN] = US"EXPN",
+ [SCH_HELO] = US"HELO",
+ [SCH_HELP] = US"HELP",
+ [SCH_MAIL] = US"MAIL",
+ [SCH_NOOP] = US"NOOP",
+ [SCH_QUIT] = US"QUIT",
+ [SCH_RCPT] = US"RCPT",
+ [SCH_RSET] = US"RSET",
+ [SCH_STARTTLS] = US"STARTTLS",
+ [SCH_VRFY] = US"VRFY",
+ };
static uschar *protocols_local[] = {
US"local-smtp", /* HELO */
static void smtp_quit_handler(uschar **, uschar **);
static void smtp_rset_handler(void);
-/*************************************************
-* Recheck synchronization *
-*************************************************/
-
-/* Synchronization checks can never be perfect because a packet may be on its
-way but not arrived when the check is done. Normally, the checks happen when
-commands are read: Exim ensures that there is no more input in the input buffer.
-In normal cases, the response to the command will be fast, and there is no
-further check.
-
-However, for some commands an ACL is run, and that can include delays. In those
-cases, it is useful to do another check on the input just before sending the
-response. This also applies at the start of a connection. This function does
-that check by means of the select() function, as long as the facility is not
-disabled or inappropriate. A failure of select() is ignored.
-
-When there is unwanted input, we read it so that it appears in the log of the
-error.
-
-Arguments: none
-Returns: TRUE if all is well; FALSE if there is input pending
-*/
-
-static BOOL
-wouldblock_reading(void)
-{
-int fd, rc;
-fd_set fds;
-struct timeval tzero = {.tv_sec = 0, .tv_usec = 0};
-
-#ifndef DISABLE_TLS
-if (tls_in.active.sock >= 0)
- return !tls_could_read();
-#endif
-
-if (smtp_inptr < smtp_inend)
- return FALSE;
-
-fd = fileno(smtp_in);
-FD_ZERO(&fds);
-FD_SET(fd, &fds);
-rc = select(fd + 1, (SELECT_ARG2_TYPE *)&fds, NULL, NULL, &tzero);
-
-if (rc <= 0) return TRUE; /* Not ready to read */
-rc = smtp_getc(GETC_BUFFER_UNLIMITED);
-if (rc < 0) return TRUE; /* End of file or error */
-
-smtp_ungetc(rc);
-return FALSE;
-}
-
-static BOOL
-check_sync(void)
-{
-if (!smtp_enforce_sync || !sender_host_address || f.sender_host_notsocket)
- return TRUE;
-
-return wouldblock_reading();
-}
-
-
-/* If there's input waiting (and we're doing pipelineing) then we can pipeline
-a reponse with the one following. */
-
-static BOOL
-pipeline_response(void)
-{
-if ( !smtp_enforce_sync || !sender_host_address
- || f.sender_host_notsocket || !f.smtp_in_pipelining_advertised)
- return FALSE;
-
-if (wouldblock_reading()) return FALSE;
-f.smtp_in_pipelining_used = TRUE;
-return TRUE;
-}
-
-
-#ifndef DISABLE_PIPE_CONNECT
-static BOOL
-pipeline_connect_sends(void)
-{
-if (!sender_host_address || f.sender_host_notsocket || !fl.pipe_connect_acceptable)
- return FALSE;
-
-if (wouldblock_reading()) return FALSE;
-f.smtp_in_early_pipe_used = TRUE;
-return TRUE;
-}
-#endif
-
/*************************************************
* Log incomplete transactions *
*************************************************/
*/
static void
-incomplete_transaction_log(uschar *what)
+incomplete_transaction_log(uschar * what)
{
if (!sender_address /* No transaction in progress */
|| !LOGGING(smtp_incomplete_transaction))
if (recipients_count > 0)
{
- raw_recipients = store_get(recipients_count * sizeof(uschar *), FALSE);
+ raw_recipients = store_get(recipients_count * sizeof(uschar *), GET_UNTAINTED);
for (int i = 0; i < recipients_count; i++)
raw_recipients[i] = recipients_list[i].address;
raw_recipients_count = recipients_count;
+static void
+log_close_event(const uschar * reason)
+{
+log_write(L_smtp_connection, LOG_MAIN, "%s D=%s closed %s",
+ smtp_get_connection_info(), string_timesince(&smtp_connection_start), reason);
+}
+
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));
+ LOG_MAIN, "SMTP command timeout on%s connection from %s D=%s",
+ tls_in.active.sock >= 0 ? " TLS" : "", host_and_ident(FALSE),
+ string_timesince(&smtp_connection_start));
if (smtp_batched_input)
moan_smtp_batch(NULL, "421 SMTP command timeout"); /* Does not return */
smtp_notquit_exit(US"command-timeout", US"421",
void
smtp_command_sigterm_exit(void)
{
-log_write(0, LOG_MAIN, "%s closed after SIGTERM", smtp_get_connection_info());
+log_close_event(US"after SIGTERM");
if (smtp_batched_input)
moan_smtp_batch(NULL, "421 SIGTERM received"); /* Does not return */
smtp_notquit_exit(US"signal-exit", US"421",
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);
+log_write(L_lost_incoming_connection, LOG_MAIN,
+ "SMTP data timeout (message abandoned) on connection from %s F=<%s> D=%s",
+ sender_fullhost ? sender_fullhost : US"local process", sender_address,
+ string_timesince(&smtp_connection_start));
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");
+log_close_event(had_data_sigint == SIGTERM ? US"SIGTERM":US"SIGINT");
receive_bomb_out(US"signal-exit",
US"Service not available - SIGTERM or SIGINT received");
/* Does not return */
}
+/******************************************************************************/
+/* SMTP input buffer handling. Most of these are similar to stdio routines. */
+
+static void
+smtp_buf_init(void)
+{
+/* Set up the buffer for inputting using direct read() calls, and arrange to
+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';
+
+smtp_inptr = smtp_inend = smtp_inbuffer;
+smtp_had_eof = smtp_had_error = 0;
+}
+
+
/* Refill the buffer, and notify DKIM verification code.
Return false for error or EOF.
smtp_refill(unsigned lim)
{
int rc, save_errno;
+
if (!smtp_out) return FALSE;
fflush(smtp_out);
if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout);
return TRUE;
}
-/*************************************************
-* SMTP version of getc() *
-*************************************************/
-/* This gets the next byte from the SMTP input buffer. If the buffer is empty,
+/* Check if there is buffered data */
+
+BOOL
+smtp_hasc(void)
+{
+return smtp_inptr < smtp_inend;
+}
+
+/* SMTP version of getc()
+
+This gets the next byte from the SMTP input buffer. If the buffer is empty,
it flushes the output, and refills the buffer, with a timeout. The signal
handler is set appropriately by the calling function. This function is not used
after a connection has negotiated itself into an TLS/SSL state.
int
smtp_getc(unsigned lim)
{
-if (smtp_inptr >= smtp_inend)
- if (!smtp_refill(lim))
- return EOF;
+if (!smtp_hasc() && !smtp_refill(lim)) return EOF;
return *smtp_inptr++;
}
-BOOL
-smtp_hasc(void)
-{
-return smtp_inptr < smtp_inend;
-}
+/* Get many bytes, refilling buffer if needed */
uschar *
smtp_getbuf(unsigned * len)
unsigned size;
uschar * buf;
-if (smtp_inptr >= smtp_inend)
- if (!smtp_refill(*len))
- { *len = 0; return NULL; }
+if (!smtp_hasc() && !smtp_refill(*len))
+ { *len = 0; return NULL; }
if ((size = smtp_inend - smtp_inptr) > *len) size = *len;
buf = smtp_inptr;
return buf;
}
+/* Copy buffered data to the dkim feed.
+Called, unless TLS, just before starting to read message headers. */
+
void
smtp_get_cache(unsigned lim)
{
}
+/* SMTP version of ungetc()
+Puts a character back in the input buffer. Only ever called once.
+
+Arguments:
+ ch the character
+
+Returns: the character
+*/
+
+int
+smtp_ungetc(int ch)
+{
+if (smtp_inptr <= smtp_inbuffer) /* NB: NOT smtp_hasc() ! */
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "buffer underflow in smtp_ungetc");
+
+*--smtp_inptr = ch;
+return ch;
+}
+
+
+/* SMTP version of feof()
+Tests for a previous EOF
+
+Arguments: none
+Returns: non-zero if the eof flag is set
+*/
+
+int
+smtp_feof(void)
+{
+return smtp_had_eof;
+}
+
+
+/* SMTP version of ferror()
+Tests for a previous read error, and returns with errno
+restored to what it was when the error was detected.
+
+Arguments: none
+Returns: non-zero if the error flag is set
+*/
+
+int
+smtp_ferror(void)
+{
+errno = smtp_had_error;
+return smtp_had_error;
+}
+
+
+/* Check if a getc will block or not */
+
+static BOOL
+smtp_could_getc(void)
+{
+int fd, rc;
+fd_set fds;
+struct timeval tzero = {.tv_sec = 0, .tv_usec = 0};
+
+if (smtp_inptr < smtp_inend)
+ return TRUE;
+
+fd = fileno(smtp_in);
+FD_ZERO(&fds);
+FD_SET(fd, &fds);
+rc = select(fd + 1, (SELECT_ARG2_TYPE *)&fds, NULL, NULL, &tzero);
+
+if (rc <= 0) return FALSE; /* Not ready to read */
+rc = smtp_getc(GETC_BUFFER_UNLIMITED);
+if (rc < 0) return FALSE; /* End of file or error */
+
+smtp_ungetc(rc);
+return TRUE;
+}
+
+
+/******************************************************************************/
+/*************************************************
+* Recheck synchronization *
+*************************************************/
+
+/* Synchronization checks can never be perfect because a packet may be on its
+way but not arrived when the check is done. Normally, the checks happen when
+commands are read: Exim ensures that there is no more input in the input buffer.
+In normal cases, the response to the command will be fast, and there is no
+further check.
+
+However, for some commands an ACL is run, and that can include delays. In those
+cases, it is useful to do another check on the input just before sending the
+response. This also applies at the start of a connection. This function does
+that check by means of the select() function, as long as the facility is not
+disabled or inappropriate. A failure of select() is ignored.
+
+When there is unwanted input, we read it so that it appears in the log of the
+error.
+
+Arguments: none
+Returns: TRUE if all is well; FALSE if there is input pending
+*/
+
+static BOOL
+wouldblock_reading(void)
+{
+#ifndef DISABLE_TLS
+if (tls_in.active.sock >= 0)
+ return !tls_could_getc();
+#endif
+
+return !smtp_could_getc();
+}
+
+static BOOL
+check_sync(void)
+{
+if (!smtp_enforce_sync || !sender_host_address || f.sender_host_notsocket)
+ return TRUE;
+
+return wouldblock_reading();
+}
+
+
+/******************************************************************************/
+/* Variants of the smtp_* input handling functions for use in CHUNKING mode */
+
/* Forward declarations */
static inline void bdat_push_receive_functions(void);
static inline void bdat_pop_receive_functions(void);
}
bdat_pop_receive_functions();
-
-if (chunking_state != CHUNKING_LAST)
- {
- chunking_state = CHUNKING_OFFERED;
- DEBUG(D_receive) debug_printf("chunking state %d\n", (int)chunking_state);
- }
+chunking_state = CHUNKING_OFFERED;
+DEBUG(D_receive) debug_printf("chunking state %d\n", (int)chunking_state);
}
lwr_receive_ungetc = NULL;
}
-/*************************************************
-* SMTP version of ungetc() *
-*************************************************/
-
-/* Puts a character back in the input buffer. Only ever
-called once.
-
-Arguments:
- ch the character
-
-Returns: the character
-*/
-
-int
-smtp_ungetc(int ch)
-{
-if (smtp_inptr <= smtp_inbuffer)
- log_write(0, LOG_MAIN|LOG_PANIC_DIE, "buffer underflow in smtp_ungetc");
-
-*--smtp_inptr = ch;
-return ch;
-}
-
-
int
bdat_ungetc(int ch)
{
-/*************************************************
-* SMTP version of feof() *
-*************************************************/
-
-/* Tests for a previous EOF
-
-Arguments: none
-Returns: non-zero if the eof flag is set
-*/
-
-int
-smtp_feof(void)
-{
-return smtp_had_eof;
-}
-
-
-
-
-/*************************************************
-* SMTP version of ferror() *
-*************************************************/
-
-/* Tests for a previous read error, and returns with errno
-restored to what it was when the error was detected.
-
-Arguments: none
-Returns: non-zero if the error flag is set
-*/
-
-int
-smtp_ferror(void)
-{
-errno = smtp_had_error;
-return smtp_had_error;
-}
-
-
-
-/*************************************************
-* Test for characters in the SMTP buffer *
-*************************************************/
-
-/* Used at the end of a message
-
-Arguments: none
-Returns: TRUE/FALSE
-*/
-
-BOOL
-smtp_buffered(void)
-{
-return smtp_inptr < smtp_inend;
-}
-
-
+/******************************************************************************/
/*************************************************
* Write formatted string to SMTP channel *
yield = !! string_vformat(&gs, SVFMT_TAINT_NOCHK, format, ap);
string_from_gstring(&gs);
-DEBUG(D_receive)
- {
- uschar *msg_copy, *cr, *end;
- msg_copy = string_copy(gs.s);
- end = msg_copy + gs.ptr;
- while ((cr = Ustrchr(msg_copy, '\r')) != NULL) /* lose CRs */
- memmove(cr, cr + 1, (end--) - cr);
- debug_printf("SMTP>> %s", msg_copy);
- }
+DEBUG(D_receive) for (const uschar * t, * s = gs.s;
+ s && (t = Ustrchr(s, '\r'));
+ s = t + 2) /* \r\n */
+ debug_printf("%s %.*s\n",
+ s == gs.s ? "SMTP>>" : " ",
+ (int)(t - s), s);
if (!yield)
{
if (fl.rcpt_in_progress)
{
- if (rcpt_smtp_response == NULL)
+ if (!rcpt_smtp_response)
rcpt_smtp_response = string_copy(big_buffer);
else if (fl.rcpt_smtp_response_same &&
Ustrcmp(rcpt_smtp_response, big_buffer) != 0)
+/* If there's input waiting (and we're doing pipelineing) then we can pipeline
+a reponse with the one following. */
+
+static BOOL
+pipeline_response(void)
+{
+if ( !smtp_enforce_sync || !sender_host_address
+ || f.sender_host_notsocket || !f.smtp_in_pipelining_advertised)
+ return FALSE;
+
+if (wouldblock_reading()) return FALSE;
+f.smtp_in_pipelining_used = TRUE;
+return TRUE;
+}
+
+
+#ifndef DISABLE_PIPE_CONNECT
+static BOOL
+pipeline_connect_sends(void)
+{
+if (!sender_host_address || f.sender_host_notsocket || !fl.pipe_connect_acceptable)
+ return FALSE;
+
+if (wouldblock_reading()) return FALSE;
+f.smtp_in_early_pipe_used = TRUE;
+return TRUE;
+}
+#endif
+
/*************************************************
* SMTP command read timeout *
*************************************************/
return -1;
}
+
+static void
+proxy_debug(uschar * buf, unsigned start, unsigned end)
+{
+debug_printf("PROXY<<");
+while (start < end) debug_printf(" %02x", buf[start++]);
+debug_printf("\n");
+}
+
+
/*************************************************
* Setup host for proxy protocol *
*************************************************/
we have to do a minimum of 3 read calls, not 1. Eww.
*/
-#define PROXY_INITIAL_READ 14
-#define PROXY_V2_HEADER_SIZE 16
-#if PROXY_INITIAL_READ > PROXY_V2_HEADER_SIZE
-# error Code bug in sizes of data to read for proxy usage
-#endif
+# define PROXY_INITIAL_READ 14
+# define PROXY_V2_HEADER_SIZE 16
+# if PROXY_INITIAL_READ > PROXY_V2_HEADER_SIZE
+# error Code bug in sizes of data to read for proxy usage
+# endif
int get_ok = 0;
int size, ret;
"safe". Can't take it all because TLS-on-connect clients follow
immediately with TLS handshake. */
ret = read(fd, &hdr, PROXY_INITIAL_READ);
- }
- while (ret == -1 && errno == EINTR && !had_command_timeout);
+ } while (ret == -1 && errno == EINTR && !had_command_timeout);
if (ret == -1)
goto proxyfail;
+DEBUG(D_receive) proxy_debug(US &hdr, 0, ret);
/* For v2, handle reading the length, and then the rest. */
if ((ret == PROXY_INITIAL_READ) && (memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0))
int retmore;
uint8_t ver;
+ DEBUG(D_receive) debug_printf("v2\n");
+
/* First get the length fields. */
do
{
} while (retmore == -1 && errno == EINTR && !had_command_timeout);
if (retmore == -1)
goto proxyfail;
+ DEBUG(D_receive) proxy_debug(US &hdr, ret, ret + retmore);
+
ret += retmore;
ver = (hdr.v2.ver_cmd & 0xf0) >> 4;
} while (retmore == -1 && errno == EINTR && !had_command_timeout);
if (retmore == -1)
goto proxyfail;
+ DEBUG(D_receive) proxy_debug(US &hdr, ret, ret + retmore);
ret += retmore;
DEBUG(D_receive) debug_printf("PROXYv2: have %d/%d required octets\n", ret, size);
} while (ret < size);
ALARM(0);
return;
}
-#endif
+#endif /*SUPPORT_PROXY*/
/*************************************************
* Read one command line *
to the start of the actual data characters. Check for SMTP synchronization
if required. */
-for (smtp_cmd_list * p = cmd_list; p < cmd_list_end; p++)
+for (smtp_cmd_list * p = cmd_list; p < cmd_list + nelem(cmd_list); p++)
{
#ifdef SUPPORT_PROXY
/* Only allow QUIT command if Proxy Protocol parsing failed */
|| smtp_cmd_buffer[p->len] == ' '
) )
{
- if (smtp_inptr < smtp_inend && /* Outstanding input */
- p->cmd < sync_cmd_limit && /* Command should sync */
- check_sync && /* Local flag set */
- smtp_enforce_sync && /* Global flag set */
- sender_host_address != NULL && /* Not local input */
- !f.sender_host_notsocket) /* Really is a socket */
+ if ( smtp_inptr < smtp_inend /* Outstanding input */
+ && p->cmd < sync_cmd_limit /* Command should sync */
+ && check_sync /* Local flag set */
+ && smtp_enforce_sync /* Global flag set */
+ && sender_host_address != NULL /* Not local input */
+ && !f.sender_host_notsocket /* Really is a socket */
+ )
return BADSYN_CMD;
/* The variables $smtp_command and $smtp_command_argument point into the
&& check_sync /* Local flag set */
&& smtp_enforce_sync /* Global flag set */
&& sender_host_address /* Not local input */
- && !f.sender_host_notsocket) /* Really is a socket */
+ && !f.sender_host_notsocket /* Really is a socket */
+ )
return BADSYN_CMD;
return OTHER_CMD;
*/
void
-smtp_closedown(uschar *message)
+smtp_closedown(uschar * message)
{
if (!smtp_in || smtp_batched_input) return;
receive_swallow_smtp();
#ifdef SUPPORT_I18N
message_smtputf8 = FALSE;
#endif
+#ifdef WITH_CONTENT_SCAN
+regex_vars_clear();
+#endif
body_linecount = body_zerocount = 0;
+lookup_value = NULL; /* Can be set by ACL */
sender_rate = sender_rate_limit = sender_rate_period = NULL;
ratelimiters_mail = NULL; /* Updated by ratelimit ACL condition */
/* Note that ratelimiters_conn persists across resets. */
#ifndef DISABLE_TLS
static BOOL
-smtp_log_tls_fail(uschar * errstr)
+smtp_log_tls_fail(const uschar * errstr)
{
-uschar * conn_info = smtp_get_connection_info();
+const uschar * conn_info = smtp_get_connection_info();
if (Ustrncmp(conn_info, US"SMTP ", 5) == 0) conn_info += 5;
/* I'd like to get separated H= here, but too hard for now */
#endif
+static void
+log_connect_tls_drop(const uschar * what, const uschar * log_msg)
+{
+gstring * g = s_tlslog(NULL);
+uschar * tls = string_from_gstring(g);
+
+log_write(L_connection_reject,
+ log_reject_target, "%s%s%s dropped by %s%s%s",
+ LOGGING(dnssec) && sender_host_dnssec ? US" DS" : US"",
+ host_and_ident(TRUE),
+ tls ? tls : US"",
+ what,
+ log_msg ? US": " : US"", log_msg);
+}
+
+
/*************************************************
* Start an SMTP session *
*************************************************/
/* Allow for trailing 0 in the command and data buffers. Tainted. */
-smtp_cmd_buffer = store_get_perm(2*SMTP_CMD_BUFFER_SIZE + 2, TRUE);
+smtp_cmd_buffer = store_get_perm(2*SMTP_CMD_BUFFER_SIZE + 2, GET_TAINTED);
smtp_cmd_buffer[0] = 0;
smtp_data_buffer = smtp_cmd_buffer + SMTP_CMD_BUFFER_SIZE + 1;
(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. Place a NUL at the
-end of the buffer to safety-stop C-string reads from it. */
+call the local functions instead of the standard C ones. */
-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';
+smtp_buf_init();
receive_getc = smtp_getc;
receive_getbuf = smtp_getbuf;
receive_ungetc = smtp_ungetc;
receive_feof = smtp_feof;
receive_ferror = smtp_ferror;
-receive_smtp_buffered = smtp_buffered;
lwr_receive_getc = NULL;
lwr_receive_getbuf = NULL;
lwr_receive_hasc = NULL;
lwr_receive_ungetc = NULL;
-smtp_inptr = smtp_inend = smtp_inbuffer;
-smtp_had_eof = smtp_had_error = 0;
/* Set up the message size limit; this may be host-specific */
#if !HAVE_IPV6 && !defined(NO_IP_OPTIONS)
- #ifdef GLIBC_IP_OPTIONS
- #if (!defined __GLIBC__) || (__GLIBC__ < 2)
- #define OPTSTYLE 1
- #else
- #define OPTSTYLE 2
- #endif
- #elif defined DARWIN_IP_OPTIONS
- #define OPTSTYLE 2
- #else
- #define OPTSTYLE 3
- #endif
+# ifdef GLIBC_IP_OPTIONS
+# if (!defined __GLIBC__) || (__GLIBC__ < 2)
+# define OPTSTYLE 1
+# else
+# define OPTSTYLE 2
+# endif
+# elif defined DARWIN_IP_OPTIONS
+# define OPTSTYLE 2
+# else
+# define OPTSTYLE 3
+# endif
if (!host_checking && !f.sender_host_notsocket)
{
- #if OPTSTYLE == 1
+# if OPTSTYLE == 1
EXIM_SOCKLEN_T optlen = sizeof(struct ip_options) + MAX_IPOPTLEN;
- struct ip_options *ipopt = store_get(optlen, FALSE);
- #elif OPTSTYLE == 2
+ struct ip_options *ipopt = store_get(optlen, GET_UNTAINTED);
+# elif OPTSTYLE == 2
struct ip_opts ipoptblock;
struct ip_opts *ipopt = &ipoptblock;
EXIM_SOCKLEN_T optlen = sizeof(ipoptblock);
- #else
+# else
struct ipoption ipoptblock;
struct ipoption *ipopt = &ipoptblock;
EXIM_SOCKLEN_T optlen = sizeof(ipoptblock);
- #endif
+# endif
/* Occasional genuine failures of getsockopt() have been seen - for
example, "reset by peer". Therefore, just log and give up on this
else if (optlen > 0)
{
- uschar *p = big_buffer;
- uschar *pend = big_buffer + big_buffer_size;
- uschar *adptr;
+ uschar * p = big_buffer;
+ uschar * pend = big_buffer + big_buffer_size;
+ uschar * adptr;
int optcount;
struct in_addr addr;
- #if OPTSTYLE == 1
- uschar *optstart = US (ipopt->__data);
- #elif OPTSTYLE == 2
- uschar *optstart = US (ipopt->ip_opts);
- #else
- uschar *optstart = US (ipopt->ipopt_list);
- #endif
+# if OPTSTYLE == 1
+ uschar * optstart = US (ipopt->__data);
+# elif OPTSTYLE == 2
+ uschar * optstart = US (ipopt->ip_opts);
+# else
+ uschar * optstart = US (ipopt->ipopt_list);
+# endif
DEBUG(D_receive) debug_printf("IP options exist\n");
switch (*opt)
{
case IPOPT_EOL:
- opt = NULL;
- break;
+ opt = NULL;
+ break;
case IPOPT_NOP:
- opt++;
- break;
+ opt++;
+ break;
case IPOPT_SSRR:
case IPOPT_LSRR:
- if (!string_format(p, pend-p, " %s [@%s",
- (*opt == IPOPT_SSRR)? "SSRR" : "LSRR",
- #if OPTSTYLE == 1
- inet_ntoa(*((struct in_addr *)(&(ipopt->faddr))))))
- #elif OPTSTYLE == 2
- inet_ntoa(ipopt->ip_dst)))
- #else
- inet_ntoa(ipopt->ipopt_dst)))
- #endif
- {
- opt = NULL;
- break;
- }
+ if (!
+# if OPTSTYLE == 1
+ string_format(p, pend-p, " %s [@%s",
+ (*opt == IPOPT_SSRR)? "SSRR" : "LSRR",
+ inet_ntoa(*((struct in_addr *)(&(ipopt->faddr)))))
+# elif OPTSTYLE == 2
+ string_format(p, pend-p, " %s [@%s",
+ (*opt == IPOPT_SSRR)? "SSRR" : "LSRR",
+ inet_ntoa(ipopt->ip_dst))
+# else
+ string_format(p, pend-p, " %s [@%s",
+ (*opt == IPOPT_SSRR)? "SSRR" : "LSRR",
+ inet_ntoa(ipopt->ipopt_dst))
+# endif
+ )
+ {
+ opt = NULL;
+ break;
+ }
- p += Ustrlen(p);
- optcount = (opt[1] - 3) / sizeof(struct in_addr);
- adptr = opt + 3;
- while (optcount-- > 0)
- {
- memcpy(&addr, adptr, sizeof(addr));
- if (!string_format(p, pend - p - 1, "%s%s",
- (optcount == 0)? ":" : "@", inet_ntoa(addr)))
- {
- opt = NULL;
- break;
- }
- p += Ustrlen(p);
- adptr += sizeof(struct in_addr);
- }
- *p++ = ']';
- opt += opt[1];
- break;
+ p += Ustrlen(p);
+ optcount = (opt[1] - 3) / sizeof(struct in_addr);
+ adptr = opt + 3;
+ while (optcount-- > 0)
+ {
+ memcpy(&addr, adptr, sizeof(addr));
+ if (!string_format(p, pend - p - 1, "%s%s",
+ (optcount == 0)? ":" : "@", inet_ntoa(addr)))
+ {
+ opt = NULL;
+ break;
+ }
+ p += Ustrlen(p);
+ adptr += sizeof(struct in_addr);
+ }
+ *p++ = ']';
+ opt += opt[1];
+ break;
default:
- {
- if (pend - p < 4 + 3*opt[1]) { opt = NULL; break; }
- Ustrcat(p, "[ ");
- p += 2;
- for (int i = 0; i < opt[1]; i++)
- p += sprintf(CS p, "%2.2x ", opt[i]);
- *p++ = ']';
- }
- opt += opt[1];
- break;
+ {
+ if (pend - p < 4 + 3*opt[1]) { opt = NULL; break; }
+ Ustrcat(p, "[ ");
+ p += 2;
+ for (int i = 0; i < opt[1]; i++)
+ p += sprintf(CS p, "%2.2x ", opt[i]);
+ *p++ = ']';
+ }
+ opt += opt[1];
+ break;
}
*p = 0;
{
log_write(L_connection_reject, LOG_MAIN|LOG_REJECT, "refused connection "
"from %s (host_reject_connection)", host_and_ident(FALSE));
- smtp_printf("554 SMTP service not available\r\n", FALSE);
+#ifndef DISABLE_TLS
+ if (!tls_in.on_connect)
+#endif
+ smtp_printf("554 SMTP service not available\r\n", FALSE);
return FALSE;
}
setup_proxy_protocol_host();
#endif
-/* Start up TLS if tls_on_connect is set. This is for supporting the legacy
-smtps port for use with older style SSL MTAs. */
-
-#ifndef DISABLE_TLS
-if (tls_in.on_connect)
- {
- if (tls_server_start(&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 */
user_msg = NULL;
if ((rc = acl_check(ACL_WHERE_CONNECT, NULL, acl_smtp_connect, &user_msg,
&log_msg)) != OK)
{
- (void) smtp_handle_acl_fail(ACL_WHERE_CONNECT, rc, user_msg, log_msg);
+#ifndef DISABLE_TLS
+ if (tls_in.on_connect)
+ log_connect_tls_drop(US"'connect' ACL", log_msg);
+ else
+#endif
+ (void) smtp_handle_acl_fail(ACL_WHERE_CONNECT, rc, user_msg, log_msg);
return FALSE;
}
}
+/* Start up TLS if tls_on_connect is set. This is for supporting the legacy
+smtps port for use with older style SSL MTAs. */
+
+#ifndef DISABLE_TLS
+if (tls_in.on_connect)
+ {
+ if (tls_server_start(&user_msg) != OK)
+ return smtp_log_tls_fail(user_msg);
+ cmd_list[CL_TLAU].is_mail_cmd = TRUE;
+ }
+#endif
+
/* Output the initial message for a two-way SMTP connection. It may contain
newlines, which then cause a multi-line response to be given. */
esc = US""; /* Default extended status code */
esclen = 0; /* Length of esc */
-if (!user_msg)
- {
- if (!(s = expand_string(smtp_banner)))
- log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Expansion of \"%s\" (smtp_banner) "
- "failed: %s", smtp_banner, expand_string_message);
- }
-else
+if (user_msg)
{
int codelen = 3;
s = user_msg;
esclen = codelen - 4;
}
}
+else if (!(s = expand_string(smtp_banner)))
+ {
+ log_write(0, f.expand_string_forcedfail ? LOG_MAIN : LOG_MAIN|LOG_PANIC_DIE,
+ "Expansion of \"%s\" (smtp_banner) failed: %s",
+ smtp_banner, expand_string_message);
+ /* for force-fail */
+#ifndef DISABLE_TLS
+ if (tls_in.on_connect) tls_close(NULL, TLS_SHUTDOWN_WAIT);
+#endif
+ return FALSE;
+ }
/* Remove any terminating newlines; might as well remove trailing space too */
if (!drop) return 0;
-log_write(L_smtp_connection, LOG_MAIN, "%s closed by DROP in ACL",
- smtp_get_connection_info());
+log_close_event(US"by DROP in ACL");
/* Run the not-quit ACL, but without any custom messages. This should not be a
problem, because we get here only if some other ACL has issued "drop", and
in that case, *its* custom messages will have been used above. */
smtp_notquit_exit(US"acl-drop", NULL, NULL);
+
+/* An overenthusiastic fail2ban/iptables implimentation has been seen to result
+in the TCP conn staying open, and retrying, despite this process exiting. A
+malicious client could possibly do the same, tying up server netowrking
+resources. Close the socket explicitly to try to avoid that (there's a note in
+the Linux socket(7) manpage, SO_LINGER para, to the effect that exim() without
+close() results in the socket always lingering). */
+
+(void) poll_one_fd(fileno(smtp_in), POLLIN, 200);
+DEBUG(D_any) debug_printf_indent("SMTP(close)>>\n");
+(void) fclose(smtp_in);
+(void) fclose(smtp_out);
+
return 2;
}
static int
-smtp_in_auth(auth_instance *au, uschar ** s, uschar ** ss)
+smtp_in_auth(auth_instance *au, uschar ** smtp_resp, uschar ** errmsg)
{
const uschar *set_id = NULL;
int rc;
+/* Set up globals for error messages */
+
+authenticator_name = au->name;
+driver_srcfile = au->srcfile;
+driver_srcline = au->srcline;
+
/* Run the checking code, passing the remainder of the command line as
data. Initials the $auth<n> variables as empty. Initialize $0 empty and set
it as the only set numerical variable. The authenticator may set $auth<n>
if (au->set_id) set_id = expand_string(au->set_id);
expand_nmax = -1; /* Reset numeric variables */
for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL; /* Reset $auth<n> */
+driver_srcfile = authenticator_name = NULL; driver_srcline = 0;
/* The value of authenticated_id is stored in the spool file and printed in
log lines. It must not contain binary zeros or newline characters. In
received_protocol =
(sender_host_address ? protocols : protocols_local)
[pextend + pauthed + (tls_in.active.sock >= 0 ? pcrpted:0)];
- *s = *ss = US"235 Authentication succeeded";
+ *smtp_resp = *errmsg = US"235 Authentication succeeded";
authenticated_by = au;
break;
}
case DEFER:
if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
- *s = string_sprintf("435 Unable to authenticate at present%s",
+ *smtp_resp = string_sprintf("435 Unable to authenticate at present%s",
auth_defer_user_msg);
- *ss = string_sprintf("435 Unable to authenticate at present%s: %s",
+ *errmsg = string_sprintf("435 Unable to authenticate at present%s: %s",
set_id, auth_defer_msg);
break;
case BAD64:
- *s = *ss = US"501 Invalid base64 data";
+ *smtp_resp = *errmsg = US"501 Invalid base64 data";
break;
case CANCELLED:
- *s = *ss = US"501 Authentication cancelled";
+ *smtp_resp = *errmsg = US"501 Authentication cancelled";
break;
case UNEXPECTED:
- *s = *ss = US"553 Initial data not expected";
+ *smtp_resp = *errmsg = US"553 Initial data not expected";
break;
case FAIL:
if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
- *s = US"535 Incorrect authentication data";
- *ss = string_sprintf("535 Incorrect authentication data%s", set_id);
+ *smtp_resp = US"535 Incorrect authentication data";
+ *errmsg = string_sprintf("535 Incorrect authentication data%s", set_id);
break;
default:
if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
- *s = US"435 Internal error";
- *ss = string_sprintf("435 Internal error%s: return %d from authentication "
+ *smtp_resp = US"435 Internal error";
+ *errmsg = string_sprintf("435 Internal error%s: return %d from authentication "
"check", set_id, rc);
break;
}
tls_close(NULL, TLS_SHUTDOWN_NOWAIT);
# endif
-log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT",
- smtp_get_connection_info());
+log_close_event(US"by QUIT");
#else
# ifndef DISABLE_TLS
tls_close(NULL, TLS_SHUTDOWN_WAIT);
# endif
-log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT",
- smtp_get_connection_info());
+log_close_event(US"by QUIT");
/* Pause, hoping client will FIN first so that they get the TIME_WAIT.
The socket should become readble (though with no data) */
- {
- int fd = fileno(smtp_in);
- fd_set fds;
- struct timeval t_limit = {.tv_sec = 0, .tv_usec = 200*1000};
-
- FD_ZERO(&fds);
- FD_SET(fd, &fds);
- (void) select(fd + 1, (SELECT_ARG2_TYPE *)&fds, NULL, NULL, &t_limit);
- }
-#endif /*!DAEMON_CLOSE_NOWAIT*/
+(void) poll_one_fd(fileno(smtp_in), POLLIN, 200);
+#endif /*!SERVERSIDE_CLOSE_NOWAIT*/
}
HAD(SCH_RSET);
incomplete_transaction_log(US"RSET");
smtp_printf("250 Reset OK\r\n", FALSE);
-cmd_list[CMD_LIST_RSET].is_mail_cmd = FALSE;
+cmd_list[CL_RSET].is_mail_cmd = FALSE;
+if (chunking_state > CHUNKING_OFFERED)
+ chunking_state = CHUNKING_OFFERED;
}
chunking_state = f.chunking_offered ? CHUNKING_OFFERED : CHUNKING_NOT_OFFERED;
-cmd_list[CMD_LIST_RSET].is_mail_cmd = TRUE;
-cmd_list[CMD_LIST_HELO].is_mail_cmd = TRUE;
-cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE;
+cmd_list[CL_RSET].is_mail_cmd = TRUE;
+cmd_list[CL_HELO].is_mail_cmd = TRUE;
+cmd_list[CL_EHLO].is_mail_cmd = TRUE;
#ifndef DISABLE_TLS
-cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = TRUE;
+cmd_list[CL_STLS].is_mail_cmd = TRUE;
#endif
if (lwr_receive_getc != NULL)
if ( tls_in.active.sock >= 0
&& tls_in.peercert
&& tls_in.certificate_verified
- && cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd
+ && cmd_list[CL_TLAU].is_mail_cmd
)
{
- cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = FALSE;
+ cmd_list[CL_TLAU].is_mail_cmd = FALSE;
for (auth_instance * au = auths; au; au = au->next)
if (strcmpic(US"tls", au->driver_name) == 0)
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"); }
+ {
+ DEBUG(D_auth) debug_printf("tls auth not succeeded\n");
+#ifndef DISABLE_EVENT
+ {
+ uschar * save_name = sender_host_authenticated, * logmsg;
+ sender_host_authenticated = au->name;
+ if ((logmsg = event_raise(event_action, US"auth:fail", s, NULL)))
+ log_write(0, LOG_MAIN, "%s", logmsg);
+ sender_host_authenticated = save_name;
+ }
+#endif
+ }
}
break;
}
case AUTH_CMD:
HAD(SCH_AUTH);
authentication_failed = TRUE;
- cmd_list[CMD_LIST_AUTH].is_mail_cmd = FALSE;
+ cmd_list[CL_AUTH].is_mail_cmd = FALSE;
if (!fl.auth_advertised && !f.allow_auth_unadvertised)
{
{
auth_instance * au;
+ uschar * smtp_resp, * errmsg;
+
for (au = auths; au; au = au->next)
if (strcmpic(s, au->public_name) == 0 && au->server &&
(au->advertised || f.allow_auth_unadvertised))
if (au)
{
- c = smtp_in_auth(au, &s, &ss);
+ int rc = smtp_in_auth(au, &smtp_resp, &errmsg);
- smtp_printf("%s\r\n", FALSE, s);
- if (c != OK)
- log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s",
- au->name, host_and_ident(FALSE), ss);
+ smtp_printf("%s\r\n", FALSE, smtp_resp);
+ if (rc != OK)
+ {
+ uschar * logmsg = NULL;
+#ifndef DISABLE_EVENT
+ {uschar * save_name = sender_host_authenticated;
+ sender_host_authenticated = au->name;
+ logmsg = event_raise(event_action, US"auth:fail", smtp_resp, NULL);
+ sender_host_authenticated = save_name;
+ }
+#endif
+ if (logmsg)
+ log_write(0, LOG_MAIN|LOG_REJECT, "%s", logmsg);
+ else
+ log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s",
+ au->name, host_and_ident(FALSE), errmsg);
+ }
}
else
done = synprot_error(L_smtp_protocol_error, 504, NULL,
fl.esmtp = TRUE;
HELO_EHLO: /* Common code for HELO and EHLO */
- cmd_list[CMD_LIST_HELO].is_mail_cmd = FALSE;
- cmd_list[CMD_LIST_EHLO].is_mail_cmd = FALSE;
+ cmd_list[CL_HELO].is_mail_cmd = FALSE;
+ cmd_list[CL_EHLO].is_mail_cmd = FALSE;
/* Reject the HELO if its argument was invalid or non-existent. A
successful check causes the argument to be saved in malloc store. */
if (!user_msg)
{
/* sender_host_name below will be tainted, so save on copy when we hit it */
- g = string_get_tainted(24, TRUE);
+ g = string_get_tainted(24, GET_TAINTED);
g = string_fmt_append(g, "%.3s %s Hello %s%s%s",
smtp_code,
smtp_active_hostname,
else
{
- char *ss;
+ char * ss;
int codelen = 4;
smtp_message_code(&smtp_code, &codelen, &user_msg, NULL, TRUE);
s = string_sprintf("%.*s%s", codelen, smtp_code, user_msg);
if (fl.esmtp)
{
- g->s[3] = '-';
+ g->s[3] = '-'; /* overwrite the space after the SMTP response code */
/* I'm not entirely happy with this, as an MTA is supposed to check
that it has enough room to accept a message of maximum size before
first = FALSE;
fl.auth_advertised = TRUE;
}
- saveptr = g->ptr;
+ saveptr = gstring_length(g);
g = string_catn(g, US" ", 1);
- g = string_cat (g, au->public_name);
+ g = string_cat(g, au->public_name);
while (++saveptr < g->ptr) g->s[saveptr] = toupper(g->s[saveptr]);
au->advertised = TRUE;
}
/* Terminate the string (for debug), write it, and note that HELO/EHLO
has been seen. */
+ {
+ uschar * ehlo_resp;
+ int len = len_string_from_gstring(g, &ehlo_resp);
#ifndef DISABLE_TLS
- if (tls_in.active.sock >= 0)
- (void)tls_write(NULL, g->s, g->ptr,
+ if (tls_in.active.sock >= 0)
+ (void) tls_write(NULL, ehlo_resp, len,
# ifndef DISABLE_PIPE_CONNECT
- fl.pipe_connect_acceptable && pipeline_connect_sends());
+ fl.pipe_connect_acceptable && pipeline_connect_sends());
# else
- FALSE);
+ FALSE);
# endif
- else
+ else
#endif
- (void) fwrite(g->s, 1, g->ptr, smtp_out);
+ (void) fwrite(ehlo_resp, 1, len, smtp_out);
- DEBUG(D_receive)
- {
- uschar *cr;
-
- (void) string_from_gstring(g);
- while ((cr = Ustrchr(g->s, '\r')) != NULL) /* lose CRs */
- memmove(cr, cr + 1, (g->ptr--) - (cr - g->s));
- debug_printf("SMTP>> %s", g->s);
- }
- fl.helo_seen = TRUE;
+ DEBUG(D_receive) for (const uschar * t, * s = ehlo_resp;
+ s && (t = Ustrchr(s, '\r'));
+ s = t + 2) /* \r\n */
+ debug_printf("%s %.*s\n",
+ s == g->s ? "SMTP>>" : " ",
+ (int)(t - s), s);
+ fl.helo_seen = TRUE;
+ }
/* Reset the protocol and the state, abandoning any previous message. */
received_protocol =
count this as a protocol error. Reset was_rej_mail so that further RCPTs
get the same treatment. */
- if (sender_address == NULL)
+ if (!sender_address)
{
if (f.smtp_in_pipelining_advertised && last_was_rej_mail)
{
/* Check for an operand */
- if (smtp_cmd_data[0] == 0)
+ if (!smtp_cmd_data[0])
{
done = synprot_error(L_smtp_syntax_error, 501, NULL,
US"RCPT must have an address operand");
#endif
if (!discarded && recipients_count <= 0)
{
- if (fl.rcpt_smtp_response_same && rcpt_smtp_response != NULL)
+ if (fl.rcpt_smtp_response_same && rcpt_smtp_response)
{
uschar *code = US"503";
int len = Ustrlen(rcpt_smtp_response);
ACL may have delayed. To handle cutthrough delivery enforce a dummy call
to get the DATA command sent. */
- if (acl_smtp_predata == NULL && cutthrough.cctx.sock < 0)
+ if (!acl_smtp_predata && cutthrough.cctx.sock < 0)
rc = OK;
else
{
cancel_cutthrough_connection(TRUE, US"STARTTLS received");
reset_point = smtp_reset(reset_point);
toomany = FALSE;
- cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = FALSE;
+ cmd_list[CL_STLS].is_mail_cmd = FALSE;
/* There's an attack where more data is read in past the STARTTLS command
before TLS is negotiated, then assumed to be part of the secure session
Pipelining sync checks will normally have protected us too, unless disabled
by configuration. */
- if (receive_smtp_buffered())
+ if (receive_hasc())
{
DEBUG(D_any)
debug_printf("Non-empty input buffer after STARTTLS; naive attack?\n");
{
if (!tls_remember_esmtp)
fl.helo_seen = fl.esmtp = fl.auth_advertised = f.smtp_in_pipelining_advertised = FALSE;
- 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;
+ cmd_list[CL_EHLO].is_mail_cmd = TRUE;
+ cmd_list[CL_AUTH].is_mail_cmd = TRUE;
+ cmd_list[CL_TLAU].is_mail_cmd = TRUE;
if (sender_helo_name)
{
sender_helo_name = NULL;
while (done <= 0) switch(smtp_read_command(FALSE, GETC_BUFFER_UNLIMITED))
{
case EOF_CMD:
- log_write(L_smtp_connection, LOG_MAIN, "%s closed by EOF",
- smtp_get_connection_info());
+ log_close_event(US"by EOF");
smtp_notquit_exit(US"tls-failed", NULL, NULL);
done = 2;
break;
smtp_respond(US"221", 3, TRUE, user_msg);
else
smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname);
- log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT",
- smtp_get_connection_info());
+ log_close_event(US"by QUIT");
done = 2;
break;
case HELP_CMD:
HAD(SCH_HELP);
- smtp_printf("214-Commands supported:\r\n", TRUE);
- {
- uschar buffer[256];
- buffer[0] = 0;
- Ustrcat(buffer, US" AUTH");
- #ifndef DISABLE_TLS
- if (tls_in.active.sock < 0 &&
- verify_check_host(&tls_advertise_hosts) != FAIL)
- Ustrcat(buffer, US" STARTTLS");
- #endif
- Ustrcat(buffer, US" HELO EHLO MAIL RCPT DATA BDAT");
- Ustrcat(buffer, US" NOOP QUIT RSET HELP");
- if (acl_smtp_etrn) Ustrcat(buffer, US" ETRN");
- if (acl_smtp_expn) Ustrcat(buffer, US" EXPN");
- if (acl_smtp_vrfy) Ustrcat(buffer, US" VRFY");
- smtp_printf("214%s\r\n", FALSE, buffer);
- }
+ smtp_printf("214-Commands supported:\r\n214", TRUE);
+ smtp_printf(" AUTH", TRUE);
+#ifndef DISABLE_TLS
+ if (tls_in.active.sock < 0 &&
+ verify_check_host(&tls_advertise_hosts) != FAIL)
+ smtp_printf(" STARTTLS", TRUE);
+#endif
+ smtp_printf(" HELO EHLO MAIL RCPT DATA BDAT", TRUE);
+ smtp_printf(" NOOP QUIT RSET HELP", TRUE);
+ if (acl_smtp_etrn) smtp_printf(" ETRN", TRUE);
+ if (acl_smtp_expn) smtp_printf(" EXPN", TRUE);
+ if (acl_smtp_vrfy) smtp_printf(" VRFY", TRUE);
+ smtp_printf("\r\n", FALSE);
break;
etrn_command = smtp_etrn_command;
deliver_domain = smtp_cmd_data;
rc = transport_set_up_command(&argv, smtp_etrn_command, TRUE, 0, NULL,
- US"ETRN processing", &error);
+ FALSE, US"ETRN processing", &error);
deliver_domain = NULL;
if (!rc)
{
{
DEBUG(D_exec) debug_print_argv(argv);
exim_nullstd(); /* Ensure std{in,out,err} exist */
+ /* argv[0] should be untainted, from child_exec_exim() */
execv(CS argv[0], (char *const *)argv);
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "exec of \"%s\" (ETRN) failed: %s",
etrn_command, strerror(errno));
COMMAND_LOOP:
last_was_rej_mail = was_rej_mail; /* Remember some last commands for */
last_was_rcpt = was_rcpt; /* protocol error handling */
- continue;
}
return done - 2; /* Convert yield values */