diff options
Diffstat (limited to 'src/vm/securitypolicy.cpp')
-rw-r--r-- | src/vm/securitypolicy.cpp | 1283 |
1 files changed, 1283 insertions, 0 deletions
diff --git a/src/vm/securitypolicy.cpp b/src/vm/securitypolicy.cpp new file mode 100644 index 0000000000..fe1da90b8d --- /dev/null +++ b/src/vm/securitypolicy.cpp @@ -0,0 +1,1283 @@ +// Licensed to the .NET Foundation under one or more agreements. +//The .NET Foundation licenses this file to you under the MIT license. +//See the LICENSE file in the project root for more information. + + +#include "common.h" + +#include "security.h" +#include "perfcounters.h" +#include "eventtrace.h" +#include "appdomainstack.inl" + +#ifndef FEATURE_PAL +#include <shlobj.h> +#include <Accctrl.h> +#include <Aclapi.h> +#include "urlmon.h" +#endif // !FEATURE_PAL + +#ifndef CROSSGEN_COMPILE +void *SecurityProperties::operator new(size_t size, LoaderHeap *pHeap) +{ + WRAPPER_NO_CONTRACT; + return pHeap->AllocMem(S_SIZE_T(size)); +} + +void SecurityProperties::operator delete(void *pMem) +{ + LIMITED_METHOD_CONTRACT; + // No action required +} + +#ifdef FEATURE_CAS_POLICY + +// static +CrstStatic SecurityPolicy::s_crstPolicyInit; + +// static +bool SecurityPolicy::s_fPolicyInitialized = false; + +void SecurityPolicy::InitPolicyConfig() +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM();); + } CONTRACTL_END; + + GCX_PREEMP(); + + CrstHolder initializePolicy(&s_crstPolicyInit); + + if (!s_fPolicyInitialized) + { + // Note: These buffers should be at least as big as the longest possible + // string that will be placed into them by the code below. + const size_t cchcache = MAX_LONGPATH + sizeof( W("defaultusersecurity.config.cch") ) / sizeof( WCHAR ) + 1; + const size_t cchconfig = MAX_LONGPATH + sizeof( W("defaultusersecurity.config.cch") ) / sizeof( WCHAR ) + 1; + NewArrayHolder<WCHAR> cache(new WCHAR[cchcache]); + NewArrayHolder<WCHAR> config(new WCHAR[cchconfig]); + + HRESULT hr = SecurityConfig::GetMachineDirectory(config, MAX_LONGPATH); + if (FAILED(hr)) + ThrowHR(hr); + + wcscat_s( config, cchconfig, W("security.config") ); + wcscpy_s( cache, cchcache, config ); + wcscat_s( cache, cchcache, W(".cch") ); + SecurityConfig::InitData( SecurityConfig::MachinePolicyLevel, config, cache ); + + hr = SecurityConfig::GetMachineDirectory(config, MAX_LONGPATH); + if (FAILED(hr)) + ThrowHR(hr); + + wcscat_s( config, cchconfig, W("enterprisesec.config") ); + wcscpy_s( cache, cchcache, config ); + wcscat_s( cache, cchcache, W(".cch") ); + SecurityConfig::InitData( SecurityConfig::EnterprisePolicyLevel, config, cache ); + + BOOL result = SecurityConfig::GetUserDirectory(config, MAX_LONGPATH); + if (result) { + wcscat_s( config, cchconfig, W("security.config") ); + wcscpy_s( cache, cchcache, config ); + wcscat_s( cache, cchcache, W(".cch") ); + SecurityConfig::InitData( SecurityConfig::UserPolicyLevel, config, cache ); + } + + s_fPolicyInitialized = true; + } +} +#endif // FEATURE_CAS_POLICY + +void SecurityPolicy::Start() +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM();); + } CONTRACTL_END; + +#ifndef FEATURE_PAL + // Making sure we are in sync with URLMon + _ASSERTE(URLZONE_LOCAL_MACHINE == LocalMachine); + _ASSERTE(URLZONE_INTRANET == Intranet); + _ASSERTE(URLZONE_TRUSTED == Trusted); + _ASSERTE(URLZONE_INTERNET == Internet); + _ASSERTE(URLZONE_UNTRUSTED == Untrusted); +#endif // !FEATURE_PAL + +#ifdef FEATURE_CAS_POLICY + s_crstPolicyInit.Init(CrstSecurityPolicyInit); + + SecurityConfig::Init(); + + if (Security::IsProcessWideLegacyCasPolicyEnabled()) + { + SecurityPolicy::InitPolicyConfig(); + } + + g_pCertificateCache = new CertificateCache(); +#endif // FEATURE_CAS_POLICY +} + +void SecurityPolicy::Stop() +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM();); + } CONTRACTL_END; + +} + +#ifdef FEATURE_CAS_POLICY +void SecurityPolicy::SaveCache() +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM();); + } CONTRACTL_END; + + Thread *pThread = GetThread(); + if (pThread == NULL) + { + BOOL fRet = FALSE; + EX_TRY + { + // If CLR is hosted, a host can deny a thread during SetupThread call. + if (IsShutdownSpecialThread()) + { + SetupInternalThread(); + } + else + { + SetupThread(); + } + } + EX_CATCH + { + fRet = TRUE; + } + EX_END_CATCH(SwallowAllExceptions); + if (fRet) + { + return; + } + } + + SecurityConfig::SaveCacheData( SecurityConfig::MachinePolicyLevel ); + SecurityConfig::SaveCacheData( SecurityConfig::UserPolicyLevel ); + SecurityConfig::SaveCacheData( SecurityConfig::EnterprisePolicyLevel ); + + SecurityConfig::Cleanup(); +} +#endif + +void QCALLTYPE SecurityPolicy::GetGrantedPermissions(QCall::ObjectHandleOnStack retGranted, QCall::ObjectHandleOnStack retDenied, QCall::StackCrawlMarkHandle stackmark) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + AppDomain* pDomain = NULL; + + Assembly* callerAssembly = SystemDomain::GetCallersAssembly( stackmark, &pDomain ); + _ASSERTE( callerAssembly != NULL); + + IAssemblySecurityDescriptor* pSecDesc = callerAssembly->GetSecurityDescriptor(pDomain); + _ASSERTE( pSecDesc != NULL ); + + { + GCX_COOP(); + + OBJECTREF orDenied; + OBJECTREF orGranted = pSecDesc->GetGrantedPermissionSet(&orDenied); + + retGranted.Set(orGranted); + retDenied.Set(orDenied); + } + + END_QCALL; +} + +#ifdef FEATURE_IMPERSONATION +FCIMPL0(DWORD, SecurityPolicy::GetImpersonationFlowMode) +{ + FCALL_CONTRACT; + return (g_pConfig->ImpersonationMode()); +} +FCIMPLEND +#endif + +void SecurityPolicy::CreateSecurityException(__in_z const char *szDemandClass, DWORD dwFlags, OBJECTREF *pThrowable) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + INJECT_FAULT(COMPlusThrowOM();); + } CONTRACTL_END; + + MAKE_WIDEPTR_FROMUTF8(wszDemandClass, szDemandClass); + + MethodTable * pMT = MscorlibBinder::GetClass(CLASS__SECURITY_EXCEPTION); + +#ifdef FEATURE_CAS_POLICY + MethodTable * pMTSecPerm = MscorlibBinder::GetClass(CLASS__SECURITY_PERMISSION); + + struct _gc { + STRINGREF strDemandClass; + OBJECTREF secPerm; + STRINGREF strPermState; + OBJECTREF secPermType; + OBJECTREF secElement; + } gc; + memset(&gc, 0, sizeof(gc)); + + GCPROTECT_BEGIN(gc); + + gc.strDemandClass = StringObject::NewString(wszDemandClass); + if (gc.strDemandClass == NULL) COMPlusThrowOM(); + // Get the type seen by reflection + gc.secPermType = pMTSecPerm->GetManagedClassObject(); + // Allocate the security exception object + *pThrowable = AllocateObject(pMT); + // Allocate the security permission object + gc.secPerm = AllocateObject(pMTSecPerm); + + // Call the construtor with the correct flag + MethodDescCallSite ctor(METHOD__SECURITY_PERMISSION__CTOR); + ARG_SLOT arg3[2] = { + ObjToArgSlot(gc.secPerm), + (ARG_SLOT)dwFlags + }; + ctor.Call(arg3); + + // Now, get the ToXml method + MethodDescCallSite toXML(METHOD__SECURITY_PERMISSION__TOXML, &gc.secPerm); + ARG_SLOT arg4 = ObjToArgSlot(gc.secPerm); + gc.secElement = toXML.Call_RetOBJECTREF(&arg4); + + MethodDescCallSite toString(METHOD__SECURITY_ELEMENT__TO_STRING, &gc.secElement); + ARG_SLOT arg5 = ObjToArgSlot(gc.secElement); + gc.strPermState = toString.Call_RetSTRINGREF(&arg5); + + MethodDescCallSite exceptionCtor(METHOD__SECURITY_EXCEPTION__CTOR); + + ARG_SLOT arg6[4] = { + ObjToArgSlot(*pThrowable), + ObjToArgSlot(gc.strDemandClass), + ObjToArgSlot(gc.secPermType), + ObjToArgSlot(gc.strPermState), + }; + exceptionCtor.Call(arg6); + + GCPROTECT_END(); +#else // FEATURE_CAS_POLICY + + UNREFERENCED_PARAMETER(szDemandClass); + UNREFERENCED_PARAMETER(dwFlags); + + // Allocate the security exception object + *pThrowable = AllocateObject(pMT); + CallDefaultConstructor(*pThrowable); + +#endif // FEATURE_CAS_POLICY +} + +DECLSPEC_NORETURN void SecurityPolicy::ThrowSecurityException(__in_z const char *szDemandClass, DWORD dwFlags) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM();); + } CONTRACTL_END; + + GCX_COOP(); + + struct _gc { + OBJECTREF throwable; + } gc; + memset(&gc, 0, sizeof(gc)); + + GCPROTECT_BEGIN(gc); + + CreateSecurityException(szDemandClass, dwFlags, &gc.throwable); + COMPlusThrow(gc.throwable); + + GCPROTECT_END(); +} + +#ifdef FEATURE_CAS_POLICY +//----------------------------------------------------------------------------------------- +// +// Fire an ETW event to indicate that an evidence object has been generated for an assembly +// +// Arguments: +// type - Type of evidence that was generated +// pPEFile - PEFile for the assembly the evidence was for +// + +// static +void SecurityPolicy::TraceEvidenceGeneration(EvidenceType type, PEFile *pPEFile) +{ + CONTRACTL + { + STANDARD_VM_CHECK; + PRECONDITION(CheckPointer(pPEFile)); + PRECONDITION(type >= kAssemblySupplied && type <= kZone); + } + CONTRACTL_END; + + const SString& strPath = pPEFile->GetILimage()->GetPath(); + FireEtwEvidenceGenerated(type, + GetThread()->GetDomain()->GetId().m_dwId, + strPath.IsEmpty() ? W("") : strPath.GetUnicode(), + GetClrInstanceId()); +} + +// Called if CAS policy is not enabled, but we either have a host or a simple sandbox domain which will +// determine the grant set of some evidence. +OBJECTREF SecurityPolicy::ResolveGrantSet(OBJECTREF evidence, DWORD *pdwSpecialFlags, BOOL fCheckExecutionPermission) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(!GetAppDomain()->GetSecurityDescriptor()->IsLegacyCasPolicyEnabled()); + PRECONDITION(CheckPointer(pdwSpecialFlags)); + } + CONTRACTL_END; + + struct + { + OBJECTREF evidence; + OBJECTREF grantSet; + } + gc; + ZeroMemory(&gc, sizeof(gc)); + + gc.evidence = evidence; + + GCPROTECT_BEGIN(gc); + + MethodDescCallSite resolve(METHOD__SECURITY_ENGINE__RESOLVE_GRANT_SET); + + ARG_SLOT args[3]; + args[0] = ObjToArgSlot(gc.evidence); + args[1] = PtrToArgSlot(pdwSpecialFlags); + args[2] = BoolToArgSlot(fCheckExecutionPermission); + + gc.grantSet = resolve.Call_RetOBJECTREF(args); + + GCPROTECT_END(); + + return gc.grantSet; +} + +// Resolve legacy CAS policy +OBJECTREF SecurityPolicy::ResolveCasPolicy(OBJECTREF evidence, + OBJECTREF reqdPset, + OBJECTREF optPset, + OBJECTREF denyPset, + OBJECTREF* grantdenied, + DWORD* dwSpecialFlags, + BOOL checkExecutionPermission) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + INJECT_FAULT(COMPlusThrowOM();); + PRECONDITION(GetAppDomain()->GetSecurityDescriptor()->IsLegacyCasPolicyEnabled()); + PRECONDITION(SecurityPolicy::s_fPolicyInitialized); + PRECONDITION(CheckPointer(dwSpecialFlags)); + } CONTRACTL_END; + + + OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED); + + // If we got here, then we are going to do at least one security + // check. Make sure security is initialized. + + struct _gc { + OBJECTREF reqdPset; // Required Requested Permissions + OBJECTREF optPset; // Optional Requested Permissions + OBJECTREF denyPset; // Denied Permissions + OBJECTREF evidence; // Object containing evidence + OBJECTREF refRetVal; + } gc; + ZeroMemory(&gc, sizeof(gc)); + gc.evidence = evidence; + gc.reqdPset = reqdPset; + gc.denyPset = denyPset; + gc.optPset = optPset; + + GCPROTECT_BEGIN(gc); + + MethodDescCallSite resolvePolicy(METHOD__SECURITY_MANAGER__RESOLVE_CAS_POLICY); + + ARG_SLOT args[7]; + args[0] = ObjToArgSlot(gc.evidence); + args[1] = ObjToArgSlot(gc.reqdPset); + args[2] = ObjToArgSlot(gc.optPset); + args[3] = ObjToArgSlot(gc.denyPset); + args[4] = PtrToArgSlot(grantdenied); + args[5] = PtrToArgSlot(dwSpecialFlags); + args[6] = BoolToArgSlot(checkExecutionPermission); + + { + // Elevate thread's allowed loading level. This can cause load failures if assemblies loaded from this point on require + // any assemblies currently being loaded. + OVERRIDE_LOAD_LEVEL_LIMIT(FILE_ACTIVE); + // call policy resolution routine in managed code + gc.refRetVal = resolvePolicy.Call_RetOBJECTREF(args); + } + + GCPROTECT_END(); + return gc.refRetVal; +} +#endif // FEATURE_CAS_POLICY + +#endif // CROSSGEN_COMPILE + +BOOL SecurityPolicy::CanSkipVerification(DomainAssembly * pAssembly) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM();); + PRECONDITION(CheckPointer(pAssembly)); + } CONTRACTL_END; + + BOOL canSkipVerification = TRUE; + if (!pAssembly->IsSystem()) + { + AssemblySecurityDescriptor *pSec; + { + GCX_COOP(); + pSec = static_cast<AssemblySecurityDescriptor*>(pAssembly->GetSecurityDescriptor()); + } + _ASSERTE(pSec); + if (pSec) + { + canSkipVerification = pSec->CanSkipVerification(); + } + else + { + canSkipVerification = FALSE; + } + } + + return canSkipVerification; +} + +BOOL SecurityPolicy::CanCallUnmanagedCode(Module *pModule) +{ + CONTRACTL { + THROWS; + MODE_ANY; + PRECONDITION(CheckPointer(pModule)); + INJECT_FAULT(COMPlusThrowOM();); + } CONTRACTL_END; + + SharedSecurityDescriptor *pSharedSecDesc = static_cast<SharedSecurityDescriptor*>(pModule->GetAssembly()->GetSharedSecurityDescriptor()); + if (pSharedSecDesc) + return pSharedSecDesc->CanCallUnmanagedCode(); + + AssemblySecurityDescriptor *pSec = static_cast<AssemblySecurityDescriptor*>(pModule->GetSecurityDescriptor()); + _ASSERTE(pSec); + return pSec->CanCallUnmanagedCode(); +} + +#ifndef CROSSGEN_COMPILE + +#ifdef FEATURE_CAS_POLICY +SecZone QCALLTYPE SecurityPolicy::CreateFromUrl(LPCWSTR wszUrl) +{ + QCALL_CONTRACT; + + SecZone dwZone = NoZone; + + BEGIN_QCALL; + + if (wszUrl != NULL) + { + dwZone = SecurityPolicy::MapUrlToZone(wszUrl); + } + + END_QCALL; + + return dwZone; +} + +HRESULT +GetSecurityPolicyRegKey( + __out WCHAR **ppszSecurityPolicy) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + INJECT_FAULT(return E_OUTOFMEMORY); + } + CONTRACTL_END; + + DWORD dwLen = 0; + + HRESULT hr = g_pCLRRuntime->GetVersionString(NULL, &dwLen); + if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) + return hr; + + size_t bufSize = _countof(FRAMEWORK_REGISTRY_KEY_W) + 1 + dwLen + _countof(KEY_COM_SECURITY_POLICY); + NewArrayHolder<WCHAR> key(new(nothrow) WCHAR[bufSize]); + if (key == NULL) + return E_OUTOFMEMORY; + wcscpy_s(key, bufSize, FRAMEWORK_REGISTRY_KEY_W W("\\")); + + hr = g_pCLRRuntime->GetVersionString(key + NumItems(FRAMEWORK_REGISTRY_KEY_W), &dwLen); + if (FAILED(hr)) + return hr; + + size_t offset = _countof(FRAMEWORK_REGISTRY_KEY_W)+dwLen-1; + wcscpy_s(key + offset, bufSize - offset, KEY_COM_SECURITY_POLICY); + key.SuppressRelease(); + *ppszSecurityPolicy = key; + return S_OK; +} // GetSecurityPolicyRegKey + +HRESULT SecurityPolicy::ApplyCustomZoneOverride(SecZone *pdwZone) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + PRECONDITION(*pdwZone >= NumZones); + INJECT_FAULT(return E_OUTOFMEMORY); + } + CONTRACTL_END; + + NewArrayHolder<WCHAR> key(NULL); + HRESULT hr = GetSecurityPolicyRegKey(&key); + if (FAILED(hr)) + return hr; + if (REGUTIL::GetLong(KEY_COM_SECURITY_ZONEOVERRIDE, 0, key, HKEY_POLICY_ROOT) == 1) + *pdwZone=Internet; + return S_OK; +} // ApplyCustomZoneOverride + +//--------------------------------------------------------------------------------------- +// +// Determine which security zone a URL belongs to +// +// Arguments: +// wszUrl - URL to get zone information about +// +// Return Value: +// Security zone the URL belongs to +// +// Notes: +// If the runtime cannot map the URL, we'll return NoZone. A mapping to a zone that the VM doesn't +// know about will cause us to check the TreatCustomZonesAsInternetZone registry key and potentially +// map it back to the Internet zone. +// + +// static +SecZone SecurityPolicy::MapUrlToZone(__in_z LPCWSTR wszUrl) +{ + CONTRACTL + { + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(wszUrl != NULL); + } + CONTRACTL_END; + + SecZone dwZone = NoZone; + + ReleaseHolder<IInternetSecurityManager> securityManager = NULL; + HRESULT hr = CoInternetCreateSecurityManager(NULL, &securityManager, 0); + + if (SUCCEEDED(hr)) + { + _ASSERTE(sizeof(SecZone) == sizeof(DWORD)); + hr = securityManager->MapUrlToZone(wszUrl, reinterpret_cast<DWORD *>(&dwZone), 0); + + if (SUCCEEDED(hr)) + { + // if this is a custom zone, see if the user wants us to map it back to the Internet zone + if (dwZone >= NumZones) + { + SecZone dwMappedZone = dwZone; + hr = ApplyCustomZoneOverride(&dwMappedZone); + if (SUCCEEDED(hr)) + { + dwZone = dwMappedZone; + } + } + } + else + { + dwZone = NoZone; + } + } + + return dwZone; +} +#endif //FEATURE_CAS_POLICY + +BOOL QCALLTYPE SecurityPolicy::IsLocalDrive(LPCWSTR wszPath) +{ + QCALL_CONTRACT; + + BOOL retVal = FALSE; + +#ifndef FEATURE_PAL + BEGIN_QCALL; + + WCHAR rootPath[4]; + ZeroMemory( rootPath, sizeof( rootPath ) ); + + rootPath[0] = wszPath[0]; + wcscat_s( rootPath, COUNTOF(rootPath), W(":\\") ); + + UINT driveType = WszGetDriveType( rootPath ); + retVal = + (driveType == DRIVE_REMOVABLE || + driveType == DRIVE_FIXED || + driveType == DRIVE_CDROM || + driveType == DRIVE_RAMDISK); + + END_QCALL; + +#else // !FEATURE_PAL + retVal = TRUE; +#endif // !FEATURE_PAL + + return retVal; +} + +void QCALLTYPE SecurityPolicy::_GetLongPathName(LPCWSTR wszPath, QCall::StringHandleOnStack retLongPath) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + +#if !defined(PLATFORM_UNIX) + PathString wszBuffer; + + if (SecurityPolicy::GetLongPathNameHelper( wszPath, wszBuffer ) != 0) + { + retLongPath.Set( wszBuffer.GetUnicode() ); + } +#endif // !PLATFORM_UNIX + + END_QCALL; +} + +#if !defined(PLATFORM_UNIX) +size_t GetLongPathNameHelperthatThrows(const WCHAR* wszShortPath, SString& wszBuffer) +{ + CONTRACTL{ + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } CONTRACTL_END; + + DWORD size = WszGetLongPathName(wszShortPath, wszBuffer); + + if (size == 0) + { + // We have to deal with files that do not exist so just + // because GetLongPathName doesn't give us anything doesn't + // mean that we can give up. We iterate through the input + // trying GetLongPathName on every subdirectory until + // it succeeds or we run out of string. + + size_t len = wcslen(wszShortPath); + NewArrayHolder<WCHAR> wszIntermediateBuffer = new (nothrow) WCHAR[len + 1]; + + if (wszIntermediateBuffer == NULL) + { + return 0; + } + + wcscpy_s(wszIntermediateBuffer, len + 1, wszShortPath); + + size_t index = len; + + do + { + while (index > 0 && (wszIntermediateBuffer[index - 1] != W('\\') && wszIntermediateBuffer[index - 1] != W('/'))) + --index; + + if (index == 0) + break; + +#ifdef _PREFAST_ +#pragma prefast(push) +#pragma prefast(disable:26001, "suppress prefast warning about underflow by doing index-1 which is checked above.") +#endif // _PREFAST_ + + wszIntermediateBuffer[index - 1] = W('\0'); + +#ifdef _PREFAST_ +#pragma prefast(pop) +#endif + + size = WszGetLongPathName(wszIntermediateBuffer, wszBuffer); + + if (size != 0) + { + + int sizeBuffer = wszBuffer.GetCount(); + + if (wszBuffer[sizeBuffer - 1] != W('\\') && wszBuffer[sizeBuffer - 1] != W('/')) + wszBuffer.Append(W("\\")); + + wszBuffer.Append(&wszIntermediateBuffer[index]); + + + return (DWORD)wszBuffer.GetCount(); + + } + } while (true); + + return 0; + } + else + { + return (DWORD)wszBuffer.GetCount(); + } +} +size_t SecurityPolicy::GetLongPathNameHelper(const WCHAR* wszShortPath, SString& wszBuffer) +{ + CONTRACTL{ + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } CONTRACTL_END; + + HRESULT hr = S_OK; + size_t retval = 0; + + EX_TRY + { + retval = GetLongPathNameHelperthatThrows(wszShortPath,wszBuffer); + } + EX_CATCH_HRESULT(hr); + + if (hr != S_OK) + { + retval = 0; + } + + return retval; +} + +#endif // !PLATFORM_UNIX + +void QCALLTYPE SecurityPolicy::GetDeviceName(LPCWSTR wszDriveLetter, QCall::StringHandleOnStack retDeviceName) +{ + QCALL_CONTRACT; + +#if !defined(FEATURE_CORECLR) + BEGIN_QCALL; + + WCHAR networkName[MAX_LONGPATH]; + DWORD networkNameSize = MAX_LONGPATH; + ZeroMemory( networkName, sizeof( networkName ) ); + + UINT driveType = WszGetDriveType( wszDriveLetter ); + if (driveType == DRIVE_REMOVABLE || + driveType == DRIVE_FIXED || + driveType == DRIVE_CDROM || + driveType == DRIVE_RAMDISK) + { + retDeviceName.Set( wszDriveLetter ); + goto lExit; + } + + if (WszWNetGetConnection(wszDriveLetter, networkName, &networkNameSize) != NO_ERROR) + { + goto lExit; + } + + retDeviceName.Set( networkName ); + +lExit: ; + + END_QCALL; +#endif // !FEATURE_CORECLR +} + +#ifdef FEATURE_CAS_POLICY + +// +// Fire the ETW event that signals that a specific type of evidence has been created +// +// Arguments: +// pPEFile - PEFile the evidence was generated for +// type - type of evidence generated +// + +// static +void QCALLTYPE SecurityPolicy::FireEvidenceGeneratedEvent(PEFile *pPEFile, + EvidenceType type) +{ + CONTRACTL + { + QCALL_CHECK; + PRECONDITION(CheckPointer(pPEFile)); + } + CONTRACTL_END; + + BEGIN_QCALL; + + TraceEvidenceGeneration(type, pPEFile); + + END_QCALL; +} + +// static + +void QCALLTYPE SecurityPolicy::GetEvidence(QCall::AssemblyHandle pAssembly, QCall::ObjectHandleOnStack retEvidence) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + IAssemblySecurityDescriptor *pSecDesc = pAssembly->GetSecurityDescriptor(); + + _ASSERTE(pSecDesc->GetDomain() == GetAppDomain()); + + GCX_COOP(); + if (pSecDesc->IsEvidenceComputed()) + retEvidence.Set(pSecDesc->GetAdditionalEvidence()); + else + retEvidence.Set(pSecDesc->GetEvidence()); + + END_QCALL; +} + +//--------------------------------------------------------------------------------------- +// +// Determine if an evidence collection has a delay generated strong name evidence object +// which was used during the process of demand evaluation. +// +// Arguments: +// orEvidence - evidence collection to examine +// +// Return Value: +// true if orEvidence contains unverified strong name evidence which has been used to generate a grant, +// false if orEvidence does not contain strong name evidence or that evidence was verified / not used +// + +// static +BOOL SecurityPolicy::WasStrongNameEvidenceUsed(OBJECTREF orEvidence) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + // If we don't have any evidence, then there isn't any strong name evidence, and therefore it couldn't + // have been used. + if (orEvidence == NULL) + { + return FALSE; + } + + BOOL fStrongNameEvidenceWasUsed = FALSE; + + GCPROTECT_BEGIN(orEvidence); + + MethodDescCallSite wasSnEvidenceUsed(METHOD__EVIDENCE__WAS_STRONGNAME_EVIDENCE_USED); + + ARG_SLOT args[] = { ObjToArgSlot(orEvidence) }; + fStrongNameEvidenceWasUsed = !!wasSnEvidenceUsed.Call_RetBool(args); + + GCPROTECT_END(); + + return fStrongNameEvidenceWasUsed; +} +#endif // FEATURE_CAS_POLICY + +FCIMPL0(void, SecurityPolicy::IncrementOverridesCount) +{ + FCALL_CONTRACT; + + Thread *pThread = GetThread(); + pThread->IncrementOverridesCount(); +} +FCIMPLEND + +FCIMPL0(void, SecurityPolicy::DecrementOverridesCount) +{ + FCALL_CONTRACT; + + Thread *pThread = GetThread(); + pThread->DecrementOverridesCount(); +} +FCIMPLEND + +FCIMPL0(void, SecurityPolicy::IncrementAssertCount) +{ + FCALL_CONTRACT; + + Thread *pThread = GetThread(); + pThread->IncrementAssertCount(); +} +FCIMPLEND + +FCIMPL0(void, SecurityPolicy::DecrementAssertCount) +{ + FCALL_CONTRACT; + + Thread *pThread = GetThread(); + pThread->DecrementAssertCount(); +} +FCIMPLEND + +#ifdef FEATURE_CAS_POLICY +// +// Evidence QCalls +// + +//--------------------------------------------------------------------------------------- +// +// Get the assembly level permission requests +// +// Arguments: +// pAssembly - Assembly to get the declarative security of +// retMinimumPermissions - [out] RequestMinimum set of the assembly +// retOptionalPermissions - [out] RequestOptional set of the assembly +// retRefusedPermissions - [out] RequestRefuse set of the assembly +// + +// static +void QCALLTYPE SecurityPolicy::GetAssemblyPermissionRequests(QCall::AssemblyHandle pAssembly, + QCall::ObjectHandleOnStack retMinimumPermissions, + QCall::ObjectHandleOnStack retOptionalPermissions, + QCall::ObjectHandleOnStack retRefusedPermissions) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + + TraceEvidenceGeneration(kPermissionRequest, pAssembly->GetFile()); + AssemblySecurityDescriptor *pSecurityDescriptor = static_cast<AssemblySecurityDescriptor*>(pAssembly->GetSecurityDescriptor()); + + _ASSERTE(pSecurityDescriptor->GetDomain()->GetSecurityDescriptor()->IsLegacyCasPolicyEnabled()); + + struct + { + OBJECTREF objMinimumPermissions; + OBJECTREF objOptionalPermissions; + OBJECTREF objRefusedPermissions; + } + gc; + ZeroMemory(&gc, sizeof(gc)); + + GCX_COOP(); + GCPROTECT_BEGIN(gc); + + gc.objMinimumPermissions = pSecurityDescriptor->GetRequestedPermissionSet(&gc.objOptionalPermissions, + &gc.objRefusedPermissions); + + retMinimumPermissions.Set(gc.objMinimumPermissions); + retOptionalPermissions.Set(gc.objOptionalPermissions); + retRefusedPermissions.Set(gc.objRefusedPermissions); + + GCPROTECT_END(); + + END_QCALL; +} + +//--------------------------------------------------------------------------------------- +// +// Get the serialized evidence stream from an assembly +// +// Arguments: +// pPEFile - PEFile to load the evidence stream from +// retSerializedEvidence - [out] contents of the serialized evidence +// + +// static +void QCALLTYPE SecurityPolicy::GetAssemblySuppliedEvidence(PEFile *pPEFile, + QCall::ObjectHandleOnStack retSerializedEvidence) +{ + CONTRACTL + { + QCALL_CHECK; + PRECONDITION(CheckPointer(pPEFile)); + } + CONTRACTL_END; + + BEGIN_QCALL; + + DWORD cbResource; + BYTE *pbResource; + + // Load the resource from the PE file. We do not need to free this memory, since we're getting a direct + // pointer into the PE contents rather than a buffer. + TraceEvidenceGeneration(kAssemblySupplied, pPEFile); + BOOL fFoundSerializedEvidence = pPEFile->GetResource("Security.Evidence", + &cbResource, + &pbResource, + NULL, + NULL, + NULL, + NULL, + FALSE, + TRUE, + NULL, + NULL); + + if (fFoundSerializedEvidence) + { + retSerializedEvidence.SetByteArray(pbResource, cbResource); + } + + END_QCALL; +} + +//--------------------------------------------------------------------------------------- +// +// Get the zone and URL that the PEFile was loaded from +// +// Arguments: +// pPEFile - PEFile to load the evidence stream from +// pZone - [out] SecurityZone the file was loaded from +// retUrl - [out] URL the file was loaded from + +// static +void QCALLTYPE SecurityPolicy::GetLocationEvidence(PEFile *pPEFile, + SecZone *pZone, + QCall::StringHandleOnStack retUrl) +{ + CONTRACTL + { + QCALL_CHECK; + PRECONDITION(CheckPointer(pPEFile)); + } + CONTRACTL_END; + + BEGIN_QCALL; + + StackSString ssCodeBase; + BYTE pbUniqueID[MAX_SIZE_SECURITY_ID]; + DWORD cbUniqueID = COUNTOF(pbUniqueID); + + // The location information is used to create Site, Url, and Zone evidence so fire all three events + TraceEvidenceGeneration(kSite, pPEFile); + TraceEvidenceGeneration(kUrl, pPEFile); + TraceEvidenceGeneration(kZone, pPEFile); + + pPEFile->GetSecurityIdentity(ssCodeBase, pZone, 0, pbUniqueID, &cbUniqueID); + + retUrl.Set(ssCodeBase); + + END_QCALL; +} + +//--------------------------------------------------------------------------------------- +// +// Get the X.509 certificate that the PE file's Authenticode signature was created with +// +// Arguments: +// pPEFile - PEFile to load the evidence stream from +// retCertificate - [out] certificate that signed the file + +// static +void QCALLTYPE SecurityPolicy::GetPublisherCertificate(PEFile *pPEFile, + QCall::ObjectHandleOnStack retCertificate) +{ + CONTRACTL + { + QCALL_CHECK; + PRECONDITION(CheckPointer(pPEFile)); + } + CONTRACTL_END; + + BEGIN_QCALL; + + TraceEvidenceGeneration(kPublisher, pPEFile); + COR_TRUST *pAuthenticodeSignature = pPEFile->GetAuthenticodeSignature(); + if (pAuthenticodeSignature != NULL && pAuthenticodeSignature->pbSigner != NULL) + { + retCertificate.SetByteArray(pAuthenticodeSignature->pbSigner, pAuthenticodeSignature->cbSigner); + } + + END_QCALL; +} + +//--------------------------------------------------------------------------------------- +// +// Get the components of an assembly's strong name to generate strong name evidence with +// +// Arguments: +// pAssembly - assembly to get the strong name of +// retPublicKeyBlob - [out] public component of the key the assembly is signed with +// retSimpleName - [out] simple name of the file +// piMajorVersion - [out] major version +// piMinorVersion - [out] minor version +// piBuild - [out] build +// piRevision - [out] revision +// +// Notes: +// retPublicKeyBlob will be null for a simply named assembly +// + +// static +void QCALLTYPE SecurityPolicy::GetStrongNameInformation(QCall::AssemblyHandle pAssembly, + QCall::ObjectHandleOnStack retPublicKeyBlob, + QCall::StringHandleOnStack retSimpleName, + USHORT *piMajorVersion, + USHORT *piMinorVersion, + USHORT *piBuild, + USHORT *piRevision) +{ + CONTRACTL + { + QCALL_CHECK; + PRECONDITION(CheckPointer(piMajorVersion)); + PRECONDITION(CheckPointer(piMinorVersion)); + PRECONDITION(CheckPointer(piBuild)); + PRECONDITION(CheckPointer(piRevision)); + } + CONTRACTL_END; + + BEGIN_QCALL; + + PEAssembly *pPEAssembly = pAssembly->GetFile(); + TraceEvidenceGeneration(kStrongName, pPEAssembly); + + DWORD cbPublicKey; + const BYTE *pbPublicKey = reinterpret_cast<const BYTE*>(pPEAssembly->GetPublicKey(&cbPublicKey)); + + if (pbPublicKey != NULL && cbPublicKey > 0) + { + pPEAssembly->GetVersion(piMajorVersion, piMinorVersion, piBuild, piRevision); + retPublicKeyBlob.SetByteArray(pbPublicKey, cbPublicKey); + retSimpleName.Set(pPEAssembly->GetSimpleName()); + } + else + { + GCX_COOP(); + retPublicKeyBlob.Set(NULL); + } + + END_QCALL; +} + +#endif // FEATURE_CAS_POLICY + +#ifdef FEATURE_FUSION +static void GetFusionNameFromAssemblyQualifiedTypeName(LPCWSTR pAssemblyQualifedTypeName, IAssemblyName ** ppFusionName) +{ + STANDARD_VM_CONTRACT; + + StackSString ssAssemblyQualifedTypeName(pAssemblyQualifedTypeName); + StackSString ssAssemblyName; + + SString::Iterator iter = ssAssemblyQualifedTypeName.Begin(); + + if (ssAssemblyQualifedTypeName.Find( iter, ',' )) + { + iter++; + while (*iter == ' ' ) + iter++; + + ssAssemblyName.Set( ssAssemblyQualifedTypeName, + iter, + ssAssemblyQualifedTypeName.End() ); +} + + StackScratchBuffer sBuffer; + AssemblySpec spec; + spec.Init(ssAssemblyName.GetANSI(sBuffer)); + + IfFailThrow(spec.CreateFusionName(ppFusionName)); +} +#endif // FEATURE_FUSION + +BOOL QCALLTYPE SecurityPolicy::IsSameType(LPCWSTR pLeft, LPCWSTR pRight) +{ + QCALL_CONTRACT; + + BOOL bEqual = FALSE; + + BEGIN_QCALL; + +// @telesto: Is this #ifdef-#else-#endif required anymore? Used to be needed when security was bypassing +// loader and accessing Fusion interfaces. Seems like that's been fixed to use GetFusionNameFrom... +#ifdef FEATURE_FUSION + + AppDomain* pDomain = GetAppDomain(); + IApplicationContext* pAppCtx = pDomain->GetFusionContext(); + + _ASSERTE( pAppCtx != NULL && "Fusion context not setup yet" ); + + SafeComHolderPreemp<IAssemblyName> pAssemblyNameLeft; + SafeComHolderPreemp<IAssemblyName> pAssemblyNameRight; + + GetFusionNameFromAssemblyQualifiedTypeName(pLeft, &pAssemblyNameLeft); + GetFusionNameFromAssemblyQualifiedTypeName(pRight, &pAssemblyNameRight); + + SafeComHolderPreemp<IAssemblyName> pAssemblyNamePostPolicyLeft; + SafeComHolderPreemp<IAssemblyName> pAssemblyNamePostPolicyRight; + + if (FAILED(PreBindAssembly(pAppCtx, pAssemblyNameLeft, NULL, &pAssemblyNamePostPolicyLeft, NULL)) || + FAILED(PreBindAssembly(pAppCtx, pAssemblyNameRight, NULL, &pAssemblyNamePostPolicyRight, NULL))) + { + // version-agnostic comparison. + bEqual = pAssemblyNameLeft->IsEqual(pAssemblyNameRight, ASM_CMPF_NAME | ASM_CMPF_PUBLIC_KEY_TOKEN | ASM_CMPF_CULTURE) == S_OK; + } + else + { + // version-agnostic comparison. + bEqual = pAssemblyNamePostPolicyLeft->IsEqual(pAssemblyNamePostPolicyRight, ASM_CMPF_NAME | ASM_CMPF_PUBLIC_KEY_TOKEN | ASM_CMPF_CULTURE) == S_OK; + } +#else // FEATURE_FUSION + bEqual=TRUE; +#endif // FEATURE_FUSION + + END_QCALL; + + return bEqual; +} + +FCIMPL1(FC_BOOL_RET, SecurityPolicy::SetThreadSecurity, CLR_BOOL fThreadSecurity) +{ + FCALL_CONTRACT; + + Thread* pThread = GetThread(); + BOOL inProgress = pThread->IsSecurityStackwalkInProgess(); + pThread->SetSecurityStackwalkInProgress(fThreadSecurity); + FC_RETURN_BOOL(inProgress); +} +FCIMPLEND + +FCIMPL0(FC_BOOL_RET, SecurityPolicy::IsDefaultThreadSecurityInfo) +{ + FCALL_CONTRACT; + + FC_RETURN_BOOL(SecurityStackWalk::HasFlagsOrFullyTrusted(0)); +} +FCIMPLEND + +#endif // CROSSGEN_COMPILE |