Exemplo n.º 1
0
 public function updateUserName($internalID, $externalID = 0, $name = 'LoginSystem:userOperations:updateUserNameFailed')
 {
     // update usernames based on data frome external logins
     // usernames based on internal login can not be updated
     if ($externalID === 0) {
         return;
     }
     // copy name so we have original name if an error occurs
     $newName = $name;
     // currently only bzbb module allowed as external login
     // so hardwire it until we have a database field for
     // external login module used
     // check if bzbb module is loaded
     if (array_search('bzbb', loginSystem::getModules()) === true) {
         $result = bzbb::updateUserName($externalID, $newName);
     }
     if ($newName !== false && strlen($newName) > 0) {
         $query = $this->prepare('UPDATE `users` SET `name`=:name WHERE `id`=:id LIMIT 1');
         $this->execute($query, array(':name' => array($newName, PDO::PARAM_STR, 50), ':id' => array($internalID, PDO::PARAM_INT)));
         $this->free($query);
     }
     // report error if external login module (e.g. bzbb) failed doing its job
     if (isset($result) && $result === false) {
         $this->logError('login/classes/userOperations.php: updateUserName:'******' bzbb::updateUserName(' . strval($externalID) . ', ' . strval($name) . ')' . ' returned false (unknown error).');
     }
 }
Exemplo n.º 2
0
 public function validateLogin(&$output)
 {
     global $config;
     global $user;
     global $db;
     // initialise permissions
     $user->removeAllPermissions();
     // too many login attempts
     if (!$this->loginAllowed) {
         // print out the same error message an incorrect password would trigger
         $output .= 'Your password does not match the stored password.' . ' You may want to <a href="./">try logging in again</a>.' . "\n";
         return false;
     }
     // set loginname based on POST parameter
     // no loginname -> login failed
     if (!isset($_POST['loginname'])) {
         $output = 'Error: You must specify a name. ' . 'You may <a href="./?module=local&amp;action=form">try logging in again</a>.';
         $this->logFailedLoginAttempt('', 'fieldMissing');
         return false;
     }
     // escape username before storing it in db
     // that way encoding when displaying (on other pages)
     // using (X)HTML is not needed
     $loginname = htmlent($_POST['loginname']);
     $lenLogin = strlen($loginname);
     // now take care of empty loginname
     // also present error message to user if too long or too short loginname
     if ($lenLogin > 50 || $lenLogin < 1) {
         $output .= 'User names must be using less than 50 ' . 'but more than 0 <abbr title="characters">chars</abbr>.' . "\n";
         $this->logFailedLoginAttempt(substr($_POST['loginname'], 0, 50), $lenLogin > 50 ? 'tooLongUserName' : 'emptyPassword');
         return false;
     }
     // set password based on POST parameter
     // no password -> login failed
     if (!isset($_POST['pw'])) {
         $this->logFailedLoginAttempt($loginname, 'fieldMissing');
         return false;
     }
     // check length of password
     $pw_supplied = $_POST['pw'];
     $lenPw = strlen($pw_supplied);
     if ($lenPw < 9 || $lenPw > 32) {
         $output .= 'Passwords must be using less than 32 but more than 9 ' . '<abbr title="characters">chars</abbr>.' . ' You may want to <a href="./">try logging in again</a>.</p>' . "\n";
         $this->logFailedLoginAttempt($loginname, $lenPw > 32 ? 'tooLongPassword' : 'emptyPassword');
         return false;
     }
     // initialise match variables for password and user
     $correctUser = false;
     $correctPw = false;
     // get user id
     $query = 'SELECT `id`';
     if ($config->getValue('login.modules.forceExternalLoginOnly')) {
         $query .= ', `external_id` ';
     }
     // only one user tries to login so only fetch one entry, speeds up login a lot
     $query .= ' FROM `users` WHERE `name`=? LIMIT 1';
     $query = $db->prepare($query);
     $db->execute($query, $loginname);
     // initialise with reserved user id 0 (no user)
     $userid = (int) 0;
     $convert_to_external_login = true;
     while ($row = $db->fetchRow($query)) {
         $userid = $row['id'];
         // external_id might contain NULL values
         // so it is more practical to use isset
         // instead of checking $convert_to_external_login
         // and then investigate a possible NULL value
         if (!isset($row['external_id']) || !(strcmp($row['external_id'], '') === 0)) {
             $convert_to_external_login = false;
         }
     }
     // local login tried but external login forced in settings
     if (!$convert_to_external_login && $config->getValue('login.modules.forceExternalLoginOnly')) {
         $msg = '<span class="unread_messages">You already enabled ';
         $modules = loginSystem::getModules();
         if (array_search('bzbb', $modules) !== false) {
             $url = urlencode($config->getValue('baseaddress') . 'Login/?module=bzbb&action=login&auth=%TOKEN%,%USERNAME%');
             $msg .= '<a href="' . htmlspecialchars('http://my.bzflag.org/weblogin.php?action=weblogin&url=') . $url;
             $msg .= '">global (my.bzflag.org/bb/) login</a>';
         } else {
             $msg .= 'external logins';
         }
         $msg .= ' for this account.</span>' . "\n";
         $output .= $msg;
         unset($modules);
         return false;
     } elseif ($convert_to_external_login) {
         // convert user account to external login implicitly
         // find out which login modules are installed
         $modules = loginSystem::getModules();
         // convert to use bzbb external login
         if (array_search('bzbb', $modules) !== false) {
             // try to convert the account using the module's inbuilt function
             $moduleConvertMsg = '';
             if (!bzbb::convertAccount($userid, $loginname, $moduleConvertMsg)) {
                 if (strlen($moduleConvertMsg) > 0) {
                     $output .= 'Module bzbb has returned the following error on convertAccount: ' . $moduleConvertMsg;
                 }
                 return false;
             }
             if (strlen($moduleConvertMsg) > 0) {
                 // Module bzbb has returned the following success message on convertAccount
                 $output .= $moduleConvertMsg . ' ';
             }
             unset($moduleConvertMsg);
             // stop here if local login not allowed
             if ($config->getValue('login.modules.forceExternalLoginOnly')) {
                 $output .= 'Local login is disabled by the site admin, though. ' . 'You must use the bzbb login instead.';
                 return false;
             }
         }
     }
     if (intval($userid) === 0) {
         $user->logout();
         $output .= 'The specified user is not registered. ' . 'You may want to <a href="./">try logging in again</a>.';
         $this->logFailedLoginAttempt($loginname);
         return false;
     }
     // get password from database in order to compare it with the user entered password
     // only one user tries to login so only fetch one entry, speeds up login a lot
     $query = 'SELECT `password`, `cipher` FROM `users_passwords` WHERE `userid`=? LIMIT 1';
     // execute query
     $query = $db->prepare($query);
     if (!$db->execute($query, $userid)) {
         // query failed
         $output .= 'Could not retrieve password for you in database.';
         $this->logFailedLoginAttempt($loginname, 'missconfiguration');
         return false;
     }
     // find out what database tells about password algorithm
     while ($row = $db->fetchRow($query)) {
         if (!isset($row['cipher'])) {
             $output .= 'FATAL ERROR: Cipher not set.' . ' You may want to <a href="./">try logging in again</a>.' . "\n";
             $db->logError('FATAL ERROR: Local login module: Cipher not set.');
             $this->logFailedLoginAttempt($loginname, 'missconfiguration');
             return false;
         }
         // the algorithm
         $cipher = $row['cipher'];
         // the password in db
         $pw_db = $row['password'];
     }
     // little article about password security
     // http://www.php.net/manual/en/faq.passwords.php
     // cryptographic salt to use
     // see http://www.php.net/manual/en/function.crypt.php
     // TODO: allow overriding user configurable salt part using config
     $salt = '';
     switch ($cipher) {
         case 'md5':
             // is md5 cipher supported on this system?
             if (CRYPT_MD5 != 1) {
                 $salt = false;
                 break;
             }
             // 12 char salt, beginning with $1$
             $salt = '$1$';
             $userSalt = 'thisi$sp$';
             break;
         case 'blowfish':
             // is blowfish cipher supported on this system?
             if (CRYPT_BLOWFISH != 1) {
                 $salt = false;
                 break;
             }
             // 33 char salt,
             // beginning with $2a$, followed by cost between 04 and 31, a % and 22 chars in ./0-9A-Za-z
             $salt = '$2a$';
             $userSalt = '09$th1s1sSp4rt4.O.RlySure4b0$';
             //$userSalt='07$usesomesillystringforsalt$';
             break;
         default:
             // an undefined case, very bad
             // but not caused by end user so do not subtract free login attempts
             $output .= 'FATAL ERROR: Action for stored cipher in db not set.' . ' This case means you need admin support to resolve the technical issue.' . ' You may want to <a href="./">try logging in again</a>.' . "\n";
             $db->logError('FATAL ERROR: Local login module: No action for cipher (' . $cipher . ') set.');
             $this->logFailedLoginAttempt($loginname, 'missconfiguration');
             return false;
     }
     // mention the error to user but don't tell about cipher information
     // as the latter would likely only help exploiting the password check
     // again not caused by end user
     if ($salt === false) {
         $output .= 'FATAL ERROR: Cipher set and valid but library error on server detected.' . ' This case means you need admin support to resolve the technical issue.' . ' You may want to <a href="./">try logging in again</a>.' . "\n";
         $db->logError('FATAL ERROR: Local login module: Cipher (' . $cipher . ') set and valid' . ' but no proper encoding lib available.');
         $this->logFailedLoginAttempt($loginname, 'missconfiguration');
         return false;
     }
     // build the final salt
     $salt .= $userSalt;
     // compute the password from user input
     $pw_gen = crypt($pw_supplied, $salt);
     // do the actual comparison
     if (!(strcmp($pw_db, $pw_gen) === 0) && strlen($pw_db) > 0) {
         // TODO: automatically log these cases and lock account for some hours after several unsuccessful tries
         $output .= 'Your password does not match the stored password.' . ' You may want to <a href="./">try logging in again</a>.' . "\n";
         $this->logFailedLoginAttempt($loginname, 'passwordMismatch');
         return false;
     }
     // put information into class variable for usage outside of this function
     $this->info['id'] = $userid;
     $this->info['username'] = $loginname;
     // sanity checks passed -> login successful
     return true;
     // username and password did match but there might be circumstances
     // where the caller script decides the login was not successful, though
 }