File roundcubemail-1.1-csrf.patch of Package roundcubemail (Revision bf1458aad63086b6e6584b282e39f559)
Currently displaying revision bf1458aad63086b6e6584b282e39f559 , Show latest
812
1
diff --git a/.htaccess b/.htaccess
2
index f21cf34..d0efd31 100644
3
--- a/.htaccess
4
+++ b/.htaccess
5
6
# security rules:
7
# - deny access to files not containing a dot or starting with a dot
8
# in all locations except installer directory
9
-RewriteRule ^(?!installer)(\.?[^\.]+)$ - [F]
10
+RewriteRule ^(?!installer|[a-f0-9]{16})(\.?[^\.]+)$ - [F]
11
# - deny access to some locations
12
RewriteRule ^/?(\.git|\.tx|SQL|bin|config|logs|temp|tests|program\/(include|lib|localization|steps)) - [F]
13
# - deny access to some documentation files
14
diff --git a/config/defaults.inc.php b/config/defaults.inc.php
15
index 56791fb..3297dc2 100644
16
--- a/config/defaults.inc.php
17
+++ b/config/defaults.inc.php
18
19
// Note: useful when SMTP server stores sent mail in user mailbox
20
$config['no_save_sent_messages'] = false;
21
22
+// Improve system security by using special URL with security token.
23
+// This can be set to a number defining token length. Default: 16.
24
+// Warning: This requires http server configuration. Sample:
25
+// RewriteRule ^/roundcubemail/[a-f0-9]{16}/(.*) /roundcubemail/$1 [PT]
26
+// Alias /roundcubemail /var/www/roundcubemail/
27
+// Note: Use assets_path to not prevent the browser from caching assets
28
+$config['use_secure_urls'] = false;
29
+
30
+// Allows to define separate server/path for image/js/css files
31
+// Warning: If the domain is different cross-domain access to some
32
+// resources need to be allowed
33
+// Sample:
34
+// <FilesMatch ".(eot|ttf|woff)">
35
+// Header set Access-Control-Allow-Origin "*"
36
+// </FilesMatch>
37
+$config['assets_path'] = '';
38
+
39
// ----------------------------------
40
// PLUGINS
41
// ----------------------------------
42
diff --git a/index.php b/index.php
43
index 3154daf..7001918 100644
44
--- a/index.php
45
+++ b/index.php
46
47
48
// try to log in
49
if ($RCMAIL->task == 'login' && $RCMAIL->action == 'login') {
50
- $request_valid = $_SESSION['temp'] && $RCMAIL->check_request(rcube_utils::INPUT_POST, 'login');
51
+ $request_valid = $_SESSION['temp'] && $RCMAIL->check_request();
52
53
- // purge the session in case of new login when a session already exists
54
+ // purge the session in case of new login when a session already exists
55
$RCMAIL->kill_session();
56
57
$auth = $RCMAIL->plugins->exec_hook('authenticate', array(
58
59
unset($redir['abort'], $redir['_err']);
60
61
// send redirect
62
- $OUTPUT->redirect($redir);
63
+ $OUTPUT->redirect($redir, 0, true);
64
}
65
else {
66
if (!$auth['valid']) {
67
68
}
69
}
70
71
-// end session (after optional referer check)
72
-else if ($RCMAIL->task == 'logout' && isset($_SESSION['user_id'])
73
- && (!$RCMAIL->config->get('referer_check') || rcube_utils::check_referer())
74
-) {
75
+// end session
76
+else if ($RCMAIL->task == 'logout' && isset($_SESSION['user_id'])) {
77
+ $RCMAIL->request_security_check($mode = rcube_utils::INPUT_GET);
78
+
79
$userdata = array(
80
'user' => $_SESSION['username'],
81
'host' => $_SESSION['storage_host'],
82
83
84
$OUTPUT->send($plugin['task']);
85
}
86
-// CSRF prevention
87
else {
88
- // don't check for valid request tokens in these actions
89
- $request_check_whitelist = array('login'=>1, 'spell'=>1, 'spell_html'=>1);
90
-
91
- if (!$request_check_whitelist[$RCMAIL->action]) {
92
- // check client X-header to verify request origin
93
- if ($OUTPUT->ajax_call) {
94
- if (rcube_utils::request_header('X-Roundcube-Request') != $RCMAIL->get_request_token()) {
95
- header('HTTP/1.1 403 Forbidden');
96
- die("Invalid Request");
97
- }
98
- }
99
- // check request token in POST form submissions
100
- else if (!empty($_POST) && !$RCMAIL->check_request()) {
101
- $OUTPUT->show_message('invalidrequest', 'error');
102
- $OUTPUT->send($RCMAIL->task);
103
- }
104
-
105
- // check referer if configured
106
- if ($RCMAIL->config->get('referer_check') && !rcube_utils::check_referer()) {
107
- raise_error(array(
108
- 'code' => 403, 'type' => 'php',
109
- 'message' => "Referer check failed"), true, true);
110
- }
111
- }
112
+ // CSRF prevention
113
+ $RCMAIL->request_security_check();
114
115
// check access to disabled actions
116
$disabled_actions = (array) $RCMAIL->config->get('disabled_actions');
117
diff --git a/plugins/acl/acl.js b/plugins/acl/acl.js
118
index e59ac72..1463453 100644
119
--- a/plugins/acl/acl.js
120
+++ b/plugins/acl/acl.js
121
122
var users = this.acl_get_usernames();
123
124
if (users && users.length && confirm(this.get_label('acl.deleteconfirm'))) {
125
- this.http_request('settings/plugin.acl', '_act=delete&_user='+urlencode(users.join(','))
126
- + '&_mbox='+urlencode(this.env.mailbox),
127
+ this.http_post('settings/plugin.acl', {
128
+ _act: 'delete',
129
+ _user: users.join(','),
130
+ _mbox: this.env.mailbox
131
+ },
132
this.set_busy(true, 'acl.deleting'));
133
}
134
}
135
136
// Save ACL data
137
rcube_webmail.prototype.acl_save = function()
138
{
139
- var user = $('#acluser', this.acl_form).val(), rights = '', type;
140
+ var data, type, rights = '', user = $('#acluser', this.acl_form).val();
141
142
$((this.env.acl_advanced ? '#advancedrights :checkbox' : '#simplerights :checkbox'), this.acl_form).map(function() {
143
if (this.checked)
144
145
return;
146
}
147
148
- this.http_request('settings/plugin.acl', '_act=save'
149
- + '&_user='+urlencode(user)
150
- + '&_acl=' +rights
151
- + '&_mbox='+urlencode(this.env.mailbox)
152
- + (this.acl_id ? '&_old='+this.acl_id : ''),
153
- this.set_busy(true, 'acl.saving'));
154
+ data = {
155
+ _act: 'save',
156
+ _user: user,
157
+ _acl: rights,
158
+ _mbox: this.env.mailbox
159
+ }
160
+
161
+ if (this.acl_id) {
162
+ data._old = this.acl_id;
163
+ }
164
+
165
+ this.http_post('settings/plugin.acl', data, this.set_busy(true, 'acl.saving'));
166
}
167
168
// Cancel/Hide form
169
diff --git a/plugins/acl/acl.php b/plugins/acl/acl.php
170
index 33bd91e..cb1a720 100644
171
--- a/plugins/acl/acl.php
172
+++ b/plugins/acl/acl.php
173
174
*/
175
private function action_save()
176
{
177
- $mbox = trim(rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_GPC, true)); // UTF7-IMAP
178
- $user = trim(rcube_utils::get_input_value('_user', rcube_utils::INPUT_GPC));
179
- $acl = trim(rcube_utils::get_input_value('_acl', rcube_utils::INPUT_GPC));
180
- $oldid = trim(rcube_utils::get_input_value('_old', rcube_utils::INPUT_GPC));
181
+ $mbox = trim(rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true)); // UTF7-IMAP
182
+ $user = trim(rcube_utils::get_input_value('_user', rcube_utils::INPUT_POST));
183
+ $acl = trim(rcube_utils::get_input_value('_acl', rcube_utils::INPUT_POST));
184
+ $oldid = trim(rcube_utils::get_input_value('_old', rcube_utils::INPUT_POST));
185
186
$acl = array_intersect(str_split($acl), $this->rights_supported());
187
$users = $oldid ? array($user) : explode(',', $user);
188
189
*/
190
private function action_delete()
191
{
192
- $mbox = trim(rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_GPC, true)); //UTF7-IMAP
193
- $user = trim(rcube_utils::get_input_value('_user', rcube_utils::INPUT_GPC));
194
+ $mbox = trim(rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true)); //UTF7-IMAP
195
+ $user = trim(rcube_utils::get_input_value('_user', rcube_utils::INPUT_POST));
196
197
$user = explode(',', $user);
198
199
diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php
200
index 59e116f..8fba018 100644
201
--- a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php
202
+++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php
203
204
}
205
}
206
else if ($action == 'setact' && !$error) {
207
- $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_GPC, true);
208
+ $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_POST, true);
209
$result = $this->activate_script($script_name);
210
$kep14 = $this->rc->config->get('managesieve_kolab_master');
211
212
213
}
214
}
215
else if ($action == 'deact' && !$error) {
216
- $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_GPC, true);
217
+ $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_POST, true);
218
$result = $this->deactivate_script($script_name);
219
220
if ($result === true) {
221
222
}
223
}
224
else if ($action == 'setdel' && !$error) {
225
- $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_GPC, true);
226
+ $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_POST, true);
227
$result = $this->remove_script($script_name);
228
229
if ($result === true) {
230
231
$this->rc->output->command('managesieve_updatelist', 'list', array('list' => $result));
232
}
233
else if ($action == 'ruleadd') {
234
- $rid = rcube_utils::get_input_value('_rid', rcube_utils::INPUT_GPC);
235
+ $rid = rcube_utils::get_input_value('_rid', rcube_utils::INPUT_POST);
236
$id = $this->genid();
237
$content = $this->rule_div($fid, $id, false);
238
239
$this->rc->output->command('managesieve_rulefill', $content, $id, $rid);
240
}
241
else if ($action == 'actionadd') {
242
- $aid = rcube_utils::get_input_value('_aid', rcube_utils::INPUT_GPC);
243
+ $aid = rcube_utils::get_input_value('_aid', rcube_utils::INPUT_POST);
244
$id = $this->genid();
245
$content = $this->action_div($fid, $id, false);
246
247
diff --git a/program/include/rcmail.php b/program/include/rcmail.php
248
index ceb369a..fba537e 100644
249
--- a/program/include/rcmail.php
250
+++ b/program/include/rcmail.php
251
252
}
253
254
/**
255
- * Generate a unique token to be used in a form request
256
- *
257
- * @return string The request token
258
- */
259
- public function get_request_token()
260
- {
261
- $sess_id = $_COOKIE[ini_get('session.name')];
262
-
263
- if (!$sess_id) {
264
- $sess_id = session_id();
265
- }
266
-
267
- $plugin = $this->plugins->exec_hook('request_token', array(
268
- 'value' => md5('RT' . $this->get_user_id() . $this->config->get('des_key') . $sess_id)));
269
-
270
- return $plugin['value'];
271
- }
272
-
273
- /**
274
- * Check if the current request contains a valid token
275
- *
276
- * @param int Request method
277
- *
278
- * @return boolean True if request token is valid false if not
279
- */
280
- public function check_request($mode = rcube_utils::INPUT_POST)
281
- {
282
- $token = rcube_utils::get_input_value('_token', $mode);
283
- $sess_id = $_COOKIE[ini_get('session.name')];
284
-
285
- return !empty($sess_id) && $token == $this->get_request_token();
286
- }
287
-
288
- /**
289
* Build a valid URL to this instance of Roundcube
290
*
291
* @param mixed Either a string with the action or url parameters as key-value pairs
292
+ * @param bool Return absolute URL to secure location
293
*
294
* @return string Valid application URL
295
*/
296
- public function url($p)
297
+ public function url($p, $secure = false)
298
{
299
if (!is_array($p)) {
300
if (strpos($p, 'http') === 0) {
301
302
$p['_task'] = $task;
303
unset($p['task']);
304
305
- $url = './' . $this->filename;
306
- $delm = '?';
307
+ $prefix = './';
308
+ $url = $this->filename;
309
+ $delm = '?';
310
311
foreach (array_reverse($p) as $key => $val) {
312
if ($val !== '' && $val !== null) {
313
314
}
315
}
316
317
- return $url;
318
+ // generate absolute URL if URL token exists
319
+ if ($secure && ($token = $this->get_secure_url_token(true))) {
320
+ $uri = preg_replace('/[?#&].*$/', '', $_SERVER['REQUEST_URI']);
321
+ if (strpos($uri, '/' . $token) === false) {
322
+ $uri = rtrim($uri, '/');
323
+ // remove old token
324
+ $uri = preg_replace('/\/[a-f0-9]{' . strlen($token) . '}$/', '', $uri);
325
+ // add new token
326
+ $uri .= '/' . $token . '/';
327
+
328
+ $prefix = (rcube_utils::https_check() ? 'https' : 'http') . '://'
329
+ . $_SERVER['SERVER_NAME'] . $uri;
330
+ }
331
+ }
332
+
333
+ return $prefix . $url;
334
}
335
336
/**
337
338
}
339
340
/**
341
+ * CSRF attack prevention code
342
+ *
343
+ * @param int Request mode
344
+ */
345
+ public function request_security_check($mode = rcube_utils::INPUT_POST)
346
+ {
347
+ // don't check for valid request tokens in these actions
348
+ // @TODO: get rid of this
349
+ $request_check_whitelist = array('spell'=>1, 'spell_html'=>1);
350
+
351
+ if ($request_check_whitelist[$this->action]) {
352
+ return;
353
+ }
354
+
355
+ // check request token
356
+ if (!$this->check_request($mode)) {
357
+ self::raise_error(array(
358
+ 'code' => 403, 'type' => 'php',
359
+ 'message' => "Request security check failed"), false, true);
360
+ }
361
+
362
+ // check referer if configured
363
+ if ($this->config->get('referer_check') && !rcube_utils::check_referer()) {
364
+ self::raise_error(array(
365
+ 'code' => 403, 'type' => 'php',
366
+ 'message' => "Referer check failed"), true, true);
367
+ }
368
+ }
369
+
370
+ /**
371
* Registers action aliases for current task
372
*
373
* @param array $map Alias-to-filename hash array
374
diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php
375
index a848246..2d8853f 100644
376
--- a/program/include/rcmail_output_html.php
377
+++ b/program/include/rcmail_output_html.php
378
379
protected $footer = '';
380
protected $body = '';
381
protected $base_path = '';
382
+ protected $assets_path;
383
protected $devel_mode = false;
384
385
// deprecated names of templates used before 0.5
386
387
$this->set_skin($skin);
388
$this->set_env('skin', $skin);
389
390
+ $this->set_assets_path($this->config->get('assets_path'));
391
+
392
if (!empty($_REQUEST['_extwin']))
393
$this->set_env('extwin', 1);
394
if ($this->framed || $framed)
395
396
}
397
398
/**
399
+ * Parse and set assets path
400
+ *
401
+ * @param string Assets path (relative or absolute URL)
402
+ */
403
+ public function set_assets_path($path)
404
+ {
405
+ $path = trim($path, '/') . '/';
406
+
407
+ if (empty($path)) {
408
+ return;
409
+ }
410
+
411
+ // convert to absolute URL
412
+ if (!preg_match('|^https?://|', $path)) {
413
+ $base = preg_replace('/[?#&].*$/', '', $_SERVER['REQUEST_URI']);
414
+ $base = rtrim($base, '/');
415
+
416
+ // remove url token if exists
417
+ if ($len = intval($this->config->get('use_secure_urls'))) {
418
+ $_base = explode('/', $base);
419
+ $length = $len > 1 ? $len : 16; // as in rcube::get_secure_url_token()
420
+
421
+ // we can't use real token here because it
422
+ // does not exists in unauthenticated state,
423
+ // hope this will not produce false-positive matches
424
+ foreach ($_base as $idx => $token) {
425
+ if (preg_match('/^[a-f0-9]{' . $length . '}$/', $token)) {
426
+ unset($_base[$idx]);
427
+ break;
428
+ }
429
+ }
430
+
431
+ $base = implode('/', $_base);
432
+ }
433
+
434
+ $path = (rcube_utils::https_check() ? 'https' : 'http') . '://'
435
+ . $_SERVER['SERVER_NAME'] . $base . '/' . $path;
436
+ }
437
+
438
+ $this->assets_path = $path;
439
+ $this->set_env('assets_path', $path);
440
+ }
441
+
442
+ /**
443
* Getter for the current page title
444
*
445
* @return string The page title
446
447
/**
448
* Redirect to a certain url
449
*
450
- * @param mixed $p Either a string with the action or url parameters as key-value pairs
451
- * @param int $delay Delay in seconds
452
+ * @param mixed $p Either a string with the action or url parameters as key-value pairs
453
+ * @param int $delay Delay in seconds
454
+ * @param bool $secure Redirect to secure location (see rcmail::url())
455
*/
456
- public function redirect($p = array(), $delay = 1)
457
+ public function redirect($p = array(), $delay = 1, $secure = false)
458
{
459
if ($this->env['extwin'])
460
$p['extwin'] = 1;
461
- $location = $this->app->url($p);
462
+ $location = $this->app->url($p, $secure);
463
header('Location: ' . $location);
464
exit;
465
}
466
467
exit;
468
}
469
470
+ /**
471
+ * Modify path by adding URL prefix if configured
472
+ */
473
+ public function asset_url($path)
474
+ {
475
+ // iframe content can't be in a different domain
476
+ // @TODO: check if assests are on a different domain
477
+ if (!$this->assets_path || $path == $this->env['blankpage']) {
478
+ return $path;
479
+ }
480
+
481
+ if ($path[0] == '.' && $path[1] == '/') {
482
+ $path = substr($path, 2);
483
+ }
484
+
485
+ return $this->assets_path . $path;
486
+ }
487
+
488
489
/***** Template parsing methods *****/
490
491
492
$file = $this->file_mod($file);
493
}
494
495
+ $file = $this->asset_url($file);
496
+
497
return $matches[1] . '=' . $matches[2] . $file . $matches[4];
498
}
499
500
501
{
502
if (!preg_match('|^https?://|i', $file) && $file[0] != '/') {
503
$file = $this->file_mod($this->scripts_path . $file);
504
+ $file = $this->asset_url($file);
505
}
506
507
if (!is_array($this->script_files[$position])) {
508
509
}
510
511
$attrib['name'] = $attrib['id'];
512
- $attrib['src'] = $attrib['src'] ? $this->abs_url($attrib['src'], true) : 'program/resources/blank.gif';
513
+ $attrib['src'] = $this->asset_url($attrib['src'] ? $this->abs_url($attrib['src'], true) : 'program/resources/blank.gif');
514
515
// register as 'contentframe' object
516
if ($is_contentframe || $attrib['contentframe']) {
517
518
{
519
$images = preg_split('/[\s\t\n,]+/', $attrib['images'], -1, PREG_SPLIT_NO_EMPTY);
520
$images = array_map(array($this, 'abs_url'), $images);
521
+ $images = array_map(array($this, 'asset_url'), $images);
522
523
- if (empty($images) || $this->app->task == 'logout')
524
+ if (empty($images) || $_REQUEST['_task'] == 'logout') {
525
return;
526
+ }
527
528
$this->add_script('var images = ' . self::json_serialize($images) .';
529
for (var i=0; i<images.length; i++) {
530
diff --git a/program/include/rcmail_output_json.php b/program/include/rcmail_output_json.php
531
index fa35824..91262ac 100644
532
--- a/program/include/rcmail_output_json.php
533
+++ b/program/include/rcmail_output_json.php
534
535
*/
536
public function raise_error($code, $message)
537
{
538
+ if ($code == 403) {
539
+ header('HTTP/1.1 403 Forbidden');
540
+ die("Invalid Request");
541
+ }
542
+
543
$this->show_message("Application Error ($code): $message", 'error');
544
$this->remote_response();
545
exit;
546
diff --git a/program/js/app.js b/program/js/app.js
547
index 5a87347..0f51331 100644
548
--- a/program/js/app.js
549
+++ b/program/js/app.js
550
551
552
if (task == 'mail')
553
url += '&_mbox=INBOX';
554
- else if (task == 'logout' && !this.env.server_error)
555
+ else if (task == 'logout' && !this.env.server_error) {
556
+ url += '&_token=' + this.env.request_token;
557
this.clear_compose_data();
558
+ }
559
560
this.redirect(url);
561
};
562
563
if (!url)
564
url = this.env.comm_path;
565
566
- return url.replace(/_task=[a-z0-9_-]+/i, '_task='+task);
567
+ if (url.match(/[?&]_task=[a-zA-Z0-9_-]+/))
568
+ return url.replace(/_task=[a-zA-Z0-9_-]+/, '_task=' + task);
569
+ else
570
+ return url.replace(/\?.*$/, '') + '?_task=' + task;
571
};
572
573
this.reload = function(delay)
574
diff --git a/program/js/editor.js b/program/js/editor.js
575
index b2e96d1..74a3134 100644
576
--- a/program/js/editor.js
577
+++ b/program/js/editor.js
578
579
function rcube_text_editor(config, id)
580
{
581
var ref = this,
582
+ abs_url = location.href.replace(/[?#].*$/, '').replace(/\/$/, ''),
583
conf = {
584
selector: '#' + ($('#' + id).is('.mce_editor') ? id : 'fake-editor-id'),
585
theme: 'modern',
586
language: config.lang,
587
- content_css: 'program/js/tinymce/roundcube/content.css?v1',
588
+ content_css: (rcmail.env.assets_path || '') + 'program/js/tinymce/roundcube/content.css?v1',
589
menubar: false,
590
statusbar: false,
591
toolbar_items_size: 'small',
592
593
toolbar: 'bold italic underline | alignleft aligncenter alignright alignjustify'
594
+ ' | bullist numlist outdent indent ltr rtl blockquote | forecolor backcolor | fontselect fontsizeselect'
595
+ ' | link unlink table | emoticons charmap image media | code searchreplace undo redo',
596
- spellchecker_rpc_url: '../../../../../?_task=utils&_action=spell_html&_remote=1',
597
+ spellchecker_rpc_url: abs_url + '/?_task=utils&_action=spell_html&_remote=1',
598
spellchecker_language: rcmail.env.spell_lang,
599
accessibility_focus: false,
600
file_browser_callback: function(name, url, type, win) { ref.file_browser_callback(name, url, type); },
601
diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php
602
index eedc46c..e557138 100644
603
--- a/program/lib/Roundcube/rcube.php
604
+++ b/program/lib/Roundcube/rcube.php
605
606
*/
607
class rcube
608
{
609
- const INIT_WITH_DB = 1;
610
+ // Init options
611
+ const INIT_WITH_DB = 1;
612
const INIT_WITH_PLUGINS = 2;
613
614
+ // Request status
615
+ const REQUEST_VALID = 0;
616
+ const REQUEST_ERROR_URL = 1;
617
+ const REQUEST_ERROR_TOKEN = 2;
618
+
619
/**
620
* Singleton instace of rcube
621
*
622
623
*/
624
public $user;
625
626
+ /**
627
+ * Request status
628
+ *
629
+ * @var int
630
+ */
631
+ public $request_status = 0;
632
633
/* private/protected vars */
634
protected $texts;
635
636
637
638
/**
639
+ * Returns session token for secure URLs
640
+ *
641
+ * @param bool $generate Generate token if not exists in session yet
642
+ *
643
+ * @return string|bool Token string, False when disabled
644
+ */
645
+ public function get_secure_url_token($generate = false)
646
+ {
647
+ if ($len = $this->config->get('use_secure_urls')) {
648
+ if (empty($_SESSION['secure_token']) && $generate) {
649
+ // generate x characters long token
650
+ $length = $len > 1 ? $len : 16;
651
+ $token = openssl_random_pseudo_bytes($length / 2);
652
+ $token = bin2hex($token);
653
+
654
+ $plugin = $this->plugins->exec_hook('secure_token',
655
+ array('value' => $token, 'length' => $length));
656
+
657
+ $_SESSION['secure_token'] = $plugin['value'];
658
+ }
659
+
660
+ return $_SESSION['secure_token'];
661
+ }
662
+
663
+ return false;
664
+ }
665
+
666
+
667
+ /**
668
+ * Generate a unique token to be used in a form request
669
+ *
670
+ * @return string The request token
671
+ */
672
+ public function get_request_token()
673
+ {
674
+ $sess_id = $_COOKIE[ini_get('session.name')];
675
+ if (!$sess_id) {
676
+ $sess_id = session_id();
677
+ }
678
+
679
+ $plugin = $this->plugins->exec_hook('request_token', array(
680
+ 'value' => md5('RT' . $this->get_user_id() . $this->config->get('des_key') . $sess_id)));
681
+
682
+ return $plugin['value'];
683
+ }
684
+
685
+
686
+ /**
687
+ * Check if the current request contains a valid token.
688
+ * Empty requests aren't checked until use_secure_urls is set.
689
+ *
690
+ * @param int Request method
691
+ *
692
+ * @return boolean True if request token is valid false if not
693
+ */
694
+ public function check_request($mode = rcube_utils::INPUT_POST)
695
+ {
696
+ // check secure token in URL if enabled
697
+ if ($token = $this->get_secure_url_token()) {
698
+ foreach (explode('/', preg_replace('/[?#&].*$/', '', $_SERVER['REQUEST_URI'])) as $tok) {
699
+ if ($tok == $token) {
700
+ return true;
701
+ }
702
+ }
703
+
704
+ $this->request_status = self::REQUEST_ERROR_URL;
705
+
706
+ return false;
707
+ }
708
+
709
+ $sess_tok = $this->get_request_token();
710
+
711
+ // ajax requests
712
+ if (rcube_utils::request_header('X-Roundcube-Request') == $sess_tok) {
713
+ return true;
714
+ }
715
+
716
+ // skip empty requests
717
+ if (($mode == rcube_utils::INPUT_POST && empty($_POST))
718
+ || ($mode == rcube_utils::INPUT_GET && empty($_GET))
719
+ ) {
720
+ return true;
721
+ }
722
+
723
+ // default method of securing requests
724
+ $token = rcube_utils::get_input_value('_token', $mode);
725
+ $sess_id = $_COOKIE[ini_get('session.name')];
726
+
727
+ if (empty($sess_id) || $token != $sess_tok) {
728
+ $this->request_status = self::REQUEST_ERROR_TOKEN;
729
+ return false;
730
+ }
731
+
732
+ return true;
733
+ }
734
+
735
+
736
+ /**
737
* Build a valid URL to this instance of Roundcube
738
*
739
* @param mixed Either a string with the action or url parameters as key-value pairs
740
741
742
$cli = php_sapi_name() == 'cli';
743
744
- if (($log || $terminate) && !$cli && $arg['message']) {
745
+ if ($log && !$cli && $arg['message']) {
746
$arg['fatal'] = $terminate;
747
self::log_bug($arg);
748
}
749
diff --git a/program/steps/addressbook/delete.inc b/program/steps/addressbook/delete.inc
750
index 3d57d70..727b597 100644
751
--- a/program/steps/addressbook/delete.inc
752
+++ b/program/steps/addressbook/delete.inc
753
754
*/
755
756
// process ajax requests only
757
-if (!$OUTPUT->ajax_call)
758
+if (!$OUTPUT->ajax_call) {
759
return;
760
+}
761
762
-$cids = rcmail_get_cids();
763
+$cids = rcmail_get_cids(null, rcube_utils::INPUT_POST);
764
$delcnt = 0;
765
766
// remove previous deletes
767
diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc
768
index 2c22d5a..84dff35 100644
769
--- a/program/steps/addressbook/func.inc
770
+++ b/program/steps/addressbook/func.inc
771
772
*
773
* @return array List of contact IDs per-source
774
*/
775
-function rcmail_get_cids($filter = null)
776
+function rcmail_get_cids($filter = null, $request_type = rcube_utils::INPUT_GPC)
777
{
778
// contact ID (or comma-separated list of IDs) is provided in two
779
// forms. If _source is an empty string then the ID is a string
780
// containing contact ID and source name in form: <ID>-<SOURCE>
781
782
- $cid = rcube_utils::get_input_value('_cid', rcube_utils::INPUT_GPC);
783
+ $cid = rcube_utils::get_input_value('_cid', $request_type);
784
$source = (string) rcube_utils::get_input_value('_source', rcube_utils::INPUT_GPC);
785
786
if (is_array($cid)) {
787
diff --git a/program/steps/utils/error.inc b/program/steps/utils/error.inc
788
index 2a3a9a6..b7ccf92 100644
789
--- a/program/steps/utils/error.inc
790
+++ b/program/steps/utils/error.inc
791
792
793
// forbidden due to request check
794
else if ($ERROR_CODE == 403) {
795
+ if ($_SERVER['REQUEST_METHOD'] == 'GET' && $rcmail->request_status == rcube::REQUEST_ERROR_URL) {
796
+ parse_str($_SERVER['QUERY_STRING'], $url);
797
+ $url = $rcmail->url($url, true);
798
+ $add = "<br /><a href=\"$url\">Click here to try again.<a/>";
799
+ }
800
+ else {
801
+ $add = "Please contact your server-administrator.";
802
+ }
803
+
804
$__error_title = "REQUEST CHECK FAILED";
805
- $__error_text = "Access to this service was denied due to failing security checks!<br />\n"
806
- . "Please contact your server-administrator.";
807
+ $__error_text = "Access to this service was denied due to failing security checks!<br />\n$add";
808
}
809
810
// failed request (wrong step in URL)
811
812