Инструменты пользователя

Инструменты сайта


programming:1c-bitrix:ntlm-fake-auth

NTLM псевдо авторизация

<?
#
# Description: http://dev.1c-bitrix.ru/community/blogs/howto/2535.php
#

AddEventHandler("main", "OnAfterUserLogin", "BX_StoreNTLMHash");
function BX_StoreNTLMHash($arFields)
{
	if ($arFields['USER_ID'] > 0)
	{
		$rsUser = CUser::GetList(($by="ID"), ($order="desc"), array("ID"=>$arFields['USER_ID']),array("SELECT"=>array("UF_*")));
		if ($f = $rsUser->Fetch())
		{
			$NTLM = new BX_NTLM();
			if (!array_key_exists($NTLM->uf, $f))
			{
				$obUserField  = new CUserTypeEntity;
				$obUserField->Add(array(
					'ENTITY_ID' => 'USER',
					'FIELD_NAME' => $NTLM->uf,
					'USER_TYPE_ID' => 'string',
					'SORT' => 100,
					'SHOW_FILTER' => 'N',
					'SHOW_IN_LIST' => 'N',
					'EDIT_IN_LIST' => 'N'
					)
				);
				$obUserField->Add(array(
					'ENTITY_ID' => 'USER',
					'FIELD_NAME' => $NTLM->uf_time,
					'USER_TYPE_ID' => 'datetime',
					'SORT' => 150,
					'SHOW_FILTER' => 'N',
					'SHOW_IN_LIST' => 'N',
					'EDIT_IN_LIST' => 'Y'
					)
				);
			}

			$pw_hash = $NTLM->pw_hash($arFields['PASSWORD']);

			$ob = new CUser();
			$ob->Update($arFields['USER_ID'], array(
					$NTLM->uf => $pw_hash,
					$NTLM->uf_time  => GetTime(time(), 'FULL')
				)
			);
		}
	}
}

AddEventHandler("main", "OnBeforeProlog", "BX_NTLMAuth", 1);
function BX_NTLMAuth()
{
	global $USER;
	if (!$USER->IsAuthorized())
	{
		$NTLM = new BX_NTLM();
		if ($NTLM->init())
		{
			$u = $NTLM->Header['user'];
			$d = $NTLM->Header['domain'];

			$rsUser = CUser::GetList(($by="ID"), ($order="desc"), array("LOGIN" => $u, "LOGIN_EXACT_MATCH" => 'Y'),array("SELECT"=>array($NTLM->uf)));
			if ($f = $rsUser->Fetch())
			{
				$lm_resp = $NTLM->lm_resp($NTLM->_chr($f[$NTLM->uf]));
				if ($lm_resp == $NTLM->Header['LM_resp'] && !BX_PasswordExpired($f, $d, $u))
					$_SERVER[COption::GetOptionString('ldap', 'ntlm_varname', 'REMOTE_USER')] = ($d ? $d . '\\' : '') . $u;
			}
		}
	}
}

function BX_PasswordExpired($arParams, $d, $u)
{
	if (!CModule::IncludeModule('ldap'))
		return;

	if (!class_exists('BX_CLDAP'))
	{
		class BX_CLDAP extends CLDAP
		{
			function GetUserFields($arLdapUser, &$departmentCache=FALSE)
			{
				return $arLdapUser;
			}
		}
	}

	$bExpired = false;
	$NTLM = new BX_NTLM();

	$rsUser = CUser::GetList(($by="ID"), ($order="desc"), array("=LOGIN"=>$u),array("SELECT"=>array("UF_*")));
	if (($f = $rsUser->Fetch()) && $f[$NTLM->uf])
	{
		$UF_TIMESTAMP = MakeTimeStamp($f[$NTLM->uf_time]);

		$arFilter = array('ACTIVE' => 'Y');
		if ($d)
			$arFilter['CODE'] = $d;

		$db_ldap_serv = CLdapServer::GetList(Array(), $arFilter);
		while($r = $db_ldap_serv->GetNext())
		{
			$xLDAP = new BX_CLDAP();
			$xLDAP->arFields = $r;
			if($xLDAP->Connect())
			{
				if ($arLdapUser = $xLDAP->FindUser($u, false))
				{
					$dateLargeInt = $arLdapUser['pwdlastset'];
					$secsAfterADEpoch = $dateLargeInt / 10000000;
					$ADToUnixConvertor= (1970-1601) * (365 * 86400 + 5 * 3600 + 48 * 60 + 46);
					$AD_TIMESTAMP = intval($secsAfterADEpoch - $ADToUnixConvertor);

					if ($bExpired = $AD_TIMESTAMP > $UF_TIMESTAMP)
					{
						$ob = new CUser();
						$ob->Update($arParams['ID'], array(
								$NTLM->uf => '',
								$NTLM->uf_time  => GetTime(time(), 'FULL')
							)
						);
					}
					break;
				}
				$xLDAP->Disconnect();
			}
		}
	}
	return $bExpired;
}

class BX_NTLM
{
	function __construct()
	{
		$this->nonce = self::bin_substr(md5($_SERVER['REMOTE_ADDR']),0,8);
		$this->uf = 'UF_NTLM_HASH';
		$this->uf_time = 'UF_NTLM_TIMESTAMP';
	}

	function init()
	{
		if (!function_exists('hash'))
		{
			print ('<h3>`hash` function is not available, PHP 5.1.2 or above required</h3>');
			return false;
		}
		if (!function_exists('mcrypt_encrypt'))
		{
			print ('<h3>`mcrypt` extension is not available</h3>');
			return false;
		}

		if (function_exists('apache_request_headers'))
		{
			$tmp = apache_request_headers();
			$auth = $tmp['Authorization'];
		}
		else
			$auth = $_SERVER['HTTP_AUTHORIZATION'];

		if (isset($auth))
		{
			if ($this->bin_substr($auth,0,5) != 'NTLM ') 
				return false;

			$chain = base64_decode($this->bin_substr($auth, 5));
			if(1 == $o = ord($chain[8]))
			{
				if (ord($chain[13]) == 130) 
				{
					$chain = "NTLMSSP\x00".
						 "\x02"  ."\x00\x00\x00\x00\x00\x00\x00".
						 "\x28\x00"  ."\x00\x00".
						 "\x01\x82"  ."\x00\x00".
						 $this->nonce .
						 "\x00\x00\x00\x00\x00\x00\x00\x00";
					CHTTP::SetStatus('401 Unauthorized');
					header('WWW-Authenticate: NTLM '.base64_encode($chain));
					die();
				}
				else
					return false;
			}
			elseif (3 == $o)
			{
				foreach (array('LM_resp','NT_resp','domain','user','host') as $k=>$v) 
				{
					extract(unpack('vlength/voffset',$this->bin_substr($chain,$k*8+14,4)));
					$val = $this->bin_substr($chain,$offset,$length);
					$this->Header[$v] = $k < 2 ? $this->_ord($val) : $GLOBALS['APPLICATION']->ConvertCharset($val, 'UTF-16LE', SITE_CHARSET);
				}
				return true;
			}
			else
				return false;
		}
		$this->auth();
	}

	function auth()
	{
		CHTTP::SetStatus('401 Unauthorized');
		header('WWW-Authenticate: NTLM');
		echo '<h1>Authentication required!</h1>';
		die();
	}

	function _ord($val)
	{
		$l = function_exists('mb_strlen') ? mb_strlen($val, 'ISO-8859-1') : strlen($val);
		for($i=0;$i<$l;$i++)
		{
			$c = $this->bin_substr($val,$i,1);
			$res .= sprintf("%02x",ord($c));
		}
		return $res;
	}

	function _chr($val)
	{
		$l = function_exists('mb_strlen') ? mb_strlen($val, 'ISO-8859-1') : strlen($val);
		for($i=0;$i<$l;$i+=2)
			$res .= chr(hexdec($this->bin_substr($val,$i,2)));
		return $res;
	}

	function set_odd_parity($byte)
	{
		$parity = 0;
		$ordbyte = ord($byte);
		for ($i = 0; $i < 8; ++$i) 
		{
			if ($ordbyte & 0x01)
				++$parity;
			$ordbyte >>= 1;
		}
		$ordbyte = ord($byte);

		if ($parity % 2 == 0)
		{
			if ($ordbyte & 0x01)
				$ordbyte &= 0xFE;
			else
				$ordbyte |= 0x01;
		}
		return chr($ordbyte);
	}

	function convert_key($in_key)
	{
		$byte = array();
		$result = "";

		$byte[0] = $this->bin_substr($in_key, 0, 1);
		$byte[1] = chr(((ord($this->bin_substr($in_key, 0, 1)) << 7) & 0xFF) | (ord($this->bin_substr($in_key, 1, 1)) >> 1));
		$byte[2] = chr(((ord($this->bin_substr($in_key, 1, 1)) << 6) & 0xFF) | (ord($this->bin_substr($in_key, 2, 1)) >> 2));
		$byte[3] = chr(((ord($this->bin_substr($in_key, 2, 1)) << 5) & 0xFF) | (ord($this->bin_substr($in_key, 3, 1)) >> 3));
		$byte[4] = chr(((ord($this->bin_substr($in_key, 3, 1)) << 4) & 0xFF) | (ord($this->bin_substr($in_key, 4, 1)) >> 4));
		$byte[5] = chr(((ord($this->bin_substr($in_key, 4, 1)) << 3) & 0xFF) | (ord($this->bin_substr($in_key, 5, 1)) >> 5));
		$byte[6] = chr(((ord($this->bin_substr($in_key, 5, 1)) << 2) & 0xFF) | (ord($this->bin_substr($in_key, 6, 1)) >> 6));
		$byte[7] = chr((ord($this->bin_substr($in_key, 6, 1)) << 1) & 0xFF);
		for ($i = 0; $i < 8; ++$i) 
		{
			$byte[$i] = $this->set_odd_parity($byte[$i]);
			$result .= $byte[$i];
		}
		return $result;
	}

	function des_crypt($key, $data, $iv = "\x00\x00\x00\x00\x00\x00\x00\x00")
	{
		return mcrypt_encrypt(MCRYPT_DES, $key, $data, MCRYPT_MODE_CBC, $iv);
	}

	function pw_hash($pw, $raw = false)
	{
		$pw = preg_replace('#.#s','$0'.chr(0),$pw);
		$r = hash('md4', $pw, true) . pack("H10", "0000000000");
		return $raw ? $r : $this->_ord($r);
	}

	function lm_resp($key, $raw = false)
	{
		$cipher1 = $this->des_crypt($this->convert_key($this->bin_substr($key, 0, 7)),$this->nonce);
		$cipher2 = $this->des_crypt($this->convert_key($this->bin_substr($key, 7, 7)),$this->nonce);
		$cipher3 = $this->des_crypt($this->convert_key($this->bin_substr($key, 14, 7)),$this->nonce);
		$r = $cipher1 . $cipher2 . $cipher3;
		return $raw ? $r : $this->_ord($r);
	}

	function bin_substr($a, $b, $c = 9999)
	{
		return function_exists('mb_substr') ? mb_substr($a, $b, $c, 'ISO-8859-1') : substr($a, $b, $c);
	}
}
?>
programming/1c-bitrix/ntlm-fake-auth.txt · Последнее изменение: 2017/03/27 11:26 — artur

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki