summaryrefslogtreecommitdiff
path: root/lib/phpmailer/class.phpmailer.php
diff options
context:
space:
mode:
Diffstat (limited to 'lib/phpmailer/class.phpmailer.php')
-rw-r--r--lib/phpmailer/class.phpmailer.php163
1 files changed, 132 insertions, 31 deletions
diff --git a/lib/phpmailer/class.phpmailer.php b/lib/phpmailer/class.phpmailer.php
index f9013eb..8ff13f1 100644
--- a/lib/phpmailer/class.phpmailer.php
+++ b/lib/phpmailer/class.phpmailer.php
@@ -31,7 +31,7 @@ class PHPMailer
* The PHPMailer Version number.
* @var string
*/
- public $Version = '5.2.16';
+ public $Version = '5.2.21';
/**
* Email priority.
@@ -201,6 +201,9 @@ class PHPMailer
/**
* An ID to be used in the Message-ID header.
* If empty, a unique id will be generated.
+ * You can set your own, but it must be in the format "<id@domain>",
+ * as defined in RFC5322 section 3.6.4 or it will be ignored.
+ * @see https://tools.ietf.org/html/rfc5322#section-3.6.4
* @var string
*/
public $MessageID = '';
@@ -421,6 +424,13 @@ class PHPMailer
public $DKIM_private = '';
/**
+ * DKIM private key string.
+ * If set, takes precedence over `$DKIM_private`.
+ * @var string
+ */
+ public $DKIM_private_string = '';
+
+ /**
* Callback Action function name.
*
* The function that handles the result of the send email action.
@@ -681,16 +691,16 @@ class PHPMailer
} else {
$subject = $this->encodeHeader($this->secureHeader($subject));
}
- //Can't use additional_parameters in safe_mode
+
+ //Can't use additional_parameters in safe_mode, calling mail() with null params breaks
//@link http://php.net/manual/en/function.mail.php
- if (ini_get('safe_mode') or !$this->UseSendmailOptions) {
+ if (ini_get('safe_mode') or !$this->UseSendmailOptions or is_null($params)) {
$result = @mail($to, $subject, $body, $header);
} else {
$result = @mail($to, $subject, $body, $header, $params);
}
return $result;
}
-
/**
* Output debugging info via user-defined method.
* Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
@@ -1284,9 +1294,11 @@ class PHPMailer
// Sign with DKIM if enabled
if (!empty($this->DKIM_domain)
- && !empty($this->DKIM_private)
&& !empty($this->DKIM_selector)
- && file_exists($this->DKIM_private)) {
+ && (!empty($this->DKIM_private_string)
+ || (!empty($this->DKIM_private) && file_exists($this->DKIM_private))
+ )
+ ) {
$header_dkim = $this->DKIM_Add(
$this->MIMEHeader . $this->mailHeader,
$this->encodeHeader($this->secureHeader($this->Subject)),
@@ -1352,19 +1364,24 @@ class PHPMailer
*/
protected function sendmailSend($header, $body)
{
- if ($this->Sender != '') {
+ // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
+ if (!empty($this->Sender) and self::isShellSafe($this->Sender)) {
if ($this->Mailer == 'qmail') {
- $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
+ $sendmailFmt = '%s -f%s';
} else {
- $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
+ $sendmailFmt = '%s -oi -f%s -t';
}
} else {
if ($this->Mailer == 'qmail') {
- $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail));
+ $sendmailFmt = '%s';
} else {
- $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail));
+ $sendmailFmt = '%s -oi -t';
}
}
+
+ // TODO: If possible, this should be changed to escapeshellarg. Needs thorough testing.
+ $sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender);
+
if ($this->SingleTo) {
foreach ($this->SingleToArray as $toAddr) {
if (!@$mail = popen($sendmail, 'w')) {
@@ -1411,6 +1428,40 @@ class PHPMailer
}
/**
+ * Fix CVE-2016-10033 and CVE-2016-10045 by disallowing potentially unsafe shell characters.
+ *
+ * Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows.
+ * @param string $string The string to be validated
+ * @see https://github.com/PHPMailer/PHPMailer/issues/924 CVE-2016-10045 bug report
+ * @access protected
+ * @return boolean
+ */
+ protected static function isShellSafe($string)
+ {
+ // Future-proof
+ if (escapeshellcmd($string) !== $string
+ or !in_array(escapeshellarg($string), array("'$string'", "\"$string\""))
+ ) {
+ return false;
+ }
+
+ $length = strlen($string);
+
+ for ($i = 0; $i < $length; $i++) {
+ $c = $string[$i];
+
+ // All other characters have a special meaning in at least one common shell, including = and +.
+ // Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here.
+ // Note that this does permit non-Latin alphanumeric characters based on the current locale.
+ if (!ctype_alnum($c) && strpos('@_-.', $c) === false) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
* Send mail using the PHP mail() function.
* @param string $header The message headers
* @param string $body The message body
@@ -1429,10 +1480,13 @@ class PHPMailer
$params = null;
//This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
- if (!empty($this->Sender)) {
- $params = sprintf('-f%s', $this->Sender);
+ if (!empty($this->Sender) and $this->validateAddress($this->Sender)) {
+ // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
+ if (self::isShellSafe($this->Sender)) {
+ $params = sprintf('-f%s', $this->Sender);
+ }
}
- if ($this->Sender != '' and !ini_get('safe_mode')) {
+ if (!empty($this->Sender) and !ini_get('safe_mode') and $this->validateAddress($this->Sender)) {
$old_from = ini_get('sendmail_from');
ini_set('sendmail_from', $this->Sender);
}
@@ -1486,10 +1540,10 @@ class PHPMailer
if (!$this->smtpConnect($this->SMTPOptions)) {
throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
}
- if ('' == $this->Sender) {
- $smtp_from = $this->From;
- } else {
+ if (!empty($this->Sender) and $this->validateAddress($this->Sender)) {
$smtp_from = $this->Sender;
+ } else {
+ $smtp_from = $this->From;
}
if (!$this->smtp->mail($smtp_from)) {
$this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
@@ -1681,6 +1735,19 @@ class PHPMailer
*/
public function setLanguage($langcode = 'en', $lang_path = '')
{
+ // Backwards compatibility for renamed language codes
+ $renamed_langcodes = array(
+ 'br' => 'pt_br',
+ 'cz' => 'cs',
+ 'dk' => 'da',
+ 'no' => 'nb',
+ 'se' => 'sv',
+ );
+
+ if (isset($renamed_langcodes[$langcode])) {
+ $langcode = $renamed_langcodes[$langcode];
+ }
+
// Define full set of translatable strings in English
$PHPMAILER_LANG = array(
'authenticate' => 'SMTP Error: Could not authenticate.',
@@ -1707,6 +1774,10 @@ class PHPMailer
// Calculate an absolute path so it can work if CWD is not here
$lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;
}
+ //Validate $langcode
+ if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) {
+ $langcode = 'en';
+ }
$foundlang = true;
$lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
// There is no English translation file
@@ -2000,6 +2071,8 @@ class PHPMailer
$result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
}
+ // Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4
+ // https://tools.ietf.org/html/rfc5322#section-3.6.4
if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) {
$this->lastMessageID = $this->MessageID;
} else {
@@ -2106,6 +2179,14 @@ class PHPMailer
}
/**
+ * Create unique ID
+ * @return string
+ */
+ protected function generateId() {
+ return md5(uniqid(time()));
+ }
+
+ /**
* Assemble the message body.
* Returns an empty string on failure.
* @access public
@@ -2116,7 +2197,7 @@ class PHPMailer
{
$body = '';
//Create unique IDs and preset boundaries
- $this->uniqueid = md5(uniqid(time()));
+ $this->uniqueid = $this->generateId();
$this->boundary[1] = 'b1_' . $this->uniqueid;
$this->boundary[2] = 'b2_' . $this->uniqueid;
$this->boundary[3] = 'b3_' . $this->uniqueid;
@@ -3296,16 +3377,18 @@ class PHPMailer
}
/**
- * Create a message from an HTML string.
- * Automatically makes modifications for inline images and backgrounds
- * and creates a plain-text version by converting the HTML.
- * Overwrites any existing values in $this->Body and $this->AltBody
+ * Create a message body from an HTML string.
+ * Automatically inlines images and creates a plain-text version by converting the HTML,
+ * overwriting any existing values in Body and AltBody.
+ * $basedir is used when handling relative image paths, e.g. <img src="images/a.png">
+ * will look for an image file in $basedir/images/a.png and convert it to inline.
+ * If you don't want to apply these transformations to your HTML, just set Body and AltBody yourself.
* @access public
* @param string $message HTML message string
- * @param string $basedir baseline directory for path
+ * @param string $basedir base directory for relative paths to images
* @param boolean|callable $advanced Whether to use the internal HTML to text converter
* or your own custom converter @see PHPMailer::html2text()
- * @return string $message
+ * @return string $message The transformed message Body
*/
public function msgHTML($message, $basedir = '', $advanced = false)
{
@@ -3375,7 +3458,7 @@ class PHPMailer
* Convert an HTML string into plain text.
* This is used by msgHTML().
* Note - older versions of this function used a bundled advanced converter
- * which was been removed for license reasons in #232
+ * which was been removed for license reasons in #232.
* Example usage:
* <code>
* // Use default conversion
@@ -3675,7 +3758,7 @@ class PHPMailer
* @access public
* @param string $signHeader
* @throws phpmailerException
- * @return string
+ * @return string The DKIM signature value
*/
public function DKIM_Sign($signHeader)
{
@@ -3685,15 +3768,33 @@ class PHPMailer
}
return '';
}
- $privKeyStr = file_get_contents($this->DKIM_private);
- if ($this->DKIM_passphrase != '') {
+ $privKeyStr = !empty($this->DKIM_private_string) ? $this->DKIM_private_string : file_get_contents($this->DKIM_private);
+ if ('' != $this->DKIM_passphrase) {
$privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
} else {
$privKey = openssl_pkey_get_private($privKeyStr);
}
- if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) { //sha1WithRSAEncryption
- openssl_pkey_free($privKey);
- return base64_encode($signature);
+ //Workaround for missing digest algorithms in old PHP & OpenSSL versions
+ //@link http://stackoverflow.com/a/11117338/333340
+ if (version_compare(PHP_VERSION, '5.3.0') >= 0 and
+ in_array('sha256WithRSAEncryption', openssl_get_md_methods(true))) {
+ if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) {
+ openssl_pkey_free($privKey);
+ return base64_encode($signature);
+ }
+ } else {
+ $pinfo = openssl_pkey_get_details($privKey);
+ $hash = hash('sha256', $signHeader);
+ //'Magic' constant for SHA256 from RFC3447
+ //@link https://tools.ietf.org/html/rfc3447#page-43
+ $t = '3031300d060960864801650304020105000420' . $hash;
+ $pslen = $pinfo['bits'] / 8 - (strlen($t) / 2 + 3);
+ $eb = pack('H*', '0001' . str_repeat('FF', $pslen) . '00' . $t);
+
+ if (openssl_private_encrypt($eb, $signature, $privKey, OPENSSL_NO_PADDING)) {
+ openssl_pkey_free($privKey);
+ return base64_encode($signature);
+ }
}
openssl_pkey_free($privKey);
return '';