summaryrefslogtreecommitdiff
path: root/src/vm/securitytransparentassembly.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/securitytransparentassembly.cpp')
-rw-r--r--src/vm/securitytransparentassembly.cpp1819
1 files changed, 1819 insertions, 0 deletions
diff --git a/src/vm/securitytransparentassembly.cpp b/src/vm/securitytransparentassembly.cpp
new file mode 100644
index 0000000000..4a23c276d8
--- /dev/null
+++ b/src/vm/securitytransparentassembly.cpp
@@ -0,0 +1,1819 @@
+// 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.
+//--------------------------------------------------------------------------
+// securityTransparentAssembly.cpp
+//
+// Implementation for transparent code feature
+//
+//--------------------------------------------------------------------------
+
+
+#include "common.h"
+#include "field.h"
+#include "securitydeclarative.h"
+#include "security.h"
+#include "customattribute.h"
+#include "securitytransparentassembly.h"
+#include "securitymeta.h"
+#include "typestring.h"
+#include "comdelegate.h"
+
+#if defined(FEATURE_PREJIT)
+#include "compile.h"
+#endif
+
+#ifdef _DEBUG
+//
+// In debug builds of the CLR, we support a mode where transparency errors are not enforced with exceptions; instead
+// they are written to the CLR debug log. This allows us to migrate tests from the v2 to the v4 transparency model by
+// allowing test runs to continue to the end of the run, and keeping a log file of which assemblies need migration.
+//
+
+// static
+void SecurityTransparent::LogTransparencyError(Assembly *pAssembly, const LPCSTR szError)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pAssembly));
+ PRECONDITION(CheckPointer(szError));
+ PRECONDITION(g_pConfig->LogTransparencyErrors());
+ }
+ CONTRACTL_END;
+
+ const SString &strAssemblyName = pAssembly->GetManifestModule()->GetPath();
+
+ LOG((LF_SECURITY,
+ LL_INFO1000,
+ "Security Transparency Violation: Assembly '%S': %s\n",
+ strAssemblyName.GetUnicode(),
+ szError));
+}
+
+// static
+void SecurityTransparent::LogTransparencyError(MethodTable *pMT, const LPCSTR szError)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pMT));
+ PRECONDITION(CheckPointer(szError));
+ PRECONDITION(g_pConfig->LogTransparencyErrors());
+ }
+ CONTRACTL_END;
+
+ Assembly *pAssembly = pMT->GetAssembly();
+ const SString &strAssemblyName = pAssembly->GetManifestModule()->GetPath();
+
+ LOG((LF_SECURITY,
+ LL_INFO1000,
+ "Security Transparency Violation: Assembly '%S' - Type '%s': %s\n",
+ strAssemblyName.GetUnicode(),
+ pMT->GetDebugClassName(),
+ szError));
+}
+
+// static
+void SecurityTransparent::LogTransparencyError(MethodDesc *pMD, const LPCSTR szError, MethodDesc *pTargetMD /* = NULL */)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pMD));
+ PRECONDITION(CheckPointer(szError));
+ PRECONDITION(g_pConfig->LogTransparencyErrors());
+ }
+ CONTRACTL_END;
+
+ Assembly *pAssembly = pMD->GetAssembly();
+ const SString &strAssemblyName = pAssembly->GetManifestModule()->GetPath();
+
+ if (pTargetMD == NULL)
+ {
+ LOG((LF_SECURITY,
+ LL_INFO1000,
+ "Security Transparency Violation: Assembly '%S' - Method '%s::%s': %s\n",
+ strAssemblyName.GetUnicode(),
+ pMD->m_pszDebugClassName,
+ pMD->m_pszDebugMethodName,
+ szError));
+ }
+ else
+ {
+ Assembly *pTargetAssembly = pTargetMD->GetAssembly();
+ const SString &strTargetAssemblyName = pTargetAssembly->GetManifestModule()->GetPath();
+
+ LOG((LF_SECURITY,
+ LL_INFO1000,
+ "Security Transparency Violation: Assembly '%S' - Method '%s::%s' - Target Assembly '%S': %s\n",
+ strAssemblyName.GetUnicode(),
+ pMD->m_pszDebugClassName,
+ pMD->m_pszDebugMethodName,
+ strTargetAssemblyName.GetUnicode(),
+ szError));
+ }
+}
+
+#endif // _DEBUG
+
+// There are a few places we throw transparency method access exceptions that aren't "real"
+// method access exceptions - such as unverifiable code in a transparent assembly, and having a critical
+// attribute on a transparent method. Those continue to use the one-MethodDesc form of throwing -
+// everything else should use the standard ::ThrowMethodAccessException call
+
+// static
+void DECLSPEC_NORETURN SecurityTransparent::ThrowMethodAccessException(MethodDesc* pMD,
+ DWORD dwMessageId /* = IDS_CRITICAL_METHOD_ACCESS_DENIED */)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(pMD));
+ }
+ CONTRACTL_END;
+
+ // throw method access exception
+ StackSString strMethod;
+ TypeString::AppendMethod(strMethod, pMD, pMD->GetClassInstantiation(), TypeString::FormatNamespace | TypeString::FormatAngleBrackets| TypeString::FormatSignature);
+ COMPlusThrowHR(COR_E_METHODACCESS, dwMessageId, strMethod.GetUnicode());
+}
+
+// static
+void DECLSPEC_NORETURN SecurityTransparent::ThrowTypeLoadException(MethodDesc* pMethod, DWORD dwMessageID /* = IDS_METHOD_INHERITANCE_RULES_VIOLATED */)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ PRECONDITION(CheckPointer(pMethod));
+ }
+ CONTRACTL_END;
+
+ // Throw an exception here
+ StackSString strMethod;
+ StackScratchBuffer buffer;
+ TypeString::AppendMethod(strMethod, pMethod, pMethod->GetClassInstantiation(), TypeString::FormatNamespace | TypeString::FormatAngleBrackets | TypeString::FormatSignature);
+ pMethod->GetAssembly()->ThrowTypeLoadException(strMethod.GetUTF8(buffer), dwMessageID);
+}
+
+// static
+void DECLSPEC_NORETURN SecurityTransparent::ThrowTypeLoadException(MethodTable *pMT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ PRECONDITION(CheckPointer(pMT));
+ }
+ CONTRACTL_END;
+
+ // Throw an exception here
+ StackScratchBuffer buffer;
+ SString strType;
+ TypeString::AppendType(strType, TypeHandle(pMT), TypeString::FormatNamespace | TypeString::FormatAngleBrackets );
+ pMT->GetAssembly()->ThrowTypeLoadException(strType.GetUTF8(buffer), IDS_TYPE_INHERITANCE_RULES_VIOLATED);
+}
+
+static BOOL IsTransparentCallerAllowed(MethodDesc *pCallerMD, MethodDesc *pCalleeMD, SecurityTransparencyError *pError)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ PRECONDITION(CheckPointer(pCallerMD));
+ PRECONDITION(CheckPointer(pCalleeMD));
+ PRECONDITION(CheckPointer(pError, NULL_OK));
+ PRECONDITION(pCallerMD->IsTransparent());
+ }
+ CONTRACTL_END;
+
+ // If the target is critical, and not treat as safe, then we cannot allow the call
+ if (Security::IsMethodCritical(pCalleeMD) && !Security::IsMethodSafeCritical(pCalleeMD))
+ {
+ if (pError != NULL)
+ {
+ *pError = SecurityTransparencyError_CallCriticalMethod;
+ }
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Convert the critical member to a LinkDemand for FullTrust, and convert that LinkDemand to a
+// full demand. If the current call stack allows this conversion to succeed, this method returns. Otherwise
+// a security exception is thrown.
+//
+// Arguments:
+// pCallerMD - The method calling the critical method
+//
+
+static void ConvertCriticalMethodToLinkDemand(MethodDesc *pCallerMD)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pCallerMD));
+ PRECONDITION(pCallerMD->IsTransparent());
+ PRECONDITION(pCallerMD->GetAssembly()->GetSecurityTransparencyBehavior()->CanTransparentCodeCallLinkDemandMethods());
+ }
+ CONTRACTL_END;
+
+#if !defined(CROSSGEN_COMPILE) && defined(FEATURE_CAS_POLICY)
+ if (NingenEnabled())
+ return;
+
+ GCX_COOP();
+
+ OBJECTREF permSet = NULL;
+ GCPROTECT_BEGIN(permSet);
+
+ Security::GetPermissionInstance(&permSet, SECURITY_FULL_TRUST);
+ Security::DemandSet(SSWT_LATEBOUND_LINKDEMAND, permSet);
+
+ GCPROTECT_END();
+#endif // !CROSSGEN_COMPILE && FEATURE_CAS_POLICY
+}
+
+// static
+BOOL SecurityTransparent::CheckCriticalAccess(AccessCheckContext* pContext,
+ MethodDesc* pOptionalTargetMethod,
+ FieldDesc* pOptionalTargetField,
+ MethodTable * pOptionalTargetType)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pContext));
+ }
+ CONTRACTL_END;
+
+ // At most one of these should be non-NULL
+ _ASSERTE(1 >= ((pOptionalTargetMethod ? 1 : 0) +
+ (pOptionalTargetField ? 1 : 0) +
+ (pOptionalTargetType ? 1 : 0)));
+
+ // okay caller is transparent, additional checks needed
+ BOOL fIsTargetCritical = FALSE; // check if target is critical
+ BOOL fIsTargetSafe = FALSE; // check if target is marked safe
+ Assembly *pTargetAssembly = NULL;
+
+ if (pOptionalTargetMethod != NULL)
+ {
+ fIsTargetCritical = IsMethodCritical(pOptionalTargetMethod);
+ fIsTargetSafe = IsMethodSafeCritical(pOptionalTargetMethod);
+ pTargetAssembly = pOptionalTargetMethod->GetAssembly();
+ }
+ else if (pOptionalTargetField != NULL)
+ {
+ FieldSecurityDescriptor fieldSecurityDescriptor(pOptionalTargetField);
+ fIsTargetCritical = fieldSecurityDescriptor.IsCritical();
+ fIsTargetSafe = fieldSecurityDescriptor.IsTreatAsSafe();
+ pTargetAssembly = pOptionalTargetField->GetModule()->GetAssembly();
+ }
+ else if (pOptionalTargetType != NULL)
+ {
+ fIsTargetCritical = IsTypeAllCritical(pOptionalTargetType); // check for only all critical classes
+ fIsTargetSafe = IsTypeSafeCritical(pOptionalTargetType);
+ pTargetAssembly = pOptionalTargetType->GetAssembly();
+ }
+
+ // If the target is transparent or safe critical, then no further checks are needed. Otherwise, if a
+ // legacy caller is targeting a new critical method, we may be able to allow the call by converting
+ // the critical method to a LinkDemand for FullTrust and converting the LinkDemand to a full demand.
+ //
+ // This allows for the case where a v2 transparent assembly called a method that was proteced by a
+ // LinkDemand in v2 and followed our suggested path of converting to being critical in v4. By treating
+ // the v4 critical method as if it were protected with a LinkDmeand instead, we're simply reversing this
+ // conversion to provide compatible behavior with legacy binaries
+ if (!fIsTargetCritical || fIsTargetSafe)
+ {
+ return TRUE;
+ }
+
+ if (pContext->IsCalledFromInterop())
+ return TRUE;
+
+ MethodDesc* pCurrentMD = pContext->GetCallerMethod();
+ MethodTable* pCurrentMT = pContext->GetCallerMT();
+
+ // Not from interop but the caller is NULL, this can only happen
+ // when we are checking from a Type/Assembly.
+ if (pCurrentMD != NULL)
+ {
+ // TODO: need to probably CheckCastToClass as well..
+ if (!IsMethodTransparent(pCurrentMD))
+ {
+ // Return TRUE if caller is NULL (interop caller) or critical.
+ return TRUE;
+ }
+
+#ifdef FEATURE_CORECLR
+ // On the coreCLR, a method can be transparent even if the containing type is marked Critical.
+ // This will happen when that method is an override of a base transparent method, and the type that
+ // contains the override is marked Critical. And that's the only case it can happen.
+ // This particular case is not a failure. To state this another way, from a security transpararency perspective,
+ // a method will always have access to the type that it is a member of.
+ if (pOptionalTargetType == pCurrentMD->GetMethodTable())
+ {
+ return TRUE;
+ }
+#endif // FEATURE_CORECLR
+
+ // an attached profiler may wish to have these checks suppressed
+ if (Security::BypassSecurityChecksForProfiler(pCurrentMD))
+ {
+ return TRUE;
+ }
+
+ if (pTargetAssembly != NULL &&
+ pTargetAssembly->GetSecurityTransparencyBehavior()->CanCriticalMembersBeConvertedToLinkDemand() &&
+ pCurrentMD->GetAssembly()->GetSecurityTransparencyBehavior()->CanTransparentCodeCallLinkDemandMethods())
+ {
+ // Convert the critical member to a LinkDemand for FullTrust, and convert that LinkDemand to a
+ // full demand. If the resulting full demand for FullTrust is successful, then we'll allow the access
+ // to the critical method to succeed
+ ConvertCriticalMethodToLinkDemand(pCurrentMD);
+ return TRUE;
+ }
+ }
+ else if (pCurrentMT != NULL)
+ {
+ if (!IsTypeTransparent(pCurrentMT))
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+// Determine if a method is allowed to perform a CAS assert within the transparency rules. Generally, only
+// critical code may assert. However, for compatibility with v2.0 we allow asserts from transparent code if
+// the following criteria are met:
+// 1. The assembly is a true v2.0 binary, and is not just using v2.0 transparency rules via the
+// SecurityRuleSet.Level1 annotation.
+// 2. The assembly is agnostic to transparency (that is, if it were fully trusted it would be
+// opprotunistically critical).
+// 3. We are currently in a heterogenous AppDomain.
+//
+// This compensates for the fact that while partial trust code could have asserted in v2.0, it can no longer
+// assert in v4.0 as we force it to be transparent. While the v2.0 transparency rules still don't allow
+// asserting, assemblies that would have been critical in v2.0 are allowed to continue asserting in v4.0.
+
+// static
+BOOL SecurityTransparent::IsAllowedToAssert(MethodDesc *pMD)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(pMD));
+ }
+ CONTRACTL_END;
+
+ // Critical code is always allowed to assert
+ if (IsMethodCritical(pMD))
+ {
+ return TRUE;
+ }
+
+#ifdef FEATURE_CORECLR
+ // On CoreCLR only critical code may ever assert - there are no compatibility reasons to allow
+ // transparent asserts.
+ return FALSE;
+#else // !FEATURE_CORECLR
+ // We must be in a heterogenous AppDomain for transparent asserts to work
+ if (GetAppDomain()->GetSecurityDescriptor()->IsHomogeneous())
+ {
+ return FALSE;
+ }
+
+ ModuleSecurityDescriptor *pMSD = ModuleSecurityDescriptor::GetModuleSecurityDescriptor(pMD->GetAssembly());
+
+ // Only assemblies whose version requires them to use legacy transparency (rather than assemblies which
+ // get legacy transparency via RuleSet.Level1) can assert from transparent code
+ if (!pMSD->AssemblyVersionRequiresLegacyTransparency())
+ {
+ return FALSE;
+ }
+
+ // Finally, the assembly must not have had any of the transparency attributes on it
+ const TokenSecurityDescriptorFlags transparencyAwareFlags =
+ TokenSecurityDescriptorFlags_AllCritical | // [SecurityCritical(SecurityCriticalScope.All)]
+ TokenSecurityDescriptorFlags_Critical | // [SecurityCritical]
+ TokenSecurityDescriptorFlags_SafeCritical | // [SecuritySafeCritical]
+ TokenSecurityDescriptorFlags_Transparent | // [SecurityTransparent]
+ TokenSecurityDescriptorFlags_TreatAsSafe; // [SecurityTreatAsSafe]
+ TokenSecurityDescriptorFlags moduleAttributes = pMSD->GetTokenFlags();
+ if ((moduleAttributes & transparencyAwareFlags) != TokenSecurityDescriptorFlags_None)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+#endif // FEATURE_CORECLR
+}
+
+// Functor class to aid in determining if a type requires a transparency check
+class TypeRequiresTransparencyCheckFunctor
+{
+private:
+ bool m_requiresTransparencyCheck;
+ bool m_checkForLinkDemands;
+
+public:
+ TypeRequiresTransparencyCheckFunctor(bool checkForLinkDemands) :
+ m_requiresTransparencyCheck(false),
+ m_checkForLinkDemands(checkForLinkDemands)
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+
+ TypeRequiresTransparencyCheckFunctor(const TypeRequiresTransparencyCheckFunctor &other); // not implemented
+
+ bool RequiresTransparencyCheck() const
+ {
+ LIMITED_METHOD_CONTRACT;
+ return m_requiresTransparencyCheck;
+ }
+
+ void operator()(MethodTable *pMT)
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // We only need to do a check if so far none of the other component typpes required a transparency
+ // check. Critical, but not safe critical, types require transparency checks of their callers.
+ if (!m_requiresTransparencyCheck)
+ {
+ m_requiresTransparencyCheck = Security::IsTypeCritical(pMT) && !Security::IsTypeSafeCritical(pMT) &&
+ (!m_checkForLinkDemands || pMT->GetAssembly()->GetSecurityTransparencyBehavior()->CanCriticalMembersBeConvertedToLinkDemand());
+ }
+ }
+};
+
+// Determine if accessing a type requires doing a transparency check - this checks to see if the type
+// itself, or any of its generic variables are security critical.
+
+// static
+bool SecurityTransparent::TypeRequiresTransparencyCheck(TypeHandle type, bool checkForLinkDemands)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ TypeRequiresTransparencyCheckFunctor typeChecker(checkForLinkDemands);
+ type.ForEachComponentMethodTable(typeChecker);
+ return typeChecker.RequiresTransparencyCheck();
+}
+
+CorInfoCanSkipVerificationResult SecurityTransparent::JITCanSkipVerification(MethodDesc * pMD)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+ /* XXX Fri 1/12/2007
+ * This code is cloned from security.inl!Security::CanSkipVerification(MethodDesc, BOOL).
+ */
+ // Special case the System.Object..ctor:
+ // System.Object..ctor is not verifiable according to current verifier rules (that require to call the
+ // base class ctor). But since we want System.Object..ctor() to be marked transparent, it cannot be
+ // unverifiable (telesto security rules prohibit transparent code from being unverifiable)
+
+#ifndef DACCESS_COMPILE
+ if (g_pObjectCtorMD == pMD)
+ return CORINFO_VERIFICATION_CAN_SKIP;
+#endif //!DACCESS_COMPILE
+
+ // If a profiler is attached, we may want to bypass verification as well
+ if (Security::BypassSecurityChecksForProfiler(pMD))
+ {
+ return CORINFO_VERIFICATION_CAN_SKIP;
+ }
+
+ BOOL hasSkipVerificationPermisson = false;
+ DomainAssembly * pDomainAssembly = pMD->GetAssembly()->GetDomainAssembly();
+ hasSkipVerificationPermisson = Security::CanSkipVerification(pDomainAssembly);
+
+ CorInfoCanSkipVerificationResult canSkipVerif = hasSkipVerificationPermisson ? CORINFO_VERIFICATION_CAN_SKIP : CORINFO_VERIFICATION_CANNOT_SKIP;
+
+#ifndef FEATURE_CORECLR
+ // also check to see if the method is marked transparent
+ if (hasSkipVerificationPermisson)
+ {
+ if (pDomainAssembly == GetAppDomain()->GetAnonymouslyHostedDynamicMethodsAssembly())
+ {
+ // This assembly is FullTrust. However, it cannot contain unverifiable code.
+ // The JIT compiler is not hardened to deal with invalid code. Hence, we cannot
+ // return CORINFO_VERIFICATION_RUNTIME_CHECK for IL that could have been generated
+ // by a low-trust assembly.
+ canSkipVerif = CORINFO_VERIFICATION_CANNOT_SKIP;
+ }
+ // also check to see if the method is marked transparent
+ else if (SecurityTransparent::IsMethodTransparent(pMD))
+ {
+ // If the assembly requested that even its transparent members not be verified, then we can skip
+ // verification. Otherwise, we need to either inject a runtime demand in the v2 model, or fail
+ // verification in the v4 model.
+ ModuleSecurityDescriptor *pModuleSecDesc = ModuleSecurityDescriptor::GetModuleSecurityDescriptor(pMD->GetAssembly());
+ if (pModuleSecDesc->CanTransparentCodeSkipVerification())
+ {
+ canSkipVerif = CORINFO_VERIFICATION_CAN_SKIP;
+ }
+ else if (pMD->GetAssembly()->GetSecurityTransparencyBehavior()->CanTransparentCodeSkipVerification())
+ {
+ canSkipVerif = CORINFO_VERIFICATION_RUNTIME_CHECK;
+ }
+ else
+ {
+ canSkipVerif = CORINFO_VERIFICATION_CANNOT_SKIP;
+ }
+ }
+ }
+#endif //FEATURE_CORECLR
+
+ return canSkipVerif;
+}
+
+CorInfoCanSkipVerificationResult SecurityTransparent::JITCanSkipVerification(DomainAssembly * pAssembly)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ BOOL hasSkipVerificationPermisson = Security::CanSkipVerification(pAssembly);
+
+ CorInfoCanSkipVerificationResult canSkipVerif = hasSkipVerificationPermisson ? CORINFO_VERIFICATION_CAN_SKIP : CORINFO_VERIFICATION_CANNOT_SKIP;
+
+ // If the assembly has permission to skip verification, but its transparency model requires that
+ // transparency can only be skipped with a runtime demand, then we need to make sure that there is a
+ // runtime check done.
+ if (hasSkipVerificationPermisson)
+ {
+ // In CoreCLR, do not enable transparency checks here. We depend on this method being "honest" in
+ // JITCanSkipVerification to skip transparency checks on profile assemblies.
+#ifndef FEATURE_CORECLR
+ ModuleSecurityDescriptor *pMsd = ModuleSecurityDescriptor::GetModuleSecurityDescriptor(pAssembly->GetAssembly());
+ if (pMsd->IsAllTransparent() &&
+ pAssembly->GetAssembly()->GetSecurityTransparencyBehavior()->CanTransparentCodeSkipVerification())
+ {
+ canSkipVerif = CORINFO_VERIFICATION_RUNTIME_CHECK;
+ }
+#endif // !FEATURE_CORECLR
+ }
+
+ return canSkipVerif;
+}
+
+// Determine if a method can quickly exit a runtime callout from the JIT - a true return value indicates
+// that the callout is not needed, false means that we cannot quicky exit
+
+// static
+bool SecurityTransparent::SecurityCalloutQuickCheck(MethodDesc *pCallerMD)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ PRECONDITION(CheckPointer(pCallerMD));
+ PRECONDITION(pCallerMD->HasCriticalTransparentInfo());
+ }
+ CONTRACTL_END;
+
+ // In coreclr, we modified the logic in the callout to also do some transparency method access checks
+ // These checks need to happen regardless of trust level and we shouldn't be bailing out early
+ // just because we happen to be in Full Trust
+#ifndef FEATURE_CORECLR
+ // See if we need to process this callout for real, or if we can bail out early before setting up a HMF,
+ // and spending a lot of time processing the transparency evaluation. The simplest case where we can do
+ // this is if the caller is critical. In that case, we know that the caller is allowed to do whatever
+ // it wants, so we quit out.
+ //
+ // Additionally, if the caller is using SecurityRuleSet.Level1, which turns transparency violations into
+ // security demands, we can bail out early if we know for sure all demands will succeed on the current
+ // call stack. (Note: this remains true as long as we don't start generating callouts for transparent
+ // level 1 calling critical level 1, or transparent level 1 doing an assert, which are the only two
+ // violations which do not succeed in the face of a successful demand).
+ if (pCallerMD->IsCritical())
+ {
+ return true;
+ }
+ else
+ {
+ // The caller is transparent, so let's see if demands can cause transparency violations to succeed,
+ // and also if all demands issued from this context will succeed.
+ const SecurityTransparencyBehavior *pCallerTransparency = pCallerMD->GetAssembly()->TryGetSecurityTransparencyBehavior();
+ if (pCallerTransparency != NULL &&
+ pCallerTransparency->CanTransparentCodeCallLinkDemandMethods() &&
+ SecurityStackWalk::HasFlagsOrFullyTrustedIgnoreMode(0))
+ {
+ return true;
+ }
+ }
+#endif // !FEATURE_CORECLR
+
+ return false;
+}
+
+CorInfoIsAccessAllowedResult SecurityTransparent::RequiresTransparentAssemblyChecks(MethodDesc* pCallerMD,
+ MethodDesc* pCalleeMD,
+ SecurityTransparencyError *pError)
+{
+ LIMITED_METHOD_CONTRACT;
+ return RequiresTransparentCodeChecks(pCallerMD, pCalleeMD, pError);
+}
+
+CorInfoIsAccessAllowedResult SecurityTransparent::RequiresTransparentCodeChecks(MethodDesc* pCallerMD,
+ MethodDesc* pCalleeMD,
+ SecurityTransparencyError *pError)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ PRECONDITION(CheckPointer(pCallerMD));
+ PRECONDITION(CheckPointer(pCalleeMD));
+ PRECONDITION(CheckPointer(pError, NULL_OK));
+ PRECONDITION(!pCalleeMD->IsILStub());
+ }
+ CONTRACTL_END;
+
+ // check if the caller assembly is transparent and NOT an interception stub (e.g. marshalling)
+ bool doChecks = !pCallerMD->IsILStub() && IsMethodTransparent(pCallerMD);
+
+ if (doChecks && Security::IsTransparencyEnforcementEnabled())
+ {
+ if (!IsTransparentCallerAllowed(pCallerMD, pCalleeMD, pError))
+ {
+ // intercept the call to throw a MAE at runtime (more debuggable than throwing MAE at JIT-time)
+ // IsTransparentCallerAllowed will have set pError if necessary
+ return CORINFO_ACCESS_RUNTIME_CHECK;
+ }
+
+ // Check to see if the callee has a LinkDemand, if so we may need to intercept the call.
+ if (pCalleeMD->RequiresLinktimeCheck())
+ {
+ if (pCalleeMD->RequiresLinkTimeCheckHostProtectionOnly()
+#ifndef CROSSGEN_COMPILE
+ && GetHostProtectionManager()->GetProtectedCategories() == eNoChecks
+#endif // CROSSGEN_COMPILE
+ )
+ {
+ // exclude HPA which are marked as LinkDemand and there is no HostProtection enabled currently
+ return CORINFO_ACCESS_ALLOWED;
+ }
+
+ // There was a reason other than simply conditional APTCA that the method required a linktime
+ // check - intercept the call later.
+ if (pError != NULL)
+ {
+ *pError = SecurityTransparencyError_CallLinkDemand;
+ }
+
+ return CORINFO_ACCESS_RUNTIME_CHECK;
+ }
+ }
+
+ return CORINFO_ACCESS_ALLOWED;
+}
+
+
+#ifndef CROSSGEN_COMPILE
+
+// Perform appropriate Transparency checks if the caller to the Load(byte[] ) without passing in an input Evidence is Transparent
+VOID SecurityTransparent::PerformTransparencyChecksForLoadByteArray(MethodDesc* pCallerMD, AssemblySecurityDescriptor* pLoadedSecDesc)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END
+
+#ifdef FEATURE_CAS_POLICY
+ GCX_COOP();
+ // check to see if the method that does the Load(byte[] ) is transparent
+ if (IsMethodTransparent(pCallerMD))
+ {
+ Assembly* pLoadedAssembly = pLoadedSecDesc->GetAssembly();
+ // check to see if the byte[] being loaded is critical, i.e. not Transparent
+ if (!ModuleSecurityDescriptor::IsMarkedTransparent(pLoadedAssembly))
+ {
+ // if transparent code loads a byte[] that is critical, need to inject appropriate demands
+ if (pLoadedSecDesc->IsFullyTrusted()) // if the loaded code is full-trust
+ {
+ // do a full-demand for Full-Trust
+ OBJECTREF permSet = NULL;
+ GCPROTECT_BEGIN(permSet);
+ Security::GetPermissionInstance(&permSet, SECURITY_FULL_TRUST);
+ Security::DemandSet(SSWT_LATEBOUND_LINKDEMAND, permSet);
+ GCPROTECT_END();// do a full-demand for Full-Trust
+ }
+ else
+ {
+ // otherwise inject a Demand for permissions being granted?
+ struct _localGC {
+ OBJECTREF granted;
+ OBJECTREF denied;
+ } localGC;
+ ZeroMemory(&localGC, sizeof(localGC));
+
+ GCPROTECT_BEGIN(localGC);
+ {
+ localGC.granted = pLoadedSecDesc->GetGrantedPermissionSet(&(localGC.denied));
+ Security::DemandSet(SSWT_LATEBOUND_LINKDEMAND, localGC.granted);
+ }
+ GCPROTECT_END();
+ }
+ }
+ }
+#endif // FEATURE_CAS_POLICY
+}
+
+static void ConvertLinkDemandToFullDemand(MethodDesc* pCallerMD, MethodDesc* pCalleeMD)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(pCallerMD));
+ PRECONDITION(CheckPointer(pCalleeMD));
+ PRECONDITION(pCallerMD->IsTransparent());
+ }
+ CONTRACTL_END;
+
+ if (!pCalleeMD->RequiresLinktimeCheck() ||
+ pCalleeMD->RequiresLinkTimeCheckHostProtectionOnly())
+ {
+ return;
+ }
+
+ if (!Security::IsTransparencyEnforcementEnabled())
+ {
+ return;
+ }
+
+ // Profilers may wish to suppress linktime checks for methods they're profiling
+ if (Security::BypassSecurityChecksForProfiler(pCallerMD))
+ {
+ return;
+ }
+
+ struct
+ {
+ OBJECTREF refClassNonCasDemands;
+ OBJECTREF refClassCasDemands;
+ OBJECTREF refMethodNonCasDemands;
+ OBJECTREF refMethodCasDemands;
+ OBJECTREF refThrowable;
+ }
+ gc;
+ ZeroMemory(&gc, sizeof(gc));
+ GCPROTECT_BEGIN(gc);
+
+ LinktimeCheckReason linktimeCheckReason = Security::GetLinktimeCheckReason(pCalleeMD,
+ &gc.refClassCasDemands,
+ &gc.refClassNonCasDemands,
+ &gc.refMethodCasDemands,
+ &gc.refMethodNonCasDemands);
+
+#ifdef FEATURE_APTCA
+ BOOL fCallerIsAPTCA = pCallerMD->GetAssembly()->AllowUntrustedCaller();
+
+ if ((linktimeCheckReason & LinktimeCheckReason_AptcaCheck))
+ {
+ if (fCallerIsAPTCA &&
+ Security::IsUntrustedCallerCheckNeeded(pCalleeMD, pCallerMD->GetAssembly()))
+ {
+#ifdef _DEBUG
+ if (g_pConfig->LogTransparencyErrors())
+ {
+ SecurityTransparent::LogTransparencyError(pCallerMD, "Transparent method calling an APTCA protected assembly", pCalleeMD);
+ }
+ if (!g_pConfig->DisableTransparencyEnforcement())
+#endif // _DEBUG
+ {
+ // Depending on the transparency model, we need to either fail the attempt to call a method
+ // protected with the APTCA link demand, or conver it to a full demand. Note that we need to
+ // upgrade to a full demand if either the caller of callee are in v2 mode, the APTCA check is
+ // conceptually a link demand, and for link demands we do the conversion if either assembly is
+ // using the v2 rules.
+ if (pCallerMD->GetAssembly()->GetSecurityTransparencyBehavior()->CanTransparentCodeCallLinkDemandMethods() ||
+ pCalleeMD->GetAssembly()->GetSecurityTransparencyBehavior()->CanTransparentCodeCallLinkDemandMethods())
+ {
+ OBJECTREF permSet = NULL;
+ GCPROTECT_BEGIN(permSet);
+ Security::GetPermissionInstance(&permSet, SECURITY_FULL_TRUST);
+ Security::DemandSet(SSWT_LATEBOUND_LINKDEMAND, permSet);
+ GCPROTECT_END();
+ }
+ else
+ {
+ ::ThrowMethodAccessException(pCallerMD, pCalleeMD, FALSE, IDS_E_TRANSPARENT_CALL_LINKDEMAND);
+ }
+ }
+ }
+ }
+#endif // FEATURE_APTCA
+
+
+ // The following logic turns link demands on the target method into full stack walks
+
+ if ((linktimeCheckReason & LinktimeCheckReason_CasDemand) ||
+ (linktimeCheckReason & LinktimeCheckReason_NonCasDemand))
+ {
+ // If we found a link demand, then we need to make sure that both the callee's transparency model
+ // allows for it to satisfy a link demand. We check both since a v4 caller calling a v2 assembly may
+ // be attempting to satisfy a LinkDemand which the v2 assembly has not yet had a chance to remove.
+ if (!pCallerMD->GetAssembly()->GetSecurityTransparencyBehavior()->CanTransparentCodeCallLinkDemandMethods() &&
+ !pCalleeMD->GetAssembly()->GetSecurityTransparencyBehavior()->CanTransparentCodeCallLinkDemandMethods() &&
+ (gc.refClassCasDemands != NULL || gc.refMethodCasDemands != NULL))
+ {
+#ifdef _DEBUG
+ if (g_pConfig->LogTransparencyErrors())
+ {
+ SecurityTransparent::LogTransparencyError(pCallerMD, "Transparent method calling a LinkDemand protected method", pCalleeMD);
+ }
+ if (!g_pConfig->DisableTransparencyEnforcement())
+#endif // _DEBUG
+ {
+ ::ThrowMethodAccessException(pCallerMD, pCalleeMD, FALSE, IDS_E_TRANSPARENT_CALL_LINKDEMAND);
+ }
+ }
+
+ // CAS Link Demands
+ if (gc.refClassCasDemands != NULL)
+ Security::DemandSet(SSWT_LATEBOUND_LINKDEMAND, gc.refClassCasDemands);
+ if (gc.refMethodCasDemands != NULL)
+ Security::DemandSet(SSWT_LATEBOUND_LINKDEMAND, gc.refMethodCasDemands);
+
+ // Non-CAS demands are not applied against a grant set, they're standalone.
+ if (gc.refClassNonCasDemands != NULL)
+ Security::CheckNonCasDemand(&gc.refClassNonCasDemands);
+ if (gc.refMethodNonCasDemands != NULL)
+ Security::CheckNonCasDemand(&gc.refMethodNonCasDemands);
+ }
+
+
+ //
+ // Make sure that the callee is allowed to call unmanaged code if the target is native.
+ //
+
+ if (linktimeCheckReason & LinktimeCheckReason_NativeCodeCall)
+ {
+#ifdef _DEBUG
+ if (g_pConfig->LogTransparencyErrors())
+ {
+ SecurityTransparent::LogTransparencyError(pCallerMD, "Transparent method calling unmanaged code");
+ }
+#endif // _DEBUG
+
+ if (pCallerMD->GetAssembly()->GetSecurityTransparencyBehavior()->CanTransparentCodeCallUnmanagedCode())
+ {
+#ifdef FEATURE_APTCA
+ if (fCallerIsAPTCA)
+ {
+ // if the caller assembly is APTCA, then only inject this demand, for NON-APTCA we will allow
+ // calls to native code
+ // NOTE: the JIT would have already performed the LinkDemand for this anyways
+ Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_UNMANAGED_CODE);
+ }
+#endif // FEATURE_APTCA
+ }
+ else
+ {
+ ::ThrowMethodAccessException(pCallerMD, pCalleeMD, FALSE, IDS_E_TRANSPARENT_CALL_NATIVE);
+ }
+ }
+
+ GCPROTECT_END();
+}
+
+
+VOID SecurityTransparent::EnforceTransparentAssemblyChecks(MethodDesc* pCallerMD, MethodDesc* pCalleeMD)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(pCallerMD));
+ PRECONDITION(Security::IsMethodTransparent(pCallerMD));
+ PRECONDITION(CheckPointer(pCalleeMD));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ if (!Security::IsTransparencyEnforcementEnabled())
+ {
+ return;
+ }
+
+ // Profilers may wish to suppress transparency checks for methods they're profiling
+ if (Security::BypassSecurityChecksForProfiler(pCallerMD))
+ {
+ return;
+ }
+
+ // if target is critical, and not marked as TreatAsSafe, Access ERROR.
+ if (Security::IsMethodCritical(pCalleeMD) && !Security::IsMethodSafeCritical(pCalleeMD))
+ {
+
+ const SecurityTransparencyBehavior *pCalleeTransparency =
+ pCalleeMD->GetAssembly()->GetSecurityTransparencyBehavior();
+ const SecurityTransparencyBehavior *pCallerTransparency =
+ pCallerMD->GetAssembly()->GetSecurityTransparencyBehavior();
+
+ // If critical methods in the target can be converted to a link demand for legacy callers, then we
+ // need to do that conversion. Otherwise, this access is disallowed.
+ if (pCalleeTransparency->CanCriticalMembersBeConvertedToLinkDemand() &&
+ pCallerTransparency->CanTransparentCodeCallLinkDemandMethods())
+ {
+ ConvertCriticalMethodToLinkDemand(pCallerMD);
+ }
+ else
+ {
+ // Conversion to a LinkDemand was not allowed, so we need to
+#ifdef _DEBUG
+ if (g_pConfig->LogTransparencyErrors())
+ {
+ LogTransparencyError(pCallerMD, "Transparent method accessing a critical method", pCalleeMD);
+ }
+#endif // _DEBUG
+ ::ThrowMethodAccessException(pCallerMD, pCalleeMD, TRUE, IDS_E_CRITICAL_METHOD_ACCESS_DENIED);
+ }
+ }
+
+ ConvertLinkDemandToFullDemand(pCallerMD, pCalleeMD);
+}
+
+
+VOID SecurityTransparent::EnforceTransparentDelegateChecks(MethodTable* pDelegateMT, MethodDesc* pCalleeMD)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ PRECONDITION(CheckPointer(pDelegateMT));
+ PRECONDITION(CheckPointer(pCalleeMD));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+#ifdef FEATURE_CORECLR
+ // We only enforce delegate binding rules in partial trust
+ if (GetAppDomain()->GetSecurityDescriptor()->IsFullyTrusted())
+ return;
+
+ StackSString strMethod;
+ TypeString::AppendMethod(strMethod, pCalleeMD, pCalleeMD->GetClassInstantiation(), TypeString::FormatNamespace | TypeString::FormatAngleBrackets| TypeString::FormatSignature);
+ StackSString strDelegateType;
+ TypeString::AppendType(strDelegateType, pDelegateMT, TypeString::FormatNamespace | TypeString::FormatAngleBrackets| TypeString::FormatSignature);
+
+ COMPlusThrowHR(COR_E_METHODACCESS, IDS_E_DELEGATE_BINDING_TRANSPARENCY, strDelegateType.GetUnicode(), strMethod.GetUnicode());
+#endif // FEATURE_CORECLR
+}
+
+#endif // CROSSGEN_COMPILE
+
+
+BOOL SecurityTransparent::IsMethodTransparent(MethodDesc* pMD)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ SO_INTOLERANT;
+ PRECONDITION(CheckPointer(pMD));
+ }
+ CONTRACTL_END;
+
+ // Is transparency info cached?
+ if (pMD->HasCriticalTransparentInfo())
+ {
+ return !pMD->IsCritical();
+ }
+
+ MethodSecurityDescriptor methSecurityDescriptor(pMD);
+ return !methSecurityDescriptor.IsCritical();
+}
+
+BOOL SecurityTransparent::IsMethodCritical(MethodDesc* pMD)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ SO_INTOLERANT;
+ PRECONDITION(CheckPointer(pMD));
+ }
+ CONTRACTL_END;
+
+ // Is transparency info cached?
+ if (pMD->HasCriticalTransparentInfo())
+ {
+ return pMD->IsCritical();
+ }
+
+ MethodSecurityDescriptor methSecurityDescriptor(pMD);
+ return methSecurityDescriptor.IsCritical();
+}
+
+// Returns True if a method is SafeCritical (=> not Transparent and not Critical)
+BOOL SecurityTransparent::IsMethodSafeCritical(MethodDesc* pMD)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ SO_INTOLERANT;
+ PRECONDITION(CheckPointer(pMD));
+ }
+ CONTRACTL_END;
+
+ // Is transparency info cached?
+ if (pMD->HasCriticalTransparentInfo())
+ {
+ return (pMD->IsCritical() && pMD->IsTreatAsSafe());
+ }
+
+ MethodSecurityDescriptor methSecurityDescriptor(pMD);
+ return (methSecurityDescriptor.IsCritical() && methSecurityDescriptor.IsTreatAsSafe());
+}
+
+BOOL SecurityTransparent::IsTypeCritical(MethodTable *pMT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ SO_INTOLERANT;
+ PRECONDITION(CheckPointer(pMT));
+ }
+ CONTRACTL_END;
+
+ EEClass *pClass = pMT->GetClass();
+ if (pClass->HasCriticalTransparentInfo())
+ {
+ return pClass->IsCritical();
+ }
+
+ TypeSecurityDescriptor typeSecurityDescriptor(pMT);
+ return typeSecurityDescriptor.IsCritical();
+}
+
+BOOL SecurityTransparent::IsTypeSafeCritical(MethodTable *pMT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ SO_INTOLERANT;
+ PRECONDITION(CheckPointer(pMT));
+ }
+ CONTRACTL_END;
+
+ EEClass *pClass = pMT->GetClass();
+ if (pClass->HasCriticalTransparentInfo())
+ {
+ return pClass->IsCritical() && pClass->IsTreatAsSafe();
+ }
+
+ TypeSecurityDescriptor typeSecurityDescriptor(pMT);
+ return typeSecurityDescriptor.IsCritical() &&
+ typeSecurityDescriptor.IsTreatAsSafe();
+}
+
+BOOL SecurityTransparent::IsTypeTransparent(MethodTable *pMT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ SO_INTOLERANT;
+ PRECONDITION(CheckPointer(pMT));
+ }
+ CONTRACTL_END;
+
+ EEClass *pClass = pMT->GetClass();
+ if (pClass->HasCriticalTransparentInfo())
+ {
+ return !pClass->IsCritical();
+ }
+
+ TypeSecurityDescriptor typeSecurityDescriptor(pMT);
+ return !typeSecurityDescriptor.IsCritical();
+}
+
+// Returns TRUE if a type is transparent and contains only transparent members
+// static
+BOOL SecurityTransparent::IsTypeAllTransparent(MethodTable * pMT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ SO_INTOLERANT;
+ PRECONDITION(CheckPointer(pMT));
+ }
+ CONTRACTL_END;
+
+ EEClass *pClass = pMT->GetClass();
+ if (pClass->HasCriticalTransparentInfo())
+ {
+ return pClass->IsAllTransparent();
+ }
+
+ TypeSecurityDescriptor typeSecurityDescriptor(pMT);
+ return typeSecurityDescriptor.IsAllTransparent();
+}
+
+BOOL SecurityTransparent::IsTypeAllCritical(MethodTable * pMT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ SO_INTOLERANT;
+ PRECONDITION(CheckPointer(pMT));
+ }
+ CONTRACTL_END;
+
+ EEClass *pClass = pMT->GetClass();
+ if (pClass->HasCriticalTransparentInfo())
+ {
+ return pClass->IsAllCritical();
+ }
+
+ TypeSecurityDescriptor typeSecurityDescriptor(pMT);
+ return typeSecurityDescriptor.IsAllCritical();
+}
+
+BOOL SecurityTransparent::IsFieldTransparent(FieldDesc* pFD)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ SO_INTOLERANT;
+ PRECONDITION(CheckPointer(pFD));
+ }
+ CONTRACTL_END;
+
+ FieldSecurityDescriptor fsd(pFD);
+ return !fsd.IsCritical();
+}
+
+BOOL SecurityTransparent::IsFieldCritical(FieldDesc* pFD)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ SO_INTOLERANT;
+ PRECONDITION(CheckPointer(pFD));
+ }
+ CONTRACTL_END;
+
+ FieldSecurityDescriptor fsd(pFD);
+ return fsd.IsCritical();
+}
+
+// Returns True if a method is SafeCritical (=> not Transparent and not Critical)
+BOOL SecurityTransparent::IsFieldSafeCritical(FieldDesc* pFD)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ SO_INTOLERANT;
+ PRECONDITION(CheckPointer(pFD));
+ }
+ CONTRACTL_END;
+
+ FieldSecurityDescriptor fsd(pFD);
+ return fsd.IsCritical() && fsd.IsTreatAsSafe();
+}
+
+// Returns True if the token is transparent
+BOOL SecurityTransparent::IsTokenTransparent(Module *pModule, mdToken tkToken)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+ ModuleSecurityDescriptor *pMsd = ModuleSecurityDescriptor::GetModuleSecurityDescriptor(pModule->GetAssembly());
+ if (pMsd->IsAllCritical())
+ {
+ return FALSE;
+ }
+
+ const TokenSecurityDescriptorFlags criticalMask = TokenSecurityDescriptorFlags_AllCritical |
+ TokenSecurityDescriptorFlags_Critical |
+ TokenSecurityDescriptorFlags_SafeCritical;
+ TokenSecurityDescriptor tokenSecurityDescriptor(pModule, tkToken);
+ return !(tokenSecurityDescriptor.GetMetadataFlags() & criticalMask);
+}
+
+// Fuctor type to do perform class access checks on any disallowed transparent -> critical accesses.
+class DoSecurityClassAccessChecksFunctor
+{
+private:
+ MethodDesc *m_pCallerMD;
+ CorInfoSecurityRuntimeChecks m_check;
+
+public:
+ DoSecurityClassAccessChecksFunctor(MethodDesc *pCallerMD, CorInfoSecurityRuntimeChecks check)
+ : m_pCallerMD(pCallerMD),
+ m_check(check)
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+
+ DoSecurityClassAccessChecksFunctor(const DoSecurityClassAccessChecksFunctor &other); // not implemented
+
+ void operator()(MethodTable *pMT)
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ // We can get caller checks of 0 if we're in AlwaysInsertCallout mode, so make sure to do all of our
+ // work under checks for specific flags
+ if (m_check & CORINFO_ACCESS_SECURITY_TRANSPARENCY)
+ {
+ StaticAccessCheckContext accessContext(m_pCallerMD);
+
+ if (!Security::CheckCriticalAccess(&accessContext, NULL, NULL, pMT))
+ {
+ ThrowTypeAccessException(m_pCallerMD, pMT, TRUE, IDS_E_CRITICAL_TYPE_ACCESS_DENIED);
+ }
+ }
+ }
+};
+
+// Check that a calling method is allowed to access a type handle for security reasons. This checks:
+// 1. That transparency allows the caller to use the type
+//
+// The method returns if the checks succeed and throws on error.
+//
+// static
+void SecurityTransparent::DoSecurityClassAccessChecks(MethodDesc *pCallerMD,
+ const TypeHandle &calleeTH,
+ CorInfoSecurityRuntimeChecks check)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ DoSecurityClassAccessChecksFunctor classAccessChecks(pCallerMD, check);
+ calleeTH.ForEachComponentMethodTable(classAccessChecks);
+}
+
+//
+// Transparency behavior implementations
+//
+
+//---------------------------------------------------------------------------------------
+//
+// Transparency behavior implementation for v4 and CoreCLR assemblies
+//
+
+class TransparencyBehaviorImpl : public ISecurityTransparencyImpl
+{
+public:
+
+ // Get bits that indicate how transparency should behave in different situations
+ virtual SecurityTransparencyBehaviorFlags GetBehaviorFlags() const
+ {
+ LIMITED_METHOD_CONTRACT;
+ return SecurityTransparencyBehaviorFlags_AttributesRequireTransparencyCheck |
+ SecurityTransparencyBehaviorFlags_CriticalMembersConvertToLinkDemand |
+ SecurityTransparencyBehaviorFlags_InheritanceRulesEnforced |
+ SecurityTransparencyBehaviorFlags_PartialTrustImpliesAllTransparent |
+ SecurityTransparencyBehaviorFlags_ScopeAppliesOnlyToIntroducedMethods;
+ }
+
+ // Transparency field behavior mappings:
+ // Attribute Behavior
+ // -----------------------------------------------------
+ // Critical (any) Critical
+ // SafeCritical Safe critical
+ // TAS (no critical) No effect
+ // TAS (with any critical) Safe critical
+ virtual FieldSecurityDescriptorFlags MapFieldAttributes(TokenSecurityDescriptorFlags tokenFlags) const
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ SO_INTOLERANT;
+ }
+ CONTRACTL_END;
+
+ FieldSecurityDescriptorFlags fieldFlags = FieldSecurityDescriptorFlags_None;
+
+ if (tokenFlags & TokenSecurityDescriptorFlags_Critical)
+ {
+ fieldFlags |= FieldSecurityDescriptorFlags_IsCritical;
+
+ if (tokenFlags & TokenSecurityDescriptorFlags_TreatAsSafe)
+ {
+ fieldFlags |= FieldSecurityDescriptorFlags_IsTreatAsSafe;
+ }
+ }
+
+ if (tokenFlags & TokenSecurityDescriptorFlags_SafeCritical)
+ {
+ fieldFlags |= FieldSecurityDescriptorFlags_IsCritical | FieldSecurityDescriptorFlags_IsTreatAsSafe;
+ }
+
+ return fieldFlags;
+ }
+
+ // Transparency module behavior mappings for an introduced method:
+ // Attribute Behavior
+ // -----------------------------------------------------
+ // Critical (any) Critical
+ // SafeCritical Safe critical
+ // TAS (no critical) No effect
+ // TAS (with any critical) Safe critical
+ virtual MethodSecurityDescriptorFlags MapMethodAttributes(TokenSecurityDescriptorFlags tokenFlags) const
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ SO_INTOLERANT;
+ }
+ CONTRACTL_END;
+
+ MethodSecurityDescriptorFlags methodFlags = MethodSecurityDescriptorFlags_None;
+
+ if (tokenFlags & TokenSecurityDescriptorFlags_Critical)
+ {
+ methodFlags |= MethodSecurityDescriptorFlags_IsCritical;
+
+ if (tokenFlags & TokenSecurityDescriptorFlags_TreatAsSafe)
+ {
+ methodFlags |= MethodSecurityDescriptorFlags_IsTreatAsSafe;
+ }
+ }
+
+ if (tokenFlags & TokenSecurityDescriptorFlags_SafeCritical)
+ {
+ methodFlags |= MethodSecurityDescriptorFlags_IsCritical |
+ MethodSecurityDescriptorFlags_IsTreatAsSafe;
+ }
+
+ return methodFlags;
+ }
+
+ // Transparency module behavior mappings:
+ // Attribute Behavior
+ // -----------------------------------------------------
+ // APTCA Mixed transparency + APTCA
+ // Critical (scoped) All critical + APTCA
+ // Critical (all) All critical + APTCA
+ // SafeCritical No effect
+ // TAS (no critical) No effect
+ // TAS (with scoped critical) All safe critical + APTCA
+ // TAS (with all critical) All safe critical + APTCA
+ // Transparent All transparent + APTCA
+ //
+ // If the assembly has no attributes, then it will be opportunistically critical.
+ //
+ // APTCA is granted to all assemblies because we rely upon transparent code being unable to call critical
+ // code to enforce the APTCA check. Since all partial trust code must be transparent, this provides the
+ // same effect.
+ virtual ModuleSecurityDescriptorFlags MapModuleAttributes(TokenSecurityDescriptorFlags tokenFlags) const
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ SO_INTOLERANT;
+ }
+ CONTRACTL_END;
+
+ ModuleSecurityDescriptorFlags moduleFlags = ModuleSecurityDescriptorFlags_None;
+
+#if defined(FEATURE_APTCA) || defined(FEATURE_CORESYSTEM)
+ if (tokenFlags & TokenSecurityDescriptorFlags_APTCA)
+ {
+ moduleFlags |= ModuleSecurityDescriptorFlags_IsAPTCA;
+ }
+#endif // defined(FEATURE_APTCA) || defined(FEATURE_CORESYSTEM)
+
+ if (tokenFlags & TokenSecurityDescriptorFlags_Critical)
+ {
+ // We don't pay attention to the critical scope if we're not a legacy assembly
+ moduleFlags |= ModuleSecurityDescriptorFlags_IsAllCritical;
+
+ if (tokenFlags & TokenSecurityDescriptorFlags_TreatAsSafe)
+ {
+ moduleFlags |= ModuleSecurityDescriptorFlags_IsTreatAsSafe;
+ }
+ }
+
+ if (tokenFlags & TokenSecurityDescriptorFlags_Transparent)
+ {
+ moduleFlags |= ModuleSecurityDescriptorFlags_IsAllTransparent;
+ }
+
+ // If we didn't see APTCA/CA, Transparent, or any form of Critical, then the assembly is opportunistically
+ // critical.
+ const ModuleSecurityDescriptorFlags transparencyMask = ModuleSecurityDescriptorFlags_IsAPTCA |
+ ModuleSecurityDescriptorFlags_IsAllTransparent |
+ ModuleSecurityDescriptorFlags_IsAllCritical;
+ if (!(moduleFlags & transparencyMask))
+ {
+ moduleFlags |= ModuleSecurityDescriptorFlags_IsOpportunisticallyCritical;
+ }
+
+ // If the token asks to not have IL verification done in full trust, propigate that to the module
+ if (tokenFlags & TokenSecurityDescriptorFlags_SkipFullTrustVerification)
+ {
+ moduleFlags |= ModuleSecurityDescriptorFlags_SkipFullTrustVerification;
+ }
+
+ // We rely on transparent / critical checks to provide APTCA enforcement in the v4 model, so all assemblies
+ // get APTCA.
+ moduleFlags |= ModuleSecurityDescriptorFlags_IsAPTCA;
+
+ return moduleFlags;
+ }
+
+ // Transparency type behavior mappings:
+ // Attribute Behavior
+ // -----------------------------------------------------
+ // Critical (any) All critical
+ // SafeCritical All safe critical
+ // TAS (no critical) No effect on the type, but save TAS bit since members of the type may be critical
+ // TAS (with any critical) All SafeCritical
+ virtual TypeSecurityDescriptorFlags MapTypeAttributes(TokenSecurityDescriptorFlags tokenFlags) const
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ SO_INTOLERANT;
+ }
+ CONTRACTL_END;
+
+ TypeSecurityDescriptorFlags typeFlags = TypeSecurityDescriptorFlags_None;
+
+ if (tokenFlags & TokenSecurityDescriptorFlags_Critical)
+ {
+ typeFlags |= TypeSecurityDescriptorFlags_IsCritical |
+ TypeSecurityDescriptorFlags_IsAllCritical;
+ }
+
+ // SafeCritical always means all critical + TAS
+ if (tokenFlags & TokenSecurityDescriptorFlags_SafeCritical)
+ {
+ typeFlags |= TypeSecurityDescriptorFlags_IsCritical |
+ TypeSecurityDescriptorFlags_IsAllCritical |
+ TypeSecurityDescriptorFlags_IsTreatAsSafe;
+ }
+
+ if (tokenFlags & TokenSecurityDescriptorFlags_TreatAsSafe)
+ {
+ typeFlags |= TypeSecurityDescriptorFlags_IsTreatAsSafe;
+ }
+
+ return typeFlags;
+ }
+};
+
+#ifndef FEATURE_CORECLR
+
+//---------------------------------------------------------------------------------------
+//
+// Transparency behavior implementation for v2 assemblies
+//
+
+class LegacyTransparencyBehaviorImpl : public ISecurityTransparencyImpl
+{
+public:
+ // Get bits that indicate how transparency should behave in different situations
+ virtual SecurityTransparencyBehaviorFlags GetBehaviorFlags() const
+ {
+ LIMITED_METHOD_CONTRACT;
+ return SecurityTransparencyBehaviorFlags_IntroducedCriticalsMayAddTreatAsSafe |
+ SecurityTransparencyBehaviorFlags_OpportunisticIsSafeCriticalMethods |
+ SecurityTransparencyBehaviorFlags_PartialTrustImpliesAllTransparent |
+ SecurityTransparencyBehaviorFlags_PublicImpliesTreatAsSafe |
+ SecurityTransparencyBehaviorFlags_TransparentCodeCanCallLinkDemand |
+ SecurityTransaprencyBehaviorFlags_TransparentCodeCanCallUnmanagedCode |
+ SecurityTransparencyBehaviorFlags_TransparentCodeCanSkipVerification |
+ SecurityTransparencyBehaviorFlags_UnsignedImpliesAPTCA;
+ }
+
+ // Legacy transparency field behavior mappings:
+ // Attribute Behavior
+ // -----------------------------------------------------
+ // Critical (any) Critical
+ // SafeCritical Safe critical
+ // TAS (no critical) No effect
+ // TAS (with any critical) Safe critical
+ virtual FieldSecurityDescriptorFlags MapFieldAttributes(TokenSecurityDescriptorFlags tokenFlags) const
+ {
+ WRAPPER_NO_CONTRACT;
+
+ // Legacy transparency behaves the same for fields as the current transparency model, so we just forward
+ // this call to that implementation.
+ TransparencyBehaviorImpl forwardImpl;
+ return forwardImpl.MapFieldAttributes(tokenFlags);
+ }
+
+
+ // Legacy transparency method behavior mappings:
+ // Attribute Behavior
+ // -----------------------------------------------------
+ // Critical (any) Critical
+ // SafeCritical Safe critical
+ // TAS (no critical) No effect
+ // TAS (with any critical) Safe critical
+ virtual MethodSecurityDescriptorFlags MapMethodAttributes(TokenSecurityDescriptorFlags tokenFlags) const
+ {
+ WRAPPER_NO_CONTRACT;
+
+ // Legacy transparency behaves the same for methods as the current transparency model, so we just forward
+ // this call to that implementation.
+ TransparencyBehaviorImpl forwardImpl;
+ return forwardImpl.MapMethodAttributes(tokenFlags);
+ }
+
+ // Legacy transparency module behavior mappings:
+ // Attribute Behavior
+ // -----------------------------------------------------
+ // APTCA APTCA
+ // ConditionlAPTCA Exception
+ // Critical (scoped) Mixed transparency
+ // Critical (all) All critical
+ // SafeCritical All safe critical
+ // TAS (no critical) No effect
+ // TAS (with scoped critical) No effect
+ // TAS (with all critical) All safe critical
+ // Transparent All transparent
+ //
+ // Having no transparent, critical, or safe critical attributes means that the assembly should have all
+ // transparent types and all safe critical methods.
+ virtual ModuleSecurityDescriptorFlags MapModuleAttributes(TokenSecurityDescriptorFlags tokenFlags) const
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ SO_INTOLERANT;
+ }
+ CONTRACTL_END;
+
+ ModuleSecurityDescriptorFlags moduleFlags = ModuleSecurityDescriptorFlags_None;
+ bool fShouldBeOpportunisticallyCritical = true;
+
+#if defined(FEATURE_APTCA) || defined(FEATURE_CORESYSTEM)
+ if (tokenFlags & TokenSecurityDescriptorFlags_APTCA)
+ {
+ moduleFlags |= ModuleSecurityDescriptorFlags_IsAPTCA;
+ }
+#endif // defined(FEATURE_APTCA) || defined(FEATURE_CORESYSTEM)
+
+ if (tokenFlags & TokenSecurityDescriptorFlags_Transparent)
+ {
+ moduleFlags |= ModuleSecurityDescriptorFlags_IsAllTransparent;
+ fShouldBeOpportunisticallyCritical = false;
+ }
+
+ if (tokenFlags & TokenSecurityDescriptorFlags_Critical)
+ {
+ fShouldBeOpportunisticallyCritical = false;
+
+ // If we're critical, but not all critical that means we're mixed.
+ if (tokenFlags & TokenSecurityDescriptorFlags_AllCritical)
+ {
+ moduleFlags |= ModuleSecurityDescriptorFlags_IsAllCritical;
+
+ // If we're all critical and treat as safe, that means we're safe critical
+ if (tokenFlags & TokenSecurityDescriptorFlags_TreatAsSafe)
+ {
+ moduleFlags |= ModuleSecurityDescriptorFlags_IsTreatAsSafe;
+ }
+ }
+ }
+
+ // SafeCritical always means Critical + TreatAsSafe; we can get in this case for legacy assemblies if the
+ // assembly is actually a v4 assembly which is using the Legacy attribute.
+ if (tokenFlags & TokenSecurityDescriptorFlags_SafeCritical)
+ {
+ moduleFlags |= ModuleSecurityDescriptorFlags_IsAllCritical |
+ ModuleSecurityDescriptorFlags_IsTreatAsSafe;
+ fShouldBeOpportunisticallyCritical = false;
+ }
+
+ // If we didn't find an attribute that indicates the assembly cares about transparency, then it is
+ // opportunistically critical.
+ if (fShouldBeOpportunisticallyCritical)
+ {
+ _ASSERTE(!(moduleFlags & ModuleSecurityDescriptorFlags_IsAllTransparent));
+ _ASSERTE(!(moduleFlags & ModuleSecurityDescriptorFlags_IsAllCritical));
+
+ moduleFlags |= ModuleSecurityDescriptorFlags_IsOpportunisticallyCritical;
+ }
+
+ // If the token asks to not have IL verification done in full trust, propigate that to the module
+ if (tokenFlags & TokenSecurityDescriptorFlags_SkipFullTrustVerification)
+ {
+ moduleFlags |= ModuleSecurityDescriptorFlags_SkipFullTrustVerification;
+ }
+
+ return moduleFlags;
+ }
+
+ // Legacy transparency type behavior mappings:
+ // Attribute Behavior
+ // -----------------------------------------------------
+ // Critical (scoped) Critical, but not all critical
+ // Critical (all) All critical
+ // SafeCritical All safe critical
+ // TAS (no critical) No effect on the type, but save TAS bit for members of the type
+ // TAS (with scoped critical) SafeCritical, but not all critical
+ // TAS (with all critical) All SafeCritical
+ virtual TypeSecurityDescriptorFlags MapTypeAttributes(TokenSecurityDescriptorFlags tokenFlags) const
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ SO_INTOLERANT;
+ }
+ CONTRACTL_END;
+
+ TypeSecurityDescriptorFlags typeFlags = TypeSecurityDescriptorFlags_None;
+
+ if (tokenFlags & TokenSecurityDescriptorFlags_Critical)
+ {
+ typeFlags |= TypeSecurityDescriptorFlags_IsCritical;
+
+ // We only consider all critical if the critical attribute was present
+ if (tokenFlags & TokenSecurityDescriptorFlags_AllCritical)
+ {
+ typeFlags |= TypeSecurityDescriptorFlags_IsAllCritical;
+ }
+ }
+
+ // SafeCritical always means all critical + TAS
+ if (tokenFlags & TokenSecurityDescriptorFlags_SafeCritical)
+ {
+ typeFlags |= TypeSecurityDescriptorFlags_IsCritical |
+ TypeSecurityDescriptorFlags_IsAllCritical |
+ TypeSecurityDescriptorFlags_IsTreatAsSafe;
+ }
+
+ if (tokenFlags & TokenSecurityDescriptorFlags_TreatAsSafe)
+ {
+ typeFlags |= TypeSecurityDescriptorFlags_IsTreatAsSafe;
+ }
+
+ return typeFlags;
+ }
+};
+
+#endif // !FEATURE_CORECLR
+
+//
+// Shared transparency behavior objects
+//
+
+//---------------------------------------------------------------------------------------
+//
+// Access a shared security transparency behavior object, creating it if the object has
+// not yet been used.
+//
+
+template <class T>
+const SecurityTransparencyBehavior *GetOrCreateTransparencyBehavior(SecurityTransparencyBehavior **ppBehavior)
+{
+ CONTRACT(const SecurityTransparencyBehavior *)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ PRECONDITION(CheckPointer(ppBehavior));
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ if (*ppBehavior == NULL)
+ {
+ NewHolder<ISecurityTransparencyImpl> pImpl(new T);
+ NewHolder<SecurityTransparencyBehavior> pBehavior(new SecurityTransparencyBehavior(pImpl));
+
+ SecurityTransparencyBehavior *pPrevBehavior =
+ InterlockedCompareExchangeT(ppBehavior, pBehavior.GetValue(), NULL);
+
+ if (pPrevBehavior == NULL)
+ {
+ pBehavior.SuppressRelease();
+ pImpl.SuppressRelease();
+ }
+ }
+
+ RETURN(*ppBehavior);
+}
+
+// Transparency behavior object for v4 transparent assemblies
+// static
+SecurityTransparencyBehavior *SecurityTransparencyBehavior::s_pStandardTransparencyBehavior = NULL;
+
+#ifndef FEATURE_CORECLR
+
+// Transpraency behavior object for v2 transparent assemblies
+// static
+SecurityTransparencyBehavior *SecurityTransparencyBehavior::s_pLegacyTransparencyBehavior = NULL;
+
+#endif // !FEATURE_CORECLR
+
+//---------------------------------------------------------------------------------------
+//
+// Get a security transparency object for an assembly with the specified attributes on
+// its manifest
+//
+// Arguments:
+// moduleTokenFlags - flags from reading the security attributes of the assembly's
+// manifest module
+//
+
+const SecurityTransparencyBehavior *SecurityTransparencyBehavior::GetTransparencyBehavior(SecurityRuleSet ruleSet)
+{
+ CONTRACT(const SecurityTransparencyBehavior *)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ PRECONDITION(ruleSet == SecurityRuleSet_Level1 || ruleSet == SecurityRuleSet_Level2);
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+#ifndef FEATURE_CORECLR
+ if (ruleSet == SecurityRuleSet_Level1)
+ {
+ // Level 1 rules - v2.0 behavior
+ RETURN(GetOrCreateTransparencyBehavior<LegacyTransparencyBehaviorImpl>(&s_pLegacyTransparencyBehavior));
+ }
+ else
+#endif // FEATURE_CORECLR;
+ {
+ // Level 2 rules - v4.0 behavior
+ RETURN(GetOrCreateTransparencyBehavior<TransparencyBehaviorImpl>(&s_pStandardTransparencyBehavior));
+ }
+}