includes/sessions.php
changeset 1132 05fe0039d952
parent 1101 30d8bb88572d
child 1148 dcf6e9394e02
equal deleted inserted replaced
1131:adfbe522c95f 1132:05fe0039d952
   674    * Technically it still uses crypto, but it only decrypts the password already stored, which is (obviously) required for authentication
   674    * Technically it still uses crypto, but it only decrypts the password already stored, which is (obviously) required for authentication
   675    * @param string $username The username
   675    * @param string $username The username
   676    * @param string $password The password -OR- the MD5 hash of the password if $already_md5ed is true
   676    * @param string $password The password -OR- the MD5 hash of the password if $already_md5ed is true
   677    * @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.
   677    * @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.
   678    * @param int $level The privilege level we're authenticating for, defaults to 0
   678    * @param int $level The privilege level we're authenticating for, defaults to 0
   679    * @param string $captcha_hash Optional. If we're locked out and the lockout policy is captcha, this should be the identifier for the code.
       
   680    * @param string $captcha_code Optional. If we're locked out and the lockout policy is captcha, this should be the code the user entered.
       
   681    * @param bool $remember Optional. If true, remembers the session for X days. Otherwise, assigns a short session. Defaults to false.
   679    * @param bool $remember Optional. If true, remembers the session for X days. Otherwise, assigns a short session. Defaults to false.
   682    */
   680    */
   683   
   681   
   684   function login_without_crypto($username, $password, $already_md5ed = false, $level = USER_LEVEL_MEMBER, $captcha_hash = false, $captcha_code = false, $remember = false)
   682   function login_without_crypto($username, $password, $already_md5ed = false, $level = USER_LEVEL_MEMBER, $remember = false)
   685   {
   683   {
   686     global $db, $session, $paths, $template, $plugins; // Common objects
   684     global $db, $session, $paths, $template, $plugins; // Common objects
   687     
   685     
   688     if ( $already_md5ed )
   686     if ( $already_md5ed )
   689     {
   687     {
   700     
   698     
   701     // Perhaps we're upgrading Enano?
   699     // Perhaps we're upgrading Enano?
   702     if($this->compat)
   700     if($this->compat)
   703     {
   701     {
   704       return $this->login_compat($username, md5($password), $level);
   702       return $this->login_compat($username, md5($password), $level);
   705     }
       
   706     
       
   707     // Lockout check
       
   708     if ( !defined('IN_ENANO_INSTALL') )
       
   709     {
       
   710       $lockout_data = $this->get_lockout_info($lockout_data);
       
   711       
       
   712       $captcha_good = false;
       
   713       if ( $lockout_data['lockout_policy'] == 'captcha' && $captcha_hash && $captcha_code )
       
   714       {
       
   715         // policy is captcha -- check if it's correct, and if so, bypass lockout check
       
   716         $real_code = $this->get_captcha($captcha_hash);
       
   717         if ( strtolower($real_code) === strtolower($captcha_code) )
       
   718         {
       
   719           $captcha_good = true;
       
   720         }
       
   721       }
       
   722       if ( $lockout_data['lockout_policy'] != 'disable' && !$captcha_good )
       
   723       {
       
   724         if ( $lockout_data['lockout_fails'] >= $lockout_data['lockout_threshold'] )
       
   725         {
       
   726           // ooh boy, somebody's in trouble ;-)
       
   727           return array(
       
   728               'success' => false,
       
   729               'error' => 'locked_out',
       
   730               'lockout_threshold' => $lockout_data['lockout_threshold'],
       
   731               'lockout_duration' => ( $lockout_data['lockout_duration'] ),
       
   732               'lockout_fails' => $lockout_data['lockout_fails'],
       
   733               'lockout_policy' => $lockout_data['lockout_policy'],
       
   734               'time_rem' => $lockout_data['time_rem'],
       
   735               'lockout_last_time' => $lockout_data['lockout_last_time']
       
   736             );
       
   737         }
       
   738       }
       
   739     }
   703     }
   740     
   704     
   741     // Instanciate the Rijndael encryption object
   705     // Instanciate the Rijndael encryption object
   742     $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
   706     $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
   743     
   707     
   764         $this->sql('INSERT INTO ' . table_prefix . "logs(log_type,action,time_id,date_string,author,edit_summary) VALUES\n"
   728         $this->sql('INSERT INTO ' . table_prefix . "logs(log_type,action,time_id,date_string,author,edit_summary) VALUES\n"
   765                    . '  (\'security\', \'auth_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', '
   729                    . '  (\'security\', \'auth_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', '
   766                       . '\''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
   730                       . '\''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
   767       
   731       
   768       // Do we also need to increment the lockout countdown?
   732       // Do we also need to increment the lockout countdown?
   769       if ( @$lockout_data['lockout_policy'] != 'disable' && !defined('IN_ENANO_INSTALL') )
   733       if ( !defined('IN_ENANO_INSTALL') )
       
   734         $lockout_data = $this->get_lockout_info();
       
   735       else
       
   736         $lockout_data = array(
       
   737           'lockout_policy' => 'disable'
       
   738           );
       
   739       
       
   740       if ( $lockout_data['lockout_policy'] != 'disable' && !defined('IN_ENANO_INSTALL') )
   770       {
   741       {
   771         $ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
   742         $ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
   772         // increment fail count
   743         // increment fail count
   773         $this->sql('INSERT INTO '.table_prefix.'lockout(ipaddr, timestamp, action) VALUES(\'' . $ipaddr . '\', ' . time() . ', \'credential\');');
   744         $this->sql('INSERT INTO '.table_prefix.'lockout(ipaddr, timestamp, action) VALUES(\'' . $ipaddr . '\', ' . time() . ', \'credential\');');
   774         $lockout_data['lockout_fails']++;
   745         $lockout_data['lockout_fails']++;
   889         $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(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
   860         $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(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
   890       else
   861       else
   891         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
   862         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
   892         
   863         
   893       // Do we also need to increment the lockout countdown?
   864       // Do we also need to increment the lockout countdown?
   894       if ( !defined('IN_ENANO_INSTALL') && $lockout_data['lockout_policy'] != 'disable' )
   865       if ( !defined('IN_ENANO_INSTALL') && getConfig('lockout_policy', 'lockout') !== 'disable' )
   895       {
   866       {
   896         $ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
   867         $ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
   897         // increment fail count
   868         // increment fail count
   898         $this->sql('INSERT INTO '.table_prefix.'lockout(ipaddr, timestamp, action) VALUES(\'' . $ipaddr . '\', ' . time() . ', \'credential\');');
   869         $this->sql('INSERT INTO '.table_prefix.'lockout(ipaddr, timestamp, action) VALUES(\'' . $ipaddr . '\', ' . time() . ', \'credential\');');
   899         $lockout_data['lockout_fails']++;
       
   900         return array(
       
   901             'success' => false,
       
   902             'error' => ( $lockout_data['lockout_fails'] >= $lockout_data['lockout_threshold'] ) ? 'locked_out' : 'invalid_credentials',
       
   903             'lockout_threshold' => $lockout_data['lockout_threshold'],
       
   904             'lockout_duration' => ( $lockout_data['lockout_duration'] ),
       
   905             'lockout_fails' => $lockout_data['lockout_fails'],
       
   906             'lockout_policy' => $lockout_data['lockout_policy']
       
   907           );
       
   908       }
   870       }
   909         
   871         
   910       return array(
   872       return array(
   911         'success' => false,
   873         'success' => false,
   912         'error' => 'invalid_credentials'
   874         'error' => 'invalid_credentials'
  1069    * Tells us if we're locked out from logging in or not.
  1031    * Tells us if we're locked out from logging in or not.
  1070    * @param reference will be filled with information regarding in-progress lockout
  1032    * @param reference will be filled with information regarding in-progress lockout
  1071    * @return bool True if locked out, false otherwise
  1033    * @return bool True if locked out, false otherwise
  1072    */
  1034    */
  1073   
  1035   
  1074   function get_lockout_info(&$lockdata)
  1036   function get_lockout_info()
  1075   {
  1037   {
  1076     global $db;
  1038     global $db;
  1077     
  1039     
  1078     // this has to be initialized to hide warnings
  1040     // this has to be initialized to hide warnings
  1079     $lockdata = null;
  1041     $lockdata = null;
  1094       $q = $this->sql('SELECT timestamp FROM ' . table_prefix . 'lockout WHERE timestamp > ' . $timestamp_cutoff . ' AND ipaddr = \'' . $ipaddr . '\' ORDER BY timestamp DESC;');
  1056       $q = $this->sql('SELECT timestamp FROM ' . table_prefix . 'lockout WHERE timestamp > ' . $timestamp_cutoff . ' AND ipaddr = \'' . $ipaddr . '\' ORDER BY timestamp DESC;');
  1095       $fails = $db->numrows($q);
  1057       $fails = $db->numrows($q);
  1096       $row = $db->fetchrow($q);
  1058       $row = $db->fetchrow($q);
  1097       $locked_out = ( $fails >= $threshold );
  1059       $locked_out = ( $fails >= $threshold );
  1098       $lockdata = array(
  1060       $lockdata = array(
  1099           'locked_out' => $locked_out,
  1061           'active' => $locked_out,
  1100           'lockout_threshold' => $threshold,
  1062           'threshold' => $threshold,
  1101           'lockout_duration' => ( $duration / 60 ),
  1063           'duration' => ( $duration / 60 ),
  1102           'lockout_fails' => $fails,
  1064           'fails' => $fails,
  1103           'lockout_policy' => $policy,
  1065           'policy' => $policy,
  1104           'lockout_last_time' => $row['timestamp'],
  1066           'last_time' => $row['timestamp'],
  1105           'time_rem' => $locked_out ? ( $duration / 60 ) - round( ( time() - $row['timestamp'] ) / 60 ) : 0,
  1067           'time_rem' => $locked_out ? ( $duration / 60 ) - round( ( time() - $row['timestamp'] ) / 60 ) : 0,
  1106           'captcha' => ''
  1068           'captcha' => $policy == 'captcha' ? $this->make_captcha() : ''
  1107         );
  1069         );
  1108       $db->free_result();
  1070       $db->free_result();
  1109     }
  1071     }
  1110     else
  1072     else
  1111     {
  1073     {
  1112       // disabled; send back default dataset
  1074       // disabled; send back default dataset
  1113       $lockdata = array(
  1075       $lockdata = array(
  1114         'locked_out' => false,
  1076         'active' => false,
  1115         'lockout_threshold' => $threshold,
  1077         'threshold' => $threshold,
  1116         'lockout_duration' => ( $duration / 60 ),
  1078         'duration' => ( $duration / 60 ),
  1117         'lockout_fails' => 0,
  1079         'fails' => 0,
  1118         'lockout_policy' => $policy,
  1080         'policy' => $policy,
  1119         'lockout_last_time' => 0,
  1081         'last_time' => 0,
  1120         'time_rem' => 0,
  1082         'time_rem' => 0,
  1121         'captcha' => ''
  1083         'captcha' => ''
  1122       );
  1084       );
  1123     }
  1085     }
  1124     return $lockdata;
  1086     return $lockdata;
  3867     global $_math;
  3829     global $_math;
  3868     
  3830     
  3869     // Check for the mode
  3831     // Check for the mode
  3870     if ( !isset($req['mode']) )
  3832     if ( !isset($req['mode']) )
  3871     {
  3833     {
  3872       return array(
  3834       return $this->get_login_response('api_error', 'ERR_JSON_NO_MODE');
  3873           'mode' => 'error',
       
  3874           'error' => 'ERR_JSON_NO_MODE'
       
  3875         );
       
  3876     }
  3835     }
  3877     
  3836     
  3878     // Main processing switch
  3837     // Main processing switch
  3879     switch ( $req['mode'] )
  3838     switch ( $req['mode'] )
  3880     {
  3839     {
  3881       default:
  3840       default:
  3882         return array(
  3841         return $this->get_login_response('api_error', 'ERR_JSON_INVALID_MODE');
  3883             'mode' => 'error',
       
  3884             'error' => 'ERR_JSON_INVALID_MODE'
       
  3885           );
       
  3886         break;
  3842         break;
  3887       case 'getkey':
  3843       case 'getkey':
  3888         
  3844         
  3889         $this->start();
  3845         $this->start();
  3890         
  3846         
  3891         $locked_out = $this->get_lockout_info($lockdata);
  3847         return $this->get_login_response('initial');
  3892         
       
  3893         $response = array('mode' => 'build_box');
       
  3894         $response['allow_diffiehellman'] = $dh_supported;
       
  3895         
       
  3896         $response['username'] = ( $this->user_logged_in ) ? $this->username : false;
       
  3897         $response['aes_key'] = $this->rijndael_genkey();
       
  3898         
       
  3899         $response['extended_time'] = intval(getConfig('session_remember_time', '30'));
       
  3900         
       
  3901         // Lockout info
       
  3902         $response['locked_out'] = $locked_out;
       
  3903         
       
  3904         $response['lockout_info'] = $lockdata;
       
  3905         if ( $lockdata['lockout_policy'] == 'captcha' && $locked_out )
       
  3906         {
       
  3907           $response['lockout_info']['captcha'] = $this->make_captcha();
       
  3908         }
       
  3909         
       
  3910         // Can we do Diffie-Hellman? If so, generate and stash a public/private key pair.
       
  3911         if ( $dh_supported )
       
  3912         {
       
  3913           $dh_key_priv = dh_gen_private();
       
  3914           $dh_key_pub = dh_gen_public($dh_key_priv);
       
  3915           $dh_key_priv = $_math->str($dh_key_priv);
       
  3916           $dh_key_pub = $_math->str($dh_key_pub);
       
  3917           $response['dh_public_key'] = $dh_key_pub;
       
  3918           // store the keys in the DB
       
  3919           $q = $db->sql_query('INSERT INTO ' . table_prefix . "diffiehellman( public_key, private_key ) VALUES ( '$dh_key_pub', '$dh_key_priv' );");
       
  3920           if ( !$q )
       
  3921             $db->die_json();
       
  3922         }
       
  3923         
       
  3924         return $response;
       
  3925         break;
  3848         break;
  3926       case 'login_dh':
  3849       case 'login_dh':
  3927         // User is requesting a login and has sent Diffie-Hellman data.
  3850         // User is requesting a login and has sent Diffie-Hellman data.
  3928         
  3851         
  3929         //
  3852         //
  3935         $dh_hash = $req['dh_secret_hash'];
  3858         $dh_hash = $req['dh_secret_hash'];
  3936         
  3859         
  3937         // Check the key
  3860         // Check the key
  3938         if ( !ctype_digit($dh_public) || !ctype_digit($req['dh_client_key']) )
  3861         if ( !ctype_digit($dh_public) || !ctype_digit($req['dh_client_key']) )
  3939         {
  3862         {
  3940           return array(
  3863           return $this->get_login_response('api_error', 'ERR_DH_KEY_NOT_NUMERIC');
  3941             'mode' => 'error',
       
  3942             'error' => 'ERR_DH_KEY_NOT_NUMERIC'
       
  3943           );
       
  3944         }
  3864         }
  3945         
  3865         
  3946         // Fetch private key
  3866         // Fetch private key
  3947         $q = $db->sql_query('SELECT private_key, key_id FROM ' . table_prefix . "diffiehellman WHERE public_key = '$dh_public';");
  3867         $q = $db->sql_query('SELECT private_key, key_id FROM ' . table_prefix . "diffiehellman WHERE public_key = '$dh_public';");
  3948         if ( !$q )
  3868         if ( !$q )
  3949           $db->die_json();
  3869           $db->die_json();
  3950         
  3870         
  3951         if ( $db->numrows() < 1 )
  3871         if ( $db->numrows() < 1 )
  3952         {
  3872         {
  3953           return array(
  3873           return $this->get_login_response('api_error', 'ERR_DH_KEY_NOT_FOUND');
  3954             'mode' => 'error',
       
  3955             'error' => 'ERR_DH_KEY_NOT_FOUND'
       
  3956           );
       
  3957         }
  3874         }
  3958         
  3875         
  3959         list($dh_private, $dh_key_id) = $db->fetchrow_num();
  3876         list($dh_private, $dh_key_id) = $db->fetchrow_num();
  3960         $db->free_result();
  3877         $db->free_result();
  3961         
  3878         
  3970         
  3887         
  3971         // Did we get all our math right?
  3888         // Did we get all our math right?
  3972         $dh_secret_check = sha1($dh_secret);
  3889         $dh_secret_check = sha1($dh_secret);
  3973         if ( $dh_secret_check !== $dh_hash )
  3890         if ( $dh_secret_check !== $dh_hash )
  3974         {
  3891         {
  3975           return array(
  3892           return $this->get_login_response('api_error', 'ERR_DH_HASH_NO_MATCH');
  3976             'mode' => 'error',
       
  3977             'error' => 'ERR_DH_HASH_NO_MATCH',
       
  3978           );
       
  3979         }
  3893         }
  3980         
  3894         
  3981         // All good! Generate the AES key
  3895         // All good! Generate the AES key
  3982         $aes_key = substr(sha256($dh_secret), 0, ( AES_BITS / 4 ));
  3896         $aes_key = substr(sha256($dh_secret), 0, ( AES_BITS / 4 ));
  3983       case 'login_aes':
  3897       case 'login_aes':
  3985         {
  3899         {
  3986           // login_aes-specific code
  3900           // login_aes-specific code
  3987           $aes_key = $this->fetch_public_key($req['key_aes']);
  3901           $aes_key = $this->fetch_public_key($req['key_aes']);
  3988           if ( !$aes_key )
  3902           if ( !$aes_key )
  3989           {
  3903           {
  3990             return array(
  3904             return $this->get_login_response('api_error', 'ERR_AES_LOOKUP_FAILED');
  3991               'mode' => 'error',
       
  3992               'error' => 'ERR_AES_LOOKUP_FAILED'
       
  3993             );
       
  3994           }
  3905           }
  3995           $userinfo_crypt = $req['userinfo'];
  3906           $userinfo_crypt = $req['userinfo'];
  3996         }
  3907         }
  3997         // shared between the two systems from here on out
  3908         // shared between the two systems from here on out
  3998         
  3909         
  4001         $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  3912         $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  4002         // using "true" here disables caching of the decrypted login info (which includes the password)
  3913         // using "true" here disables caching of the decrypted login info (which includes the password)
  4003         $userinfo_json = $aes->decrypt($userinfo_crypt, $aes_key, ENC_HEX, true);
  3914         $userinfo_json = $aes->decrypt($userinfo_crypt, $aes_key, ENC_HEX, true);
  4004         if ( !$userinfo_json )
  3915         if ( !$userinfo_json )
  4005         {
  3916         {
  4006           return array(
  3917           return $this->get_login_response('api_error', 'ERR_AES_DECRYPT_FAILED');
  4007             'mode' => 'error',
       
  4008             'error' => 'ERR_AES_DECRYPT_FAILED'
       
  4009           );
       
  4010         }
  3918         }
  4011         // de-JSON user info
  3919         // de-JSON user info
  4012         try
  3920         try
  4013         {
  3921         {
  4014           $userinfo = enano_json_decode($userinfo_json);
  3922           $userinfo = enano_json_decode($userinfo_json);
  4015         }
  3923         }
  4016         catch ( Exception $e )
  3924         catch ( Exception $e )
  4017         {
  3925         {
  4018           return array(
  3926           return $this->get_login_response('api_error', 'ERR_USERINFO_DECODE_FAILED');
  4019             'mode' => 'error',
  3927         }
  4020             'error' => 'ERR_USERINFO_DECODE_FAILED'
  3928         
  4021           );
  3929       case 'login_pt':
       
  3930         // plaintext login
       
  3931         if ( $req['mode'] == 'login_pt' )
       
  3932         {
       
  3933           $userinfo = isset($req['userinfo']) ? $req['userinfo'] : array();
  4022         }
  3934         }
  4023         
  3935         
  4024         if ( !isset($userinfo['username']) || !isset($userinfo['password']) )
  3936         if ( !isset($userinfo['username']) || !isset($userinfo['password']) )
  4025         {
  3937         {
  4026           return array(
  3938           return $this->get_login_response('api_error', 'ERR_USERINFO_MISSING_VALUES');
  4027             'mode' => 'error',
       
  4028             'error' => 'ERR_USERINFO_MISSING_VALUES'
       
  4029           );
       
  4030         }
  3939         }
  4031         
  3940         
  4032         $username =& $userinfo['username'];
  3941         $username =& $userinfo['username'];
  4033         $password =& $userinfo['password'];
  3942         $password =& $userinfo['password'];
       
  3943         
       
  3944         // locked out? check captcha
       
  3945         $lockout_data = $this->get_lockout_info();
       
  3946         if ( $lockout_data['policy'] == 'captcha' && $lockout_data['active'] )
       
  3947         {
       
  3948           // policy is captcha -- check if it's correct, and if so, bypass lockout check
       
  3949           $real_code = $this->get_captcha($req['captcha_hash']);
       
  3950           if ( strtolower($real_code) !== strtolower($req['captcha_code']) )
       
  3951           {
       
  3952             // captcha is bad
       
  3953             return $this->get_login_response('login_failure', 'lockout_bad_captcha');
       
  3954           }
       
  3955         }
       
  3956         else if ( $lockout_data['policy'] == 'lockout' && $lockout_data['active'] )
       
  3957         {
       
  3958           // we're fully locked out
       
  3959           return $this->get_login_response('login_failure', 'lockout_request_denied');
       
  3960         }
  4034         
  3961         
  4035         // At this point if any extra info was injected into the login data packet, we need to let plugins process it
  3962         // At this point if any extra info was injected into the login data packet, we need to let plugins process it
  4036         /**
  3963         /**
  4037          * Called upon processing an incoming login request. If you added anything to the userinfo object during the jshook
  3964          * Called upon processing an incoming login request. If you added anything to the userinfo object during the jshook
  4038          * login_build_userinfo, that will be in the $userinfo array here. Expected return values are: true if your plugin has
  3965          * login_build_userinfo, that will be in the $userinfo array here. Expected return values are: true if your plugin has
  4047         foreach ( $code as $cmd )
  3974         foreach ( $code as $cmd )
  4048         {
  3975         {
  4049           $result = eval($cmd);
  3976           $result = eval($cmd);
  4050           if ( $result === true )
  3977           if ( $result === true )
  4051           {
  3978           {
  4052             return array(
  3979             return $this->get_login_response('login_success', false, array(
  4053                 'mode' => 'login_success',
  3980                 'key' => $this->sid_super,
  4054                 'key' => ( $this->sid_super ) ? $this->sid_super : false,
       
  4055                 'user_id' => $this->user_id,
  3981                 'user_id' => $this->user_id,
  4056                 'user_level' => $this->user_level
  3982                 'user_level' => $this->user_level,
  4057               );
  3983                 'reset' => false
       
  3984               ));
  4058           }
  3985           }
  4059           else if ( is_array($result) )
  3986           else if ( is_array($result) )
  4060           {
  3987           {
  4061             if ( isset($result['mode']) && $result['mode'] === 'error' && isset($result['error']) )
  3988             if ( isset($result['mode']) && $result['mode'] === 'error' && isset($result['error']) )
  4062             {
  3989             {
  4063               // Pass back any additional information from the error response
  3990               // Pass back any additional information from the error response
  4064               $append = $result;
  3991               $append = $result;
  4065               unset($append['mode'], $append['error']);
  3992               unset($append['mode'], $append['error']);
       
  3993               $append['from_plugin'] = true;
  4066               
  3994               
  4067               $return = array(
  3995               return $this->get_login_response('login_failure', $result['error'], $append);
  4068                 'mode' => 'login_failure',
       
  4069                 'error_code' => $result['error'],
       
  4070                 // Use this to provide a way to respawn the login box
       
  4071                 'respawn_info' => $this->process_login_request(array('mode' => 'getkey'))
       
  4072               );
       
  4073               
       
  4074               $return = array_merge($append, $return);
       
  4075               return $return;
       
  4076             }
  3996             }
  4077           }
  3997           }
  4078         }
  3998         }
  4079         
  3999         
  4080         // If we're logging in with a temp password, attach to the login_password_reset hook to send our JSON response
       
  4081         // A bit hackish since it just dies with the response :-(
       
  4082         $plugins->attachHook('login_password_reset', '$this->process_login_request(array(\'mode\' => \'respond_password_reset\', \'user_id\' => $row[\'user_id\'], \'temp_password\' => $this->pk_encrypt($password)));');
       
  4083         
       
  4084         // attempt the login
  4000         // attempt the login
  4085         // function login_without_crypto($username, $password, $already_md5ed = false, $level = USER_LEVEL_MEMBER, $captcha_hash = false, $captcha_code = false)
  4001         $login_result = $this->login_without_crypto($username, $password, false, intval($req['level']), @$req['remember']);
  4086         $login_result = $this->login_without_crypto($username, $password, false, intval($req['level']), @$req['captcha_hash'], @$req['captcha_code'], @$req['remember']);
       
  4087         
  4002         
  4088         if ( $login_result['success'] )
  4003         if ( $login_result['success'] )
  4089         {
  4004         {
  4090           return array(
  4005           return $this->get_login_response('login_success', false, array(
  4091               'mode' => 'login_success',
  4006               'key' => $this->sid_super,
  4092               'key' => ( $this->sid_super ) ? $this->sid_super : false,
       
  4093               'user_id' => $this->user_id,
  4007               'user_id' => $this->user_id,
  4094               'user_level' => $this->user_level
  4008               'user_level' => $this->user_level,
  4095             );
  4009             ));
       
  4010         }
       
  4011         else if ( !$login_result['success'] && $login_result['error'] === 'valid_reset' )
       
  4012         {
       
  4013           return $this->get_login_response('reset_pass_used', false, array(
       
  4014               'redirect_url' => $login_result['redirect_url']
       
  4015             ));
  4096         }
  4016         }
  4097         else
  4017         else
  4098         {
  4018         {
  4099           return array(
  4019           return $this->get_login_response('login_failure', 'invalid_credentials');
  4100               'mode' => 'login_failure',
       
  4101               'error_code' => $login_result['error'],
       
  4102               // Use this to provide a way to respawn the login box
       
  4103               'respawn_info' => $this->process_login_request(array('mode' => 'getkey'))
       
  4104             );
       
  4105         }
  4020         }
  4106         
  4021         
  4107         break;
  4022         break;
  4108       case 'clean_key':
  4023       case 'clean_key':
  4109         // Clean out a key, since it won't be used.
  4024         // Clean out a key, since it won't be used.
  4162         break;
  4077         break;
  4163     }
  4078     }
  4164     
  4079     
  4165   }
  4080   }
  4166   
  4081   
       
  4082   /**
       
  4083    * Generate a packet to send to the client for logins.
       
  4084    * @param string mode
       
  4085    * @param array 
       
  4086    * @return array
       
  4087    */
       
  4088   
       
  4089   function get_login_response($mode, $error = false, $base = array())
       
  4090   {
       
  4091     $this->start();
       
  4092     
       
  4093     // init
       
  4094     $response = $base;
       
  4095     // modules in the packet
       
  4096     $response['mode'] = $mode;
       
  4097     $response['error'] = $error;
       
  4098     $response['crypto'] = $mode !== 'login_success' ? $this->get_login_crypto_packet() : false;
       
  4099     $response['lockout'] = $mode !== 'login_success' ? $this->get_lockout_info() : false;
       
  4100     $response['extended_time'] = intval(getConfig('session_remember_time', '30'));
       
  4101     $response['username'] = $this->user_logged_in ? $this->username : false;
       
  4102     return $response;
       
  4103   }
       
  4104   
       
  4105   /**
       
  4106    * Get a packet of crypto flags for login.
       
  4107    * @return array
       
  4108    */
       
  4109   
       
  4110   function get_login_crypto_packet()
       
  4111   {
       
  4112     global $dh_supported, $_math;
       
  4113     
       
  4114     $response = array();
       
  4115     
       
  4116     $response['dh_enable'] = $dh_supported;
       
  4117     $response['aes_key'] = $this->rijndael_genkey();
       
  4118     
       
  4119     // Can we do Diffie-Hellman? If so, generate and stash a public/private key pair.
       
  4120     if ( $dh_supported )
       
  4121     {
       
  4122       $dh_key_priv = dh_gen_private();
       
  4123       $dh_key_pub = dh_gen_public($dh_key_priv);
       
  4124       $dh_key_priv = $_math->str($dh_key_priv);
       
  4125       $dh_key_pub = $_math->str($dh_key_pub);
       
  4126       $response['dh_public_key'] = $dh_key_pub;
       
  4127       // store the keys in the DB
       
  4128       $this->sql('INSERT INTO ' . table_prefix . "diffiehellman( public_key, private_key ) VALUES ( '$dh_key_pub', '$dh_key_priv' );");
       
  4129     }
       
  4130     
       
  4131     return $response;
       
  4132   }
       
  4133   
  4167 }
  4134 }
  4168 
  4135 
  4169 /**
  4136 /**
  4170  * Class used to fetch permissions for a specific page. Used internally by SessionManager.
  4137  * Class used to fetch permissions for a specific page. Used internally by SessionManager.
  4171  * @package Enano
  4138  * @package Enano