diff --git a/admin/inc/class.uiconfig.inc.php b/admin/inc/class.uiconfig.inc.php index 7ea161b240..aa11c15a25 100644 --- a/admin/inc/class.uiconfig.inc.php +++ b/admin/inc/class.uiconfig.inc.php @@ -18,6 +18,11 @@ function index() { + // for POST requests validate CSRF token (or terminate request) + if ($_SERVER['REQUEST_METHOD'] == 'POST') + { + egw_csrf::validate($_POST['csrf_token'], __CLASS__); + } if ($GLOBALS['egw']->acl->check('site_config_access',1,'admin')) { $GLOBALS['egw']->redirect_link('/index.php'); @@ -179,7 +184,9 @@ $t->set_var('th_text', $GLOBALS['egw_info']['theme']['th_text']); $t->set_var('row_on', $GLOBALS['egw_info']['theme']['row_on']); $t->set_var('row_off', $GLOBALS['egw_info']['theme']['row_off']); - $t->set_var('hidden_vars',''); + $t->set_var('hidden_vars', html::input_hidden('referer', $referer). + html::input_hidden('csrf_token', egw_csrf::token(__CLASS__))); + $t->pparse('out','header'); $vars = $t->get_undefined('body'); diff --git a/phpgwapi/inc/class.egw_csrf.inc.php b/phpgwapi/inc/class.egw_csrf.inc.php new file mode 100644 index 0000000000..06eab8493a --- /dev/null +++ b/phpgwapi/inc/class.egw_csrf.inc.php @@ -0,0 +1,71 @@ + + * @copyright (c) 2014 by Ralf Becker + * @version $Id$ + */ + +/** + * Class supplying methods to prevent successful CSRF by requesting a random token, + * stored on server and validated when request get posted. + * + * CSRF token generation used openssl_random_pseudo_bytes, if available, otherwise + * mt_rand based auth::randomstring is used. + * + * CSRF tokens are stored (incl. optional purpose) in user session. + * + * If a token does not validate (incl. purpose, if specified in generation) + * the request will be imediatly terminated. + */ +class egw_csrf +{ + /** + * Get a CSRF token for an optional $purpose, which can be validated + * + * @param mixed $_purpose=true if given it need to be used in validate too! (It must NOT be NULL) + * @return string CSRF token + */ + public static function token($_purpose=true) + { + if (is_null($_purpose)) + { + throw new egw_exception_wrong_parameter(__METHOD__.'(NULL) $_purspose must NOT be NULL!'); + } + // generate random token (using oppenssl if available otherwise mt_rand based auth::randomstring) + $token = function_exists('openssl_random_pseudo_bytes') ? + base64_encode(openssl_random_pseudo_bytes(64)) : + auth::randomstring(64); + + // store it in session for later validation + egw_cache::setSession(__CLASS__, $token, $_purpose); + + return $token; + } + + /** + * Validate a CSRF token or teminate the request + * + * @param string $_token CSRF token generated with egw_csfr::token() + * @param string $_purpose=true optional purpose string passed to token method + * @param boolean $_delete_token=true true if token should be deleted after validation, it will validate no second time + */ + public static function validate($_token, $_purpose=true, $_delete_token=true) + { + $stored_purpose = egw_cache::getSession(__CLASS__, $_token); + + // if token and purpose dont validate, log and terminate request + if (!isset($stored_purpose) || $stored_purpose !== $_purpose) + { + error_log('CSRF detected from IP '.$_SERVER['REMOTE_ADDR'].' to '.$_SERVER['REQUEST_METHOD'].' '.$_SERVER['REQUEST_URI']); + if ($_POST) error_log(array2string($_POST)); + // we are not throwing an exception here, but die, to not allow catching it! + die("CSRF detected, request terminated!"); + } + if ($_delete_token) egw_cache::unsetTree (__CLASS__, $_token); + } +} \ No newline at end of file diff --git a/setup/account_migration.php b/setup/account_migration.php index b6bd61fc55..87caa2bd49 100644 --- a/setup/account_migration.php +++ b/setup/account_migration.php @@ -29,6 +29,13 @@ $setup_tpl->set_file(array( 'T_footer' => 'footer.tpl', 'T_alert_msg' => 'msg_alert_msg.tpl' )); +$setup_tpl->set_var('hidden_vars', html::input_hidden('csrf_token', egw_csrf::token(__FILE__))); + +// check CSRF token for POST requests with any content (setup uses empty POST to call it's modules!) +if ($_SERVER['REQUEST_METHOD'] == 'POST' && $_POST) +{ + egw_csrf::validate($_POST['csrf_token'], __FILE__); +} // determine from where we migrate to what if (!is_object($GLOBALS['egw_setup']->db)) diff --git a/setup/admin_account.php b/setup/admin_account.php index fc3302d54e..d2297e3e81 100644 --- a/setup/admin_account.php +++ b/setup/admin_account.php @@ -26,6 +26,12 @@ if (strpos($_SERVER['PHP_SELF'],'admin_account.php') !== false) $error = ''; if ($_POST['submit']) { + // for POST (not GET or cli call via setup_cmd_admin) validate CSRF token + if ($_SERVER['REQUEST_METHOD'] == 'POST') + { + egw_csrf::validate($_POST['csrf_token'], __FILE__); + } + /* Posted admin data */ $passwd = get_var('passwd',Array('POST')); $passwd2 = get_var('passwd2',Array('POST')); @@ -83,6 +89,8 @@ if(!$_POST['submit'] || $error) $setup_tpl->set_var('create_demo_accounts',lang('Create demo accounts')); $setup_tpl->set_var('demo_desc',lang('The username/passwords are: demo/guest, demo2/guest and demo3/guest.')); + $setup_tpl->set_var('hidden_vars', html::input_hidden('csrf_token', egw_csrf::token(__FILE__))); + $setup_tpl->set_var('lang_submit',lang('Save')); $setup_tpl->set_var('lang_cancel',lang('Cancel')); $setup_tpl->pparse('out','T_admin_account'); diff --git a/setup/applications.php b/setup/applications.php index d4de5d5f3a..cb882d4abe 100644 --- a/setup/applications.php +++ b/setup/applications.php @@ -40,6 +40,13 @@ $setup_tpl->set_file(array( 'T_login_stage_header' => 'login_stage_header.tpl', 'T_setup_main' => 'applications.tpl' )); +$setup_tpl->set_var('hidden_vars', html::input_hidden('csrf_token', egw_csrf::token(__FILE__))); + +// check CSRF token for POST requests with any content (setup uses empty POST to call it's modules!) +if ($_SERVER['REQUEST_METHOD'] == 'POST' && $_POST) +{ + egw_csrf::validate($_POST['csrf_token'], __FILE__); +} $setup_tpl->set_block('T_login_stage_header','B_multi_domain','V_multi_domain'); $setup_tpl->set_block('T_login_stage_header','B_single_domain','V_single_domain'); diff --git a/setup/config.php b/setup/config.php index 9afd3cdda2..79ef14a0fb 100644 --- a/setup/config.php +++ b/setup/config.php @@ -32,6 +32,13 @@ $setup_tpl->set_file(array( 'T_config_pre_script' => 'config_pre_script.tpl', 'T_config_post_script' => 'config_post_script.tpl' )); +$setup_tpl->set_var('hidden_vars', html::input_hidden('csrf_token', egw_csrf::token(__FILE__))); + +// check CSRF token for POST requests with any content (setup uses empty POST to call it's modules!) +if ($_SERVER['REQUEST_METHOD'] == 'POST' && $_POST) +{ + egw_csrf::validate($_POST['csrf_token'], __FILE__); +} /* Following to ensure windows file paths are saved correctly */ if (function_exists('get_magic_quotes_runtime') && get_magic_quotes_runtime()) @@ -130,16 +137,14 @@ $setup_tpl->pparse('out','T_config_pre_script'); /* Now parse each of the templates we want to show here */ class phpgw { - var $common; var $accounts; var $applications; var $db; } $GLOBALS['egw'] = new phpgw; -$GLOBALS['egw']->common =& CreateObject('phpgwapi.common'); $GLOBALS['egw']->db =& $GLOBALS['egw_setup']->db; -$t = CreateObject('phpgwapi.Template',$GLOBALS['egw']->common->get_tpl_dir('setup')); +$t = CreateObject('phpgwapi.Template', common::get_tpl_dir('setup')); $t->set_unknowns('keep'); $t->set_file(array('config' => 'config.tpl')); diff --git a/setup/db_backup.php b/setup/db_backup.php index d3c17421da..3d71a81cc5 100644 --- a/setup/db_backup.php +++ b/setup/db_backup.php @@ -49,6 +49,13 @@ $setup_tpl->set_file(array( 'T_footer' => 'footer.tpl', 'T_db_backup' => 'db_backup.tpl', )); +$setup_tpl->set_var('hidden_vars', html::input_hidden('csrf_token', egw_csrf::token(__FILE__))); + +// check CSRF token for POST requests with any content (setup uses empty POST to call it's modules!) +if ($_SERVER['REQUEST_METHOD'] == 'POST' && $_POST) +{ + egw_csrf::validate($_POST['csrf_token'], __FILE__); +} $setup_tpl->set_block('T_db_backup','schedule_row','schedule_rows'); $setup_tpl->set_block('T_db_backup','set_row','set_rows'); diff --git a/setup/index.php b/setup/index.php index 97451b8f5d..176c60ac15 100644 --- a/setup/index.php +++ b/setup/index.php @@ -268,6 +268,7 @@ switch($GLOBALS['egw_info']['setup']['stage']['db']) $setup_tpl->set_var('V_db_filled_block',$db_filled_block); break; case 5: + $setup_tpl->set_var('hidden_vars', html::input_hidden('csrf_token', egw_csrf::token(__FILE__))); $setup_tpl->set_var('are_you_sure',lang('ARE YOU SURE?')); $setup_tpl->set_var('really_uninstall_all_applications',lang('REALLY Uninstall all applications')); $setup_tpl->set_var('dropwarn',lang('Your tables will be dropped and you will lose data')); @@ -293,6 +294,7 @@ switch($GLOBALS['egw_info']['setup']['stage']['db']) $GLOBALS['egw_setup']->db->create_database($_POST['db_root'], $_POST['db_pass'],'utf8'); // create all new db's with utf8 break; case 'drop': + egw_csrf::validate($_POST['csrf_token'], __FILE__); $setup_info = $GLOBALS['egw_setup']->detection->get_versions($setup_info); $setup_info = $GLOBALS['egw_setup']->process->droptables($setup_info); break; diff --git a/setup/templates/default/account_migration.tpl b/setup/templates/default/account_migration.tpl index d0c1c243e4..65e5bc6baf 100644 --- a/setup/templates/default/account_migration.tpl +++ b/setup/templates/default/account_migration.tpl @@ -1,5 +1,6 @@
+ {hidden_vars} diff --git a/setup/templates/default/admin_account.tpl b/setup/templates/default/admin_account.tpl new file mode 100644 index 0000000000..5317b387ff --- /dev/null +++ b/setup/templates/default/admin_account.tpl @@ -0,0 +1,63 @@ + + +{hidden_vars} +
{description}
+ + + + + + + + + +
+

{description}

+

{lang_deleteall}

+ {error} +
{detailadmin}
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{adminusername}
{adminfirstname}
{adminlastname}
{adminemail}
{adminpassword}
{adminpassword2}
{admin_all_apps}{all_apps_desc}
{create_demo_accounts}{demo_desc}
+
+
+ diff --git a/setup/templates/default/applications.tpl b/setup/templates/default/applications.tpl new file mode 100644 index 0000000000..f0a148c556 --- /dev/null +++ b/setup/templates/default/applications.tpl @@ -0,0 +1,149 @@ + + + +
+
+ + + + +
{description}
+
+{hidden_vars} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +{goback} + + + + + + + + + + +
{appdata}{actions}
{app_info}{app_title}{app_currentver}{app_version}{app_install}{app_upgrade}{app_resolve}{app_remove}
  + {install_all} + + {upgrade_all} +   + {remove_all} +
{instalt}{appinfo} {apptitle} {currentver} {version} {install}{upgrade}{resolution} {remove}
{name} {details} 
{tables}
{hooks}
{deps}
{resolution}
{debug} {lang_debug} + {install_all} + + {upgrade_all} +   + {remove_all} +
+ + + + +
+ + +
+
+ + + +
+ + + + +
+
+ diff --git a/setup/templates/default/config_pre_script.tpl b/setup/templates/default/config_pre_script.tpl new file mode 100644 index 0000000000..b7eac5edcb --- /dev/null +++ b/setup/templates/default/config_pre_script.tpl @@ -0,0 +1,6 @@ + +
+{hidden_vars} + + + diff --git a/setup/templates/default/db_backup.tpl b/setup/templates/default/db_backup.tpl new file mode 100644 index 0000000000..8fda8f3e6b --- /dev/null +++ b/setup/templates/default/db_backup.tpl @@ -0,0 +1,176 @@ + +

{error_msg}

+ + + + +{hidden_vars} + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+  {stage_title} +
+ {stage_desc} +
+ {lang_scheduled_backups} + + {backup_now_button} +
+ + + + + + + + + + + + + + + + + + + + + + + +
{lang_year}{lang_month}{lang_day}{lang_dow}{lang_hour}{lang_minute}{lang_next_run}{lang_actions}
{year}{month}{day}{dow}{hour}{min}{next_run}{actions}
+
+ {lang_backup_sets} {backup_dir} + + {upload} +
+ {lang_backup_cleanup} + + {lang_backup_mincount} {backup_mincount} +
+ {lang_backup_files_info} + + {lang_backup_files} {backup_files} +
+ {backup_save_settings} +
+ + + + + + + + + + + + + + + +
{lang_filename}{lang_mod}{lang_size}{lang_actions}
{filename}{mod}{size}{actions}
+
+
+ + diff --git a/setup/templates/default/setup_db_blocks.tpl b/setup/templates/default/setup_db_blocks.tpl new file mode 100644 index 0000000000..c7f8b3fda6 --- /dev/null +++ b/setup/templates/default/setup_db_blocks.tpl @@ -0,0 +1,242 @@ + + +    + + + + + {notcomplete} + + +
+

{lang_system_charset}
{system_charset}

+

{dbnotexist}
{makesure}.

+

{instr}

+

{createdb}
+ DB root username:
+ DB root password:
+ +

+
+

+ +
+ + + + + + + + {notcomplete} + + +

{dbnotexist}
{makesure}.

+

{instr}

+
+ +

+ + + + +    + + + + + + {notcomplete} + + + {prebeta} + + + + +    + + + + + {Complete} + + +
+ + +

{dbexists}

+ +

{lang_system_charset}
{system_charset}

+ {lang_debug}
+ {coreapps} +
+ {lang_restore}
+ {upload}
+ {convert_checkbox} {lang_convert_charset} +
+ + + + +    + + + + + not complete + + + {oldver}.
+ {automatic} + {backupwarn}
+
+ + + + {lang_backup}
+ {lang_debug}
+
+
+
+
+ + + +
({dropwarn}) +
+
+ {dont_touch_my_data}.  {goto}: +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + + + + + +  {are_you_sure} + + + + {Complete} + + +
+ {hidden_vars} + + + {dropwarn} +
+
+ +
+ + + + +    + + + + + {notcomplete} + + + + + + + + + + + + +    + + + + + + + + +
+  {subtitle} +
+ {submsg} +
+  {status} +
{tableshave} {subaction}
+ +

+ + +
+ + + + +    + + + + + completed + + + {tablescurrent} +
+ + +
({dropwarn}) +
+ + + + +    + + + + + not complete + + +
+ {dbnotexist}.
+ +
+ + + + +    + + +