Esempio n. 1
0
 /**
  * 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"];
 }