Skip to content
Snippets Groups Projects
session.php 10.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • <?
    // 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	$printbalance;		// printer balance
    
    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
    
    private	$timeout = 2880;	// Idle timeout limit in minutes (session deleted), 2880 == 48 hours
    
    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)
    
    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
    
    	private function newsession()
    	{
    
    		$token = $this->genSessionID(); 
    		$DB->Execute("insert into {$this->table} (hash, lastseen, ip) values (?,NOW(),?)", array($token, $_SERVER['REMOTE_ADDR']));
    
    		setcookie("sucssite_session", $token, NULL, "/", $preferred_hostname);
    
    	
    		// 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;
    
    		} 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;
    
    		unset($token);
    
    
    		// 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
    
    			"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)"
    
    
    
    		// 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;
    
    
    		// 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));
    
    
    		// 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;
    
    		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];
    
    
    	}
    
    	/* 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 */
    
    Denis Walker's avatar
    Denis Walker committed
    	public function check_pass($user, $pass)
    
    	{
    		// 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();
    
    		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
    
    		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