<?PHP
/*
* phpMyAuth
* Jason Gerfen [hide@address.com]
*
* class.authentication.php - Handle user authentication
*/
class Authenticate
{
function DecideAuth( $token, $user, $pass, $server )
{ //echo "<pre>"; print_r( func_get_args() ); echo "</pre>";
global $defined;
if( isset( $token ) ) {
return $this->ReAuthenticate( $token, $server );
} else {
return $this->PrimaryAuthentication( $user, $pass, $server );
}
}
function checkLDAP( $user, $pass )
{
global $defined;
global $handles;
// are we configured to use ldap?
if( ( !empty( $defined['ldapuser'] ) ) && ( !empty( $defined['ldappass'] ) ) && ( !empty( $defined['ldapdomain'] ) ) && ( !empty( $defined['ldapserv'] ) ) && ( !empty( $defined['ldapport'] ) ) && ( !empty( $defined['binddn'] ) ) && ( !empty( $defined['basedn'] ) ) ) {
// attempt to bind for this user as long as there is at least a username
// found in the local mysql database
$data = $handles['db']->dbConnect( $defined['dbhost'], $defined['username'], $defined['password'], $defined['dbname'] );
$query = "SELECT * FROM `users` WHERE `txtUserName` = \"" . $user . "\"";
if( ( $value = $handles['db']->dbQuery( $handles['val']->ValidateSQL( $query, $data ), $data ) ) !== -1 ) {
if( $handles['db']->dbNumRows( $value ) > 0 ) {
// proceed to bind because a user was found locally
if( $handles['ldap']->auth( $user, $pass, $defined['ldapserv'], $defined['ldapport'] ) === 0 ) {
// a good bind? lets propogate the user/pass to the database to limit ldap queries /cached
$sql = "UPDATE `users` SET `txtUserPassword1` = sha1( \"" . $pass . "\" ) WHERE `txtUserName` = \"" . $user . "\" LIMIT 1";
if( ( $value = $handles['db']->dbQuery( $handles['val']->ValidateSQL( $sql, $data ), $data ) ) !== -1 ) {
if( $handles['db']->dbNumRowsAffected( $data ) === 1 ) {
$ret = 0;
} else {
$ret = 1;
}
} else {
$ret = 2;
}
} else {
$ret = 3;
}
} else {
$ret = 4;
}
} else {
$ret = 5;
}
} else {
$ret = 6;
}
$handles['ldap']->closeConn();
return $ret;
}
function PrimaryAuthentication( $user, $pass, $server )
{
global $defined;
global $handles;
// ensure our data is present
if( ( empty( $user ) ) || ( empty( $pass ) ) || ( count( $server ) < 0 ) ) {
$ret = 1;
} else {
// validate user credentials
if( ( $handles['val']->ValidateAlphaChar( $user ) === -1 ) || ( $handles['val']->ValidatePassword( $pass ) === -1 ) ) {
$ret = 2;
} else {
// ok now we need to look the user up
$data = $handles['db']->dbConnect( $defined['dbhost'], $defined['username'], $defined['password'], $defined['dbname'] );
$query = "SELECT * FROM `users` WHERE `txtUserName` = \"" . $user . "\" AND `txtUserPassword1` = sha1( \"$pass\" )";
if( ( $value = $handles['db']->dbQuery( $handles['val']->ValidateSQL( $query, $data ), $data ) ) === -1 ) {
$ret = 3;
} else {
if( $handles['db']->dbNumRows( $value ) <= 0 ) {
// check ldap real quick...
if( $this->checkLDAP( $user, $pass ) === 0 ) {
$ret = 0;
} else {
$ret = 4;
}
} else {
$array = $handles['db']->dbArrayResultsAssoc( $value );
$array[0]['password'] = $pass;
// reassign $array[0]['password'] hash to submitted password
// since we have a valid user we need to perform the following:
// + Create a private, and public encryption key pair
// + Update the user record to store the private key
// + Generate a re-usable encrypted token we can pass
// back and forth between applications
$privatekey = $handles['encrypt']->EncodePrivToHex( $handles['encrypt']->GeneratePrivateKey( $defined['enckeygen'] ) );
$publickey = $handles['encrypt']->GeneratePublicKey( $privatekey );
// since we are going to store the session private key iv data in the database we
// must initialize the encryption class (if the mcrypt libs are present)
if( function_exists( "mcrypt_encrypt" ) ) {
$cipher = new Cipher(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
$cipher->setIV();
$sessioniv = $cipher->getIV();
} else {
// since this php configuration does not
// utilize the mcrypt libs we need to generate
// a spoofed iv
$sessioniv = rand( microtime(), microtime() );
}
// create some time stamp data
$access_date = $handles['misc']->GenDate();
$access_time = $handles['misc']->GenTimeRead();
$time = $handles['misc']->GenTime();
// destroy any existing sessions for this user
$query = "UPDATE `users` SET `ip` = '', `referrer` = '', `agent` = '', `session` = '', `iv` = '' WHERE `id` = \"" . $array[0]['id'] . "\" LIMIT 1";
$value = $handles['db']->dbQuery( $handles['val']->ValidateSQL( $query, $data ), $data );
// update user record with session requirements
$query = "UPDATE `users` SET `ip` = \"" . md5($server['REMOTE_ADDR']) . "\", `referrer` = \"" . $server['HTTP_REFERER'] . "\", `agent` = \"" . md5($server['HTTP_USER_AGENT']) . "\", `access_date` = \"" . $access_date . "\", `access_time` = \"" . $access_time . "\", `session` = \"" . $privatekey . "\", `iv` = \"" . $sessioniv . "\" WHERE `id` = \"" . $array[0]['id'] . "\"";
if( ( $value = $handles['db']->dbQuery( $handles['val']->ValidateSQL( $query, $data ), $data ) ) === -1 ) {
$ret = 5;
} else {
if( ( $token = $handles['encrypt']->EncodeAuthTokenHeavy( $array, $sessioniv, $time, $publickey, $server ) ) === -1 ) {
$ret = 6;
} else {
$ret = 0;
}
}
}
}
}
}
if( $ret === 0 ) {
$handles['session']->regen( true );
$_SESSION['token'] = $token;
}
$handles['db']->dbFixTable( "sessions", $dbconn );
$handles['db']->dbFixTable( "users", $dbconn );
return $ret;
}
/*
* During the PrimaryAuthentication function we created a private and public key
* The private key gets placed in the users record for later use
*
* The private key is passed to the encode function and is used to encrypt and base64_encode
* the various user attributes we require during the Re-Authentication function.
*
* The process takes the following steps during re-authentication
* 1. Ensure we have a registered and valid session token
* 2. Split the token apart and use the base64_decode function to decode each element
* 3. Use the last element of the session token (the public key) to unencrypt each element
* 4. Pass the array of unencrypted elements to check the following:
* + Ensure the remote IP address is the same as the one registered during the primary authentication
* + Ensure the referring page is coming from something located on the $defined['server'] ie. http://server.com/page.php is from server.com
* + Ensure the browser agent string has not changed as well
* + Make sure the user has not been timed out based on the authenticated time stamp vs. the defined timeout variable
* + Do a lookup on the decoded username and password to ensure they are indeed a valid user in the database
* + Regenerate the session id, destroy everything and let the user continue about their business
*/
function ReAuthenticate( $token, $server )
{
global $defined;
global $handles;
// set a flag depending on the referer (SSO referer fix)
$sso = ( !eregi( $defined['hostname'], $server['HTTP_REFERER'] ) ) ? $sso = true : $sso = false;
// this next step will give us plaintext of the base64_encoded / encrypted token elements
if(isset($token)) {
if(($array = $handles['encrypt']->DecodeAuthTokenHeavy($token))===1) {
return ( $sso === true ) ? "7" : 7;
}
} else {
return ( $sso === true ) ? "7" : 7;
}
if( count( $array ) < 10 ) {
return ( $sso === true ) ? "8" : 8;
} else {
// do a lookup based on the username
$sql = "SELECT * FROM `users` WHERE `txtUserName` = \"" . $array[0] . "\" LIMIT 1";
$dbconn = $handles['db']->dbConnect( $defined['dbhost'], $defined['username'], $defined['password'], $defined['dbname'] );
if( ( $value = $handles['db']->dbQuery( $handles['val']->ValidateSQL( $sql, $dbconn ), $dbconn ) ) === -1 ) {
return ( $sso === true ) ? "9" : 9;
} else {
if( count( $handles['db']->dbNumRows( $value ) ) <= 0 ) {
return ( $sso === true ) ? "10" : 10;
} else {
$user_data = $handles['db']->dbArrayResultsAssoc( $value );
// perform our verifications by first checking that the public key and iv data matches up with the stored private key and iv
if( ( md5( $user_data[0]['session'] ) !== $array[9] ) || ( md5( $user_data[0]['iv'] ) !== $array[8] ) ) {
return ( $sso === true ) ? "11" : 11;
} else {
// reassign referer check if necessary for SSO functionality
if( eregi( $defined['hostname'], $server['HTTP_REFERER'] ) ) { $serv[0] = $defined['hostname']; } else { $serv = preg_split( '/\?/', $server['HTTP_REFERER'] ); }
// do comparisons on ip, referer and browser agent
if( ( $user_data[0]['ip'] !== $array[5] ) || ( !strstr( $user_data[0]['referrer'], $serv[0] ) ) || ( $user_data[0]['agent'] !== $array[7] ) ) {
return ( $sso === true ) ? "12" : 12;
} else {
// make sure they haven't expired their session
if( ( $this->AuthTimeOut( $defined['timeout'], $array[4], $handles['misc']->GenTime() ) ) === -1 ) {
return ( $sso === true ) ? "13" : 13;
} else {
// all right, make sure the username, password within decrypted token info is
// valid and let the user proceed
$sql = "SELECT * FROM `users` WHERE `txtUserName` = \"" . $array[0] . "\" AND `txtUserPassword1` = \"" . sha1( $array[1] ) . "\" LIMIT 1";
if( ( $value = $handles['db']->dbQuery( $handles['val']->ValidateSQL( $sql, $dbconn ), $dbconn ) ) === -1 ) {
return ( $sso === true ) ? "14" : 14;
} else {
if( count( $handles['db']->dbNumRows( $value ) ) !== 1 ) {
return ( $sso === true ) ? "15" : 15;
} else {
// fix for json ajax requests expecting string where an integer is returned
if( $sso === false ) { $handles['session']->regen( true ); }
return ( $sso === true ) ? "0" : 0;
}
}
}
}
}
}
}
}
}
function logout( $token, $id )
{
global $handles;
global $defined;
$array = $handles['encrypt']->DecodeAuthTokenHeavy( $token );
$dbconn = $handles['db']->dbConnect( $defined['dbhost'], $defined['username'], $defined['password'], $defined['dbname'] );
$sql = "UPDATE `users` SET `ip` = '', `referrer` = '', `agent` = '', `session` = '', `iv` = '' WHERE `txtUserName` = \"" . $array[0] . "\" LIMIT 1";
$handles['db']->dbQuery( $handles['val']->ValidateSQL( $sql, $dbconn ), $dbconn );
return session_destroy();
}
function AuthTimeOut( $constant, $time, $current )
{
if( ( $current - $time ) > $constant ) {
$data->value = -1;
} else {
$data->value = 0;
}
return $data->value;
}
}
?>