Projects
Kolab:3.4
roundcubemail
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 46
View file
roundcubemail-1.1-appjs.patch
Added
@@ -0,0 +1,21 @@ +--- roundcubemail-1.1.orig/program/js/app.js 2014-09-26 16:12:22.610968447 +0200 ++++ roundcubemail-1.1/program/js/app.js 2014-09-26 16:15:03.962081230 +0200 +@@ -58,7 +58,7 @@ + request_timeout: 180, // seconds + draft_autosave: 0, // seconds + comm_path: './', +- blankpage: 'program/resources/blank.gif', ++ blankpage: (rcmail.env.assets_path || '') + 'program/resources/blank.gif', + recipients_separator: ',', + recipients_delimiter: ', ', + popup_width: 1150, +@@ -7972,7 +7972,7 @@ + + img.onload = function() { ref.env.browser_capabilities.tif = 1; }; + img.onerror = function() { ref.env.browser_capabilities.tif = 0; }; +- img.src = 'program/resources/blank.tif'; ++ img.src = (rcmail.env.assets_path || '') + 'program/resources/blank.tif'; + }; + + this.pdf_support_check = function() +
View file
roundcubemail-1.1-assets-csrf.patch
Deleted
@@ -1,865 +0,0 @@ -diff --git a/config/defaults.inc.php b/config/defaults.inc.php -index 6f61a4c..7af56a8 100644 ---- a/config/defaults.inc.php -+++ b/config/defaults.inc.php -@@ -532,6 +532,23 @@ $config['email_dns_check'] = false; - // Note: useful when SMTP server stores sent mail in user mailbox - $config['no_save_sent_messages'] = false; - -+// Improve system security by using special URL with security token. -+// This can be set to a number defining token length. Default: 16. -+// Warning: This requires http server configuration. Sample: -+// RewriteRule ^/roundcubemail/[a-f0-9]{16}/(.*) /roundcubemail/$1 [PT] -+// Alias /roundcubemail /var/www/roundcubemail/ -+// Note: Use assets_path to not prevent the browser from caching assets -+$config['use_secure_urls'] = false; -+ -+// Allows to define separate server/path for image/js/css files -+// Warning: If the domain is different cross-domain access to some -+// resources need to be allowed -+// Sample: -+// <FilesMatch ".(eot|ttf|woff)"> -+// Header set Access-Control-Allow-Origin "*" -+// </FilesMatch> -+$config['assets_path'] = ''; -+ - // ---------------------------------- - // PLUGINS - // ---------------------------------- -diff --git a/index.php b/index.php -index 9c87f9a..df05fd8 100644 ---- a/index.php -+++ b/index.php -@@ -90,9 +90,9 @@ $RCMAIL->action = $startup['action']; - - // try to log in - if ($RCMAIL->task == 'login' && $RCMAIL->action == 'login') { -- $request_valid = $_SESSION['temp'] && $RCMAIL->check_request(rcube_utils::INPUT_POST, 'login'); -+ $request_valid = $_SESSION['temp'] && $RCMAIL->check_request(); - -- // purge the session in case of new login when a session already exists -+ // purge the session in case of new login when a session already exists - $RCMAIL->kill_session(); - - $auth = $RCMAIL->plugins->exec_hook('authenticate', array( -@@ -140,7 +140,7 @@ if ($RCMAIL->task == 'login' && $RCMAIL->action == 'login') { - unset($redir['abort'], $redir['_err']); - - // send redirect -- $OUTPUT->redirect($redir); -+ $OUTPUT->redirect($redir, 0, true); - } - else { - if (!$auth['valid']) { -@@ -171,10 +171,10 @@ if ($RCMAIL->task == 'login' && $RCMAIL->action == 'login') { - } - } - --// end session (after optional referer check) --else if ($RCMAIL->task == 'logout' && isset($_SESSION['user_id']) -- && (!$RCMAIL->config->get('referer_check') || rcube_utils::check_referer()) --) { -+// end session -+else if ($RCMAIL->task == 'logout' && isset($_SESSION['user_id'])) { -+ $RCMAIL->request_security_check($mode = rcube_utils::INPUT_GET); -+ - $userdata = array( - 'user' => $_SESSION['username'], - 'host' => $_SESSION['storage_host'], -@@ -234,32 +234,9 @@ if (empty($RCMAIL->user->ID)) { - - $OUTPUT->send($plugin['task']); - } --// CSRF prevention - else { -- // don't check for valid request tokens in these actions -- $request_check_whitelist = array('login'=>1, 'spell'=>1, 'spell_html'=>1); -- -- if (!$request_check_whitelist[$RCMAIL->action]) { -- // check client X-header to verify request origin -- if ($OUTPUT->ajax_call) { -- if (rcube_utils::request_header('X-Roundcube-Request') != $RCMAIL->get_request_token()) { -- header('HTTP/1.1 403 Forbidden'); -- die("Invalid Request"); -- } -- } -- // check request token in POST form submissions -- else if (!empty($_POST) && !$RCMAIL->check_request()) { -- $OUTPUT->show_message('invalidrequest', 'error'); -- $OUTPUT->send($RCMAIL->task); -- } -- -- // check referer if configured -- if ($RCMAIL->config->get('referer_check') && !rcube_utils::check_referer()) { -- raise_error(array( -- 'code' => 403, 'type' => 'php', -- 'message' => "Referer check failed"), true, true); -- } -- } -+ // CSRF prevention -+ $RCMAIL->request_security_check(); - - // check access to disabled actions - $disabled_actions = (array) $RCMAIL->config->get('disabled_actions'); -diff --git a/plugins/acl/acl.js b/plugins/acl/acl.js -index e59ac72..1463453 100644 ---- a/plugins/acl/acl.js -+++ b/plugins/acl/acl.js -@@ -58,8 +58,11 @@ rcube_webmail.prototype.acl_delete = function() - var users = this.acl_get_usernames(); - - if (users && users.length && confirm(this.get_label('acl.deleteconfirm'))) { -- this.http_request('settings/plugin.acl', '_act=delete&_user='+urlencode(users.join(',')) -- + '&_mbox='+urlencode(this.env.mailbox), -+ this.http_post('settings/plugin.acl', { -+ _act: 'delete', -+ _user: users.join(','), -+ _mbox: this.env.mailbox -+ }, - this.set_busy(true, 'acl.deleting')); - } - } -@@ -67,7 +70,7 @@ rcube_webmail.prototype.acl_delete = function() - // Save ACL data - rcube_webmail.prototype.acl_save = function() - { -- var user = $('#acluser', this.acl_form).val(), rights = '', type; -+ var data, type, rights = '', user = $('#acluser', this.acl_form).val(); - - $((this.env.acl_advanced ? '#advancedrights :checkbox' : '#simplerights :checkbox'), this.acl_form).map(function() { - if (this.checked) -@@ -88,12 +91,18 @@ rcube_webmail.prototype.acl_save = function() - return; - } - -- this.http_request('settings/plugin.acl', '_act=save' -- + '&_user='+urlencode(user) -- + '&_acl=' +rights -- + '&_mbox='+urlencode(this.env.mailbox) -- + (this.acl_id ? '&_old='+this.acl_id : ''), -- this.set_busy(true, 'acl.saving')); -+ data = { -+ _act: 'save', -+ _user: user, -+ _acl: rights, -+ _mbox: this.env.mailbox -+ } -+ -+ if (this.acl_id) { -+ data._old = this.acl_id; -+ } -+ -+ this.http_post('settings/plugin.acl', data, this.set_busy(true, 'acl.saving')); - } - - // Cancel/Hide form -diff --git a/plugins/acl/acl.php b/plugins/acl/acl.php -index 33bd91e..cb1a720 100644 ---- a/plugins/acl/acl.php -+++ b/plugins/acl/acl.php -@@ -452,10 +452,10 @@ class acl extends rcube_plugin - */ - private function action_save() - { -- $mbox = trim(rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_GPC, true)); // UTF7-IMAP -- $user = trim(rcube_utils::get_input_value('_user', rcube_utils::INPUT_GPC)); -- $acl = trim(rcube_utils::get_input_value('_acl', rcube_utils::INPUT_GPC)); -- $oldid = trim(rcube_utils::get_input_value('_old', rcube_utils::INPUT_GPC)); -+ $mbox = trim(rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true)); // UTF7-IMAP -+ $user = trim(rcube_utils::get_input_value('_user', rcube_utils::INPUT_POST)); -+ $acl = trim(rcube_utils::get_input_value('_acl', rcube_utils::INPUT_POST)); -+ $oldid = trim(rcube_utils::get_input_value('_old', rcube_utils::INPUT_POST)); - - $acl = array_intersect(str_split($acl), $this->rights_supported()); - $users = $oldid ? array($user) : explode(',', $user); -@@ -508,8 +508,8 @@ class acl extends rcube_plugin - */ - private function action_delete() - { -- $mbox = trim(rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_GPC, true)); //UTF7-IMAP -- $user = trim(rcube_utils::get_input_value('_user', rcube_utils::INPUT_GPC)); -+ $mbox = trim(rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true)); //UTF7-IMAP -+ $user = trim(rcube_utils::get_input_value('_user', rcube_utils::INPUT_POST)); - - $user = explode(',', $user); - -diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php -index 302c7c7..cb214a1 100644 ---- a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php -+++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php -@@ -349,7 +349,7 @@ class rcube_sieve_engine - } - } - else if ($action == 'setact' && !$error) { -- $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_GPC, true); -+ $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_POST, true); - $result = $this->activate_script($script_name); - $kep14 = $this->rc->config->get('managesieve_kolab_master'); - -@@ -363,7 +363,7 @@ class rcube_sieve_engine - } - } - else if ($action == 'deact' && !$error) { -- $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_GPC, true); -+ $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_POST, true); - $result = $this->deactivate_script($script_name); - - if ($result === true) { -@@ -376,7 +376,7 @@ class rcube_sieve_engine - } - } - else if ($action == 'setdel' && !$error) { -- $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_GPC, true); -+ $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_POST, true); - $result = $this->remove_script($script_name); - - if ($result === true) { -@@ -419,14 +419,14 @@ class rcube_sieve_engine - $this->rc->output->command('managesieve_updatelist', 'list', array('list' => $result)); - } - else if ($action == 'ruleadd') { -- $rid = rcube_utils::get_input_value('_rid', rcube_utils::INPUT_GPC); -+ $rid = rcube_utils::get_input_value('_rid', rcube_utils::INPUT_POST); - $id = $this->genid(); - $content = $this->rule_div($fid, $id, false); - - $this->rc->output->command('managesieve_rulefill', $content, $id, $rid); - } - else if ($action == 'actionadd') { -- $aid = rcube_utils::get_input_value('_aid', rcube_utils::INPUT_GPC); -+ $aid = rcube_utils::get_input_value('_aid', rcube_utils::INPUT_POST); - $id = $this->genid(); - $content = $this->action_div($fid, $id, false); - -diff --git a/program/include/rcmail.php b/program/include/rcmail.php -index e1a6276..7139bf2 100644 ---- a/program/include/rcmail.php -+++ b/program/include/rcmail.php -@@ -760,49 +760,16 @@ class rcmail extends rcube - } - - /** -- * Generate a unique token to be used in a form request -- * -- * @return string The request token -- */ -- public function get_request_token() -- { -- $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->get_user_id() . $this->config->get('des_key') . $sess_id))); -- -- return $plugin['value']; -- } -- -- /** -- * Check if the current request contains a valid token -- * -- * @param int Request method -- * -- * @return boolean True if request token is valid false if not -- */ -- public function check_request($mode = rcube_utils::INPUT_POST) -- { -- $token = rcube_utils::get_input_value('_token', $mode); -- $sess_id = $_COOKIE[ini_get('session.name')]; -- -- return !empty($sess_id) && $token == $this->get_request_token(); -- } -- -- /** - * Build a valid URL to this instance of Roundcube - * - * @param mixed Either a string with the action or url parameters as key-value pairs - * @param boolean Build an URL absolute to document root - * @param boolean Create fully qualified URL including http(s):// and hostname -+ * @param bool Return absolute URL in secure location - * - * @return string Valid application URL - */ -- public function url($p, $absolute = false, $full = false) -+ public function url($p, $absolute = false, $full = false, $secure = false) - { - if (!is_array($p)) { - if (strpos($p, 'http') === 0) { -@@ -828,9 +795,21 @@ class rcmail extends rcube - } - } - -+ $base_path = preg_replace('![^/]+$!', '', strval($_SERVER['SCRIPT_NAME'])); -+ -+ if ($secure && ($token = $this->get_secure_url_token(true))) { -+ // add token to the url -+ $url = $token . '/' . $url; -+ -+ // remove old token from the path -+ $base_path = preg_replace('/\/[a-f0-9]{' . strlen($token) . '}$/', '', $base_path); -+ -+ // this need to be full url to make redirects work -+ $absolute = true; -+ } -+ - if ($absolute || $full) { - // add base path to this Roundcube installation -- $base_path = preg_replace('![^/]+$!', '', strval($_SERVER['SCRIPT_NAME'])); - if ($base_path == '') $base_path = '/'; - $prefix = $base_path; - -@@ -880,6 +859,36 @@ class rcmail extends rcube - } - - /** -+ * CSRF attack prevention code -+ * -+ * @param int Request mode -+ */ -+ public function request_security_check($mode = rcube_utils::INPUT_POST) -+ { -+ // don't check for valid request tokens in these actions -+ // @TODO: get rid of this -+ $request_check_whitelist = array('spell'=>1, 'spell_html'=>1); -+ -+ if ($request_check_whitelist[$this->action]) { -+ return; -+ } -+ -+ // check request token -+ if (!$this->check_request($mode)) { -+ self::raise_error(array( -+ 'code' => 403, 'type' => 'php', -+ 'message' => "Request security check failed"), false, true); -+ } -+ -+ // check referer if configured -+ if ($this->config->get('referer_check') && !rcube_utils::check_referer()) { -+ self::raise_error(array( -+ 'code' => 403, 'type' => 'php', -+ 'message' => "Referer check failed"), true, true); -+ } -+ } -+ -+ /** - * Registers action aliases for current task - * - * @param array $map Alias-to-filename hash array -diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php -index 0f2a0a6..5be7cd8 100644 ---- a/program/include/rcmail_output_html.php -+++ b/program/include/rcmail_output_html.php -@@ -45,6 +45,7 @@ class rcmail_output_html extends rcmail_output - protected $footer = ''; - protected $body = ''; - protected $base_path = ''; -+ protected $assets_path; - protected $devel_mode = false; - - // deprecated names of templates used before 0.5 -@@ -80,6 +81,8 @@ class rcmail_output_html extends rcmail_output - $this->set_skin($skin); - $this->set_env('skin', $skin); - -+ $this->set_assets_path($this->config->get('assets_path')); -+ - if (!empty($_REQUEST['_extwin'])) - $this->set_env('extwin', 1); - if ($this->framed || $framed) -@@ -145,6 +148,52 @@ EOF; - } - - /** -+ * Parse and set assets path -+ * -+ * @param string Assets path (relative or absolute URL) -+ */ -+ public function set_assets_path($path) -+ { -+ if (empty($path)) { -+ return; -+ } -+ -+ $path = trim($path, '/') . '/'; -+ -+ // convert to absolute URL -+ if (!preg_match('|^https?://|', $path)) { -+ // save the path to search for asset files later -+ $this->assets_dir = $path; -+ -+ $base = preg_replace('/[?#&].*$/', '', $_SERVER['REQUEST_URI']); -+ $base = rtrim($base, '/'); -+ -+ // remove url token if exists -+ if ($len = intval($this->config->get('use_secure_urls'))) { -+ $_base = explode('/', $base); -+ $length = $len > 1 ? $len : 16; // as in rcube::get_secure_url_token() -+ -+ // we can't use real token here because it -+ // does not exists in unauthenticated state, -+ // hope this will not produce false-positive matches -+ foreach ($_base as $idx => $token) { -+ if (preg_match('/^[a-f0-9]{' . $length . '}$/', $token)) { -+ unset($_base[$idx]); -+ break; -+ } -+ } -+ -+ $base = implode('/', $_base); -+ } -+ -+ $path = rcube_utils::resolve_url($base . '/' . $path); -+ } -+ -+ $this->assets_path = $path; -+ $this->set_env('assets_path', $path); -+ } -+ -+ /** - * Getter for the current page title - * - * @return string The page title -@@ -249,6 +298,7 @@ EOF; - * @param string File name/path to resolve (starting with /) - * @param string Reference to the base path of the matching skin - * @param string Additional path to search in -+ * - * @return mixed Relative path to the requested file or False if not found - */ - public function get_skin_file($file, &$skin_path = null, $add_path = null) -@@ -263,6 +313,14 @@ EOF; - if (is_file($path)) { - return $skin_path . $file; - } -+ -+ // try assets path -+ if ($this->assets_dir) { -+ $path = realpath($this->assets_dir . $skin_path . $file); -+ if (is_file($path)) { -+ return $this->assets_dir . $skin_path . $file; -+ } -+ } - } - - return false; -@@ -367,14 +425,15 @@ EOF; - /** - * Redirect to a certain url - * -- * @param mixed $p Either a string with the action or url parameters as key-value pairs -- * @param int $delay Delay in seconds -+ * @param mixed $p Either a string with the action or url parameters as key-value pairs -+ * @param int $delay Delay in seconds -+ * @param bool $secure Redirect to secure location (see rcmail::url()) - */ -- public function redirect($p = array(), $delay = 1) -+ public function redirect($p = array(), $delay = 1, $secure = false) - { - if ($this->env['extwin']) - $p['extwin'] = 1; -- $location = $this->app->url($p); -+ $location = $this->app->url($p, false, false, $secure); - header('Location: ' . $location); - exit; - } -@@ -659,6 +718,28 @@ EOF; - exit; - } - -+ /** -+ * Modify path by adding URL prefix if configured -+ */ -+ public function asset_url($path) -+ { -+ // iframe content can't be in a different domain -+ // @TODO: check if assests are on a different domain -+ if (!$this->assets_path || $path == $this->env['blankpage']) { -+ return $path; -+ } -+ -+ if ($this->assets_dir && strpos($path, $this->assets_dir) === 0) { -+ $path = substr($path, strlen($this->assets_dir)); -+ } -+ -+ if ($path[0] == '.' && $path[1] == '/') { -+ $path = substr($path, 2); -+ } -+ -+ return $this->assets_path . $path; -+ } -+ - - /***** Template parsing methods *****/ - -@@ -715,6 +796,8 @@ EOF; - $file = $this->file_mod($file); - } - -+ $file = $this->asset_url($file); -+ - return $matches[1] . '=' . $matches[2] . $file . $matches[4]; - } - -@@ -732,11 +815,17 @@ EOF; - if ($fs = @filemtime($minified_file)) { - return $minified_file . '?s=' . $fs; - } -+ else if ($this->assets_dir && ($fs = @filemtime($this->assets_dir . $minified_file))) { -+ return $minified_file . '?s=' . $fs; -+ } - } - - if ($fs = @filemtime($file)) { - $file .= '?s=' . $fs; - } -+ else if ($this->assets_dir && ($fs = @filemtime($this->assets_dir . $file))) { -+ $file .= '?s=' . $fs; -+ } - - return $file; - } -@@ -1329,6 +1418,7 @@ EOF; - { - if (!preg_match('|^https?://|i', $file) && $file[0] != '/') { - $file = $this->file_mod($this->scripts_path . $file); -+ $file = $this->asset_url($file); - } - - if (!is_array($this->script_files[$position])) { -@@ -1541,7 +1631,7 @@ EOF; - } - - $attrib['name'] = $attrib['id']; -- $attrib['src'] = $attrib['src'] ? $this->abs_url($attrib['src'], true) : 'program/resources/blank.gif'; -+ $attrib['src'] = $this->asset_url($attrib['src'] ? $this->abs_url($attrib['src'], true) : 'program/resources/blank.gif'); - - // register as 'contentframe' object - if ($is_contentframe || $attrib['contentframe']) { -@@ -1758,9 +1848,11 @@ EOF; - { - $images = preg_split('/[\s\t\n,]+/', $attrib['images'], -1, PREG_SPLIT_NO_EMPTY); - $images = array_map(array($this, 'abs_url'), $images); -+ $images = array_map(array($this, 'asset_url'), $images); - -- if (empty($images) || $this->app->task == 'logout') -+ if (empty($images) || $_REQUEST['_task'] == 'logout') { - return; -+ } - - $this->add_script('var images = ' . self::json_serialize($images) .'; - for (var i=0; i<images.length; i++) { -diff --git a/program/include/rcmail_output_json.php b/program/include/rcmail_output_json.php -index fa35824..91262ac 100644 ---- a/program/include/rcmail_output_json.php -+++ b/program/include/rcmail_output_json.php -@@ -181,6 +181,11 @@ class rcmail_output_json extends rcmail_output - */ - public function raise_error($code, $message) - { -+ if ($code == 403) { -+ header('HTTP/1.1 403 Forbidden'); -+ die("Invalid Request"); -+ } -+ - $this->show_message("Application Error ($code): $message", 'error'); - $this->remote_response(); - exit; -diff --git a/program/js/app.js b/program/js/app.js -index 2018cfb..e7eae57 100644 ---- a/program/js/app.js -+++ b/program/js/app.js -@@ -58,7 +58,6 @@ function rcube_webmail() - request_timeout: 180, // seconds - draft_autosave: 0, // seconds - comm_path: './', -- blankpage: 'program/resources/blank.gif', - recipients_separator: ',', - recipients_delimiter: ', ', - popup_width: 1150, -@@ -163,6 +162,9 @@ function rcube_webmail() - return; - } - -+ if (!this.env.blankpage) -+ this.env.blankpage = (this.env.assets_path ? this.env.assets_path : '') + 'program/resources/blank.gif'; -+ - // find all registered gui containers - for (n in this.gui_containers) - this.gui_containers[n] = $('#'+this.gui_containers[n]); -@@ -1405,8 +1407,10 @@ function rcube_webmail() - - if (task == 'mail') - url += '&_mbox=INBOX'; -- else if (task == 'logout' && !this.env.server_error) -+ else if (task == 'logout' && !this.env.server_error) { -+ url += '&_token=' + this.env.request_token; - this.clear_compose_data(); -+ } - - this.redirect(url); - }; -@@ -1416,7 +1420,10 @@ function rcube_webmail() - if (!url) - url = this.env.comm_path; - -- return url.replace(/_task=[a-z0-9_-]+/i, '_task='+task); -+ if (url.match(/[?&]_task=[a-zA-Z0-9_-]+/)) -+ return url.replace(/_task=[a-zA-Z0-9_-]+/, '_task=' + task); -+ else -+ return url.replace(/\?.*$/, '') + '?_task=' + task; - }; - - this.reload = function(delay) -@@ -7967,7 +7974,7 @@ function rcube_webmail() - - img.onload = function() { ref.env.browser_capabilities.tif = 1; }; - img.onerror = function() { ref.env.browser_capabilities.tif = 0; }; -- img.src = 'program/resources/blank.tif'; -+ img.src = (this.env.assets_path ? this.env.assets_path : '') + 'program/resources/blank.tif'; - }; - - this.pdf_support_check = function() -diff --git a/program/js/editor.js b/program/js/editor.js -index b2e96d1..74a3134 100644 ---- a/program/js/editor.js -+++ b/program/js/editor.js -@@ -36,11 +36,12 @@ - function rcube_text_editor(config, id) - { - var ref = this, -+ abs_url = location.href.replace(/[?#].*$/, '').replace(/\/$/, ''), - conf = { - selector: '#' + ($('#' + id).is('.mce_editor') ? id : 'fake-editor-id'), - theme: 'modern', - language: config.lang, -- content_css: 'program/js/tinymce/roundcube/content.css?v1', -+ content_css: (rcmail.env.assets_path || '') + 'program/js/tinymce/roundcube/content.css?v1', - menubar: false, - statusbar: false, - toolbar_items_size: 'small', -@@ -82,7 +83,7 @@ function rcube_text_editor(config, id) - toolbar: 'bold italic underline | alignleft aligncenter alignright alignjustify' - + ' | bullist numlist outdent indent ltr rtl blockquote | forecolor backcolor | fontselect fontsizeselect' - + ' | link unlink table | emoticons charmap image media | code searchreplace undo redo', -- spellchecker_rpc_url: '../../../../../?_task=utils&_action=spell_html&_remote=1', -+ spellchecker_rpc_url: abs_url + '/?_task=utils&_action=spell_html&_remote=1', - spellchecker_language: rcmail.env.spell_lang, - accessibility_focus: false, - file_browser_callback: function(name, url, type, win) { ref.file_browser_callback(name, url, type); }, -diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php -index 3ab650c..5ffb0b2 100644 ---- a/program/lib/Roundcube/rcube.php -+++ b/program/lib/Roundcube/rcube.php -@@ -28,9 +28,15 @@ - */ - class rcube - { -- const INIT_WITH_DB = 1; -+ // Init options -+ const INIT_WITH_DB = 1; - const INIT_WITH_PLUGINS = 2; - -+ // Request status -+ const REQUEST_VALID = 0; -+ const REQUEST_ERROR_URL = 1; -+ const REQUEST_ERROR_TOKEN = 2; -+ - /** - * Singleton instace of rcube - * -@@ -101,6 +107,12 @@ class rcube - */ - public $user; - -+ /** -+ * Request status -+ * -+ * @var int -+ */ -+ public $request_status = 0; - - /* private/protected vars */ - protected $texts; -@@ -976,6 +988,104 @@ class rcube - - - /** -+ * Returns session token for secure URLs -+ * -+ * @param bool $generate Generate token if not exists in session yet -+ * -+ * @return string|bool Token string, False when disabled -+ */ -+ public function get_secure_url_token($generate = false) -+ { -+ if ($len = $this->config->get('use_secure_urls')) { -+ if (empty($_SESSION['secure_token']) && $generate) { -+ // generate x characters long token -+ $length = $len > 1 ? $len : 16; -+ $token = openssl_random_pseudo_bytes($length / 2); -+ $token = bin2hex($token); -+ -+ $plugin = $this->plugins->exec_hook('secure_token', -+ array('value' => $token, 'length' => $length)); -+ -+ $_SESSION['secure_token'] = $plugin['value']; -+ } -+ -+ return $_SESSION['secure_token']; -+ } -+ -+ return false; -+ } -+ -+ -+ /** -+ * Generate a unique token to be used in a form request -+ * -+ * @return string The request token -+ */ -+ public function get_request_token() -+ { -+ $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->get_user_id() . $this->config->get('des_key') . $sess_id))); -+ -+ return $plugin['value']; -+ } -+ -+ -+ /** -+ * Check if the current request contains a valid token. -+ * Empty requests aren't checked until use_secure_urls is set. -+ * -+ * @param int Request method -+ * -+ * @return boolean True if request token is valid false if not -+ */ -+ public function check_request($mode = rcube_utils::INPUT_POST) -+ { -+ // check secure token in URL if enabled -+ if ($token = $this->get_secure_url_token()) { -+ foreach (explode('/', preg_replace('/[?#&].*$/', '', $_SERVER['REQUEST_URI'])) as $tok) { -+ if ($tok == $token) { -+ return true; -+ } -+ } -+ -+ $this->request_status = self::REQUEST_ERROR_URL; -+ -+ return false; -+ } -+ -+ $sess_tok = $this->get_request_token(); -+ -+ // ajax requests -+ if (rcube_utils::request_header('X-Roundcube-Request') == $sess_tok) { -+ return true; -+ } -+ -+ // skip empty requests -+ if (($mode == rcube_utils::INPUT_POST && empty($_POST)) -+ || ($mode == rcube_utils::INPUT_GET && empty($_GET)) -+ ) { -+ return true; -+ } -+ -+ // default method of securing requests -+ $token = rcube_utils::get_input_value('_token', $mode); -+ $sess_id = $_COOKIE[ini_get('session.name')]; -+ -+ if (empty($sess_id) || $token != $sess_tok) { -+ $this->request_status = self::REQUEST_ERROR_TOKEN; -+ return false; -+ } -+ -+ return true; -+ } -+ -+ -+ /** - * Build a valid URL to this instance of Roundcube - * - * @param mixed Either a string with the action or url parameters as key-value pairs -diff --git a/program/steps/addressbook/delete.inc b/program/steps/addressbook/delete.inc -index f5b8e4e..9a23c59 100644 ---- a/program/steps/addressbook/delete.inc -+++ b/program/steps/addressbook/delete.inc -@@ -20,10 +20,11 @@ - */ - - // process ajax requests only --if (!$OUTPUT->ajax_call) -+if (!$OUTPUT->ajax_call) { - return; -+} - --$cids = rcmail_get_cids(); -+$cids = rcmail_get_cids(null, rcube_utils::INPUT_POST); - $delcnt = 0; - - // remove previous deletes -diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc -index 2c22d5a..4a0815b 100644 ---- a/program/steps/addressbook/func.inc -+++ b/program/steps/addressbook/func.inc -@@ -785,9 +785,10 @@ function rcmail_contact_photo($attrib) - if ($result = $CONTACTS->get_result()) - $record = $result->first(); - -- $photo_img = $attrib['placeholder'] ? $RCMAIL->output->get_skin_file($attrib['placeholder']) : 'program/resources/blank.gif'; -+ $photo_img = $attrib['placeholder'] ? $RCMAIL->output->abs_url($attrib['placeholder'], true) : 'program/resources/blank.gif'; - if ($record['_type'] == 'group' && $attrib['placeholdergroup']) -- $photo_img = $RCMAIL->output->get_skin_file($attrib['placeholdergroup']); -+ $photo_img = $RCMAIL->output->abs_url($attrib['placeholdergroup']); -+ $photo_img = $RCMAIL->output->asset_url($photo_img); - - $RCMAIL->output->set_env('photo_placeholder', $photo_img); - unset($attrib['placeholder']); -@@ -896,13 +897,13 @@ function rcmail_search_update($return = false) - * - * @return array List of contact IDs per-source - */ --function rcmail_get_cids($filter = null) -+function rcmail_get_cids($filter = null, $request_type = rcube_utils::INPUT_GPC) - { - // contact ID (or comma-separated list of IDs) is provided in two - // forms. If _source is an empty string then the ID is a string - // containing contact ID and source name in form: <ID>-<SOURCE> - -- $cid = rcube_utils::get_input_value('_cid', rcube_utils::INPUT_GPC); -+ $cid = rcube_utils::get_input_value('_cid', $request_type); - $source = (string) rcube_utils::get_input_value('_source', rcube_utils::INPUT_GPC); - - if (is_array($cid)) { -diff --git a/program/steps/utils/error.inc b/program/steps/utils/error.inc -index 2a3a9a6..2c4f18d 100644 ---- a/program/steps/utils/error.inc -+++ b/program/steps/utils/error.inc -@@ -50,9 +50,17 @@ else if ($ERROR_CODE == 401) { - - // forbidden due to request check - else if ($ERROR_CODE == 403) { -+ if ($_SERVER['REQUEST_METHOD'] == 'GET' && $rcmail->request_status == rcube::REQUEST_ERROR_URL) { -+ parse_str($_SERVER['QUERY_STRING'], $url); -+ $url = $rcmail->url($url, true, false, true); -+ $add = "<br /><a href=\"$url\">Click here to try again.<a/>"; -+ } -+ else { -+ $add = "Please contact your server-administrator."; -+ } -+ - $__error_title = "REQUEST CHECK FAILED"; -- $__error_text = "Access to this service was denied due to failing security checks!<br />\n" -- . "Please contact your server-administrator."; -+ $__error_text = "Access to this service was denied due to failing security checks!<br />\n$add"; - } - - // failed request (wrong step in URL) -
View file
roundcubemail-1.1-assets.patch
Added
@@ -0,0 +1,67 @@ +diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php +index aae54d4..3df5589 100644 +--- a/program/include/rcmail_output_html.php ++++ b/program/include/rcmail_output_html.php +@@ -184,7 +184,7 @@ EOF; + } + + $path = (rcube_utils::https_check() ? 'https' : 'http') . '://' +- . $_SERVER['SERVER_NAME'] . $base . '/' . $path; ++ . $_SERVER['HTTP_HOST'] . rtrim($base, '/') . '/' . ltrim($path, '/'); + } + + $this->assets_path = $path; +@@ -306,10 +306,27 @@ EOF; + } + + foreach ($skin_paths as $skin_path) { +- $path = realpath($skin_path . $file); ++ $path = $skin_path . $file; ++ ++ if (is_file($path)) { ++ return $skin_path . $file; ++ } ++ ++ $path = rtrim(RCUBE_INSTALL_PATH, '/'); ++ $path .= '/' . 'public_html/assets/'; ++ $path .= rtrim($skin_path, '/') . '/' . $file; ++ + if (is_file($path)) { + return $skin_path . $file; + } ++ ++ $path = rtrim(RCUBE_INSTALL_PATH, '/'); ++ $path .= '/' . $skin_path . $file; ++ $path = realpath($path); ++ ++ if ($skin_path[0] != '/' && is_file($path)) { ++ return $path; ++ } + } + + return false; +@@ -491,9 +491,10 @@ EOF; + + // fallback to deprecated template names + if (!is_readable($path) && $this->deprecated_templates[$realname]) { +- $path = "$skin_path/templates/" . $this->deprecated_templates[$realname] . ".html"; ++ // Try an old path ++ $tpath = "$skin_path/templates/" . $this->deprecated_templates[$realname] . ".html"; + +- if (is_readable($path)) { ++ if (is_readable($tpath)) { + rcube::raise_error(array( + 'code' => 502, 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, +@@ -552,6 +569,10 @@ EOF; + } + } + ++ if (!is_readable($path) && $path[0] != '/' && is_readable(rtrim(RCUBE_INSTALL_PATH, '/') . '/' . $path)) { ++ $path = rtrim(RCUBE_INSTALL_PATH, '/') . '/' . $path; ++ } ++ + if (is_readable($path)) { + $this->config->set('skin_path', $skin_path); + $this->base_path = preg_replace('!plugins/\w+/!', '', $skin_path); // set base_path to core skin directory (not plugin's skin) +
View file
roundcubemail-1.1-csrf.patch
Added
@@ -0,0 +1,786 @@ +diff -ur roundcubemail-1.1.orig/config/defaults.inc.php roundcubemail-1.1/config/defaults.inc.php +--- roundcubemail-1.1.orig/config/defaults.inc.php 2014-09-04 13:26:10.000000000 +0200 ++++ roundcubemail-1.1/config/defaults.inc.php 2014-09-06 14:30:33.910366429 +0200 +@@ -532,6 +532,23 @@ + // Note: useful when SMTP server stores sent mail in user mailbox + $config['no_save_sent_messages'] = false; + ++// Improve system security by using special URL with security token. ++// This can be set to a number defining token length. Default: 16. ++// Warning: This requires http server configuration. Sample: ++// RewriteRule ^/roundcubemail/[a-f0-9]{16}/(.*) /roundcubemail/$1 [PT] ++// Alias /roundcubemail /var/www/roundcubemail/ ++// Note: Use assets_path to not prevent the browser from caching assets ++$config['use_secure_urls'] = false; ++ ++// Allows to define separate server/path for image/js/css files ++// Warning: If the domain is different cross-domain access to some ++// resources need to be allowed ++// Sample: ++// <FilesMatch ".(eot|ttf|woff)"> ++// Header set Access-Control-Allow-Origin "*" ++// </FilesMatch> ++$config['assets_path'] = ''; ++ + // ---------------------------------- + // PLUGINS + // ---------------------------------- +diff -ur roundcubemail-1.1.orig/.htaccess roundcubemail-1.1/.htaccess +--- roundcubemail-1.1.orig/.htaccess 2014-09-04 13:26:10.000000000 +0200 ++++ roundcubemail-1.1/.htaccess 2014-09-06 14:30:33.908366428 +0200 +@@ -31,7 +31,7 @@ + # security rules: + # - deny access to files not containing a dot or starting with a dot + # in all locations except installer directory +-RewriteRule ^(?!installer)(\.?[^\.]+)$ - [F] ++RewriteRule ^(?!installer|[a-f0-9]{16})(\.?[^\.]+)$ - [F] + # - deny access to some locations + RewriteRule ^/?(\.git|\.tx|SQL|bin|config|logs|temp|tests|program\/(include|lib|localization|steps)) - [F] + # - deny access to some documentation files +diff -ur roundcubemail-1.1.orig/index.php roundcubemail-1.1/index.php +--- roundcubemail-1.1.orig/index.php 2014-09-04 13:26:10.000000000 +0200 ++++ roundcubemail-1.1/index.php 2014-09-06 14:30:33.911366430 +0200 +@@ -90,9 +90,9 @@ + + // try to log in + if ($RCMAIL->task == 'login' && $RCMAIL->action == 'login') { +- $request_valid = $_SESSION['temp'] && $RCMAIL->check_request(rcube_utils::INPUT_POST, 'login'); ++ $request_valid = $_SESSION['temp'] && $RCMAIL->check_request(); + +- // purge the session in case of new login when a session already exists ++ // purge the session in case of new login when a session already exists + $RCMAIL->kill_session(); + + $auth = $RCMAIL->plugins->exec_hook('authenticate', array( +@@ -140,7 +140,7 @@ + unset($redir['abort'], $redir['_err']); + + // send redirect +- $OUTPUT->redirect($redir); ++ $OUTPUT->redirect($redir, 0, true); + } + else { + if (!$auth['valid']) { +@@ -171,10 +171,10 @@ + } + } + +-// end session (after optional referer check) +-else if ($RCMAIL->task == 'logout' && isset($_SESSION['user_id']) +- && (!$RCMAIL->config->get('referer_check') || rcube_utils::check_referer()) +-) { ++// end session ++else if ($RCMAIL->task == 'logout' && isset($_SESSION['user_id'])) { ++ $RCMAIL->request_security_check($mode = rcube_utils::INPUT_GET); ++ + $userdata = array( + 'user' => $_SESSION['username'], + 'host' => $_SESSION['storage_host'], +@@ -234,32 +234,9 @@ + + $OUTPUT->send($plugin['task']); + } +-// CSRF prevention + else { +- // don't check for valid request tokens in these actions +- $request_check_whitelist = array('login'=>1, 'spell'=>1, 'spell_html'=>1); +- +- if (!$request_check_whitelist[$RCMAIL->action]) { +- // check client X-header to verify request origin +- if ($OUTPUT->ajax_call) { +- if (rcube_utils::request_header('X-Roundcube-Request') != $RCMAIL->get_request_token()) { +- header('HTTP/1.1 403 Forbidden'); +- die("Invalid Request"); +- } +- } +- // check request token in POST form submissions +- else if (!empty($_POST) && !$RCMAIL->check_request()) { +- $OUTPUT->show_message('invalidrequest', 'error'); +- $OUTPUT->send($RCMAIL->task); +- } +- +- // check referer if configured +- if ($RCMAIL->config->get('referer_check') && !rcube_utils::check_referer()) { +- raise_error(array( +- 'code' => 403, 'type' => 'php', +- 'message' => "Referer check failed"), true, true); +- } +- } ++ // CSRF prevention ++ $RCMAIL->request_security_check(); + + // check access to disabled actions + $disabled_actions = (array) $RCMAIL->config->get('disabled_actions'); +diff -ur roundcubemail-1.1.orig/plugins/acl/acl.js roundcubemail-1.1/plugins/acl/acl.js +--- roundcubemail-1.1.orig/plugins/acl/acl.js 2014-09-04 13:26:10.000000000 +0200 ++++ roundcubemail-1.1/plugins/acl/acl.js 2014-09-06 14:30:33.911366430 +0200 +@@ -58,8 +58,11 @@ + var users = this.acl_get_usernames(); + + if (users && users.length && confirm(this.get_label('acl.deleteconfirm'))) { +- this.http_request('settings/plugin.acl', '_act=delete&_user='+urlencode(users.join(',')) +- + '&_mbox='+urlencode(this.env.mailbox), ++ this.http_post('settings/plugin.acl', { ++ _act: 'delete', ++ _user: users.join(','), ++ _mbox: this.env.mailbox ++ }, + this.set_busy(true, 'acl.deleting')); + } + } +@@ -67,7 +70,7 @@ + // Save ACL data + rcube_webmail.prototype.acl_save = function() + { +- var user = $('#acluser', this.acl_form).val(), rights = '', type; ++ var data, type, rights = '', user = $('#acluser', this.acl_form).val(); + + $((this.env.acl_advanced ? '#advancedrights :checkbox' : '#simplerights :checkbox'), this.acl_form).map(function() { + if (this.checked) +@@ -88,12 +91,18 @@ + return; + } + +- this.http_request('settings/plugin.acl', '_act=save' +- + '&_user='+urlencode(user) +- + '&_acl=' +rights +- + '&_mbox='+urlencode(this.env.mailbox) +- + (this.acl_id ? '&_old='+this.acl_id : ''), +- this.set_busy(true, 'acl.saving')); ++ data = { ++ _act: 'save', ++ _user: user, ++ _acl: rights, ++ _mbox: this.env.mailbox ++ } ++ ++ if (this.acl_id) { ++ data._old = this.acl_id; ++ } ++ ++ this.http_post('settings/plugin.acl', data, this.set_busy(true, 'acl.saving')); + } + + // Cancel/Hide form +diff -ur roundcubemail-1.1.orig/plugins/acl/acl.php roundcubemail-1.1/plugins/acl/acl.php +--- roundcubemail-1.1.orig/plugins/acl/acl.php 2014-09-04 13:26:10.000000000 +0200 ++++ roundcubemail-1.1/plugins/acl/acl.php 2014-09-06 14:30:33.912366431 +0200 +@@ -452,10 +452,10 @@ + */ + private function action_save() + { +- $mbox = trim(rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_GPC, true)); // UTF7-IMAP +- $user = trim(rcube_utils::get_input_value('_user', rcube_utils::INPUT_GPC)); +- $acl = trim(rcube_utils::get_input_value('_acl', rcube_utils::INPUT_GPC)); +- $oldid = trim(rcube_utils::get_input_value('_old', rcube_utils::INPUT_GPC)); ++ $mbox = trim(rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true)); // UTF7-IMAP ++ $user = trim(rcube_utils::get_input_value('_user', rcube_utils::INPUT_POST)); ++ $acl = trim(rcube_utils::get_input_value('_acl', rcube_utils::INPUT_POST)); ++ $oldid = trim(rcube_utils::get_input_value('_old', rcube_utils::INPUT_POST)); + + $acl = array_intersect(str_split($acl), $this->rights_supported()); + $users = $oldid ? array($user) : explode(',', $user); +@@ -508,8 +508,8 @@ + */ + private function action_delete() + { +- $mbox = trim(rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_GPC, true)); //UTF7-IMAP +- $user = trim(rcube_utils::get_input_value('_user', rcube_utils::INPUT_GPC)); ++ $mbox = trim(rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true)); //UTF7-IMAP ++ $user = trim(rcube_utils::get_input_value('_user', rcube_utils::INPUT_POST)); + + $user = explode(',', $user); + +diff -ur roundcubemail-1.1.orig/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php roundcubemail-1.1/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php +--- roundcubemail-1.1.orig/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php 2014-09-04 13:26:10.000000000 +0200 ++++ roundcubemail-1.1/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php 2014-09-06 14:30:33.914366432 +0200 +@@ -349,7 +349,7 @@ + } + } + else if ($action == 'setact' && !$error) { +- $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_GPC, true); ++ $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_POST, true); + $result = $this->activate_script($script_name); + $kep14 = $this->rc->config->get('managesieve_kolab_master'); + +@@ -363,7 +363,7 @@ + } + } + else if ($action == 'deact' && !$error) { +- $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_GPC, true); ++ $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_POST, true); + $result = $this->deactivate_script($script_name); + + if ($result === true) { +@@ -376,7 +376,7 @@ + } + } + else if ($action == 'setdel' && !$error) { +- $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_GPC, true); ++ $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_POST, true); + $result = $this->remove_script($script_name); + + if ($result === true) { +@@ -419,14 +419,14 @@ + $this->rc->output->command('managesieve_updatelist', 'list', array('list' => $result)); + } + else if ($action == 'ruleadd') { +- $rid = rcube_utils::get_input_value('_rid', rcube_utils::INPUT_GPC); ++ $rid = rcube_utils::get_input_value('_rid', rcube_utils::INPUT_POST); + $id = $this->genid(); + $content = $this->rule_div($fid, $id, false); + + $this->rc->output->command('managesieve_rulefill', $content, $id, $rid); + } + else if ($action == 'actionadd') { +- $aid = rcube_utils::get_input_value('_aid', rcube_utils::INPUT_GPC); ++ $aid = rcube_utils::get_input_value('_aid', rcube_utils::INPUT_POST); + $id = $this->genid(); + $content = $this->action_div($fid, $id, false); + +Only in roundcubemail-1.1/plugins/managesieve/lib/Roundcube: rcube_sieve_engine.php.orig +diff -ur roundcubemail-1.1.orig/program/include/rcmail_output_html.php roundcubemail-1.1/program/include/rcmail_output_html.php +--- roundcubemail-1.1.orig/program/include/rcmail_output_html.php 2014-09-04 13:26:10.000000000 +0200 ++++ roundcubemail-1.1/program/include/rcmail_output_html.php 2014-09-06 14:30:33.917366435 +0200 +@@ -45,6 +45,7 @@ + protected $footer = ''; + protected $body = ''; + protected $base_path = ''; ++ protected $assets_path; + protected $devel_mode = false; + + // deprecated names of templates used before 0.5 +@@ -80,6 +81,8 @@ + $this->set_skin($skin); + $this->set_env('skin', $skin); + ++ $this->set_assets_path($this->config->get('assets_path')); ++ + if (!empty($_REQUEST['_extwin'])) + $this->set_env('extwin', 1); + if ($this->framed || $framed) +@@ -145,6 +148,50 @@ + } + + /** ++ * Parse and set assets path ++ * ++ * @param string Assets path (relative or absolute URL) ++ */ ++ public function set_assets_path($path) ++ { ++ $path = trim($path, '/') . '/'; ++ ++ if (empty($path)) { ++ return; ++ } ++ ++ // convert to absolute URL ++ if (!preg_match('|^https?://|', $path)) { ++ $base = preg_replace('/[?#&].*$/', '', $_SERVER['REQUEST_URI']); ++ $base = rtrim($base, '/'); ++ ++ // remove url token if exists ++ if ($len = intval($this->config->get('use_secure_urls'))) { ++ $_base = explode('/', $base); ++ $length = $len > 1 ? $len : 16; // as in rcube::get_secure_url_token() ++ ++ // we can't use real token here because it ++ // does not exists in unauthenticated state, ++ // hope this will not produce false-positive matches ++ foreach ($_base as $idx => $token) { ++ if (preg_match('/^[a-f0-9]{' . $length . '}$/', $token)) { ++ unset($_base[$idx]); ++ break; ++ } ++ } ++ ++ $base = implode('/', $_base); ++ } ++ ++ $path = (rcube_utils::https_check() ? 'https' : 'http') . '://' ++ . $_SERVER['SERVER_NAME'] . $base . '/' . $path; ++ } ++ ++ $this->assets_path = $path; ++ $this->set_env('assets_path', $path); ++ } ++ ++ /** + * Getter for the current page title + * + * @return string The page title +@@ -367,14 +414,15 @@ + /** + * Redirect to a certain url + * +- * @param mixed $p Either a string with the action or url parameters as key-value pairs +- * @param int $delay Delay in seconds ++ * @param mixed $p Either a string with the action or url parameters as key-value pairs ++ * @param int $delay Delay in seconds ++ * @param bool $secure Redirect to secure location (see rcmail::url()) + */ +- public function redirect($p = array(), $delay = 1) ++ public function redirect($p = array(), $delay = 1, $secure = false) + { + if ($this->env['extwin']) + $p['extwin'] = 1; +- $location = $this->app->url($p); ++ $location = $this->app->url($p, false, false, $secure); + header('Location: ' . $location); + exit; + } +@@ -659,6 +707,24 @@ + exit; + } + ++ /** ++ * Modify path by adding URL prefix if configured ++ */ ++ public function asset_url($path) ++ { ++ // iframe content can't be in a different domain ++ // @TODO: check if assests are on a different domain ++ if (!$this->assets_path || $path == $this->env['blankpage']) { ++ return $path; ++ } ++ ++ if ($path[0] == '.' && $path[1] == '/') { ++ $path = substr($path, 2); ++ } ++ ++ return $this->assets_path . $path; ++ } ++ + + /***** Template parsing methods *****/ + +@@ -715,6 +781,8 @@ + $file = $this->file_mod($file); + } + ++ $file = $this->asset_url($file); ++ + return $matches[1] . '=' . $matches[2] . $file . $matches[4]; + } + +@@ -1329,6 +1397,7 @@ + { + if (!preg_match('|^https?://|i', $file) && $file[0] != '/') { + $file = $this->file_mod($this->scripts_path . $file); ++ $file = $this->asset_url($file); + } + + if (!is_array($this->script_files[$position])) { +@@ -1541,7 +1610,7 @@ + } + + $attrib['name'] = $attrib['id']; +- $attrib['src'] = $attrib['src'] ? $this->abs_url($attrib['src'], true) : 'program/resources/blank.gif'; ++ $attrib['src'] = $this->asset_url($attrib['src'] ? $this->abs_url($attrib['src'], true) : 'program/resources/blank.gif'); + + // register as 'contentframe' object + if ($is_contentframe || $attrib['contentframe']) { +@@ -1758,9 +1827,11 @@ + { + $images = preg_split('/[\s\t\n,]+/', $attrib['images'], -1, PREG_SPLIT_NO_EMPTY); + $images = array_map(array($this, 'abs_url'), $images); ++ $images = array_map(array($this, 'asset_url'), $images); + +- if (empty($images) || $this->app->task == 'logout') ++ if (empty($images) || $_REQUEST['_task'] == 'logout') { + return; ++ } + + $this->add_script('var images = ' . self::json_serialize($images) .'; + for (var i=0; i<images.length; i++) { +diff -ur roundcubemail-1.1.orig/program/include/rcmail_output_json.php roundcubemail-1.1/program/include/rcmail_output_json.php +--- roundcubemail-1.1.orig/program/include/rcmail_output_json.php 2014-09-04 13:26:10.000000000 +0200 ++++ roundcubemail-1.1/program/include/rcmail_output_json.php 2014-09-06 14:30:33.917366435 +0200 +@@ -181,6 +181,11 @@ + */ + public function raise_error($code, $message) + { ++ if ($code == 403) { ++ header('HTTP/1.1 403 Forbidden'); ++ die("Invalid Request"); ++ } ++ + $this->show_message("Application Error ($code): $message", 'error'); + $this->remote_response(); + exit; +diff -ur roundcubemail-1.1.orig/program/include/rcmail.php roundcubemail-1.1/program/include/rcmail.php +--- roundcubemail-1.1.orig/program/include/rcmail.php 2014-09-04 13:26:10.000000000 +0200 ++++ roundcubemail-1.1/program/include/rcmail.php 2014-09-06 14:30:33.916366434 +0200 +@@ -758,49 +758,16 @@ + } + + /** +- * Generate a unique token to be used in a form request +- * +- * @return string The request token +- */ +- public function get_request_token() +- { +- $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->get_user_id() . $this->config->get('des_key') . $sess_id))); +- +- return $plugin['value']; +- } +- +- /** +- * Check if the current request contains a valid token +- * +- * @param int Request method +- * +- * @return boolean True if request token is valid false if not +- */ +- public function check_request($mode = rcube_utils::INPUT_POST) +- { +- $token = rcube_utils::get_input_value('_token', $mode); +- $sess_id = $_COOKIE[ini_get('session.name')]; +- +- return !empty($sess_id) && $token == $this->get_request_token(); +- } +- +- /** + * Build a valid URL to this instance of Roundcube + * + * @param mixed Either a string with the action or url parameters as key-value pairs + * @param boolean Build an URL absolute to document root + * @param boolean Create fully qualified URL including http(s):// and hostname ++ * @param bool Return absolute URL in secure location + * + * @return string Valid application URL + */ +- public function url($p, $absolute = false, $full = false) ++ public function url($p, $absolute = false, $full = false, $secure = false) + { + if (!is_array($p)) { + if (strpos($p, 'http') === 0) { +@@ -826,9 +793,21 @@ + } + } + ++ $base_path = preg_replace('![^/]+$!', '', strval($_SERVER['SCRIPT_NAME'])); ++ ++ if ($secure && ($token = $this->get_secure_url_token(true))) { ++ // add token to the url ++ $url = $token . '/' . $url; ++ ++ // remove old token from the path ++ $base_path = preg_replace('/\/[a-f0-9]{' . strlen($token) . '}$/', '', $base_path); ++ ++ // this need to be full url to make redirects work ++ $absolute = true; ++ } ++ + if ($absolute || $full) { + // add base path to this Roundcube installation +- $base_path = preg_replace('![^/]+$!', '', strval($_SERVER['SCRIPT_NAME'])); + if ($base_path == '') $base_path = '/'; + $prefix = $base_path; + +@@ -877,6 +856,36 @@ + } + } + ++ /** ++ * CSRF attack prevention code ++ * ++ * @param int Request mode ++ */ ++ public function request_security_check($mode = rcube_utils::INPUT_POST) ++ { ++ // don't check for valid request tokens in these actions ++ // @TODO: get rid of this ++ $request_check_whitelist = array('spell'=>1, 'spell_html'=>1); ++ ++ if ($request_check_whitelist[$this->action]) { ++ return; ++ } ++ ++ // check request token ++ if (!$this->check_request($mode)) { ++ self::raise_error(array( ++ 'code' => 403, 'type' => 'php', ++ 'message' => "Request security check failed"), false, true); ++ } ++ ++ // check referer if configured ++ if ($this->config->get('referer_check') && !rcube_utils::check_referer()) { ++ self::raise_error(array( ++ 'code' => 403, 'type' => 'php', ++ 'message' => "Referer check failed"), true, true); ++ } ++ } ++ + /** + * Registers action aliases for current task + * +diff -ur roundcubemail-1.1.orig/program/js/app.js roundcubemail-1.1/program/js/app.js +--- roundcubemail-1.1.orig/program/js/app.js 2014-09-04 13:26:10.000000000 +0200 ++++ roundcubemail-1.1/program/js/app.js 2014-09-06 14:30:33.920366437 +0200 +@@ -1405,8 +1405,10 @@ + + if (task == 'mail') + url += '&_mbox=INBOX'; +- else if (task == 'logout' && !this.env.server_error) ++ else if (task == 'logout' && !this.env.server_error) { ++ url += '&_token=' + this.env.request_token; + this.clear_compose_data(); ++ } + + this.redirect(url); + }; +@@ -1416,7 +1418,10 @@ + if (!url) + url = this.env.comm_path; + +- return url.replace(/_task=[a-z0-9_-]+/i, '_task='+task); ++ if (url.match(/[?&]_task=[a-zA-Z0-9_-]+/)) ++ return url.replace(/_task=[a-zA-Z0-9_-]+/, '_task=' + task); ++ else ++ return url.replace(/\?.*$/, '') + '?_task=' + task; + }; + + this.reload = function(delay) +diff -ur roundcubemail-1.1.orig/program/js/editor.js roundcubemail-1.1/program/js/editor.js +--- roundcubemail-1.1.orig/program/js/editor.js 2014-09-04 13:26:10.000000000 +0200 ++++ roundcubemail-1.1/program/js/editor.js 2014-09-06 14:30:33.921366438 +0200 +@@ -36,11 +36,12 @@ + function rcube_text_editor(config, id) + { + var ref = this, ++ abs_url = location.href.replace(/[?#].*$/, '').replace(/\/$/, ''), + conf = { + selector: '#' + ($('#' + id).is('.mce_editor') ? id : 'fake-editor-id'), + theme: 'modern', + language: config.lang, +- content_css: 'program/js/tinymce/roundcube/content.css?v1', ++ content_css: (rcmail.env.assets_path || '') + 'program/js/tinymce/roundcube/content.css?v1', + menubar: false, + statusbar: false, + toolbar_items_size: 'small', +@@ -82,7 +83,7 @@ + toolbar: 'bold italic underline | alignleft aligncenter alignright alignjustify' + + ' | bullist numlist outdent indent ltr rtl blockquote | forecolor backcolor | fontselect fontsizeselect' + + ' | link unlink table | emoticons charmap image media | code searchreplace undo redo', +- spellchecker_rpc_url: '../../../../../?_task=utils&_action=spell_html&_remote=1', ++ spellchecker_rpc_url: abs_url + '/?_task=utils&_action=spell_html&_remote=1', + spellchecker_language: rcmail.env.spell_lang, + accessibility_focus: false, + file_browser_callback: function(name, url, type, win) { ref.file_browser_callback(name, url, type); }, +diff -ur roundcubemail-1.1.orig/program/lib/Roundcube/rcube.php roundcubemail-1.1/program/lib/Roundcube/rcube.php +--- roundcubemail-1.1.orig/program/lib/Roundcube/rcube.php 2014-09-04 13:26:10.000000000 +0200 ++++ roundcubemail-1.1/program/lib/Roundcube/rcube.php 2014-09-06 14:30:33.921366438 +0200 +@@ -28,9 +28,15 @@ + */ + class rcube + { +- const INIT_WITH_DB = 1; ++ // Init options ++ const INIT_WITH_DB = 1; + const INIT_WITH_PLUGINS = 2; + ++ // Request status ++ const REQUEST_VALID = 0; ++ const REQUEST_ERROR_URL = 1; ++ const REQUEST_ERROR_TOKEN = 2; ++ + /** + * Singleton instace of rcube + * +@@ -101,6 +107,12 @@ + */ + public $user; + ++ /** ++ * Request status ++ * ++ * @var int ++ */ ++ public $request_status = 0; + + /* private/protected vars */ + protected $texts; +@@ -976,6 +988,104 @@ + + + /** ++ * Returns session token for secure URLs ++ * ++ * @param bool $generate Generate token if not exists in session yet ++ * ++ * @return string|bool Token string, False when disabled ++ */ ++ public function get_secure_url_token($generate = false) ++ { ++ if ($len = $this->config->get('use_secure_urls')) { ++ if (empty($_SESSION['secure_token']) && $generate) { ++ // generate x characters long token ++ $length = $len > 1 ? $len : 16; ++ $token = openssl_random_pseudo_bytes($length / 2); ++ $token = bin2hex($token); ++ ++ $plugin = $this->plugins->exec_hook('secure_token', ++ array('value' => $token, 'length' => $length)); ++ ++ $_SESSION['secure_token'] = $plugin['value']; ++ } ++ ++ return $_SESSION['secure_token']; ++ } ++ ++ return false; ++ } ++ ++ ++ /** ++ * Generate a unique token to be used in a form request ++ * ++ * @return string The request token ++ */ ++ public function get_request_token() ++ { ++ $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->get_user_id() . $this->config->get('des_key') . $sess_id))); ++ ++ return $plugin['value']; ++ } ++ ++ ++ /** ++ * Check if the current request contains a valid token. ++ * Empty requests aren't checked until use_secure_urls is set. ++ * ++ * @param int Request method ++ * ++ * @return boolean True if request token is valid false if not ++ */ ++ public function check_request($mode = rcube_utils::INPUT_POST) ++ { ++ // check secure token in URL if enabled ++ if ($token = $this->get_secure_url_token()) { ++ foreach (explode('/', preg_replace('/[?#&].*$/', '', $_SERVER['REQUEST_URI'])) as $tok) { ++ if ($tok == $token) { ++ return true; ++ } ++ } ++ ++ $this->request_status = self::REQUEST_ERROR_URL; ++ ++ return false; ++ } ++ ++ $sess_tok = $this->get_request_token(); ++ ++ // ajax requests ++ if (rcube_utils::request_header('X-Roundcube-Request') == $sess_tok) { ++ return true; ++ } ++ ++ // skip empty requests ++ if (($mode == rcube_utils::INPUT_POST && empty($_POST)) ++ || ($mode == rcube_utils::INPUT_GET && empty($_GET)) ++ ) { ++ return true; ++ } ++ ++ // default method of securing requests ++ $token = rcube_utils::get_input_value('_token', $mode); ++ $sess_id = $_COOKIE[ini_get('session.name')]; ++ ++ if (empty($sess_id) || $token != $sess_tok) { ++ $this->request_status = self::REQUEST_ERROR_TOKEN; ++ return false; ++ } ++ ++ return true; ++ } ++ ++ ++ /** + * Build a valid URL to this instance of Roundcube + * + * @param mixed Either a string with the action or url parameters as key-value pairs +@@ -1263,7 +1373,7 @@ + + $cli = php_sapi_name() == 'cli'; + +- if (($log || $terminate) && !$cli && $arg['message']) { ++ if ($log && !$cli && $arg['message']) { + $arg['fatal'] = $terminate; + self::log_bug($arg); + } +diff -ur roundcubemail-1.1.orig/program/steps/addressbook/delete.inc roundcubemail-1.1/program/steps/addressbook/delete.inc +--- roundcubemail-1.1.orig/program/steps/addressbook/delete.inc 2014-09-04 13:26:10.000000000 +0200 ++++ roundcubemail-1.1/program/steps/addressbook/delete.inc 2014-09-06 14:30:33.922366438 +0200 +@@ -20,10 +20,11 @@ + */ + + // process ajax requests only +-if (!$OUTPUT->ajax_call) ++if (!$OUTPUT->ajax_call) { + return; ++} + +-$cids = rcmail_get_cids(); ++$cids = rcmail_get_cids(null, rcube_utils::INPUT_POST); + $delcnt = 0; + + // remove previous deletes +diff -ur roundcubemail-1.1.orig/program/steps/addressbook/func.inc roundcubemail-1.1/program/steps/addressbook/func.inc +--- roundcubemail-1.1.orig/program/steps/addressbook/func.inc 2014-09-04 13:26:10.000000000 +0200 ++++ roundcubemail-1.1/program/steps/addressbook/func.inc 2014-09-06 14:30:33.922366438 +0200 +@@ -896,13 +896,13 @@ + * + * @return array List of contact IDs per-source + */ +-function rcmail_get_cids($filter = null) ++function rcmail_get_cids($filter = null, $request_type = rcube_utils::INPUT_GPC) + { + // contact ID (or comma-separated list of IDs) is provided in two + // forms. If _source is an empty string then the ID is a string + // containing contact ID and source name in form: <ID>-<SOURCE> + +- $cid = rcube_utils::get_input_value('_cid', rcube_utils::INPUT_GPC); ++ $cid = rcube_utils::get_input_value('_cid', $request_type); + $source = (string) rcube_utils::get_input_value('_source', rcube_utils::INPUT_GPC); + + if (is_array($cid)) { +diff -ur roundcubemail-1.1.orig/program/steps/utils/error.inc roundcubemail-1.1/program/steps/utils/error.inc +--- roundcubemail-1.1.orig/program/steps/utils/error.inc 2014-09-04 13:26:10.000000000 +0200 ++++ roundcubemail-1.1/program/steps/utils/error.inc 2014-09-06 14:30:33.923366439 +0200 +@@ -50,9 +50,17 @@ + + // forbidden due to request check + else if ($ERROR_CODE == 403) { ++ if ($_SERVER['REQUEST_METHOD'] == 'GET' && $rcmail->request_status == rcube::REQUEST_ERROR_URL) { ++ parse_str($_SERVER['QUERY_STRING'], $url); ++ $url = $rcmail->url($url, true, false, true); ++ $add = "<br /><a href=\"$url\">Click here to try again.<a/>"; ++ } ++ else { ++ $add = "Please contact your server-administrator."; ++ } ++ + $__error_title = "REQUEST CHECK FAILED"; +- $__error_text = "Access to this service was denied due to failing security checks!<br />\n" +- . "Please contact your server-administrator."; ++ $__error_text = "Access to this service was denied due to failing security checks!<br />\n$add"; + } + + // failed request (wrong step in URL) +
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.