function Login2() { global $txt, $scripturl, $user_info, $user_settings, $smcFunc; global $cookiename, $maintenance, $modSettings, $context, $sc, $sourcedir; // Load cookie authentication stuff. require_once $sourcedir . '/Subs-Auth.php'; if (isset($_GET['sa']) && $_GET['sa'] == 'salt' && !$user_info['is_guest']) { if (isset($_COOKIE[$cookiename]) && preg_match('~^a:[34]:\\{i:0;(i:\\d{1,6}|s:[1-8]:"\\d{1,8}");i:1;s:(0|40):"([a-fA-F0-9]{40})?";i:2;[id]:\\d{1,14};(i:3;i:\\d;)?\\}$~', $_COOKIE[$cookiename]) === 1) { list(, , $timeout) = @unserialize($_COOKIE[$cookiename]); } elseif (isset($_SESSION['login_' . $cookiename])) { list(, , $timeout) = @unserialize($_SESSION['login_' . $cookiename]); } else { trigger_error('Login2(): Cannot be logged in without a session or cookie', E_USER_ERROR); } $user_settings['password_salt'] = substr(md5(mt_rand()), 0, 4); updateMemberData($user_info['id'], array('password_salt' => $user_settings['password_salt'])); setLoginCookie($timeout - time(), $user_info['id'], sha1($user_settings['passwd'] . $user_settings['password_salt'])); redirectexit('action=login2;sa=check;member=' . $user_info['id'], $context['server']['needs_login_fix']); } elseif (isset($_GET['sa']) && $_GET['sa'] == 'check') { // Strike! You're outta there! if ($_GET['member'] != $user_info['id']) { fatal_lang_error('login_cookie_error', false); } // Some whitelisting for login_url... if (empty($_SESSION['login_url'])) { redirectexit(); } else { // Best not to clutter the session data too much... $temp = $_SESSION['login_url']; unset($_SESSION['login_url']); redirectexit($temp); } } // Beyond this point you are assumed to be a guest trying to login. if (!$user_info['is_guest']) { redirectexit(); } // Set the login_url if it's not already set (but careful not to send us to an attachment). if (empty($_SESSION['login_url']) && isset($_SESSION['old_url']) && strpos($_SESSION['old_url'], 'dlattach') === false && preg_match('~(board|topic)[=,]~', $_SESSION['old_url']) != 0) { $_SESSION['login_url'] = $_SESSION['old_url']; } // Are you guessing with a script that doesn't keep the session id? spamProtection('login'); // Been guessing a lot, haven't we? if (isset($_SESSION['failed_login']) && $_SESSION['failed_login'] >= $modSettings['failed_login_threshold'] * 3) { fatal_lang_error('login_threshold_fail', 'critical'); } // Set up the cookie length. (if it's invalid, just fall through and use the default.) if (isset($_POST['cookieneverexp']) || !empty($_POST['cookielength']) && $_POST['cookielength'] == -1) { $modSettings['cookieTime'] = 3153600; } elseif (!empty($_POST['cookielength']) && ($_POST['cookielength'] >= 1 || $_POST['cookielength'] <= 525600)) { $modSettings['cookieTime'] = (int) $_POST['cookielength']; } loadLanguage('Login'); // Load the template stuff - wireless or normal. if (WIRELESS) { $context['sub_template'] = WIRELESS_PROTOCOL . '_login'; } else { loadTemplate('Login'); $context['sub_template'] = 'login'; } // Set up the default/fallback stuff. $context['default_username'] = isset($_REQUEST['user']) ? preg_replace('~&#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', htmlspecialchars($_REQUEST['user'])) : ''; $context['default_password'] = ''; $context['never_expire'] = $modSettings['cookieTime'] == 525600 || $modSettings['cookieTime'] == 3153600; $context['login_errors'] = array($txt['error_occured']); $context['page_title'] = $txt['login']; // Add the login chain to the link tree. $context['linktree'][] = array('url' => $scripturl . '?action=login', 'name' => $txt['login']); if (!empty($_REQUEST['openid_identifier']) && !empty($modSettings['enableOpenID'])) { require_once $sourcedir . '/Subs-OpenID.php'; return smf_openID_validate($_REQUEST['openid_identifier']); } // You forgot to type your username, dummy! if (!isset($_REQUEST['user']) || $_REQUEST['user'] == '') { $context['login_errors'] = array($txt['need_username']); return; } // Hmm... maybe 'admin' will login with no password. Uhh... NO! if ((!isset($_POST['passwrd']) || $_POST['passwrd'] == '') && (!isset($_REQUEST['hash_passwrd']) || strlen($_REQUEST['hash_passwrd']) != 40)) { $context['login_errors'] = array($txt['no_password']); return; } // No funky symbols either. if (preg_match('~[<>&"\'=\\\\]~', preg_replace('~(&#(\\d{1,7}|x[0-9a-fA-F]{1,6});)~', '', $_REQUEST['user'])) != 0) { $context['login_errors'] = array($txt['error_invalid_characters_username']); return; } // Are we using any sort of integration to validate the login? if (isset($modSettings['integrate_validate_login']) && is_callable($modSettings['integrate_validate_login'])) { if (call_user_func(strpos($modSettings['integrate_validate_login'], '::') === false ? $modSettings['integrate_validate_login'] : explode('::', $modSettings['integrate_validate_login']), $_REQUEST['user'], isset($_REQUEST['hash_passwrd']) && strlen($_REQUEST['hash_passwrd']) == 40 ? $_REQUEST['hash_passwrd'] : null, $modSettings['cookieTime']) == 'retry') { $context['login_errors'] = array($txt['login_hash_error']); $context['disable_login_hashing'] = true; return; } } // Load the data up! $request = $smcFunc['db_query']('', ' SELECT passwd, id_member, id_group, lngfile, is_activated, email_address, additional_groups, member_name, password_salt, openid_uri, passwd_flood FROM {db_prefix}members WHERE ' . ($smcFunc['db_case_sensitive'] ? 'LOWER(member_name) = LOWER({string:user_name})' : 'member_name = {string:user_name}') . ' LIMIT 1', array('user_name' => $smcFunc['db_case_sensitive'] ? strtolower($_REQUEST['user']) : $_REQUEST['user'])); // Probably mistyped or their email, try it as an email address. (member_name first, though!) if ($smcFunc['db_num_rows']($request) == 0) { $smcFunc['db_free_result']($request); $request = $smcFunc['db_query']('', ' SELECT passwd, id_member, id_group, lngfile, is_activated, email_address, additional_groups, member_name, password_salt, openid_uri, passwd_flood FROM {db_prefix}members WHERE email_address = {string:user_name} LIMIT 1', array('user_name' => $_REQUEST['user'])); // Let them try again, it didn't match anything... if ($smcFunc['db_num_rows']($request) == 0) { $context['login_errors'] = array($txt['username_no_exist']); return; } } $user_settings = $smcFunc['db_fetch_assoc']($request); $smcFunc['db_free_result']($request); // Figure out the password using SMF's encryption - if what they typed is right. if (isset($_REQUEST['hash_passwrd']) && strlen($_REQUEST['hash_passwrd']) == 40) { // Needs upgrading? if (strlen($user_settings['passwd']) != 40) { $context['login_errors'] = array($txt['login_hash_error']); $context['disable_login_hashing'] = true; unset($user_settings); return; } elseif ($_REQUEST['hash_passwrd'] == sha1($user_settings['passwd'] . $sc)) { $sha_passwd = $user_settings['passwd']; } else { // Don't allow this! validatePasswordFlood($user_settings['id_member'], $user_settings['passwd_flood']); $_SESSION['failed_login'] = @$_SESSION['failed_login'] + 1; if ($_SESSION['failed_login'] >= $modSettings['failed_login_threshold']) { redirectexit('action=reminder'); } else { log_error($txt['incorrect_password'] . ' - <span class="remove">' . $user_settings['member_name'] . '</span>', 'user'); $context['disable_login_hashing'] = true; $context['login_errors'] = array($txt['incorrect_password']); unset($user_settings); return; } } } else { $sha_passwd = sha1(strtolower($user_settings['member_name']) . un_htmlspecialchars($_POST['passwrd'])); } // Bad password! Thought you could fool the database?! if ($user_settings['passwd'] != $sha_passwd) { // Let's be cautious, no hacking please. thanx. validatePasswordFlood($user_settings['id_member'], $user_settings['passwd_flood']); // Maybe we were too hasty... let's try some other authentication methods. $other_passwords = array(); // None of the below cases will be used most of the time (because the salt is normally set.) if ($user_settings['password_salt'] == '') { // YaBB SE, Discus, MD5 (used a lot), SHA-1 (used some), SMF 1.0.x, IkonBoard, and none at all. $other_passwords[] = crypt($_POST['passwrd'], substr($_POST['passwrd'], 0, 2)); $other_passwords[] = crypt($_POST['passwrd'], substr($user_settings['passwd'], 0, 2)); $other_passwords[] = md5($_POST['passwrd']); $other_passwords[] = sha1($_POST['passwrd']); $other_passwords[] = md5_hmac($_POST['passwrd'], strtolower($user_settings['member_name'])); $other_passwords[] = md5($_POST['passwrd'] . strtolower($user_settings['member_name'])); $other_passwords[] = $_POST['passwrd']; // This one is a strange one... MyPHP, crypt() on the MD5 hash. $other_passwords[] = crypt(md5($_POST['passwrd']), md5($_POST['passwrd'])); // Snitz style - SHA-256. Technically, this is a downgrade, but most PHP configurations don't support sha256 anyway. if (strlen($user_settings['passwd']) == 64 && function_exists('mhash') && defined('MHASH_SHA256')) { $other_passwords[] = bin2hex(mhash(MHASH_SHA256, $_POST['passwrd'])); } // phpBB3 users new hashing. We now support it as well ;). $other_passwords[] = phpBB3_password_check($_POST['passwrd'], $user_settings['passwd']); // APBoard 2 Login Method. $other_passwords[] = md5(crypt($_REQUEST['passwrd'], 'CRYPT_MD5')); } elseif (strlen($user_settings['passwd']) == 32) { // vBulletin 3 style hashing? Let's welcome them with open arms \o/. $other_passwords[] = md5(md5($_POST['passwrd']) . $user_settings['password_salt']); // Hmm.. p'raps it's Invision 2 style? $other_passwords[] = md5(md5($user_settings['password_salt']) . md5($_POST['passwrd'])); // Some common md5 ones. $other_passwords[] = md5($user_settings['password_salt'] . $_POST['passwrd']); $other_passwords[] = md5($_POST['passwrd'] . $user_settings['password_salt']); $other_passwords[] = md5($_POST['passwrd']); $other_passwords[] = md5(md5($_POST['passwrd'])); } elseif (strlen($user_settings['passwd']) == 40) { // Maybe they are using a hash from before the password fix. $other_passwords[] = sha1(strtolower($user_settings['member_name']) . un_htmlspecialchars($_POST['passwrd'])); // BurningBoard3 style of hashing. $other_passwords[] = sha1($user_settings['password_salt'] . sha1($user_settings['password_salt'] . sha1($_REQUEST['passwrd']))); // Perhaps we converted to UTF-8 and have a valid password being hashed differently. if ($context['character_set'] == 'utf8' && !empty($modSettings['previousCharacterSet']) && $modSettings['previousCharacterSet'] != 'utf8') { // Try iconv first, for no particular reason. if (function_exists('iconv')) { $other_passwords['iconv'] = sha1(strtolower(iconv('UTF-8', $modSettings['previousCharacterSet'], $user_settings['member_name'])) . un_htmlspecialchars(iconv('UTF-8', $modSettings['previousCharacterSet'], $_POST['passwrd']))); } // Say it aint so, iconv failed! if (empty($other_passwords['iconv']) && function_exists('mb_convert_encoding')) { $other_passwords[] = sha1(strtolower(mb_convert_encoding($user_settings['member_name'], 'UTF-8', $modSettings['previousCharacterSet'])) . un_htmlspecialchars(mb_convert_encoding($_POST['passwrd'], 'UTF-8', $modSettings['previousCharacterSet']))); } } } // SMF'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! if (strpos(strtolower(PHP_OS), 'win') !== 0) { require_once $sourcedir . '/Subs-Compat.php'; $other_passwords[] = sha1_smf(strtolower($user_settings['member_name']) . un_htmlspecialchars($_POST['passwrd'])); } // Whichever encryption it was using, let's make it use SMF's now ;). if (in_array($user_settings['passwd'], $other_passwords)) { $user_settings['passwd'] = $sha_passwd; $user_settings['password_salt'] = substr(md5(mt_rand()), 0, 4); // Update the password and set up the hash. updateMemberData($user_settings['id_member'], array('passwd' => $user_settings['passwd'], 'password_salt' => $user_settings['password_salt'])); } else { // They've messed up again - keep a count to see if they need a hand. $_SESSION['failed_login'] = @$_SESSION['failed_login'] + 1; // Hmm... don't remember it, do you? Here, try the password reminder ;). if ($_SESSION['failed_login'] >= $modSettings['failed_login_threshold']) { redirectexit('action=reminder'); } else { // Log an error so we know that it didn't go well in the error log. log_error($txt['incorrect_password'] . ' - <span class="remove">' . $user_settings['member_name'] . '</span>', 'user'); $context['login_errors'] = array($txt['incorrect_password']); return; } } } elseif (!empty($user_settings['passwd_flood'])) { // Let's be sure they wern't a little hacker. validatePasswordFlood($user_settings['id_member'], $user_settings['passwd_flood'], true); // If we got here then we can reset the flood counter. updateMemberData($user_settings['id_member'], array('passwd_flood' => '')); } // Correct password, but they've got no salt; fix it! if ($user_settings['password_salt'] == '') { $user_settings['password_salt'] = substr(md5(mt_rand()), 0, 4); updateMemberData($user_settings['id_member'], array('password_salt' => $user_settings['password_salt'])); } // Check their activation status. if (!checkActivation()) { return; } DoLogin(); }
/** * Will authenticate the username/password combo * * Use this before setting the cookie to check if the username password are correct. * * @param mixed $username the user's member name, email or member id * @param string $password the password plaintext or encrypted in any of several * methods including smf's method: sha1(strtolower($username) . $password) * @param bool $encrypted whether the password is encrypted or not. If you get this wrong we'll figure it out anyways, just saves some work if it's right * @return bool whether the user is authenticated or not * @since 0.1.0 */ function smfapi_authenticate($username = '', $password = '', $encrypted = true) { global $scripturl, $user_info, $user_settings, $smcFunc; global $cookiename, $modSettings, $sc, $sourcedir; if ('' == $username || '' == $password) { return false; } // just in case they used the email or member id... $data = smfapi_getUserData($username); if (empty($data)) { return false; } else { $username = $data['member_name']; } // load the data up! $request = $smcFunc['db_query']('', ' SELECT passwd, id_member, id_group, lngfile, is_activated, email_address, additional_groups, member_name, password_salt, openid_uri, passwd_flood FROM {db_prefix}members WHERE ' . ($smcFunc['db_case_sensitive'] ? 'LOWER(member_name) = LOWER({string:user_name})' : 'member_name = {string:user_name}') . ' LIMIT 1', array('user_name' => $smcFunc['db_case_sensitive'] ? strtolower($username) : $username)); // no user data found... invalid username if ($smcFunc['db_num_rows']($request) == 0) { return false; } $user_settings = $smcFunc['db_fetch_assoc']($request); $smcFunc['db_free_result']($request); if (40 != strlen($user_settings['passwd'])) { // invalid hash in the db return false; } // if it's not encrypted, do it now if (!$encrypted) { $sha_passwd = sha1(strtolower($user_settings['member_name']) . smfapi_unHtmlspecialchars($password)); } else { $sha_passwd = $password; } // if they match the password/hash is correct if ($user_settings['passwd'] == $sha_passwd) { $user_info["id"] = $user_settings['id_member']; return true; } else { // try other hashing schemes $other_passwords = array(); // in case they sent the encrypted password into this as unencrypted $other_passwords[] = $password; // none of the below cases will be used most of the time // (because the salt is normally set) if ('' == $user_settings['password_salt']) { // YaBB SE, Discus, MD5 (used a lot), SHA-1 (used some), SMF 1.0.x, // IkonBoard, and none at all $other_passwords[] = crypt($password, substr($password, 0, 2)); $other_passwords[] = crypt($password, substr($user_settings['passwd'], 0, 2)); $other_passwords[] = md5($password); $other_passwords[] = sha1($password); $other_passwords[] = md5_hmac($password, strtolower($user_settings['member_name'])); $other_passwords[] = md5($password . strtolower($user_settings['member_name'])); $other_passwords[] = md5(md5($password)); $other_passwords[] = $password; // this one is a strange one... MyPHP, crypt() on the MD5 hash $other_passwords[] = crypt(md5($password), md5($password)); // Snitz style - SHA-256. Technically, this is a downgrade, but most PHP // configurations don't support sha256 anyway. if (strlen($user_settings['passwd']) == 64 && function_exists('mhash') && defined('MHASH_SHA256')) { $other_passwords[] = bin2hex(mhash(MHASH_SHA256, $password)); } // phpBB3 users new hashing. We now support it as well ;) $other_passwords[] = phpBB3_password_check($password, $user_settings['passwd']); // APBoard 2 login method $other_passwords[] = md5(crypt($password, 'CRYPT_MD5')); } elseif (strlen($user_settings['passwd']) == 32) { // vBulletin 3 style hashing? Let's welcome them with open arms \o/ $other_passwords[] = md5(md5($password) . $user_settings['password_salt']); // hmm.. p'raps it's Invision 2 style? $other_passwords[] = md5(md5($user_settings['password_salt']) . md5($password)); // some common md5 ones $other_passwords[] = md5($user_settings['password_salt'] . $password); $other_passwords[] = md5($password . $user_settings['password_salt']); } elseif (strlen($user_settings['passwd']) == 40) { // maybe they are using a hash from before the password fix $other_passwords[] = sha1(strtolower($user_settings['member_name']) . smfapi_unHtmlspecialchars($password)); // BurningBoard3 style of hashing $other_passwords[] = sha1($user_settings['password_salt'] . sha1($user_settings['password_salt'] . sha1($password))); // perhaps we converted to UTF-8 and have a valid password being // hashed differently if (!empty($modSettings['previousCharacterSet']) && $modSettings['previousCharacterSet'] != 'utf8') { // try iconv first, for no particular reason if (function_exists('iconv')) { $other_passwords['iconv'] = sha1(strtolower(iconv('UTF-8', $modSettings['previousCharacterSet'], $user_settings['member_name'])) . un_htmlspecialchars(iconv('UTF-8', $modSettings['previousCharacterSet'], $password))); } // say it aint so, iconv failed if (empty($other_passwords['iconv']) && function_exists('mb_convert_encoding')) { $other_passwords[] = sha1(strtolower(mb_convert_encoding($user_settings['member_name'], 'UTF-8', $modSettings['previousCharacterSet'])) . un_htmlspecialchars(mb_convert_encoding($password, 'UTF-8', $modSettings['previousCharacterSet']))); } } } // SMF'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! if (strpos(strtolower(PHP_OS), 'win') !== 0) { require_once $sourcedir . '/Subs-Compat.php'; $other_passwords[] = sha1_smf(strtolower($user_settings['member_name']) . smfapi_unHtmlspecialchars($password)); } // if ANY of these other hashes match we'll accept it if (in_array($user_settings['passwd'], $other_passwords)) { // we're not going to update the password or the hash. whatever was // used worked, so it will work again through this api, or SMF will // update it if the user authenticates through there. No sense messing // with it if it's not broken imo. Authentication successful $user_info["id"] = $user_settings['id_member']; return true; } } //authentication failed return false; }
/** * Loads other possible password hash / crypts using the post data * * What it does: * - Used when a board is converted to see if the user credentials and a 3rd * party hash satisfy whats in the db passwd field * * @param mixed[] $user_settings */ private function _other_passwords($user_settings) { global $modSettings, $sc; // What kind of data are we dealing with $pw_strlen = strlen($user_settings['passwd']); // Start off with none, thats safe $other_passwords = array(); // None of the below cases will be used most of the time (because the salt is normally set.) if (!empty($modSettings['enable_password_conversion']) && $user_settings['password_salt'] == '') { // YaBB SE, Discus, MD5 (used a lot), SHA-1 (used some), SMF 1.0.x, IkonBoard, and none at all. $other_passwords[] = crypt($_POST['passwrd'], substr($_POST['passwrd'], 0, 2)); $other_passwords[] = crypt($_POST['passwrd'], substr($user_settings['passwd'], 0, 2)); $other_passwords[] = md5($_POST['passwrd']); $other_passwords[] = sha1($_POST['passwrd']); $other_passwords[] = md5_hmac($_POST['passwrd'], strtolower($user_settings['member_name'])); $other_passwords[] = md5($_POST['passwrd'] . strtolower($user_settings['member_name'])); $other_passwords[] = md5(md5($_POST['passwrd'])); $other_passwords[] = $_POST['passwrd']; // This one is a strange one... MyPHP, crypt() on the MD5 hash. $other_passwords[] = crypt(md5($_POST['passwrd']), md5($_POST['passwrd'])); // SHA-256 if ($pw_strlen === 64) { // Snitz style $other_passwords[] = bin2hex(hash('sha256', $_POST['passwrd'])); // Normal SHA-256 $other_passwords[] = hash('sha256', $_POST['passwrd']); } // phpBB3 users new hashing. We now support it as well ;). $other_passwords[] = phpBB3_password_check($_POST['passwrd'], $user_settings['passwd']); // APBoard 2 Login Method. $other_passwords[] = md5(crypt($_POST['passwrd'], 'CRYPT_MD5')); } elseif (!empty($modSettings['enable_password_conversion']) && $pw_strlen === 32) { // vBulletin 3 style hashing? Let's welcome them with open arms \o/. $other_passwords[] = md5(md5($_POST['passwrd']) . stripslashes($user_settings['password_salt'])); // Hmm.. p'raps it's Invision 2 style? $other_passwords[] = md5(md5($user_settings['password_salt']) . md5($_POST['passwrd'])); // Some common md5 ones. $other_passwords[] = md5($user_settings['password_salt'] . $_POST['passwrd']); $other_passwords[] = md5($_POST['passwrd'] . $user_settings['password_salt']); } elseif ($pw_strlen === 40) { // Maybe they are using a hash from before our password upgrade $other_passwords[] = sha1(strtolower($user_settings['member_name']) . un_htmlspecialchars($_POST['passwrd'])); $other_passwords[] = sha1($user_settings['passwd'] . $sc); if (!empty($modSettings['enable_password_conversion'])) { // BurningBoard3 style of hashing. $other_passwords[] = sha1($user_settings['password_salt'] . sha1($user_settings['password_salt'] . sha1($_POST['passwrd']))); // PunBB 1.4 and later $other_passwords[] = sha1($user_settings['password_salt'] . sha1($_POST['passwrd'])); } // Perhaps we converted from a non UTF-8 db and have a valid password being hashed differently. if (!empty($modSettings['previousCharacterSet']) && $modSettings['previousCharacterSet'] != 'utf8') { // Try iconv first, for no particular reason. if (function_exists('iconv')) { $other_passwords['iconv'] = sha1(strtolower(iconv('UTF-8', $modSettings['previousCharacterSet'], $user_settings['member_name'])) . un_htmlspecialchars(iconv('UTF-8', $modSettings['previousCharacterSet'], $_POST['passwrd']))); } // Say it aint so, iconv failed! if (empty($other_passwords['iconv']) && function_exists('mb_convert_encoding')) { $other_passwords[] = sha1(strtolower(mb_convert_encoding($user_settings['member_name'], 'UTF-8', $modSettings['previousCharacterSet'])) . un_htmlspecialchars(mb_convert_encoding($_POST['passwrd'], 'UTF-8', $modSettings['previousCharacterSet']))); } } } elseif (!empty($modSettings['enable_password_conversion']) && $pw_strlen === 64) { // PHP-Fusion7 $other_passwords[] = hash_hmac('sha256', $_POST['passwrd'], $user_settings['password_salt']); // Plain SHA-256? $other_passwords[] = hash('sha256', $_POST['passwrd'] . $user_settings['password_salt']); // Xenforo? $other_passwords[] = sha1(sha1($_POST['passwrd']) . $user_settings['password_salt']); $other_passwords[] = hash('sha256', hash('sha256', $_POST['passwrd'] . $user_settings['password_salt'])); } // 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! if (stripos(PHP_OS, 'win') !== 0) { require_once SUBSDIR . '/Compat.subs.php'; $other_passwords[] = sha1_smf(strtolower($user_settings['member_name']) . un_htmlspecialchars($_POST['passwrd'])); } // Allows mods to easily extend the $other_passwords array call_integration_hook('integrate_other_passwords', array(&$other_passwords)); return $other_passwords; }
function sha1($str) { return sha1_smf($str); }
function Login2() { global $txt, $db_prefix, $scripturl, $user_info, $user_settings; global $cookiename, $maintenance, $ID_MEMBER, $modSettings, $context, $sc; global $sourcedir; // Load cookie authentication stuff. require_once $sourcedir . '/Subs-Auth.php'; if (isset($_GET['sa']) && $_GET['sa'] == 'salt' && !$user_info['is_guest']) { if (isset($_COOKIE[$cookiename]) && preg_match('~^a:[34]:\\{i:0;(i:\\d{1,6}|s:[1-8]:"\\d{1,8}");i:1;s:(0|40):"([a-fA-F0-9]{40})?";i:2;[id]:\\d{1,14};(i:3;i:\\d;)?\\}$~', $_COOKIE[$cookiename]) === 1) { list(, , $timeout) = @unserialize($_COOKIE[$cookiename]); } elseif (isset($_SESSION['login_' . $cookiename])) { list(, , $timeout) = @unserialize(stripslashes($_SESSION['login_' . $cookiename])); } else { trigger_error('Login2(): Cannot be logged in without a session or cookie', E_USER_ERROR); } $user_settings['passwordSalt'] = substr(md5(mt_rand()), 0, 4); updateMemberData($ID_MEMBER, array('passwordSalt' => '\'' . $user_settings['passwordSalt'] . '\'')); setLoginCookie($timeout - time(), $ID_MEMBER, sha1($user_settings['passwd'] . $user_settings['passwordSalt'])); redirectexit('action=login2;sa=check;member=' . $ID_MEMBER, $context['server']['needs_login_fix']); } elseif (isset($_GET['sa']) && $_GET['sa'] == 'check') { // Strike! You're outta there! if ($_GET['member'] != $ID_MEMBER) { fatal_lang_error('login_cookie_error', false); } // Some whitelisting for login_url... if (empty($_SESSION['login_url'])) { redirectexit(); } else { // Best not to clutter the session data too much... $temp = $_SESSION['login_url']; unset($_SESSION['login_url']); redirectexit($temp); } } // Beyond this point you are assumed to be a guest trying to login. if (!$user_info['is_guest']) { redirectexit(); } // Set the login_url if it's not already set. if (empty($_SESSION['login_url']) && isset($_SESSION['old_url']) && preg_match('~(board|topic)[=,]~', $_SESSION['old_url']) != 0) { $_SESSION['login_url'] = $_SESSION['old_url']; } // Are you guessing with a script that doesn't keep the session id? spamProtection('login'); // Been guessing a lot, haven't we? if (isset($_SESSION['failed_login']) && $_SESSION['failed_login'] >= $modSettings['failed_login_threshold'] * 3) { fatal_lang_error('login_threshold_fail'); } // Set up the cookie length. (if it's invalid, just fall through and use the default.) if (isset($_POST['cookieneverexp']) || !empty($_POST['cookielength']) && $_POST['cookielength'] == -1) { $modSettings['cookieTime'] = 3153600; } elseif (!empty($_POST['cookielength']) && ($_POST['cookielength'] >= 1 || $_POST['cookielength'] <= 525600)) { $modSettings['cookieTime'] = (int) $_POST['cookielength']; } // Set things up in case an error occurs. if (!empty($maintenance) || empty($modSettings['allow_guestAccess'])) { $context['sub_template'] = 'kick_guest'; } loadLanguage('Login'); // Load the template stuff - wireless or normal. if (WIRELESS) { $context['sub_template'] = WIRELESS_PROTOCOL . '_login'; } else { loadTemplate('Login'); $context['sub_template'] = 'login'; } // Set up the default/fallback stuff. $context['default_username'] = isset($_REQUEST['user']) ? htmlspecialchars(stripslashes($_REQUEST['user'])) : ''; $context['default_password'] = ''; $context['never_expire'] = $modSettings['cookieTime'] == 525600 || $modSettings['cookieTime'] == 3153600; $context['login_error'] =& $txt[106]; $context['page_title'] = $txt[34]; // You forgot to type your username, dummy! if (!isset($_REQUEST['user']) || $_REQUEST['user'] == '') { $context['login_error'] =& $txt[37]; return; } // Hmm... maybe 'admin' will login with no password. Uhh... NO! if ((!isset($_REQUEST['passwrd']) || $_REQUEST['passwrd'] == '') && (!isset($_REQUEST['hash_passwrd']) || strlen($_REQUEST['hash_passwrd']) != 40)) { $context['login_error'] =& $txt[38]; return; } // No funky symbols either. if (preg_match('~[<>&"\'=\\\\]~', $_REQUEST['user']) != 0) { $context['login_error'] =& $txt[240]; return; } // Are we using any sort of integration to validate the login? if (isset($modSettings['integrate_validate_login']) && function_exists($modSettings['integrate_validate_login'])) { if (call_user_func($modSettings['integrate_validate_login'], $_REQUEST['user'], isset($_REQUEST['hash_passwrd']) && strlen($_REQUEST['hash_passwrd']) == 40 ? $_REQUEST['hash_passwrd'] : null, $modSettings['cookieTime']) == 'retry') { $context['login_error'] = $txt['login_hash_error']; $context['disable_login_hashing'] = true; return; } } // Load the data up! $request = db_query("\n\t\tSELECT passwd, ID_MEMBER, ID_GROUP, lngfile, is_activated, emailAddress, additionalGroups, memberName, passwordSalt\n\t\tFROM {$db_prefix}members\n\t\tWHERE memberName = '{$_REQUEST['user']}'\n\t\tLIMIT 1", __FILE__, __LINE__); // Probably mistyped or their email, try it as an email address. (memberName first, though!) if (mysql_num_rows($request) == 0) { mysql_free_result($request); $request = db_query("\n\t\t\tSELECT passwd, ID_MEMBER, ID_GROUP, lngfile, is_activated, emailAddress, additionalGroups, memberName, passwordSalt\n\t\t\tFROM {$db_prefix}members\n\t\t\tWHERE emailAddress = '{$_REQUEST['user']}'\n\t\t\tLIMIT 1", __FILE__, __LINE__); // Let them try again, it didn't match anything... if (mysql_num_rows($request) == 0) { $context['login_error'] =& $txt[40]; return; } } $user_settings = mysql_fetch_assoc($request); mysql_free_result($request); // What is the true activation status of this account? $activation_status = $user_settings['is_activated'] > 10 ? $user_settings['is_activated'] - 10 : $user_settings['is_activated']; // Check if the account is activated - COPPA first... if ($activation_status == 5) { $context['login_error'] = $txt['coppa_not_completed1'] . ' <a href="' . $scripturl . '?action=coppa;member=' . $user_settings['ID_MEMBER'] . '">' . $txt['coppa_not_completed2'] . '</a>'; return; } elseif ($activation_status == 3) { fatal_lang_error('still_awaiting_approval'); } elseif ($activation_status == 4) { // Display an error if we haven't decided to undelete. if (!isset($_REQUEST['undelete'])) { $context['login_error'] = $txt['awaiting_delete_account']; $context['login_show_undelete'] = true; return; } else { updateMemberData($user_settings['ID_MEMBER'], array('is_activated' => 1)); updateSettings(array('unapprovedMembers' => $modSettings['unapprovedMembers'] > 0 ? $modSettings['unapprovedMembers'] - 1 : 0)); } } elseif ($activation_status != 1) { log_error($txt['activate_not_completed1'] . ' - <span class="remove">' . $user_settings['memberName'] . '</span>', false); $context['login_error'] = $txt['activate_not_completed1'] . ' <a href="' . $scripturl . '?action=activate;sa=resend;u=' . $user_settings['ID_MEMBER'] . '">' . $txt['activate_not_completed2'] . '</a>'; return; } // Figure out the password using SMF's encryption - if what they typed is right. if (isset($_REQUEST['hash_passwrd']) && strlen($_REQUEST['hash_passwrd']) == 40) { // Needs upgrading? if (strlen($user_settings['passwd']) != 40) { $context['login_error'] = $txt['login_hash_error']; $context['disable_login_hashing'] = true; return; } elseif ($_REQUEST['hash_passwrd'] == sha1($user_settings['passwd'] . $sc)) { $sha_passwd = $user_settings['passwd']; } else { $_SESSION['failed_login'] = @$_SESSION['failed_login'] + 1; if ($_SESSION['failed_login'] >= $modSettings['failed_login_threshold']) { redirectexit('action=reminder'); } else { log_error($txt[39] . ' - <span class="remove">' . $user_settings['memberName'] . '</span>'); $context['disable_login_hashing'] = true; $context['login_error'] = $txt[39]; return; } } } else { $sha_passwd = sha1(strtolower($user_settings['memberName']) . un_htmlspecialchars(stripslashes($_REQUEST['passwrd']))); } // Bad password! Thought you could fool the database?! if ($user_settings['passwd'] != $sha_passwd) { // Maybe we were too hasty... let's try some other authentication methods. $other_passwords = array(); // None of the below cases will be used most of the time (because the salt is normally set.) if ($user_settings['passwordSalt'] == '') { // YaBB SE, Discus, MD5 (used a lot), SHA-1 (used some), SMF 1.0.x, IkonBoard, and none at all. $other_passwords[] = crypt($_REQUEST['passwrd'], substr($_REQUEST['passwrd'], 0, 2)); $other_passwords[] = crypt($_REQUEST['passwrd'], substr($user_settings['passwd'], 0, 2)); $other_passwords[] = md5($_REQUEST['passwrd']); $other_passwords[] = sha1($_REQUEST['passwrd']); $other_passwords[] = md5_hmac($_REQUEST['passwrd'], strtolower($user_settings['memberName'])); $other_passwords[] = md5($_REQUEST['passwrd'] . strtolower($user_settings['memberName'])); $other_passwords[] = $_REQUEST['passwrd']; // This one is a strange one... MyPHP, crypt() on the MD5 hash. $other_passwords[] = crypt(md5($_REQUEST['passwrd']), md5($_REQUEST['passwrd'])); // Snitz style - SHA-256. Technically, this is a downgrade, but most PHP configurations don't support sha256 anyway. if (strlen($user_settings['passwd']) == 64 && function_exists('mhash') && defined('MHASH_SHA256')) { $other_passwords[] = bin2hex(mhash(MHASH_SHA256, $_REQUEST['passwrd'])); } } elseif (strlen($user_settings['passwd']) == 32) { // vBulletin 3 style hashing? Let's welcome them with open arms \o/. $other_passwords[] = md5(md5($_REQUEST['passwrd']) . $user_settings['passwordSalt']); // Hmm.. p'raps it's Invision 2 style? $other_passwords[] = md5(md5($user_settings['passwordSalt']) . md5($_REQUEST['passwrd'])); } // Maybe they are using a hash from before the password fix. $other_passwords[] = sha1(strtolower($user_settings['memberName']) . addslashes(un_htmlspecialchars(stripslashes($_REQUEST['passwrd'])))); // SMF'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! require_once $sourcedir . '/Subs-Compat.php'; $other_passwords[] = sha1_smf(strtolower($user_settings['memberName']) . un_htmlspecialchars(stripslashes($_REQUEST['passwrd']))); // Whichever encryption it was using, let's make it use SMF's now ;). if (in_array($user_settings['passwd'], $other_passwords)) { $user_settings['passwd'] = $sha_passwd; $user_settings['passwordSalt'] = substr(md5(mt_rand()), 0, 4); // Update the password and set up the hash. updateMemberData($user_settings['ID_MEMBER'], array('passwd' => '\'' . $user_settings['passwd'] . '\'', 'passwordSalt' => '\'' . $user_settings['passwordSalt'] . '\'')); } else { // They've messed up again - keep a count to see if they need a hand. $_SESSION['failed_login'] = @$_SESSION['failed_login'] + 1; // Hmm... don't remember it, do you? Here, try the password reminder ;). if ($_SESSION['failed_login'] >= $modSettings['failed_login_threshold']) { redirectexit('action=reminder'); } else { // Log an error so we know that it didn't go well in the error log. log_error($txt[39] . ' - <span class="remove">' . $user_settings['memberName'] . '</span>'); $context['login_error'] = $txt[39]; return; } } } elseif ($user_settings['passwordSalt'] == '') { $user_settings['passwordSalt'] = substr(md5(mt_rand()), 0, 4); updateMemberData($user_settings['ID_MEMBER'], array('passwordSalt' => '\'' . $user_settings['passwordSalt'] . '\'')); } if (isset($modSettings['integrate_login']) && function_exists($modSettings['integrate_login'])) { $modSettings['integrate_login']($user_settings['memberName'], isset($_REQUEST['hash_passwrd']) && strlen($_REQUEST['hash_passwrd']) == 40 ? $_REQUEST['hash_passwrd'] : null, $modSettings['cookieTime']); } // Get ready to set the cookie... $username = $user_settings['memberName']; $ID_MEMBER = $user_settings['ID_MEMBER']; // Bam! Cookie set. A session too, just incase. setLoginCookie(60 * $modSettings['cookieTime'], $user_settings['ID_MEMBER'], sha1($user_settings['passwd'] . $user_settings['passwordSalt'])); // Reset the login threshold. if (isset($_SESSION['failed_login'])) { unset($_SESSION['failed_login']); } $user_info['is_guest'] = false; $user_settings['additionalGroups'] = explode(',', $user_settings['additionalGroups']); $user_info['is_admin'] = $user_settings['ID_GROUP'] == 1 || in_array(1, $user_settings['additionalGroups']); // Are you banned? is_not_banned(true); // An administrator, set up the login so they don't have to type it again. if ($user_info['is_admin']) { $_SESSION['admin_time'] = time(); unset($_SESSION['just_registered']); } // Don't stick the language or theme after this point. unset($_SESSION['language']); unset($_SESSION['ID_THEME']); // You've logged in, haven't you? updateMemberData($ID_MEMBER, array('lastLogin' => time(), 'memberIP' => '\'' . $user_info['ip'] . '\'', 'memberIP2' => '\'' . $_SERVER['BAN_CHECK_IP'] . '\'')); // Get rid of the online entry for that old guest.... db_query("\n\t\tDELETE FROM {$db_prefix}log_online\n\t\tWHERE session = 'ip{$user_info['ip']}'\n\t\tLIMIT 1", __FILE__, __LINE__); $_SESSION['log_time'] = 0; // Just log you back out if it's in maintenance mode and you AREN'T an admin. if (empty($maintenance) || allowedTo('admin_forum')) { redirectexit('action=login2;sa=check;member=' . $ID_MEMBER, $context['server']['needs_login_fix']); } else { redirectexit('action=logout;sesc=' . $sc, $context['server']['needs_login_fix']); } }