X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/7242147951e127e0db14f9edc070251e110fedea..1d28cc061677bd07d9bed48dd84bd5c590247043:/src/src/smtp_in.c diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 8e9b93ab3..9b60702c1 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -2,9 +2,10 @@ * 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 */ /* 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. */ @@ -139,7 +140,7 @@ static struct { #endif BOOL dsn_advertised :1; BOOL esmtp :1; - BOOL helo_required :1; + BOOL helo_verify_required :1; BOOL helo_verify :1; BOOL helo_seen :1; BOOL helo_accept_junk :1; @@ -153,7 +154,7 @@ static struct { BOOL smtputf8_advertised :1; #endif } fl = { - .helo_required = FALSE, + .helo_verify_required = FALSE, .helo_verify = FALSE, .smtp_exit_function_called = FALSE, }; @@ -319,96 +320,6 @@ static int synprot_error(int type, int code, uschar *data, uschar *errmess); 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 * *************************************************/ @@ -432,7 +343,7 @@ if (!sender_address /* No transaction in progress */ 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; @@ -491,6 +402,25 @@ receive_bomb_out(US"signal-exit", } +/******************************************************************************/ +/* 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. @@ -500,6 +430,7 @@ static BOOL 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); @@ -541,11 +472,18 @@ smtp_inptr = smtp_inbuffer; 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. @@ -557,21 +495,20 @@ Returns: the next character or EOF 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++; } +/* 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; @@ -580,19 +517,146 @@ smtp_inptr += size; return buf; } +/* Copy buffered data to the dkim feed. +Called, unless TLS, just before starting to read message headers. */ + void -smtp_get_cache(void) +smtp_get_cache(unsigned lim) { #ifndef DISABLE_DKIM int n = smtp_inend - smtp_inptr; -if (chunking_state == CHUNKING_LAST && chunking_data_left < n) - n = chunking_data_left; +if (n > lim) + n = lim; if (n > 0) dkim_exim_verify_feed(smtp_inptr, n); #endif } +/* 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); @@ -661,7 +725,9 @@ for(;;) if (chunking_state == CHUNKING_LAST) { #ifndef DISABLE_DKIM + dkim_collect_input = dkim_save; dkim_exim_verify_feed(NULL, 0); /* notify EOD */ + dkim_collect_input = 0; #endif return EOD; } @@ -743,6 +809,14 @@ next_cmd: } } +BOOL +bdat_hasc(void) +{ +if (chunking_data_left > 0) + return lwr_receive_hasc(); +return TRUE; +} + uschar * bdat_getbuf(unsigned * len) { @@ -767,12 +841,8 @@ while (chunking_data_left) } 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); } @@ -782,10 +852,11 @@ bdat_push_receive_functions(void) /* 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. */ -if (lwr_receive_getc == NULL) +if (!lwr_receive_getc) { lwr_receive_getc = receive_getc; lwr_receive_getbuf = receive_getbuf; + lwr_receive_hasc = receive_hasc; lwr_receive_ungetc = receive_ungetc; } else @@ -795,50 +866,29 @@ else receive_getc = bdat_getc; receive_getbuf = bdat_getbuf; +receive_hasc = bdat_hasc; receive_ungetc = bdat_ungetc; } static inline void bdat_pop_receive_functions(void) { -if (lwr_receive_getc == NULL) +if (!lwr_receive_getc) { DEBUG(D_receive) debug_printf("chunking double-pop receive functions\n"); return; } receive_getc = lwr_receive_getc; receive_getbuf = lwr_receive_getbuf; +receive_hasc = lwr_receive_hasc; receive_ungetc = lwr_receive_ungetc; lwr_receive_getc = NULL; lwr_receive_getbuf = NULL; +lwr_receive_hasc = NULL; 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) { @@ -849,62 +899,7 @@ return lwr_receive_ungetc(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 * @@ -958,15 +953,12 @@ that we'll never expand it. */ 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) { @@ -983,7 +975,7 @@ which sometimes uses smtp_printf() and sometimes smtp_respond(). */ 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) @@ -1034,6 +1026,35 @@ return smtp_write_error; +/* 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 * *************************************************/ @@ -1655,12 +1676,13 @@ for (smtp_cmd_list * p = cmd_list; p < cmd_list_end; p++) || 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 @@ -1709,7 +1731,8 @@ if ( smtp_inptr < smtp_inend /* Outstanding input */ && 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; @@ -1735,7 +1758,7 @@ Returns: nothing */ void -smtp_closedown(uschar *message) +smtp_closedown(uschar * message) { if (!smtp_in || smtp_batched_input) return; receive_swallow_smtp(); @@ -2135,8 +2158,12 @@ prdr_requested = FALSE; #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. */ @@ -2413,9 +2440,9 @@ return done - 2; /* Convert yield values */ #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 */ @@ -2543,7 +2570,7 @@ acl_var_c = NULL; /* 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; @@ -2564,25 +2591,21 @@ else (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_get_cache = smtp_get_cache; +receive_hasc = smtp_hasc; 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 */ @@ -2657,7 +2680,7 @@ if (!f.sender_host_unknown) { #if OPTSTYLE == 1 EXIM_SOCKLEN_T optlen = sizeof(struct ip_options) + MAX_IPOPTLEN; - struct ip_options *ipopt = store_get(optlen, FALSE); + struct ip_options *ipopt = store_get(optlen, GET_UNTAINTED); #elif OPTSTYLE == 2 struct ip_opts ipoptblock; struct ip_opts *ipopt = &ipoptblock; @@ -2936,8 +2959,8 @@ if (!f.sender_host_unknown) /* Determine whether HELO/EHLO is required for this host. The requirement can be hard or soft. */ - fl.helo_required = verify_check_host(&helo_verify_hosts) == OK; - if (!fl.helo_required) + fl.helo_verify_required = verify_check_host(&helo_verify_hosts) == OK; + if (!fl.helo_verify_required) fl.helo_verify = verify_check_host(&helo_try_verify_hosts) == OK; /* Determine whether this hosts is permitted to send syntactic junk @@ -3015,7 +3038,7 @@ else p = s + Ustrlen(s); while (p > s && isspace(p[-1])) p--; -*p = 0; +s = string_copyn(s, p-s); /* It seems that CC:Mail is braindead, and assumes that the greeting message is all contained in a single IP packet. The original code wrote out the @@ -3195,7 +3218,7 @@ which sometimes uses smtp_printf() and sometimes smtp_respond(). */ if (fl.rcpt_in_progress) { - if (rcpt_smtp_response == NULL) + if (!rcpt_smtp_response) rcpt_smtp_response = string_copy(msg); else if (fl.rcpt_smtp_response_same && Ustrcmp(rcpt_smtp_response, msg) != 0) @@ -3210,7 +3233,7 @@ not the whole MAIL/RCPT/DATA response set. */ for (;;) { uschar *nl = Ustrchr(msg, '\n'); - if (nl == NULL) + if (!nl) { smtp_printf("%.3s%c%.*s%s\r\n", !final, code, final ? ' ':'-', esclen, esc, msg); return; @@ -3266,27 +3289,26 @@ void smtp_message_code(uschar **code, int *codelen, uschar **msg, uschar **log_msg, BOOL check_valid) { -int n; -int ovector[3]; - -if (!msg || !*msg) return; +uschar * match; +int len; -if ((n = pcre_exec(regex_smtp_code, NULL, CS *msg, Ustrlen(*msg), 0, - PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int))) < 0) return; +if (!msg || !*msg || !regex_match(regex_smtp_code, *msg, -1, &match)) + return; +len = Ustrlen(match); if (check_valid && (*msg)[0] != (*code)[0]) { log_write(0, LOG_MAIN|LOG_PANIC, "configured error code starts with " "incorrect digit (expected %c) in \"%s\"", (*code)[0], *msg); - if (log_msg != NULL && *log_msg == *msg) - *log_msg = string_sprintf("%s %s", *code, *log_msg + ovector[1]); + if (log_msg && *log_msg == *msg) + *log_msg = string_sprintf("%s %s", *code, *log_msg + len); } else { *code = *msg; - *codelen = ovector[1]; /* Includes final space */ + *codelen = len; /* Includes final space */ } -*msg += ovector[1]; /* Chop the code off the message */ +*msg += len; /* Chop the code off the message */ return; } @@ -3748,11 +3770,17 @@ smtp_respond(code, len, TRUE, user_msg); 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 variables as empty. Initialize $0 empty and set it as the only set numerical variable. The authenticator may set $auth @@ -3773,6 +3801,7 @@ rc = (au->info->servercode)(au, smtp_cmd_data); 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 */ +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 @@ -3805,7 +3834,7 @@ switch(rc) 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; } @@ -3818,34 +3847,34 @@ switch(rc) 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; } @@ -3921,16 +3950,8 @@ log_write(L_smtp_connection, LOG_MAIN, "%s closed 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*/ } @@ -3941,6 +3962,8 @@ 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; +if (chunking_state > CHUNKING_OFFERED) + chunking_state = CHUNKING_OFFERED; } @@ -3981,7 +4004,6 @@ int smtp_setup_msg(void) { int done = 0; -int mailmax = -1; BOOL toomany = FALSE; BOOL discarded = FALSE; BOOL last_was_rej_mail = FALSE; @@ -4081,7 +4103,18 @@ while (done <= 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; } @@ -4171,6 +4204,8 @@ while (done <= 0) { 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)) @@ -4178,12 +4213,25 @@ while (done <= 0) 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, @@ -4251,7 +4299,7 @@ while (done <= 0) /* If sender_host_unknown is true, we have got here via the -bs interface, not called from inetd. Otherwise, we are running an IP connection and the host address will be set. If the helo name is the primary name of this - host and we haven't done a reverse lookup, force one now. If helo_required + host and we haven't done a reverse lookup, force one now. If helo_verify_required is set, ensure that the HELO name matches the actual host. If helo_verify is set, do the same check, but softly. */ @@ -4279,19 +4327,19 @@ while (done <= 0) 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, + make some people happy to be able to do it. If helo_verify_required is set, (host matches helo_verify_hosts) failure forces rejection. If helo_verify is set (host matches helo_try_verify_hosts), it does not. This is perhaps now obsolescent, since the verification can now be requested selectively at ACL time. */ f.helo_verified = f.helo_verify_failed = sender_helo_dnssec = FALSE; - if (fl.helo_required || fl.helo_verify) + if (fl.helo_verify_required || fl.helo_verify) { BOOL tempfail = !smtp_verify_helo(); if (!f.helo_verified) { - if (fl.helo_required) + if (fl.helo_verify_required) { smtp_printf("%d %s argument does not match calling host\r\n", FALSE, tempfail? 451 : 550, hello); @@ -4348,13 +4396,13 @@ while (done <= 0) #endif /* Expand the per-connection message count limit option */ - mailmax = expand_mailmax(smtp_accept_max_per_connection); + smtp_mailcmd_max = expand_mailmax(smtp_accept_max_per_connection); smtp_code = US"250 "; /* Default response code plus space*/ 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, @@ -4372,7 +4420,7 @@ while (done <= 0) 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); @@ -4410,12 +4458,12 @@ while (done <= 0) } #ifdef EXPERIMENTAL_ESMTP_LIMITS - if ( (mailmax > 0 || recipients_max) + if ( (smtp_mailcmd_max > 0 || recipients_max) && verify_check_host(&limits_advertise_hosts) == OK) { g = string_fmt_append(g, "%.3s-LIMITS", smtp_code); - if (mailmax > 0) - g = string_fmt_append(g, " MAILMAX=%d", mailmax); + if (smtp_mailcmd_max > 0) + g = string_fmt_append(g, " MAILMAX=%d", smtp_mailcmd_max); if (recipients_max) g = string_fmt_append(g, " RCPTMAX=%d", recipients_max); g = string_catn(g, US"\r\n", 2); @@ -4600,15 +4648,12 @@ while (done <= 0) #endif (void) fwrite(g->s, 1, g->ptr, 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); - } + DEBUG(D_receive) for (const uschar * t, * s = string_from_gstring(g); + 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. */ @@ -4639,15 +4684,16 @@ while (done <= 0) env_mail_type_t * mail_args; /* Sanity check & validate args */ if (!fl.helo_seen) - if (fl.helo_required) + if ( fl.helo_verify_required + || verify_check_host(&hosts_require_helo) == OK) { smtp_printf("503 HELO or EHLO required\r\n", FALSE); log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL from %s: no " "HELO/EHLO given", host_and_ident(FALSE)); break; } - else if (mailmax < 0) - mailmax = expand_mailmax(smtp_accept_max_per_connection); + else if (smtp_mailcmd_max < 0) + smtp_mailcmd_max = expand_mailmax(smtp_accept_max_per_connection); if (sender_address) { @@ -4666,7 +4712,7 @@ while (done <= 0) /* Check to see if the limit for messages per connection would be exceeded by accepting further messages. */ - if (mailmax > 0 && smtp_mailcmd_count > mailmax) + if (smtp_mailcmd_max > 0 && smtp_mailcmd_count > smtp_mailcmd_max) { smtp_printf("421 too many messages in this connection\r\n", FALSE); log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL command %s: too many " @@ -5064,7 +5110,7 @@ while (done <= 0) 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) { @@ -5083,7 +5129,7 @@ while (done <= 0) /* 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"); @@ -5374,7 +5420,7 @@ while (done <= 0) #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); @@ -5424,7 +5470,7 @@ while (done <= 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.cctx.sock < 0) + if (!acl_smtp_predata && cutthrough.cctx.sock < 0) rc = OK; else { @@ -5577,7 +5623,7 @@ while (done <= 0) 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"); @@ -5811,7 +5857,7 @@ while (done <= 0) 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) { @@ -5888,6 +5934,7 @@ while (done <= 0) { 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)); @@ -6016,7 +6063,6 @@ while (done <= 0) 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 */