includes/sessions.php
changeset 688 f2a824ce5f18
parent 685 17ebe24cdf85
child 691 dab7850c098d
equal deleted inserted replaced
687:ea43ac1ff2ee 688:f2a824ce5f18
   561    * @param string $username The username
   561    * @param string $username The username
   562    * @param string $aes_data The encrypted password, hex-encoded
   562    * @param string $aes_data The encrypted password, hex-encoded
   563    * @param string $aes_key The MD5 hash of the encryption key, hex-encoded
   563    * @param string $aes_key The MD5 hash of the encryption key, hex-encoded
   564    * @param string $challenge The 256-bit MD5 challenge string - first 128 bits should be the hash, the last 128 should be the challenge salt
   564    * @param string $challenge The 256-bit MD5 challenge string - first 128 bits should be the hash, the last 128 should be the challenge salt
   565    * @param int $level The privilege level we're authenticating for, defaults to 0
   565    * @param int $level The privilege level we're authenticating for, defaults to 0
   566    * @param array $captcha_hash Optional. If we're locked out and the lockout policy is captcha, this should be the identifier for the code.
   566    * @param string $captcha_hash Optional. If we're locked out and the lockout policy is captcha, this should be the identifier for the code.
   567    * @param array $captcha_code Optional. If we're locked out and the lockout policy is captcha, this should be the code the user entered.
   567    * @param string $captcha_code Optional. If we're locked out and the lockout policy is captcha, this should be the code the user entered.
       
   568    * @param bool $remember Optional. If true, remembers the session for X days. Otherwise, assigns a short session. Defaults to false.
   568    * @param bool $lookup_key Optional. If true (default) this queries the database for the "real" encryption key. Else, uses what is given.
   569    * @param bool $lookup_key Optional. If true (default) this queries the database for the "real" encryption key. Else, uses what is given.
   569    * @return string 'success' on success, or error string on failure
   570    * @return string 'success' on success, or error string on failure
   570    */
   571    */
   571    
   572    
   572   function login_with_crypto($username, $aes_data, $aes_key_id, $challenge, $level = USER_LEVEL_MEMBER, $captcha_hash = false, $captcha_code = false, $lookup_key = true)
   573   function login_with_crypto($username, $aes_data, $aes_key_id, $challenge, $level = USER_LEVEL_MEMBER, $captcha_hash = false, $captcha_code = false, $remember = false, $lookup_key = true)
   573   {
   574   {
   574     global $db, $session, $paths, $template, $plugins; // Common objects
   575     global $db, $session, $paths, $template, $plugins; // Common objects
   575     
       
   576     $privcache = $this->private_key;
       
   577 
       
   578     if ( !defined('IN_ENANO_INSTALL') )
       
   579     {
       
   580       $timestamp_cutoff = time() - $duration;
       
   581       $q = $this->sql('SELECT timestamp FROM '.table_prefix.'lockout WHERE timestamp > ' . $timestamp_cutoff . ' AND ipaddr = \'' . $ipaddr . '\' ORDER BY timestamp DESC;');
       
   582       $fails = $db->numrows();
       
   583       // Lockout stuff
       
   584       $threshold = ( $_ = getConfig('lockout_threshold') ) ? intval($_) : 5;
       
   585       $duration  = ( $_ = getConfig('lockout_duration') ) ? intval($_) : 15;
       
   586       // convert to minutes
       
   587       $duration  = $duration * 60;
       
   588       $policy = ( $x = getConfig('lockout_policy') && in_array(getConfig('lockout_policy'), array('lockout', 'disable', 'captcha')) ) ? getConfig('lockout_policy') : 'lockout';
       
   589       if ( $policy == 'captcha' && $captcha_hash && $captcha_code )
       
   590       {
       
   591         // policy is captcha -- check if it's correct, and if so, bypass lockout check
       
   592         $real_code = $this->get_captcha($captcha_hash);
       
   593       }
       
   594       if ( $policy != 'disable' && !( $policy == 'captcha' && isset($real_code) && strtolower($real_code) == strtolower($captcha_code) ) )
       
   595       {
       
   596         $ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
       
   597         if ( $fails >= $threshold )
       
   598         {
       
   599           // ooh boy, somebody's in trouble ;-)
       
   600           $row = $db->fetchrow();
       
   601           $db->free_result();
       
   602           return array(
       
   603               'success' => false,
       
   604               'error' => 'locked_out',
       
   605               'lockout_threshold' => $threshold,
       
   606               'lockout_duration' => ( $duration / 60 ),
       
   607               'lockout_fails' => $fails,
       
   608               'lockout_policy' => $policy,
       
   609               'time_rem' => ( $duration / 60 ) - round( ( time() - $row['timestamp'] ) / 60 ),
       
   610               'lockout_last_time' => $row['timestamp']
       
   611             );
       
   612         }
       
   613       }
       
   614       $db->free_result();
       
   615     }
       
   616     
   576     
   617     // Instanciate the Rijndael encryption object
   577     // Instanciate the Rijndael encryption object
   618     $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
   578     $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
   619     
   579     
   620     // Fetch our decryption key
   580     // Fetch our decryption key
   654         );
   614         );
   655     
   615     
   656     // Decrypt our password
   616     // Decrypt our password
   657     $password = $aes->decrypt($aes_data, $bin_key, ENC_HEX);
   617     $password = $aes->decrypt($aes_data, $bin_key, ENC_HEX);
   658     
   618     
   659     // Initialize our success switch
   619     // Let the LoginAPI do the rest.
   660     $success = false;
   620     return $this->login_without_crypto($username, $password, false, $level, $captcha_hash, $captcha_code, $remember);
   661     
       
   662     // Escaped username
       
   663     $username = str_replace('_', ' ', $username);
       
   664     $db_username_lower = $this->prepare_text(strtolower($username));
       
   665     $db_username       = $this->prepare_text($username);
       
   666     
       
   667     // Select the user data from the table, and decrypt that so we can verify the password
       
   668     $this->sql('SELECT password,old_encryption,user_id,user_level,theme,style,temp_password,temp_password_time FROM '.table_prefix.'users WHERE ' . ENANO_SQLFUNC_LOWERCASE . '(username)=\''.$db_username_lower.'\' OR username=\'' . $db_username . '\';');
       
   669     if($db->numrows() < 1)
       
   670     {
       
   671       // This wasn't logged in <1.0.2, dunno how it slipped through
       
   672       if($level > USER_LEVEL_MEMBER)
       
   673         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.enano_date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
       
   674       else
       
   675         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.enano_date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
       
   676     
       
   677       if ( $policy != 'disable' && !defined('IN_ENANO_INSTALL') )
       
   678       {
       
   679         $ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
       
   680         // increment fail count
       
   681         $this->sql('INSERT INTO '.table_prefix.'lockout(ipaddr, timestamp, action) VALUES(\'' . $ipaddr . '\', ' . time() . ', \'credential\');');
       
   682         $fails++;
       
   683         // ooh boy, somebody's in trouble ;-)
       
   684         return array(
       
   685             'success' => false,
       
   686             'error' => ( $fails >= $threshold ) ? 'locked_out' : 'invalid_credentials',
       
   687             'lockout_threshold' => $threshold,
       
   688             'lockout_duration' => ( $duration / 60 ),
       
   689             'lockout_fails' => $fails,
       
   690             'time_rem' => ( $duration / 60 ),
       
   691             'lockout_policy' => $policy
       
   692           );
       
   693       }
       
   694       
       
   695       return array(
       
   696           'success' => false,
       
   697           'error' => 'invalid_credentials'
       
   698         );
       
   699     }
       
   700     $row = $db->fetchrow();
       
   701     
       
   702     // Check to see if we're logging in using a temporary password
       
   703     
       
   704     if((intval($row['temp_password_time']) + 3600*24) > time() )
       
   705     {
       
   706       $temp_pass = $aes->decrypt( $row['temp_password'], $this->private_key, ENC_HEX );
       
   707       if( $temp_pass == $password )
       
   708       {
       
   709         $url = makeUrlComplete('Special', 'PasswordReset/stage2/' . $row['user_id'] . '/' . $row['temp_password']);
       
   710         
       
   711         $code = $plugins->setHook('login_password_reset');
       
   712         foreach ( $code as $cmd )
       
   713         {
       
   714           eval($cmd);
       
   715         }
       
   716         
       
   717         redirect($url, '', '', 0);
       
   718         exit;
       
   719       }
       
   720     }
       
   721     
       
   722     if($row['old_encryption'] == 1)
       
   723     {
       
   724       // The user's password is stored using the obsolete and insecure MD5 algorithm, so we'll update the field with the new password
       
   725       if(md5($password) == $row['password'])
       
   726       {
       
   727         $pass_stashed = $aes->encrypt($password, $this->private_key, ENC_HEX);
       
   728         $this->sql('UPDATE '.table_prefix.'users SET password=\''.$pass_stashed.'\',old_encryption=0 WHERE user_id='.$row['user_id'].';');
       
   729         $success = true;
       
   730       }
       
   731     }
       
   732     else
       
   733     {
       
   734       // Our password field is up-to-date with the >=1.0RC1 encryption standards, so decrypt the password in the table and see if we have a match; if so then do challenge authentication
       
   735       $real_pass = $aes->decrypt(hexdecode($row['password']), $this->private_key, ENC_BINARY);
       
   736       if($password === $real_pass && is_string($password))
       
   737       {
       
   738         // Yay! We passed AES authentication. Previously an MD5 challenge was done here, this was deemed redundant in 1.1.3.
       
   739         // It didn't seem to provide any additional security...
       
   740         $success = true;
       
   741       }
       
   742     }
       
   743     if($success)
       
   744     {
       
   745       if($level > $row['user_level'])
       
   746         return array(
       
   747           'success' => false,
       
   748           'error' => 'too_big_for_britches'
       
   749         );
       
   750 
       
   751       /*        
       
   752       return array(
       
   753         'success' => false,
       
   754         'error' => 'Successful authentication, but session manager is in debug mode - remove the "return array(...);" in includes/sessions.php:' . ( __LINE__ - 2 )
       
   755       );
       
   756       */
       
   757       
       
   758       $sess = $this->register_session(intval($row['user_id']), $username, $password, $level);
       
   759       if($sess)
       
   760       {
       
   761         $this->username = $username;
       
   762         $this->user_id = intval($row['user_id']);
       
   763         $this->theme = $row['theme'];
       
   764         $this->style = $row['style'];
       
   765         
       
   766         if($level > USER_LEVEL_MEMBER)
       
   767           $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_good\', '.time().', \''.enano_date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
       
   768         else
       
   769           $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_good\', '.time().', \''.enano_date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
       
   770         
       
   771         $code = $plugins->setHook('login_success');
       
   772         foreach ( $code as $cmd )
       
   773         {
       
   774           eval($cmd);
       
   775         }
       
   776         return array(
       
   777           'success' => true
       
   778         );
       
   779       }
       
   780       else
       
   781         return array(
       
   782           'success' => false,
       
   783           'error' => 'backend_fail'
       
   784         );
       
   785     }
       
   786     else
       
   787     {
       
   788       if($level > USER_LEVEL_MEMBER)
       
   789         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.enano_date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
       
   790       else
       
   791         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.enano_date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
       
   792         
       
   793       // Do we also need to increment the lockout countdown?
       
   794       if ( $policy != 'disable' && !defined('IN_ENANO_INSTALL') )
       
   795       {
       
   796         $ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
       
   797         // increment fail count
       
   798         $this->sql('INSERT INTO '.table_prefix.'lockout(ipaddr, timestamp, action) VALUES(\'' . $ipaddr . '\', ' . time() . ', \'credential\');');
       
   799         $fails++;
       
   800         return array(
       
   801             'success' => false,
       
   802             'error' => ( $fails >= $threshold ) ? 'locked_out' : 'invalid_credentials',
       
   803             'lockout_threshold' => $threshold,
       
   804             'lockout_duration' => ( $duration / 60 ),
       
   805             'lockout_fails' => $fails,
       
   806             'time_rem' => ( $duration / 60 ),
       
   807             'lockout_policy' => $policy
       
   808           );
       
   809       }
       
   810         
       
   811       return array(
       
   812         'success' => false,
       
   813         'error' => 'invalid_credentials'
       
   814       );
       
   815     }
       
   816   }
   621   }
   817   
   622   
   818   /**
   623   /**
   819    * Attempts to login without using crypto stuff, mainly for use when the other side doesn't like Javascript
   624    * Attempts to login without using crypto stuff, mainly for use when the other side doesn't like Javascript
   820    * This method of authentication is inherently insecure, there's really nothing we can do about it except hope and pray that everyone moves to Firefox
   625    * This method of authentication is inherently insecure, there's really nothing we can do about it except hope and pray that everyone moves to Firefox
   821    * Technically it still uses crypto, but it only decrypts the password already stored, which is (obviously) required for authentication
   626    * Technically it still uses crypto, but it only decrypts the password already stored, which is (obviously) required for authentication
   822    * @param string $username The username
   627    * @param string $username The username
   823    * @param string $password The password -OR- the MD5 hash of the password if $already_md5ed is true
   628    * @param string $password The password -OR- the MD5 hash of the password if $already_md5ed is true
   824    * @param bool $already_md5ed This should be set to true if $password is an MD5 hash, and should be false if it's plaintext. Defaults to false.
   629    * @param bool $already_md5ed This should be set to true if $password is an MD5 hash, and should be false if it's plaintext. Defaults to false.
   825    * @param int $level The privilege level we're authenticating for, defaults to 0
   630    * @param int $level The privilege level we're authenticating for, defaults to 0
   826    */
   631    * @param string $captcha_hash Optional. If we're locked out and the lockout policy is captcha, this should be the identifier for the code.
   827   
   632    * @param string $captcha_code Optional. If we're locked out and the lockout policy is captcha, this should be the code the user entered.
   828   function login_without_crypto($username, $password, $already_md5ed = false, $level = USER_LEVEL_MEMBER, $captcha_hash = false, $captcha_code = false)
   633    * @param bool $remember Optional. If true, remembers the session for X days. Otherwise, assigns a short session. Defaults to false.
       
   634    */
       
   635   
       
   636   function login_without_crypto($username, $password, $already_md5ed = false, $level = USER_LEVEL_MEMBER, $captcha_hash = false, $captcha_code = false, $remember = false)
   829   {
   637   {
   830     global $db, $session, $paths, $template, $plugins; // Common objects
   638     global $db, $session, $paths, $template, $plugins; // Common objects
   831     
   639     
   832     $pass_hashed = ( $already_md5ed ) ? $password : md5($password);
   640     $pass_hashed = ( $already_md5ed ) ? $password : md5($password);
   833     
   641     
   977       if((int)$level > (int)$row['user_level'])
   785       if((int)$level > (int)$row['user_level'])
   978         return array(
   786         return array(
   979           'success' => false,
   787           'success' => false,
   980           'error' => 'too_big_for_britches'
   788           'error' => 'too_big_for_britches'
   981         );
   789         );
   982       $sess = $this->register_session(intval($row['user_id']), $username, $real_pass, $level);
   790       $sess = $this->register_session(intval($row['user_id']), $username, $real_pass, $level, $remember);
   983       if($sess)
   791       if($sess)
   984       {
   792       {
   985         if($level > USER_LEVEL_MEMBER)
   793         if($level > USER_LEVEL_MEMBER)
   986           $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_good\', '.time().', \''.enano_date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
   794           $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_good\', '.time().', \''.enano_date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
   987         else
   795         else
  1070    * Basically the session key is a hex-encoded cookie (encrypted with the site's private key) that says "u=[username];p=[sha1 of password];s=[unique key id]"
   878    * Basically the session key is a hex-encoded cookie (encrypted with the site's private key) that says "u=[username];p=[sha1 of password];s=[unique key id]"
  1071    * @param int $user_id
   879    * @param int $user_id
  1072    * @param string $username
   880    * @param string $username
  1073    * @param string $password
   881    * @param string $password
  1074    * @param int $level The level of access to grant, defaults to USER_LEVEL_MEMBER
   882    * @param int $level The level of access to grant, defaults to USER_LEVEL_MEMBER
       
   883    * @param bool $remember Whether the session should be long-term (true) or not (false). Defaults to short-term.
  1075    * @return bool
   884    * @return bool
  1076    */
   885    */
  1077    
   886    
  1078   function register_session($user_id, $username, $password, $level = USER_LEVEL_MEMBER)
   887   function register_session($user_id, $username, $password, $level = USER_LEVEL_MEMBER, $remember = false)
  1079   {
   888   {
  1080     // Random key identifier
   889     // Random key identifier
  1081     $salt = md5(microtime() . mt_rand());
   890     $salt = md5(microtime() . mt_rand());
  1082     
   891     
  1083     // SHA1 hash of password, stored in the key
   892     // SHA1 hash of password, stored in the key
  1084     $passha1 = sha1($password);
   893     $passha1 = sha1($password);
  1085     
   894     
  1086     // Unencrypted session key
   895     // Unencrypted session key
  1087     $session_key = "u=$username;p=$passha1;s=$salt";
   896     $session_key = "u=$username;p=$passha1;s=$salt";
       
   897     
       
   898     // Type of key
       
   899     $key_type = ( $level > USER_LEVEL_MEMBER ) ? SK_ELEV : ( $remember ? SK_LONG : SK_SHORT );
  1088     
   900     
  1089     // Encrypt the key
   901     // Encrypt the key
  1090     $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
   902     $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  1091     $session_key = $aes->encrypt($session_key, $this->private_key, ENC_HEX);
   903     $session_key = $aes->encrypt($session_key, $this->private_key, ENC_HEX);
  1092     $dec_DEBUG = $aes->decrypt($session_key, $this->private_key, ENC_HEX);
       
  1093     
   904     
  1094     // If we're registering an elevated-privilege key, it needs to be on GET
   905     // If we're registering an elevated-privilege key, it needs to be on GET
  1095     if($level > USER_LEVEL_MEMBER)
   906     if($level > USER_LEVEL_MEMBER)
  1096     {
   907     {
  1097       // Reverse it - cosmetic only ;-)
   908       // Reverse it - cosmetic only ;-)
  1120       die('Somehow an SQL injection attempt crawled into our session registrar! (1)');
   931       die('Somehow an SQL injection attempt crawled into our session registrar! (1)');
  1121     if(!is_int($level))
   932     if(!is_int($level))
  1122       die('Somehow an SQL injection attempt crawled into our session registrar! (2)');
   933       die('Somehow an SQL injection attempt crawled into our session registrar! (2)');
  1123     
   934     
  1124     // All done!
   935     // All done!
  1125     $query = $this->sql('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time) VALUES(\''.$keyhash.'\', \''.$salt.'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.');');
   936     $query = $this->sql('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time, key_type) VALUES(\''.$keyhash.'\', \''.$salt.'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.', ' . $key_type . ');');
  1126     return true;
   937     return true;
  1127   }
   938   }
  1128   
   939   
  1129   /**
   940   /**
  1130    * Identical to register_session in nature, but uses the old login/table structure. DO NOT use this except in the upgrade script under very controlled circumstances.
   941    * Identical to register_session in nature, but uses the old login/table structure. DO NOT use this except in the upgrade script under very controlled circumstances.
  1218     $keyhash = md5($key);
  1029     $keyhash = md5($key);
  1219     $salt = $db->escape($keydata[3]);
  1030     $salt = $db->escape($keydata[3]);
  1220     profiler_log("SessionManager: checking session: " . sha1($key) . ": decrypted session key to $decrypted_key");
  1031     profiler_log("SessionManager: checking session: " . sha1($key) . ": decrypted session key to $decrypted_key");
  1221     // using a normal call to $db->sql_query to avoid failing on errors here
  1032     // using a normal call to $db->sql_query to avoid failing on errors here
  1222     $query = $db->sql_query('SELECT u.user_id AS uid,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,' . "\n"
  1033     $query = $db->sql_query('SELECT u.user_id AS uid,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,' . "\n"
  1223                              . '    u.reg_time,u.account_active,u.activation_key,u.user_lang,u.user_title,k.source_ip,k.time,k.auth_level,COUNT(p.message_id) AS num_pms,' . "\n"
  1034                              . '    u.reg_time,u.account_active,u.activation_key,u.user_lang,u.user_title,k.source_ip,k.time,k.auth_level,k.key_type,COUNT(p.message_id) AS num_pms,' . "\n"
  1224                              . '    u.user_timezone, x.* FROM '.table_prefix.'session_keys AS k' . "\n"
  1035                              . '    u.user_timezone, x.* FROM '.table_prefix.'session_keys AS k' . "\n"
  1225                              . '  LEFT JOIN '.table_prefix.'users AS u' . "\n"
  1036                              . '  LEFT JOIN '.table_prefix.'users AS u' . "\n"
  1226                              . '    ON ( u.user_id=k.user_id )' . "\n"
  1037                              . '    ON ( u.user_id=k.user_id )' . "\n"
  1227                              . '  LEFT JOIN '.table_prefix.'users_extra AS x' . "\n"
  1038                              . '  LEFT JOIN '.table_prefix.'users_extra AS x' . "\n"
  1228                              . '    ON ( u.user_id=x.user_id OR x.user_id IS NULL )' . "\n"
  1039                              . '    ON ( u.user_id=x.user_id OR x.user_id IS NULL )' . "\n"
  1232                              . '    AND k.salt=\''.$salt.'\'' . "\n"
  1043                              . '    AND k.salt=\''.$salt.'\'' . "\n"
  1233                              . '  GROUP BY u.user_id,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,u.user_lang,u.user_timezone,u.user_title,k.source_ip,k.time,k.auth_level,x.user_id, x.user_aim, x.user_yahoo, x.user_msn, x.user_xmpp, x.user_homepage, x.user_location, x.user_job, x.user_hobbies, x.email_public, x.disable_js_fx;');
  1044                              . '  GROUP BY u.user_id,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,u.user_lang,u.user_timezone,u.user_title,k.source_ip,k.time,k.auth_level,x.user_id, x.user_aim, x.user_yahoo, x.user_msn, x.user_xmpp, x.user_homepage, x.user_location, x.user_job, x.user_hobbies, x.email_public, x.disable_js_fx;');
  1234     
  1045     
  1235     if ( !$query && ( defined('IN_ENANO_INSTALL') or defined('IN_ENANO_UPGRADE') ) )
  1046     if ( !$query && ( defined('IN_ENANO_INSTALL') or defined('IN_ENANO_UPGRADE') ) )
  1236     {
  1047     {
  1237       $query = $this->sql('SELECT u.user_id AS uid,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,COUNT(p.message_id) AS num_pms, 1440 AS user_timezone FROM '.table_prefix.'session_keys AS k
  1048       $query = $this->sql('SELECT u.user_id AS uid,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,COUNT(p.message_id) AS num_pms, 1440 AS user_timezone, ' . SK_SHORT . ' AS key_type FROM '.table_prefix.'session_keys AS k
  1238                              LEFT JOIN '.table_prefix.'users AS u
  1049                              LEFT JOIN '.table_prefix.'users AS u
  1239                                ON ( u.user_id=k.user_id )
  1050                                ON ( u.user_id=k.user_id )
  1240                              LEFT JOIN '.table_prefix.'privmsgs AS p
  1051                              LEFT JOIN '.table_prefix.'privmsgs AS p
  1241                                ON ( p.message_to=u.username AND p.message_read=0 )
  1052                                ON ( p.message_to=u.username AND p.message_read=0 )
  1242                              WHERE k.session_key=\''.$keyhash.'\'
  1053                              WHERE k.session_key=\''.$keyhash.'\'
  1287       // Failed password check
  1098       // Failed password check
  1288       // echo '(debug) $session->validate_session: encrypted password is wrong<br />Real password: '.$real_pass.'<br />Real hash: '.sha1($real_pass).'<br />User hash: '.$keydata[2];
  1099       // echo '(debug) $session->validate_session: encrypted password is wrong<br />Real password: '.$real_pass.'<br />Real hash: '.sha1($real_pass).'<br />User hash: '.$keydata[2];
  1289       return false;
  1100       return false;
  1290     }
  1101     }
  1291     
  1102     
  1292     $time_now = time();
  1103     // timestamp check
  1293     $time_key = $row['time'] + 900;
  1104     switch ( $row['key_type'] )
  1294     if($time_now > $time_key && $row['auth_level'] > USER_LEVEL_MEMBER)
  1105     {
  1295     {
  1106       case SK_SHORT:
  1296       // Session timed out
  1107         $time_now = time();
  1297       // echo '(debug) $session->validate_session: super session timed out<br />';
  1108         $time_key = $row['time'] + ( 60 * intval(getConfig('session_short', '720')) );
  1298       $this->sw_timed_out = true;
  1109         if ( $time_now > $time_key )
  1299       return false;
  1110         {
  1300     }
  1111           // Session timed out
  1301     
  1112           return false;
  1302     // If this is an elevated-access session key, update the time
  1113         }
  1303     if( $row['auth_level'] > USER_LEVEL_MEMBER )
  1114         break;
       
  1115       case SK_LONG:
       
  1116         if ( intval(getConfig('session_remember_time', '0')) === 0 )
       
  1117         {
       
  1118           // sessions last infinitely, timestamp validation is therefore successful
       
  1119           break;
       
  1120         }
       
  1121         $time_now = time();
       
  1122         $time_key = $row['time'] + ( 86400 * intval(getConfig('session_remember_time', '30')) );
       
  1123         if ( $time_now > $time_key )
       
  1124         {
       
  1125           // Session timed out
       
  1126           return false;
       
  1127         }
       
  1128         break;
       
  1129       case SK_ELEV:
       
  1130         $time_now = time();
       
  1131         $time_key = $row['time'] + 900;
       
  1132         if($time_now > $time_key && $row['auth_level'] > USER_LEVEL_MEMBER)
       
  1133         {
       
  1134           // Session timed out
       
  1135           // echo '(debug) $session->validate_session: super session timed out<br />';
       
  1136           $this->sw_timed_out = true;
       
  1137           return false;
       
  1138         }
       
  1139         break;
       
  1140     }
       
  1141         
       
  1142     // If this is an elevated-access or short-term session key, update the time
       
  1143     if( $row['key_type'] == SK_ELEV || $row['key_type'] == SK_SHORT )
  1304     {
  1144     {
  1305       $this->sql('UPDATE '.table_prefix.'session_keys SET time='.time().' WHERE session_key=\''.$keyhash.'\';');
  1145       $this->sql('UPDATE '.table_prefix.'session_keys SET time='.time().' WHERE session_key=\''.$keyhash.'\';');
  1306     }
  1146     }
  1307     
  1147     
  1308     $user_extra = array();
  1148     $user_extra = array();
  3615         $response['allow_diffiehellman'] = $dh_supported;
  3455         $response['allow_diffiehellman'] = $dh_supported;
  3616         
  3456         
  3617         $response['username'] = ( $this->user_logged_in ) ? $this->username : false;
  3457         $response['username'] = ( $this->user_logged_in ) ? $this->username : false;
  3618         $response['aes_key'] = $this->rijndael_genkey();
  3458         $response['aes_key'] = $this->rijndael_genkey();
  3619         
  3459         
       
  3460         $response['extended_time'] = intval(getConfig('session_remember_time', '30'));
       
  3461         
  3620         // Lockout info
  3462         // Lockout info
  3621         $response['locked_out'] = $locked_out;
  3463         $response['locked_out'] = $locked_out;
  3622         
  3464         
  3623         $response['lockout_info'] = $lockdata;
  3465         $response['lockout_info'] = $lockdata;
  3624         if ( $policy == 'captcha' && $locked_out )
  3466         if ( $policy == 'captcha' && $locked_out )
  3755         // A bit hackish since it just dies with the response :-(
  3597         // A bit hackish since it just dies with the response :-(
  3756         $plugins->attachHook('login_password_reset', '$this->process_login_request(array(\'mode\' => \'respond_password_reset\', \'user_id\' => $row[\'user_id\'], \'temp_password\' => $row[\'temp_password\']));');
  3598         $plugins->attachHook('login_password_reset', '$this->process_login_request(array(\'mode\' => \'respond_password_reset\', \'user_id\' => $row[\'user_id\'], \'temp_password\' => $row[\'temp_password\']));');
  3757         
  3599         
  3758         // attempt the login
  3600         // attempt the login
  3759         // function login_without_crypto($username, $password, $already_md5ed = false, $level = USER_LEVEL_MEMBER, $captcha_hash = false, $captcha_code = false)
  3601         // function login_without_crypto($username, $password, $already_md5ed = false, $level = USER_LEVEL_MEMBER, $captcha_hash = false, $captcha_code = false)
  3760         $login_result = $this->login_without_crypto($username, $password, false, intval($req['level']), @$req['captcha_hash'], @$req['captcha_code']);
  3602         $login_result = $this->login_without_crypto($username, $password, false, intval($req['level']), @$req['captcha_hash'], @$req['captcha_code'], @$req['remember']);
  3761         
  3603         
  3762         if ( $login_result['success'] )
  3604         if ( $login_result['success'] )
  3763         {
  3605         {
  3764           return array(
  3606           return array(
  3765               'mode' => 'login_success',
  3607               'mode' => 'login_success',