-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Fixed bug #68776 #1273
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixed bug #68776 #1273
Changes from all commits
ec6a881
8fd3c7b
fc0266b
89def69
500b70d
bbdb669
f46fdd7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -221,6 +221,44 @@ void php_mail_log_to_file(char *filename, char *message, size_t message_size TSR | |
} | ||
|
||
|
||
static int php_mail_detect_multiple_crlf(char *hdr) { | ||
/* This function detects multiple/malformed multiple newlines. */ | ||
size_t len; | ||
|
||
if (!hdr) { | ||
return 0; | ||
} | ||
|
||
/* Should not have any newlines at the beginning. */ | ||
/* RFC 2822 2.2. Header Fields */ | ||
if (*hdr < 33 || *hdr > 126 || *hdr == ':') { | ||
return 1; | ||
} | ||
|
||
while(*hdr) { | ||
if (*hdr == '\r') { | ||
if (*(hdr+1) == '\0' || *(hdr+1) == '\r' || (*(hdr+1) == '\n' && (*(hdr+2) == '\0' || *(hdr+2) == '\n' || *(hdr+2) == '\r'))) { | ||
/* Malformed or multiple newlines. */ | ||
return 1; | ||
} else { | ||
hdr += 2; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This may be dangerous if the *hdr string ends on \r. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right. I'm being sloppy here by assuming caller trimmed leading/ending \r, \n. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like this still can hop over \0 if *hdr ends with \r\0. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you. Reasonable anxiety.
but I agree that it would be better to detect such case also. Since php_mail() does not get string length, null in hdr cannot be detected because we cannot change php_mail() signature. Do you have suggestion for the time being? I think current code is OK for released versions, but we may change API for PHP7. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about use this patch for released version? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PHP_FUNCTION(mail) is not enough, as php_mail can be called from other code (it's PHPAPI) so assuming the string passed to it never ends in \r, without even documenting it, is not a good idea. And it string ends in \r, then hdr+=2 hops right over end of the string and proceeds to read into unallocated memory (or allocated for other things, even worse). The check should be added so that it never reads past the end of the string. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, but current API does not pass the length to php_mail(). |
||
} | ||
} else if (*hdr == '\n') { | ||
if (*(hdr+1) == '\0' || *(hdr+1) == '\r' || *(hdr+1) == '\n') { | ||
/* Malformed or multiple newlines. */ | ||
return 1; | ||
} else { | ||
hdr += 2; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here - what if the string ends with \n? |
||
} | ||
} else { | ||
hdr++; | ||
} | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
|
||
/* {{{ php_mail | ||
*/ | ||
PHPAPI int php_mail(char *to, char *subject, char *message, char *headers, char *extra_cmd TSRMLS_DC) | ||
|
@@ -266,6 +304,7 @@ PHPAPI int php_mail(char *to, char *subject, char *message, char *headers, char | |
|
||
efree(tmp); | ||
} | ||
|
||
if (PG(mail_x_header)) { | ||
const char *tmp = zend_get_executed_filename(TSRMLS_C); | ||
char *f; | ||
|
@@ -281,6 +320,11 @@ PHPAPI int php_mail(char *to, char *subject, char *message, char *headers, char | |
efree(f); | ||
} | ||
|
||
if (hdr && php_mail_detect_multiple_crlf(hdr)) { | ||
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Multiple or malformed newlines found in additional_header"); | ||
MAIL_RET(0); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This breaks sending multipart attachments, which need a double CR LF There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Change
into
and you end up with an empty attachment. Tested with mandrill There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Complete code:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that this is a serious bug, which could have catastrophic consequences for PHP users. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The message body (text) should maybe in the message body (parameter), but you cannot send a message (text) with one or more attachments in this way. An attachment needs to specify a content type and other things, which can be done using headers only. So, back to the problem, Mandrill is widely used to send messages in a reliable way and this change breaks sending attachments using Mandrill and I couldn't find a workaround for this. How can this be fixed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @M66B I'm not sure I understand you. You can put necessary headers in the header field, and the body (including multi-part message, which included necessary headers for MIME parts) into the body, I see absolutely no problem in that. I have no idea why Mandrill puts whole body of the message in the headers, but if it does so, it's time for it to be fixed and use the proper way of doing it. As I said, the workaround is putting the headers in headers and body in body. I don't see anything preventing it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Supposed we have a message like this:
The text Please find my thoughts in the attachment should go into the message parameter and attachment.pdf should go into the headers. http://php.net/manual/en/function.mail.php
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
No it should not. Both should be part of multipart MIME message that goes in the body of the message. I am sorry, but I get a suspicion that you are not completely familiar with MIME message format and how headers and body of the multipart message work. In this case, I would advise inspecting some of the multipart messages as the source (most mail clients have this option) and reading about MIME. If I am mistaken, than I must still misunderstand you as I have no idea why you think attachments go into headers. Could you explain me what is the basis of such claim? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Am I understanding correctly that attachment headers should go into the message (body) parameter? It is not about claiming something, but this is not (clearly) documented: Looking around for examples I see both methods (using the message and the headers parameters for attachments) being used. In any case if you are right I see a workaround, but I still believe this change shouldn't break things. |
||
} | ||
|
||
if (!sendmail_path) { | ||
#if (defined PHP_WIN32 || defined NETWARE) | ||
/* handle old style win smtp sending */ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You've got a potential buffer overflow here.
(*hdr+2)
could very well go past the buffer and not equal\0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume this relies on the fact that strings are implicitly 0-terminated in PHP. But if you have counter-example, please submit the code.