X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/90341c71c19c82ba8b1cbf4d1693940b8bb8f70b..40525d07a858c90293bc09188fb539a1cec3f8aa:/src/src/smtp_in.c diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index bd83de045..8832908f3 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -304,7 +304,6 @@ static int smtp_had_error; /* forward declarations */ -int bdat_ungetc(int ch); static int smtp_read_command(BOOL check_sync, unsigned buffer_lim); static int synprot_error(int type, int code, uschar *data, uschar *errmess); static void smtp_quit_handler(uschar **, uschar **); @@ -344,6 +343,9 @@ if (!smtp_enforce_sync || sender_host_address == NULL || sender_host_notsocket || tls_in.active >= 0) return TRUE; +if (smtp_inptr < smtp_inend) + return FALSE; + fd = fileno(smtp_in); FD_ZERO(&fds); FD_SET(fd, &fds); @@ -401,6 +403,44 @@ log_write(L_smtp_incomplete_transaction, LOG_MAIN|LOG_SENDER|LOG_RECIPIENTS, +/* Refill the buffer, and notify DKIM verification code. +Return false for error or EOF. +*/ + +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); + +/* Limit amount read, so non-message data is not fed to DKIM */ + +rc = read(fileno(smtp_in), smtp_inbuffer, MIN(IN_BUFFER_SIZE, lim)); +save_errno = errno; +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) + { + smtp_had_error = save_errno; + smtp_read_error = string_copy_malloc( + string_sprintf(" (error: %s)", strerror(save_errno))); + } + else smtp_had_eof = 1; + return FALSE; + } +#ifndef DISABLE_DKIM +dkim_exim_verify_feed(smtp_inbuffer, rc); +#endif +smtp_inend = smtp_inbuffer + rc; +smtp_inptr = smtp_inbuffer; +return TRUE; +} + /************************************************* * SMTP version of getc() * *************************************************/ @@ -418,39 +458,28 @@ int smtp_getc(unsigned lim) { if (smtp_inptr >= smtp_inend) - { - int rc, save_errno; - if (!smtp_out) return 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 */ - - rc = read(fileno(smtp_in), smtp_inbuffer, MIN(IN_BUFFER_SIZE, lim)); - save_errno = errno; - 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) - { - smtp_had_error = save_errno; - smtp_read_error = string_copy_malloc( - string_sprintf(" (error: %s)", strerror(save_errno))); - } - else smtp_had_eof = 1; + if (!smtp_refill(lim)) return EOF; - } -#ifndef DISABLE_DKIM - dkim_exim_verify_feed(smtp_inbuffer, rc); -#endif - smtp_inend = smtp_inbuffer + rc; - smtp_inptr = smtp_inbuffer; - } return *smtp_inptr++; } +uschar * +smtp_getbuf(unsigned * len) +{ +unsigned size; +uschar * buf; + +if (smtp_inptr >= smtp_inend) + if (!smtp_refill(*len)) + { *len = 0; return NULL; } + +if ((size = smtp_inend - smtp_inptr) > *len) size = *len; +buf = smtp_inptr; +smtp_inptr += size; +*len = size; +return buf; +} + void smtp_get_cache(void) { @@ -486,12 +515,18 @@ uschar * log_msg; for(;;) { +#ifndef DISABLE_DKIM + BOOL dkim_save; +#endif + if (chunking_data_left > 0) return lwr_receive_getc(chunking_data_left--); receive_getc = lwr_receive_getc; + receive_getbuf = lwr_receive_getbuf; receive_ungetc = lwr_receive_ungetc; #ifndef DISABLE_DKIM + dkim_save = dkim_collect_input; dkim_collect_input = FALSE; #endif @@ -500,12 +535,15 @@ for(;;) if (!pipelining_advertised && !check_sync()) { + unsigned n = smtp_inend - smtp_inptr; + if (n > 32) n = 32; + incomplete_transaction_log(US"sync failure"); log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol synchronization error " "(next input sent too soon: pipelining was not advertised): " "rejected \"%s\" %s next input=\"%s\"", smtp_cmd_buffer, host_and_ident(TRUE), - string_printing(smtp_inptr)); + string_printing(string_copyn(smtp_inptr, n))); (void) synprot_error(L_smtp_protocol_error, 554, NULL, US"SMTP synchronization error"); goto repeat_until_rset; @@ -590,9 +628,10 @@ next_cmd: } receive_getc = bdat_getc; + receive_getbuf = bdat_getbuf; receive_ungetc = bdat_ungetc; #ifndef DISABLE_DKIM - dkim_collect_input = TRUE; + dkim_collect_input = dkim_save; #endif break; /* to top of main loop */ } @@ -600,14 +639,28 @@ next_cmd: } } +uschar * +bdat_getbuf(unsigned * len) +{ +uschar * buf; + +if (chunking_data_left <= 0) + { *len = 0; return NULL; } + +if (*len > chunking_data_left) *len = chunking_data_left; +buf = lwr_receive_getbuf(len); /* Either smtp_getbuf or tls_getbuf */ +chunking_data_left -= *len; +return buf; +} + void bdat_flush_data(void) { -while (chunking_data_left > 0) - if (lwr_receive_getc(chunking_data_left--) < 0) - break; +unsigned n = chunking_data_left; +(void) bdat_getbuf(&n); receive_getc = lwr_receive_getc; +receive_getbuf = lwr_receive_getbuf; receive_ungetc = lwr_receive_ungetc; if (chunking_state != CHUNKING_LAST) @@ -945,43 +998,45 @@ Return the amount read. static int swallow_until_crlf(int fd, uschar *base, int already, int capacity) { - uschar *to = base + already; - uschar *cr; - int have = 0; - int ret; - int last = 0; - - /* For "PROXY UNKNOWN\r\n" we, at time of writing, expect to have read - up through the \r; for the _normal_ case, we haven't yet seen the \r. */ - cr = memchr(base, '\r', already); - if (cr != NULL) - { - if ((cr - base) < already - 1) - { - /* \r and presumed \n already within what we have; probably not - actually proxy protocol, but abort cleanly. */ - return 0; - } - /* \r is last character read, just need one more. */ - last = 1; - } +uschar *to = base + already; +uschar *cr; +int have = 0; +int ret; +int last = 0; - while (capacity > 0) +/* For "PROXY UNKNOWN\r\n" we, at time of writing, expect to have read +up through the \r; for the _normal_ case, we haven't yet seen the \r. */ + +cr = memchr(base, '\r', already); +if (cr != NULL) + { + if ((cr - base) < already - 1) { - do { ret = recv(fd, to, 1, 0); } while (ret == -1 && errno == EINTR); - if (ret == -1) - return -1; - have++; - if (last) - return have; - if (*to == '\r') - last = 1; - capacity--; - to++; + /* \r and presumed \n already within what we have; probably not + actually proxy protocol, but abort cleanly. */ + return 0; } - // reached end without having room for a final newline, abort - errno = EOVERFLOW; - return -1; + /* \r is last character read, just need one more. */ + last = 1; + } + +while (capacity > 0) + { + do { ret = recv(fd, to, 1, 0); } while (ret == -1 && errno == EINTR); + if (ret == -1) + return -1; + have++; + if (last) + return have; + if (*to == '\r') + last = 1; + capacity--; + to++; + } + +/* reached end without having room for a final newline, abort */ +errno = EOVERFLOW; +return -1; } /************************************************* @@ -1089,9 +1144,9 @@ if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, CS &tv, sizeof(tv)) < 0) do { /* The inbound host was declared to be a Proxy Protocol host, so - don't do a PEEK into the data, actually slurp up enough to be - "safe". Can't take it all because TLS-on-connect clients follow - immediately with TLS handshake. */ + don't do a PEEK into the data, actually slurp up enough to be + "safe". Can't take it all because TLS-on-connect clients follow + immediately with TLS handshake. */ ret = recv(fd, &hdr, PROXY_INITIAL_READ, 0); } while (ret == -1 && errno == EINTR); @@ -1117,9 +1172,9 @@ if ((ret == PROXY_INITIAL_READ) && (memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0)) ver = (hdr.v2.ver_cmd & 0xf0) >> 4; /* May 2014: haproxy combined the version and command into one byte to - allow two full bytes for the length field in order to proxy SSL - connections. SSL Proxy is not supported in this version of Exim, but - must still separate values here. */ + allow two full bytes for the length field in order to proxy SSL + connections. SSL Proxy is not supported in this version of Exim, but + must still separate values here. */ if (ver != 0x02) { @@ -1266,7 +1321,7 @@ else if (ret >= 8 && memcmp(hdr.v1.line, "PROXY", 5) == 0) DEBUG(D_receive) debug_printf("Detected PROXYv1 header\n"); DEBUG(D_receive) debug_printf("Bytes read not within PROXY header: %d\n", ret - size); /* Step through the string looking for the required fields. Ensure - strict adherence to required formatting, exit for any error. */ + strict adherence to required formatting, exit for any error. */ p += 5; if (!isspace(*(p++))) { @@ -1881,7 +1936,6 @@ smtp_reset(void *reset_point) recipients_list = NULL; rcpt_count = rcpt_defer_count = rcpt_fail_count = raw_recipients_count = recipients_count = recipients_list_max = 0; -cancel_cutthrough_connection("smtp reset"); message_linecount = 0; message_size = -1; acl_added_headers = NULL; @@ -2012,6 +2066,7 @@ bsmtp_transaction_linecount = receive_linecount; if ((receive_feof)()) return 0; /* Treat EOF as QUIT */ +cancel_cutthrough_connection(TRUE, US"smtp_setup_batch_msg"); smtp_reset(reset_point); /* Reset for start of message */ /* Deal with SMTP commands. This loop is exited by setting done to a POSITIVE @@ -2036,6 +2091,7 @@ while (done <= 0) /* Fall through */ case RSET_CMD: + cancel_cutthrough_connection(TRUE, US"RSET received"); smtp_reset(reset_point); bsmtp_transaction_linecount = receive_linecount; break; @@ -2059,6 +2115,7 @@ while (done <= 0) /* Reset to start of message */ + cancel_cutthrough_connection(TRUE, US"MAIL received"); smtp_reset(reset_point); /* Apply SMTP rewrite */ @@ -2218,6 +2275,19 @@ return done - 2; /* Convert yield values */ +static BOOL +smtp_log_tls_fail(uschar * errstr) +{ +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 */ + +log_write(0, LOG_MAIN, "TLS error on %s %s", conn_info, errstr); +return FALSE; +} + + /************************************************* * Start an SMTP session * *************************************************/ @@ -2311,6 +2381,7 @@ if (!(smtp_inbuffer = (uschar *)malloc(IN_BUFFER_SIZE))) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "malloc() failed for SMTP input buffer"); receive_getc = smtp_getc; +receive_getbuf = smtp_getbuf; receive_get_cache = smtp_get_cache; receive_ungetc = smtp_ungetc; receive_feof = smtp_feof; @@ -2706,8 +2777,8 @@ if (check_proxy_protocol_host()) smtps port for use with older style SSL MTAs. */ #ifdef SUPPORT_TLS - if (tls_in.on_connect && tls_server_start(tls_require_ciphers) != OK) - return FALSE; + if (tls_in.on_connect && tls_server_start(tls_require_ciphers, &user_msg) != OK) + return smtp_log_tls_fail(user_msg); #endif /* Run the connect ACL if it exists */ @@ -2798,10 +2869,13 @@ this synchronisation check is disabled. */ if (!check_sync()) { + unsigned n = smtp_inend - smtp_inptr; + if (n > 32) n = 32; + log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol " "synchronization error (input sent without waiting for greeting): " "rejected connection from %s input=\"%s\"", host_and_ident(TRUE), - string_printing(smtp_inptr)); + string_printing(string_copyn(smtp_inptr, n))); smtp_printf("554 SMTP synchronization error\r\n"); return FALSE; } @@ -4234,6 +4308,7 @@ while (done <= 0) : pnormal) + (tls_in.active >= 0 ? pcrpted : 0) ]; + cancel_cutthrough_connection(TRUE, US"sent EHLO response"); smtp_reset(reset_point); toomany = FALSE; break; /* HELO/EHLO */ @@ -4288,6 +4363,7 @@ while (done <= 0) /* Reset for start of message - even if this is going to fail, we obviously need to throw away any previous data. */ + cancel_cutthrough_connection(TRUE, US"MAIL received"); smtp_reset(reset_point); toomany = FALSE; sender_data = recipient_data = NULL; @@ -4952,6 +5028,7 @@ while (done <= 0) (int)chunking_state, chunking_data_left); lwr_receive_getc = receive_getc; + lwr_receive_getbuf = receive_getbuf; lwr_receive_ungetc = receive_ungetc; receive_getc = bdat_getc; receive_ungetc = bdat_ungetc; @@ -5143,6 +5220,7 @@ while (done <= 0) do an implied RSET when STARTTLS is received. */ incomplete_transaction_log(US"STARTTLS"); + cancel_cutthrough_connection(TRUE, US"STARTTLS received"); smtp_reset(reset_point); toomany = FALSE; cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = FALSE; @@ -5181,7 +5259,8 @@ while (done <= 0) We must allow for an extra EHLO command and an extra AUTH command after STARTTLS that don't add to the nonmail command count. */ - if ((rc = tls_server_start(tls_require_ciphers)) == OK) + s = NULL; + if ((rc = tls_server_start(tls_require_ciphers, &s)) == OK) { if (!tls_remember_esmtp) helo_seen = esmtp = auth_advertised = pipelining_advertised = FALSE; @@ -5210,11 +5289,13 @@ while (done <= 0) DEBUG(D_tls) debug_printf("TLS active\n"); break; /* Successful STARTTLS */ } + else + (void) smtp_log_tls_fail(s); /* Some local configuration problem was discovered before actually trying to do a TLS handshake; give a temporary error. */ - else if (rc == DEFER) + if (rc == DEFER) { smtp_printf("454 TLS currently unavailable\r\n"); break; @@ -5276,6 +5357,7 @@ while (done <= 0) case RSET_CMD: smtp_rset_handler(); + cancel_cutthrough_connection(TRUE, US"RSET received"); smtp_reset(reset_point); toomany = FALSE; break;