Subject: | Long header lines parsed improperly |
Header lines longer than 1023 characters cause Mail::Box::Parser::C to
parse the header improperly and corrupt the message.
Yes, I realize that nothing is supposed to generate header lines that
long, and yet, there are things that do, and "Be generous in what you
accept" dictates that this could should do its best to parse them
successfully.
The attached patch implements a dynamic buffer for reading message
lines, which is reallocated as needed to make enough space for the
longest line in the mailbox, and freed when the mailbox is freed.
I considered putting an upper limit on the line length to prevent memory
exhaustion DoS attacks against the application running the code, but I
decided not to because there is no length check on folded header lines
in the existing code, which means the DoS potential is already there.
Subject: | Mail-Box-Parser-C-3.006-long-lines.patch |
--- C.xs.orig 2004-09-22 03:31:51.000000000 -0400
+++ C.xs 2013-03-04 08:41:08.083279924 -0500
@@ -12,8 +12,8 @@
#define TRACE_NOTICES 2
#define TRACE_DEBUG 1
-#ifndef MAX_LINE
-#define MAX_LINE 1024
+#ifndef DFLT_LINE
+#define DFLT_LINE 1024
#endif
#ifndef NULL
@@ -53,8 +53,9 @@
int strip_gt;
int keep_line; /* unget line */
- char line[MAX_LINE+2]; /* one more for missing newline on *
- * last line of file on Windows */
+ char * line;
+ int line_buf_size;
+
long line_start;
} Mailbox;
@@ -78,6 +79,9 @@
New(0, box->filename, strlen(filename)+1, char);
strcpy(box->filename, filename);
+ New(0, box->line, DFLT_LINE, char);
+ box->line_buf_size = DFLT_LINE;
+
return box;
}
@@ -144,8 +148,24 @@
}
box->line_start = (long)ftell(box->file);
- if(!fgets(box->line, MAX_LINE, box->file))
- return NULL;
+
+ int already_read = 0;
+ while (fgets(box->line + already_read, box->line_buf_size - already_read,
+ box->file))
+ {
+ int len = strlen(box->line);
+ already_read += len;
+ if (len == box->line_buf_size - 1 && box->line[len-1] != '\n') {
+ int new_size = box->line_buf_size * 2;
+ Renew(box->line, new_size, char);
+ box->line_buf_size = new_size;
+ continue;
+ }
+ break;
+ }
+
+ if (! already_read)
+ return NULL;
if(box->dosmode)
{ int len = strlen(box->line);
@@ -640,6 +660,7 @@
}
Safefree(box->filename);
+ Safefree(box->line);
Safefree(box);