From acf633c73bc8df9a5036bc52d7568f4213ab73c7 Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Fri, 06 May 2016 02:32:01 -0400
Subject: [PATCH] Fix XSS issue in href attribute on area tag (#5240, #5241)

---
 program/lib/Roundcube/rcube.php |  106 +++++++++++++++++++++++++++++++---------------------
 1 files changed, 63 insertions(+), 43 deletions(-)

diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php
index 87103be..5fe4a4b 100644
--- a/program/lib/Roundcube/rcube.php
+++ b/program/lib/Roundcube/rcube.php
@@ -373,15 +373,16 @@
 
         // set class options
         $options = array(
-            'auth_type'   => $this->config->get("{$driver}_auth_type", 'check'),
-            'auth_cid'    => $this->config->get("{$driver}_auth_cid"),
-            'auth_pw'     => $this->config->get("{$driver}_auth_pw"),
-            'debug'       => (bool) $this->config->get("{$driver}_debug"),
-            'force_caps'  => (bool) $this->config->get("{$driver}_force_caps"),
-            'disabled_caps' => $this->config->get("{$driver}_disabled_caps"),
-            'timeout'     => (int) $this->config->get("{$driver}_timeout"),
-            'skip_deleted' => (bool) $this->config->get('skip_deleted'),
-            'driver'      => $driver,
+            'auth_type'      => $this->config->get("{$driver}_auth_type", 'check'),
+            'auth_cid'       => $this->config->get("{$driver}_auth_cid"),
+            'auth_pw'        => $this->config->get("{$driver}_auth_pw"),
+            'debug'          => (bool) $this->config->get("{$driver}_debug"),
+            'force_caps'     => (bool) $this->config->get("{$driver}_force_caps"),
+            'disabled_caps'  => $this->config->get("{$driver}_disabled_caps"),
+            'socket_options' => $this->config->get("{$driver}_conn_options"),
+            'timeout'        => (int) $this->config->get("{$driver}_timeout"),
+            'skip_deleted'   => (bool) $this->config->get('skip_deleted'),
+            'driver'         => $driver,
         );
 
         if (!empty($_SESSION['storage_host'])) {
@@ -463,8 +464,13 @@
         // use database for storing session data
         $this->session = new rcube_session($this->get_dbh(), $this->config);
 
+        $path = $_SERVER['SCRIPT_NAME'];
+        if (strpos($path, '://')) {
+            $path = parse_url($path, PHP_URL_PATH); // #1490582
+        }
+
         $this->session->register_gc_handler(array($this, 'gc'));
-        $this->session->set_secret($this->config->get('des_key') . dirname($_SERVER['SCRIPT_NAME']));
+        $this->session->set_secret($this->config->get('des_key') . dirname($path));
         $this->session->set_ip_check($this->config->get('ip_check'));
 
         if ($this->config->get('session_auth_name')) {
@@ -1457,20 +1463,15 @@
         // send thru SMTP server using custom SMTP library
         if ($this->config->get('smtp_server')) {
             // generate list of recipients
-            $a_recipients = array($mailto);
+            $a_recipients = (array) $mailto;
 
             if (strlen($headers['Cc']))
                 $a_recipients[] = $headers['Cc'];
             if (strlen($headers['Bcc']))
                 $a_recipients[] = $headers['Bcc'];
 
-            // clean Bcc from header for recipients
-            $send_headers = $headers;
-            unset($send_headers['Bcc']);
-            // here too, it because txtHeaders() below use $message->_headers not only $send_headers
-            unset($message->_headers['Bcc']);
-
-            $smtp_headers = $message->txtHeaders($send_headers, true);
+            // remove Bcc header and get the whole head of the message as string
+            $smtp_headers = $this->message_head($message, array('Bcc'));
 
             if ($message->getParam('delay_file_io')) {
                 // use common temp dir
@@ -1502,24 +1503,18 @@
             if (!$sent) {
                 self::raise_error(array('code' => 800, 'type' => 'smtp',
                     'line' => __LINE__, 'file' => __FILE__,
-                    'message' => "SMTP error: ".join("\n", $response)), TRUE, FALSE);
+                    'message' => join("\n", $response)), true, false);
             }
         }
         // send mail using PHP's mail() function
         else {
-            // unset some headers because they will be added by the mail() function
-            $headers_enc = $message->headers($headers);
-            $headers_php = $message->_headers;
-            unset($headers_php['To'], $headers_php['Subject']);
-
-            // reset stored headers and overwrite
-            $message->_headers = array();
-            $header_str = $message->txtHeaders($headers_php);
+            // unset To,Subject headers because they will be added by the mail() function
+            $header_str = $this->message_head($message, array('To', 'Subject'));
 
             // #1485779
             if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
-                if (preg_match_all('/<([^@]+@[^>]+)>/', $headers_enc['To'], $m)) {
-                    $headers_enc['To'] = implode(', ', $m[1]);
+                if (preg_match_all('/<([^@]+@[^>]+)>/', $headers['To'], $m)) {
+                    $headers['To'] = implode(', ', $m[1]);
                 }
             }
 
@@ -1533,8 +1528,8 @@
             }
             else {
                 $delim   = $this->config->header_delimiter();
-                $to      = $headers_enc['To'];
-                $subject = $headers_enc['Subject'];
+                $to      = $headers['To'];
+                $subject = $headers['Subject'];
                 $header_str = rtrim($header_str);
 
                 if ($delim != "\r\n") {
@@ -1557,19 +1552,24 @@
             // remove MDN headers after sending
             unset($headers['Return-Receipt-To'], $headers['Disposition-Notification-To']);
 
-            // get all recipients
-            if ($headers['Cc'])
-                $mailto .= $headers['Cc'];
-            if ($headers['Bcc'])
-                $mailto .= $headers['Bcc'];
-            if (preg_match_all('/<([^@]+@[^>]+)>/', $mailto, $m))
-                $mailto = implode(', ', array_unique($m[1]));
-
             if ($this->config->get('smtp_log')) {
+                // get all recipient addresses
+                if (is_array($mailto)) {
+                    $mailto = implode(',', $mailto);
+                }
+                if ($headers['Cc']) {
+                    $mailto .= ',' . $headers['Cc'];
+                }
+                if ($headers['Bcc']) {
+                    $mailto .= ',' . $headers['Bcc'];
+                }
+
+                $mailto = rcube_mime::decode_address_list($mailto, null, false, null, true);
+
                 self::write_log('sendmail', sprintf("User %s [%s]; Message for %s; %s",
                     $this->user->get_username(),
-                    $_SERVER['REMOTE_ADDR'],
-                    $mailto,
+                    rcube_utils::remote_addr(),
+                    implode(', ', $mailto),
                     !empty($response) ? join('; ', $response) : ''));
             }
         }
@@ -1582,12 +1582,32 @@
             fclose($msg_body);
         }
 
-        $message->_headers = array();
-        $message->headers($headers);
+        $message->headers($headers, true);
 
         return $sent;
     }
 
+    /**
+     * Return message headers as a string
+     */
+    protected function message_head($message, $unset = array())
+    {
+        // Mail_mime >= 1.9.0
+        if (method_exists($message, 'isMultipart')) {
+            foreach ($unset as $header) {
+                $headers[$header] = null;
+            }
+        }
+        else {
+            $headers = $message->headers();
+            foreach ($unset as $header) {
+                unset($headers[$header]);
+            }
+            $message->_headers = array();
+        }
+
+        return $message->txtHeaders($headers, true);
+    }
 }
 
 

--
Gitblit v1.9.1