diff options
author | Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de> | 2021-03-30 22:03:49 +0200 |
---|---|---|
committer | Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de> | 2021-04-28 00:40:44 +0200 |
commit | 4daba4bec729a57fb0863af786a1395e70794c76 (patch) | |
tree | 8438c9fe1ab4e9efc882e01740cbe93da223d8e0 | |
parent | 33d4c87653ddbbea9fd8cb8eb2ff78c149850006 (diff) | |
download | exim4-4daba4bec729a57fb0863af786a1395e70794c76.tar.gz |
CVE-2020-28026: Line truncation and injection in spool_read_header()
This also fixes:
2/ In src/spool_in.c:
462 while ( (len = Ustrlen(big_buffer)) == big_buffer_size-1
463 && big_buffer[len-1] != '\n'
464 )
465 { /* buffer not big enough for line; certs make this possible */
466 uschar * buf;
467 if (big_buffer_size >= BIG_BUFFER_SIZE*4) goto SPOOL_READ_ERROR;
468 buf = store_get_perm(big_buffer_size *= 2, FALSE);
469 memcpy(buf, big_buffer, --len);
The --len in memcpy() chops off a useful byte (we know for sure that
big_buffer[len-1] is not a '\n' because we entered the while loop).
Based on a patch done by Qualys.
(cherry picked from commit f0c307458e1ee81abbe7ed2d4a8d16b5cbd8a799)
-rw-r--r-- | src/src/spool_in.c | 48 |
1 files changed, 32 insertions, 16 deletions
diff --git a/src/src/spool_in.c b/src/src/spool_in.c index 1433123c3..e67d0f4f6 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -304,6 +304,35 @@ dsn_ret = 0; dsn_envid = NULL; } +static void * +fgets_big_buffer(FILE *fp) +{ +int len = 0; + +big_buffer[0] = 0; +if (Ufgets(big_buffer, big_buffer_size, fp) == NULL) return NULL; + +while ((len = Ustrlen(big_buffer)) == big_buffer_size-1 + && big_buffer[len-1] != '\n') + { + uschar *newbuffer; + int newsize; + + if (big_buffer_size >= BIG_BUFFER_SIZE * 4) return NULL; + newsize = big_buffer_size * 2; + newbuffer = store_get_perm(newsize, FALSE); + memcpy(newbuffer, big_buffer, len); + + big_buffer = newbuffer; + big_buffer_size = newsize; + if (Ufgets(big_buffer + len, big_buffer_size - len, fp) == NULL) return NULL; + } + +if (len <= 0 || big_buffer[len-1] != '\n') return NULL; +return big_buffer; +} + + /************************************************* * Read spool header file * @@ -452,26 +481,13 @@ If the line starts with "--" the content of the variable is tainted. */ for (;;) { - int len; BOOL tainted; uschar * var; const uschar * p; - if (Ufgets(big_buffer, big_buffer_size, fp) == NULL) goto SPOOL_READ_ERROR; + if (fgets_big_buffer(fp) == NULL) goto SPOOL_READ_ERROR; if (big_buffer[0] != '-') break; - while ( (len = Ustrlen(big_buffer)) == big_buffer_size-1 - && big_buffer[len-1] != '\n' - ) - { /* buffer not big enough for line; certs make this possible */ - uschar * buf; - if (big_buffer_size >= BIG_BUFFER_SIZE*4) goto SPOOL_READ_ERROR; - buf = store_get_perm(big_buffer_size *= 2, FALSE); - memcpy(buf, big_buffer, len); - big_buffer = buf; - if (Ufgets(big_buffer+len, big_buffer_size-len, fp) == NULL) - goto SPOOL_READ_ERROR; - } - big_buffer[len-1] = 0; + big_buffer[Ustrlen(big_buffer)-1] = 0; tainted = big_buffer[1] == '-'; var = big_buffer + (tainted ? 2 : 1); @@ -764,7 +780,7 @@ for (recipients_count = 0; recipients_count < rcount; recipients_count++) uschar *errors_to = NULL; uschar *p; - if (Ufgets(big_buffer, big_buffer_size, fp) == NULL) goto SPOOL_READ_ERROR; + if (fgets_big_buffer(fp) == NULL) goto SPOOL_READ_ERROR; nn = Ustrlen(big_buffer); if (nn < 2) goto SPOOL_FORMAT_ERROR; |