diff -urN ../exim-4.88.orig/src/auths/get_data.c ./src/auths/get_data.c --- ../exim-4.88.orig/src/auths/get_data.c 2016-12-18 16:02:28.000000000 +0200 +++ ./src/auths/get_data.c 2017-02-25 00:22:20.000000000 +0200 @@ -31,7 +31,7 @@ int c; int p = 0; smtp_printf("334 %s\r\n", b64encode(challenge, challen)); -while ((c = receive_getc()) != '\n' && c != EOF) +while ((c = receive_getc(GETC_BUFFER_UNLIMITED)) != '\n' && c != EOF) { if (p >= big_buffer_size - 1) return BAD64; big_buffer[p++] = c; diff -urN ../exim-4.88.orig/src/auths/get_no64_data.c ./src/auths/get_no64_data.c --- ../exim-4.88.orig/src/auths/get_no64_data.c 2016-12-18 16:02:28.000000000 +0200 +++ ./src/auths/get_no64_data.c 2017-02-25 00:22:20.000000000 +0200 @@ -32,7 +32,7 @@ int c; int p = 0; smtp_printf("334 %s\r\n", challenge); -while ((c = receive_getc()) != '\n' && c != EOF) +while ((c = receive_getc(GETC_BUFFER_UNLIMITED)) != '\n' && c != EOF) { if (p >= big_buffer_size - 1) return BAD64; big_buffer[p++] = c; diff -urN ../exim-4.88.orig/src/dkim.c ./src/dkim.c --- ../exim-4.88.orig/src/dkim.c 2016-12-18 16:02:28.000000000 +0200 +++ ./src/dkim.c 2017-02-25 00:30:10.219481000 +0200 @@ -18,6 +18,7 @@ pdkim_ctx *dkim_verify_ctx = NULL; pdkim_signature *dkim_signatures = NULL; pdkim_signature *dkim_cur_sig = NULL; +static BOOL dkim_collect_error = FALSE; static int dkim_exim_query_dns_txt(char *name, char *answer) @@ -87,6 +88,7 @@ dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing); dkim_collect_input = !!dkim_verify_ctx; +dkim_collect_error = FALSE; /* Start feed up with any cached data */ receive_get_cache(); @@ -106,6 +108,7 @@ { log_write(0, LOG_MAIN, "DKIM: validation error: %.100s", pdkim_errstr(rc)); + dkim_collect_error = TRUE; dkim_collect_input = FALSE; } store_pool = dkim_verify_oldpool; @@ -127,11 +130,7 @@ dkim_signatures = NULL; -/* If we have arrived here with dkim_collect_input == FALSE, it -means there was a processing error somewhere along the way. -Log the incident and disable futher verification. */ - -if (!dkim_collect_input) +if (dkim_collect_error) { log_write(0, LOG_MAIN, "DKIM: Error while running this message through validation," diff -urN ../exim-4.88.orig/src/functions.h ./src/functions.h --- ../exim-4.88.orig/src/functions.h 2016-12-18 16:02:28.000000000 +0200 +++ ./src/functions.h 2017-02-25 00:22:20.000000000 +0200 @@ -55,7 +55,7 @@ extern int tls_feof(void); extern int tls_ferror(void); extern void tls_free_cert(void **); -extern int tls_getc(void); +extern int tls_getc(unsigned); extern void tls_get_cache(void); extern int tls_import_cert(const uschar *, void **); extern int tls_read(BOOL, uschar *, size_t); @@ -101,7 +101,7 @@ extern uschar *b64encode(uschar *, int); extern int b64decode(uschar *, uschar **); -extern int bdat_getc(void); +extern int bdat_getc(unsigned); extern void bits_clear(unsigned int *, size_t, int *); extern void bits_set(unsigned int *, size_t, int *); @@ -395,7 +395,7 @@ extern BOOL smtp_get_interface(uschar *, int, address_item *, uschar **, uschar *); extern BOOL smtp_get_port(uschar *, address_item *, int *, uschar *); -extern int smtp_getc(void); +extern int smtp_getc(unsigned); extern void smtp_get_cache(void); extern int smtp_handle_acl_fail(int, int, uschar *, uschar *); extern void smtp_log_no_mail(void); @@ -421,7 +421,7 @@ extern int spool_open_temp(uschar *); extern int spool_read_header(uschar *, BOOL, BOOL); extern int spool_write_header(uschar *, int, uschar **); -extern int stdin_getc(void); +extern int stdin_getc(unsigned); extern int stdin_feof(void); extern int stdin_ferror(void); extern int stdin_ungetc(int); diff -urN ../exim-4.88.orig/src/globals.c ./src/globals.c --- ../exim-4.88.orig/src/globals.c 2016-12-18 16:02:28.000000000 +0200 +++ ./src/globals.c 2017-02-25 00:22:20.000000000 +0200 @@ -183,9 +183,9 @@ stand-alone tests. */ #ifndef STAND_ALONE -int (*lwr_receive_getc)(void) = stdin_getc; +int (*lwr_receive_getc)(unsigned) = stdin_getc; int (*lwr_receive_ungetc)(int) = stdin_ungetc; -int (*receive_getc)(void) = stdin_getc; +int (*receive_getc)(unsigned) = stdin_getc; void (*receive_get_cache)(void)= NULL; int (*receive_ungetc)(int) = stdin_ungetc; int (*receive_feof)(void) = stdin_feof; diff -urN ../exim-4.88.orig/src/globals.h ./src/globals.h --- ../exim-4.88.orig/src/globals.h 2016-12-18 16:02:28.000000000 +0200 +++ ./src/globals.h 2017-02-25 00:22:20.000000000 +0200 @@ -136,9 +136,9 @@ /* Input-reading functions for messages, so we can use special ones for incoming TCP/IP. */ -extern int (*lwr_receive_getc)(void); +extern int (*lwr_receive_getc)(unsigned); extern int (*lwr_receive_ungetc)(int); -extern int (*receive_getc)(void); +extern int (*receive_getc)(unsigned); extern void (*receive_get_cache)(void); extern int (*receive_ungetc)(int); extern int (*receive_feof)(void); diff -urN ../exim-4.88.orig/src/macros.h ./src/macros.h --- ../exim-4.88.orig/src/macros.h 2016-12-18 16:02:28.000000000 +0200 +++ ./src/macros.h 2017-02-25 00:22:20.000000000 +0200 @@ -968,5 +968,9 @@ #define PEER_OFFERED_SIZE BIT(6) #define PEER_OFFERED_CHUNKING BIT(7) +/* Argument for *_getc */ + +#define GETC_BUFFER_UNLIMITED UINT_MAX + /* End of macros.h */ diff -urN ../exim-4.88.orig/src/pdkim/pdkim.c ./src/pdkim/pdkim.c --- ../exim-4.88.orig/src/pdkim/pdkim.c 2016-12-18 16:02:28.000000000 +0200 +++ ./src/pdkim/pdkim.c 2017-02-25 00:22:20.000000000 +0200 @@ -962,6 +962,11 @@ /* DKIM-Signature: headers are added to the verification list */ else { + DEBUG(D_acl) + { + debug_printf("PDKIM >> raw hdr: "); + pdkim_quoteprint(CUS ctx->cur_header, Ustrlen(ctx->cur_header)); + } if (strncasecmp(CCS ctx->cur_header, DKIM_SIGNATURE_HEADERNAME, Ustrlen(DKIM_SIGNATURE_HEADERNAME)) == 0) diff -urN ../exim-4.88.orig/src/receive.c ./src/receive.c --- ../exim-4.88.orig/src/receive.c 2016-12-18 16:02:28.000000000 +0200 +++ ./src/receive.c 2017-02-25 00:22:20.000000000 +0200 @@ -37,7 +37,7 @@ changing the pointer variables.) */ int -stdin_getc(void) +stdin_getc(unsigned lim) { return getc(stdin); } @@ -626,7 +626,7 @@ { register int last_ch = '\n'; - for (; (ch = (receive_getc)()) != EOF; last_ch = ch) + for (; (ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF; last_ch = ch) { if (ch == 0) body_zerocount++; if (last_ch == '\r' && ch != '\n') @@ -668,7 +668,7 @@ ch_state = 1; -while ((ch = (receive_getc)()) != EOF) +while ((ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF) { if (ch == 0) body_zerocount++; switch (ch_state) @@ -786,7 +786,7 @@ int ch; int linelength = 0; -while ((ch = (receive_getc)()) != EOF) +while ((ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF) { if (ch == 0) body_zerocount++; switch (ch_state) @@ -913,7 +913,7 @@ int ch; int linelength = 0; -for (;;) switch (ch = bdat_getc()) +for (;;) switch (ch = bdat_getc(GETC_BUFFER_UNLIMITED)) { case EOF: return END_EOF; case EOD: return END_DOT; @@ -1682,7 +1682,7 @@ for (;;) { - int ch = (receive_getc)(); + int ch = (receive_getc)(GETC_BUFFER_UNLIMITED); /* If we hit EOF on a SMTP connection, it's an error, since incoming SMTP must have a correct "." terminator. */ @@ -1761,10 +1761,10 @@ if (ptr == 0 && ch == '.' && (smtp_input || dot_ends)) { - ch = (receive_getc)(); + ch = (receive_getc)(GETC_BUFFER_UNLIMITED); if (ch == '\r') { - ch = (receive_getc)(); + ch = (receive_getc)(GETC_BUFFER_UNLIMITED); if (ch != '\n') { receive_ungetc(ch); @@ -1795,7 +1795,7 @@ if (ch == '\r') { - ch = (receive_getc)(); + ch = (receive_getc)(GETC_BUFFER_UNLIMITED); if (ch == '\n') { if (first_line_ended_crlf == TRUE_UNSET) first_line_ended_crlf = TRUE; @@ -1890,7 +1890,7 @@ if (ch != EOF) { - int nextch = (receive_getc)(); + int nextch = (receive_getc)(GETC_BUFFER_UNLIMITED); if (nextch == ' ' || nextch == '\t') { next->text[ptr++] = nextch; @@ -4024,7 +4024,7 @@ if (select(fileno(smtp_in) + 1, &select_check, NULL, NULL, &tv) != 0) { - int c = (receive_getc)(); + int c = (receive_getc)(GETC_BUFFER_UNLIMITED); if (c != EOF) (receive_ungetc)(c); else { smtp_notquit_exit(US"connection-lost", NULL, NULL); diff -urN ../exim-4.88.orig/src/smtp_in.c ./src/smtp_in.c --- ../exim-4.88.orig/src/smtp_in.c 2017-02-24 23:35:57.000000000 +0200 +++ ./src/smtp_in.c 2017-02-25 00:39:58.647113000 +0200 @@ -44,11 +44,11 @@ we need room to handle large base64-encoded AUTHs for GSSAPI. */ -#define smtp_cmd_buffer_size 16384 +#define SMTP_CMD_BUFFER_SIZE 16384 /* Size of buffer for reading SMTP incoming packets */ -#define in_buffer_size 8192 +#define IN_BUFFER_SIZE 8192 /* Structure for SMTP command list */ @@ -301,7 +301,7 @@ /* forward declarations */ int bdat_ungetc(int ch); -static int smtp_read_command(BOOL check_sync); +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 **); static void smtp_rset_handler(void); @@ -315,12 +315,12 @@ handler is set appropriately by the calling function. This function is not used after a connection has negotated itself into an TLS/SSL state. -Arguments: none +Arguments: lim Maximum amount to read/buffer Returns: the next character or EOF */ int -smtp_getc(void) +smtp_getc(unsigned lim) { if (smtp_inptr >= smtp_inend) { @@ -328,7 +328,10 @@ if (!smtp_out) return EOF; fflush(smtp_out); if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout); - rc = read(fileno(smtp_in), smtp_inbuffer, in_buffer_size); + + /* 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) @@ -376,23 +379,26 @@ Placed here due to the correlation with the above smtp_getc(), which it wraps, and also by the need to do smtp command/response handling. -Arguments: none +Arguments: lim (ignored) Returns: the next character or ERR, EOD or EOF */ int -bdat_getc(void) +bdat_getc(unsigned lim) { uschar * user_msg = NULL; uschar * log_msg; for(;;) { - if (chunking_data_left-- > 0) - return lwr_receive_getc(); + if (chunking_data_left > 0) + return lwr_receive_getc(chunking_data_left--); receive_getc = lwr_receive_getc; receive_ungetc = lwr_receive_ungetc; +#ifndef DISABLE_DKIM + dkim_collect_input = FALSE; +#endif /* If not the last, ack the received chunk. The last response is delayed until after the data ACL decides on it */ @@ -405,21 +411,22 @@ return EOD; } - chunking_state = CHUNKING_OFFERED; smtp_printf("250 %u byte chunk received\r\n", chunking_datasize); + chunking_state = CHUNKING_OFFERED; + DEBUG(D_receive) debug_printf("chunking state %d\n", (int)chunking_state); /* Expect another BDAT cmd from input. RFC 3030 says nothing about QUIT, RSET or NOOP but handling them seems obvious */ next_cmd: - switch(smtp_read_command(TRUE)) + switch(smtp_read_command(TRUE, 1)) { default: (void) synprot_error(L_smtp_protocol_error, 503, NULL, US"only BDAT permissible after non-LAST BDAT"); repeat_until_rset: - switch(smtp_read_command(TRUE)) + switch(smtp_read_command(TRUE, 1)) { case QUIT_CMD: smtp_quit_handler(&user_msg, &log_msg); /*FALLTHROUGH */ case EOF_CMD: return EOF; @@ -471,6 +478,9 @@ receive_getc = bdat_getc; receive_ungetc = bdat_ungetc; +#ifndef DISABLE_DKIM + dkim_collect_input = TRUE; +#endif break; /* to top of main loop */ } } @@ -480,15 +490,18 @@ static void bdat_flush_data(void) { -while (chunking_data_left-- > 0) - if (lwr_receive_getc() < 0) +while (chunking_data_left > 0) + if (lwr_receive_getc(chunking_data_left--) < 0) break; receive_getc = lwr_receive_getc; receive_ungetc = lwr_receive_ungetc; if (chunking_state != CHUNKING_LAST) + { chunking_state = CHUNKING_OFFERED; + DEBUG(D_receive) debug_printf("chunking state %d\n", (int)chunking_state); + } } @@ -1126,13 +1139,14 @@ return when it runs. Arguments: - check_sync if TRUE, check synchronization rules if global option is TRUE + check_sync if TRUE, check synchronization rules if global option is TRUE + buffer_lim maximum to buffer in lower layer Returns: a code identifying the command (enumerated above) */ static int -smtp_read_command(BOOL check_sync) +smtp_read_command(BOOL check_sync, unsigned buffer_lim) { int c; int ptr = 0; @@ -1141,9 +1155,9 @@ os_non_restarting_signal(SIGALRM, command_timeout_handler); -while ((c = (receive_getc)()) != '\n' && c != EOF) +while ((c = (receive_getc)(buffer_lim)) != '\n' && c != EOF) { - if (ptr >= smtp_cmd_buffer_size) + if (ptr >= SMTP_CMD_BUFFER_SIZE) { os_non_restarting_signal(SIGALRM, sigalrm_handler); return OTHER_CMD; @@ -1301,7 +1315,7 @@ rc = select(fd + 1, (SELECT_ARG2_TYPE *)&fds, NULL, NULL, &tzero); if (rc <= 0) return TRUE; /* Not ready to read */ -rc = smtp_getc(); +rc = smtp_getc(GETC_BUFFER_UNLIMITED); if (rc < 0) return TRUE; /* End of file or error */ smtp_ungetc(rc); @@ -1337,7 +1351,7 @@ receive_swallow_smtp(); smtp_printf("421 %s\r\n", message); -for (;;) switch(smtp_read_command(FALSE)) +for (;;) switch(smtp_read_command(FALSE, GETC_BUFFER_UNLIMITED)) { case EOF_CMD: return; @@ -1781,7 +1795,7 @@ uschar *recipient = NULL; int start, end, sender_domain, recipient_domain; - switch(smtp_read_command(FALSE)) + switch(smtp_read_command(FALSE, GETC_BUFFER_UNLIMITED)) { /* The HELO/EHLO commands set sender_address_helo if they have valid data; otherwise they are ignored, except that they do @@ -2043,12 +2057,12 @@ /* Allow for trailing 0 in the command and data buffers. */ -if (!(smtp_cmd_buffer = US malloc(2*smtp_cmd_buffer_size + 2))) +if (!(smtp_cmd_buffer = US malloc(2*SMTP_CMD_BUFFER_SIZE + 2))) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "malloc() failed for SMTP command buffer"); smtp_cmd_buffer[0] = 0; -smtp_data_buffer = smtp_cmd_buffer + smtp_cmd_buffer_size + 1; +smtp_data_buffer = smtp_cmd_buffer + SMTP_CMD_BUFFER_SIZE + 1; /* For batched input, the protocol setting can be overridden from the command line by a trusted caller. */ @@ -2068,7 +2082,7 @@ /* Set up the buffer for inputting using direct read() calls, and arrange to call the local functions instead of the standard C ones. */ -if (!(smtp_inbuffer = (uschar *)malloc(in_buffer_size))) +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; @@ -3557,7 +3571,7 @@ US &off, sizeof(off)); #endif - switch(smtp_read_command(TRUE)) + switch(smtp_read_command(TRUE, GETC_BUFFER_UNLIMITED)) { /* The AUTH command is not permitted to occur inside a transaction, and may occur successfully only once per connection. Actually, that isn't quite @@ -4757,14 +4771,16 @@ chunking_state = strcmpic(smtp_cmd_data+n, US"LAST") == 0 ? CHUNKING_LAST : CHUNKING_ACTIVE; chunking_data_left = chunking_datasize; + DEBUG(D_receive) debug_printf("chunking state %d, %d bytes\n", + (int)chunking_state, chunking_data_left); + DEBUG(D_receive) debug_printf("chunking state %d, %d bytes\n", + (int)chunking_state, chunking_data_left); lwr_receive_getc = receive_getc; lwr_receive_ungetc = receive_ungetc; receive_getc = bdat_getc; receive_ungetc = bdat_ungetc; - DEBUG(D_any) - debug_printf("chunking state %d\n", (int)chunking_state); goto DATA_BDAT; } @@ -4980,7 +4996,7 @@ It seems safest to just wipe away the content rather than leave it as a target to jump to. */ - memset(smtp_inbuffer, 0, in_buffer_size); + memset(smtp_inbuffer, 0, IN_BUFFER_SIZE); /* Attempt to start up a TLS session, and if successful, discard all knowledge that was obtained previously. At least, that's what the RFC says, @@ -5034,7 +5050,7 @@ set, but we must still reject all incoming commands. */ DEBUG(D_tls) debug_printf("TLS failed to start\n"); - while (done <= 0) switch(smtp_read_command(FALSE)) + 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", @@ -5322,8 +5338,8 @@ case BADSYN_CMD: SYNC_FAILURE: - if (smtp_inend >= smtp_inbuffer + in_buffer_size) - smtp_inend = smtp_inbuffer + in_buffer_size - 1; + 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; smtp_inptr[c] = 0; diff -urN ../exim-4.88.orig/src/tls-gnu.c ./src/tls-gnu.c --- ../exim-4.88.orig/src/tls-gnu.c 2016-12-18 16:02:28.000000000 +0200 +++ ./src/tls-gnu.c 2017-02-25 00:22:20.000000000 +0200 @@ -2158,12 +2158,12 @@ This feeds DKIM and should be used for all message-body reads. -Arguments: none +Arguments: lim Maximum amount to read/bufffer Returns: the next character or EOF */ int -tls_getc(void) +tls_getc(unsigned lim) { exim_gnutls_state_st *state = &state_server; if (state->xfer_buffer_lwm >= state->xfer_buffer_hwm) @@ -2175,7 +2175,7 @@ if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout); inbytes = gnutls_record_recv(state->session, state->xfer_buffer, - ssl_xfer_buffer_size); + MIN(ssl_xfer_buffer_size, lim)); alarm(0); /* Timeouts do not get this far; see command_timeout_handler(). @@ -2213,7 +2213,7 @@ state->tlsp->peercert = NULL; state->tlsp->peerdn = NULL; - return smtp_getc(); + return smtp_getc(lim); } /* Handle genuine errors */ diff -urN ../exim-4.88.orig/src/tls-openssl.c ./src/tls-openssl.c --- ../exim-4.88.orig/src/tls-openssl.c 2016-12-18 16:02:28.000000000 +0200 +++ ./src/tls-openssl.c 2017-02-25 00:22:20.000000000 +0200 @@ -2360,14 +2360,14 @@ /* This gets the next byte from the TLS input buffer. If the buffer is empty, it refills the buffer via the SSL reading function. -Arguments: none +Arguments: lim Maximum amount to read/buffer Returns: the next character or EOF Only used by the server-side TLS. */ int -tls_getc(void) +tls_getc(unsigned lim) { if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm) { @@ -2378,7 +2378,8 @@ ssl_xfer_buffer, ssl_xfer_buffer_size); if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout); - inbytes = SSL_read(server_ssl, CS ssl_xfer_buffer, ssl_xfer_buffer_size); + inbytes = SSL_read(server_ssl, CS ssl_xfer_buffer, + MIN(ssl_xfer_buffer_size, lim)); error = SSL_get_error(server_ssl, inbytes); alarm(0); @@ -2405,7 +2406,7 @@ tls_in.peerdn = NULL; tls_in.sni = NULL; - return smtp_getc(); + return smtp_getc(lim); } /* Handle genuine errors */