Your IP : 172.28.240.42


Current Path : /var/www/html/clients/nkpgkx11.e-nk.ru/bitrix/modules/main/lib/security/
Upload File :
Current File : /var/www/html/clients/nkpgkx11.e-nk.ru/bitrix/modules/main/lib/security/authentication.php

<?php
namespace Bitrix\Main\Security;

use \Bitrix\Main;
use \Bitrix\Main\Config;

class Authentication
{
	protected static $lastError;

	const AUTHENTICATED_BY_SESSION = 1;
	const AUTHENTICATED_BY_HASH = 2;
	const AUTHENTICATED_BY_PASSWORD = 3;

	public static function checkSessionSecurity(CurrentUser $user)
	{
		$context = \Bitrix\Main\Application::getInstance()->getContext();
		if (!($context instanceof \Bitrix\Main\HttpContext))
			throw new \Bitrix\Main\NotSupportedException();

		$policy = $user->getPolicy();
		$currentTime = time();

		/** @var $request \Bitrix\Main\HttpRequest */
		$request = $context->getRequest();
		$remoteAddress = $request->getRemoteAddress();

		// IP address changed
		$destroySession = $_SESSION['SESS_IP']
			&& strlen($policy["SESSION_IP_MASK"]) > 0
			&& (
				(ip2long($policy["SESSION_IP_MASK"]) & ip2long($_SESSION['SESS_IP']))
				!=
				(ip2long($policy["SESSION_IP_MASK"]) & ip2long($remoteAddress))
			);

		// session timeout
		if (!$destroySession)
			$destroySession = $policy["SESSION_TIMEOUT"] > 0
				&& $_SESSION['SESS_TIME'] > 0
				&& $currentTime - $policy["SESSION_TIMEOUT"] * 60 > $_SESSION['SESS_TIME'];

		// session expander control
		if (!$destroySession)
			$destroySession = $_SESSION["BX_SESSION_TERMINATE_TIME"] > 0
				&& $currentTime > $_SESSION["BX_SESSION_TERMINATE_TIME"];

		if ($destroySession)
		{
			$_SESSION = array();
			@session_destroy();

			//session_destroy cleans user session handles in some PHP versions
			//see http://bugs.php.net/bug.php?id=32330 discussion
			if (
				Config\Option::get("security", "session", "N") === "Y"
				&& Main\Loader::includeModule("security"))
			{
				\CSecuritySession::init();
			}

			session_id(md5(uniqid(rand(), true)));
			session_start();
		}
		$_SESSION['SESS_IP'] = $remoteAddress;
		$_SESSION['SESS_TIME'] = time();

		//session control from security module
		if (
			(Config\Option::get("main", "use_session_id_ttl", "N") == "Y")
			&& (intval(Config\Option::get("main", "session_id_ttl", 0)) > 0)
			&& !defined("BX_SESSION_ID_CHANGE")
		)
		{
			if (!array_key_exists('SESS_ID_TIME', $_SESSION))
			{
				$_SESSION['SESS_ID_TIME'] = $_SESSION['SESS_TIME'];
			}
			elseif (($_SESSION['SESS_ID_TIME'] + intval(Config\Option::get("main", "session_id_ttl"))) < $_SESSION['SESS_TIME'])
			{
				if (Config\Option::get("security", "session", "N") === "Y"
					&& \Bitrix\Main\Loader::includeModule("security"))
				{
					\CSecuritySession::updateSessID();
				}
				else
				{
					session_regenerate_id();
				}
				$_SESSION['SESS_ID_TIME'] = $_SESSION['SESS_TIME'];
			}
		}

		return !$destroySession;
	}

	public static function getUserByCookie()
	{
		if (Config\Option::get("main", "store_password", "Y") != "Y")
			return null;

		$context = Main\Application::getInstance()->getContext();
		/** @var $request \Bitrix\Main\HttpRequest */
		$request = $context->getRequest();

		$cookieLogin = $request->getCookie('LOGIN');
		$cookieMd5Pass = $request->getCookie('UIDH');

		if (!empty($cookieLogin) && !empty($cookieMd5Pass)
			&& $_SESSION["SESS_PWD_HASH_TESTED"] != md5($cookieLogin."|".$cookieMd5Pass))
		{
			$user = static::getUserByHash($cookieLogin, $cookieMd5Pass);
			$_SESSION["SESS_PWD_HASH_TESTED"] = md5($cookieLogin."|".$cookieMd5Pass);

			return $user;
		}

		return null;
	}

	public static function getUserBySession()
	{
		if (isset($_SESSION["SESS_AUTH"]) && is_array($_SESSION["SESS_AUTH"]))
		{
			if ($_SESSION["SESS_AUTH"]["AUTHORIZED"] === "Y")
			{
				$user = CurrentUser::createFromArray($_SESSION["SESS_AUTH"]);
				$user->setAuthType(static::AUTHENTICATED_BY_SESSION);
				return $user;
			}
		}

		return null;
	}

	public static function copyToSession(CurrentUser $user)
	{
		$_SESSION["SESS_AUTH"] = $user->exportToArray();
	}

	protected static function getStoredHashId(CurrentUser $user, $hash, $onlyTempHash = false)
	{
		if (!$user->isAuthenticated())
			throw new SecurityException();

		$context = Main\Application::getInstance()->getContext();
		if (!($context instanceof \Bitrix\Main\HttpContext))
			throw new Main\NotSupportedException();

		/** @var $request \Bitrix\Main\HttpRequest */
		$request = $context->getRequest();
		$remoteAddress = $request->getRemoteAddress();

		$policy = $user->getPolicy();

		$cnt = 0;
		$hashId = null;

		$connection = Main\Application::getDbConnection();

		$sql =
			"SELECT A.* ".
			"FROM b_user_stored_auth A ".
			"WHERE A.USER_ID = ".intval($user->getUserId())." ".
			"ORDER BY A.LAST_AUTH DESC";
		$recordset = $connection->query($sql);
		while ($record = $recordset->fetch())
		{
			if ($record["TEMP_HASH"] === "N")
				$cnt++;

			/** @var $lastAuth \Bitrix\Main\Type\DateTime */
			$lastAuth = $record["LAST_AUTH"];
			if ($policy["MAX_STORE_NUM"] < $cnt
				|| ($record["TEMP_HASH"] === "N" && mktime() - $policy["STORE_TIMEOUT"] * 60 > $lastAuth->getTimestamp())
				|| ($record["TEMP_HASH"] === "Y" && mktime() - $policy["SESSION_TIMEOUT"] * 60 > $lastAuth->getTimestamp())
			)
			{
				$connection->queryExecute("DELETE FROM b_user_stored_auth WHERE ID = ".$record["ID"]);
			}
			elseif (!$hashId)
			{
				//for domain spreaded external auth we should check only temporary hashes
				if ($onlyTempHash === false || $record["TEMP_HASH"] === "Y")
				{
					$remoteNet = ip2long($policy["STORE_IP_MASK"]) & ip2long($remoteAddress);
					$storedNet = ip2long($policy["STORE_IP_MASK"]) & (float)$record["IP_ADDR"];
					if ($hash === $record["STORED_HASH"] && $remoteNet === $storedNet)
						$hashId = $record["ID"];
				}
			}
		}
		return $hashId;
	}

	public static function getUserByHash($login, $hash)
	{
		if (empty($login))
			throw new Main\ArgumentNullException("login");
		if (empty($hash))
			throw new Main\ArgumentNullException("hash");

		$event = new Main\Event(
			"main",
			"OnBeforeUserLoginByHash",
			array("LOGIN" => $login, "HASH" => $hash)
		);
		$event->send();
		if (($eventResults = $event->getResults()) !== null)
		{
			foreach ($eventResults as $eventResult)
			{
				if ($eventResult->getResultType() === Main\EventResult::ERROR)
					return null;
			}
		}

		$connection = Main\Application::getDbConnection();
		$sqlHelper = $connection->getSqlHelper();

		$sql =
			"SELECT U.ID, U.EXTERNAL_AUTH_ID ".
			"FROM b_user U ".
			"WHERE U.LOGIN = '".$sqlHelper->forSql($login, 50)."' ".
			"   AND U.ACTIVE = 'Y' ";

		$isFound = false;
		$user = null;

		$recordset = $connection->query($sql);
		while ($record = $recordset->fetch())
		{
			$user = new CurrentUser($record["ID"]);
			$isExternal = ($record["EXTERNAL_AUTH_ID"] != '');

			$storedHashId = static::getStoredHashId($user, $hash, $isExternal);
			if ($storedHashId)
			{
				$isFound = true;
				$user->setSessionHash($hash);
				$user->setAuthType(static::AUTHENTICATED_BY_HASH);
				break;
			}

			unset($user);
		}

		$isFound = $isFound && isset($user) && ($user instanceof CurrentUser);

		$event = new \Bitrix\Main\Event(
			"main",
			"OnAfterUserLoginByHash",
			array(
				"LOGIN" => $login,
				"HASH" => $hash,
				"USER_ID" => ($isFound && isset($user) ? $user->getUserId() : null)
			)
		);
		$event->send();

		if (!$isFound && (Config\Option::get("main", "event_log_login_fail", "N") === "Y"))
			\CEventLog::log("SECURITY", "USER_LOGINBYHASH", "main", $login, "Unknown error");

		return $isFound && isset($user) ? $user : null;
	}

	public static function loginByHttp()
	{

	}

	public static function getUserByPassword($login, $password, $passwordIsOriginal = true)
	{
		if (empty($login))
			throw new Main\ArgumentNullException("login");

		$event = new Main\Event(
			"main",
			"OnBeforeUserLogin",
			array(array("LOGIN" => $login, "PASSWORD" => $password, "PASSWORD_ORIGINAL" => $passwordIsOriginal))
		);
		$event->send();
		if (($eventResults = $event->getResults()) !== null)
		{
			foreach ($eventResults as $eventResult)
			{
				if ($eventResult->getResultType() === Main\EventResult::ERROR)
				{
					static::$lastError = $eventResult->getParameters();
					return null;
				}
				elseif ($eventResult->getResultType() === Main\EventResult::SUCCESS)
				{
					if (($resultParams = $eventResult->getParameters()) && is_array($resultParams))
					{
						if (isset($resultParams["LOGIN"]))
							$login = $resultParams["LOGIN"];
						if (isset($resultParams["PASSWORD"]))
							$password = $resultParams["PASSWORD"];
						if (isset($resultParams["PASSWORD_ORIGINAL"]))
							$passwordIsOriginal = $resultParams["PASSWORD_ORIGINAL"];
					}
				}
			}
		}

		$user = null;

		$event = new Main\Event(
			"main",
			"OnUserLoginExternal",
			array(array("LOGIN" => $login, "PASSWORD" => $password, "PASSWORD_ORIGINAL" => $passwordIsOriginal))
		);
		$event->send();
		if (($eventResults = $event->getResults()) !== null)
		{
			foreach ($eventResults as $eventResult)
			{
				if ($eventResult->getResultType() === Main\EventResult::SUCCESS)
				{
					$userId = $eventResult->getParameters();
					if (!Main\Type\Int::isInteger($userId))
						throw new SecurityException();

					$user = new CurrentUser($userId);
					break;
				}
			}
		}

		$connection = Main\Application::getDbConnection();
		$sqlHelper = $connection->getSqlHelper();

		if (is_null($user))
		{
			$sql =
				"SELECT U.ID, U.PASSWORD, U.LOGIN_ATTEMPTS ".
				"FROM b_user U  ".
				"WHERE U.LOGIN = '".$sqlHelper->forSql($login)."' ".
				"	AND (U.EXTERNAL_AUTH_ID IS NULL OR U.EXTERNAL_AUTH_ID = '') ".
				"   AND U.ACTIVE = 'Y' ";
			$userRecordset = $connection->query($sql);
			if ($userRecord = $userRecordset->fetch())
			{
				$userTmp = new CurrentUser($userRecord["ID"]);

				$salt = substr($userRecord["PASSWORD"], 0, -32);
				$passwordFromDb = substr($userRecord["PASSWORD"], -32);

				if ($passwordIsOriginal)
					$passwordFromUser = md5($salt.$password);
				else
					$passwordFromUser = (strlen($password) > 32) ? substr($password, -32) : $password;

				$policy = $userTmp->getPolicy();
				$policyLoginAttempts = intval($policy["LOGIN_ATTEMPTS"]);
				$userLoginAttempts = intval($userRecord["LOGIN_ATTEMPTS"]) + 1;
				if ($policyLoginAttempts > 0 && $userLoginAttempts > $policyLoginAttempts)
				{
//					$_SESSION["BX_LOGIN_NEED_CAPTCHA"] = true;
//					if (!$APPLICATION->captchaCheckCode($_REQUEST["captcha_word"], $_REQUEST["captcha_sid"]))
//					{
//						$passwordUser = false;
//					}
				}

				if ($passwordFromDb === $passwordFromUser)
				{
					$user = $userTmp;

					//update digest hash for http digest authorization
					if ($passwordIsOriginal && Main\Config\Option::get('main', 'use_digest_auth', 'N') == 'Y')
						static::updateDigest($user->getUserId(), $password);
				}
				else
				{
					$connection->query(
						"UPDATE b_user SET ".
						"   LOGIN_ATTEMPTS = ".$userLoginAttempts." ".
						"WHERE ID = ".intval($userRecord["ID"])
					);
				}
			}
		}

		if (is_null($user))
		{
			if ((Main\Config\Option::get("main", "event_log_login_fail", "N") === "Y"))
				\CEventLog::log("SECURITY", "USER_LOGIN", "main", $login, "LOGIN_FAILED");

			return null;
		}

		if ($user->getUserId() !== 1)
		{
			$limitUsersCount = intval(Main\Config\Option::get("main", "PARAM_MAX_USERS", 0));
			if ($limitUsersCount > 0)
			{
				$usersCount = Main\UserTable::getActiveUsersCount();
				if ($usersCount > $limitUsersCount)
				{
					$sql = "SELECT 'x' ".
						"FROM b_user ".
						"WHERE ACTIVE = 'Y' ".
						"   AND ID = ".intval($user->getUserId())." ".
						"   AND LAST_LOGIN IS NULL ";
					$recordset = $connection->query($sql);
					if ($recordset->fetch())
					{
						$user = null;
						static::$lastError = array(
							"CODE" => "LIMIT_USERS_COUNT",
							"MESSAGE" => Main\Localization\Loc::getMessage("LIMIT_USERS_COUNT"),
						);
					}
				}
			}
		}

		if (is_null($user))
		{
			if ((Main\Config\Option::get("main", "event_log_login_fail", "N") === "Y"))
				\CEventLog::log("SECURITY", "USER_LOGIN", "main", $login, "LIMIT_USERS_COUNT");

			return null;
		}

		$user->setAuthType(static::AUTHENTICATED_BY_PASSWORD);

		$event = new \Bitrix\Main\Event(
			"main",
			"OnAfterUserLogin",
			array(array(
				"LOGIN" => $login, "PASSWORD" => $password, "PASSWORD_ORIGINAL" => $passwordIsOriginal,
				"USER_ID" => $user->getUserId()
			))
		);
		$event->send();

		return $user;
	}

	protected function updateDigest($userId, $password)
	{
		if (empty($userId))
			throw new Main\ArgumentNullException("userId");
		if (!Main\Type\Int::isInteger($userId))
			throw new Main\ArgumentTypeException("userId");

		$userId = intval($userId);

		$connection = Main\Application::getDbConnection();
		$sqlHelper = $connection->getSqlHelper();

		$recordset = $connection->query(
			"SELECT U.LOGIN, UD.DIGEST_HA1 ".
			"FROM b_user U ".
			"   LEFT JOIN b_user_digest UD on U.ID = UD.USER_ID ".
			"WHERE U.ID = ".$userId
		);
		if ($record = $recordset->fetch())
		{
			$realm = Main\Config\Configuration::getValue("http_auth_realm");
			if (is_null($realm))
				$realm = "Bitrix Site Manager";

			$digest = md5($record["LOGIN"].':'.$realm.':'.$password);

			if ($record["DIGEST_HA1"] == '')
			{
				//new digest
				$connection->queryExecute(
					"INSERT INTO b_user_digest (USER_ID, DIGEST_HA1) ".
					"VALUES('".$userId."', '".$sqlHelper->forSql($digest)."')"
				);
			}
			else
			{
				//update digest (login, password or realm were changed)
				if ($record["DIGEST_HA1"] !== $digest)
				{
					$connection->queryExecute(
						"UPDATE b_user_digest SET ".
						"   DIGEST_HA1 = '".$sqlHelper->forSql($digest)."' ".
						"WHERE USER_ID = ".$userId
					);
				}
			}
		}
	}

	public static function setAuthentication(CurrentUser $user, $isPersistent = false)
	{
		/** @var $context \Bitrix\Main\HttpContext */
		$context = \Bitrix\Main\Application::getInstance()->getContext();
		$context->setUser($user);

		static::copyToSession($user);

		/** @var $response \Bitrix\Main\HttpResponse */
		$response = $context->getResponse();

		if (!$user->isAuthenticated())
		{
			$cookie = new \Bitrix\Main\Web\Cookie("UIDH", "", time() - 3600);
			$response->addCookie($cookie);
			return;
		}

		$connection = \Bitrix\Main\Application::getDbConnection();
		$sqlHelper = $connection->getSqlHelper();

		$connection->queryExecute(
			"UPDATE b_user SET ".
			"   STORED_HASH = NULL, ".
			"   LAST_LOGIN = ".$sqlHelper->getCurrentDateTimeFunction().", ".
			"   TIMESTAMP_X = TIMESTAMP_X,  ".
			"   LOGIN_ATTEMPTS = 0, ".
			"   TIME_ZONE_OFFSET = ".\CTimeZone::getOffset()." ".
			"WHERE ID = ".$user->getUserId()." "
		);

		$cookie = new \Bitrix\Main\Web\Cookie("LOGIN", $user->getLogin(), time()+60*60*24*30*60);
		$cookie->setSpread((\Bitrix\Main\Config\Option::get("main", "auth_multisite", "N") == "Y") ? \Bitrix\Main\Web\Cookie::SPREAD_SITES : \Bitrix\Main\Web\Cookie::SPREAD_DOMAIN);
		$response->addCookie($cookie);

		if ($isPersistent || \Bitrix\Main\Config\Option::get("main", "auth_multisite", "N") == "Y")
		{
			$hash = $user->getSessionHash();

			/** @var $request \Bitrix\Main\HttpRequest */
			$request = $context->getRequest();

			if ($isPersistent)
				$cookie = new \Bitrix\Main\Web\Cookie("UIDH", $hash, time() + 60 * 60 * 24 * 30 * 60);
			else
				$cookie = new \Bitrix\Main\Web\Cookie("UIDH", $hash, 0);

			$cookie->setSecure(\Bitrix\Main\Config\Option::get("main", "use_secure_password_cookies", "N") == "Y" && $request->isHttps());
			$response->addCookie($cookie);

			$storedId = static::getStoredHashId($user, $hash);
			if ($storedId)
			{
				$connection->queryExecute(
					"UPDATE b_user_stored_auth SET ".
					"	LAST_AUTH = ".$sqlHelper->getCurrentDateTimeFunction().", ".
					"	".(($user->getAuthType() === static::AUTHENTICATED_BY_HASH) ? "" : "TEMP_HASH='".($isPersistent ? "N" : "Y")."', ")." ".
					"	IP_ADDR = '".sprintf("%u", ip2long($request->getRemoteAddress()))."' ".
					"WHERE ID = ".intval($storedId)
				);
			}
			else
			{
				$sqlTmp1 = "";
				$sqlTmp2 = "";
				if ($connection->getType() === "oracle")
				{
					$storedId = $connection->getIdentity("sq_b_user_stored_auth");
					$sqlTmp1 = "ID, ";
					$sqlTmp2 = intval($storedId).", ";
				}

				$sql =
					"INSERT INTO b_user_stored_auth (".$sqlTmp1."USER_ID, DATE_REG, LAST_AUTH, TEMP_HASH, ".
					"   IP_ADDR, STORED_HASH) ".
					"VALUES (".$sqlTmp2.intval($user->getUserId()).", ".$sqlHelper->getCurrentDateTimeFunction().", ".
					"   ".$sqlHelper->getCurrentDateTimeFunction().", '".($isPersistent ? "N" : "Y")."', ".
					"   '".$sqlHelper->forSql(sprintf("%u", ip2long($request->getRemoteAddress())))."', ".
					"   '".$sqlHelper->forSql($hash)."')";
				$connection->queryExecute($sql);

				if ($connection->getType() !== "oracle")
					$storedId = $connection->getIdentity();
			}

			$user->setStoredAuthId($storedId);
		}

		$event = new Main\Event(
			"main",
			"OnUserLogin",
			array("USER" => $user)
		);
		$event->send();

		if (\Bitrix\Main\Config\Option::get("main", "event_log_login_success", "N") === "Y")
			\CEventLog::log("SECURITY", "USER_AUTHORIZE", "main", $user->getUserId());
	}

	public static function getLastError()
	{
		return static::$lastError;
	}
}