-
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
Conversation
ext/standard/mail.c
Outdated
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.
This may be dangerous if the *hdr string ends on \r.
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.
Right. I'm being sloppy here by assuming caller trimmed leading/ending \r, \n.
I may get rid of this assumption and previously applied trim code.
Please try to build on TSRM, this patch doesn't even build there. |
@yohgaki If we want to have it in next 5.4 release, we don't have too much time as it is planned for the next week. |
…not removed for better compatibility. Add missing TSRMLS_CC.
Sorry for the delay. |
ext/standard/mail.c
Outdated
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.
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you. Reasonable anxiety.
There is NULL byte check before this function call in PHP_FUNCTION(mail),
if (headers) {
MAIL_ASCIIZ_CHECK(headers, headers_len);
headers_trimmed = php_trim(headers, headers_len, NULL, 0, NULL, 2 TSRMLS_CC);
}
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 comment
The reason will be displayed to describe this comment to others. Learn more.
How about use this patch for released version?
I'll prepare another patch to clean up mail mess, including mb_send_mail, for master.
IMHO, we are better to raise error when user supply strings contain NULL char rather than replacing NULL to ' '.
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.
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but current API does not pass the length to php_mail().
Nothing we can do without modifying API.
That's the reason why I propose updating API and fix the mess. (NULL byte check/replacement and invalid newlines)
@smalyshev Thank you for catching pointer error. And I was lazy for trailing newline conditions because PHP_FUNCTION(mail)/PHP_FUNCTION(mb_send_mail) applies php_trim(). This one is not. I agree php_mail() should be as secure as PHP_FUNCTION(mail/mb_send_mail), but it does NULL char check/replace in them. php_trim() should be removed also. php_mail() does not have string length now. I would like to address this issue by using zend_string in PHP7 and leave as it is now for released versions. What do you think? |
merged |
@smalyshev |
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.
This breaks sending multipart attachments, which need a double CR LF
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.
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.
$headers .= '--' . $separator . $eol;
$headers .= "Content-Type: application/pdf; name=\"" . $filename . "\"" . $eol;
$headers .= 'Content-Transfer-Encoding: base64' . $eol;
$headers .= "Content-Disposition: attachment; filename=\"" . $filename . "\"" . $eol . $eol;
$headers .= chunk_split(base64_encode($invoicePDF));
$headers .= '--' . $separator . '--';
Change
$headers .= "Content-Disposition: attachment; filename=\"" . $filename . "\"" . $eol . $eol;
into
$headers .= "Content-Disposition: attachment; filename=\"" . $filename . "\"" . $eol;
and you end up with an empty attachment.
Tested with mandrill
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.
Complete code:
$eol = "\n";
// Main headers
$headers = 'From: ' . $data['ap_name'] . ' <' . $data['ap_email'] . '>' . $eol;
$headers .= 'Reply-To: ' . $data['ap_name'] . ' <' . $data['ap_email'] . '>' . $eol;
$headers .= 'MIME-Version: 1.0' . $eol;
$headers .= "Content-Type: multipart/mixed; boundary=\"" . $separator . "\"" . $eol;
$headers .= 'Content-Transfer-Encoding: 8bit' . $eol . $eol;
$headers .= $subject . $eol;
// Message headers
$headers .= "--" . $separator . $eol;
$headers .= "Content-Type: text/html; charset=UTF-8" . $eol;
$headers .= 'Content-Transfer-Encoding: 8bit' . $eol . $eol;
$headers .= $message . $eol;
// Attachment headers
$headers .= '--' . $separator . $eol;
$headers .= "Content-Type: application/pdf; name=\"" . $filename . "\"" . $eol;
$headers .= 'Content-Transfer-Encoding: base64' . $eol;
$headers .= "Content-Disposition: attachment; filename=\"" . $filename . "\"" . $eol . $eol;
$headers .= chunk_split(base64_encode($invoicePDF));
$headers .= '--' . $separator . '--';
// Send
if (!mail($data['email'], $subject, $message, $headers))
throw new Exception('e-mail failed, invoice=' . $invoice);
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.
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 comment
The 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 comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
Supposed we have a message like this:
To: you
From: me
Subject: text
Please find my thoughts in the attachment
[[attachment.pdf]]]
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
message
Message to be sent.
Each line should be separated with a CRLF (\r\n). Lines should not be larger than 70 characters.
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.
and attachment.pdf should go into the headers.
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 comment
The 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:
http://php.net/manual/en/function.mail.php
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.
@M66B I'm not sure I understand why you need double EOL after the Content-Transfer-Encoding header. I've just checked sending multi-attachment email and I see that there is no double EOL in the headers. Header and body are separated by multiple EOL but inside headers there's no extra EOLs. Also, there are two EOLs separating header from body within each part of multipart message, but multipart messages are not part of the header, they are part of the body. I'm not sure why you're putting attachment headers and whole $message into headers, it doesn't sound right - maybe I miss something in your code. Could you provide a full stand-alone script that worked before and doesn't work now? The produced mail source would also be helpful. I'd also advise submitting bug to bugs.php.net - it's much more convenient to track it there. |
Leaving out the double EOL when sending an attachment using Mandril will result in an empty attachment. I spent an hour trying to workaround this, but I couldn't find any way. Don't ask me why an EOL is necessary, but it simply is. I have reverted back to PHP 5.6.9 to solve this problem for an unhappy client. Just try the script I have already provided with Mandril and you'll see. Note that Mandril is quite often used to send mail from servers. |
@M66B I suspect that you are trying to send malformed messages, putting message body into header, and by some miracle it worked before. However, it does not seem to be the right thing to do. I'm not sure what "Mandril" is but if a stand-alone script is provided I could look deeper into this. I still suggest to submit a bug on bugs.php.net. |
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.
Hello,
Has worked for years. Now broken. |
@HonorMan I would recommend putting message in the body of the message, not in the header. |
Thank you I misspoke. Emails with attachments are my issue. Cannot seem to send messages with attachments and it is not clear where to separate the message from the header when using attachments. Any examples? |
/**************************************************************************
|
I figured it out... I am glad this happened because we definitely had our header's set up wrong based on some examples we saw out there. Here is what we did to fix our problem. It was quite simple when we did it right. /**************************************************************************
**************************************************************************/ $messageAll = ' ';$messageAll .= ''; $messageAll .= ''; $messageAll .= '
'; /*************************************************************************
$attachment = chunk_split(base64_encode($pdfdoc)); // encode data (puts attachment in proper format) $mailbody = ""; $to = $mail; $subject = $cName . " Estimate"; // main header (multipart mandatory) // We removed the extra $eol from the next statement. $mailbody = "Content-Type: multipart/mixed; boundary="" . $separator . """ . $eol; $mailbody .= "--".$separator.$eol; // attachment if (mail($to, |
HonorMan - Just used your code above and works great! But to work for me I needed to add one line to your code, at least with Outlook Express 2007 as the target email client: At the end of your "main header (multipart mandatory)" section I had to add this line Without that, Outlook Express displays the mime encoding of the message in the message body, not as an attachment. It may be particular to that mail client. Posting here in case it helps anyone else. |
That works too! We had that statement in the body and it works with Outlook. simplified our headers. $mailbody = "Content-Type: multipart/mixed; boundary="" . $separator . """ . $eol; |
Sorry read too fast. Outlook Express may need it in the header. |
Seems like there are known issues: https://support.microsoft.com/en-us/kb/833004 |
https://bugs.php.net/bug.php?id=68776