Chunking: do not treat the first lonely dot special. CVE-2017-16944, Bug 2201
[exim.git] / src / src / receive.c
index 5125a4f47525fbb6af01d8b0add9cd255daea2df..2812ea2c87743f5db2171cbf86e00c6c24d08a9e 100644 (file)
@@ -906,7 +906,8 @@ a cut-down version of the state-machine above; we don't need to do leading-dot
 detection and unstuffing.
 
 Arguments:
-  fout      a FILE to which to write the message; NULL if skipping
+  fout      a FILE to which to write the message; NULL if skipping;
+            must be open for both writing and reading.
 
 Returns:    One of the END_xxx values indicating why it stopped reading
 */
@@ -937,8 +938,11 @@ for(;;)
       that would need to be duplicated here.  So we simply do some ungetc
       trickery.
       */
-      fseek(fout, -1, SEEK_CUR);
-      if (fgetc(fout) == '\n') return END_DOT;
+      if (fout)
+       {
+       if (fseek(fout, -1, SEEK_CUR) < 0)      return END_PROTOCOL;
+       if (fgetc(fout) == '\n')                return END_DOT;
+       }
 
       if (linelength == -1)    /* \r already seen (see below) */
         {
@@ -986,7 +990,7 @@ for(;;)
       else
        {
        message_size++;
-       if (fout != NULL && fputc('\n', fout) == EOF) return END_WERROR;
+       if (fout && fputc('\n', fout) == EOF) return END_WERROR;
        (void) cutthrough_put_nl();
        if (ch == '\r') continue;       /* don't write CR */
        ch_state = MID_LINE;
@@ -1768,8 +1772,8 @@ for (;;)
   (and sometimes lunatic messages can have ones that are 100s of K long) we
   call store_release() for strings that have been copied - if the string is at
   the start of a block (and therefore the only thing in it, because we aren't
-  doing any other gets), the block gets freed. We can only do this because we
-  know there are no other calls to store_get() going on. */
+  doing any other gets), the block gets freed. We can only do this release if
+  there were no allocations since the once that we want to free. */
 
   if (ptr >= header_size - 4)
     {
@@ -1778,9 +1782,10 @@ for (;;)
     header_size *= 2;
     if (!store_extend(next->text, oldsize, header_size))
       {
+      BOOL release_ok = store_last_get[store_pool] == next->text;
       uschar *newtext = store_get(header_size);
       memcpy(newtext, next->text, ptr);
-      store_release(next->text);
+      if (release_ok) store_release(next->text);
       next->text = newtext;
       }
     }
@@ -1822,7 +1827,7 @@ for (;;)
   prevent further reading), and break out of the loop, having freed the
   empty header, and set next = NULL to indicate no data line. */
 
-  if (ptr == 0 && ch == '.' && (smtp_input || dot_ends))
+  if (ptr == 0 && ch == '.' && dot_ends)
     {
     ch = (receive_getc)(GETC_BUFFER_UNLIMITED);
     if (ch == '\r')
@@ -2578,8 +2583,9 @@ letter and it is not used internally.
 NOTE: If ever the format of message ids is changed, the regular expression for
 checking that a string is in this format must be updated in a corresponding
 way. It appears in the initializing code in exim.c. The macro MESSAGE_ID_LENGTH
-must also be changed to reflect the correct string length. Then, of course,
-other programs that rely on the message id format will need updating too. */
+must also be changed to reflect the correct string length. The queue-sort code
+needs to know the layout. Then, of course, other programs that rely on the
+message id format will need updating too. */
 
 Ustrncpy(message_id, string_base62((long int)(message_id_tv.tv_sec)), 6);
 message_id[6] = '-';