From bd0551b22076b82a6d49e9f7a2b2e0c90a1b2326 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak <alec@alec.pl> Date: Fri, 05 Feb 2016 07:25:27 -0500 Subject: [PATCH] Secure also downloads of addressbook exports, managesieve script exports and Enigma keys exports --- program/lib/Roundcube/rcube.php | 220 +++++++++++++++++-------------------------------------- 1 files changed, 68 insertions(+), 152 deletions(-) diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php index 02e11e2..7388472 100644 --- a/program/lib/Roundcube/rcube.php +++ b/program/lib/Roundcube/rcube.php @@ -124,8 +124,8 @@ /** * This implements the 'singleton' design pattern * - * @param integer Options to initialize with this instance. See rcube::INIT_WITH_* constants - * @param string Environment name to run (e.g. live, dev, test) + * @param integer $mode Options to initialize with this instance. See rcube::INIT_WITH_* constants + * @param string $env Environment name to run (e.g. live, dev, test) * * @return rcube The one and only instance */ @@ -505,7 +505,7 @@ } ini_set('session.cookie_secure', $is_secure); - ini_set('session.name', $sess_name ? $sess_name : 'roundcube_sessid'); + ini_set('session.name', $sess_name ?: 'roundcube_sessid'); ini_set('session.use_cookies', 1); ini_set('session.use_only_cookies', 1); ini_set('session.cookie_httponly', 1); @@ -601,7 +601,7 @@ $attrib = array('name' => $attrib); } - $name = $attrib['name'] ? $attrib['name'] : ''; + $name = (string) $attrib['name']; // attrib contain text values: use them from now if (($setval = $attrib[strtolower($_SESSION['language'])]) || ($setval = $attrib['en_us'])) { @@ -619,7 +619,7 @@ // replace vars in text if (is_array($attrib['vars'])) { foreach ($attrib['vars'] as $var_key => $var_value) { - $text = str_replace($var_key[0]!='$' ? '$'.$var_key : $var_key, $var_value, $text); + $text = str_replace($var_key[0] != '$' ? '$'.$var_key : $var_key, $var_value, $text); } } @@ -685,7 +685,7 @@ */ public function load_language($lang = null, $add = array(), $merge = array()) { - $lang = $this->language_prop(($lang ? $lang : $_SESSION['language'])); + $lang = $this->language_prop($lang ?: $_SESSION['language']); // load localized texts if (empty($this->texts) || $lang != $_SESSION['language']) { @@ -810,69 +810,37 @@ } /** - * Encrypt using 3DES + * Encrypt a string * * @param string $clear Clear text input * @param string $key Encryption key to retrieve from the configuration, defaults to 'des_key' * @param boolean $base64 Whether or not to base64_encode() the result before returning * - * @return string encrypted text + * @return string Encrypted text */ public function encrypt($clear, $key = 'des_key', $base64 = true) { - if (!$clear) { + if (!is_string($clear) || !strlen($clear)) { return ''; } - // Add a single canary byte to the end of the clear text, which - // will help find out how much of padding will need to be removed - // upon decryption; see http://php.net/mcrypt_generic#68082. - $clear = pack("a*H2", $clear, "80"); - $ckey = $this->config->get_crypto_key($key); - - if (function_exists('openssl_encrypt')) { - $method = 'DES-EDE3-CBC'; - $opts = defined('OPENSSL_RAW_DATA') ? OPENSSL_RAW_DATA : true; - $iv = $this->create_iv(openssl_cipher_iv_length($method)); - $cipher = $iv . openssl_encrypt($clear, $method, $ckey, $opts, $iv); - } - else if (function_exists('mcrypt_module_open') && - ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, "")) - ) { - $iv = $this->create_iv(mcrypt_enc_get_iv_size($td)); - mcrypt_generic_init($td, $ckey, $iv); - $cipher = $iv . mcrypt_generic($td, $clear); - mcrypt_generic_deinit($td); - mcrypt_module_close($td); - } - else { - @include_once 'des.inc'; - - if (function_exists('des')) { - $des_iv_size = 8; - $iv = $this->create_iv($des_iv_size); - $cipher = $iv . des($ckey, $clear, 1, 1, $iv); - } - else { - self::raise_error(array( - 'code' => 500, 'type' => 'php', - 'file' => __FILE__, 'line' => __LINE__, - 'message' => "Could not perform encryption; make sure OpenSSL or Mcrypt or lib/des.inc is available" - ), true, true); - } - } + $ckey = $this->config->get_crypto_key($key); + $method = $this->config->get_crypto_method(); + $opts = defined('OPENSSL_RAW_DATA') ? OPENSSL_RAW_DATA : true; + $iv = rcube_utils::random_bytes(openssl_cipher_iv_length($method), true); + $cipher = $iv . openssl_encrypt($clear, $method, $ckey, $opts, $iv); return $base64 ? base64_encode($cipher) : $cipher; } /** - * Decrypt 3DES-encrypted string + * Decrypt a string * * @param string $cipher Encrypted text * @param string $key Encryption key to retrieve from the configuration, defaults to 'des_key' * @param boolean $base64 Whether or not input is base64-encoded * - * @return string decrypted text + * @return string Decrypted text */ public function decrypt($cipher, $key = 'des_key', $base64 = true) { @@ -880,82 +848,22 @@ return ''; } - $cipher = $base64 ? base64_decode($cipher) : $cipher; - $ckey = $this->config->get_crypto_key($key); + $cipher = $base64 ? base64_decode($cipher) : $cipher; + $ckey = $this->config->get_crypto_key($key); + $method = $this->config->get_crypto_method(); + $opts = defined('OPENSSL_RAW_DATA') ? OPENSSL_RAW_DATA : true; + $iv_size = openssl_cipher_iv_length($method); + $iv = substr($cipher, 0, $iv_size); - if (function_exists('openssl_decrypt')) { - $method = 'DES-EDE3-CBC'; - $opts = defined('OPENSSL_RAW_DATA') ? OPENSSL_RAW_DATA : true; - $iv_size = openssl_cipher_iv_length($method); - $iv = substr($cipher, 0, $iv_size); - - // session corruption? (#1485970) - if (strlen($iv) < $iv_size) { - return ''; - } - - $cipher = substr($cipher, $iv_size); - $clear = openssl_decrypt($cipher, $method, $ckey, $opts, $iv); - } - else if (function_exists('mcrypt_module_open') && - ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, "")) - ) { - $iv_size = mcrypt_enc_get_iv_size($td); - $iv = substr($cipher, 0, $iv_size); - - // session corruption? (#1485970) - if (strlen($iv) < $iv_size) { - return ''; - } - - $cipher = substr($cipher, $iv_size); - mcrypt_generic_init($td, $ckey, $iv); - $clear = mdecrypt_generic($td, $cipher); - mcrypt_generic_deinit($td); - mcrypt_module_close($td); - } - else { - @include_once 'des.inc'; - - if (function_exists('des')) { - $des_iv_size = 8; - $iv = substr($cipher, 0, $des_iv_size); - $cipher = substr($cipher, $des_iv_size); - $clear = des($ckey, $cipher, 0, 1, $iv); - } - else { - self::raise_error(array( - 'code' => 500, 'type' => 'php', - 'file' => __FILE__, 'line' => __LINE__, - 'message' => "Could not perform decryption; make sure OpenSSL or Mcrypt or lib/des.inc is available" - ), true, true); - } + // session corruption? (#1485970) + if (strlen($iv) < $iv_size) { + return ''; } - // Trim PHP's padding and the canary byte; see note in - // rcube::encrypt() and http://php.net/mcrypt_generic#68082 - $clear = substr(rtrim($clear, "\0"), 0, -1); + $cipher = substr($cipher, $iv_size); + $clear = openssl_decrypt($cipher, $method, $ckey, $opts, $iv); return $clear; - } - - /** - * Generates encryption initialization vector (IV) - * - * @param int $size Vector size - * - * @return string Vector string - */ - private function create_iv($size) - { - // mcrypt_create_iv() can be slow when system lacks entrophy - // we'll generate IV vector manually - $iv = ''; - for ($i = 0; $i < $size; $i++) { - $iv .= chr(mt_rand(0, 255)); - } - - return $iv; } /** @@ -992,15 +900,14 @@ */ public function get_request_token() { - $sess_id = $_COOKIE[ini_get('session.name')]; - if (!$sess_id) { - $sess_id = session_id(); + if (empty($_SESSION['request_token'])) { + $plugin = $this->plugins->exec_hook('request_token', array( + 'value' => rcube_utils::random_bytes(32))); + + $_SESSION['request_token'] = $plugin['value']; } - $plugin = $this->plugins->exec_hook('request_token', array( - 'value' => md5('RT' . $this->get_user_id() . $this->config->get('des_key') . $sess_id))); - - return $plugin['value']; + return $_SESSION['request_token']; } /** @@ -1360,7 +1267,7 @@ */ public static function log_bug($arg_arr) { - $program = strtoupper(!empty($arg_arr['type']) ? $arg_arr['type'] : 'php'); + $program = strtoupper($arg_arr['type'] ?: 'php'); $level = self::get_instance()->config->get('debug_level'); // disable errors for ajax requests, write to log instead (#1487831) @@ -1370,11 +1277,18 @@ // write error to local log file if (($level & 1) || !empty($arg_arr['fatal'])) { + $post_query = ''; if ($_SERVER['REQUEST_METHOD'] == 'POST') { - $post_query = '?_task='.urlencode($_POST['_task']).'&_action='.urlencode($_POST['_action']); - } - else { - $post_query = ''; + foreach (array('_task', '_action') as $arg) { + if ($_POST[$arg] && !$_GET[$arg]) { + $post_query[$arg] = $_POST[$arg]; + } + } + + if (!empty($post_query)) { + $post_query = (strpos($_SERVER['REQUEST_URI'], '?') != false ? '&' : '?') + . http_build_query($post_query, '', '&'); + } } $log_entry = sprintf("%s Error: %s%s (%s %s)", @@ -1642,13 +1556,8 @@ 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 @@ -1688,19 +1597,13 @@ } // 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]); } } @@ -1714,8 +1617,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") { @@ -1768,11 +1671,24 @@ 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()) + { + // requires Mail_mime >= 1.9.0 + $headers = array(); + foreach ((array) $unset as $header) { + $headers[$header] = null; + } + + return $message->txtHeaders($headers, true); + } } -- Gitblit v1.9.1