summaryrefslogtreecommitdiff
path: root/src/vm/securityattributes.cpp
diff options
context:
space:
mode:
authorJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
committerJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
commit4b4aad7217d3292650e77eec2cf4c198ea9c3b4b (patch)
tree98110734c91668dfdbb126fcc0e15ddbd93738ca /src/vm/securityattributes.cpp
parentfa45f57ed55137c75ac870356a1b8f76c84b229c (diff)
downloadcoreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.gz
coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.bz2
coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.zip
Imported Upstream version 1.1.0upstream/1.1.0
Diffstat (limited to 'src/vm/securityattributes.cpp')
-rw-r--r--src/vm/securityattributes.cpp2764
1 files changed, 2764 insertions, 0 deletions
diff --git a/src/vm/securityattributes.cpp b/src/vm/securityattributes.cpp
new file mode 100644
index 0000000000..2c89540974
--- /dev/null
+++ b/src/vm/securityattributes.cpp
@@ -0,0 +1,2764 @@
+// 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 "field.h"
+#include "comcallablewrapper.h"
+#include "typeparse.h"
+#include "appdomain.inl"
+#include "mdaassistants.h"
+#include "fstring.h"
+
+
+HRESULT BlobToAttributeSet(BYTE* pBuffer, ULONG cbBuffer, CORSEC_ATTRSET* pAttrSet, DWORD dwAction);
+
+#ifndef CROSSGEN_COMPILE
+
+OBJECTREF SecurityAttributes::CreatePermissionSet(BOOL fTrusted)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ } CONTRACTL_END;
+
+ OBJECTREF pPermSet = NULL;
+ GCPROTECT_BEGIN(pPermSet);
+
+ MethodTable* pMT = MscorlibBinder::GetClass(CLASS__PERMISSION_SET);
+ pPermSet = (OBJECTREF) AllocateObject(pMT);
+
+ ARG_SLOT fStatus = (fTrusted) ? 1 : 0;
+
+ MethodDescCallSite ctor(METHOD__PERMISSION_SET__CTOR);
+
+ ARG_SLOT arg[2] = {
+ ObjToArgSlot(pPermSet),
+ BoolToArgSlot(fStatus)
+ };
+ ctor.Call(arg);
+
+ GCPROTECT_END();
+
+ return pPermSet;
+}
+
+#ifdef FEATURE_CAS_POLICY
+
+// todo: remove the non-cas parameters (because they're bogus now anyway)
+void SecurityAttributes::XmlToPermissionSet(PBYTE pbXmlBlob,
+ DWORD cbXmlBlob,
+ OBJECTREF* pPermSet,
+ OBJECTREF* pEncoding,
+ PBYTE pbNonCasXmlBlob,
+ DWORD cbNonCasXmlBlob,
+ OBJECTREF* pNonCasPermSet,
+ OBJECTREF* pNonCasEncoding)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(IsProtectedByGCFrame (pPermSet));
+ PRECONDITION(IsProtectedByGCFrame (pEncoding));
+ PRECONDITION(IsProtectedByGCFrame (pNonCasPermSet));
+ PRECONDITION(IsProtectedByGCFrame (pNonCasEncoding));
+ } CONTRACTL_END;
+
+ // Get Host Protection Flags
+ EApiCategories eProtectedCategories = GetHostProtectionManager()->GetProtectedCategories();
+
+ MethodDescCallSite decodeXML(METHOD__PERMISSION_SET__DECODE_XML, pPermSet); // can trigger GC
+
+ // Deserialize the CAS PermissionSet
+ if(pbXmlBlob && cbXmlBlob > 0)
+ {
+ _ASSERTE(*pbXmlBlob != LAZY_DECL_SEC_FLAG);
+
+ // Create a new (empty) permission set.
+ *pPermSet = SecurityAttributes::CreatePermissionSet(FALSE);
+
+ // Buffer in managed space.
+ SecurityAttributes::CopyEncodingToByteArray(pbXmlBlob, cbXmlBlob, pEncoding);
+
+ ARG_SLOT args[] = {
+ ObjToArgSlot(*pPermSet),
+ ObjToArgSlot(*pEncoding),
+ (ARG_SLOT)eProtectedCategories,
+ (ARG_SLOT)0,
+ };
+
+ // Deserialize into a managed object.
+ BOOL success = FALSE;
+ EX_TRY
+ {
+ // 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);
+ OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
+ success = decodeXML.Call_RetBool(args);
+ }
+ EX_SWALLOW_NONTERMINAL
+
+ if (!success)
+ COMPlusThrow(kSecurityException, IDS_ENCODEDPERMSET_DECODEFAILURE);
+ }
+
+ // Deserialize the non-CAS PermissionSet
+ if(pbNonCasXmlBlob && cbNonCasXmlBlob > 0)
+ {
+ _ASSERTE(*pbNonCasXmlBlob != LAZY_DECL_SEC_FLAG);
+
+ // Create a new (empty) permission set.
+ *pNonCasPermSet = SecurityAttributes::CreatePermissionSet(FALSE);
+
+ // Buffer in managed space.
+ SecurityAttributes::CopyEncodingToByteArray(pbNonCasXmlBlob, cbNonCasXmlBlob, pNonCasEncoding);
+
+ ARG_SLOT args[] = {
+ ObjToArgSlot(*pNonCasPermSet),
+ ObjToArgSlot(*pNonCasEncoding),
+ (ARG_SLOT)eProtectedCategories,
+ (ARG_SLOT)0,
+ };
+
+ // Deserialize into a managed object.
+ BOOL success = FALSE;
+ EX_TRY
+ {
+ // 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);
+ OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
+ success = decodeXML.Call_RetBool(args);
+ }
+ EX_SWALLOW_NONTERMINAL
+
+ if (!success)
+ COMPlusThrow(kSecurityException, IDS_ENCODEDPERMSET_DECODEFAILURE);
+ }
+}
+
+#endif // FEATURE_CAS_POLICY
+
+//
+// Determine if a security action allows an optimization where an empty permission set can be represented as
+// NULL. Some VM optimizations kick in if an empty permission set can be represented as NULL; however since
+// some security actions have a semantic difference between not being specified at all and having an explicit
+// empty permission set specified, permission sets associated with those actions must be represented as an
+// empty object rather than as NULL.
+//
+// Arguments:
+// action - security action to check
+//
+// Return Value:
+// true if the security action may have an empty permission set optimized to NULL, false otherwise
+//
+// Notes:
+// The security actions which cannot have NULL represent an empty permission set are:
+//
+// * PermitOnly - a PermitOnly set containing no permissions means that all demands should fail, as
+// opposed to not having a PermitOnly set on a method.
+// * RequestOptional - not specifying a RequestOptional set is equivilent to having a RequestOptional set
+// of FullTrust, rather than having an empty RequestOptional set.
+//
+
+// static
+bool SecurityAttributes::ActionAllowsNullPermissionSet(CorDeclSecurity action)
+{
+ LIMITED_METHOD_CONTRACT;
+ return action != dclPermitOnly && action != dclRequestOptional;
+}
+
+#ifdef FEATURE_CAS_POLICY
+
+PsetCacheEntry* SecurityAttributes::MergePermissionSets(IN PsetCacheEntry *pPCE1, IN PsetCacheEntry *pPCE2, IN bool fIntersect, DWORD dwAction)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ } CONTRACTL_END;
+
+ SecurityDeclarativeCache *pSDC;
+ PsetCacheEntry* pMergedPCE;
+
+ struct _gc {
+ OBJECTREF orSet1;
+ OBJECTREF orSet2;
+ OBJECTREF orMergedSet;
+ } gc;
+ memset(&gc, '\0', sizeof(gc));
+ GCPROTECT_BEGIN(gc);
+ {
+ // Union or Intersect the two PermissionSets
+ gc.orSet1 = pPCE1->CreateManagedPsetObject (dwAction);
+
+ if(gc.orSet1 == NULL)
+ pMergedPCE = fIntersect ? pPCE1 : pPCE2;
+ else
+ {
+ gc.orSet2 = pPCE2->CreateManagedPsetObject (dwAction);
+ if(gc.orSet2 == NULL)
+ pMergedPCE = fIntersect ? pPCE2 : pPCE1;
+ else
+ {
+ BinderMethodID methID = (fIntersect ? METHOD__PERMISSION_SET__INTERSECT : METHOD__PERMISSION_SET__UNION);
+ MethodDescCallSite mergeMethod(methID, &gc.orSet1);
+
+ ARG_SLOT args[2] = {
+ ObjToArgSlot(gc.orSet1),
+ ObjToArgSlot(gc.orSet2),
+ };
+ gc.orMergedSet = mergeMethod.Call_RetOBJECTREF(args);
+
+ if(gc.orMergedSet == NULL)
+ gc.orMergedSet = CreatePermissionSet(false);
+
+ // Convert to XML blob
+ PBYTE pbData;
+ DWORD cbData;
+ EncodePermissionSet(&gc.orMergedSet, &pbData, &cbData);
+
+ // Store XML blob and obtain an index to reference it
+ pSDC = &(GetAppDomain()->m_pSecContext->m_pSecurityDeclarativeCache);
+ pMergedPCE = pSDC->CreateAndCachePset (pbData, cbData);
+
+ }
+ }
+ }
+ GCPROTECT_END();
+
+ return pMergedPCE;
+}
+
+#endif // FEATURE_CAS_POLICY
+
+void SecurityAttributes::CopyEncodingToByteArray(IN PBYTE pbData,
+ IN DWORD cbData,
+ OUT OBJECTREF* pArray)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ } CONTRACTL_END;
+
+ U1ARRAYREF pObj;
+ _ASSERTE(pArray);
+
+ pObj = (U1ARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_U1,cbData);
+ memcpyNoGCRefs(pObj->m_Array, pbData, cbData);
+ *pArray = (OBJECTREF) pObj;
+}
+
+void SecurityAttributes::CopyByteArrayToEncoding(IN U1ARRAYREF* pArray,
+ OUT PBYTE* ppbData,
+ OUT DWORD* pcbData)
+{
+ CONTRACTL {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(pArray));
+ PRECONDITION(CheckPointer(ppbData));
+ PRECONDITION(CheckPointer(pcbData));
+ PRECONDITION(*pArray != NULL);
+ } CONTRACTL_END;
+
+ DWORD size = (DWORD) (*pArray)->GetNumComponents();
+ *ppbData = new BYTE[size];
+ *pcbData = size;
+
+ CopyMemory(*ppbData, (*pArray)->GetDirectPointerToNonObjectElements(), size);
+}
+
+#ifdef FEATURE_CAS_POLICY
+void SecurityAttributes::EncodePermissionSet(IN OBJECTREF* pRef,
+ OUT PBYTE* ppbData,
+ OUT DWORD* pcbData)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(IsProtectedByGCFrame (pRef));
+ } CONTRACTL_END;
+
+ MethodDescCallSite encodeXML(METHOD__PERMISSION_SET__ENCODE_XML);
+
+ // Encode up the result
+ ARG_SLOT args1[1];
+ args1[0] = ObjToArgSlot(*pRef);
+ OBJECTREF pByteArray = NULL;
+ pByteArray = encodeXML.Call_RetOBJECTREF(args1);
+
+ SecurityAttributes::CopyByteArrayToEncoding((U1ARRAYREF*) &pByteArray,
+ ppbData,
+ pcbData);
+}
+#endif // FEATURE_CAS_POLICY
+
+#ifdef FEATURE_CAS_POLICY
+static void SetupRestrictSecAttributes()
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ } CONTRACTL_END;
+
+ EX_TRY
+ {
+ MethodDescCallSite setupSecurity(METHOD__PERMISSION_SET__SETUP_SECURITY);
+
+ setupSecurity.Call(NULL);
+ }
+ EX_CATCH
+ {
+ // There is a possibility that we've already set the appdomain policy
+ // level for this process. In that case we'll get a policy exception
+ // that we are free to ignore.
+ OBJECTREF pThrowable = GET_THROWABLE();
+ DefineFullyQualifiedNameForClassOnStack();
+ LPCUTF8 szClass = GetFullyQualifiedNameForClass(pThrowable->GetMethodTable());
+ if (strcmp(g_PolicyExceptionClassName, szClass) != 0)
+ COMPlusThrow(pThrowable);
+ }
+ EX_END_CATCH(RethrowTerminalExceptions)
+}
+#endif // FEATURE_CAS_POLICY
+
+Assembly* SecurityAttributes::LoadAssemblyFromToken(IMetaDataAssemblyImport *pImport, mdAssemblyRef tkAssemblyRef)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(TypeFromToken(tkAssemblyRef) == mdtAssemblyRef);
+
+ // Find all the details needed to name an assembly for loading.
+ ASSEMBLYMETADATA sContext;
+ BYTE *pbPublicKeyOrToken;
+ DWORD cbPublicKeyOrToken;
+ DWORD dwFlags;
+ LPWSTR wszName;
+ DWORD cchName;
+
+ // Initialize ASSEMBLYMETADATA structure.
+ ZeroMemory(&sContext, sizeof(ASSEMBLYMETADATA));
+
+ // Retrieve size of assembly name.
+ HRESULT hr = pImport->GetAssemblyRefProps(tkAssemblyRef, // [IN] The AssemblyRef for which to get the properties.
+ NULL, // [OUT] Pointer to the public key or token.
+ NULL, // [OUT] Count of bytes in the public key or token.
+ NULL, // [OUT] Buffer to fill with name.
+ NULL, // [IN] Size of buffer in wide chars.
+ &cchName, // [OUT] Actual # of wide chars in name.
+ &sContext, // [OUT] Assembly MetaData.
+ NULL, // [OUT] Hash blob.
+ NULL, // [OUT] Count of bytes in the hash blob.
+ NULL); // [OUT] Flags.
+ _ASSERTE(SUCCEEDED(hr));
+
+ // Allocate the necessary buffers.
+ wszName = (LPWSTR)_alloca(cchName * sizeof(WCHAR));
+ sContext.szLocale = (LPWSTR)_alloca(sContext.cbLocale * sizeof(WCHAR));
+ sContext.rProcessor = (DWORD *)_alloca(sContext.ulProcessor * sizeof(DWORD));
+ sContext.rOS = (OSINFO *)_alloca(sContext.ulOS * sizeof(OSINFO));
+
+ // Get the assembly name and rest of naming properties.
+ hr = pImport->GetAssemblyRefProps(tkAssemblyRef,
+ (const void **)&pbPublicKeyOrToken,
+ &cbPublicKeyOrToken,
+ wszName,
+ cchName,
+ &cchName,
+ &sContext,
+ NULL,
+ NULL,
+ &dwFlags);
+ _ASSERTE(SUCCEEDED(hr));
+
+ // We've got the details of the assembly, just need to load it.
+
+ // Convert assembly name to UTF8.
+ MAKE_UTF8PTR_FROMWIDE(uszAssemblyName, wszName);
+
+ // Unfortunately we've got an ASSEMBLYMETADATA structure, but we need
+ // an AssemblyMetaDataInternal
+ AssemblyMetaDataInternal internalContext;
+
+ // Initialize the structure.
+ ZeroMemory(&internalContext, sizeof(AssemblyMetaDataInternal));
+
+ internalContext.usMajorVersion = sContext.usMajorVersion;
+ internalContext.usMinorVersion = sContext.usMinorVersion;
+ internalContext.usBuildNumber = sContext.usBuildNumber;
+ internalContext.usRevisionNumber = sContext.usRevisionNumber;
+ internalContext.rProcessor = sContext.rProcessor;
+ internalContext.ulProcessor = sContext.ulProcessor;
+ internalContext.rOS = sContext.rOS;
+ internalContext.ulOS = sContext.ulOS;
+ if(sContext.cbLocale)
+ {
+ MAKE_UTF8PTR_FROMWIDE(pLocale, sContext.szLocale);
+ internalContext.szLocale = pLocale;
+ }
+ else
+ {
+ internalContext.szLocale = "";
+ }
+
+ Assembly* pAssembly = NULL;
+ {
+ // 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);
+ pAssembly = AssemblySpec::LoadAssembly(uszAssemblyName,
+ &internalContext,
+ pbPublicKeyOrToken,
+ cbPublicKeyOrToken,
+ dwFlags);
+ }
+
+ // @todo: Add CORSECATTR_E_ASSEMBLY_LOAD_FAILED_EX context to this exception path?
+
+ return pAssembly;
+}
+
+TypeHandle FindSecurityAttributeHandle(LPCWSTR wszTypeName)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ } CONTRACTL_END;
+
+ TypeHandle hType;
+ MethodDescCallSite findSecurityAttributeTypeHandle(METHOD__SECURITY_ATTRIBUTE__FIND_SECURITY_ATTRIBUTE_TYPE_HANDLE);
+
+ struct _gc {
+ STRINGREF str;
+ } gc;
+
+ ZeroMemory(&gc, sizeof(gc));
+
+ GCPROTECT_BEGIN(gc);
+ gc.str = StringObject::NewString(wszTypeName);
+ ARG_SLOT arg[1] = {
+ ObjToArgSlot(gc.str)
+ };
+
+ TypeHandle th = TypeHandle::FromPtr(findSecurityAttributeTypeHandle.Call_RetLPVOID(arg));
+ hType = th;
+ GCPROTECT_END();
+
+ return hType;
+}
+
+// @TODO: replace this method with a call to the reflection code that decodes CA blobs
+// and instantiates managed attribute objects. Currently the most significant perf
+// cost of this method is due to TypeName::GetTypeWorker which it calls via
+// GetTypeFromAssemblyQualifiedName, and GetTypeUsingCASearchRules
+HRESULT SecurityAttributes::AttributeSetToManaged(OBJECTREF* /*OUT*/obj, CORSEC_ATTRSET* pAttrSet, OBJECTREF* pThrowable, DWORD* pdwErrorIndex, bool bLazy)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ // Assumption: if the first obj is protected, the whole array is protected
+ if (pAttrSet->dwAttrCount > 0) {PRECONDITION(IsProtectedByGCFrame (obj));}
+ } CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ DWORD i;
+ TypeHandle hType;
+ MethodTable *pMT = NULL;
+ MethodDesc *pMD = NULL;
+
+ // Elevate the allowed loading level
+ // 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);
+ OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
+
+ for (i = 0; i < pAttrSet->dwAttrCount; i++)
+ {
+ CORSEC_ATTRIBUTE *pAttr = &pAttrSet->pAttrs[i];
+
+ if (pdwErrorIndex)
+ *pdwErrorIndex = pAttr->dwIndex;
+
+ // Find the assembly that contains the security attribute class.
+ _ASSERTE(pAttr->pName);
+ Assembly *pAssembly;
+
+ if (bLazy)
+ {
+ // Convert type name to Unicode
+ MAKE_WIDEPTR_FROMUTF8(wszTypeName, pAttr->pName);
+
+ {
+ // Load the type
+ {
+ DWORD error = (DWORD)-1;
+ NewHolder<TypeName> pTypeName = new TypeName(wszTypeName, &error);
+
+ if (error == (DWORD)(-1) && !(pTypeName->GetAssembly()->IsEmpty()))
+ {
+ hType = pTypeName->GetTypeFromAsm(FALSE);
+ }
+ else
+ {
+ hType = TypeName::GetTypeFromAssembly(wszTypeName, SystemDomain::SystemAssembly());
+ }
+ }
+
+ // Special workaround for if the compile-time version of the attribute is no longer available
+ if (hType.IsNull() || hType.GetMethodTable() == NULL)
+ hType = FindSecurityAttributeHandle(wszTypeName);
+ }
+ }
+ else
+ {
+ if (!IsNilToken(pAttr->tkAssemblyRef) && TypeFromToken(pAttr->tkAssemblyRef) == mdtAssemblyRef)
+ {
+ // Load from AssemblyRef token stored in the CORSEC_ATTRSET
+ pAssembly = LoadAssemblyFromToken(pAttrSet->pImport, pAttr->tkAssemblyRef);
+ }
+ else
+ {
+ // Load from MSCORLIB.
+ pAssembly = SystemDomain::SystemAssembly();
+ }
+ _ASSERTE(pAssembly && "Failed to find assembly with declarative attribute");
+
+ EX_TRY
+ {
+ hType = ClassLoader::LoadTypeByNameThrowing(pAssembly, NULL, pAttr->pName);
+ }
+ EX_CATCH_THROWABLE(pThrowable);
+ }
+
+ // Load the security attribute class.
+ if (hType.IsNull() || (pMT = hType.GetMethodTable()) == NULL)
+ {
+ MAKE_WIDEPTR_FROMUTF8(wszTemp, pAttr->pName);
+ SString sMessage;
+ GetExceptionMessage(*pThrowable, sMessage);
+ if (!sMessage.IsEmpty())
+ hr = VMPostError(CORSECATTR_E_TYPE_LOAD_FAILED_EX, wszTemp, sMessage.GetUnicode());
+ else
+ hr = VMPostError(CORSECATTR_E_TYPE_LOAD_FAILED, wszTemp);
+ return hr;
+ }
+
+ // Make sure it's not abstract.
+ if (pMT->IsAbstract())
+ return VMPostError(CORSECATTR_E_ABSTRACT);
+
+#ifdef _DEBUG
+ // Make sure it's really a security attribute class
+ /*{
+ MethodTable *pParentMT = pMT->GetParentMethodTable();
+ CHAR *szClass;
+ DefineFullyQualifiedNameForClassOnStack();
+ while (pParentMT) {
+ szClass = GetFullyQualifiedNameForClass(pParentMT->GetClass());
+ if (stricmpUTF8(szClass, COR_BASE_SECURITY_ATTRIBUTE_CLASS_ANSI) == 0)
+ break;
+ pParentMT = pParentMT->GetParentMethodTable();
+ }
+ _ASSERTE(pParentMT && "Security attribute not derived from COR_BASE_SECURITY_ATTRIBUTE_CLASS");
+ }*/
+#endif
+
+ // Instantiate an instance.
+ obj[i] = pMT->Allocate();
+
+ // Find and call the constructor.
+ pMD = MemberLoader::FindConstructor(pMT, &gsig_IM_SecurityAction_RetVoid);
+ if (pMD == NULL)
+ return VMPostError(CORSECATTR_E_MISSING_CONSTRUCTOR);
+ MethodDescCallSite ctor(pMD);
+ ARG_SLOT args[] = {
+ ObjToArgSlot(obj[i]),
+ (ARG_SLOT)pAttrSet->dwAction
+ };
+ ctor.Call(args);
+
+ // Set the attributes and properties
+ hr = SetAttrFieldsAndProperties(pAttr, pThrowable, pMT, &obj[i]);
+ if (FAILED(hr))
+ return hr;
+ }
+
+ return hr;
+}
+
+
+HRESULT SecurityAttributes::SetAttrFieldsAndProperties(CORSEC_ATTRIBUTE *pAttr, OBJECTREF* pThrowable, MethodTable* pMT, OBJECTREF* pObj)
+{
+ // Setup fields and properties on the object, as specified by the
+ // serialized data passed to us.
+ BYTE *pbBuffer = pAttr->pbValues;
+ SIZE_T cbBuffer = pAttr->cbValues;
+ BYTE *pbBufferEnd = pbBuffer + cbBuffer;
+ DWORD j;
+ HRESULT hr = S_OK;
+
+ EX_TRY
+ {
+ for (j = 0; j < pAttr->wValues; j++)
+ {
+ DWORD dwType = 0;
+ BOOL bIsField = FALSE;
+ BYTE *pbName;
+ DWORD cbName;
+ DWORD dwLength;
+ NewArrayHolder<CHAR> szName(NULL);
+ TypeHandle hEnum;
+ CorElementType eEnumType = ELEMENT_TYPE_END;
+
+ // Check we've got at least the field/property specifier and the
+ // type code.
+ if(cbBuffer < (sizeof(BYTE) + sizeof(BYTE)))
+ {
+ hr = VMPostError(CORSECATTR_E_TRUNCATED);
+ goto Error;
+ }
+
+ // Grab the field/property specifier.
+ bIsField = *(BYTE*)pbBuffer == SERIALIZATION_TYPE_FIELD;
+ if(!bIsField && *(BYTE*)pbBuffer != SERIALIZATION_TYPE_PROPERTY)
+ {
+ hr = VMPostError(CORSECATTR_E_TRUNCATED);
+ goto Error;
+ }
+ pbBuffer += sizeof(BYTE);
+ cbBuffer -= sizeof(BYTE);
+
+ // Grab the value type.
+ dwType = *(BYTE*)pbBuffer;
+ pbBuffer += sizeof(BYTE);
+ cbBuffer -= sizeof(BYTE);
+
+ // If it's a type that needs further specification, get that information
+ switch (dwType)
+ {
+ case SERIALIZATION_TYPE_ENUM:
+ // Immediately after the enum type token is the fully
+ // qualified name of the value type used to represent
+ // the enum.
+ if (FAILED(CPackedLen::SafeGetData((BYTE const *)pbBuffer,
+ (BYTE const *)pbBufferEnd,
+ &cbName,
+ (BYTE const **)&pbName)))
+ {
+ hr = VMPostError(CORSECATTR_E_TRUNCATED);
+ goto Error;
+ }
+
+ // SafeGetData ensured that the name is within the buffer
+ _ASSERTE(FitsIn<DWORD>((pbName - pbBuffer) + cbName));
+ dwLength = static_cast<DWORD>((pbName - pbBuffer) + cbName);
+ pbBuffer += dwLength;
+ cbBuffer -= dwLength;
+
+ // Buffer the name and nul terminate it.
+ szName = new (nothrow) CHAR[cbName + 1];
+ if (szName == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ goto Error;
+ }
+ memcpy(szName, pbName, cbName);
+ szName[cbName] = '\0';
+
+ // Lookup the type (possibly loading an assembly containing
+ // the type).
+ hEnum = TypeName::GetTypeUsingCASearchRules(szName, NULL);
+
+ //If we couldn't find the type, post an error
+ if (hEnum.IsNull())
+ {
+ MAKE_WIDEPTR_FROMUTF8(wszTemp, szName);
+ SString sMessage;
+ GetExceptionMessage(*pThrowable, sMessage);
+ if (!sMessage.IsEmpty())
+ hr = VMPostError(CORSECATTR_E_TYPE_LOAD_FAILED_EX, wszTemp, sMessage.GetUnicode());
+ else
+ hr = VMPostError(CORSECATTR_E_TYPE_LOAD_FAILED, wszTemp);
+ goto Error;
+ }
+
+ // Calculate the underlying primitive type of the
+ // enumeration.
+ eEnumType = hEnum.GetInternalCorElementType();
+ break;
+ case SERIALIZATION_TYPE_SZARRAY:
+ case SERIALIZATION_TYPE_TYPE:
+ // Can't deal with these yet.
+ hr = VMPostError(CORSECATTR_E_UNSUPPORTED_TYPE);
+ goto Error;
+ }
+
+ // Grab the field/property name and length.
+ if (FAILED(CPackedLen::SafeGetData((BYTE const *)pbBuffer,
+ (BYTE const *)pbBufferEnd,
+ &cbName,
+ (BYTE const **)&pbName)))
+ {
+ hr = VMPostError(CORSECATTR_E_TRUNCATED);
+ goto Error;
+ }
+
+ // SafeGetData ensured that the name is within the buffer
+ _ASSERTE(FitsIn<DWORD>((pbName - pbBuffer) + cbName));
+ dwLength = static_cast<DWORD>((pbName - pbBuffer) + cbName);
+ pbBuffer += dwLength;
+ cbBuffer -= dwLength;
+
+ // Buffer the name and null terminate it.
+ szName = new (nothrow) CHAR[cbName + 1];
+ if (szName == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ goto Error;
+ }
+ memcpy(szName, pbName, cbName);
+ szName[cbName] = '\0';
+
+ // Set the field or property
+ if (bIsField)
+ hr = SetAttrField(&pbBuffer, &cbBuffer, dwType, hEnum, pMT, szName, pObj, dwLength, pbName, cbName, eEnumType);
+ else
+ hr = SetAttrProperty(&pbBuffer, &cbBuffer, pMT, dwType, szName, pObj, dwLength, pbName, cbName, eEnumType);
+ }
+ }
+Error:;
+ EX_CATCH
+ {
+ hr = GET_EXCEPTION()->GetHR();
+ if (pThrowable)
+ {
+ *pThrowable = GET_THROWABLE();
+ }
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+ return hr;
+}
+
+HRESULT SecurityAttributes::SetAttrField(BYTE** ppbBuffer, SIZE_T* pcbBuffer, DWORD dwType, TypeHandle hEnum, MethodTable* pMT, __in_z LPSTR szName, OBJECTREF* pObj, DWORD dwLength, BYTE* pbName, DWORD cbName, CorElementType eEnumType)
+{
+ DWORD cbSig = 0;
+ NewArrayHolder<BYTE> pbSig(new (nothrow) BYTE[128]);
+ if (pbSig == NULL)
+ return E_OUTOFMEMORY;
+
+ BYTE *pbBufferEnd = *ppbBuffer + *pcbBuffer;
+
+ // Build the field signature.
+ cbSig += CorSigCompressData((ULONG)IMAGE_CEE_CS_CALLCONV_FIELD, &pbSig[cbSig]);
+ switch (dwType)
+ {
+ case SERIALIZATION_TYPE_BOOLEAN:
+ case SERIALIZATION_TYPE_I1:
+ case SERIALIZATION_TYPE_I2:
+ case SERIALIZATION_TYPE_I4:
+ case SERIALIZATION_TYPE_I8:
+ case SERIALIZATION_TYPE_U1:
+ case SERIALIZATION_TYPE_U2:
+ case SERIALIZATION_TYPE_U4:
+ case SERIALIZATION_TYPE_U8:
+ case SERIALIZATION_TYPE_R4:
+ case SERIALIZATION_TYPE_R8:
+ case SERIALIZATION_TYPE_CHAR:
+ static_assert_no_msg(SERIALIZATION_TYPE_BOOLEAN == (CorSerializationType)ELEMENT_TYPE_BOOLEAN);
+ static_assert_no_msg(SERIALIZATION_TYPE_I1 == (CorSerializationType)ELEMENT_TYPE_I1);
+ static_assert_no_msg(SERIALIZATION_TYPE_I2 == (CorSerializationType)ELEMENT_TYPE_I2);
+ static_assert_no_msg(SERIALIZATION_TYPE_I4 == (CorSerializationType)ELEMENT_TYPE_I4);
+ static_assert_no_msg(SERIALIZATION_TYPE_I8 == (CorSerializationType)ELEMENT_TYPE_I8);
+ static_assert_no_msg(SERIALIZATION_TYPE_U1 == (CorSerializationType)ELEMENT_TYPE_U1);
+ static_assert_no_msg(SERIALIZATION_TYPE_U2 == (CorSerializationType)ELEMENT_TYPE_U2);
+ static_assert_no_msg(SERIALIZATION_TYPE_U4 == (CorSerializationType)ELEMENT_TYPE_U4);
+ static_assert_no_msg(SERIALIZATION_TYPE_U8 == (CorSerializationType)ELEMENT_TYPE_U8);
+ static_assert_no_msg(SERIALIZATION_TYPE_R4 == (CorSerializationType)ELEMENT_TYPE_R4);
+ static_assert_no_msg(SERIALIZATION_TYPE_R8 == (CorSerializationType)ELEMENT_TYPE_R8);
+ static_assert_no_msg(SERIALIZATION_TYPE_CHAR == (CorSerializationType)ELEMENT_TYPE_CHAR);
+ cbSig += CorSigCompressData(dwType, &pbSig[cbSig]);
+ break;
+ case SERIALIZATION_TYPE_STRING:
+ cbSig += CorSigCompressData((ULONG)ELEMENT_TYPE_STRING, &pbSig[cbSig]);
+ break;
+ case SERIALIZATION_TYPE_ENUM:
+ // To avoid problems when the field and enum are defined
+ // in different scopes (we'd have to go hunting for
+ // typerefs), we build a signature with a special type
+ // (ELEMENT_TYPE_INTERNAL, which contains a TypeHandle).
+ // This compares loaded types for indentity.
+ cbSig += CorSigCompressData((ULONG)ELEMENT_TYPE_INTERNAL, &pbSig[cbSig]);
+ cbSig += CorSigCompressPointer(hEnum.AsPtr(), &pbSig[cbSig]);
+ break;
+ default:
+ return VMPostError(CORSECATTR_E_UNSUPPORTED_TYPE);
+ }
+
+
+ // Locate a field desc.
+ FieldDesc* pFD = MemberLoader::FindField(pMT, szName, (PCCOR_SIGNATURE)pbSig,
+ cbSig, pMT->GetModule());
+ if (pFD == NULL)
+ {
+ MAKE_WIDEPTR_FROMUTF8(wszTemp, szName);
+ return VMPostError(CORSECATTR_E_NO_FIELD, wszTemp);
+ }
+
+ // Set the field value.
+ LPSTR szString;
+ switch (dwType)
+ {
+ case SERIALIZATION_TYPE_BOOLEAN:
+ case SERIALIZATION_TYPE_I1:
+ case SERIALIZATION_TYPE_U1:
+ if(*pcbBuffer < sizeof(BYTE))
+ return VMPostError(CORSECATTR_E_TRUNCATED);
+ pFD->SetValue8(*pObj, *(BYTE*)(*ppbBuffer));
+ (*ppbBuffer) += sizeof(BYTE);
+ (*pcbBuffer) -= sizeof(BYTE);
+ break;
+ case SERIALIZATION_TYPE_CHAR:
+ case SERIALIZATION_TYPE_I2:
+ case SERIALIZATION_TYPE_U2:
+ if(*pcbBuffer < sizeof(WORD))
+ return VMPostError(CORSECATTR_E_TRUNCATED);
+ pFD->SetValue16(*pObj, GET_UNALIGNED_VAL16(*ppbBuffer));
+ (*ppbBuffer) += sizeof(WORD);
+ (*pcbBuffer) -= sizeof(WORD);
+ break;
+ case SERIALIZATION_TYPE_I4:
+ case SERIALIZATION_TYPE_U4:
+ case SERIALIZATION_TYPE_R4:
+ if(*pcbBuffer < sizeof(DWORD))
+ return VMPostError(CORSECATTR_E_TRUNCATED);
+ pFD->SetValue32(*pObj, GET_UNALIGNED_VAL32(*ppbBuffer));
+ (*ppbBuffer) += sizeof(DWORD);
+ (*pcbBuffer) -= sizeof(DWORD);
+ break;
+ case SERIALIZATION_TYPE_I8:
+ case SERIALIZATION_TYPE_U8:
+ case SERIALIZATION_TYPE_R8:
+ if(*pcbBuffer < sizeof(INT64))
+ return VMPostError(CORSECATTR_E_TRUNCATED);
+ pFD->SetValue64(*pObj, GET_UNALIGNED_VAL64(*ppbBuffer));
+ (*ppbBuffer) += sizeof(INT64);
+ (*pcbBuffer) -= sizeof(INT64);
+ break;
+ case SERIALIZATION_TYPE_STRING:
+ // Ensures special case 'null' check below does not overrun buffer
+ if(*ppbBuffer >= pbBufferEnd) {
+ return VMPostError(CORSECATTR_E_TRUNCATED);
+ }
+ // Special case 'null' (represented as a length byte of '0xFF').
+ if (*(*ppbBuffer) == 0xFF) {
+ szString = NULL;
+ dwLength = sizeof(BYTE);
+ } else {
+ if (FAILED(CPackedLen::SafeGetData((BYTE const *)*ppbBuffer,
+ (BYTE const *)pbBufferEnd,
+ &cbName,
+ (BYTE const **)&pbName)))
+ {
+ return VMPostError(CORSECATTR_E_TRUNCATED);
+ }
+
+ // SafeGetData will ensure the name is within the buffer
+ _ASSERTE(FitsIn<DWORD>((pbName - *ppbBuffer) + cbName));
+ dwLength = static_cast<DWORD>((pbName - *ppbBuffer) + cbName);
+
+ DWORD allocLen = cbName + 1;
+ // Buffer and nul terminate it.
+ szString = (LPSTR)_alloca(allocLen);
+ memcpy(szString, pbName, cbName);
+ szString[cbName] = '\0';
+
+ }
+
+ // Allocate and initialize a managed version of the string.
+ {
+ STRINGREF orString;
+ if (szString)
+ {
+ orString = StringObject::NewString(szString, cbName);
+ if (orString == NULL)
+ COMPlusThrowOM();
+ }
+ else
+ orString = NULL;
+
+ pFD->SetRefValue(*pObj, (OBJECTREF)orString);
+ }
+
+ (*ppbBuffer) += dwLength;
+ (*pcbBuffer) -= dwLength;
+ break;
+ case SERIALIZATION_TYPE_ENUM:
+ // Get the underlying primitive type.
+ switch (eEnumType)
+ {
+ case ELEMENT_TYPE_I1:
+ case ELEMENT_TYPE_U1:
+ if(*pcbBuffer < sizeof(BYTE))
+ return VMPostError(CORSECATTR_E_TRUNCATED);
+ pFD->SetValue8(*pObj, *(BYTE*)(*ppbBuffer));
+ (*ppbBuffer) += sizeof(BYTE);
+ (*pcbBuffer) -= sizeof(BYTE);
+ break;
+ case ELEMENT_TYPE_I2:
+ case ELEMENT_TYPE_U2:
+ if(*pcbBuffer < sizeof(WORD))
+ return VMPostError(CORSECATTR_E_TRUNCATED);
+ pFD->SetValue16(*pObj, GET_UNALIGNED_VAL16(*ppbBuffer));
+ (*ppbBuffer) += sizeof(WORD);
+ (*pcbBuffer) -= sizeof(WORD);
+ break;
+ case ELEMENT_TYPE_I4:
+ case ELEMENT_TYPE_U4:
+ if(*pcbBuffer < sizeof(DWORD))
+ return VMPostError(CORSECATTR_E_TRUNCATED);
+ pFD->SetValue32(*pObj, GET_UNALIGNED_VAL32(*ppbBuffer));
+ (*ppbBuffer) += sizeof(DWORD);
+ (*pcbBuffer) -= sizeof(DWORD);
+ break;
+ default:
+ return VMPostError(CORSECATTR_E_UNSUPPORTED_ENUM_TYPE);
+ }
+ break;
+ default:
+ return VMPostError(CORSECATTR_E_UNSUPPORTED_TYPE);
+ }
+ return S_OK;
+}
+
+HRESULT SecurityAttributes::SetAttrProperty(BYTE** ppbBuffer, SIZE_T* pcbBuffer, MethodTable* pMT, DWORD dwType, __in_z LPSTR szName, OBJECTREF* pObj, DWORD dwLength, BYTE* pbName, DWORD cbName, CorElementType eEnumType)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ PRECONDITION(IsProtectedByGCFrame (pObj));
+ } CONTRACTL_END;
+
+ // Locate the property setter.
+ MethodDesc* pMD = MemberLoader::FindPropertyMethod(pMT, szName, PropertySet);
+ if (pMD == NULL)
+ {
+ MAKE_WIDEPTR_FROMUTF8(wszTemp, szName);
+ return VMPostError(CORSECATTR_E_NO_PROPERTY, wszTemp);
+ }
+
+ MethodDescCallSite propSet(pMD);
+
+ // Build the argument list.
+ ARG_SLOT args[2] = { NULL, NULL };
+ LPSTR szString;
+ NewHolder<BYTE> tmpLargeStringHolder (NULL);
+
+ switch (dwType)
+ {
+ case SERIALIZATION_TYPE_BOOLEAN:
+ case SERIALIZATION_TYPE_I1:
+ case SERIALIZATION_TYPE_U1:
+ if(*pcbBuffer < sizeof(BYTE))
+ return VMPostError(CORSECATTR_E_TRUNCATED);
+ args[1] = (ARG_SLOT)*(BYTE*)(*ppbBuffer);
+ (*ppbBuffer) += sizeof(BYTE);
+ (*pcbBuffer) -= sizeof(BYTE);
+ break;
+ case SERIALIZATION_TYPE_CHAR:
+ case SERIALIZATION_TYPE_I2:
+ case SERIALIZATION_TYPE_U2:
+ if(*pcbBuffer < sizeof(WORD))
+ return VMPostError(CORSECATTR_E_TRUNCATED);
+ args[1] = (ARG_SLOT)GET_UNALIGNED_VAL16(*ppbBuffer);
+ (*ppbBuffer) += sizeof(WORD);
+ (*pcbBuffer) -= sizeof(WORD);
+ break;
+ case SERIALIZATION_TYPE_I4:
+ case SERIALIZATION_TYPE_U4:
+ case SERIALIZATION_TYPE_R4:
+ if(*pcbBuffer < sizeof(DWORD))
+ return VMPostError(CORSECATTR_E_TRUNCATED);
+ args[1] = (ARG_SLOT)GET_UNALIGNED_VAL32(*ppbBuffer);
+ (*ppbBuffer) += sizeof(DWORD);
+ (*pcbBuffer) -= sizeof(DWORD);
+ break;
+ case SERIALIZATION_TYPE_I8:
+ case SERIALIZATION_TYPE_U8:
+ case SERIALIZATION_TYPE_R8:
+ if(*pcbBuffer < sizeof(INT64))
+ return VMPostError(CORSECATTR_E_TRUNCATED);
+ args[1] = (ARG_SLOT)GET_UNALIGNED_VAL64(*ppbBuffer);
+ (*ppbBuffer) += sizeof(INT64);
+ (*pcbBuffer) -= sizeof(INT64);
+ break;
+ case SERIALIZATION_TYPE_STRING:
+ // Ensures special case 'null' check below does not overrun buffer
+ if(*pcbBuffer < sizeof(BYTE)) {
+ return VMPostError(CORSECATTR_E_TRUNCATED);
+ }
+ // Special case 'null' (represented as a length byte of '0xFF').
+ if (*(*ppbBuffer) == 0xFF) {
+ szString = NULL;
+ dwLength = sizeof(BYTE);
+ if(*pcbBuffer < sizeof(BYTE))
+ return VMPostError(CORSECATTR_E_TRUNCATED);
+ } else {
+
+ if (FAILED(CPackedLen::SafeGetData((BYTE const *)(*ppbBuffer),
+ (BYTE const *)(*ppbBuffer + *pcbBuffer),
+ &cbName,
+ (BYTE const **)&pbName)))
+ {
+ return VMPostError(CORSECATTR_E_TRUNCATED);
+ }
+
+ // Used below - SafeGetData ensures that name is within the buffer
+ _ASSERTE(FitsIn<DWORD>((pbName - *ppbBuffer) + cbName));
+ dwLength = static_cast<DWORD>((pbName - *ppbBuffer) + cbName);
+
+ DWORD allocLen = cbName + 1;
+
+ //
+ // For smaller size strings allocate from stack, use heap otherwise
+ //
+
+ if ((pbName - *ppbBuffer) < 4) {
+ // Buffer and nul terminate it.
+ szString = (LPSTR)_alloca(allocLen);
+ } else {
+ tmpLargeStringHolder = new BYTE[allocLen];
+ szString = (LPSTR) ((BYTE*)tmpLargeStringHolder);
+ }
+
+ memcpy(szString, pbName, cbName);
+ szString[cbName] = '\0';
+ }
+
+ // Allocate and initialize a managed version of the string.
+ {
+ STRINGREF orString;
+
+ if (szString) {
+ orString = StringObject::NewString(szString, cbName);
+ if (orString == NULL)
+ COMPlusThrowOM();
+ } else
+ orString = NULL;
+
+ args[1] = ObjToArgSlot(orString);
+ }
+
+ (*ppbBuffer) += dwLength;
+ (*pcbBuffer) -= dwLength;
+ break;
+ case SERIALIZATION_TYPE_ENUM:
+ // Get the underlying primitive type.
+ switch (eEnumType)
+ {
+ case ELEMENT_TYPE_I1:
+ case ELEMENT_TYPE_U1:
+ if(*pcbBuffer < sizeof(BYTE))
+ return VMPostError(CORSECATTR_E_TRUNCATED);
+ args[1] = (ARG_SLOT)*(BYTE*)(*ppbBuffer);
+ (*ppbBuffer) += sizeof(BYTE);
+ (*pcbBuffer) -= sizeof(BYTE);
+ break;
+ case ELEMENT_TYPE_I2:
+ case ELEMENT_TYPE_U2:
+ if(*pcbBuffer < sizeof(WORD))
+ return VMPostError(CORSECATTR_E_TRUNCATED);
+ args[1] = (ARG_SLOT)GET_UNALIGNED_VAL16(*ppbBuffer);
+ (*ppbBuffer) += sizeof(WORD);
+ (*pcbBuffer) -= sizeof(WORD);
+ break;
+ case ELEMENT_TYPE_I4:
+ case ELEMENT_TYPE_U4:
+ if(*pcbBuffer < sizeof(DWORD))
+ return VMPostError(CORSECATTR_E_TRUNCATED);
+ args[1] = (ARG_SLOT)GET_UNALIGNED_VAL32(*ppbBuffer);
+ (*ppbBuffer) += sizeof(DWORD);
+ (*pcbBuffer) -= sizeof(DWORD);
+ break;
+ default:
+ return VMPostError(CORSECATTR_E_UNSUPPORTED_ENUM_TYPE);
+ }
+ break;
+ default:
+ return VMPostError(CORSECATTR_E_UNSUPPORTED_TYPE);
+ }
+
+
+ // ! don't move this up, StringObject::NewString
+ // ! inside the switch causes a GC
+ args[0] = ObjToArgSlot(*pObj);
+
+ // Call the setter.
+ propSet.Call(args);
+
+ return S_OK;
+}
+
+
+void SecurityAttributes::AttrSetBlobToPermissionSets(
+ IN BYTE* pbRawPermissions,
+ IN DWORD cbRawPermissions,
+ OUT OBJECTREF* pObj,
+ DWORD dwAction)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ } CONTRACTL_END;
+
+ _ASSERTE(pbRawPermissions);
+ _ASSERTE(cbRawPermissions > 0);
+ _ASSERTE(pbRawPermissions[0] == LAZY_DECL_SEC_FLAG);
+
+ HRESULT hr = S_OK;
+ CORSEC_ATTRSET pset;
+
+ // Deserialize the CORSEC_ATTRSET
+ hr = BlobToAttributeSet(pbRawPermissions, cbRawPermissions, &pset, dwAction);
+ if(FAILED(hr))
+ COMPlusThrowHR(hr);
+
+ OBJECTREF throwable = NULL;
+ GCPROTECT_BEGIN(throwable);
+ {
+ // allocate and GC-protect an array of objectrefs to reference the permissions
+ OBJECTREF* attrArray = (OBJECTREF*)_alloca(pset.dwAttrCount * sizeof(OBJECTREF));
+ memset(attrArray, 0, pset.dwAttrCount * sizeof(OBJECTREF));
+ GCPROTECT_ARRAY_BEGIN(*attrArray, pset.dwAttrCount);
+ {
+ // Convert to a managed array of attribute objects
+ DWORD dwErrorIndex;
+ hr = AttributeSetToManaged(/*OUT*/attrArray, &pset, &throwable, &dwErrorIndex, true);
+
+ // Convert the array of attribute objects to a serialized PermissionSet
+ if (SUCCEEDED(hr))
+ {
+ BYTE* pbXmlBlob = NULL;
+ DWORD cbXmlBlob = 0;
+ BYTE* pbNonCasXmlBlob = NULL;
+ DWORD cbNonCasXmlBlob = 0;
+
+ AttrArrayToPermissionSet(attrArray,
+ false,
+ pset.dwAttrCount,
+ &pbXmlBlob,
+ &cbXmlBlob,
+ &pbNonCasXmlBlob,
+ &cbNonCasXmlBlob,
+ ActionAllowsNullPermissionSet(static_cast<CorDeclSecurity>(dwAction)),
+ pObj);
+
+ _ASSERTE(pbXmlBlob == NULL && cbXmlBlob == 0 && pbNonCasXmlBlob == NULL && cbNonCasXmlBlob == 0);
+ }
+ }
+ GCPROTECT_END();
+ }
+ GCPROTECT_END();
+
+ if(FAILED(hr))
+ COMPlusThrowHR(hr);
+}
+
+#ifdef FEATURE_CAS_POLICY
+HRESULT SecurityAttributes::TranslateSecurityAttributesHelper(
+ CORSEC_ATTRSET *pAttrSet,
+ BYTE **ppbOutput,
+ DWORD *pcbOutput,
+ BYTE **ppbNonCasOutput,
+ DWORD *pcbNonCasOutput,
+ DWORD *pdwErrorIndex)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ } CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ OBJECTREF *attrArray;
+ DWORD dwGlobalError = 0;
+
+ EX_TRY
+ {
+ if (pdwErrorIndex)
+ dwGlobalError = *pdwErrorIndex;
+
+ // Get into the context of the special compilation appdomain (which has an
+ // AppBase set to the current directory).
+ ComCallWrapper *pWrap = ComCallWrapper::GetWrapperFromIP(pAttrSet->pAppDomain);
+
+ ENTER_DOMAIN_ID(pWrap->GetDomainID())
+ {
+ struct _gc {
+ OBJECTREF throwable;
+ OBJECTREF orPermSet;
+ } gc;
+ ZeroMemory(&gc, sizeof(gc));
+ GCPROTECT_BEGIN(gc);
+ {
+ // we need to setup special security settings that we use during compilation
+ SetupRestrictSecAttributes();
+
+ // allocate and protect an array of objectrefs to reference the permissions
+ attrArray = (OBJECTREF*)_alloca(pAttrSet->dwAttrCount * sizeof(OBJECTREF));
+ memset(attrArray, 0, pAttrSet->dwAttrCount * sizeof(OBJECTREF));
+ GCPROTECT_ARRAY_BEGIN(*attrArray, pAttrSet->dwAttrCount);
+ {
+ // Convert to an array of attributes, and then serialize to XML
+ hr = AttributeSetToManaged(/*OUT*/attrArray, pAttrSet, &gc.throwable, pdwErrorIndex, false);
+ if (SUCCEEDED(hr))
+ {
+ if (pdwErrorIndex)
+ *pdwErrorIndex = dwGlobalError;
+
+ // Convert the array of attribute objects to a serialized PermissionSet or PermissionSetCollection
+ AttrArrayToPermissionSet(attrArray,
+ true,
+ pAttrSet->dwAttrCount,
+ ppbOutput,
+ pcbOutput,
+ ppbNonCasOutput,
+ pcbNonCasOutput,
+ ActionAllowsNullPermissionSet(static_cast<CorDeclSecurity>(pAttrSet->dwAction)),
+ &gc.orPermSet);
+ }
+ }
+ GCPROTECT_END();
+ }
+ GCPROTECT_END(); // for throwable
+ }
+ END_DOMAIN_TRANSITION;
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+}
+#endif // FEATURE_CAS_POLICY
+
+// Call into managed code to group permissions into a PermissionSet and serialize it to XML
+void SecurityAttributes::AttrArrayToPermissionSet(OBJECTREF* attrArray,
+ bool fSerialize,
+ DWORD attrCount,
+ BYTE **ppbOutput,
+ DWORD *pcbOutput,
+ BYTE **ppbNonCasOutput,
+ DWORD *pcbNonCasOutput,
+ bool fAllowEmptyPermissionSet,
+ OBJECTREF* pPermSet)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ } CONTRACTL_END;
+
+ EApiCategories eProtectedCategories = (EApiCategories)(GetHostProtectionManager()->GetProtectedCategories());
+
+ MethodDescCallSite createSerialized(METHOD__PERMISSION_SET__CREATE_SERIALIZED);
+
+ // Allocate a managed array of security attribute objects for input to the function.
+ PTRARRAYREF orInput = (PTRARRAYREF) AllocateObjectArray(attrCount, g_pObjectClass);
+
+ // Copy over the permission objects references.
+ DWORD i;
+ for (i = 0; i < attrCount; i++)
+ {
+ orInput->SetAt(i, attrArray[i]);
+ }
+
+ // Call the routine.
+ struct _gc {
+ U1ARRAYREF orNonCasOutput;
+ } gc;
+ ZeroMemory(&gc, sizeof(gc));
+ GCPROTECT_BEGIN(gc);
+
+ ARG_SLOT args[] = {
+ ObjToArgSlot(orInput),
+ BoolToArgSlot(fSerialize),
+ PtrToArgSlot(&gc.orNonCasOutput),
+ PtrToArgSlot(pPermSet),
+ (ARG_SLOT)eProtectedCategories,
+ BoolToArgSlot(fAllowEmptyPermissionSet)
+ };
+ U1ARRAYREF orOutput = NULL;
+
+ {
+ // Elevate the allowed loading level
+ // 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);
+ OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
+
+ orOutput = (U1ARRAYREF) createSerialized.Call_RetOBJECTREF(args);
+ }
+
+ // Buffer the managed output in a native binary blob.
+ // Special case the empty blob. We might get a second blob output if
+ // there were any non-CAS permissions present.
+ NewArrayHolder<BYTE> TempOutput(NULL);
+ NewArrayHolder<BYTE> TempNonCasOutput(NULL);
+
+ if (orOutput == NULL)
+ {
+ *pcbOutput = 0;
+ }
+ else
+ {
+ BYTE *pbArray = orOutput->GetDataPtr();
+ DWORD cbArray = orOutput->GetNumComponents();
+ TempOutput = new BYTE[cbArray];
+ memcpy(TempOutput, pbArray, cbArray);
+ *pcbOutput = cbArray;
+ }
+
+ if (gc.orNonCasOutput == NULL)
+ {
+ *pcbNonCasOutput = 0;
+ }
+ else
+ {
+ BYTE *pbArray = gc.orNonCasOutput->GetDataPtr();
+ DWORD cbArray = gc.orNonCasOutput->GetNumComponents();
+ TempNonCasOutput = new BYTE[cbArray];
+ memcpy(TempNonCasOutput, pbArray, cbArray);
+ *pcbNonCasOutput = cbArray;
+ }
+
+ *ppbOutput = TempOutput;
+ *ppbNonCasOutput = TempNonCasOutput;
+
+ TempOutput.SuppressRelease();
+ TempNonCasOutput.SuppressRelease();
+
+ GCPROTECT_END();
+}
+
+
+//
+// This is a public exported method
+//
+
+// Translate a set of security custom attributes into a serialized permission set blob.
+HRESULT STDMETHODCALLTYPE TranslateSecurityAttributes(CORSEC_ATTRSET *pAttrSet,
+ BYTE **ppbOutput,
+ DWORD *pcbOutput,
+ BYTE **ppbNonCasOutput,
+ DWORD *pcbNonCasOutput,
+ DWORD *pdwErrorIndex)
+{
+#ifdef FEATURE_CAS_POLICY
+ CONTRACTL {
+ NOTHROW;
+ GC_TRIGGERS;
+ ENTRY_POINT;
+ MODE_ANY;
+ } CONTRACTL_END;
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ GCX_COOP(); // because it calls into managed code to instantiate the PermissionSet objects
+ hr = SecurityAttributes::TranslateSecurityAttributesHelper(pAttrSet, ppbOutput, pcbOutput,
+ ppbNonCasOutput, pcbNonCasOutput, pdwErrorIndex);
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#else
+ return E_NOTIMPL;
+#endif
+}
+
+
+//
+// This is a public exported method
+//
+
+// Reads permission requests (if any) from the manifest of an assembly.
+HRESULT STDMETHODCALLTYPE GetPermissionRequests(LPCWSTR pwszFileName,
+ BYTE **ppbMinimal,
+ DWORD *pcbMinimal,
+ BYTE **ppbOptional,
+ DWORD *pcbOptional,
+ BYTE **ppbRefused,
+ DWORD *pcbRefused)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ ENTRY_POINT;
+ } CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ BEGIN_EXTERNAL_ENTRYPOINT(&hr)
+ {
+ IMetaDataDispenser *pMD = NULL;
+ IMetaDataAssemblyImport *pMDAsmImport = NULL;
+ IMetaDataImport *pMDImport = NULL;
+ mdAssembly mdAssembly;
+ BYTE *pbMinimal = NULL;
+ DWORD cbMinimal = 0;
+ BYTE *pbOptional = NULL;
+ DWORD cbOptional = 0;
+ BYTE *pbRefused = NULL;
+ DWORD cbRefused = 0;
+ HCORENUM hEnumDcl = NULL;
+ mdPermission rPSets[dclMaximumValue + 1];
+ DWORD dwSets;
+ DWORD i;
+
+ *ppbMinimal = NULL;
+ *pcbMinimal = 0;
+ *ppbOptional = NULL;
+ *pcbOptional = 0;
+ *ppbRefused = NULL;
+ *pcbRefused = 0;
+
+ // Get the meta data interface dispenser.
+ hr = MetaDataGetDispenser(CLSID_CorMetaDataDispenser,
+ IID_IMetaDataDispenserEx,
+ (void **)&pMD);
+ if (FAILED(hr))
+ goto Error;
+
+ // Open a scope on the assembly file.
+ hr = pMD->OpenScope(pwszFileName,
+ 0,
+ IID_IMetaDataAssemblyImport,
+ (IUnknown**)&pMDAsmImport);
+ if (FAILED(hr))
+ goto Error;
+
+ // Determine the assembly token.
+ hr = pMDAsmImport->GetAssemblyFromScope(&mdAssembly);
+ if (FAILED(hr))
+ goto Error;
+
+ // QI for a normal import interface.
+ hr = pMDAsmImport->QueryInterface(IID_IMetaDataImport, (void**)&pMDImport);
+ if (FAILED(hr))
+ goto Error;
+
+ // Look for permission request sets hung off the assembly token.
+ hr = pMDImport->EnumPermissionSets(&hEnumDcl,
+ mdAssembly,
+ dclActionNil,
+ rPSets,
+ dclMaximumValue + 1,
+ &dwSets);
+ if (FAILED(hr))
+ goto Error;
+
+ for (i = 0; i < dwSets; i++) {
+ BYTE *pbData;
+ DWORD cbData;
+ DWORD dwAction;
+
+ pMDImport->GetPermissionSetProps(rPSets[i],
+ &dwAction,
+ (void const **)&pbData,
+ &cbData);
+
+ switch (dwAction) {
+ case dclRequestMinimum:
+ _ASSERTE(pbMinimal == NULL);
+ pbMinimal = pbData;
+ cbMinimal = cbData;
+ break;
+ case dclRequestOptional:
+ _ASSERTE(pbOptional == NULL);
+ pbOptional = pbData;
+ cbOptional = cbData;
+ break;
+ case dclRequestRefuse:
+ _ASSERTE(pbRefused == NULL);
+ pbRefused = pbData;
+ cbRefused = cbData;
+ break;
+ default:
+ _ASSERTE(FALSE);
+ }
+ }
+
+ pMDImport->CloseEnum(hEnumDcl);
+
+ // Buffer the results (since we're about to close the metadata scope and
+ // lose the original data).
+ if (pbMinimal) {
+ *ppbMinimal = new (nothrow) BYTE[cbMinimal];
+ if (*ppbMinimal == NULL) {
+ hr = E_OUTOFMEMORY;
+ goto Error;
+ }
+ memcpy(*ppbMinimal, pbMinimal, cbMinimal);
+ *pcbMinimal = cbMinimal;
+ }
+
+ if (pbOptional) {
+ *ppbOptional = new (nothrow) BYTE[cbOptional];
+ if (*ppbOptional == NULL) {
+ hr = E_OUTOFMEMORY;
+ goto Error;
+ }
+ memcpy(*ppbOptional, pbOptional, cbOptional);
+ *pcbOptional = cbOptional;
+ }
+
+ if (pbRefused) {
+ *ppbRefused = new (nothrow) BYTE[cbRefused];
+ if (*ppbRefused == NULL) {
+ hr = E_OUTOFMEMORY;
+ goto Error;
+ }
+ memcpy(*ppbRefused, pbRefused, cbRefused);
+ *pcbRefused = cbRefused;
+ }
+
+ Error:
+ if (pMDImport)
+ pMDImport->Release();
+ if (pMDAsmImport)
+ pMDAsmImport->Release();
+ if (pMD)
+ pMD->Release();
+ }
+ END_EXTERNAL_ENTRYPOINT;
+
+ return hr;
+}
+
+// Load permission requests in their serialized form from assembly metadata.
+// This consists of a required permissions set and optionally an optional and
+// deny permission set.
+void SecurityAttributes::LoadPermissionRequestsFromAssembly(IN IMDInternalImport* pImport,
+ OUT OBJECTREF* pReqdPermissions,
+ OUT OBJECTREF* pOptPermissions,
+ OUT OBJECTREF* pDenyPermissions)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pReqdPermissions));
+ PRECONDITION(CheckPointer(pOptPermissions));
+ PRECONDITION(CheckPointer(pDenyPermissions));
+ } CONTRACTL_END;
+
+ mdAssembly mdAssembly;
+ HRESULT hr;
+
+ *pReqdPermissions = NULL;
+ *pOptPermissions = NULL;
+ *pDenyPermissions = NULL;
+
+ // It's OK to be called with a NULL assembly. This can happen in the code
+ // path where we're just checking for a signature, nothing else. So just
+ // return without doing anything.
+ if (pImport == NULL)
+ return;
+
+ // Locate assembly metadata token since the various permission sets are
+ // written as custom values against this token.
+ if (pImport->GetAssemblyFromScope(&mdAssembly) != S_OK) {
+ _ASSERT(FALSE);
+ return;
+ }
+
+ struct _gc
+ {
+ OBJECTREF reqdPset;
+ OBJECTREF optPset;
+ OBJECTREF denyPset;
+ } gc;
+ ZeroMemory(&gc, sizeof(gc));
+
+ {
+ GCX_COOP(); // because GetDeclaredPermissions may call into managed code
+ GCPROTECT_BEGIN(gc);
+
+ // Read and translate required permission set.
+ hr = Security::GetDeclaredPermissions(pImport, mdAssembly, dclRequestMinimum, &gc.reqdPset, NULL);
+ _ASSERT(SUCCEEDED(hr) || (hr == CLDB_E_RECORD_NOTFOUND));
+
+ // Now the optional permission set.
+ PsetCacheEntry *pOptPSCacheEntry = NULL;
+ hr = Security::GetDeclaredPermissions(pImport, mdAssembly, dclRequestOptional, &gc.optPset, &pOptPSCacheEntry);
+ _ASSERT(SUCCEEDED(hr) || (hr == CLDB_E_RECORD_NOTFOUND));
+
+ // An empty permission set has semantic meaning if it is an assembly's optional permission set.
+ // If we have an optional set, then we need to make sure it is created.
+ if (SUCCEEDED(hr) && gc.optPset == NULL && pOptPSCacheEntry != NULL)
+ {
+ gc.optPset = pOptPSCacheEntry->CreateManagedPsetObject(dclRequestOptional, /* createEmptySet */ true);
+ }
+
+ // And finally the refused permission set.
+ hr = Security::GetDeclaredPermissions(pImport, mdAssembly, dclRequestRefuse, &gc.denyPset, NULL);
+ _ASSERT(SUCCEEDED(hr) || (hr == CLDB_E_RECORD_NOTFOUND));
+
+ *pReqdPermissions = gc.reqdPset;
+ *pOptPermissions = gc.optPset;
+ *pDenyPermissions = gc.denyPset;
+
+ GCPROTECT_END();
+ }
+}
+
+// Determine whether a RequestOptional or RequestRefused are made in the assembly manifest.
+BOOL SecurityAttributes::RestrictiveRequestsInAssembly(IMDInternalImport* pImport)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ } CONTRACTL_END;
+
+ mdAssembly mdAssembly;
+ HRESULT hr;
+ HENUMInternal hEnumDcl;
+
+ // Locate assembly metadata token since the various permission sets are
+ // written as custom values against this token.
+ hr = pImport->GetAssemblyFromScope(&mdAssembly);
+ if (FAILED(hr))
+ return TRUE;
+
+ hr = pImport->EnumPermissionSetsInit(mdAssembly,
+ dclRequestRefuse,
+ &hEnumDcl);
+
+ BOOL bFoundRequestRefuse = (hr != CLDB_E_RECORD_NOTFOUND);
+ pImport->EnumClose(&hEnumDcl);
+
+ if (bFoundRequestRefuse)
+ return TRUE;
+
+ hr = pImport->EnumPermissionSetsInit(mdAssembly,
+ dclRequestOptional,
+ &hEnumDcl);
+ BOOL bFoundRequestOptional = (hr != CLDB_E_RECORD_NOTFOUND);
+ pImport->EnumClose(&hEnumDcl);
+
+ return bFoundRequestOptional;
+}
+#endif // CROSSGEN_COMPILE
+
+HRESULT SecurityAttributes::GetPermissionsFromMetaData(IN IMDInternalImport *pInternalImport,
+ IN mdToken token,
+ IN CorDeclSecurity action,
+ OUT PBYTE* ppbPerm,
+ OUT ULONG* pcbPerm)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ } CONTRACTL_END;
+ HRESULT hr = S_OK;
+ mdPermission tkPerm;
+ void const ** ppData = const_cast<void const**> (reinterpret_cast<void**> (ppbPerm));
+ DWORD dwActionDummy;
+ // Get the blob for the CAS action from the security action table in metadata
+ HENUMInternalHolder hEnumDcl(pInternalImport);
+ if (hEnumDcl.EnumPermissionSetsInit(token,action))
+ {
+ _ASSERTE(pInternalImport->EnumGetCount(&hEnumDcl) == 1 && "Multiple permissions sets for the same declaration aren't currently supported.");
+ if (pInternalImport->EnumNext(&hEnumDcl, &tkPerm))
+ {
+ hr = pInternalImport->GetPermissionSetProps(
+ tkPerm,
+ &dwActionDummy,
+ ppData,
+ pcbPerm);
+
+ if (FAILED(hr) )
+ {
+ COMPlusThrowHR(hr);
+ }
+ }
+ else
+ {
+ _ASSERTE(!"At least one enumeration expected");
+ }
+ }
+ else
+ {
+ hr = CLDB_E_RECORD_NOTFOUND;
+ }
+ return hr;
+}
+
+void SecurityAttributes::CreateAndCachePermissions(
+ IN PBYTE pbPerm,
+ IN ULONG cbPerm,
+ IN CorDeclSecurity action,
+ OUT OBJECTREF *pDeclaredPermissions,
+ OUT PsetCacheEntry **pPSCacheEntry)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ } CONTRACTL_END;
+
+ SecurityDeclarativeCache *pSDC;
+ PsetCacheEntry* pPCE;
+
+ pSDC = &(GetAppDomain()->m_pSecContext->m_pSecurityDeclarativeCache);
+
+
+ pPCE = pSDC->CreateAndCachePset (pbPerm, cbPerm);
+ if (pDeclaredPermissions) {
+#ifdef CROSSGEN_COMPILE
+ _ASSERTE(!"This codepath should be unreachable during crossgen");
+ *pDeclaredPermissions = NULL;
+#else
+ *pDeclaredPermissions = pPCE->CreateManagedPsetObject (action);
+#endif
+ }
+ if (pPSCacheEntry) {
+ *pPSCacheEntry = pPCE;
+ }
+}
+
+// Returns the declared PermissionSet for the specified action type.
+HRESULT SecurityAttributes::GetDeclaredPermissions(IN IMDInternalImport *pInternalImport,
+ IN mdToken token,
+ IN CorDeclSecurity action,
+ OUT OBJECTREF *pDeclaredPermissions,
+ OUT PsetCacheEntry **pPSCacheEntry)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ } CONTRACTL_END;
+
+ HRESULT hr = S_FALSE;
+ PBYTE pbPerm = NULL;
+ ULONG cbPerm = 0;
+
+
+
+ _ASSERTE(action > dclActionNil && action <= dclMaximumValue);
+
+ // Initialize the output parameters.
+ if (pDeclaredPermissions)
+ *pDeclaredPermissions = NULL;
+ if(pPSCacheEntry)
+ *pPSCacheEntry = NULL;
+
+ bool bCas = !(action == dclNonCasDemand || action == dclNonCasLinkDemand || action == dclNonCasInheritance);
+
+ hr = GetPermissionsFromMetaData(pInternalImport, token, action, &pbPerm, &cbPerm);
+ if(pbPerm && cbPerm > 0)
+ {
+ CreateAndCachePermissions(pbPerm, cbPerm, action, pDeclaredPermissions, pPSCacheEntry);
+ }
+ else if(!bCas)
+ {
+ // We're looking for a non-CAS action which may be encoded with the corresponding CAS action
+ // Pre-Whidbey, we used to encode CAS and non-CAS actions separately because we used to do
+ // declarative security processing at build time (we used to create a
+ // permset object corresponding to a declarative action, convert it into XML and then store the serialized
+ // XML in the assembly).
+ //
+ // In Whidbey the default is what we call LAZY declarative security (LAZY_DECL_SEC_FLAG below) - to not do any
+ // declarative security processing at build time (we just take the declarative annotiation and store it as a
+ // serialzied blob - no permsets created/converted to XML). And at runtime, we do the actual processing (create permsets etc.)
+ //
+ // What does this mean? It means that in Whidbey (and beyond), we cannot tell at build time if it is a declarative CAS action
+ // or non-CAS action. So at runtime, we need to check the permset stored under the cas action for a non-CAS action.
+ // Of course, we need to do this only if LAZY_DECL_SEC_FLAG is in effect.
+
+ // Determine the corresponding CAS action
+ CorDeclSecurity casAction = dclDemand;
+ if(action == dclNonCasLinkDemand)
+ casAction = dclLinktimeCheck;
+ else if(action == dclNonCasInheritance)
+ casAction = dclInheritanceCheck;
+
+ // Get the blob for the CAS action from the security action table in metadata
+ hr = GetPermissionsFromMetaData(pInternalImport, token, casAction, &pbPerm, &cbPerm);
+
+ if(pbPerm && cbPerm > 0 && pbPerm[0] == LAZY_DECL_SEC_FLAG) // if it's a serialized CORSEC_ATTRSET
+ {
+ CreateAndCachePermissions(pbPerm, cbPerm, casAction, pDeclaredPermissions, pPSCacheEntry);
+ }
+
+ }
+
+ return hr;
+}
+
+bool SecurityAttributes::IsHostProtectionAttribute(CORSEC_ATTRIBUTE* pAttr)
+{
+ static const char s_HostProtectionAttributeName[] = "System.Security.Permissions.HostProtectionAttribute, mscorlib";
+
+ return (strncmp(pAttr->pName, s_HostProtectionAttributeName, sizeof(s_HostProtectionAttributeName)-1) == 0);
+}
+
+bool SecurityAttributes::IsBuiltInCASPermissionAttribute(CORSEC_ATTRIBUTE* pAttr)
+{
+ WRAPPER_NO_CONTRACT;
+ static const char s_permissionsNamespace[] = "System.Security.Permissions.";
+ if(strncmp(pAttr->pName, s_permissionsNamespace, sizeof(s_permissionsNamespace) - 1) != 0)
+ return false; // not built-in permission
+ static const char s_principalPermissionName[] = "System.Security.Permissions.PrincipalPermissionAttribute, mscorlib";
+
+ // ASSERT: at this point we know we are in builtin namespace...so compare with PrincipalPermissionAttribute
+ if (strncmp(pAttr->pName, s_principalPermissionName, sizeof(s_principalPermissionName)-1) == 0)
+ return false; // found a principal permission => Not a built-in CAS permission
+
+ // special-case the unrestricted permission set attribute.
+ static const char s_PermissionSetName[] = "System.Security.Permissions.PermissionSetAttribute, mscorlib";
+ if (strncmp(pAttr->pName, s_PermissionSetName, sizeof(s_PermissionSetName)-1) == 0)
+ return IsUnrestrictedPermissionSetAttribute(pAttr);
+
+ return true; //built-in perm, but not principal perm => IsBuiltInCASPermissionAttribute
+}
+
+bool SecurityAttributes::IsUnrestrictedPermissionSetAttribute(CORSEC_ATTRIBUTE* pPerm)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ BYTE const * pbBuffer = pPerm->pbValues;
+ SIZE_T cbBuffer = pPerm->cbValues;
+ BYTE const * pbBufferEnd = pbBuffer + cbBuffer;
+
+ if (cbBuffer < 2 * sizeof(BYTE))
+ return false;
+
+ // Get the field/property specifier
+ if (*(BYTE*)pbBuffer == SERIALIZATION_TYPE_FIELD)
+ return false;
+
+ _ASSERTE(*(BYTE*)pbBuffer == SERIALIZATION_TYPE_PROPERTY);
+ pbBuffer += sizeof(BYTE);
+ cbBuffer -= sizeof(BYTE);
+
+ // Get the value type
+ DWORD dwType = *(BYTE*)pbBuffer;
+ pbBuffer += sizeof(BYTE);
+ cbBuffer -= sizeof(BYTE);
+ if (dwType != SERIALIZATION_TYPE_BOOLEAN)
+ return false;
+
+ // Grab the field/property name and length.
+ DWORD cbName;
+ BYTE const * pbName;
+ if (FAILED(CPackedLen::SafeGetData(pbBuffer,
+ pbBufferEnd,
+ &cbName,
+ &pbName)))
+ {
+ return false;
+ }
+
+ PREFIX_ASSUME(pbName != NULL);
+
+ // SafeGetData will ensure the name is within the buffer
+ SIZE_T cbNameOffset = pbName - pbBuffer;
+ _ASSERTE(FitsIn<DWORD>(cbNameOffset));
+ DWORD dwLength = static_cast<DWORD>(cbNameOffset + cbName);
+ pbBuffer += dwLength;
+ cbBuffer -= dwLength;
+
+ // Buffer the name of the property and null terminate it.
+ DWORD allocLen = cbName + 1;
+ if (allocLen < cbName)
+ return false;
+
+ LPSTR szName = (LPSTR)_alloca(allocLen);
+ memcpy(szName, pbName, cbName);
+ szName[cbName] = '\0';
+
+ if (strcmp(szName, "Unrestricted") != 0)
+ return false;
+
+ // Make sure the value isn't "false"
+ return (*pbBuffer != 0);
+}
+
+// This takes a PermissionSetAttribute blob and looks to see if it uses the "FILE" property. If it
+// does, then it loads the file now and modifies the attribute to use the XML property instead
+// (because the file may not be available at runtime.)
+HRESULT SecurityAttributes::FixUpPermissionSetAttribute(CORSEC_ATTRIBUTE* pPerm)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(pPerm->wValues == 1 && strcmp(pPerm->pName, "System.Security.Permissions.PermissionSetAttribute") == 0);
+ BYTE const * pbBuffer = pPerm->pbValues;
+ SIZE_T cbBuffer = pPerm->cbValues;
+ BYTE const * pbBufferEnd = pbBuffer + cbBuffer;
+ HRESULT hr;
+
+ // Check we've got at least the field/property specifier and the
+ // type code.
+ _ASSERTE(cbBuffer >= (sizeof(BYTE) + sizeof(BYTE)));
+
+ // Grab the field/property specifier.
+ bool bIsField = *(BYTE*)pbBuffer == SERIALIZATION_TYPE_FIELD;
+ _ASSERTE(bIsField || (*(BYTE*)pbBuffer == SERIALIZATION_TYPE_PROPERTY));
+ pbBuffer += sizeof(BYTE);
+ cbBuffer -= sizeof(BYTE);
+
+ // Grab the value type.
+ DWORD dwType = *(BYTE*)pbBuffer;
+ pbBuffer += sizeof(BYTE);
+ cbBuffer -= sizeof(BYTE);
+
+ if(bIsField)
+ return S_OK;
+ if(dwType != SERIALIZATION_TYPE_STRING)
+ return S_OK;
+
+ // Grab the field/property name and length.
+ ULONG cbName;
+ BYTE const * pbName;
+ IfFailRet(CPackedLen::SafeGetData(pbBuffer, pbBufferEnd, &cbName, &pbName));
+ PREFIX_ASSUME(pbName != NULL);
+
+ // SafeGetData ensures name is within buffer
+ SIZE_T cbNameOffset = pbName - pbBuffer;
+ _ASSERTE(FitsIn<DWORD>(cbNameOffset));
+ DWORD dwLength = static_cast<DWORD>(cbNameOffset + cbName);
+ pbBuffer += dwLength;
+ cbBuffer -= dwLength;
+
+ // Buffer the name of the property and null terminate it.
+ DWORD allocLen = cbName + 1;
+ LPSTR szName = (LPSTR)_alloca(allocLen);
+ memcpy(szName, pbName, cbName);
+ szName[cbName] = '\0';
+
+ if(strcmp(szName, "File") != 0)
+ return S_OK;
+ if(*pbBuffer == 0xFF) // special case that represents NULL string
+ return S_OK;
+
+ IfFailRet(CPackedLen::SafeGetData(pbBuffer, pbBufferEnd, &cbName, &pbName));
+ PREFIX_ASSUME(pbName != NULL);
+
+ // SafeGetData ensures name is within buffer
+ cbNameOffset = pbName - pbBuffer;
+ _ASSERTE(FitsIn<DWORD>(cbNameOffset));
+ dwLength = static_cast<DWORD>(cbNameOffset + cbName);
+ _ASSERTE(cbBuffer >= dwLength);
+
+ // Open the file
+ MAKE_WIDEPTR_FROMUTF8N(wszFileName, (LPCSTR)pbName, cbName);
+ HandleHolder hFile(WszCreateFile (wszFileName,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
+ NULL));
+ if (hFile == INVALID_HANDLE_VALUE)
+ return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+ DWORD dwFileLen = SafeGetFileSize(hFile, 0);
+ if (dwFileLen == 0xFFFFFFFF)
+ return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+
+ // Read the file
+ BYTE* pFileBuffer = new (nothrow) BYTE[(dwFileLen + 4) * sizeof(BYTE)];
+ if(!pFileBuffer)
+ return E_OUTOFMEMORY;
+ DWORD dwBytesRead;
+ if ((SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == 0xFFFFFFFF) ||
+ (!ReadFile(hFile, pFileBuffer, dwFileLen, &dwBytesRead, NULL)))
+ {
+ delete [] pFileBuffer;
+ return E_FAIL;
+ }
+ if(dwBytesRead < dwFileLen)
+ {
+ delete [] pFileBuffer;
+ return E_FAIL;
+ }
+
+ // Make the new attribute blob
+ BYTE* pNewAttrBuffer = new (nothrow) BYTE[(dwFileLen + 10) * 2 * sizeof(BYTE)];
+ if(!pNewAttrBuffer)
+ return E_OUTOFMEMORY;
+ BYTE* pCurBuf = pNewAttrBuffer;
+ *pCurBuf = (BYTE)SERIALIZATION_TYPE_PROPERTY;
+ pCurBuf++;
+ *pCurBuf = (BYTE)SERIALIZATION_TYPE_STRING;
+ pCurBuf++;
+ pCurBuf = (BYTE*)CPackedLen::PutLength(pCurBuf, 3);
+ memcpy(pCurBuf, "Hex", 3);
+ pCurBuf += 3;
+ pCurBuf = (BYTE*)CPackedLen::PutLength(pCurBuf, dwFileLen * 2);
+ DWORD n;
+ BYTE b;
+ for(n = 0; n < dwFileLen; n++)
+ {
+ b = (pFileBuffer[n] >> 4) & 0xf;
+ *pCurBuf = (b < 10 ? '0' + b : 'a' + b - 10);
+ pCurBuf++;
+ b = pFileBuffer[n] & 0xf;
+ *pCurBuf = (b < 10 ? '0' + b : 'a' + b - 10);
+ pCurBuf++;
+ }
+ delete [] pFileBuffer;
+
+ // We shouldn't have a serialized permission set that can be this large, but to be safe we'll ensure
+ // that we fit in the output DWORD size.
+ SIZE_T cbNewAttrSize = pCurBuf - pNewAttrBuffer;
+
+ // Set the new values
+ delete(pPerm->pbValues);
+ pPerm->pbValues = pNewAttrBuffer;
+ pPerm->cbValues = cbNewAttrSize;
+ return S_OK;
+}
+
+// if tkAssemblyRef is NULL, this assumes the type is in this assembly
+// uszClassName should be a UTF8 string including both namespace and class
+HRESULT GetFullyQualifiedTypeName(SString* pString, mdAssemblyRef tkAssemblyRef, __in_z CHAR* uszClassName, IMetaDataAssemblyImport *pImport, mdToken tkCtor)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+ // Add class name
+ MAKE_WIDEPTR_FROMUTF8(wszClassName, uszClassName);
+ (*pString) += (LPCWSTR) wszClassName;
+ if(IsNilToken(tkAssemblyRef))
+ tkAssemblyRef = TokenFromRid(1, mdtAssembly);
+
+ // Add a comma separator
+ (*pString) += W(", ");
+
+ DWORD dwDisplayFlags = ASM_DISPLAYF_VERSION | ASM_DISPLAYF_PUBLIC_KEY_TOKEN | ASM_DISPLAYF_CULTURE;
+#ifdef FEATURE_FUSION // why is Security accessing Fusion interfaces bypassing Loader?
+ // Retrieve size of assembly name
+ ASSEMBLYMETADATA sContext;
+ ZeroMemory(&sContext, sizeof(ASSEMBLYMETADATA));
+ HRESULT hr = S_OK;
+ LPWSTR wszAssemblyName = NULL;
+ BYTE *pbPublicKeyOrToken = NULL;
+ DWORD cbPublicKeyOrToken = 0;
+ DWORD dwFlags = 0;
+ if(TypeFromToken(tkAssemblyRef) == mdtAssembly)
+ {
+ DWORD cchName;
+ hr = pImport->GetAssemblyProps(tkAssemblyRef, // [IN] The Assembly for which to get the properties.
+ NULL, // [OUT] Pointer to the public key or token.
+ NULL, // [OUT] Count of bytes in the public key or token.
+ NULL, // [OUT] Hash Algorithm
+ NULL, // [OUT] Buffer to fill with name.
+ NULL, // [IN] Size of buffer in wide chars.
+ &cchName, // [OUT] Actual # of wide chars in name.
+ &sContext, // [OUT] Assembly MetaData.
+ NULL); // [OUT] Flags.
+ if(FAILED(hr))
+ return hr;
+
+ // Get the assembly name other naming properties
+ wszAssemblyName = (LPWSTR)_alloca(cchName * sizeof(WCHAR));
+ hr = pImport->GetAssemblyProps(tkAssemblyRef,
+ (const void **)&pbPublicKeyOrToken,
+ &cbPublicKeyOrToken,
+ NULL,
+ wszAssemblyName,
+ cchName,
+ &cchName,
+ &sContext,
+ &dwFlags);
+ if(FAILED(hr))
+ return hr;
+ }
+ else if(TypeFromToken(tkAssemblyRef) == mdtAssemblyRef)
+ {
+ DWORD cchName;
+ hr = pImport->GetAssemblyRefProps(tkAssemblyRef, // [IN] The AssemblyRef for which to get the properties.
+ NULL, // [OUT] Pointer to the public key or token.
+ NULL, // [OUT] Count of bytes in the public key or token.
+ NULL, // [OUT] Buffer to fill with name.
+ NULL, // [IN] Size of buffer in wide chars.
+ &cchName, // [OUT] Actual # of wide chars in name.
+ &sContext, // [OUT] Assembly MetaData.
+ NULL, // [OUT] Hash blob.
+ NULL, // [OUT] Count of bytes in the hash blob.
+ NULL); // [OUT] Flags.
+ if(FAILED(hr))
+ return hr;
+
+ // Get the assembly name other naming properties
+ wszAssemblyName = (LPWSTR)_alloca(cchName * sizeof(WCHAR));
+ hr = pImport->GetAssemblyRefProps(tkAssemblyRef,
+ (const void **)&pbPublicKeyOrToken,
+ &cbPublicKeyOrToken,
+ wszAssemblyName,
+ cchName,
+ &cchName,
+ &sContext,
+ NULL,
+ NULL,
+ &dwFlags);
+ if(FAILED(hr))
+ return hr;
+ }
+ else
+ {
+ _ASSERTE(false && "unexpected token");
+ }
+
+ // Convert to an AssemblyNameObject
+ ReleaseHolder<IAssemblyName> pAssemblyNameObj;
+ hr = CreateAssemblyNameObject(&pAssemblyNameObj, wszAssemblyName, CANOF_PARSE_DISPLAY_NAME, NULL);
+ if(FAILED(hr))
+ return hr;
+ _ASSERTE(pAssemblyNameObj && "assembly name object shouldn't be NULL");
+ pAssemblyNameObj->SetProperty(ASM_NAME_MAJOR_VERSION, &sContext.usMajorVersion, sizeof(WORD));
+ pAssemblyNameObj->SetProperty(ASM_NAME_MINOR_VERSION, &sContext.usMinorVersion, sizeof(WORD));
+ pAssemblyNameObj->SetProperty(ASM_NAME_BUILD_NUMBER, &sContext.usBuildNumber, sizeof(WORD));
+ pAssemblyNameObj->SetProperty(ASM_NAME_REVISION_NUMBER, &sContext.usRevisionNumber, sizeof(WORD));
+ pAssemblyNameObj->SetProperty(ASM_NAME_CULTURE, W(""), sizeof(WCHAR));
+ if(pbPublicKeyOrToken && cbPublicKeyOrToken > 0)
+ {
+ if(dwFlags & afPublicKey)
+ pAssemblyNameObj->SetProperty(ASM_NAME_PUBLIC_KEY, pbPublicKeyOrToken, cbPublicKeyOrToken);
+ else
+ pAssemblyNameObj->SetProperty(ASM_NAME_PUBLIC_KEY_TOKEN, pbPublicKeyOrToken, cbPublicKeyOrToken);
+ }
+
+ // Convert assembly name to an ole string
+ StackSString name;
+ FusionBind::GetAssemblyNameDisplayName(pAssemblyNameObj, name, dwDisplayFlags);
+#else // FEATURE_FUSION
+ HRESULT hr;
+ AssemblySpec spec;
+ StackSString name;
+
+ IfFailRet(spec.Init((mdToken)tkAssemblyRef,pImport));
+ spec.GetFileOrDisplayName(dwDisplayFlags,name);
+#endif // FEATURE_FUSION
+ _ASSERTE(!name.IsEmpty() && "the assembly name should not be empty here");
+
+ (*pString) += name;
+ return S_OK;
+}
+
+HRESULT SecurityAttributes::SerializeAttribute(CORSEC_ATTRIBUTE* pAttr, BYTE* pBuffer, SIZE_T* pCount, IMetaDataAssemblyImport *pImport)
+{
+ // pBuffer can be NULL if the caller is only trying to determine the size of the serialized blob. In that case, let's make a little temp buffer to facilitate CPackedLen::PutLength
+ SIZE_T cbPos = *pCount;
+ BYTE* pTempBuf = pBuffer;
+ SIZE_T const* pTempPos = &cbPos;
+ BYTE tempBuf[8];
+ const SIZE_T zero = 0;
+ if(!pTempBuf)
+ {
+ pTempBuf = tempBuf;
+ pTempPos = &zero;
+ }
+ BYTE* pOldPos;
+
+ // Get the fully qualified type name
+ SString sType;
+ HRESULT hr = GetFullyQualifiedTypeName(&sType, pAttr->tkAssemblyRef, pAttr->pName, pImport, pAttr->tkCtor);
+ if(FAILED(hr))
+ return hr;
+
+ // Convert assembly name to UTF8.
+ const WCHAR* wszTypeName = sType.GetUnicode();
+ MAKE_UTF8PTR_FROMWIDE(uszTypeName, wszTypeName);
+ DWORD dwUTF8TypeNameLen = (DWORD)strlen(uszTypeName);
+
+ // Serialize the type name length
+ pOldPos = &pTempBuf[*pTempPos];
+ cbPos += (BYTE*)CPackedLen::PutLength(&pTempBuf[*pTempPos], dwUTF8TypeNameLen) - pOldPos;
+
+ // Serialize the type name
+ if(pBuffer)
+ memcpy(&pBuffer[cbPos], uszTypeName, dwUTF8TypeNameLen);
+ cbPos += dwUTF8TypeNameLen;
+
+ // Serialize the size of the properties blob
+ BYTE temp[32];
+ SIZE_T cbSizeOfCompressedPropertiesCount = (BYTE*)CPackedLen::PutLength(temp, pAttr->wValues) - temp;
+ pOldPos = &pTempBuf[*pTempPos];
+
+ _ASSERTE(FitsIn<ULONG>(pAttr->cbValues + cbSizeOfCompressedPropertiesCount));
+ ULONG propertiesLength = static_cast<ULONG>(pAttr->cbValues + cbSizeOfCompressedPropertiesCount);
+ cbPos += (BYTE*)CPackedLen::PutLength(&pTempBuf[*pTempPos], propertiesLength) - pOldPos;
+
+ // Serialize the count of properties
+ pOldPos = &pTempBuf[*pTempPos];
+ cbPos += (BYTE*)CPackedLen::PutLength(&pTempBuf[*pTempPos], pAttr->wValues) - pOldPos;
+
+ // Serialize the properties blob
+ if(pBuffer)
+ memcpy(&pBuffer[cbPos], pAttr->pbValues, pAttr->cbValues);
+ cbPos += pAttr->cbValues;
+
+ *pCount = cbPos;
+ return hr;
+}
+
+HRESULT SecurityAttributes::DeserializeAttribute(CORSEC_ATTRIBUTE *pAttr, BYTE* pBuffer, ULONG cbBuffer, SIZE_T* pPos)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+ HRESULT hr;
+
+ // Deserialize the size of the type name
+ BYTE* pClassName;
+ ULONG dwClassNameSize;
+ BYTE* pBufferEnd = pBuffer + cbBuffer;
+ IfFailRet(CPackedLen::SafeGetData((BYTE const *)&pBuffer[*pPos],
+ (BYTE const *)pBufferEnd,
+ &dwClassNameSize,
+ (BYTE const **)&pClassName));
+ (*pPos) += pClassName - &pBuffer[*pPos];
+
+ // Deserialize the type name
+ (*pPos) += dwClassNameSize;
+ pAttr->pName = new (nothrow) CHAR[dwClassNameSize + 1];
+ if(!pAttr->pName)
+ return E_OUTOFMEMORY;
+ memcpy(pAttr->pName, pClassName, dwClassNameSize);
+ pAttr->pName[dwClassNameSize] = '\0';
+
+ // Deserialize the CA blob size
+ BYTE* pCABlob;
+ ULONG cbCABlob;
+ IfFailRet(CPackedLen::SafeGetData((BYTE const *)&pBuffer[*pPos],
+ (BYTE const *)pBufferEnd,
+ &cbCABlob,
+ (BYTE const **)&pCABlob));
+
+ (*pPos) += pCABlob - &pBuffer[*pPos];
+
+ // Deserialize the CA blob value count
+ BYTE* pCABlobValues;
+ ULONG cCABlobValues;
+ IfFailRet(CPackedLen::SafeGetLength((BYTE const *)&pBuffer[*pPos],
+ (BYTE const *)pBufferEnd,
+ &cCABlobValues,
+ (BYTE const **)&pCABlobValues));
+
+ (*pPos) += pCABlobValues - &pBuffer[*pPos];
+ if (!FitsIn<WORD>(cCABlobValues))
+ return COR_E_OVERFLOW;
+ pAttr->wValues = static_cast<WORD>(cCABlobValues);
+
+ // We know that pCABlobValues - pCABlob will be a positive result.
+ if (cbCABlob < (ULONG)(pCABlobValues - pCABlob))
+ return COR_E_OVERFLOW;
+
+ pAttr->cbValues = cbCABlob - (pCABlobValues - pCABlob);
+
+ // Deserialize the CA blob
+ pAttr->pbValues = new (nothrow) BYTE[pAttr->cbValues];
+ if(!pAttr->pbValues)
+ return E_OUTOFMEMORY;
+ memcpy(pAttr->pbValues, pCABlobValues, pAttr->cbValues);
+
+ (*pPos) += pAttr->cbValues;
+
+ return S_OK;
+}
+
+HRESULT AttributeSetToBlob(CORSEC_ATTRSET* pAttrSet, BYTE* pBuffer, SIZE_T* pCount, IMetaDataAssemblyImport *pImport, DWORD dwAction)
+{
+ STANDARD_VM_CONTRACT;
+
+ // pBuffer can be NULL if the caller is only trying to determine the size of the serialized blob. In that case, let's make a little temp buffer to facilitate CPackedLen::PutLength
+ SIZE_T cbPos = 0;
+ BYTE* pTempBuf = pBuffer;
+ SIZE_T const *pTempPos = &cbPos;
+ BYTE tempBuf[8];
+ const SIZE_T zero = 0;
+ if(!pTempBuf)
+ {
+ pTempBuf = tempBuf;
+ pTempPos = &zero;
+ }
+ BYTE* pOldPos;
+ HRESULT hr = S_OK;
+
+ // Serialize a LAZY_DECL_SEC_FLAG to identify the blob format (as opposed to '<' which would indicate the older XML format)
+ if(pBuffer)
+ pBuffer[cbPos] = LAZY_DECL_SEC_FLAG;
+ cbPos++;
+
+ // Serialize the attribute count
+ pOldPos = &pTempBuf[*pTempPos];
+ cbPos += (BYTE*)CPackedLen::PutLength(&pTempBuf[*pTempPos], pAttrSet->dwAttrCount) - pOldPos;
+
+ // Serialize the attributes
+ DWORD i;
+ for(i = 0; i < pAttrSet->dwAttrCount; i++)
+ {
+ // Get the attribute
+ CORSEC_ATTRIBUTE *pAttr = &pAttrSet->pAttrs[i];
+
+ // Perform any necessary fix-ups on it
+ if(pAttr->wValues == 1 && strcmp(pAttr->pName, "System.Security.Permissions.PermissionSetAttribute") == 0)
+ IfFailGo(SecurityAttributes::FixUpPermissionSetAttribute(pAttr));
+ else if((dwAction == dclLinktimeCheck ||
+ dwAction == dclInheritanceCheck) &&
+ strcmp(pAttr->pName, "System.Security.Permissions.PrincipalPermissionAttribute") == 0)
+ {
+ VMPostError(CORSECATTR_E_BAD_NONCAS);
+ return CORSECATTR_E_BAD_NONCAS;
+ }
+
+ // Serialize it
+ SIZE_T dwAttrSize = 0;
+ IfFailGo(SecurityAttributes::SerializeAttribute(pAttr, pBuffer ? pBuffer + cbPos : NULL, &dwAttrSize, pImport));
+ cbPos += dwAttrSize;
+ }
+ if(pCount != NULL)
+ *pCount = cbPos;
+
+ErrExit:
+ if (FAILED(hr))
+ VMPostError(CORSECATTR_E_FAILED_TO_CREATE_PERM); // Allows for the correct message to be printed by the compiler
+
+ return hr;
+}
+
+HRESULT BlobToAttributeSet(BYTE* pBuffer, ULONG cbBuffer, CORSEC_ATTRSET* pAttrSet, DWORD dwAction)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+ HRESULT hr = S_OK;
+ SIZE_T cbPos = 0;
+ BYTE* pBufferEnd = pBuffer + cbBuffer;
+ memset(pAttrSet, '\0', sizeof(CORSEC_ATTRSET));
+ if (dwAction >= dclDemand && dwAction <= dclRequestRefuse)
+ pAttrSet->dwAction = dwAction; // Already lies in the publicly visible range ( values that managed enum SecurityAction can take)
+ else
+ {
+ // Map the action to a publicly visible value
+ if (dwAction == dclNonCasDemand)
+ pAttrSet->dwAction = dclDemand;
+ else if (dwAction == dclNonCasInheritance)
+ pAttrSet->dwAction = dclInheritanceCheck;
+ else if (dwAction == dclNonCasLinkDemand)
+ pAttrSet->dwAction = dclLinktimeCheck;
+ else
+ {
+ // We have an unexpected security action here. It would be nice to fail, but for compatibility we need to simply
+ // reset the action to Nil.
+ pAttrSet->dwAction = dclActionNil;
+ }
+ }
+
+ // Deserialize the LAZY_DECL_SEC_FLAG to identify serialization of CORSEC_ATTRSET (as opposed to '<' which would indicate a serialized permission as Xml)
+ BYTE firstChar = pBuffer[cbPos];
+ cbPos++;
+ if(firstChar != LAZY_DECL_SEC_FLAG)
+ return S_FALSE;
+
+ // Deserialize the attribute count
+ BYTE* pBufferNext;
+ IfFailRet(CPackedLen::SafeGetLength((BYTE const *)&pBuffer[cbPos],
+ (BYTE const *)pBufferEnd,
+ &pAttrSet->dwAttrCount,
+ (BYTE const **)&pBufferNext));
+
+ cbPos += pBufferNext - &pBuffer[cbPos];
+ if(pAttrSet->dwAttrCount > 0)
+ {
+ pAttrSet->pAttrs = new (nothrow) CORSEC_ATTRIBUTE[pAttrSet->dwAttrCount];
+ if(!pAttrSet->pAttrs)
+ return E_OUTOFMEMORY;
+ pAttrSet->dwAllocated = pAttrSet->dwAttrCount;
+ }
+
+ // Deserialize the attributes
+ DWORD i;
+ for(i = 0; i < pAttrSet->dwAttrCount; i++)
+ {
+ CORSEC_ATTRIBUTE *pAttr = &pAttrSet->pAttrs[i];
+ hr = SecurityAttributes::DeserializeAttribute(pAttr, pBuffer, cbBuffer, &cbPos);
+ if(FAILED(hr))
+ return hr;
+ }
+
+ return S_OK;
+}
+
+// This function takes an array of COR_SECATTR (which wrap custom security attribute blobs) and
+// converts it to an array of CORSEC_ATTRSET (which contains partially-parsed custom security attribute
+// blobs grouped by SecurityAction). Note that you must delete all the pPermissions that this allocates
+// for each COR_SECATTR
+HRESULT STDMETHODCALLTYPE GroupSecurityAttributesByAction(
+ CORSEC_ATTRSET /*OUT*/rPermSets[],
+ COR_SECATTR rSecAttrs[],
+ ULONG cSecAttrs,
+ mdToken tkObj,
+ ULONG *pulErrorAttr,
+ CMiniMdRW* pMiniMd,
+ IMDInternalImport* pInternalImport)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ DWORD i, j, k;
+ DWORD dwAction;
+ BYTE* pData = NULL;
+ CORSEC_ATTRIBUTE* pPerm;
+ mdTypeDef tkParent;
+ TypeDefRec* pTypeDefRec;
+ MemberRefRec* pMemberRefRec;
+ TypeRefRec* pTypeRefRec;
+ SIZE_T cbAllocationSize;
+
+ // If you are calling this at compile-time, you should pass in pMiniMd, and pInternalImport should be NULL
+ // If you are calling this at run-time, you should pass in pInternalImport, and pMiniMd should be NULL
+ _ASSERTE((pMiniMd && !pInternalImport) || (!pMiniMd && pInternalImport));
+
+ // Calculate number and sizes of permission sets to produce. This depends on
+ // the security action code encoded as the single parameter to the
+ // constructor for each security custom attribute.
+ for (i = 0; i < cSecAttrs; i++)
+ {
+ if (pulErrorAttr)
+ *pulErrorAttr = i;
+
+ // Perform basic validation of the header of each security custom
+ // attribute constructor call.
+ pData = (BYTE*)rSecAttrs[i].pCustomAttribute;
+
+ // Check minimum length.
+ if (rSecAttrs[i].cbCustomAttribute < (sizeof(WORD) + sizeof(DWORD) + sizeof(WORD)))
+ {
+ VMPostError(CORSECATTR_E_TRUNCATED);
+ IfFailGo(CORSECATTR_E_TRUNCATED);
+ }
+
+ // Check version.
+ if (GET_UNALIGNED_VAL16(pData) != 1)
+ {
+ VMPostError(CORSECATTR_E_BAD_VERSION);
+ IfFailGo(CORSECATTR_E_BAD_VERSION);
+ }
+ pData += sizeof(WORD);
+
+ // Extract and check security action.
+ if(pData[2] == SERIALIZATION_TYPE_PROPERTY) // check to see if it's a HostProtection attribute w/o an action
+ dwAction = dclLinktimeCheck;
+ else
+ dwAction = GET_UNALIGNED_VAL32(pData);
+ if (dwAction == dclActionNil || dwAction > dclMaximumValue)
+ {
+ VMPostError(CORSECATTR_E_BAD_ACTION);
+ IfFailGo(CORSECATTR_E_BAD_ACTION);
+ }
+
+ // All other declarative security only valid on types and methods.
+ if (TypeFromToken(tkObj) == mdtAssembly)
+ {
+ // Assemblies can only take permission requests.
+ if (dwAction != dclRequestMinimum &&
+ dwAction != dclRequestOptional &&
+ dwAction != dclRequestRefuse)
+ {
+ VMPostError(CORSECATTR_E_BAD_ACTION_ASM);
+ IfFailGo(CORSECATTR_E_BAD_ACTION_ASM);
+ }
+ }
+ else if (TypeFromToken(tkObj) == mdtTypeDef || TypeFromToken(tkObj) == mdtMethodDef)
+ {
+ // Types and methods can only take declarative security.
+ if (dwAction != dclRequest &&
+ dwAction != dclDemand &&
+ dwAction != dclAssert &&
+ dwAction != dclDeny &&
+ dwAction != dclPermitOnly &&
+ dwAction != dclLinktimeCheck &&
+ dwAction != dclInheritanceCheck)
+ {
+ VMPostError(CORSECATTR_E_BAD_ACTION_OTHER);
+ IfFailGo(CORSECATTR_E_BAD_ACTION_OTHER);
+ }
+ }
+ else
+ {
+ // Permission sets can't be attached to anything else.
+ VMPostError(CORSECATTR_E_BAD_PARENT);
+ IfFailGo(CORSECATTR_E_BAD_PARENT);
+ }
+
+ rPermSets[dwAction].dwAttrCount++;
+ }
+
+ // Initialize the descriptor for each type of permission set we are going to
+ // produce.
+ for (i = 0; i <= dclMaximumValue; i++)
+ {
+ if (rPermSets[i].dwAttrCount == 0)
+ continue;
+
+ rPermSets[i].tkObj = tkObj;
+ rPermSets[i].dwAction = i;
+ rPermSets[i].pImport = NULL;
+ rPermSets[i].pAppDomain = NULL;
+ rPermSets[i].pAttrs = new (nothrow) CORSEC_ATTRIBUTE[rPermSets[i].dwAttrCount];
+ IfNullGo(rPermSets[i].pAttrs);
+
+ // Initialize a descriptor for each permission within the permission set.
+ for (j = 0, k = 0; j < rPermSets[i].dwAttrCount; j++, k++)
+ {
+ // Locate the next security attribute that contributes to this
+ // permission set.
+ for (; k < cSecAttrs; k++)
+ {
+ pData = (BYTE*)rSecAttrs[k].pCustomAttribute;
+ if(pData[4] == SERIALIZATION_TYPE_PROPERTY) // check to see if it's a HostProtection attribute w/o an action
+ dwAction = dclLinktimeCheck;
+ else
+ dwAction = GET_UNALIGNED_VAL32(pData + sizeof(WORD));
+ if (dwAction == i)
+ break;
+ }
+ _ASSERTE(k < cSecAttrs);
+
+ if (pulErrorAttr)
+ *pulErrorAttr = k;
+
+ // Initialize the permission.
+ pPerm = &rPermSets[i].pAttrs[j];
+ pPerm->tkCtor = rSecAttrs[k].tkCtor;
+ pPerm->dwIndex = k;
+ if(pData[4] == SERIALIZATION_TYPE_PROPERTY) // check to see if it's a HostProtection attribute w/o an action
+ {
+ _ASSERTE(!pPerm->pbValues);
+ //pPerm->pbValues = pData + (sizeof (WORD) + sizeof(WORD));
+ if (!ClrSafeInt<SIZE_T>::subtraction(rSecAttrs[k].cbCustomAttribute, (sizeof (WORD) + sizeof(WORD)), pPerm->cbValues))
+ return COR_E_OVERFLOW;
+ pPerm->wValues = GET_UNALIGNED_VAL16(pData + sizeof (WORD));
+ // Prefast overflow sanity check the addition.
+ if (!ClrSafeInt<SIZE_T>::addition(pPerm->cbValues, sizeof(WORD), cbAllocationSize))
+ return COR_E_OVERFLOW;
+ pPerm->pbValues = new (nothrow) BYTE[cbAllocationSize];
+ if(!pPerm->pbValues)
+ return E_OUTOFMEMORY;
+ memcpy(pPerm->pbValues, pData + (sizeof (WORD) + sizeof(WORD)), pPerm->cbValues);
+ }
+ else
+ {
+ _ASSERTE(!pPerm->pbValues);
+ //pPerm->pbValues = pData + (sizeof (WORD) + sizeof(DWORD) + sizeof(WORD));
+ if (!ClrSafeInt<SIZE_T>::subtraction(rSecAttrs[k].cbCustomAttribute, (sizeof (WORD) + sizeof (DWORD) + sizeof(WORD)), pPerm->cbValues))
+ return COR_E_OVERFLOW;
+ pPerm->wValues = GET_UNALIGNED_VAL16(pData + sizeof (WORD) + sizeof(DWORD));
+ // Prefast overflow sanity check the addition.
+ if (!ClrSafeInt<SIZE_T>::addition(pPerm->cbValues, sizeof(WORD), cbAllocationSize))
+ return COR_E_OVERFLOW;
+ pPerm->pbValues = new (nothrow) BYTE[cbAllocationSize];
+ if(!pPerm->pbValues)
+ return E_OUTOFMEMORY;
+ memcpy(pPerm->pbValues, pData + (sizeof (WORD) + sizeof(DWORD) + sizeof(WORD)), pPerm->cbValues);
+ }
+
+ CQuickBytes qbFullName;
+ CHAR* szFullName = NULL;
+
+ LPCSTR szTypeName;
+ LPCSTR szTypeNamespace;
+
+ // Follow the security custom attribute constructor back up to its
+ // defining assembly (so we know how to load its definition). If the
+ // token resolution scope is not defined, it's assumed to be
+ // mscorlib.
+ if (TypeFromToken(rSecAttrs[k].tkCtor) == mdtMethodDef)
+ {
+ if (pMiniMd != NULL)
+ {
+ // scratch buffer for full type name
+ szFullName = (CHAR*) qbFullName.AllocNoThrow((MAX_CLASSNAME_LENGTH+1) * sizeof(CHAR));
+ if(szFullName == NULL)
+ return E_OUTOFMEMORY;
+
+ // grab the type that contains the security attribute constructor
+ IfFailGo(pMiniMd->FindParentOfMethodHelper(rSecAttrs[k].tkCtor, &tkParent));
+
+ // scratch buffer for nested type names
+ CQuickBytes qbBuffer;
+ CHAR* szBuffer;
+
+ CHAR* szName = NULL;
+ BOOL fFirstLoop = TRUE;
+ pTypeDefRec = NULL;
+ do
+ {
+ // get outer type name
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeDefRec));
+ IfFailGo(pMiniMd->getNameOfTypeDef(pTypeDefRec, (LPCSTR *)&szName));
+
+ // If this is the first time through the loop, just assign values, otherwise build nested type name.
+ if (!fFirstLoop)
+ {
+ szBuffer = (CHAR*) qbBuffer.AllocNoThrow((MAX_CLASSNAME_LENGTH+1) * sizeof(CHAR));
+ if(szBuffer == NULL)
+ return E_OUTOFMEMORY;
+
+ ns::MakeNestedTypeName(szBuffer, (MAX_CLASSNAME_LENGTH+1) * sizeof(CHAR), szName, szFullName);
+ szName = szBuffer;
+ }
+ else
+ {
+ fFirstLoop = FALSE;
+ }
+
+ // copy into buffer
+ size_t localLen = strlen(szName) + 1;
+ strcpy_s(szFullName, localLen, szName);
+
+ // move to next parent
+ DWORD dwFlags = pMiniMd->getFlagsOfTypeDef(pTypeDefRec);
+ if (IsTdNested(dwFlags))
+ {
+ RID ridNestedRec;
+ IfFailGo(pMiniMd->FindNestedClassHelper(tkParent, &ridNestedRec));
+ _ASSERTE(!InvalidRid(ridNestedRec));
+ NestedClassRec *pNestedRec;
+ IfFailGo(pMiniMd->GetNestedClassRecord(ridNestedRec, &pNestedRec));
+ tkParent = pMiniMd->getEnclosingClassOfNestedClass(pNestedRec);
+ }
+ else
+ {
+ tkParent = NULL;
+ }
+ } while (tkParent != NULL);
+
+ IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeDefRec, &szTypeNamespace));
+ szTypeName = szFullName;
+ }
+ else
+ {
+ IfFailGo(pInternalImport->GetParentToken(rSecAttrs[k].tkCtor, &tkParent));
+ IfFailGo(pInternalImport->GetNameOfTypeDef(tkParent, &szTypeName, &szTypeNamespace));
+ }
+ pPerm->tkTypeRef = mdTokenNil;
+ pPerm->tkAssemblyRef = mdTokenNil;
+ }
+ else
+ {
+ _ASSERTE(TypeFromToken(rSecAttrs[k].tkCtor) == mdtMemberRef);
+
+ // Get the type ref
+ if (pMiniMd != NULL)
+ {
+ IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(rSecAttrs[k].tkCtor), &pMemberRefRec));
+ pPerm->tkTypeRef = pMiniMd->getClassOfMemberRef(pMemberRefRec);
+ }
+ else
+ {
+ IfFailGo(pInternalImport->GetParentOfMemberRef(rSecAttrs[k].tkCtor, &pPerm->tkTypeRef));
+ }
+
+ _ASSERTE(TypeFromToken(pPerm->tkTypeRef) == mdtTypeRef);
+
+ // Get an assembly ref
+ pPerm->tkAssemblyRef = pPerm->tkTypeRef;
+ pTypeRefRec = NULL;
+ do
+ {
+ if (pMiniMd != NULL)
+ {
+ IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(pPerm->tkAssemblyRef), &pTypeRefRec));
+ pPerm->tkAssemblyRef = pMiniMd->getResolutionScopeOfTypeRef(pTypeRefRec);
+ }
+ else
+ {
+ IfFailGo(pInternalImport->GetResolutionScopeOfTypeRef(pPerm->tkAssemblyRef, &pPerm->tkAssemblyRef));
+ }
+ // loop because nested types have a resolution scope of the parent type rather than an assembly
+ } while(TypeFromToken(pPerm->tkAssemblyRef) == mdtTypeRef);
+
+ // Figure out the fully qualified type name
+ if (pMiniMd != NULL)
+ {
+ IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szTypeNamespace));
+ IfFailGo(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szTypeName));
+ }
+ else
+ {
+ IfFailGo(pInternalImport->GetNameOfTypeRef(pPerm->tkTypeRef, &szTypeNamespace, &szTypeName));
+ }
+ }
+
+ CQuickBytes qb;
+ CHAR* szTmp = (CHAR*) qb.AllocNoThrow((MAX_CLASSNAME_LENGTH+1) * sizeof(CHAR));
+ if(szTmp == NULL)
+ return E_OUTOFMEMORY;
+
+ ns::MakePath(szTmp, MAX_CLASSNAME_LENGTH, szTypeNamespace, szTypeName);
+
+ size_t len = strlen(szTmp) + 1;
+ pPerm->pName = new (nothrow) CHAR[len];
+ if(!pPerm->pName)
+ return E_OUTOFMEMORY;
+ strcpy_s(pPerm->pName, len, szTmp);
+ }
+ }
+
+ErrExit:
+ return hr;
+}