function perform_login() { global $api, $scramble_key, $enable_remote_apis, $lang, $max_login_attempts_wait_minutes, $max_login_attempts_per_ip, $max_login_attempts_per_username, $global_cookies, $username, $password, $password_hash; if (!$api && (strlen($password) == 32 || strlen($password) == 64) && getval("userkey", "") != md5($username . $scramble_key)) { exit("Invalid password."); # Prevent MD5s being entered directly while still supporting direct entry of plain text passwords (for systems that were set up prior to MD5 password encryption was added). If a special key is sent, which is the md5 hash of the username and the secret scramble key, then allow a login using the MD5 password hash as the password. This is for the 'log in as this user' feature. } if (strlen($password) != 64) { # Provided password is not a hash, so generate a hash. //$password_hash=md5("RS" . $username . $password); $password_hash = hash('sha256', md5("RS" . $username . $password)); } else { $password_hash = $password; } // ------- Automatic migration of md5 hashed or plain text passwords to SHA256 hashed passwords ------------ // This is necessary because older systems being upgraded may still have passwords stored using md5 hashes or even possibly stored in plain text. // Updated March 2015 - select password_reset_hash to force dbstruct that will update password column varchar(100) if not already $accountstoupdate = sql_query("select username, password, password_reset_hash from user where length(password)<>64"); foreach ($accountstoupdate as $account) { $oldpassword = $account["password"]; if (strlen($oldpassword) != 32) { $oldpassword = md5("RS" . $account["username"] . $oldpassword); } // Needed if we have a really old password, or if password has been manually reset in db for some reason $new_password_hash = hash('sha256', $oldpassword); sql_query("update user set password='******' where username='******'"); } $ip = get_ip(); # This may change the $username, $password, and $password_hash $externalresult = hook("externalauth", "", array($username, $password)); #Attempt external auth if configured # Generate a new session hash. $session_hash = generate_session_hash($password_hash); # Check the provided credentials $valid = sql_query("select ref,usergroup,account_expires from user where username='******' and password='******'"); # Prepare result array $result = array(); $result['valid'] = false; if (count($valid) >= 1) { # Account expiry $userref = $valid[0]["ref"]; $usergroup = $valid[0]["usergroup"]; $expires = $valid[0]["account_expires"]; if ($expires != "" && $expires != "0000-00-00 00:00:00" && strtotime($expires) <= time()) { $result['error'] = $lang["accountexpired"]; return $result; } $result['valid'] = true; $result['session_hash'] = $session_hash; $result['password_hash'] = $password_hash; $result['ref'] = $userref; # Update the user record. # Omit updating session has if using an API, because we don't want API usage to log users out, and there is no 'session' to remember in such a case. if ($api) { $session_hash_sql = ""; } else { $session_hash_sql = "session='" . escape_check($session_hash) . "',"; } sql_query("update user set {$session_hash_sql} last_active=now(),login_tries=0,lang='" . getvalescaped("language", "") . "' where ref='{$userref}'"); # Log this daily_stat("User session", $userref); if (!$api) { sql_query("insert into resource_log(date,user,resource,type) values (now()," . ($userref != "" ? "'{$userref}'" : "null") . ",0,'l')"); } # Blank the IP address lockout counter for this IP sql_query("delete from ip_lockout where ip='" . escape_check($ip) . "'"); return $result; } # Invalid login if (isset($externalresult["error"])) { $result['error'] = $externalresult["error"]; } else { $result['error'] = $lang["loginincorrect"]; } hook("loginincorrect"); # Add / increment a lockout value for this IP $lockouts = sql_value("select count(*) value from ip_lockout where ip='" . escape_check($ip) . "' and tries<'" . $max_login_attempts_per_ip . "'", ""); if ($lockouts > 0) { # Existing row with room to move $tries = sql_value("select tries value from ip_lockout where ip='" . escape_check($ip) . "'", 0); $tries++; if ($tries == $max_login_attempts_per_ip) { # Show locked out message. $result['error'] = str_replace("?", $max_login_attempts_wait_minutes, $lang["max_login_attempts_exceeded"]); } # Increment sql_query("update ip_lockout set last_try=now(),tries=tries+1 where ip='" . escape_check($ip) . "'"); } else { # New row sql_query("delete from ip_lockout where ip='" . escape_check($ip) . "'"); sql_query("insert into ip_lockout (ip,tries,last_try) values ('" . escape_check($ip) . "',1,now())"); } # Increment a lockout value for any matching username. $ulocks = sql_query("select ref,login_tries,login_last_try from user where username='******'"); if (count($ulocks) > 0) { $tries = $ulocks[0]["login_tries"]; if ($tries == "") { $tries = 1; } else { $tries++; } if ($tries > $max_login_attempts_per_username) { $tries = 1; } if ($tries == $max_login_attempts_per_username) { # Show locked out message. $result['error'] = str_replace("?", $max_login_attempts_wait_minutes, $lang["max_login_attempts_exceeded"]); } sql_query("update user set login_tries='{$tries}',login_last_try=now() where username='******'"); } return $result; }
/** * Performs the login using the global $username, and $password. Since the "externalauth" hook * is allowed to change the credentials later on, the $password_hash needs to be global as well. * * @return array Containing the login details ('valid' determines whether or not the login succeeded). */ function perform_login() { global $api, $scramble_key, $enable_remote_apis, $lang, $max_login_attempts_wait_minutes, $max_login_attempts_per_ip, $max_login_attempts_per_username, $global_cookies, $username, $password, $password_hash; if (!$api && strlen($password)==32 && getval("userkey","")!=md5($username . $scramble_key)) { exit("Invalid password."); # Prevent MD5s being entered directly while still supporting direct entry of plain text passwords (for systems that were set up prior to MD5 password encryption was added). If a special key is sent, which is the md5 hash of the username and the secret scramble key, then allow a login using the MD5 password hash as the password. This is for the 'log in as this user' feature. } if (strlen($password)!=32) { # Provided password is not a hash, so generate a hash. $password_hash=md5("RS" . $username . $password); } else { $password_hash=$password; } $ip=get_ip(); # This may change the $username, $password, and $password_hash hook("externalauth","",array($username, $password)); #Attempt external auth if configured # Generate a new session hash. $session_hash=generate_session_hash($password_hash); $valid=sql_query("select ref,usergroup from user where username='******' and (password='******' or password='******')"); # Prepare result array $result=array(); $result['valid']=false; if (count($valid)>=1) { # Account expiry $expires=sql_value("select account_expires value from user where username='******' and password='******'",""); if ($expires!="" && $expires!="0000-00-00 00:00:00" && strtotime($expires)<=time()) { $result['error']=$lang["accountexpired"]; return $result; } $result['valid']=true; $result['session_hash']=$session_hash; $result['password_hash']=$password_hash; # Update the user record. Set the password hash again in case a plain text password was provided. # Omit updating session has if using an API, because we don't want API usage to log users out, and there is no 'session' to remember in such a case. if ($api){$session_hash_sql="";} else {$session_hash_sql=",session='".escape_check($session_hash)."'";} sql_query("update user set password='******' $session_hash_sql ,last_active=now(),login_tries=0,lang='".getvalescaped("language","")."' where username='******' and (password='******' or password='******')"); # Log this $userref=$valid[0]["ref"]; $usergroup=$valid[0]["usergroup"]; daily_stat("User session",$userref); sql_query("insert into resource_log(date,user,resource,type) values (now()," . (($userref!="")?"'$userref'":"null") . ",0,'l')"); # Blank the IP address lockout counter for this IP sql_query("delete from ip_lockout where ip='" . escape_check($ip) . "'"); return $result; } # Invalid login $result['error']=$lang["loginincorrect"]; hook("loginincorrect"); # Add / increment a lockout value for this IP $lockouts=sql_value("select count(*) value from ip_lockout where ip='" . escape_check($ip) . "' and tries<'" . $max_login_attempts_per_ip . "'",""); if ($lockouts>0) { # Existing row with room to move $tries=sql_value("select tries value from ip_lockout where ip='" . escape_check($ip) . "'",0); $tries++; if ($tries==$max_login_attempts_per_ip) { # Show locked out message. $result['error']=str_replace("?",$max_login_attempts_wait_minutes,$lang["max_login_attempts_exceeded"]); } # Increment sql_query("update ip_lockout set last_try=now(),tries=tries+1 where ip='" . escape_check($ip) . "'"); } else { # New row sql_query("delete from ip_lockout where ip='" . escape_check($ip) . "'"); sql_query("insert into ip_lockout (ip,tries,last_try) values ('" . escape_check($ip) . "',1,now())"); } # Increment a lockout value for any matching username. $ulocks=sql_query("select ref,login_tries,login_last_try from user where username='******'"); if (count($ulocks)>0) { $tries=$ulocks[0]["login_tries"]; if ($tries=="") {$tries=1;} else {$tries++;} if ($tries>$max_login_attempts_per_username) {$tries=1;} if ($tries==$max_login_attempts_per_username) { # Show locked out message. $result['error']=str_replace("?",$max_login_attempts_wait_minutes,$lang["max_login_attempts_exceeded"]); } sql_query("update user set login_tries='$tries',login_last_try=now() where username='******'"); } return $result; }