/** * Authenticates the user and then authorizes him */ function Login($login, $password, $remember = "N", $password_original = "Y") { /** @global CMain $APPLICATION */ global $DB, $APPLICATION; $result_message = true; $user_id = 0; $applicationId = null; $applicationPassId = null; $arParams = array("LOGIN" => &$login, "PASSWORD" => &$password, "REMEMBER" => &$remember, "PASSWORD_ORIGINAL" => &$password_original); unset($_SESSION["SESS_OPERATIONS"]); unset($_SESSION["MODULE_PERMISSIONS"]); $_SESSION["BX_LOGIN_NEED_CAPTCHA"] = false; $bOk = true; $APPLICATION->ResetException(); foreach (GetModuleEvents("main", "OnBeforeUserLogin", true) as $arEvent) { if (ExecuteModuleEventEx($arEvent, array(&$arParams)) === false) { if ($err = $APPLICATION->GetException()) { $result_message = array("MESSAGE" => $err->GetString() . "<br>", "TYPE" => "ERROR"); } else { $APPLICATION->ThrowException("Unknown login error"); $result_message = array("MESSAGE" => "Unknown login error" . "<br>", "TYPE" => "ERROR"); } $bOk = false; break; } } if ($bOk) { //external authentication foreach (GetModuleEvents("main", "OnUserLoginExternal", true) as $arEvent) { $user_id = ExecuteModuleEventEx($arEvent, array(&$arParams)); if ($user_id > 0) { break; } } if ($user_id <= 0) { //internal authentication OR application password for external user $foundUser = false; $strSql = "SELECT U.ID, U.LOGIN, U.ACTIVE, U.PASSWORD, U.LOGIN_ATTEMPTS, U.CONFIRM_CODE, U.EMAIL " . "FROM b_user U " . "WHERE U.LOGIN='******' " . "\tAND (EXTERNAL_AUTH_ID IS NULL OR EXTERNAL_AUTH_ID='') "; $result = $DB->Query($strSql); if ($arUser = $result->Fetch()) { //internal authentication by login and password $foundUser = true; if (strlen($arUser["PASSWORD"]) > 32) { $salt = substr($arUser["PASSWORD"], 0, strlen($arUser["PASSWORD"]) - 32); $db_password = substr($arUser["PASSWORD"], -32); } else { $salt = ""; $db_password = $arUser["PASSWORD"]; } $user_password_no_otp = ""; if ($arParams["PASSWORD_ORIGINAL"] == "Y") { $user_password = md5($salt . $arParams["PASSWORD"]); if ($arParams["OTP"] != '') { $user_password_no_otp = md5($salt . substr($arParams["PASSWORD"], 0, -6)); } } else { if (strlen($arParams["PASSWORD"]) > 32) { $user_password = substr($arParams["PASSWORD"], -32); } else { $user_password = $arParams["PASSWORD"]; } } $passwordCorrect = $db_password === $user_password || $arParams["OTP"] != '' && $db_password === $user_password_no_otp; if ($db_password === $user_password) { //this password has no added otp for sure $arParams["OTP"] = ''; } if (!$passwordCorrect) { //let's try to find application password if (($appPassword = ApplicationPasswordTable::findPassword($arUser["ID"], $arParams["PASSWORD"], $arParams["PASSWORD_ORIGINAL"] == "Y")) !== false) { $passwordCorrect = true; $applicationId = $appPassword["APPLICATION_ID"]; $applicationPassId = $appPassword["ID"]; } } $arPolicy = CUser::GetGroupPolicy($arUser["ID"]); $pol_login_attempts = intval($arPolicy["LOGIN_ATTEMPTS"]); $usr_login_attempts = intval($arUser["LOGIN_ATTEMPTS"]) + 1; if ($pol_login_attempts > 0 && $usr_login_attempts > $pol_login_attempts) { $_SESSION["BX_LOGIN_NEED_CAPTCHA"] = true; if (!$APPLICATION->CaptchaCheckCode($_REQUEST["captcha_word"], $_REQUEST["captcha_sid"])) { $passwordCorrect = false; } } if ($passwordCorrect) { if ($salt == '' && $arParams["PASSWORD_ORIGINAL"] == "Y" && $applicationId === null) { $salt = randString(8, array("abcdefghijklnmopqrstuvwxyz", "ABCDEFGHIJKLNMOPQRSTUVWXYZ", "0123456789", ",.<>/?;:[]{}\\|~!@#\$%^&*()-_+=")); $new_password = $salt . md5($salt . $arParams["PASSWORD"]); $DB->Query("UPDATE b_user SET PASSWORD='******', TIMESTAMP_X = TIMESTAMP_X WHERE ID = " . intval($arUser["ID"])); } if ($arUser["ACTIVE"] == "Y") { $user_id = $arUser["ID"]; //update digest hash for http digest authorization if ($arParams["PASSWORD_ORIGINAL"] == "Y" && $applicationId === null && COption::GetOptionString('main', 'use_digest_auth', 'N') == 'Y') { CUser::UpdateDigest($arUser["ID"], $arParams["PASSWORD"]); } } elseif ($arUser["CONFIRM_CODE"] != '') { //unconfirmed registration $message = GetMessage("MAIN_LOGIN_EMAIL_CONFIRM", array("#EMAIL#" => $arUser["EMAIL"])); $APPLICATION->ThrowException($message); $result_message = array("MESSAGE" => $message . "<br>", "TYPE" => "ERROR"); } else { $APPLICATION->ThrowException(GetMessage("LOGIN_BLOCK")); $result_message = array("MESSAGE" => GetMessage("LOGIN_BLOCK") . "<br>", "TYPE" => "ERROR"); } } else { $DB->Query("UPDATE b_user SET LOGIN_ATTEMPTS = " . $usr_login_attempts . ", TIMESTAMP_X = TIMESTAMP_X WHERE ID = " . intval($arUser["ID"])); $APPLICATION->ThrowException(GetMessage("WRONG_LOGIN")); $result_message = array("MESSAGE" => GetMessage("WRONG_LOGIN") . "<br>", "TYPE" => "ERROR", "ERROR_TYPE" => "LOGIN"); } } else { //no user found by login - try to find an external user foreach (GetModuleEvents("main", "OnFindExternalUser", true) as $arEvent) { if (($external_user_id = intval(ExecuteModuleEventEx($arEvent, array($arParams["LOGIN"])))) > 0) { //external user authentication //let's try to find application password for the external user if (($appPassword = ApplicationPasswordTable::findPassword($external_user_id, $arParams["PASSWORD"], $arParams["PASSWORD_ORIGINAL"] == "Y")) !== false) { //bingo, the user has the application password $foundUser = true; $user_id = $external_user_id; $applicationId = $appPassword["APPLICATION_ID"]; $applicationPassId = $appPassword["ID"]; } break; } } } if (!$foundUser) { $APPLICATION->ThrowException(GetMessage("WRONG_LOGIN")); $result_message = array("MESSAGE" => GetMessage("WRONG_LOGIN") . "<br>", "TYPE" => "ERROR", "ERROR_TYPE" => "LOGIN"); } } } // All except Admin if ($user_id > 1 && $arParams["CONTROLLER_ADMIN"] !== "Y") { $limitUsersCount = intval(COption::GetOptionInt("main", "PARAM_MAX_USERS", 0)); if ($limitUsersCount > 0) { $by = "ID"; $order = "ASC"; $arFilter = array("LAST_LOGIN_1" => ConvertTimeStamp()); //Intranet users only if (IsModuleInstalled("intranet")) { $arFilter["!=UF_DEPARTMENT"] = false; } $rsUsers = CUser::GetList($by, $order, $arFilter, array("FIELDS" => array("ID", "LOGIN"))); while ($user = $rsUsers->fetch()) { if ($user["ID"] == $user_id) { $limitUsersCount = 1; break; } $limitUsersCount--; } if ($limitUsersCount < 0) { $user_id = 0; $APPLICATION->ThrowException(GetMessage("LIMIT_USERS_COUNT")); $result_message = array("MESSAGE" => GetMessage("LIMIT_USERS_COUNT") . "<br>", "TYPE" => "ERROR"); } } } $arParams["USER_ID"] = $user_id; $doAuthorize = true; if ($user_id > 0) { if ($applicationId === null && CModule::IncludeModule("security")) { /* MFA can allow or disallow authorization. Allowed if: - OTP is not active for the user; - correct "OTP" in the $arParams (filled by the OnBeforeUserLogin event handler). Disallowed if: - OTP is not provided; - OTP is not correct. When authorization is disallowed the OTP form will be shown on the next hit. Note: there is no MFA check for an application password. */ $arParams["CAPTCHA_WORD"] = $_REQUEST["captcha_word"]; $arParams["CAPTCHA_SID"] = $_REQUEST["captcha_sid"]; $doAuthorize = \Bitrix\Security\Mfa\Otp::verifyUser($arParams); } if ($doAuthorize) { $this->Authorize($user_id, $arParams["REMEMBER"] == "Y", true, $applicationId); if ($applicationPassId !== null) { //update usage statistics for the application Main\Authentication\ApplicationPasswordTable::update($applicationPassId, array('DATE_LOGIN' => new Main\Type\DateTime(), 'LAST_IP' => $_SERVER["REMOTE_ADDR"])); } } else { $result_message = false; } if ($applicationId === null && $arParams["LOGIN"] != '') { //the cookie is for authentication forms mostly, does not make sense for applications $APPLICATION->set_cookie("LOGIN", $arParams["LOGIN"], time() + 60 * 60 * 24 * 30 * 60, '/', false, false, COption::GetOptionString("main", "auth_multisite", "N") == "Y"); } } $arParams["RESULT_MESSAGE"] = $result_message; $APPLICATION->ResetException(); foreach (GetModuleEvents("main", "OnAfterUserLogin", true) as $arEvent) { ExecuteModuleEventEx($arEvent, array(&$arParams)); } if ($doAuthorize == true && $result_message !== true && COption::GetOptionString("main", "event_log_login_fail", "N") === "Y") { CEventLog::Log("SECURITY", "USER_LOGIN", "main", $login, $result_message["MESSAGE"]); } return $arParams["RESULT_MESSAGE"]; }