diff --git a/phpgwapi/inc/class.auth.inc.php b/phpgwapi/inc/class.auth.inc.php index 714c449445..0d553937f1 100644 --- a/phpgwapi/inc/class.auth.inc.php +++ b/phpgwapi/inc/class.auth.inc.php @@ -477,14 +477,17 @@ class auth * Create a password for storage in the accounts table * * @param string $password + * @param string $type=null default $GLOBALS['egw_info']['server']['sql_encryption_type'] * @return string hash */ - static function encrypt_sql($password) + static function encrypt_sql($password, $type=null) { /* Grab configured type, or default to md5() (old method) */ - $type = @$GLOBALS['egw_info']['server']['sql_encryption_type'] ? - strtolower($GLOBALS['egw_info']['server']['sql_encryption_type']) : 'md5'; - + if (is_null($type)) + { + $type = @$GLOBALS['egw_info']['server']['sql_encryption_type'] ? + strtolower($GLOBALS['egw_info']['server']['sql_encryption_type']) : 'md5'; + } switch($type) { case 'plain': @@ -521,6 +524,59 @@ class auth return $e_password; } + /** + * Get available password hashes sorted by securest first + * + * @param string &$securest=null on return securest available hash + * @return array hash => label + */ + public static function passwdhashes(&$securest=null) + { + $hashes = array(); + + /* Check for available crypt methods based on what is defined by php */ + if(defined('CRYPT_BLOWFISH') && CRYPT_BLOWFISH == 1) + { + $hashes['blowfish_crypt'] = 'blowfish_crypt'; + } + if(defined('CRYPT_SHA512') && CRYPT_SHA512 == 1) + { + $hashes['sha512_crypt'] = 'sha512_crypt'; + } + if(defined('CRYPT_SHA256') && CRYPT_SHA256 == 1) + { + $hashes['sha256_crypt'] = 'sha256_crypt'; + } + if(defined('CRYPT_MD5') && CRYPT_MD5 == 1) + { + $hashes['md5_crypt'] = 'md5_crypt'; + } + if(defined('CRYPT_EXT_DES') && CRYPT_EXT_DES == 1) + { + $hashes['ext_crypt'] = 'ext_crypt'; + } + $hashes += array( + 'ssha' => 'ssha', + 'smd5' => 'smd5', + 'sha' => 'sha', + ); + if(@defined('CRYPT_STD_DES') && CRYPT_STD_DES == 1) + { + $hashes['crypt'] = 'crypt'; + } + + $hashes += array( + 'md5' => 'md5', + 'plain' => 'plain', + ); + + // mark the securest algorithm for the user + list($securest) = each($hashes); reset($hashes); + $hashes[$securest] .= ' ('.lang('securest').')'; + + return $hashes; + } + /** * Checks if a given password is "safe" * diff --git a/setup/inc/class.setup.inc.php b/setup/inc/class.setup.inc.php index 18b72506c5..1004bea425 100644 --- a/setup/inc/class.setup.inc.php +++ b/setup/inc/class.setup.inc.php @@ -190,7 +190,9 @@ class setup { $this->set_cookiedomain(); } - setcookie($cookiename,$cookievalue,$cookietime,'/',$this->cookie_domain); + setcookie($cookiename, $cookievalue, $cookietime, '/', $this->cookie_domain, + // if called via HTTPS, only send cookie for https and only allow cookie access via HTTP (true) + !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off', true); } /** @@ -208,163 +210,113 @@ class setup return null; // not returning 'en', as it suppresses the language selection in check_install and manageheader } + /** + * Name of session cookie + */ + const SESSIONID = 'egw_setup_sessionid'; + + /** + * Session timeout in secs (1200 = 20min) + */ + const TIMEOUT = 1200; + /** * authenticate the setup user * - * @param $auth_type ??? + * @param string $auth_type='config' 'config' or 'header' (caseinsensitiv) */ - function auth($auth_type='Config') + function auth($auth_type='config') { - #phpinfo(); - $FormLogout = get_var('FormLogout', array('GET','POST')); - $ConfigLang = self::get_lang(); - if(!$FormLogout) + $auth_type = strtolower($auth_type); + $GLOBALS['egw_info']['setup']['HeaderLoginMSG'] = $GLOBALS['egw_info']['setup']['ConfigLoginMSG'] = ''; + + if(!$this->checkip($_SERVER['REMOTE_ADDR'])) { - $ConfigLogin = get_var('ConfigLogin', array('POST')); - $HeaderLogin = get_var('HeaderLogin', array('POST')); - $FormDomain = get_var('FormDomain', array('POST')); - $FormUser = get_var('FormUser', array('POST')); - $FormPW = get_var('FormPW', array('POST')); - - $this->ConfigDomain = get_var('ConfigDomain',array('POST','COOKIE')); - $ConfigUser = get_var('ConfigUser', array('POST','COOKIE')); - $ConfigPW = get_var('ConfigPW', array('POST','COOKIE')); - $HeaderUser = get_var('HeaderUser', array('POST','COOKIE')); - $HeaderPW = get_var('HeaderPW', array('POST','COOKIE')); - - /* Setup defaults to aid in header upgrade to version 1.26. - * This was the first version to include the following values. - */ - if(!@isset($GLOBALS['egw_domain'][$FormDomain]['config_user']) && isset($GLOBALS['egw_domain'][$FormDomain])) - { - @$GLOBALS['egw_domain'][$FormDomain]['config_user'] = 'admin'; - } - if(!@isset($GLOBALS['egw_info']['server']['header_admin_user'])) - { - @$GLOBALS['egw_info']['server']['header_admin_user'] = 'admin'; - } + //error_log(__METHOD__."('$auth_type') invalid IP"); + return false; } - $remoteip = $_SERVER['REMOTE_ADDR']; - if(!empty($remoteip) && !$this->checkip($remoteip)) { return False; } + // make sure we have session extension available, otherwise fail with exception that we need it + check_load_extension('session', true); - /* If FormLogout is set, simply invalidate the cookies (LOGOUT) */ - switch(strtolower($FormLogout)) + ini_set('session.use_cookie', true); + session_name(self::SESSIONID); + if (isset($_COOKIE[self::SESSIONID])) session_id($_COOKIE[self::SESSIONID]); + + if (!session_start() || isset($_REQUEST['FormLogout']) || + empty($_SESSION['egw_setup_auth_type']) || $_SESSION['egw_setup_auth_type'] != $auth_type) { - case 'config': - /* config logout */ - $expire = time() - 86400; - $this->set_cookie('ConfigUser','',$expire,'/'); - $this->set_cookie('ConfigPW','',$expire,'/'); - $this->set_cookie('ConfigDomain','',$expire,'/'); - $GLOBALS['egw_info']['setup']['LastDomain'] = $_COOKIE['ConfigDomain']; - $GLOBALS['egw_info']['setup']['ConfigLoginMSG'] = lang('You have successfully logged out'); - $GLOBALS['egw_info']['setup']['HeaderLoginMSG'] = ''; - return False; - case 'header': - /* header admin logout */ - $expire = time() - 86400; - $this->set_cookie('HeaderUser','',$expire,'/'); - $this->set_cookie('HeaderPW','',$expire,'/'); - $GLOBALS['egw_info']['setup']['HeaderLoginMSG'] = lang('You have successfully logged out'); - $GLOBALS['egw_info']['setup']['ConfigLoginMSG'] = ''; - return False; - } - - /* We get here if FormLogout is not set (LOGIN or subsequent pages) */ - /* Expire login if idle for 20 minutes. The cookies are updated on every page load. */ - $expire = (int)(time() + (1200*9)); - - switch(strtolower($auth_type)) - { - case 'header': - if(!empty($HeaderLogin)) + //error_log(__METHOD__."('$auth_type') \$_COOKIE['".self::SESSIONID."'] = ".array2string($_COOKIE[self::SESSIONID]).", \$_SESSION=".array2string($_SESSION).", \$_POST=".array2string($_POST)); + if (isset($_REQUEST['FormLogout'])) + { + $this->set_cookie(self::SESSIONID, '', time()-86400); + session_destroy(); + if ($_REQUEST['FormLogout'] == 'config') { - /* header admin login */ - /* New test is md5, cleartext version is for header < 1.26 */ - if ($this->check_auth($FormUser,$FormPW,$GLOBALS['egw_info']['server']['header_admin_user'], - $GLOBALS['egw_info']['server']['header_admin_password'])) - { - $this->set_cookie('HeaderUser',$FormUser,$expire,'/'); - $this->set_cookie('HeaderPW',md5($FormPW),$expire,'/'); - $this->set_cookie('ConfigLang',$ConfigLang,$expire,'/'); - return True; - } - else - { - $GLOBALS['egw_info']['setup']['HeaderLoginMSG'] = lang('Invalid password'); - $GLOBALS['egw_info']['setup']['ConfigLoginMSG'] = ''; - return False; - } + $GLOBALS['egw_info']['setup']['ConfigLoginMSG'] = lang('You have successfully logged out'); } - elseif(!empty($HeaderPW) && $auth_type == 'Header') + else { - // Returning after login to header admin - /* New test is md5, cleartext version is for header < 1.26 */ - if ($this->check_auth($HeaderUser,$HeaderPW,$GLOBALS['egw_info']['server']['header_admin_user'], - $GLOBALS['egw_info']['server']['header_admin_password'])) - { - $this->set_cookie('HeaderUser',$HeaderUser,$expire,'/'); - $this->set_cookie('HeaderPW',$HeaderPW,$expire,'/'); - $this->set_cookie('ConfigLang',$ConfigLang,$expire,'/'); - return True; - } - else - { - $GLOBALS['egw_info']['setup']['HeaderLoginMSG'] = lang('Invalid password'); - $GLOBALS['egw_info']['setup']['ConfigLoginMSG'] = ''; - return False; - } + $GLOBALS['egw_info']['setup']['HeaderLoginMSG'] = lang('You have successfully logged out'); } - break; - case 'config': - if(!empty($ConfigLogin)) - { - /* config login */ - /* New test is md5, cleartext version is for header < 1.26 */ - if (isset($GLOBALS['egw_domain'][$FormDomain]) && - $this->check_auth($FormUser,$FormPW,@$GLOBALS['egw_domain'][$FormDomain]['config_user'], - @$GLOBALS['egw_domain'][$FormDomain]['config_passwd'])) - { - $this->set_cookie('ConfigUser',$FormUser,$expire,'/'); - $this->set_cookie('ConfigPW',md5($FormPW),$expire,'/'); - $this->set_cookie('ConfigDomain',$FormDomain,$expire,'/'); - /* Set this now since the cookie will not be available until the next page load */ - $this->ConfigDomain = $FormDomain; - $this->set_cookie('ConfigLang',$ConfigLang,$expire,'/'); - return True; - } - else + return false; + } + if (!isset($_POST['FormUser']) || !isset($_POST['FormPW'])) + { + return false; + } + switch($auth_type) + { + case 'config': + if (!isset($GLOBALS['egw_domain'][$_POST['FormDomain']]) || + !$this->check_auth($_POST['FormUser'], $_POST['FormPW'], + $GLOBALS['egw_domain'][$_POST['FormDomain']]['config_user'], + $GLOBALS['egw_domain'][$_POST['FormDomain']]['config_passwd']) && + !$this->check_auth($_POST['FormUser'], $_POST['FormPW'], + $GLOBALS['egw_info']['server']['header_admin_user'], + $GLOBALS['egw_info']['server']['header_admin_passwd'])) { $GLOBALS['egw_info']['setup']['ConfigLoginMSG'] = lang('Invalid password'); - $GLOBALS['egw_info']['setup']['HeaderLoginMSG'] = ''; - return False; + return false; } - } - elseif(!empty($ConfigPW)) - { - // Returning after login to config - /* New test is md5, cleartext version is for header < 1.26 */ - if ($this->check_auth($ConfigUser,$ConfigPW,@$GLOBALS['egw_domain'][$this->ConfigDomain]['config_user'], - @$GLOBALS['egw_domain'][$this->ConfigDomain]['config_passwd'])) - { - $this->set_cookie('ConfigUser',$ConfigUser,$expire,'/'); - $this->set_cookie('ConfigPW',$ConfigPW,$expire,'/'); - $this->set_cookie('ConfigDomain',$this->ConfigDomain,$expire,'/'); - $this->set_cookie('ConfigLang',$ConfigLang,$expire,'/'); - return True; - } - else + $this->ConfigDomain = $_SESSION['egw_setup_domain'] = $_POST['FormDomain']; + break; + + default: + case 'header': + if (!$this->check_auth($_POST['FormUser'], $_POST['FormPW'], + $GLOBALS['egw_info']['server']['header_admin_user'], + $GLOBALS['egw_info']['server']['header_admin_password'])) { $GLOBALS['egw_info']['setup']['ConfigLoginMSG'] = lang('Invalid password'); - $GLOBALS['egw_info']['setup']['HeaderLoginMSG'] = ''; - return False; + return false; } - } - break; + break; + } + $_SESSION['egw_setup_auth_type'] = $auth_type; + $_SESSION['egw_last_action_time'] = time(); + session_regenerate_id(true); + $this->set_cookie(self::SESSIONID, session_id(), 0); + return true; } + //error_log(__METHOD__."('$auth_type') \$_COOKIE['".self::SESSIONID."'] = ".array2string($_COOKIE[self::SESSIONID]).", \$_SESSION=".array2string($_SESSION)); + if ($_SESSION['egw_last_action_time'] < time() - self::TIMEOUT) + { + $this->set_cookie(self::SESSIONID, '', time()-86400); + session_destroy(); + $GLOBALS['egw_info']['setup'][$_SESSION['egw_setup_auth_type'] == 'config' ? 'ConfigLoginMSG' : 'HeaderLoginMSG'] = + lang('Session expired'); + return false; + } + if ($auth_type == 'config') + { + $this->ConfigDomain = $_SESSION['egw_setup_domain']; + } + // update session timeout + $_SESSION['egw_last_action_time'] = time(); - return False; + // we have a valid session for $auth_type + return true; } /** @@ -377,34 +329,34 @@ class setup * @param string $user the user supplied username * @param string $pw the user supplied password * @param string $conf_user the configured username - * @param string $conf_pw the configured password - * @returns bool + * @param string $hash hash to check password agains (no {prefix} for plain and md5!) + * @returns bool true on success */ - function check_auth($user,$pw,$conf_user,$conf_pw) + function check_auth($user, $pw, $conf_user, $hash) { - #echo "

setup::check_auth('$user','$pw','$conf_user','$conf_pw')

\n";exit; - if ($user != $conf_user) + if ($user !== $conf_user) { - return False; // wrong username + $ret = false; // wrong username } - - // Verify that $pw is not already encoded as md5 - if(!preg_match('/^[0-9a-f]{32}$/',$conf_pw)) + else { - $conf_pw = md5($conf_pw); + // support for old plain-text passwords without "{plain}" prefix + if ($hash[0] !== '{' && !preg_match('/^[0-9a-f]{32}$/', $hash)) + { + $hash = '{plain}'.$hash; + } + $ret = auth::compare_password($pw, $hash, 'md5'); } - - - // Verify that $pw is not already encoded as md5 - if(!preg_match('/^[0-9a-f]{32}$/',$pw)) - { - $pw = md5($pw); - } - - return $pw == $conf_pw; - + //error_log(__METHOD__."('$user', '$pw', '$conf_user', '$hash') returning ".array2string($ret)); + return $ret; } + /** + * Check for correct IP, if an IP address should be enforced + * + * @param string $remoteip + * @return boolean + */ function checkip($remoteip='') { //echo "

setup::checkip($remoteip) against setup_acl='".$GLOBALS['egw_info']['server']['setup_acl']."'

\n"; @@ -434,7 +386,6 @@ class setup return True; // match } } - $GLOBALS['egw_info']['setup']['HeaderLoginMSG'] = ''; $GLOBALS['egw_info']['setup']['ConfigLoginMSG'] = lang('Invalid IP address'); error_log(__METHOD__.'-> checking IP failed:'.print_r($remoteip,true)); return False; diff --git a/setup/inc/class.setup_detection.inc.php b/setup/inc/class.setup_detection.inc.php index 2dc8a805c7..d05fbe073a 100755 --- a/setup/inc/class.setup_detection.inc.php +++ b/setup/inc/class.setup_detection.inc.php @@ -251,7 +251,7 @@ class setup_detection */ function check_header() { - if(!file_exists(EGW_SERVER_ROOT.'/header.inc.php')) + if(!file_exists(EGW_SERVER_ROOT.'/header.inc.php') || filesize(EGW_SERVER_ROOT.'/header.inc.php') < 200) { $GLOBALS['egw_info']['setup']['header_msg'] = 'Stage One'; return '1'; diff --git a/setup/inc/class.setup_header.inc.php b/setup/inc/class.setup_header.inc.php index 7394f54ce1..4fc95605c3 100644 --- a/setup/inc/class.setup_header.inc.php +++ b/setup/inc/class.setup_header.inc.php @@ -154,10 +154,12 @@ class setup_header */ function generate($egw_info,$egw_domain) { - $tpl =& CreateObject('phpgwapi.Template','../'); + $tpl = new Template('../', 'keep'); // 'keep' to not loose '{hash}' prefix of password-hashes! $tpl->set_file(array('header' => 'header.inc.php.template')); $tpl->set_block('header','domain','domain'); + auth::passwdhashes($most_secure_pw_hash); + foreach($egw_domain as $domain => $data) { $var = array('DB_DOMAIN' => $domain); @@ -166,7 +168,7 @@ class setup_header if ($name == 'db_port' && !$value) $value = $this->default_db_ports[$data['db_type']]; if ($name == 'config_passwd') { - $var['CONFIG_PASS'] = $this->is_md5($value) ? $value : md5($value); + $var['CONFIG_PASS'] = self::is_hashed($value) ? $value : auth::encrypt_sql($value, $most_secure_pw_hash); } else { @@ -181,7 +183,7 @@ class setup_header $var = Array(); foreach($egw_info['server'] as $name => $value) { - if ($name == 'header_admin_password' && !$this->is_md5($value)) $value = md5($value); + if ($name == 'header_admin_password' && $value && !self::is_hashed($value)) $value = auth::encrypt_sql($value, $most_secure_pw_hash); if ($name == 'versions') { $name = 'mcrypt_version'; @@ -205,12 +207,21 @@ class setup_header } /** - * Gernerate a random mcrypt_iv vector + * Generate a random mcrypt_iv vector * * @return string */ function generate_mcyrpt_iv() { + /*$mcrypt = mcrypt_module_open(egw_session::MCRYPT_ALGO, '', egw_session::MCRYPT_MODE, ''); + $size = mcrypt_enc_get_iv_size($mcrypt); + if (function_exists('mcrypt_create_iv')) // PHP 5.3+ + { + $iv = mcrypt_create_iv($size, MCRYPT_DEV_URANDOM); + error_log(__METHOD__."() size=$size returning ".array2string($iv)); + return $iv; + }*/ + $size = 30; srand((double)microtime()*1000000); $random_char = array( '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f', @@ -220,10 +231,11 @@ class setup_header ); $iv = ''; - for($i=0; $i<30; $i++) + for($i=0; $i < $size; $i++) { $iv .= $random_char[rand(1,count($random_char))]; } + //error_log(__METHOD__."() size=$size returning ".array2string($iv)); return $iv; } @@ -257,9 +269,17 @@ class setup_header return $supported_db; } - static function is_md5($str) + /** + * Check if pw is hashed + * + * @param string $pw + * @return boolean + */ + static function is_hashed($pw) { - return preg_match('/^[0-9a-f]{32}$/',$str); + $ret = $pw[0] == '{' || preg_match('/^[0-9a-f]{32}$/', $pw); + //error_log(__METHOD__."('$pw') returning ".array2string($ret)); + return $ret; } } diff --git a/setup/inc/hook_config.inc.php b/setup/inc/hook_config.inc.php index 3563494ed4..2e22c8f9b4 100644 --- a/setup/inc/hook_config.inc.php +++ b/setup/inc/hook_config.inc.php @@ -157,47 +157,7 @@ function passwdhashes($config,$return_hashes=false) function sql_passwdhashes($config, $return_hashes=false, &$securest=null) { - $hashes = array(); - - /* Check for available crypt methods based on what is defined by php */ - if(defined('CRYPT_SHA512') && CRYPT_SHA512 == 1) - { - $hashes['sha512_crypt'] = 'sha512_crypt'; - } - if(defined('CRYPT_SHA256') && CRYPT_SHA256 == 1) - { - $hashes['sha256_crypt'] = 'sha256_crypt'; - } - if(defined('CRYPT_BLOWFISH') && CRYPT_BLOWFISH == 1) - { - $hashes['blowfish_crypt'] = 'blowfish_crypt'; - } - if(defined('CRYPT_MD5') && CRYPT_MD5 == 1) - { - $hashes['md5_crypt'] = 'md5_crypt'; - } - if(defined('CRYPT_EXT_DES') && CRYPT_EXT_DES == 1) - { - $hashes['ext_crypt'] = 'ext_crypt'; - } - $hashes += array( - 'ssha' => 'ssha', - 'smd5' => 'smd5', - 'sha' => 'sha', - ); - if(@defined('CRYPT_STD_DES') && CRYPT_STD_DES == 1) - { - $hashes['crypt'] = 'crypt'; - } - - $hashes += array( - 'md5' => 'md5', - 'plain' => 'plain', - ); - - // mark the securest algorithm for the user - list($securest) = each($hashes); reset($hashes); - $hashes[$securest] .= ' ('.lang('securest').')'; + $hashes = auth::passwdhashes($securest); return $return_hashes ? $hashes : _options_from($hashes, $config['sql_encryption_type'] ? $config['sql_encryption_type'] : 'md5'); } diff --git a/setup/manageheader.php b/setup/manageheader.php index 9ed1dcdb1e..7df0d7c4d1 100644 --- a/setup/manageheader.php +++ b/setup/manageheader.php @@ -15,7 +15,7 @@ include('./inc/functions.inc.php'); require_once('./inc/class.setup_header.inc.php'); $GLOBALS['egw_setup']->header = new setup_header(); -$setup_tpl = CreateObject('phpgwapi.Template','./templates/default'); +$setup_tpl = new Template('./templates/default', 'keep'); // 'keep' to keep our {hash} prefix of passwords $setup_tpl->set_file(array( 'T_head' => 'head.tpl', 'T_footer' => 'footer.tpl', @@ -29,6 +29,13 @@ $setup_tpl->set_block('T_login_stage_header','B_single_domain','V_single_domain' $setup_tpl->set_block('T_setup_manage','manageheader','manageheader'); $setup_tpl->set_block('T_setup_manage','domain','domain'); +$setup_tpl->set_var(array( + 'lang_select' => '', + 'comment_l' => '', + 'comment_r' => '', + 'detected' => '', +)); + // authentication phase $GLOBALS['egw_info']['setup']['stage']['header'] = $GLOBALS['egw_setup']->detection->check_header(); @@ -66,7 +73,7 @@ switch($GLOBALS['egw_info']['setup']['stage']['header']) break; } -if (!file_exists('../header.inc.php') || !is_readable('../header.inc.php') || !defined('EGW_SERVER_ROOT') || EGW_SERVER_ROOT == '..') +if (!file_exists('../header.inc.php') || filesize('../header.inc.php') < 200 || !is_readable('../header.inc.php') || !defined('EGW_SERVER_ROOT') || EGW_SERVER_ROOT == '..') { $GLOBALS['egw_setup']->header->defaults(); } @@ -157,7 +164,7 @@ function check_header_form() $GLOBALS['egw_info']['server'][$name] = $value == 'True'; break; case 'new_admin_password': - if ($value) $GLOBALS['egw_info']['server']['header_admin_password'] = md5($value); + if ($value) $GLOBALS['egw_info']['server']['header_admin_password'] = $value; break; default: $GLOBALS['egw_info']['server'][$name] = $value; @@ -181,7 +188,7 @@ function check_header_form() if ($name == 'new_config_passwd') { - if ($value) $GLOBALS['egw_domain'][$domain]['config_passwd'] = md5($value); + if ($value) $GLOBALS['egw_domain'][$domain]['config_passwd'] = $value; continue; } $GLOBALS['egw_domain'][$domain][$name] = $value;