Skip to main content

Access Control Policy

For some platform-based applications, they may load and execute code developed by third parties. If these third-party codes are not restricted, security risks may arise. Access control mechanisms are used to control the set of functions that these third-party code can access.

Access policy file

We use access policy files to easily configure control information. Currently only class-level access control is supported (function-granular access control may be supported in the future), that is, if a class does not allow access, Then you cannot call any function of that type, including class constructors (functions from the parent class can still be called if they are not in the restricted scope). 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, which contains 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. The final set of restricted access is the union of all Rules. That is, as long as a Rule restricts access to a certain type, Then access to this type is ultimately not allowed.

Attribute or elementTypeNullableDescription
idAttributeNoThe id of the Rule. String type, cannot be empty, must be globally unique
assemblySub-elementFor the restricted set of a single assembly, a Rule can contain 0-N assemblies. There cannot be an assembly with the same name under the same Rule, but there can be an assembly with the same name between different Rules

assembly

A set of restriction rules for a single assembly configures which types of access to that assembly are prohibited.

Attribute or elementTypeNullableDescription
fullnamepropertynoassembly name. String type, cannot be empty. Assembly name does not contain '.dll' (e.g. mscorlib)
typeSub-elementRestriction rules for a single or a group of types. There can be 0-N elements

In the same assembly, if there are multiple rules related to a certain type, the last rule that takes effect will 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 following <type fullname="System.IO.File" access="1"/> has separately canceled the access restrictions on System.IO.File.

type

Restrictive rules for one or a group of types.

Attribute or elementTypeNullableDescription
fullnameAttributeNoThe full name of the type or the type wildcard. If it is a type wildcard, only * full matching or xx.yy.zz.* namespace wildcarding is supported. Prefix wildcarding such as xx.yy* or xx.*.yy is not supported. Any kind of wildcard
accessAttributeYesWhether it is accessible, the default is false. It is true when true, yes, 1 is taken and false when false, no, 0 is taken

Target

Target configures access restriction rules that are imposed on the code in the assembly.

Attribute or elementTypeNullableDescription
assemblyAttributesNoThe role of access restrictions is the target assembly, that is, the functions accessed by the code in the assembly must meet the access restriction rules specified in rules
accessAssemblyNotInRulespropertyiswhether assemblies not covered by rules can be accessed. The default is false. When true, yes, 1 is taken, it is true, and when false, no, 0 is taken, it is false
rulesAttributeNoRule id list, separated by ,. The functions accessed by the code in the assembly must not be in any of the restricted sets of Rule

XmlAccessPolicyReader and BinaryAccessPolicyWriter

The access policy file is in xml format, which is complex to parse and requires a certain amount of overhead to load at runtime. Therefore, it is necessary to convert AccessPolicy.xml to AccessPolicy.bin file in advance when packaging, and load the AccessPolicy.bin file during runtime.

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

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

Verify the legality of AccessPolicy configuration

In practice, it is easy to incorrectly fill in names such as assembly.fullname and type.fullname, resulting in the expected access control policy not being correctly implemented. HybridCLR.Editor.Security.AccessPolicyConfigValidator is used to check the validity of AccessPolicy to avoid this error.

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

The sample 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-verify whether the assembly meets AccessPolicy

Assembly.Load does not check whether there are illegal calls in the assembly when loading the assembly. During the running process, it only checks whether the calling function complies with the AccessPolicy when a function is called for the first time, which causes inconvenience. HybridCLR.Editor.Security.AssemblyValidator is used to offline pre-verify whether all calls in the assembly comply with AccessPolicy.

The sample 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 for setting access policies. This function can be called multiple times during operation to update the access policy.

The sample code is as follows:


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