- /**
-
- Write debug messages about session verification and creation to the error_log
-
-
- This will contain passwords! Don’t leave it permanently switched on!
- */
- const ERROR_LOG_DEBUG = false;
-
- /**
-
- key of eGW’s session-data in $_SESSION
- */
- const EGW_SESSION_VAR = ‘egw_session’;
-
- /**
-
- key of eGW’s application session-data in $_SESSION
- */
- const EGW_APPSESSION_VAR = ‘egw_app_session’;
-
- /**
-
- key of eGW’s required files in $_SESSION
-
-
- These files get set by egw_db and egw class, for classes which get not autoloaded (eg. ADOdb, idots_framework)
- */
- const EGW_REQUIRED_FILES = ‘egw_required_files’;
-
- /**
-
- key of eGW’s egw_info cached in $_SESSION
- */
- const EGW_INFO_CACHE = ‘egw_info_cache’;
-
- /**
-
- key of eGW’s egw object cached in $_SESSION
- */
- const EGW_OBJECT_CACHE = ‘egw_object_cache’;
-
- /**
-
- Name of cookie or get-parameter with session-id
- */
- const EGW_SESSION_NAME = ‘sessionid’;
-
- /**
-
- Used mcrypt algorithm and mode
- */
- const MCRYPT_ALGO = MCRYPT_TRIPLEDES;
- const MCRYPT_MODE = MCRYPT_MODE_ECB;
-
- /**
-
- current user login (account_lid@domain)
-
-
- */
- var $login;
-
- /**
-
-
-
- */
- var $passwd;
-
- /**
-
- current user db/ldap account id
-
-
- */
- var $account_id;
-
- /**
-
- current user account login id (without the eGW-domain/-instance part
-
-
- */
- var $account_lid;
-
- /**
-
-
-
- */
- var $account_domain;
-
- /**
-
- type flag, A - anonymous session, N - None, normal session
-
-
- */
- var $session_flags;
-
- /**
-
-
-
- */
- var $sessionid;
-
- /**
-
- an other session specific id (md5 from a random string),
-
- used together with the sessionid for xmlrpc basic auth and the encryption of session-data (if that’s enabled)
-
-
- */
- var $kp3;
-
- /**
-
- Primary key of egw_access_log row for updates
-
-
- */
- var $sessionid_access_log;
-
- /**
-
- name of XML-RPC/SOAP method called
-
-
- */
- var $xmlrpc_method_called;
-
- /**
-
- Array with the name of the system domains
-
-
- */
- private $egw_domains;
-
- /**
-
- $_SESSION at the time the constructor was called
-
-
- */
- var $required_files;
-
- /**
-
- Nummeric code why session creation failed
-
-
- */
- var $cd_reason;
- const CD_BAD_LOGIN_OR_PASSWORD = 5;
- const CD_FORCE_PASSWORD_CHANGE = 97;
- const CD_ACCOUNT_EXPIRED = 98;
- const CD_BLOCKED = 99; // to many failed attempts to loing
-
- /**
-
- Verbose reason why session creation failed
-
-
- */
- var $reason;
-
- /**
-
- Constructor just loads up some defaults from cookies
-
-
-
@param array $domain_names =null domain-names used in this install
- */
- function __construct(array $domain_names=null)
- {
-
$this->required_files = $_SESSION[self::EGW_REQUIRED_FILES];
-
-
$this->sessionid = self::get_sessionid();
-
$this->kp3 = self::get_request('kp3');
-
-
$this->egw_domains = $domain_names;
-
-
if (!isset($GLOBALS['egw_setup']))
-
{
-
// verfiy and if necessary create and save our config settings
-
//
-
$save_rep = false;
-
if (!isset($GLOBALS['egw_info']['server']['max_access_log_age']))
-
{
-
$GLOBALS['egw_info']['server']['max_access_log_age'] = 90; // default 90 days
-
$save_rep = true;
-
}
-
if (!isset($GLOBALS['egw_info']['server']['block_time']))
-
{
-
$GLOBALS['egw_info']['server']['block_time'] = 1; // default 1min, its enough to slow down brute force attacks
-
$save_rep = true;
-
}
-
if (!isset($GLOBALS['egw_info']['server']['num_unsuccessful_id']))
-
{
-
$GLOBALS['egw_info']['server']['num_unsuccessful_id'] = 3; // default 3 trys per id
-
$save_rep = true;
-
}
-
if (!isset($GLOBALS['egw_info']['server']['num_unsuccessful_ip']))
-
{
-
$GLOBALS['egw_info']['server']['num_unsuccessful_ip'] = $GLOBALS['egw_info']['server']['num_unsuccessful_id'] * 5; // default is 5 times as high as the id default; since accessing via proxy is quite common
-
$save_rep = true;
-
}
-
if (!isset($GLOBALS['egw_info']['server']['install_id']))
-
{
-
$GLOBALS['egw_info']['server']['install_id'] = md5(common::randomstring(15));
-
}
-
if (!isset($GLOBALS['egw_info']['server']['max_history']))
-
{
-
$GLOBALS['egw_info']['server']['max_history'] = 20;
-
$save_rep = true;
-
}
-
-
if ($save_rep)
-
{
-
$config = new config('phpgwapi');
-
$config->read_repository();
-
$config->value('max_access_log_age',$GLOBALS['egw_info']['server']['max_access_log_age']);
-
$config->value('block_time',$GLOBALS['egw_info']['server']['block_time']);
-
$config->value('num_unsuccessful_id',$GLOBALS['egw_info']['server']['num_unsuccessful_id']);
-
$config->value('num_unsuccessful_ip',$GLOBALS['egw_info']['server']['num_unsuccessful_ip']);
-
$config->value('install_id',$GLOBALS['egw_info']['server']['install_id']);
-
$config->value('max_history',$GLOBALS['egw_info']['server']['max_history']);
-
$config->save_repository();
-
}
-
}
-
self::set_cookiedomain();
-
-
// set session_timeout from global php.ini and default to 14400=4h, if not set
-
if (!($GLOBALS['egw_info']['server']['sessions_timeout'] = ini_get('session.gc_maxlifetime')))
-
{
-
ini_set('session.gc_maxlifetime', $GLOBALS['egw_info']['server']['sessions_timeout']=14400);
-
}
- }
-
- /**
-
- Magic function called when this class get’s restored from the session
-
- */
- function __wakeup()
- {
-
ini_set('session.gc_maxlifetime', $GLOBALS['egw_info']['server']['sessions_timeout']);
- }
-
- /**
-
-
- */
- function __destruct()
- {
-
//if (empty($GLOBALS['egw_info']['user']['passwd']) )//|| empty($this->appsession('password','phpgwapi'))
-
//{
-
// error_log('__destruct'.'~252'.'->'." REQUEST_URI".$_SERVER['REQUEST_URI']);
-
//}
-
self::encrypt($this->kp3);
- }
-
- /**
-
- commit the sessiondata to storage
-
-
- It’s necessary to use this function instead of session_write_close() direct, as otherwise the session is not encrypted!
- */
- function commit_session()
- {
-
if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."() sessionid=$this->sessionid, _SESSION[".self::EGW_SESSION_VAR.']='.array2string($_SESSION[self::EGW_SESSION_VAR]).' '.function_backtrace());
-
self::encrypt($this->kp3);
-
-
session_write_close();
- }
-
- /**
-
- Keys of session variables which get encrypted
-
-
- */
- static $egw_session_vars = array(
-
//self::EGW_SESSION_VAR, no need to encrypt and required by the session list
-
self::EGW_APPSESSION_VAR,
-
self::EGW_INFO_CACHE,
-
self::EGW_OBJECT_CACHE,
- );
-
- static $mcrypt;
-
- /**
-
- Name of flag in session to signal it is encrypted or not
- */
- const EGW_SESSION_ENCRYPTED = ‘egw_session_encrypted’;
-
- /**
-
- Encrypt the variables in the session
-
-
- Is called by self::__destruct().
- */
- static function encrypt($kp3)
- {
-
// switch that on to analyse memory usage in the session
-
//self::log_session_usage($_SESSION[self::EGW_APPSESSION_VAR],'_SESSION['.self::EGW_APPSESSION_VAR.']',true,5000);
-
-
if (!isset($_SESSION[self::EGW_SESSION_ENCRYPTED]) && self::init_crypt($kp3))
-
{
-
foreach(self::$egw_session_vars as $name)
-
{
-
if (isset($_SESSION[$name]))
-
{
-
$_SESSION[$name] = mcrypt_generic(self::$mcrypt,serialize($_SESSION[$name]));
-
//error_log(__METHOD__."() 'encrypting' session var: $name, len=".strlen($_SESSION[$name]));
-
}
-
}
-
$_SESSION[self::EGW_SESSION_ENCRYPTED] = true; // flag session as encrypted
-
-
mcrypt_generic_deinit(self::$mcrypt);
-
self::$mcrypt = null;
-
}
- }
-
- /**
-
- Log the usage of session-vars
-
-
-
-
-
@param boolean $recursion =true if true call itself for every item > $limit
-
-
@param int $limit =1000 log only differences > $limit
- */
- static function log_session_usage(&$arr,$label,$recursion=true,$limit=1000)
- {
-
if (!is_array($arr)) return;
-
-
$sizes = array();
-
foreach($arr as $key => &$data)
-
{
-
$sizes[$key] = strlen(serialize($data));
-
}
-
arsort($sizes,SORT_NUMERIC);
-
foreach($sizes as $key => $size)
-
{
-
$diff = $size - (int)$_SESSION[$label.'-sizes'][$key];
-
$_SESSION[$label.'-sizes'][$key] = $size;
-
if ($diff > $limit)
-
{
-
error_log("strlen({$label}[$key])=".egw_vfs::hsize($size).", diff=".egw_vfs::hsize($diff));
-
if ($recursion) self::log_session_usage($arr[$key],$label.'['.$key.']',$recursion,$limit);
-
}
-
}
- }
-
- /**
-
- Decrypt the variables in the session
-
-
- Is called by self::init_handler from phpgwapi/inc/functions.inc.php (called from the header.inc.php)
-
- before the restore of the eGW enviroment takes place, so that the whole thing can be encrypted
- */
- static function decrypt()
- {
-
if ($_SESSION[self::EGW_SESSION_ENCRYPTED] && self::init_crypt(self::get_request('kp3')))
-
{
-
foreach(self::$egw_session_vars as $name)
-
{
-
if (isset($_SESSION[$name]))
-
{
-
$_SESSION[$name] = unserialize(trim(mdecrypt_generic(self::$mcrypt,$_SESSION[$name])));
-
//error_log(__METHOD__."() 'decrypting' session var $name: gettype($name) = ".gettype($_SESSION[$name]));
-
}
-
}
-
unset($_SESSION[self::EGW_SESSION_ENCRYPTED]); // delete encryption flag
-
}
- }
-
- /**
-
- Check if session encryption is configured, possible and initialise it
-
-
-
@param string $kp3 mcrypt key transported via cookie or get parameter like the session id,
-
- unlike the session id it’s not know on the server, so only the client-request can decrypt the session!
-
-
@param string $algo =self::MCRYPT_ALGO
-
-
@param string $mode =self::MCRYPT_MODE
-
-
@return boolean true if encryption is used, false otherwise
- */
- static private function init_crypt($kp3,$algo=self::MCRYPT_ALGO,$mode=self::MCRYPT_MODE)
- {
-
if(!$GLOBALS['egw_info']['server']['mcrypt_enabled'])
-
{
-
return false; // session encryption is switched off
-
}
-
if ($GLOBALS['egw_info']['currentapp'] == 'syncml' || !$kp3)
-
{
-
$kp3 = 'staticsyncmlkp3'; // syncml has no kp3!
-
}
-
if (is_null(self::$mcrypt))
-
{
-
if (!check_load_extension('mcrypt'))
-
{
-
error_log(__METHOD__."() required PHP extension mcrypt not loaded and can not be loaded, sessions get NOT encrypted!");
-
return false;
-
}
-
if (!(self::$mcrypt = mcrypt_module_open($algo, '', $mode, '')))
-
{
-
error_log(__METHOD__."() could not mcrypt_module_open(algo='$algo','',mode='$mode',''), sessions get NOT encrypted!");
-
return false;
-
}
-
$iv_size = mcrypt_enc_get_iv_size(self::$mcrypt);
-
$iv = !isset($GLOBALS['egw_info']['server']['mcrypt_iv']) || strlen($GLOBALS['egw_info']['server']['mcrypt_iv']) < $iv_size ?
-
mcrypt_create_iv ($iv_size, MCRYPT_RAND) : substr($GLOBALS['egw_info']['server']['mcrypt_iv'],0,$iv_size);
-
-
if (mcrypt_generic_init(self::$mcrypt,$kp3, $iv) < 0)
-
{
-
error_log(__METHOD__."() could not initialise mcrypt, sessions get NOT encrypted!");
-
return self::$mcrypt = false;
-
}
-
}
-
return is_resource(self::$mcrypt);
- }
-
- /**
-
-
-
-
@param string $login user login
-
-
@param string $passwd user password
-
-
@param string $passwd_type type of password being used, ie plaintext, md5, sha1
-
-
@param boolean $no_session =false dont create a real session, eg. for GroupDAV clients using only basic auth, no cookie support
-
-
@param boolean $auth_check =true if false, the user is loged in without checking his password (eg. for single sign on), default = true
-
-
@param boolean $fail_on_forced_password_change =false true: do NOT create session, if password change requested
-
-
@return string|boolean session id or false if session was not created, $this->(cd_)reason contains cause
- */
- function create($login,$passwd = ‘’,$passwd_type = ‘’,$no_session=false,$auth_check=true,$fail_on_forced_password_change=false)
- {
-
try {
-
if (is_array($login))
-
{
-
$this->login = $login['login'];
-
$this->passwd = $login['passwd'];
-
$this->passwd_type = $login['passwd_type'];
-
$login = $this->login;
-
}
-
else
-
{
-
$this->login = $login;
-
$this->passwd = $passwd;
-
$this->passwd_type = $passwd_type;
-
}
-
if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."($this->login,$this->passwd,$this->passwd_type,$no_session,$auth_check) starting ...");
-
-
self::split_login_domain($login,$this->account_lid,$this->account_domain);
-
// add domain to the login, if not already there
-
if (substr($this->login,-strlen($this->account_domain)-1) != '@'.$this->account_domain)
-
{
-
$this->login .= '@'.$this->account_domain;
-
}
-
$now = time();
-
//error_log(__METHOD__."($login,$passwd,$passwd_type,$no_session,$auth_check) account_lid=$this->account_lid, account_domain=$this->account_domain, default_domain={$GLOBALS['egw_info']['server']['default_domain']}, user/domain={$GLOBALS['egw_info']['user']['domain']}");
-
-
// This is to ensure that we authenticate to the correct domain (might not be default)
-
// if no domain is given we use the default domain, so we dont need to re-create everything
-
if (!$GLOBALS['egw_info']['user']['domain'] && $this->account_domain == $GLOBALS['egw_info']['server']['default_domain'])
-
{
-
$GLOBALS['egw_info']['user']['domain'] = $this->account_domain;
-
}
-
elseif (!$this->account_domain && $GLOBALS['egw_info']['user']['domain'])
-
{
-
$this->account_domain = $GLOBALS['egw_info']['user']['domain'];
-
}
-
elseif($this->account_domain != $GLOBALS['egw_info']['user']['domain'])
-
{
-
throw new Exception("Wrong domain! '$this->account_domain' != '{$GLOBALS['egw_info']['user']['domain']}'");
-
}
-
unset($GLOBALS['egw_info']['server']['default_domain']); // we kill this for security reasons
-
-
//echo "<p>session::create(login='$login'): lid='$this->account_lid', domain='$this->account_domain'</p>\n";
-
$user_ip = self::getuser_ip();
-
-
$this->account_id = $GLOBALS['egw']->accounts->name2id($this->account_lid,'account_lid','u');
-
-
if (($blocked = $this->login_blocked($login,$user_ip)) || // too many unsuccessful attempts
-
$GLOBALS['egw_info']['server']['global_denied_users'][$this->account_lid] ||
-
$auth_check && !$GLOBALS['egw']->auth->authenticate($this->account_lid, $this->passwd, $this->passwd_type) ||
-
$this->account_id && $GLOBALS['egw']->accounts->get_type($this->account_id) == 'g')
-
{
-
$this->reason = $blocked ? 'blocked, too many attempts' : 'bad login or password';
-
$this->cd_reason = $blocked ? self::CD_BLOCKED : self::CD_BAD_LOGIN_OR_PASSWORD;
-
-
// we dont log anon users as it would block the website
-
if (!$GLOBALS['egw']->acl->get_specific_rights_for_account($this->account_id,'anonymous','phpgwapi'))
-
{
-
$this->log_access($this->reason,$login,$user_ip,0); // log unsuccessfull login
-
}
-
if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."($this->login,$this->passwd,$this->passwd_type,$no_session,$auth_check) UNSUCCESSFULL ($this->reason)");
-
return false;
-
}
-
if ($fail_on_forced_password_change && auth::check_password_change($this->reason) === false)
-
{
-
$this->cd_reason = self::CD_FORCE_PASSWORD_CHANGE;
-
return false;
-
}
-
if (!$this->account_id && $GLOBALS['egw_info']['server']['auto_create_acct'])
-
{
-
if ($GLOBALS['egw_info']['server']['auto_create_acct'] == 'lowercase')
-
{
-
$this->account_lid = strtolower($this->account_lid);
-
}
-
$this->account_id = $GLOBALS['egw']->accounts->auto_add($this->account_lid, $passwd);
-
}
-
// fix maybe wrong case in username, it makes problems eg. in filemanager (name of homedir)
-
if ($this->account_lid != ($lid = $GLOBALS['egw']->accounts->id2name($this->account_id)))
-
{
-
$this->account_lid = $lid;
-
$this->login = $lid.substr($this->login,strlen($lid));
-
}
-
-
$GLOBALS['egw_info']['user']['account_id'] = $this->account_id;
-
$GLOBALS['egw']->accounts->__construct();
-
$GLOBALS['egw']->accounts->setAccountId($this->account_id);
-
-
// for *DAV and eSync we use a pseudo sessionid created from md5(user:passwd)
-
// --> allows this stateless protocolls which use basic auth to use sessions!
-
if (($this->sessionid = self::get_sessionid(true)))
-
{
-
session_id($this->sessionid);
-
}
-
else
-
{
-
self::cache_control();
-
session_start();
-
// set a new session-id, if not syncml (already done in Horde code and can NOT be changed)
-
if (!$no_session && $GLOBALS['egw_info']['flags']['currentapp'] != 'syncml')
-
{
-
session_regenerate_id(true);
-
}
-
$this->sessionid = session_id();
-
}
-
$this->kp3 = common::randomstring(24);
-
-
$GLOBALS['egw_info']['user'] = $this->read_repositories();
-
if ($GLOBALS['egw']->accounts->is_expired($GLOBALS['egw_info']['user']))
-
{
-
$this->reason = 'account is expired';
-
$this->cd_reason = self::CD_ACCOUNT_EXPIRED;
-
-
if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."($this->login,$this->passwd,$this->passwd_type,$no_session,$auth_check) UNSUCCESSFULL ($this->reason)");
-
return false;
-
}
-
-
egw_cache::setSession('phpgwapi', 'password', base64_encode($this->passwd));
-
-
if ($GLOBALS['egw']->acl->check('anonymous',1,'phpgwapi'))
-
{
-
$this->session_flags = 'A';
-
}
-
else
-
{
-
$this->session_flags = 'N';
-
}
-
-
if (($hook_result = $GLOBALS['egw']->hooks->process(array(
-
'location' => 'session_creation',
-
'sessionid' => $this->sessionid,
-
'session_flags' => $this->session_flags,
-
'account_id' => $this->account_id,
-
'account_lid' => $this->account_lid,
-
'passwd' => $this->passwd,
-
'account_domain' => $this->account_domain,
-
'user_ip' => $user_ip,
-
),'',true))) // true = run hooks from all apps, not just the ones the current user has perms to run
-
{
-
foreach($hook_result as $reason)
-
{
-
if ($reason) // called hook requests to deny the session
-
{
-
$this->reason = $this->cd_reason = $reason;
-
$this->log_access($this->reason,$login,$user_ip,0); // log unsuccessfull login
-
if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."($this->login,$this->passwd,$this->passwd_type,$no_session,$auth_check) UNSUCCESSFULL ($this->reason)");
-
return false;
-
}
-
}
-
}
-
$GLOBALS['egw']->db->transaction_begin();
-
$this->register_session($this->login,$user_ip,$now,$this->session_flags);
-
if ($this->session_flags != 'A') // dont log anonymous sessions
-
{
-
$this->sessionid_access_log = $this->log_access($this->sessionid,$login,$user_ip,$this->account_id);
-
}
-
self::appsession('account_previous_login','phpgwapi',$GLOBALS['egw']->auth->previous_login);
-
$GLOBALS['egw']->accounts->update_lastlogin($this->account_id,$user_ip);
-
$GLOBALS['egw']->db->transaction_commit();
-
-
if ($GLOBALS['egw_info']['server']['usecookies'] && !$no_session)
-
{
-
self::egw_setcookie(self::EGW_SESSION_NAME,$this->sessionid);
-
self::egw_setcookie('kp3',$this->kp3);
-
self::egw_setcookie('domain',$this->account_domain);
-
}
-
if ($GLOBALS['egw_info']['server']['usecookies'] && !$no_session || isset($_COOKIE['last_loginid']))
-
{
-
self::egw_setcookie('last_loginid', $this->account_lid ,$now+1209600); /* For 2 weeks */
-
self::egw_setcookie('last_domain',$this->account_domain,$now+1209600);
-
}
-
//if (!$this->sessionid) echo "<p>session::create(login='$login') = '$this->sessionid': lid='$this->account_lid', domain='$this->account_domain'</p>\n";
-
if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."($this->login,$this->passwd,$this->passwd_type,$no_session,$auth_check) successfull sessionid=$this->sessionid");
-
-
return $this->sessionid;
-
}
-
// catch all exceptions, as their (allways logged) trace (eg. on a database error) would contain the user password
-
catch(Exception $e) {
-
$this->reason = $this->cd_reason = $e->getMessage();
-
error_log(__METHOD__."('$login', ".array2string(str_repeat('*', strlen($passwd))).
-
", '$passwd_type', no_session=".array2string($no_session).
-
", auth_check=".array2string($auth_check).
-
", fail_on_forced_password_change=".array2string($fail_on_forced_password_change).
-
") Exception ".$e->getMessage());
-
return false;
-
}
- }
-
- /**
-
- Store eGW specific session-vars
-
-
-
-
-
-
@param string $session_flags
- */
- private function register_session($login,$user_ip,$now,$session_flags)
- {
-
// restore session vars set before session was started
-
if (is_array($this->required_files))
-
{
-
$_SESSION[self::EGW_REQUIRED_FILES] = !is_array($_SESSION[self::EGW_REQUIRED_FILES]) ? $this->required_files :
-
array_unique(array_merge($_SESSION[self::EGW_REQUIRED_FILES],$this->required_files));
-
unset($this->required_files);
-
}
-
$_SESSION[self::EGW_SESSION_VAR] = array(
-
'session_id' => $this->sessionid,
-
'session_lid' => $login,
-
'session_ip' => $user_ip,
-
'session_logintime' => $now,
-
'session_dla' => $now,
-
'session_action' => $_SERVER['PHP_SELF'],
-
'session_flags' => $session_flags,
-
// we need the install-id to differ between serveral installs shareing one tmp-dir
-
'session_install_id' => $GLOBALS['egw_info']['server']['install_id']
-
);
- }
-
- /**
-
- */
- const ACCESS_LOG_TABLE = ‘egw_access_log’;
-
- /**
-
- Write or update (for logout) the access_log
-
-
-
@param string|int $sessionid nummeric or PHP session id or 0 for unsuccessful logins
-
-
@param string $login =’’ account_lid (evtl. with domain) or ‘’ for setting the logout-time
-
-
@param string $user_ip =’’ ip to log
-
-
@param int $account_id =0 numerical account_id
-
-
@return int $sessionid primary key of egw_access_log for login, null otherwise
- */
- private function log_access($sessionid,$login=’’,$user_ip=’’,$account_id=0)
- {
-
$now = time();
-
-
if ($login)
-
{
-
$GLOBALS['egw']->db->insert(self::ACCESS_LOG_TABLE,array(
-
'session_php' => $sessionid,
-
'loginid' => $login,
-
'ip' => $user_ip,
-
'li' => $now,
-
'account_id'=> $account_id,
-
'user_agent'=> $_SERVER['HTTP_USER_AGENT'],
-
'session_dla' => $now,
-
'session_action' => $this->update_dla(false), // dont update egw_access_log
-
),false,__LINE__,__FILE__);
-
-
$ret = $GLOBALS['egw']->db->get_last_insert_id(self::ACCESS_LOG_TABLE,'sessionid');
-
}
-
else
-
{
-
if (!is_numeric($sessionid) && $sessionid == $this->sessionid && $this->sessionid_access_log)
-
{
-
$sessionid = $this->sessionid_access_log;
-
}
-
$GLOBALS['egw']->db->update(self::ACCESS_LOG_TABLE,array(
-
'lo' => $now
-
),is_numeric($sessionid) ? array(
-
'sessionid' => $sessionid,
-
) : array(
-
'session_php' => $sessionid,
-
),__LINE__,__FILE__);
-
-
// run maintenance only on logout, to not delay login
-
if ($GLOBALS['egw_info']['server']['max_access_log_age'])
-
{
-
$max_age = $now - $GLOBALS['egw_info']['server']['max_access_log_age'] * 24 * 60 * 60;
-
-
$GLOBALS['egw']->db->delete(self::ACCESS_LOG_TABLE,"li < $max_age",__LINE__,__FILE__);
-
}
-
}
-
//error_log(__METHOD__."('$sessionid', '$login', '$user_ip', $account_id) returning ".array2string($ret));
-
return $ret;
- }
-
- /**
-
- Protect against brute force attacks, block login if too many unsuccessful login attmepts
-
*
-
-
@param string $login account_lid (evtl. with domain)
-
-
@param string $ip ip of the user
-
-
@returns bool login blocked?
- */
- private function login_blocked($login,$ip)
- {
-
$block_time = time() - $GLOBALS['egw_info']['server']['block_time'] * 60;
-
-
$false_id = $false_ip = 0;
-
foreach($GLOBALS['egw']->db->union(array(
-
array(
-
'table' => self::ACCESS_LOG_TABLE,
-
'cols' => "'false_ip' AS name,COUNT(*) AS num",
-
'where' => array(
-
'account_id' => 0,
-
'ip' => $ip,
-
"li > $block_time",
-
),
-
),
-
array(
-
'table' => self::ACCESS_LOG_TABLE,
-
'cols' => "'false_id' AS name,COUNT(*) AS num",
-
'where' => array(
-
'account_id' => 0,
-
'loginid' => $login,
-
"li > $block_time",
-
),
-
),
-
array(
-
'table' => self::ACCESS_LOG_TABLE,
-
'cols' => "'false_id' AS name,COUNT(*) AS num",
-
'where' => array(
-
'account_id' => 0,
-
'loginid LIKE '.$GLOBALS['egw']->db->quote($login.'@%'),
-
"li > $block_time",
-
)
-
),
-
), __LINE__, __FILE__) as $row)
-
{
-
${$row['name']} += $row['num'];
-
}
-
$blocked = $false_ip > $GLOBALS['egw_info']['server']['num_unsuccessful_ip'] ||
-
$false_id > $GLOBALS['egw_info']['server']['num_unsuccessful_id'];
-
//error_log(__METHOD__."('$login', '$ip') false_ip=$false_ip, false_id=$false_id --> blocked=".array2string($blocked));
-
-
if ($blocked && $GLOBALS['egw_info']['server']['admin_mails'] &&
-
$GLOBALS['egw_info']['server']['login_blocked_mail_time'] < time()-5*60) // max. one mail every 5mins
-
{
-
try {
-
$mailer = new egw_mailer();
-
// notify admin(s) via email
-
$mailer->setFrom('eGroupWare@'.$GLOBALS['egw_info']['server']['mail_suffix']);
-
$mailer->addHeader('Subject', lang("eGroupWare: login blocked for user '%1', IP %2",$login,$ip));
-
$mailer->setBody(lang("Too many unsucessful attempts to login: %1 for the user '%2', %3 for the IP %4",$false_id,$login,$false_ip,$ip));
-
foreach(preg_split('/,\s*/',$GLOBALS['egw_info']['server']['admin_mails']) as $mail)
-
{
-
$mailer->addAddress($mail);
-
}
-
$mailer->send();
-
}
-
catch(Exception $e) {
-
// ignore exception, but log it, to block the account and give a correct error-message to user
-
error_log(__METHOD__."('$login', '$ip') ".$e->getMessage());
-
}
-
// save time of mail, to not send to many mails
-
$config = new config('phpgwapi');
-
$config->read_repository();
-
$config->value('login_blocked_mail_time',time());
-
$config->save_repository();
-
}
-
return $blocked;
- }
-
- /**
-
- Basename of scripts for which we create a pseudo session-id based on user-credentials
-
-
- */
- static $pseudo_session_scripts = array(
-
'webdav.php', 'groupdav.php', 'remote.php', 'share.php'
- );
-
- /**
-
- Get the sessionid from Cookie, Get-Parameter or basic auth
-
-
-
@param boolean $only_basic_auth =false return only a basic auth pseudo sessionid, default no
-
- */
- static function get_sessionid($only_basic_auth=false)
- {
-
// for WebDAV and GroupDAV we use a pseudo sessionid created from md5(user:passwd)
-
// --> allows this stateless protocolls which use basic auth to use sessions!
-
if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW']) &&
-
(in_array(basename($_SERVER['SCRIPT_NAME']), self::$pseudo_session_scripts) ||
-
$_SERVER['SCRIPT_NAME'] === '/Microsoft-Server-ActiveSync'))
-
{
-
// we generate a pseudo-sessionid from the basic auth credentials
-
$sessionid = md5($_SERVER['PHP_AUTH_USER'].':'.$_SERVER['PHP_AUTH_PW'].':'.$_SERVER['HTTP_HOST'].':'.
-
EGW_SERVER_ROOT.':'.self::getuser_ip().':'.filemtime(EGW_SERVER_ROOT.'/phpgwapi/setup/setup.inc.php').
-
// for ActiveSync we add the DeviceID
-
(isset($_GET['DeviceId']) && $_SERVER['SCRIPT_NAME'] === '/Microsoft-Server-ActiveSync' ? ':'.$_GET['DeviceId'] : '').
-
':'.$_SERVER['HTTP_USER_AGENT']);
-
//error_log(__METHOD__."($only_basic_auth) HTTP_HOST=$_SERVER[HTTP_HOST], PHP_AUTH_USER=$_SERVER[PHP_AUTH_USER], DeviceId=$_GET[DeviceId]: sessionid=$sessionid");
-
}
-
// same for digest auth
-
elseif (isset($_SERVER['PHP_AUTH_DIGEST']) &&
-
in_array(basename($_SERVER['SCRIPT_NAME']), self::$pseudo_session_scripts))
-
{
-
// we generate a pseudo-sessionid from the digest username, realm and nounce
-
// can't use full $_SERVER['PHP_AUTH_DIGEST'], as it changes (contains eg. the url)
-
$data = egw_digest_auth::parse_digest($_SERVER['PHP_AUTH_DIGEST']);
-
$sessionid = md5($data['username'].':'.$data['realm'].':'.$data['nonce'].':'.$_SERVER['HTTP_HOST'].
-
EGW_SERVER_ROOT.':'.self::getuser_ip().':'.filemtime(EGW_SERVER_ROOT.'/phpgwapi/setup/setup.inc.php').
-
':'.$_SERVER['HTTP_USER_AGENT']);
-
}
-
elseif(!$only_basic_auth && isset($_REQUEST[self::EGW_SESSION_NAME]))
-
{
-
$sessionid = $_REQUEST[self::EGW_SESSION_NAME];
-
}
-
elseif(!$only_basic_auth && isset($_COOKIE[self::EGW_SESSION_NAME]))
-
{
-
$sessionid = $_COOKIE[self::EGW_SESSION_NAME];
-
}
-
else
-
{
-
$sessionid = false;
-
}
-
if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."() _SERVER[REQUEST_URI]='$_SERVER[REQUEST_URI]' returning ".print_r($sessionid,true));
-
return $sessionid;
- }
-
- /**
-
- Get request or cookie variable with higher precedence to $_REQUEST then $_COOKIE
-
-
- In php < 5.3 that’s identical to $_REQUEST[$name], but php5.3+ does no longer register cookied in $_REQUEST by default
-
-
- As a workaround for a bug in Safari Version 3.2.1 (5525.27.1), where cookie first letter get’s upcased, we check that too.
-
-
-
@param string $name eg. ‘kp3’ or domain
-
-
@return mixed null if it’s neither set in $_REQUEST or $_COOKIE
- */
- static function get_request($name)
- {
-
return isset($_REQUEST[$name]) ? $_REQUEST[$name] :
-
(isset($_COOKIE[$name]) ? $_COOKIE[$name] :
-
(isset($_COOKIE[$name=ucfirst($name)]) ? $_COOKIE[$name] : null));
- }
-
- /**
-
- Check to see if a session is still current and valid
-
-
-
@param string $sessionid session id to be verfied
-
-
@param string $kp3 ?? to be verified
-
-
@return bool is the session valid?
- */
- function verify($sessionid=null,$kp3=null)
- {
-
if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."('$sessionid','$kp3') ".function_backtrace());
-
-
$fill_egw_info_and_repositories = !$GLOBALS['egw_info']['flags']['restored_from_session'];
-
-
if(!$sessionid)
-
{
-
$sessionid = self::get_sessionid();
-
$kp3 = self::get_request('kp3');
-
}
-
-
$this->sessionid = $sessionid;
-
$this->kp3 = $kp3;
-
-
-
if (!$this->sessionid)
-
{
-
if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."('$sessionid') get_sessionid()='".self::get_sessionid()."' No session ID");
-
return false;
-
}
-
-
session_name(self::EGW_SESSION_NAME);
-
session_id($this->sessionid);
-
self::cache_control();
-
session_start();
-
-
// check if we have a eGroupware session --> return false if not (but dont destroy it!)
-
if (is_null($_SESSION) || !isset($_SESSION[self::EGW_SESSION_VAR]))
-
{
-
if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."('$sessionid') session does NOT exist!");
-
return false;
-
}
-
$session =& $_SESSION[self::EGW_SESSION_VAR];
-
-
if ($session['session_dla'] <= time() - $GLOBALS['egw_info']['server']['sessions_timeout'])
-
{
-
if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."('$sessionid') session timed out!");
-
$this->destroy($sessionid,$kp3);
-
return false;
-
}
-
-
$this->session_flags = $session['session_flags'];
-
-
$this->split_login_domain($