Newer
Older
<?
// To use:
// include_once "session.php"
// $mysession = new Session;
//
// $mysession->loggedin is TRUE if they have logged in
//
// other attributes are :
// username - the username they logged in with
// fullname - whatever full name we know for them
// lastseen - unix timestamp for their previous page access
// data - var/array for persistant data, commit by calling the 'save' method
// Session management and authentication mechanism.
class Session {
public $loggedin=FALSE; // Is this a valid logged in user ?
public $username=''; // Username
public $fullname; // Fullname
public $email=0; // Email waiting?
public $email_forward; // Email forwarded?
public $groups =array(); // users groups
public $data=''; // Var/array for session persistant data
public $token=''; // session identifier
public $logintime=''; // Time which user last gave us credentials
public $lastseen=''; // Time of last page request
Graham Cole
committed
private $timeout = 2880; // Idle timeout limit in minutes (session deleted), 2880 == 48 hours
Graham Cole
committed
private $anonymous_timeout = 120; // Idle timeout limit for sessions which aren't logged in (set lower to stop the session table getting swamped)
private $secure_timeout = 30; // Idle timeout limit in minutes (consider session less secure, require reauth for sensitive ops)
Graham Cole
committed
private $max_session_length = 11520; // maximum length of a session, 11520 == 8 days
private $table = "session"; // session storage table (const)
private $datahash=''; // hash of data field
// Create a new (insecure) session
Graham Cole
committed
global $DB, $preferred_hostname;
$token = $this->genSessionID();
$DB->Execute("insert into {$this->table} (hash, lastseen, ip) values (?,NOW(),?)", array($token, $_SERVER['REMOTE_ADDR']));
Graham Cole
committed
setcookie("sucssite_session", $token, NULL, "/", $preferred_hostname);
Graham Cole
committed
// delete loggedin cookie if it exists
setcookie("sucssite_loggedin", FALSE, time(), "/");
$this->token = $token;
return;
}
public function isSecure()
{
global $DB;
// is user coming from the IP address they were when they logged in?
if ($detail['ip'] != $_SERVER['REMOTE_ADDR']) {
return false;
Graham Cole
committed
} elseif (time() > ($this->logintime + $this->secure_timeout)) {
// has it been too long since we last asked for credentials?
return false;
}
}
// Public Object constructor
function __construct()
{
global $DB, $preferred_hostname, $baseurl;
// if user requests a page via HTTP and claims to be logged in, bump them to HTTPS
if (!isset($_SERVER['HTTPS']) && (@$_COOKIE['sucssite_loggedin'] == "true")) {
header("HTTP/1.0 307 Temporary redirect");
header("Location: https://{$preferred_hostname}{$baseurl}{$_SERVER['PATH_INFO']}");
return;
}
// The possible form elements
$submit = @$_POST['Login'];
$logout = @$_POST['Logout'];
$session_user = strtolower(@$_POST['session_user']);
$session_pass = @$_POST['session_pass'];
// We havent logged them in yet
$this->loggedin = FALSE;
// Time out any old sessions
Graham Cole
committed
$DB->Execute(
Graham Cole
committed
"delete from {$this->table} where lastseen < NOW() - '{$this->timeout} minutes'::reltime ".
"or logintime < NOW() - '{$this->max_session_length} minutes'::reltime ".
"or (username IS NULL AND lastseen < NOW() - '{$this->anonymous_timeout} minutes'::reltime)"
Graham Cole
committed
);
// the possible token data passed from a form
if (isset($_REQUEST['token']))
$token = $_REQUEST['token'];
// Check if we were handed a specific token identifier
// Otherwise use the value from the cookie we gave out
if (!isset($token) && isset($_COOKIE['sucssite_session']))
$token=@$_COOKIE['sucssite_session'];
if (isset($token)) $this->token = $token;
Graham Cole
committed
// Log them out if they ask
if ($logout=="Logout") {
$this->logout();
return;
}
// Okay, so we still dont have a session id
// so issue a new one and go back to core
if (!isset($token))
{
$this->newsession();
return;
}
// Is this a login attempt ?
if ($submit != '' && $session_user != '' && $session_pass != '')
{
$this->session_init($session_user, $session_pass);
}
// Retrieve session information
$oldsess=$DB->GetAll("select * from {$this->table} where hash=?", array($this->token));
if (!$oldsess || count($oldsess) < 1) {
trigger_error("Session timed out", E_USER_NOTICE);
$this->newsession();
return;
}
// Extract detail of session for pass-back
$detail = $oldsess[0];
$this->data = unserialize((string)$detail['data']);
$this->lastseen = strtotime($detail['lastseen']);
$this->logintime = strtotime($detail['logintime']);
$this->datahash = md5(serialize($this->data));
// are we actually logged in, fill in more
if ($detail['username']) {
// Are we using HTTPS?
if (!isset($_SERVER['HTTPS'])) {
trigger_error("Insecure Connection", E_USER_NOTICE);
$this->loggedin = FALSE;
return;
}
$this->username=$detail['username'];
$this->fetch_detail($detail['username']);
$this->loggedin = TRUE;
}
// update time stamp
$DB->Execute( "update {$this->table} set lastseen=NOW() where hash=?", array($this->token));
Graham Cole
committed
// check to see if there any messages stored for this user
if (isset($this->data['messages'])) {
global $messages;
if (is_array($messages)) {
$messages += $this->data['messages'];
} else {
$messages = $this->data['messages'];
}
unset($this->data['messages']);
$this->save();
}
}
// generate a string suitable to be used as a session ID
private function genSessionID()
{
global $DB;
$try = 0;
$tt=date("D M d H:i:s Y");
$ip = $_SERVER['REMOTE_ADDR'];
$nonce = rand(); // this should stop session IDs being (easily) guessable by someone with the algorithm
do {
$token = md5("$ip$tt$nonce".$try++);
$old = $DB->GetAll("select hash from {$this->table} where hash=?", array($token));
} while ($old);
return $token;
}
// Public function: Store the session data away in the database
public function save( )
{
global $DB;
$newhash = md5(serialize($this->data));
if ($newhash == $this->datahash) {
// no change in data, dont save
return;
}
$DB->Execute("update {$this->table} set data=? where hash=?", array(serialize($this->data),$this->token));
}
// Public function: force a logout of the session
public function logout( )
{
global $DB;
$DB->Execute("delete from {$this->table} where hash=?", array($this->token));
$this->newsession();
$this->loggedin = FALSE;
Graham Cole
committed
setcookie("sucssite_loggedin", FALSE, time(), "/");
}
// Fill out any extra details we know about the user
private function fetch_detail( $user )
{
if (!($ldap = @ldap_connect("ldap://localhost"))) {
trigger_error("LDAP connect failed", E_USER_ERROR);
return FALSE;
}
$info = $this->ldap_getuser($ldap, $user);
if (!$info) return FALSE;
ldap_close($ldap);
// Check the user's email status
/*$mailstat = @stat("/var/spool/mail/".$user);
if ($mailstat[size]>0) {
if ($mailstat[mtime]>$mailstat[atime]) $this->email = 2;
else $this->email = 1;
}*/
// a sure-fire way to check to see if the user has any unread email
// the bash script returns 0 for no and 1 for yes, takes one arg, username
$this->email = shell_exec("/home/member/imranh/bin/sucsunreadmail $user");
if (file_exists($info['homedirectory'][0]."/.forward")) {
$forward = file($info['homedirectory'][0]."/.forward");
$this->email_forward = preg_replace("/\n/", "", $forward[0]);
}
$this->fullname = $info['cn'][0];
$this->groups = $info['grouplist'];
$db = new SQLite3('/etc/pykota/pykota.db');
$result = $db->query("SELECT balance FROM users WHERE username='$user';");
$this->printbalance = $result->fetchArray()[0];
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
}
/* check using mod_auth_externals helper
private function check_pass($user, $pass)
{
if ($fd === FALSE) {
$this->errormsg = "Auth system error";
return FALSE;
}
fwrite($fd, "$user\n");
fwrite($fd, "$pass\n");
$ret = pclose($fd);
if ($ret == 0) return TRUE;
$this->autherror = "u='$user' p='$pass' ret=$ret";
$this->errormsg = "Invalid Username or Password";
return FALSE;
}
*/
// Get a users full record from ldap
private function ldap_getuser($ldap, $user)
{
// publically bind to find user
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
if (!($bind=@ldap_bind($ldap))) {
trigger_error("LDAP bind failed", E_USER_ERROR);
return NULL;
}
// find the user
if (!($search=@ldap_search($ldap, "dc=sucs,dc=org", "(&(uid=$user))"))) {
trigger_error("LDAP search fail", E_USER_ERROR);
return NULL;
}
$n = ldap_count_entries($ldap, $search);
if ($n < 1) {
trigger_error("Username or Password Incorrect", E_USER_WARNING);
return NULL;
}
$info = ldap_get_entries($ldap, $search);
if (($grpsearch=@ldap_search($ldap, "ou=Group,dc=sucs,dc=org", "memberuid=$user"))) {
$gn = ldap_count_entries($ldap,$grpsearch);
$gpile = ldap_get_entries($ldap, $grpsearch);
$glist=array();
for ($i=0;$i<$gn;$i++) {
$glist[ $gpile[$i]['cn'][0] ] = $gpile[$i]['gidnumber'][0];
}
$info[0]['grouplist'] = $glist;
}
return $info[0];
}
/* check using ldap directly */
{
// Open connection
if (!($ldap = @ldap_connect("ldap://localhost"))) {
trigger_error("LDAP connect failed", E_USER_ERROR);
return FALSE;
}
$info = $this->ldap_getuser($ldap, $user);
if (!$info) return FALSE;
$real = @ldap_bind($ldap, $info['dn'], $pass);
ldap_close($ldap);
if ($real) return TRUE;
trigger_error("Username or Password Incorrect", E_USER_WARNING);
return FALSE;
}
// Private function: process login form
private function session_init($user, $pass)
{
global $DB, $preferred_hostname;
// Check that this is a valid session start
// This prevents replay attacks
$sess = $DB->GetAll("select * from {$this->table} where hash=? and username is NULL", array($this->token));
if (!$sess || count($sess)<1) {
trigger_error("Invalid session, login again.", E_USER_WARNING);
return;
}
if (!$this->check_pass($user, $pass)) return;
$this->username = $user;
// the token has likely been used on an insecure connection
// so generate a new one with the secure flag set
$oldtoken = $this->token;
$this->token = $this->genSessionID();
Graham Cole
committed
setcookie("sucssite_session", $this->token, time() + $this->max_session_length * 60, "/", $preferred_hostname, TRUE);
// set a cookie as a hint that we're logged in
// this can be checked for to allow redirecting to SSL to get the secure cookie
Graham Cole
committed
setcookie("sucssite_loggedin", "true", time() + $this->max_session_length * 60, "/");
// Update the session, filling in the blanks
$DB->Execute("update {$this->table} set hash=?, username=?, logintime='NOW()', lastseen='NOW()', ip=? where hash=?",
array($this->token, $this->username, $_SERVER['REMOTE_ADDR'], $oldtoken));
// Return back to normal session retrieval
}
} // end of Class