Skip to main content
Version: Next

Access Control Policy

For some platform-type applications, they may load and execute third-party developed code. If these third-party codes are not restricted, it will bring security risks. The access control mechanism is used to control the set of functions that these third-party codes can access.

Access Policy File

We use access policy files to conveniently configure control information. Currently only class-level access control is supported (function-level access control may be supported in the future), that is, if a certain class is not allowed to be accessed, any function of that type cannot be called, including class constructors (functions from parent classes can still be called if they are not in the restriction range). A typical policy configuration file is as follows:

<AccessPolicy>

<Rule id="DisableIO">
<assembly fullname="mscorlib">
<type fullname="System.IO.*"/> disable
<type fullname="System.IO.File" access="1"/> enable
</assembly>
</Rule>

<Rule id="DisableReflection">
<assembly fullname="mscorlib">
<type fullname="System.Reflection.*"/>
</assembly>
</Rule>

<Rule id="DisableHybridCLR">
<assembly fullname="HybridCLR.Runtime">
<type fullname="*"/>
</assembly>
</Rule>

<Target assembly="Tests2" accessAssemblyNotInRules="0" rules="DisableReflection,DisableIO,DisableHybridCLR"/>

</AccessPolicy>

Configuration Rules

The top-level tag is AccessPolicy, containing 0-N Rule and Target configuration items.

Rule

Each Rule contains access control rules for multiple assemblies. Each Rule will calculate a set of types that restrict access, and the final set of restricted access is the union of all Rules, that is, as long as a certain Rule restricts access to a certain type, access to this type is not allowed in the end.

NameTypeNullableDescription
idattributeNoRule id. String type, cannot be empty, must be globally unique
assemblysub-elementRestriction set for a single assembly. Rule can contain 0-N assemblies. The same Rule cannot have assemblies with the same name, but different Rules can have assemblies with the same name

assembly

A set of restriction rules for a single assembly, configuring which types of the assembly are prohibited from access.

NameTypeNullableDescription
fullnameattributeNoAssembly name. String type, cannot be empty. Assembly name does not include '.dll' (e.g., mscorlib)
typesub-elementRestriction rules for a single type or a group of types. Can have 0-N elements

Within the same assembly, if multiple rules appear that are related to a certain type, the last effective rule shall prevail. For example:

        <assembly fullname="mscorlib">
<type fullname="System.IO.*"/> disable
<type fullname="System.IO.File" access="1"/> enable
</assembly>

In the above example, although <type fullname="System.IO.*"/> prohibits access to all types under the System.IO namespace, including System.IO.File, the subsequent <type fullname="System.IO.File" access="1"/> separately cancels the access restriction on System.IO.File.

type

Restriction rules for one or a group of types.

NameTypeNullableDescription
fullnameattributeNoType full name or type wildcard. When it's a type wildcard, only supports * full match or xx.yy.zz.* namespace wildcard, does not support xx.yy* prefix wildcard or xx.*.yy arbitrary wildcard. If you want to match all types with empty namespace, use .*
accessattributeYesWhether accessible, default is false. When set to true, yes, 1, it's true; when set to false, no, 0, it's false

Target

Target configures the access restriction rules applied to code in the assembly.

NameTypeNullableDescription
assemblyattributeNoTarget assembly for access restrictions, that is, functions accessed by code in this assembly must satisfy the access restriction rules specified in rules
accessAssemblyNotInRulesattributeYesWhether assemblies not involved in rules can be accessed. Default is false, when set to true, yes, 1, it's true; when set to false, no, 0, it's false
rulesattributeNoList of Rule ids, separated by ,. Functions accessed by code in assembly must not be in the restriction set of any Rule

XmlAccessPolicyReader and BinaryAccessPolicyWriter

Access policy files are in xml format, which is complex to parse and has certain overhead when loaded at runtime. Therefore, it's necessary to convert AccessPolicy.xml to AccessPolicy.bin file in advance during packaging, and load the AccessPolicy.bin file at runtime.

The HybridCLR.Editor.Security.AccessPolicyUtil class implements the ConvertXmlAccessPolicyToBinaryAccessPolicy function, used to convert xml format to bin format. Example code is as follows:

        [MenuItem("Test/ConvertXmlAccessPolicyToBinary")]
public static void ConvertXmlAccessPolicyToBinary()
{
string accessPolicyDir = Application.dataPath + "/AccessPolicy";
AccessPolicyUtil.ConvertXmlAccessPolicyToBinaryAccessPolicy($"{accessPolicyDir}/AccessPolicy.xml",
$"{accessPolicyDir}/AccessPolicy.bytes");
}

Validate AccessPolicy Configuration Validity

In practice, it's easy to incorrectly fill in names like assembly.fullname, type.fullname, etc., causing the expected access control policy to not be executed correctly. HybridCLR.Editor.Security.AccessPolicyConfigValidator is used to check the validity of AccessPolicy to avoid such errors.

FunctionDescription
ValidateRulesCheck the validity of Rule rules
ValidateTargetsCheck the validity of Target rules

Example code is as follows:

    public static void ValidateAccessPolicy()
{
var reader = new XmlAccessPolicyReader();
reader.LoadXmlFile("Assets/AccessPolicy/AccessPolicy.xml");
List<string> hotUpdateDllNames = SettingsUtil.HotUpdateAssemblyNamesExcludePreserved;
var assemblyCache = new AssemblyCache(MetaUtil.CreateHotUpdateAndAOTAssemblyResolver(EditorUserBuildSettings.activeBuildTarget, hotUpdateDllNames));
var validator = new AccessPolicyConfigValidator(assemblyCache);

var accessPolicy = reader.GetAccessPolicy();
validator.ValidateRules(accessPolicy);
validator.ValidateTargets(accessPolicy, new List<string> { "Tests2" });
}

Pre-validate whether assembly meets AccessPolicy

Assembly.Load does not check whether there are illegal calls in the assembly when loading it. It only checks whether calling a function conforms to AccessPolicy when the function is called for the first time during runtime, which is inconvenient. HybridCLR.Editor.Security.AssemblyValidator is used to offline pre-validate whether all calls in assembly conform to AccessPolicy.

Example code is as follows:

        public static void ValidateAssembly()
{
var reader = new XmlAccessPolicyReader();
reader.LoadXmlFile("Assets/AccessPolicy/AccessPolicy.xml");
var validator = new AssemblyValidator(reader.GetAccessPolicy());
string test2DllPath = $"{SettingsUtil.GetHotUpdateDllsOutputDirByTarget(EditorUserBuildSettings.activeBuildTarget)}/Tests2.dll";
var mod = ModuleDefMD.Load(test2DllPath);
validator.ValidateAssembly(mod);
}

Set access policy at runtime

The RuntimeApi class provides the LoadAccessPolicy function to set access policies. This function can be called multiple times during runtime to update access policies.

Example code is as follows:


void LoadAccessPolicy()
{
byte[] accessPolicyData = File.ReadAllBytes($"{Application.streamingAssetsPath}/AccessPolicy.bin");
RuntimeApi.LoadAccessPolicy(accessPolicyData);
}