setup uses now sessions too and password-hashes in header.inc.php use most secure hashing type

This commit is contained in:
Ralf Becker 2013-09-11 11:35:20 +00:00
parent 871f63a9f2
commit eb06a2adee
6 changed files with 208 additions and 214 deletions

View File

@ -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) */
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"
*

View File

@ -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)
{
$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'));
$auth_type = strtolower($auth_type);
$GLOBALS['egw_info']['setup']['HeaderLoginMSG'] = $GLOBALS['egw_info']['setup']['ConfigLoginMSG'] = '';
$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]))
if(!$this->checkip($_SERVER['REMOTE_ADDR']))
{
@$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)
{
//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')
{
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,'/');
}
else
{
$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))
{
/* 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;
return false;
}
else
if (!isset($_POST['FormUser']) || !isset($_POST['FormPW']))
{
$GLOBALS['egw_info']['setup']['HeaderLoginMSG'] = lang('Invalid password');
$GLOBALS['egw_info']['setup']['ConfigLoginMSG'] = '';
return False;
return false;
}
}
elseif(!empty($HeaderPW) && $auth_type == 'Header')
switch($auth_type)
{
// 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;
}
}
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
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;
}
$_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 "<p>setup::check_auth('$user','$pw','$conf_user','$conf_pw')</p>\n";exit;
if ($user != $conf_user)
if ($user !== $conf_user)
{
return False; // wrong username
}
// Verify that $pw is not already encoded as md5
if(!preg_match('/^[0-9a-f]{32}$/',$conf_pw))
{
$conf_pw = md5($conf_pw);
}
// Verify that $pw is not already encoded as md5
if(!preg_match('/^[0-9a-f]{32}$/',$pw))
{
$pw = md5($pw);
}
return $pw == $conf_pw;
$ret = false; // wrong username
}
else
{
// 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');
}
//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 "<p>setup::checkip($remoteip) against setup_acl='".$GLOBALS['egw_info']['server']['setup_acl']."'</p>\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;

View File

@ -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';

View File

@ -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;
}
}

View File

@ -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');
}

View File

@ -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;