From 197203727417a03d87053a47e5aa5175a76e3e0b Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Thu, 17 Oct 2013 04:24:53 -0400
Subject: [PATCH] Fix vulnerability in handling _session argument of utils/save-prefs (#1489382)

---
 program/include/rcmail.php |  133 ++++++++++++++++++++++++++++++--------------
 1 files changed, 91 insertions(+), 42 deletions(-)

diff --git a/program/include/rcmail.php b/program/include/rcmail.php
index 7967f94..a3c04ef 100644
--- a/program/include/rcmail.php
+++ b/program/include/rcmail.php
@@ -337,20 +337,41 @@
       }
 
       $this->memcache = new Memcache;
-      $mc_available = 0;
+      $this->mc_available = 0;
+      
+      // add alll configured hosts to pool
+      $pconnect = $this->config->get('memcache_pconnect', true);
       foreach ($this->config->get('memcache_hosts', array()) as $host) {
         list($host, $port) = explode(':', $host);
         if (!$port) $port = 11211;
-        // add server and attempt to connect if not already done yet
-        if ($this->memcache->addServer($host, $port) && !$mc_available)
-          $mc_available += intval($this->memcache->connect($host, $port));
+        $this->mc_available += intval($this->memcache->addServer($host, $port, $pconnect, 1, 1, 15, false, array($this, 'memcache_failure')));
       }
+      
+      // test connection and failover (will result in $this->mc_available == 0 on complete failure)
+      $this->memcache->increment('__CONNECTIONTEST__', 1);  // NOP if key doesn't exist
 
-      if (!$mc_available)
+      if (!$this->mc_available)
         $this->memcache = false;
     }
 
     return $this->memcache;
+  }
+  
+  /**
+   * Callback for memcache failure
+   */
+  public function memcache_failure($host, $port)
+  {
+    static $seen = array();
+    
+    // only report once
+    if (!$seen["$host:$port"]++) {
+      $this->mc_available--;
+      raise_error(array('code' => 604, 'type' => 'db',
+        'line' => __LINE__, 'file' => __FILE__,
+        'message' => "Memcache failure on host $host:$port"),
+        true, false);
+    }
   }
 
 
@@ -431,9 +452,12 @@
         true, true);
     }
 
+    // set configured sort order
+    if ($sort_col = $this->config->get('addressbook_sort_col'))
+        $contacts->set_sort_order($sort_col);
+
     // add to the 'books' array for shutdown function
-    if (!isset($this->address_books[$id]))
-      $this->address_books[$id] = $contacts;
+    $this->address_books[$id] = $contacts;
 
     return $contacts;
   }
@@ -462,7 +486,8 @@
         'name'     => rcube_label('personaladrbook'),
         'groups'   => $this->address_books['0']->groups,
         'readonly' => $this->address_books['0']->readonly,
-        'autocomplete' => in_array('sql', $autocomplete)
+        'autocomplete' => in_array('sql', $autocomplete),
+        'undelete' => $this->address_books['0']->undelete && $this->config->get('undo_timeout'),
       );
     }
 
@@ -572,7 +597,6 @@
       return;
 
     $this->imap = new rcube_imap();
-    $this->imap->debug_level = $this->config->get('debug_level');
     $this->imap->skip_deleted = $this->config->get('skip_deleted');
 
     // enable caching of imap data
@@ -594,7 +618,7 @@
     // Setting root and delimiter before establishing the connection
     // can save time detecting them using NAMESPACE and LIST
     $options = array(
-      'auth_method' => $this->config->get('imap_auth_type', 'check'),
+      'auth_type'   => $this->config->get('imap_auth_type', 'check'),
       'auth_cid'    => $this->config->get('imap_auth_cid'),
       'auth_pw'     => $this->config->get('imap_auth_pw'),
       'debug'       => (bool) $this->config->get('imap_debug', 0),
@@ -657,18 +681,21 @@
     if (session_id())
       return;
 
+    $sess_name   = $this->config->get('session_name');
+    $sess_domain = $this->config->get('session_domain');
+    $lifetime    = $this->config->get('session_lifetime', 0) * 60;
+
     // set session domain
-    if ($domain = $this->config->get('session_domain')) {
-      ini_set('session.cookie_domain', $domain);
+    if ($sess_domain) {
+      ini_set('session.cookie_domain', $sess_domain);
     }
     // set session garbage collecting time according to session_lifetime
-    $lifetime = $this->config->get('session_lifetime', 0) * 60;
     if ($lifetime) {
       ini_set('session.gc_maxlifetime', $lifetime * 2);
     }
 
     ini_set('session.cookie_secure', rcube_https_check());
-    ini_set('session.name', 'roundcube_sessid');
+    ini_set('session.name', $sess_name ? $sess_name : 'roundcube_sessid');
     ini_set('session.use_cookies', 1);
     ini_set('session.use_only_cookies', 1);
     ini_set('session.serialize_handler', 'php');
@@ -677,8 +704,7 @@
     $this->session = new rcube_session($this->get_dbh(), $this->config);
 
     $this->session->register_gc_handler('rcmail_temp_gc');
-    if ($this->config->get('enable_caching'))
-      $this->session->register_gc_handler('rcmail_cache_gc');
+    $this->session->register_gc_handler('rcmail_cache_gc');
 
     // start PHP session (if not in CLI mode)
     if ($_SERVER['REMOTE_ADDR'])
@@ -708,7 +734,7 @@
       $keep_alive = max(60, $keep_alive);
       $this->session->set_keep_alive($keep_alive);
     }
-    
+
     $this->session->set_secret($this->config->get('des_key') . $_SERVER['HTTP_USER_AGENT']);
     $this->session->set_ip_check($this->config->get('ip_check'));
   }
@@ -818,16 +844,8 @@
     if (!$imap_login)
       return false;
 
-    $this->set_imap_prop();
-
     // user already registered -> update user's record
     if (is_object($user)) {
-      // fix some old settings according to namespace prefix
-      $this->fix_namespace_settings($user);
-
-      // create default folders on first login
-      if (!$user->data['last_login'] && $config['create_default_folders'])
-        $this->imap->create_default_folders();
       // update last login timestamp
       $user->touch();
     }
@@ -835,9 +853,6 @@
     else if ($config['auto_create_user']) {
       if ($created = rcube_user::create($username, $host)) {
         $user = $created;
-        // create default folders on first login
-        if ($config['create_default_folders'])
-          $this->imap->create_default_folders();
       }
       else {
         raise_error(array(
@@ -857,8 +872,18 @@
 
     // login succeeded
     if (is_object($user) && $user->ID) {
+      // Configure environment
       $this->set_user($user);
+      $this->set_imap_prop();
       $this->session_configure();
+
+      // fix some old settings according to namespace prefix
+      $this->fix_namespace_settings($user);
+
+      // create default folders on first login
+      if ($config['create_default_folders'] && (!empty($created) || empty($user->data['last_login']))) {
+        $this->imap->create_default_folders();
+      }
 
       // set session vars
       $_SESSION['user_id']   = $user->ID;
@@ -868,9 +893,11 @@
       $_SESSION['imap_ssl']  = $imap_ssl;
       $_SESSION['password']  = $this->encrypt($pass);
       $_SESSION['login_time'] = mktime();
-      
+
       if (isset($_REQUEST['_timezone']) && $_REQUEST['_timezone'] != '_default_')
         $_SESSION['timezone'] = floatval($_REQUEST['_timezone']);
+      if (isset($_REQUEST['_dstactive']) && $_REQUEST['_dstactive'] != '_default_')
+        $_SESSION['dst_active'] = intval($_REQUEST['_dstactive']);
 
       // force reloading complete list of subscribed mailboxes
       $this->imap->clear_cache('mailboxes', true);
@@ -949,7 +976,9 @@
   /**
    * Get localized text in the desired language
    *
-   * @param mixed Named parameters array or label name
+   * @param mixed   $attrib  Named parameters array or label name
+   * @param string  $domain  Label domain (plugin) name
+   *
    * @return string Localized text
    */
   public function gettext($attrib, $domain=null)
@@ -964,7 +993,7 @@
 
     $nr = is_numeric($attrib['nr']) ? $attrib['nr'] : 1;
     $name = $attrib['name'] ? $attrib['name'] : '';
-    
+
     // attrib contain text values: use them from now
     if (($setval = $attrib[strtolower($_SESSION['language'])]) || ($setval = $attrib['en_us']))
         $this->texts[$name] = $setval;
@@ -1020,19 +1049,40 @@
 
 
   /**
-   * Check if the given text lable exists
+   * Check if the given text label exists
    *
-   * @param string Label name
+   * @param string  $name       Label name
+   * @param string  $domain     Label domain (plugin) name or '*' for all domains
+   * @param string  $ref_domain Sets domain name if label is found
+   *
    * @return boolean True if text exists (either in the current language or in en_US)
    */
-  public function text_exists($name, $domain=null)
+  public function text_exists($name, $domain = null, &$ref_domain = null)
   {
     // load localization files if not done yet
     if (empty($this->texts))
       $this->load_language();
 
-    // check for text with domain first
-    return ($domain && isset($this->texts[$domain.'.'.$name])) || isset($this->texts[$name]);
+    if (isset($this->texts[$name])) {
+        $ref_domain = '';
+        return true;
+    }
+
+    // any of loaded domains (plugins)
+    if ($domain == '*') {
+      foreach ($this->plugins->loaded_plugins() as $domain)
+        if (isset($this->texts[$domain.'.'.$name])) {
+          $ref_domain = $domain;
+          return true;
+        }
+    }
+    // specified domain
+    else if ($domain) {
+      $ref_domain = $domain;
+      return isset($this->texts[$domain.'.'.$name]);
+    }
+
+    return false;
   }
 
   /**
@@ -1180,7 +1230,6 @@
 
     // before closing the database connection, write session data
     if ($_SERVER['REMOTE_ADDR'] && is_object($this->session)) {
-      $this->session->cleanup();
       session_write_close();
     }
 
@@ -1222,7 +1271,7 @@
   {
     $sess_id = $_COOKIE[ini_get('session.name')];
     if (!$sess_id) $sess_id = session_id();
-    $plugin = $this->plugins->exec_hook('request_token', array('value' => md5('RT' . $this->task . $this->config->get('des_key') . $sess_id)));
+    $plugin = $this->plugins->exec_hook('request_token', array('value' => md5('RT' . $this->user->ID . $this->config->get('des_key') . $sess_id)));
     return $plugin['value'];
   }
 
@@ -1516,7 +1565,7 @@
 
     // use strtr behaviour of going through source string once
     $cmd = strtr($cmd, $replacements);
-    
+
     return (string)shell_exec($cmd);
   }
 
@@ -1552,7 +1601,7 @@
       }
     }
   }
-  
+
   /**
    * Returns current action filename
    *
@@ -1582,8 +1631,8 @@
     if (!$prefix_len)
       return;
 
-    $prefs = $user->get_prefs();
-    if (empty($prefs) || $prefs['namespace_fixed'])
+    $prefs = $this->config->all();
+    if (!empty($prefs['namespace_fixed']))
       return;
 
     // Build namespace prefix regexp

--
Gitblit v1.9.1