elkarte /
Elkarte
| 1 | <?php |
||||||
| 2 | |||||||
| 3 | /** |
||||||
| 4 | * This file is concerned pretty entirely, as you see from its name, with |
||||||
| 5 | * logging in and out members, and the validation of that. |
||||||
| 6 | * |
||||||
| 7 | * @package ElkArte Forum |
||||||
| 8 | * @copyright ElkArte Forum contributors |
||||||
| 9 | * @license BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file) |
||||||
| 10 | * |
||||||
| 11 | * This file contains code covered by: |
||||||
| 12 | * copyright: 2011 Simple Machines (http://www.simplemachines.org) |
||||||
| 13 | * |
||||||
| 14 | * @version 2.0 dev |
||||||
| 15 | * |
||||||
| 16 | */ |
||||||
| 17 | |||||||
| 18 | namespace ElkArte\Controller; |
||||||
| 19 | |||||||
| 20 | use ElkArte\AbstractController; |
||||||
| 21 | use ElkArte\Cache\Cache; |
||||||
| 22 | use ElkArte\Errors\Errors; |
||||||
|
0 ignored issues
–
show
|
|||||||
| 23 | use ElkArte\Exceptions\Exception; |
||||||
| 24 | use ElkArte\Helper\Util; |
||||||
| 25 | use ElkArte\Http\Headers; |
||||||
| 26 | use ElkArte\Languages\Txt; |
||||||
| 27 | use ElkArte\Request; |
||||||
| 28 | use ElkArte\User; |
||||||
| 29 | use ElkArte\UserSettingsLoader; |
||||||
| 30 | |||||||
| 31 | /** |
||||||
| 32 | * Deals with logging in and out members, and the validation of them |
||||||
| 33 | * |
||||||
| 34 | * @package Authorization |
||||||
| 35 | */ |
||||||
| 36 | class Auth extends AbstractController |
||||||
| 37 | { |
||||||
| 38 | /** |
||||||
| 39 | * {@inheritDoc} |
||||||
| 40 | */ |
||||||
| 41 | public function needSecurity($action = '') |
||||||
| 42 | { |
||||||
| 43 | return $action !== 'action_keepalive'; |
||||||
| 44 | } |
||||||
| 45 | |||||||
| 46 | /** |
||||||
| 47 | * Entry point in Auth controller |
||||||
| 48 | * |
||||||
| 49 | * - (well no, not really. We route directly to the rest.) |
||||||
| 50 | 2 | * |
|||||
| 51 | * @see AbstractController::action_index |
||||||
| 52 | */ |
||||||
| 53 | 2 | public function action_index() |
|||||
| 54 | 2 | { |
|||||
| 55 | // What can we do? login page! |
||||||
| 56 | $this->action_login(); |
||||||
| 57 | } |
||||||
| 58 | |||||||
| 59 | /** |
||||||
| 60 | * Ask them for their login information. |
||||||
| 61 | * |
||||||
| 62 | * What it does: |
||||||
| 63 | * - Shows a page for the user to type in their username and password. |
||||||
| 64 | * - It caches the referring URL in $_SESSION['login_url']. |
||||||
| 65 | * - It is accessed from ?action=login. |
||||||
| 66 | 2 | * |
|||||
| 67 | * @uses Login template and language file with the login sub-template. |
||||||
| 68 | 2 | */ |
|||||
| 69 | public function action_login(): void |
||||||
| 70 | { |
||||||
| 71 | 2 | global $txt, $context; |
|||||
| 72 | |||||||
| 73 | // You are already logged in, go take a tour of the boards |
||||||
| 74 | if (!empty($this->user->id)) |
||||||
|
0 ignored issues
–
show
The property
id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
|
|||||||
| 75 | { |
||||||
| 76 | redirectexit(); |
||||||
| 77 | 2 | } |
|||||
| 78 | 2 | ||||||
| 79 | 2 | // Load the Login template/language file. |
|||||
| 80 | 2 | Txt::load('Login'); |
|||||
| 81 | |||||||
| 82 | // If API, clear out the header/footer and only return the form |
||||||
| 83 | 2 | if ($this->getApi()) |
|||||
| 84 | 2 | { |
|||||
| 85 | 2 | $template_layers = theme()->getLayers(); |
|||||
| 86 | 2 | $template_layers->removeAll(); |
|||||
| 87 | 2 | } |
|||||
| 88 | 2 | ||||||
| 89 | theme()->getTemplates()->load('Login'); |
||||||
| 90 | $context['sub_template'] = 'login'; |
||||||
| 91 | 2 | ||||||
| 92 | 2 | // Get the template ready.... not really much else to do. |
|||||
| 93 | 2 | $context['page_title'] = $txt['login']; |
|||||
| 94 | $_REQUEST['u'] = isset($_REQUEST['u']) ? Util::htmlspecialchars($_REQUEST['u']) : ''; |
||||||
| 95 | $context['default_username'] = &$_REQUEST['u']; |
||||||
| 96 | $context['default_password'] = ''; |
||||||
| 97 | 2 | $context['never_expire'] = false; |
|||||
| 98 | |||||||
| 99 | // Add the login chain to the link tree. |
||||||
| 100 | $context['breadcrumbs'][] = [ |
||||||
| 101 | 'url' => getUrl('action', ['action' => 'login']), |
||||||
| 102 | 'name' => $txt['login'], |
||||||
| 103 | 2 | ]; |
|||||
| 104 | |||||||
| 105 | // Set the login URL - will be used when the login process is done (but careful not to send us to an attachment). |
||||||
| 106 | if (isset($_SESSION['old_url']) && validLoginUrl($_SESSION['old_url'], true)) |
||||||
| 107 | 2 | { |
|||||
| 108 | 2 | $_SESSION['login_url'] = $_SESSION['old_url']; |
|||||
| 109 | } |
||||||
| 110 | else |
||||||
| 111 | { |
||||||
| 112 | unset($_SESSION['login_url']); |
||||||
| 113 | } |
||||||
| 114 | |||||||
| 115 | // Create a one time token. |
||||||
| 116 | createToken('login'); |
||||||
| 117 | } |
||||||
| 118 | |||||||
| 119 | /** |
||||||
| 120 | * Actually logs you in. |
||||||
| 121 | * |
||||||
| 122 | * What it does: |
||||||
| 123 | * |
||||||
| 124 | 2 | * - Checks credentials and checks that login was successful. |
|||||
| 125 | * - It employs protection against a specific IP or user trying to brute force |
||||||
| 126 | 2 | * a login to an account. |
|||||
| 127 | * - Upgrades password encryption on login, if necessary. |
||||||
| 128 | * - After successful login, redirects you to $_SESSION['login_url']. |
||||||
| 129 | 2 | * - Accessed from ?action=login2, by forms. |
|||||
| 130 | * |
||||||
| 131 | * @uses the same templates action_login() |
||||||
| 132 | 2 | */ |
|||||
| 133 | public function action_login2(): bool |
||||||
| 134 | { |
||||||
| 135 | global $txt, $modSettings, $context; |
||||||
| 136 | |||||||
| 137 | // Load cookie authentication and all stuff. |
||||||
| 138 | 2 | require_once(SUBSDIR . '/Auth.subs.php'); |
|||||
| 139 | 2 | ||||||
| 140 | 2 | // Beyond this point you are assumed to be a guest trying to login. |
|||||
| 141 | if (empty(User::$info->is_guest)) |
||||||
|
0 ignored issues
–
show
The property
is_guest does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
|
|||||||
| 142 | { |
||||||
| 143 | 2 | redirectexit(); |
|||||
| 144 | } |
||||||
| 145 | |||||||
| 146 | // Are you guessing with a script? |
||||||
| 147 | checkSession(); |
||||||
| 148 | validateToken('login'); |
||||||
| 149 | 2 | spamProtection('login'); |
|||||
| 150 | |||||||
| 151 | // Set the login_url if it's not already set (but careful not to send us to an attachment). |
||||||
| 152 | if (empty($_SESSION['login_url']) && isset($_SESSION['old_url']) && validLoginUrl($_SESSION['old_url'], true)) |
||||||
| 153 | { |
||||||
| 154 | $_SESSION['login_url'] = $_SESSION['old_url']; |
||||||
| 155 | 2 | } |
|||||
| 156 | |||||||
| 157 | // Been guessing a lot, haven't we? |
||||||
| 158 | if (isset($_SESSION['failed_login']) && $_SESSION['failed_login'] >= $modSettings['failed_login_threshold'] * 3) |
||||||
| 159 | 2 | { |
|||||
| 160 | throw new Exception('login_threshold_fail', 'critical'); |
||||||
| 161 | } |
||||||
| 162 | |||||||
| 163 | // Set up the cookie length. (if it's invalid, just fall through and use the default.) |
||||||
| 164 | 2 | if (isset($_POST['cookieneverexp'])) |
|||||
| 165 | { |
||||||
| 166 | $modSettings['cookieTime'] = 3153600; |
||||||
| 167 | 2 | } |
|||||
| 168 | 2 | ||||||
| 169 | 2 | Txt::load('Login'); |
|||||
| 170 | |||||||
| 171 | // Load the template stuff |
||||||
| 172 | 2 | theme()->getTemplates()->load('Login'); |
|||||
| 173 | 2 | $context['sub_template'] = 'login'; |
|||||
| 174 | 2 | ||||||
| 175 | 2 | // Set up the default/fallback stuff. |
|||||
| 176 | 2 | $context['default_username'] = isset($_POST['user']) ? preg_replace('~&#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', htmlspecialchars($_POST['user'], ENT_COMPAT, 'UTF-8')) : ''; |
|||||
| 177 | $context['default_password'] = ''; |
||||||
| 178 | $context['never_expire'] = $modSettings['cookieTime'] === 525600 || $modSettings['cookieTime'] === 3153600; |
||||||
| 179 | 2 | $context['login_errors'] = [$txt['error_occurred']]; |
|||||
| 180 | 2 | $context['page_title'] = $txt['login']; |
|||||
| 181 | 2 | ||||||
| 182 | // Add the login chain to the link tree. |
||||||
| 183 | $context['breadcrumbs'][] = [ |
||||||
| 184 | 'url' => getUrl('action', ['action' => 'login']), |
||||||
| 185 | 2 | 'name' => $txt['login'], |
|||||
| 186 | ]; |
||||||
| 187 | |||||||
| 188 | // You forgot to type your username, dummy! |
||||||
| 189 | if (!isset($_POST['user']) || $_POST['user'] === '') |
||||||
| 190 | { |
||||||
| 191 | $context['login_errors'] = [$txt['need_username']]; |
||||||
| 192 | |||||||
| 193 | return false; |
||||||
| 194 | } |
||||||
| 195 | |||||||
| 196 | // No one needs a username that long, plus we only support 80 chars in the db |
||||||
| 197 | if (Util::strlen($_POST['user']) > 80) |
||||||
| 198 | { |
||||||
| 199 | $_POST['user'] = Util::substr($_POST['user'], 0, 80); |
||||||
| 200 | } |
||||||
| 201 | |||||||
| 202 | 2 | // Can't use a password > 64 characters sorry, to long and only good for a DoS attack |
|||||
| 203 | if (isset($_POST['passwrd']) && strlen($_POST['passwrd']) > 64) |
||||||
| 204 | { |
||||||
| 205 | $context['login_errors'] = [$txt['improper_password']]; |
||||||
| 206 | |||||||
| 207 | return false; |
||||||
| 208 | } |
||||||
| 209 | |||||||
| 210 | 2 | // Hmm... maybe 'admin' will login with no password. Uhh... NO! |
|||||
| 211 | if (!isset($_POST['passwrd']) || $_POST['passwrd'] === '') |
||||||
| 212 | { |
||||||
| 213 | $context['login_errors'] = [$txt['no_password']]; |
||||||
| 214 | |||||||
| 215 | return false; |
||||||
| 216 | } |
||||||
| 217 | 2 | ||||||
| 218 | // No funky symbols either. |
||||||
| 219 | if (preg_match('~[<>&"\'=\\\]~', preg_replace('~(&#(\\d{1,7}|x[0-9a-fA-F]{1,6});)~', '', $_POST['user'])) != 0) |
||||||
| 220 | { |
||||||
| 221 | $context['login_errors'] = [$txt['error_invalid_characters_username']]; |
||||||
| 222 | |||||||
| 223 | return false; |
||||||
| 224 | } |
||||||
| 225 | 2 | ||||||
| 226 | // Are we using any sort of integration to validate the login? |
||||||
| 227 | if (in_array('retry', call_integration_hook('integrate_validate_login', [$_POST['user'], null, $modSettings['cookieTime']]), true)) |
||||||
| 228 | { |
||||||
| 229 | $context['login_errors'] = [$txt['login_hash_error']]; |
||||||
| 230 | |||||||
| 231 | return false; |
||||||
| 232 | } |
||||||
| 233 | 2 | ||||||
| 234 | // Find them... if we can |
||||||
| 235 | $member_found = loadExistingMember($_POST['user']); |
||||||
| 236 | $db = database(); |
||||||
| 237 | $cache = Cache::instance(); |
||||||
| 238 | $req = Request::instance(); |
||||||
| 239 | |||||||
| 240 | $user = new UserSettingsLoader($db, $cache, $req); |
||||||
| 241 | 2 | $user->loadUserById($member_found === false ? 0 : $member_found['id_member'], true, ''); |
|||||
| 242 | |||||||
| 243 | $user_setting = $user->getSettings(); |
||||||
| 244 | |||||||
| 245 | // User using 2FA for login? Let's validate the token... |
||||||
| 246 | if (!empty($modSettings['enableOTP']) && !empty($user_setting['enable_otp']) && empty($_POST['otp_token'])) |
||||||
| 247 | { |
||||||
| 248 | $context['login_errors'] = [$txt['otp_required']]; |
||||||
| 249 | |||||||
| 250 | 2 | return false; |
|||||
| 251 | 2 | } |
|||||
| 252 | 2 | ||||||
| 253 | 2 | if (!empty($_POST['otp_token'])) |
|||||
| 254 | { |
||||||
| 255 | 2 | require_once(EXTDIR . '/GoogleAuthenticator.php'); |
|||||
| 256 | 2 | $ga = new \GoogleAuthenticator(); |
|||||
|
0 ignored issues
–
show
The type
GoogleAuthenticator was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths Loading history...
|
|||||||
| 257 | 2 | ||||||
| 258 | $ga->getCode($user_setting['otp_secret']); |
||||||
| 259 | $checkResult = $ga->verifyCode($user_setting['otp_secret'], $_POST['otp_token'], 2); |
||||||
| 260 | 2 | if (!$checkResult) |
|||||
| 261 | { |
||||||
| 262 | $context['login_errors'] = [$txt['invalid_otptoken']]; |
||||||
| 263 | |||||||
| 264 | return false; |
||||||
| 265 | } |
||||||
| 266 | |||||||
| 267 | 2 | // OTP already used? Sorry, but this is a ONE TIME password.. |
|||||
| 268 | if ($user_setting['otp_used'] === $_POST['otp_token']) |
||||||
| 269 | { |
||||||
| 270 | $context['login_errors'] = [$txt['otp_used']]; |
||||||
| 271 | |||||||
| 272 | return false; |
||||||
| 273 | } |
||||||
| 274 | } |
||||||
| 275 | |||||||
| 276 | // Let them try again, it didn't match anything... |
||||||
| 277 | if (empty($member_found)) |
||||||
| 278 | { |
||||||
| 279 | $context['login_errors'] = [$txt['username_no_exist']]; |
||||||
| 280 | |||||||
| 281 | return false; |
||||||
| 282 | } |
||||||
| 283 | |||||||
| 284 | // validateLoginPassword will hash this and check its valid |
||||||
| 285 | $sha_passwd = $_POST['passwrd']; |
||||||
| 286 | $valid_password = $user->validatePassword($sha_passwd); |
||||||
| 287 | |||||||
| 288 | require_once(SUBSDIR . '/Members.subs.php'); |
||||||
| 289 | |||||||
| 290 | 2 | // Bad password! Thought you could fool the database?! |
|||||
| 291 | if (!$valid_password) |
||||||
| 292 | 2 | { |
|||||
| 293 | // Let's be cautious, no hacking please. thanx. |
||||||
| 294 | 2 | validatePasswordFlood($user_setting['id_member'], $user_setting['passwd_flood']); |
|||||
| 295 | |||||||
| 296 | // Maybe we were too hasty... let's try some other authentication methods. |
||||||
| 297 | $other_passwords = $this->_other_passwords($_POST['passwrd'], $user_setting['password_salt'], $user_setting['passwd'], $user_setting['member_name']); |
||||||
| 298 | |||||||
| 299 | // Whichever encryption it was using, let's make it use ElkArte's now ;). |
||||||
| 300 | if (in_array($user_setting['passwd'], $other_passwords, true)) |
||||||
| 301 | { |
||||||
| 302 | $user->rehashPassword($sha_passwd); |
||||||
| 303 | |||||||
| 304 | // Update the password hash and set up the salt. |
||||||
| 305 | updateMemberData($user_setting['id_member'], ['passwd' => $user_setting['passwd'], 'password_salt' => $user_setting['password_salt'], 'passwd_flood' => '']); |
||||||
| 306 | } |
||||||
| 307 | // Okay, they for sure didn't enter the password! |
||||||
| 308 | else |
||||||
| 309 | { |
||||||
| 310 | // They've messed up again - keep a count to see if they need a hand. |
||||||
| 311 | $_SESSION['failed_login'] = isset($_SESSION['failed_login']) ? ($_SESSION['failed_login'] + 1) : 1; |
||||||
| 312 | |||||||
| 313 | // Hmm... don't remember it, do you? Here, try the password reminder ;). |
||||||
| 314 | if ($_SESSION['failed_login'] >= $modSettings['failed_login_threshold']) |
||||||
| 315 | { |
||||||
| 316 | redirectexit('action=reminder'); |
||||||
| 317 | } |
||||||
| 318 | // We'll give you another chance... |
||||||
| 319 | else |
||||||
| 320 | { |
||||||
| 321 | // Log an error so we know that it didn't go well in the error log. |
||||||
| 322 | Errors::instance()->log_error($txt['incorrect_password'] . ' - <span class="remove">' . $user_setting['member_name'] . '</span>', 'user'); |
||||||
| 323 | |||||||
| 324 | $context['login_errors'] = [$txt['incorrect_password']]; |
||||||
| 325 | |||||||
| 326 | return false; |
||||||
| 327 | } |
||||||
| 328 | } |
||||||
| 329 | } |
||||||
| 330 | elseif (!empty($user_setting['passwd_flood'])) |
||||||
| 331 | { |
||||||
| 332 | // Let's be sure they weren't a little hacker. |
||||||
| 333 | validatePasswordFlood($user_setting['id_member'], $user_setting['passwd_flood'], true); |
||||||
| 334 | |||||||
| 335 | // If we got here then we can reset the flood counter. |
||||||
| 336 | updateMemberData($user_setting['id_member'], ['passwd_flood' => '']); |
||||||
| 337 | } |
||||||
| 338 | |||||||
| 339 | if ($user_setting->fixSalt() === true) |
||||||
| 340 | { |
||||||
| 341 | updateMemberData($user_setting['id_member'], ['password_salt' => $user_setting['password_salt']]); |
||||||
| 342 | } |
||||||
| 343 | |||||||
| 344 | // Let's track the last used one-time password. |
||||||
| 345 | if (!empty($_POST['otp_token'])) |
||||||
| 346 | { |
||||||
| 347 | updateMemberData($user_setting['id_member'], ['otp_used' => (int) $_POST['otp_token']]); |
||||||
| 348 | } |
||||||
| 349 | |||||||
| 350 | // Check their activation status. |
||||||
| 351 | if ($user->checkActivation(isset($_REQUEST['undelete']))) |
||||||
| 352 | { |
||||||
| 353 | doLogin($user); |
||||||
| 354 | } |
||||||
| 355 | |||||||
| 356 | return false; |
||||||
| 357 | } |
||||||
| 358 | |||||||
| 359 | /** |
||||||
| 360 | * Loads other possible password hash / crypts using the post data |
||||||
| 361 | * |
||||||
| 362 | * What it does: |
||||||
| 363 | * |
||||||
| 364 | * - Used when a board is converted to see if the user credentials and a 3rd |
||||||
| 365 | * party hash satisfy whats in the db passwd field |
||||||
| 366 | * |
||||||
| 367 | * @param string $member_name |
||||||
| 368 | * @param string $passwrd |
||||||
| 369 | * @param string $password_salt |
||||||
| 370 | * |
||||||
| 371 | * @return array |
||||||
| 372 | */ |
||||||
| 373 | private function _other_passwords($posted_password, $member_name, $passwrd, $password_salt): array |
||||||
| 374 | { |
||||||
| 375 | global $modSettings; |
||||||
| 376 | |||||||
| 377 | // What kind of data are we dealing with |
||||||
| 378 | $pw_strlen = strlen($passwrd); |
||||||
| 379 | |||||||
| 380 | // Start off with none, that's safe |
||||||
| 381 | $other_passwords = []; |
||||||
| 382 | |||||||
| 383 | if (empty($modSettings['enable_password_conversion'])) |
||||||
| 384 | { |
||||||
| 385 | return $other_passwords; |
||||||
| 386 | } |
||||||
| 387 | |||||||
| 388 | // None of the below cases will be used most of the time (because the salt is normally set.) |
||||||
| 389 | if ($password_salt === '') |
||||||
| 390 | { |
||||||
| 391 | // YaBB SE, Discus, MD5 (used a lot), SHA-1 (used some), SMF 1.0.x, IkonBoard, and none at all. |
||||||
| 392 | $other_passwords[] = crypt($posted_password, substr($posted_password, 0, 2)); |
||||||
| 393 | $other_passwords[] = crypt($posted_password, substr($passwrd, 0, 2)); |
||||||
| 394 | $other_passwords[] = md5($posted_password); |
||||||
| 395 | $other_passwords[] = sha1($posted_password); |
||||||
| 396 | $other_passwords[] = md5_hmac($posted_password, strtolower($member_name)); |
||||||
| 397 | $other_passwords[] = md5($posted_password . strtolower($member_name)); |
||||||
| 398 | $other_passwords[] = md5(md5($posted_password)); |
||||||
| 399 | $other_passwords[] = $posted_password; |
||||||
| 400 | |||||||
| 401 | // This one is a strange one... MyPHP, crypt() on the MD5 hash. |
||||||
| 402 | $other_passwords[] = crypt(md5($posted_password), md5($posted_password)); |
||||||
| 403 | |||||||
| 404 | // SHA-256 |
||||||
| 405 | if ($pw_strlen === 64) |
||||||
| 406 | { |
||||||
| 407 | // Snitz style |
||||||
| 408 | $other_passwords[] = bin2hex(hash('sha256', $posted_password, true)); |
||||||
| 409 | |||||||
| 410 | // Normal SHA-256 |
||||||
| 411 | $other_passwords[] = hash('sha256', $posted_password); |
||||||
| 412 | } |
||||||
| 413 | |||||||
| 414 | // phpBB3 users new hashing. We now support it as well ;). |
||||||
| 415 | $other_passwords[] = phpBB3_password_check($posted_password, $passwrd); |
||||||
| 416 | |||||||
| 417 | // APBoard 2 Login Method. |
||||||
| 418 | $other_passwords[] = md5(crypt($posted_password, 'CRYPT_MD5')); |
||||||
| 419 | |||||||
| 420 | // Xenforo 1.2+ |
||||||
| 421 | $other_passwords[] = crypt($posted_password, $passwrd); |
||||||
| 422 | } |
||||||
| 423 | // The hash should be 40 if it's SHA-1, so we're safe with more here too. |
||||||
| 424 | elseif ($pw_strlen === 32) |
||||||
| 425 | { |
||||||
| 426 | // vBulletin 3 style hashing? Let's welcome them with open arms \o/. |
||||||
| 427 | $other_passwords[] = md5(md5($posted_password) . stripslashes($password_salt)); |
||||||
| 428 | |||||||
| 429 | // Hmm.. p'raps it's Invision 2 style? |
||||||
| 430 | $other_passwords[] = md5(md5($password_salt) . md5($posted_password)); |
||||||
| 431 | |||||||
| 432 | // Some common md5 ones. |
||||||
| 433 | $other_passwords[] = md5($password_salt . $posted_password); |
||||||
| 434 | $other_passwords[] = md5($posted_password . $password_salt); |
||||||
| 435 | } |
||||||
| 436 | // The hash is 40 characters, lets try some SHA-1 style auth |
||||||
| 437 | elseif ($pw_strlen === 40) |
||||||
| 438 | { |
||||||
| 439 | // Maybe they are using a hash from before our password upgrade |
||||||
| 440 | $other_passwords[] = sha1(strtolower($member_name) . un_htmlspecialchars($posted_password)); |
||||||
| 441 | $other_passwords[] = sha1($passwrd . $_SESSION['session_value']); |
||||||
| 442 | |||||||
| 443 | // BurningBoard3 style of hashing. |
||||||
| 444 | $other_passwords[] = sha1($password_salt . sha1($password_salt . sha1($posted_password))); |
||||||
| 445 | |||||||
| 446 | // PunBB 1.4 and later |
||||||
| 447 | $other_passwords[] = sha1($password_salt . sha1($posted_password)); |
||||||
| 448 | |||||||
| 449 | // Perhaps we converted from a non UTF-8 db and have a valid password being hashed differently. |
||||||
| 450 | if (!empty($modSettings['previousCharacterSet']) && $modSettings['previousCharacterSet'] !== 'utf8') |
||||||
| 451 | { |
||||||
| 452 | // Try iconv first, for no particular reason. |
||||||
| 453 | if (function_exists('iconv')) |
||||||
| 454 | { |
||||||
| 455 | $other_passwords['iconv'] = sha1(strtolower(iconv('UTF-8', $modSettings['previousCharacterSet'], $member_name)) . un_htmlspecialchars(iconv('UTF-8', $modSettings['previousCharacterSet'], $posted_password))); |
||||||
| 456 | } |
||||||
| 457 | |||||||
| 458 | // Say it aint so, iconv failed! |
||||||
| 459 | if (empty($other_passwords['iconv']) && function_exists('mb_convert_encoding')) |
||||||
| 460 | { |
||||||
| 461 | $other_passwords[] = sha1(strtolower(mb_convert_encoding($member_name, 'UTF-8', $modSettings['previousCharacterSet'])) . un_htmlspecialchars(mb_convert_encoding($posted_password, 'UTF-8', $modSettings['previousCharacterSet']))); |
||||||
|
0 ignored issues
–
show
It seems like
mb_convert_encoding($pos...previousCharacterSet']) can also be of type array; however, parameter $string of un_htmlspecialchars() does only seem to accept string, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
It seems like
mb_convert_encoding($mem...previousCharacterSet']) can also be of type array; however, parameter $string of strtolower() does only seem to accept string, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
| 462 | } |
||||||
| 463 | } |
||||||
| 464 | } |
||||||
| 465 | // SHA-256 will be 64 characters long, lets check some of these possibilities |
||||||
| 466 | elseif ($pw_strlen === 64) |
||||||
| 467 | { |
||||||
| 468 | // PHP-Fusion7 |
||||||
| 469 | $other_passwords[] = hash_hmac('sha256', $posted_password, $password_salt); |
||||||
| 470 | |||||||
| 471 | // Plain SHA-256? |
||||||
| 472 | $other_passwords[] = hash('sha256', $posted_password . $password_salt); |
||||||
| 473 | |||||||
| 474 | // Xenforo? |
||||||
| 475 | $other_passwords[] = sha1(sha1($posted_password) . $password_salt); |
||||||
| 476 | $other_passwords[] = hash('sha256', (hash('sha256', ($posted_password) . $password_salt))); |
||||||
| 477 | } |
||||||
| 478 | |||||||
| 479 | // ElkArte's sha1 function can give a funny result on Linux (Not our fault!). If we've now got the real one let the old one be valid! |
||||||
| 480 | if (strpos(PHP_OS_FAMILY, 'Win') !== 0) |
||||||
| 481 | { |
||||||
| 482 | $other_passwords[] = bin2hex(hash('sha1', strtolower($member_name) . un_htmlspecialchars($posted_password), true)); |
||||||
| 483 | } |
||||||
| 484 | |||||||
| 485 | // Allows mods to easily extend the $other_passwords array |
||||||
| 486 | call_integration_hook('integrate_other_passwords', [&$other_passwords]); |
||||||
| 487 | |||||||
| 488 | return $other_passwords; |
||||||
| 489 | } |
||||||
| 490 | |||||||
| 491 | /** |
||||||
| 492 | * Logs the current user out of their account. |
||||||
| 493 | * |
||||||
| 494 | * What it does: |
||||||
| 495 | * |
||||||
| 496 | * - It requires that the session hash is sent as well, to prevent automatic logouts by images or javascript. |
||||||
| 497 | * - It redirects back to $_SESSION['logout_url'], if it exists. |
||||||
| 498 | * - It is accessed via ?action=logout;session_var=... |
||||||
| 499 | * |
||||||
| 500 | * @param bool $internal if true, it doesn't check the session |
||||||
| 501 | * @param bool $redirect if true, redirect to the board index |
||||||
| 502 | * @throws \ElkArte\Exceptions\Exception |
||||||
| 503 | */ |
||||||
| 504 | public function action_logout($internal = false, $redirect = true): void |
||||||
| 505 | { |
||||||
| 506 | // Make sure they aren't being auto-logged out. |
||||||
| 507 | if (!$internal) |
||||||
| 508 | { |
||||||
| 509 | checkSession('get'); |
||||||
| 510 | } |
||||||
| 511 | |||||||
| 512 | require_once(SUBSDIR . '/Auth.subs.php'); |
||||||
| 513 | |||||||
| 514 | if (isset($_SESSION['ftp_connection'])) |
||||||
| 515 | { |
||||||
| 516 | $_SESSION['ftp_connection'] = null; |
||||||
| 517 | } |
||||||
| 518 | |||||||
| 519 | // It won't be first login anymore. |
||||||
| 520 | unset($_SESSION['first_login']); |
||||||
| 521 | |||||||
| 522 | // Just ensure they aren't a guest! |
||||||
| 523 | if (empty(User::$info->is_guest)) |
||||||
|
0 ignored issues
–
show
The property
is_guest does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
|
|||||||
| 524 | { |
||||||
| 525 | // Pass the logout information to integrations. |
||||||
| 526 | call_integration_hook('integrate_logout', [User::$settings['member_name']]); |
||||||
| 527 | |||||||
| 528 | // If you log out, you aren't online anymore :P. |
||||||
| 529 | require_once(SUBSDIR . '/Logging.subs.php'); |
||||||
| 530 | logOnline(User::$info['id'], false); |
||||||
| 531 | } |
||||||
| 532 | |||||||
| 533 | // Logout? Let's kill the admin/moderate/other sessions, too. |
||||||
| 534 | $types = ['admin', 'moderate']; |
||||||
| 535 | call_integration_hook('integrate_validateSession', [&$types]); |
||||||
| 536 | foreach ($types as $type) |
||||||
| 537 | { |
||||||
| 538 | unset($_SESSION[$type . '_time']); |
||||||
| 539 | } |
||||||
| 540 | |||||||
| 541 | $_SESSION['log_time'] = 0; |
||||||
| 542 | |||||||
| 543 | // Empty the cookie! (set it in the past, and for id_member = 0) |
||||||
| 544 | setLoginCookie(-3600, 0); |
||||||
| 545 | |||||||
| 546 | // And some other housekeeping while we're at it. |
||||||
| 547 | session_destroy(); |
||||||
| 548 | if (!empty(User::$info['id'])) |
||||||
| 549 | { |
||||||
| 550 | User::$settings->fixSalt(true); |
||||||
|
0 ignored issues
–
show
The method
fixSalt() does not exist on ElkArte\Helper\ValuesContainerReadOnly. Since you implemented __call, consider adding a @method annotation.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
| 551 | require_once(SUBSDIR . '/Members.subs.php'); |
||||||
| 552 | updateMemberData(User::$info['id'], ['password_salt' => User::$settings['password_salt']]); |
||||||
| 553 | } |
||||||
| 554 | |||||||
| 555 | // Off to the merry board index we go! |
||||||
| 556 | if ($redirect) |
||||||
| 557 | { |
||||||
| 558 | if (empty($_SESSION['logout_url'])) |
||||||
| 559 | { |
||||||
| 560 | redirectexit(); |
||||||
| 561 | } |
||||||
| 562 | elseif ((strpos($_SESSION['logout_url'], 'http://') !== 0 && strpos($_SESSION['logout_url'], 'https://') !== 0)) |
||||||
| 563 | { |
||||||
| 564 | unset($_SESSION['logout_url']); |
||||||
| 565 | redirectexit(); |
||||||
| 566 | } |
||||||
| 567 | else |
||||||
| 568 | { |
||||||
| 569 | $temp = $_SESSION['logout_url']; |
||||||
| 570 | unset($_SESSION['logout_url']); |
||||||
| 571 | |||||||
| 572 | redirectexit($temp); |
||||||
| 573 | } |
||||||
| 574 | } |
||||||
| 575 | } |
||||||
| 576 | |||||||
| 577 | /** |
||||||
| 578 | * Throws guests out to the login screen when guest access is off. |
||||||
| 579 | * |
||||||
| 580 | * What it does: |
||||||
| 581 | * |
||||||
| 582 | * - It sets $_SESSION['login_url'] to $_SERVER['REQUEST_URL']. |
||||||
| 583 | * |
||||||
| 584 | * @uses 'kick_guest' sub template found in Login.template.php. |
||||||
| 585 | */ |
||||||
| 586 | public function action_kickguest(): void |
||||||
| 587 | { |
||||||
| 588 | global $txt, $context; |
||||||
| 589 | |||||||
| 590 | Txt::load('Login'); |
||||||
| 591 | theme()->getTemplates()->load('Login'); |
||||||
| 592 | createToken('login'); |
||||||
| 593 | |||||||
| 594 | // Never redirect to an attachment |
||||||
| 595 | if (validLoginUrl($_SERVER['REQUEST_URL'])) |
||||||
| 596 | { |
||||||
| 597 | $_SESSION['login_url'] = $_SERVER['REQUEST_URL']; |
||||||
| 598 | } |
||||||
| 599 | |||||||
| 600 | $context['sub_template'] = 'kick_guest'; |
||||||
| 601 | $context['page_title'] = $txt['login']; |
||||||
| 602 | $context['default_password'] = ''; |
||||||
| 603 | } |
||||||
| 604 | |||||||
| 605 | /** |
||||||
| 606 | * Display a message about the forum being in maintenance mode. |
||||||
| 607 | * |
||||||
| 608 | * What it does: |
||||||
| 609 | * |
||||||
| 610 | * - Displays a login screen with sub template 'maintenance'. |
||||||
| 611 | * - It sends a 503 header, so search engines don't index while we're in maintenance mode. |
||||||
| 612 | */ |
||||||
| 613 | public function action_maintenance_mode(): void |
||||||
| 614 | { |
||||||
| 615 | global $txt, $mtitle, $mmessage, $context; |
||||||
| 616 | |||||||
| 617 | Txt::load('Login'); |
||||||
| 618 | theme()->getTemplates()->load('Login'); |
||||||
| 619 | createToken('login'); |
||||||
| 620 | |||||||
| 621 | // Send a 503 header, so search engines don't bother indexing while we're in maintenance mode. |
||||||
| 622 | Headers::instance() |
||||||
| 623 | ->httpCode(503) |
||||||
| 624 | ->header('Status', '503 Service Temporarily Unavailable') |
||||||
| 625 | ->header('Retry-After', '3600'); |
||||||
| 626 | |||||||
| 627 | // Basic template stuff.. |
||||||
| 628 | $context['sub_template'] = 'maintenance'; |
||||||
| 629 | $context['title'] = &$mtitle; |
||||||
| 630 | $context['description'] = un_htmlspecialchars($mmessage); |
||||||
| 631 | $context['page_title'] = $txt['maintain_mode']; |
||||||
| 632 | } |
||||||
| 633 | |||||||
| 634 | /** |
||||||
| 635 | * Double check the cookie. |
||||||
| 636 | */ |
||||||
| 637 | public function action_check(): void |
||||||
| 638 | { |
||||||
| 639 | // Only our members, please. |
||||||
| 640 | if ($this->user->is_guest === false) |
||||||
|
0 ignored issues
–
show
The property
is_guest does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
|
|||||||
| 641 | { |
||||||
| 642 | // Strike! You're outta there! |
||||||
| 643 | if ($_GET['member'] != $this->user->id) |
||||||
|
0 ignored issues
–
show
The property
id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
|
|||||||
| 644 | { |
||||||
| 645 | throw new Exception('login_cookie_error', false); |
||||||
| 646 | } |
||||||
| 647 | |||||||
| 648 | // Some pass listing for login_url... |
||||||
| 649 | $temp = empty($_SESSION['login_url']) || validLoginUrl($_SESSION['login_url']) === false ? '' : $_SESSION['login_url']; |
||||||
| 650 | unset($_SESSION['login_url']); |
||||||
| 651 | redirectexit($temp); |
||||||
| 652 | } |
||||||
| 653 | |||||||
| 654 | // It'll never get here... until it does :P |
||||||
| 655 | redirectexit(); |
||||||
| 656 | } |
||||||
| 657 | 2 | ||||||
| 658 | /** |
||||||
| 659 | 2 | * Ping the server to keep the session alive and not let it disappear. |
|||||
| 660 | */ |
||||||
| 661 | 2 | public function action_keepalive(): void |
|||||
| 662 | 2 | { |
|||||
| 663 | 2 | dieGif(); |
|||||
| 664 | 2 | } |
|||||
| 665 | } |
||||||
| 666 | |||||||
| 667 | 2 | /** |
|||||
| 668 | * Check activation status of the current user. |
||||||
| 669 | 2 | * |
|||||
| 670 | * What it does: |
||||||
| 671 | * |
||||||
| 672 | 2 | * is_activated value key is as follows: |
|||||
| 673 | 2 | * - > 10 Banned with activation status as value - 10 |
|||||
| 674 | 2 | * - 5 = Awaiting COPPA consent |
|||||
| 675 | 2 | * - 4 = Awaiting Deletion approval |
|||||
| 676 | * - 3 = Awaiting Admin approval |
||||||
| 677 | * - 2 = Awaiting reactivation from email change |
||||||
| 678 | * - 1 = Approved and active |
||||||
| 679 | * - 0 = Not active |
||||||
| 680 | * |
||||||
| 681 | * @package Authorization |
||||||
| 682 | */ |
||||||
| 683 | function checkActivation() |
||||||
| 684 | { |
||||||
| 685 | global $context, $txt, $modSettings; |
||||||
| 686 | |||||||
| 687 | if (!isset($context['login_errors'])) |
||||||
| 688 | { |
||||||
| 689 | $context['login_errors'] = []; |
||||||
| 690 | } |
||||||
| 691 | |||||||
| 692 | // What is the true activation status of this account? |
||||||
| 693 | $activation_status = User::$settings->getActivationStatus(); |
||||||
|
0 ignored issues
–
show
The method
getActivationStatus() does not exist on ElkArte\Helper\ValuesContainerReadOnly. Since you implemented __call, consider adding a @method annotation.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
| 694 | |||||||
| 695 | // Check if the account is activated - COPPA first... |
||||||
| 696 | if ($activation_status === 5) |
||||||
| 697 | { |
||||||
| 698 | $context['login_errors'][] = $txt['coppa_no_concent'] . ' <a href="' . getUrl('action', ['action' => 'register', 'sa' => 'coppa', 'member' => User::$settings['id_member']]) . '">' . $txt['coppa_need_more_details'] . '</a>'; |
||||||
| 699 | |||||||
| 700 | return false; |
||||||
| 701 | } |
||||||
| 702 | |||||||
| 703 | // Awaiting approval still? |
||||||
| 704 | if ($activation_status === 3) |
||||||
| 705 | { |
||||||
| 706 | throw new Exception('still_awaiting_approval', 'user'); |
||||||
| 707 | } |
||||||
| 708 | |||||||
| 709 | if ($activation_status === 4) |
||||||
| 710 | { |
||||||
| 711 | if (isset($_REQUEST['undelete'])) |
||||||
| 712 | { |
||||||
| 713 | require_once(SUBSDIR . '/Members.subs.php'); |
||||||
| 714 | updateMemberData(User::$settings['id_member'], ['is_activated' => 1]); |
||||||
| 715 | updateSettings(['unapprovedMembers' => ($modSettings['unapprovedMembers'] > 0 ? $modSettings['unapprovedMembers'] - 1 : 0)]); |
||||||
| 716 | } |
||||||
| 717 | else |
||||||
| 718 | { |
||||||
| 719 | $context['login_errors'][] = $txt['awaiting_delete_account']; |
||||||
| 720 | $context['login_show_undelete'] = true; |
||||||
| 721 | |||||||
| 722 | return false; |
||||||
| 723 | } |
||||||
| 724 | } |
||||||
| 725 | // Awaiting deletion, changed their mind? |
||||||
| 726 | // Standard activation? |
||||||
| 727 | elseif ($activation_status !== 1) |
||||||
| 728 | { |
||||||
| 729 | Errors::instance()->log_error($txt['activate_not_completed1'] . ' - <span class="remove">' . User::$settings['member_name'] . '</span>', false); |
||||||
| 730 | |||||||
| 731 | $context['login_errors'][] = $txt['activate_not_completed1'] . ' <a class="linkbutton" href="' . getUrl('action', ['action' => 'register', 'sa' => 'activate', 'resend', 'u' => User::$settings['id_member']]) . '">' . $txt['activate_not_completed2'] . '</a>'; |
||||||
| 732 | |||||||
| 733 | return false; |
||||||
| 734 | } |
||||||
| 735 | |||||||
| 736 | return true; |
||||||
| 737 | } |
||||||
| 738 | |||||||
| 739 | /** |
||||||
| 740 | * This function performs the logging in. |
||||||
| 741 | * |
||||||
| 742 | * What it does: |
||||||
| 743 | * - It sets the cookie, it call hooks, updates runtime settings for the user. |
||||||
| 744 | * |
||||||
| 745 | * @param UserSettingsLoader $user |
||||||
| 746 | * |
||||||
| 747 | * @throws Exception |
||||||
| 748 | * @package Authorization |
||||||
| 749 | */ |
||||||
| 750 | function doLogin(UserSettingsLoader $user) |
||||||
| 751 | { |
||||||
| 752 | global $maintenance, $modSettings, $context; |
||||||
| 753 | |||||||
| 754 | // Load authentication stuffs. |
||||||
| 755 | require_once(SUBSDIR . '/Auth.subs.php'); |
||||||
| 756 | |||||||
| 757 | User::reloadByUser($user, true); |
||||||
| 758 | |||||||
| 759 | // Call login integration functions. |
||||||
| 760 | call_integration_hook('integrate_login', [User::$settings['member_name'], $modSettings['cookieTime']]); |
||||||
| 761 | |||||||
| 762 | // Bam! Cookie set. A session too, just in case. |
||||||
| 763 | setLoginCookie(60 * $modSettings['cookieTime'], User::$settings['id_member'], hash('sha256', (User::$settings['passwd'] . User::$settings['password_salt']))); |
||||||
| 764 | |||||||
| 765 | // Reset the login threshold. |
||||||
| 766 | if (isset($_SESSION['failed_login'])) |
||||||
| 767 | { |
||||||
| 768 | unset($_SESSION['failed_login']); |
||||||
| 769 | } |
||||||
| 770 | |||||||
| 771 | // Are you banned? |
||||||
| 772 | is_not_banned(true); |
||||||
| 773 | |||||||
| 774 | // Don't stick the language or theme after this point. |
||||||
| 775 | unset($_SESSION['language'], $_SESSION['theme']); |
||||||
| 776 | |||||||
| 777 | // We want to know if this is first login |
||||||
| 778 | if (User::$info->isFirstLogin()) |
||||||
|
0 ignored issues
–
show
The method
isFirstLogin() does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __call, consider adding a @method annotation.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
| 779 | { |
||||||
| 780 | $_SESSION['first_login'] = true; |
||||||
| 781 | } |
||||||
| 782 | else |
||||||
| 783 | { |
||||||
| 784 | unset($_SESSION['first_login']); |
||||||
| 785 | } |
||||||
| 786 | |||||||
| 787 | // You're one of us: need to know all about you now, IP, stuff. |
||||||
| 788 | $req = Request::instance(); |
||||||
| 789 | |||||||
| 790 | // You've logged in, haven't you? |
||||||
| 791 | require_once(SUBSDIR . '/Members.subs.php'); |
||||||
| 792 | updateMemberData(User::$info->id, ['last_login' => time(), 'member_ip' => User::$info->ip, 'member_ip2' => $req->ban_ip()]); |
||||||
|
0 ignored issues
–
show
The property
id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
The property
ip does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
|
|||||||
| 793 | |||||||
| 794 | // Get rid of the online entry for that old guest.... |
||||||
| 795 | require_once(SUBSDIR . '/Logging.subs.php'); |
||||||
| 796 | deleteOnline('ip' . User::$info->ip); |
||||||
| 797 | $_SESSION['log_time'] = 0; |
||||||
| 798 | |||||||
| 799 | // Log this entry, only if we have it enabled. |
||||||
| 800 | if (!empty($modSettings['loginHistoryDays'])) |
||||||
| 801 | { |
||||||
| 802 | logLoginHistory(User::$info->id, User::$info->ip, User::$info->ip2); |
||||||
|
0 ignored issues
–
show
The property
ip2 does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
|
|||||||
| 803 | } |
||||||
| 804 | |||||||
| 805 | // Just log you back out if it's in maintenance mode and you AREN'T an admin. |
||||||
| 806 | if (empty($maintenance) || allowedTo('admin_forum')) |
||||||
| 807 | { |
||||||
| 808 | redirectexit('action=auth;sa=check;member=' . User::$info->id); |
||||||
| 809 | } |
||||||
| 810 | else |
||||||
| 811 | { |
||||||
| 812 | redirectexit('action=logout;' . $context['session_var'] . '=' . $context['session_id']); |
||||||
| 813 | } |
||||||
| 814 | } |
||||||
| 815 | |||||||
| 816 | /** |
||||||
| 817 | * MD5 Encryption used for older passwords. (SMF 1.0.x/YaBB SE 1.5.x hashing) |
||||||
| 818 | * |
||||||
| 819 | * @param string $data |
||||||
| 820 | * @param string $key |
||||||
| 821 | * @return string the HMAC MD5 of data with key |
||||||
| 822 | * @package Authorization |
||||||
| 823 | */ |
||||||
| 824 | function md5_hmac($data, $key) |
||||||
| 825 | { |
||||||
| 826 | $key = str_pad(strlen($key) <= 64 ? $key : pack('H*', md5($key)), 64, chr(0x00)); |
||||||
| 827 | |||||||
| 828 | return md5(($key ^ str_repeat(chr(0x5c), 64)) . pack('H*', md5(($key ^ str_repeat(chr(0x36), 64)) . $data))); |
||||||
| 829 | } |
||||||
| 830 | |||||||
| 831 | /** |
||||||
| 832 | * Custom encryption for phpBB3 based passwords. |
||||||
| 833 | * |
||||||
| 834 | * @param string $passwd |
||||||
| 835 | * @param string $passwd_hash |
||||||
| 836 | * @return string |
||||||
| 837 | * @package Authorization |
||||||
| 838 | */ |
||||||
| 839 | function phpBB3_password_check($passwd, $passwd_hash) |
||||||
| 840 | { |
||||||
| 841 | // Too long or too short? |
||||||
| 842 | if (strlen($passwd_hash) !== 34) |
||||||
| 843 | { |
||||||
| 844 | return false; |
||||||
|
0 ignored issues
–
show
|
|||||||
| 845 | } |
||||||
| 846 | |||||||
| 847 | // Range of characters allowed. |
||||||
| 848 | $range = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; |
||||||
| 849 | |||||||
| 850 | // Tests |
||||||
| 851 | $strpos = strpos($range, $passwd_hash[3]); |
||||||
| 852 | $count = 1 << $strpos; |
||||||
| 853 | $salt = substr($passwd_hash, 4, 8); |
||||||
| 854 | |||||||
| 855 | $hash = md5($salt . $passwd, true); |
||||||
| 856 | for (; $count !== 0; --$count) |
||||||
| 857 | { |
||||||
| 858 | $hash = md5($hash . $passwd, true); |
||||||
| 859 | } |
||||||
| 860 | |||||||
| 861 | $output = substr($passwd_hash, 0, 12); |
||||||
| 862 | $i = 0; |
||||||
| 863 | while ($i < 16) |
||||||
| 864 | { |
||||||
| 865 | $value = ord($hash[$i++]); |
||||||
| 866 | $output .= $range[$value & 0x3f]; |
||||||
| 867 | |||||||
| 868 | if ($i < 16) |
||||||
| 869 | { |
||||||
| 870 | $value |= ord($hash[$i]) << 8; |
||||||
| 871 | } |
||||||
| 872 | |||||||
| 873 | $output .= $range[($value >> 6) & 0x3f]; |
||||||
| 874 | |||||||
| 875 | if ($i++ >= 16) |
||||||
| 876 | { |
||||||
| 877 | break; |
||||||
| 878 | } |
||||||
| 879 | |||||||
| 880 | if ($i < 16) |
||||||
| 881 | { |
||||||
| 882 | $value |= ord($hash[$i]) << 16; |
||||||
| 883 | } |
||||||
| 884 | |||||||
| 885 | $output .= $range[($value >> 12) & 0x3f]; |
||||||
| 886 | |||||||
| 887 | if ($i++ >= 16) |
||||||
| 888 | { |
||||||
| 889 | break; |
||||||
| 890 | } |
||||||
| 891 | |||||||
| 892 | $output .= $range[($value >> 18) & 0x3f]; |
||||||
| 893 | } |
||||||
| 894 | |||||||
| 895 | // Return now. |
||||||
| 896 | return $output; |
||||||
| 897 | } |
||||||
| 898 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths