flonix / rpms / exim

Forked from rpms/exim 3 years ago
Clone
Blob Blame History Raw
diff --git a/src/auths/get_data.c b/src/auths/get_data.c
index f839a01..11bc581 100644
--- a/src/auths/get_data.c
+++ b/src/auths/get_data.c
@@ -31,7 +31,7 @@ auth_get_data(uschar **aptr, uschar *challenge, int challen)
 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 --git a/src/auths/get_no64_data.c b/src/auths/get_no64_data.c
index d3ffe08..71e7139 100644
--- a/src/auths/get_no64_data.c
+++ b/src/auths/get_no64_data.c
@@ -32,7 +32,7 @@ auth_get_no64_data(uschar **aptr, uschar *challenge)
 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 --git a/src/dkim.c b/src/dkim.c
index 70c9547..445d246 100644
--- a/src/dkim.c
+++ b/src/dkim.c
@@ -18,6 +18,7 @@ int dkim_verify_oldpool;
 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 @@ if (dkim_verify_ctx)
 
 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 @@ if (  dkim_collect_input
   {
   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 @@ store_pool = POOL_PERM;
 
 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 --git a/src/functions.h b/src/functions.h
index 04d9410..9c60090 100644
--- a/src/functions.h
+++ b/src/functions.h
@@ -55,7 +55,7 @@ extern int     tls_export_cert(uschar *, size_t, void *);
 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 int     auth_xtextdecode(uschar *, uschar **);
 
 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 uschar *smtp_get_connection_info(void);
 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_datafile(uschar *);
 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 --git a/src/globals.c b/src/globals.c
index c722059..649335f 100644
--- a/src/globals.c
+++ b/src/globals.c
@@ -187,9 +187,9 @@ incoming TCP/IP. The defaults use stdin. We never need these for any
 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 --git a/src/globals.h b/src/globals.h
index e3dd507..344f8ef 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -141,9 +141,9 @@ extern uschar  *dsn_advertise_hosts;   /* host for which TLS is advertised */
 /* 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 --git a/src/macros.h b/src/macros.h
index 1b7cf4a..c8957d8 100644
--- a/src/macros.h
+++ b/src/macros.h
@@ -968,5 +968,9 @@ enum { FILTER_UNSET, FILTER_FORWARD, FILTER_EXIM, FILTER_SIEVE };
 #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 --git a/src/pdkim/pdkim.c b/src/pdkim/pdkim.c
index 7bfcdf4..bcc3f09 100644
--- a/src/pdkim/pdkim.c
+++ b/src/pdkim/pdkim.c
@@ -962,6 +962,11 @@ if (ctx->flags & PDKIM_MODE_SIGN)
 /* 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 --git a/src/receive.c b/src/receive.c
index e535876..9155cf1 100644
--- a/src/receive.c
+++ b/src/receive.c
@@ -37,7 +37,7 @@ the file. (When SMTP input is occurring, different functions are used by
 changing the pointer variables.) */
 
 int
-stdin_getc(void)
+stdin_getc(unsigned lim)
 {
 return getc(stdin);
 }
@@ -626,7 +626,7 @@ if (!dot_ends)
   {
   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 @@ if (!dot_ends)
 
 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_state = 0;
 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 @@ read_message_bdat_smtp(FILE *fout)
 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 @@ next->text. */
 
 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 @@ for (;;)
 
   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 @@ for (;;)
 
   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 @@ for (;;)
 
   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 (smtp_input && sender_host_address != NULL && !sender_host_notsocket &&
 
   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 --git a/src/smtp_in.c b/src/smtp_in.c
index 1484861..82900d9 100644
--- a/src/smtp_in.c
+++ b/src/smtp_in.c
@@ -44,11 +44,11 @@ The maximum size of a Kerberos ticket under Windows 2003 is 12000 bytes, and
 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 @@ static int     smtp_had_error;
 
 /* 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 @@ 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 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_inptr >= smtp_inend)
   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 @@ to handle the BDAT command/response.
 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 @@ for(;;)
     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;
@@ -458,6 +465,8 @@ next_cmd:
       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);
 
       if (chunking_datasize == 0)
 	if (chunking_state == CHUNKING_LAST)
@@ -471,6 +480,9 @@ next_cmd:
 
       receive_getc = bdat_getc;
       receive_ungetc = bdat_ungetc;
+#ifndef DISABLE_DKIM
+      dkim_collect_input = TRUE;
+#endif
       break;	/* to top of main loop */
       }
     }
@@ -480,15 +492,18 @@ next_cmd:
 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 +1141,14 @@ signal handler that closes down the session on a timeout. Control does not
 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 +1157,9 @@ BOOL hadnull = FALSE;
 
 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 +1317,7 @@ tzero.tv_usec = 0;
 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 +1353,7 @@ if (smtp_in == NULL || smtp_batched_input) return;
 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 +1797,7 @@ while (done <= 0)
   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
@@ -2040,12 +2056,12 @@ acl_var_c = NULL;
 
 /* 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. */
@@ -2065,7 +2081,7 @@ else
 /* 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;
@@ -3550,7 +3566,7 @@ while (done <= 0)
 	    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
@@ -4750,14 +4766,14 @@ while (done <= 0)
       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);
 
       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;
       }
 
@@ -4973,7 +4989,7 @@ while (done <= 0)
     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,
@@ -5027,7 +5043,7 @@ while (done <= 0)
     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",
@@ -5315,8 +5331,8 @@ while (done <= 0)
 
     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 --git a/src/tls-gnu.c b/src/tls-gnu.c
index 10bfaca..181dde4 100644
--- a/src/tls-gnu.c
+++ b/src/tls-gnu.c
@@ -2158,12 +2158,12 @@ Only used by the server-side TLS.
 
 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 (state->xfer_buffer_lwm >= state->xfer_buffer_hwm)
 
   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 @@ if (state->xfer_buffer_lwm >= state->xfer_buffer_hwm)
     state->tlsp->peercert = NULL;
     state->tlsp->peerdn = NULL;
 
-    return smtp_getc();
+    return smtp_getc(lim);
     }
 
   /* Handle genuine errors */
diff --git a/src/tls-openssl.c b/src/tls-openssl.c
index d9426ac..0ac7d03 100644
--- a/src/tls-openssl.c
+++ b/src/tls-openssl.c
@@ -2360,14 +2360,14 @@ return OK;
 /* 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 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm)
     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 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm)
     tls_in.peerdn = NULL;
     tls_in.sni = NULL;
 
-    return smtp_getc();
+    return smtp_getc(lim);
     }
 
   /* Handle genuine errors */