The Policy Manager in the CLR performs a number of functions by using the evidence supplied to it. Essentially, the Policy Manager takes this evidence and produces a set of permissions. As shown in Figure 4.3, it includes four policy levels:
Enterprise
Machine
User
Application Domain
Each level normally produces a permission set based on the evidence. These permission sets are then “intersected” to generate the permission set for the entire assembly. As a consequence, all levels must allow a permission before it can enter the assembly's granted permission set. For example, if the Enterprise policy level does not grant a permission, regardless of what the other levels specify, that permission will not be granted. Sometimes, however, a policy may direct the security system to not evaluate lower policy levels. This flexibility provides a means to allow a higher policy level, such as the Enterprise policy level, to effectively override subordinate levels.
Each policy level within the security system has a similar architecture. Each level consists of three parts:
A tree of code groups
A set of named permissions
A list of policy assemblies
A code group consists of a conditional expression and a permission set. If an assembly satisfies the conditional expression, then it is granted the permission set. The set of code groups for each policy level is arranged into a tree structure. Every time a conditional expression evaluates to true, the permission set is granted and the traversal of that branch continues. Whenever a condition evaluates to false, the permission set is not granted and that branch is not examined further. As an example, consider the tree of code groups shown in Figure 4.4.
Assume you have an assembly with the following evidence: It came from www.project42.net and, because it came from the JJJLK product group, it has a strong name of JJJLK.
The code group tree traversal may proceed as follows:
1. |
The root node has a condition of All Code that is satisfied by any code. The permission set for All Code is granted to the assembly; it is called “Nothing” and effectively grants code no permissions. |
2. |
The next code group checked is the one requiring that the code be loaded from My Computer. This condition fails, so the permission set is not granted and no subordinate nodes of this condition are checked. |
3. |
We return to the last successfully granted code group, All Code, and continue checking its subordinate nodes. The next code group could be Zone: Internet. As the code in this example was downloaded from the Internet, this condition is satisfied. The permission set, possibly called “Internet,” is granted. |
4. |
Continuing with the next subordinate code group in the same branch, this code group has a condition of Url: stating that the code came from www.microsoft.com. This condition fails as the code came from www.project42.net. |
5. |
We return to the Zone: Internet code group, look for other nodes beneath it, and find a node for URL: www.project42.net. As this condition is satisfied, the “Project42PSet” permission set is granted. |
6. |
We find a node for Strong Name: JJJLK. As this condition is satisfied, the “JJJLK” permission set is granted. |
7. |
As no code groups remain below this level, we return to last code group that a condition matched and that has subordinate code groups and continue. |
Eventually, the conditions satisfied and the granted permission sets would include the following:
Condition: All Code; permission set: Nothing
Condition: Zone: Internet; permission set: Internet
Condition: URL: www.project42; permission set: Project42PSet
Condition: Strong Name: JJJLK; permission set: JJJLKPSet
Inspection of the policy level code group is fairly simple. It is stored as XML in a well-known location. For example, the Enterprise policy is found at a location such as the following:
C:WINDOWSMicrosoft.NETFrameworkv1.0.3705CONFIGenter prisesec.config
It looks like the following, again edited for brevity:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <mscorlib> <security> <policy> <PolicyLevel version="1"> <SecurityClasses> <SecurityClass Name="StrongNameMembershipCondition" Description= "System.Policy.StrongNameMembershipCondition, mscorlib, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> ... </SecurityClasses> <NamedPermissionSets> <PermissionSet class="NamedPermissionSet" version="1" Unrestricted="true" Name="FullTrust" Description="Allows full access to all resources"/> <PermissionSet class="NamedPermissionSet" version="1" Name="LocalIntranet" Description="Default rights...local intranet"> <IPermission class="EnvironmentPermission" version="1" Read="USERNAME"/> <IPermission class="FileDialogPermission" version="1" Unrestricted="true"/> ... <CodeGroup class="UnionCodeGroup" version="1" PermissionSetName="FullTrust" Name="All_Code" Description="Code ... of the code group tree."> <IMembershipCondition class="AllMembershipCondition" version="1"/> </CodeGroup> ... <FullTrustAssemblies> <IMembershipCondition class="StrongNameMembershipCondition" version="1" PublicKeyBlob="00000000000000000400000000000000" Name="mscorlib.resources" AssemblyVersion="1.0.3300.0"/> <IMembershipCondition class="StrongNameMembershipCondition" version="1" PublicKeyBlob="00000000000000000400000000000000" Name="System" AssemblyVersion="1.0.3300.0"/> </FullTrustAssemblies> </PolicyLevel> </policy> </security> </mscorlib> </configuration>
Administrators can edit the XML directly to modify the default policy. In addition, a Microsoft Management Console snap-in provides a visual interface for changing these settings.
A policy level contains a list of named permission sets; the code groups may assign these sets to code that satisfy various code group conditions. Examples of predefined permission sets include the following:
FullTrust: Allows unrestricted access to system resources
SkipVerification: Allows an assembly to skip verification
Execution: Allows code to execute
Nothing: No permissions; not granting the permission to execute effectively stops code from running
During security evaluation, other assemblies may need to be loaded for use in the policy evaluation process. Such assemblies may, for example, contain user-defined permission classes. Of course, if these assemblies also need to be evaluated and they refer to other assemblies, a circular dependency could result. To avoid this problem, each assembly contains a list of trusted assemblies that it needs for policy evaluation. This list of required assemblies is naturally referred to as the list of “policy assemblies.”
Listing 4.6 displays the policy levels and permission set for an application. The application is a C# program run from the local disk, so it receives a fairly powerful permission set.
using System; using System.Collections; using System.Security; using System.Security.Policy; namespace SecurityResolver { class Sample { static void Main(string[] args) { IEnumerator i = SecurityManager.PolicyHierarchy(); while(i.MoveNext()) { PolicyLevel p = (PolicyLevel) i.Current; Console.WriteLine(p.Label); IEnumerator np = p.NamedPermissionSets.GetEnumerator(); while (np.MoveNext()) { NamedPermissionSet pset = (NamedPermissionSet)np.Current; Console.WriteLine( " PSet: Name: {0} Description {1}", pset.Name, pset.Description); } } } } } |
The output of the program is shown below. It has been edited for brevity.
Enterprise PSet: Name: FullTrust Description: Allows full access to all resources ... PSet: Name: LocalIntranet Description: Default rights given to applications on your local intranet ... Machine ... PSet: Name: Nothing Description: Denies all resources, including the right to execute ... User ... Name: SkipVerification Description: Grants right to bypass the verification PSet: Name: Execution Description: Permits execution ...