The .NET security administration tools (discussed in Chapter 9) are sufficient for most users and security administrators to configure their security policy, but to have complete control over all security policy features, you must do so programmatically. As with evidence and permissions, .NET represents each of the security policy elements with classes, meaning that you can manipulate them in your own code very easily. In the following sections, we provide explanations of how to program the key components of security policy, starting with code groups and membership conditions, then moving on to policy levels, and finally the security manager. We do not discuss individual permissions or permission sets, because we covered them in Chapter 7. We conclude with an example that brings together all elements of security policy programming; we demonstrate how to manipulate the policy of an application domain to control the policy resolution process applied to the assemblies loaded into it. 8.2.1 Programming Code GroupsThe abstract System.Security.Policy.CodeGroup class provides the base representation of a code group and defines the functionality that lies at the heart of the policy resolution process. Four noninheritable subclasses extend CodeGroup to provide concrete implementations that you can use in security policy programming; these are shown in Figure 8-8. Figure 8-8. Concrete subclasses of CodeGroupFigure 8-9 illustrates the structure of the CodeGroup class, showing that CodeGroup is a container for all of the elements we discussed in Section 8.1.1.1. We summarize how CodeGroup implements each of these component elements here and provide additional details on how to use them later in this section.
Figure 8-9. Structure of the CodeGroup classThe most important method of the CodeGroup class is Resolve, which takes an Evidence collection as an argument. Policy resolution of an assembly (or application domain) consists of the runtime calling the Resolve method on the root CodeGroup in each policy level and passing it the assembly's Evidence collection. In the Resolve method, the CodeGroup is responsible for determining if the assembly's evidence qualifies it for membership, how to apply any attributes, and how or if the CodeGroup should use its children to continue the policy resolution process. Resolve returns a PolicyStatement that represents the net effect of all code groups in the tree to which the assembly qualified for membership. The key difference between each of the CodeGroup subclasses is how they process the Resolve method:
As important as Resolve is to the runtime, both Resolve and the similar ResolveMatchingCodeGroups method provide minimal value when used directly. You cannot use them to drive the policy resolution process manually, and therefore they are useful only for testing and debugging. Table 8-2 summarizes the properties and methods of CodeGroup and highlights any special behavior implemented by the subclasses. No specific code-access permissions are required to use the members of the code group classes.
Before demonstrating the creation and manipulation of code groups, we provide details of membership conditions and policy statements. 8.2.1.1 Programming membership conditionsMembership conditions are classes that implement the IMembershipCondition interface. All four types of code group contain a single IMembershipCondition instance that you must specify as an argument to the code group's constructor; you can get and set the IMembershipCondition through the CodeGroup.MembershipCondition property after construction. The IMembershipCondition interface defines a method named Check, which takes an Evidence collection argument and returns a Boolean value indicating whether the values of the contained evidence objects satisfy a configurable condition: # C# bool Check(Evidence evidence); # Visual Basic .NET Function Check(ByVal evidence As Evidence) As Boolean A CodeGroup calls the Check method of its IMembershipCondtion during policy resolution to evaluate whether an assembly or application domain qualifies for membership to the code group. The .NET Framework includes the eight standard membership condition classes listed in Table 8-3; all are members of the System.Security.Policy namespace. Seven of these classes contain the logic necessary to test the values of standard evidence classes, which we discussed in Chapter 6. One additional membership condition class, named AllMembershipCondition always returns true when Check is called, regardless of the evidence provided. This means a code group using AllMembershipCondition as its membership condition will contain all assemblies and application domains tested against it.
The constructors for each type of membership condition vary depending on the evidence types they evaluate, but all of them are relatively straightforward to use if you understand the standard evidence types. You should consult the .NET Framework SDK documentation for complete details, but here are some examples of how to create membership conditions: # C# // Create a membership condition to match all code. IMembershipCondition m1 = new AllMembershipCondition( ); // Create a membership condition to match all code with // Internet Zone evidence. IMembershipCondition m2 = new ZoneMembershipCondition(SecurityZone.Internet); // Create a membership condition to match all code from // all "oreilly.com" Sites. IMembershipCondition m3 = new SiteMembershipCondition("*.oreilly.com"); // Create a membership condition to match all code with // the same Publisher certificate as was used to sign // the SomeFile.exe assembly. IMembershipCondition m4 = new PublisherMembershipCondition( X509Certificate.CreateFromSignedFile("SomeFile.exe") ); # Visual Basic .NET ' Create a membership condition to match all code. Dim m1 As IMembershipCondition = New AllMembershipCondition( ) ' Create a membership condition to match all code with ' Internet Zone evidence. Dim m2 As IMembershipCondition = _ New ZoneMembershipCondition(SecurityZone.Internet) ' Create a membership condition to match all code from ' all "oreilly.com" Sites. Dim m3 As IMembershipCondition = _ New SiteMembershipCondition("*.oreilly.com") ' Create a membership condition to match all code with ' the same Publisher certificate as was used to sign ' the SomeFile.exe assembly. Dim m4 As IMembershipCondition = _ New PublisherMembershipCondition( _ X509Certificate.CreateFromSignedFile("SomeFile.exe")) 8.2.1.2 Programming policy statementsFor the UnionCodeGroup and FirstMatchCodeGroup classes, a PolicyStatement class represents the effect the code group has on its members, including the code group's attributes and its permission set. The FileCodeGroup and NetCodeGroup classes do not require you to set a policy statement, because they generate their permission sets dynamically and do not support attributes. Provide a PolicyStatement as an argument to the UnionCodeGroup and FirstMatchCodeGroup constructors, and you can get and set the PolicyStatement after construction through the CodeGroup.PolicyStatement property. The PolicyStatement class provides two constructors. The first takes a System.Security.PermissionSet argument specifying the permissions a code group grants to its members. The second constructor takes both a PermissionSet and a member of the System.Security.Policy.PolicyStatementAttribute enumeration, representing the code group's set of attributes. We list the possible values of PolicyStatementAttribute in Table 8-4.
The properties listed in Table 8-5 provide access to the content of the PolicyStatement after instantiation.
The following examples demonstrate how to create PolicyStatement objects. See Chapter 7 for a description of how to create PermissionSet objects: # C# // Create a PolicyStatement that grants Unrestricted access // to everything PolicyStatement p1 = new PolicyStatement( new PermissionSet(PermissionState.Unrestricted) ); // Create a PolicyStatement that grants read access to the // file "C:\File.txt" and specifies the LevelFinal attribute. PermissionSet pset = new PermissionSet( new FileIOPermission(FileIOPermissionAccess.Read,@"C:\File.txt")); PolicyStatement p2 = new PolicyStatement( pset, PolicyStatementAttribute.LevelFinal); # Visual Basic .NET ' Create a PolicyStatement that grants Unrestricted access ' to everything Dim p1 As PolicyStatement = New PolicyStatement( _ New PermissionSet(PermissionState.Unrestricted)) ' Create a PolicyStatement that grants read access to the ' file "C:\File.txt" and specifies the LevelFinal attribute. Dim pset As PermissionSet = New PermissionSet( _ New FileIOPermission(FileIOPermissionAccess.Read,"C:\File.txt")) Dim p2 As PolicyStatement = New PolicyStatement _ (pset,PolicyStatementAttribute.LevelFinal) 8.2.1.3 Creating code groupsWith the knowledge of how to create policy statements and membership conditions, creating code groups is straightforward. You create UnionCodeGroup and FirstMatchCodeGroup objects by providing an IMembershipCondition and a PolicyStatement as constructor arguments. The following example creates a UnionCodeGroup with the Exclusive attribute that matches all code downloaded from any web site in the oreilly.com domain and grants it unrestricted access to the filesystem: # C# // Create the permission set and add unrestricted file access. PermissionSet pset = new PermissionSet(PermissionState.None); pset.AddPermission(new FileIOPermission(PermissionState.Unrestricted)); // Create the policy statement and set the Exclusive attribute. PolicyStatement pstate = new PolicyStatement(pset, PolicyStatementAttribute.Exclusive); // Create the membership condition to match all "*.oreilly.com" sites. IMembershipCondition mcon = new SiteMembershipCondition("*.oreilly.com"); // Create the UnionCodeGroup UnionCodeGroup cg = new UnionCodeGroup(mcon, pstate); # Visual Basic .NET ' Create the permission set and add unrestricted file access. Dim pset As PermissionSet = New PermissionSet(PermissionState.None) pset.AddPermission(New FileIOPermission(PermissionState.Unrestricted)) ' Create the policy statement and set the Exclusive attribute. Dim pstate As PolicyStatement = _ New PolicyStatement(pset, PolicyStatementAttribute.Exclusive) ' Create the membership condition to match all "*.oreilly.com" sites. Dim mcon As IMembershipCondition = _ New SiteMembershipCondition("*.oreilly.com") ' Create the UnionCodeGroup Dim cg As UnionCodeGroup = New UnionCodeGroup(mcon,pstate) The NetCodeGroup class constructor takes only an IMembershipCondition, because it calculates dynamically its permission set and does not support attributes; therefore, there is no need for a PolicyStatement. The following example creates a NetCodeGroup that allows all code signed with the publisher certificate contained in the Publisher.cer file to connect back to the web site from where it is loaded. Details of the WebPermission class are in Chapter 7: # C# NetCodeGroup cg = new NetCodeGroup( new PublisherMembershipCondition( X509Certificate.CreateFromCerFile("Publisher.cer") )); # Visual Basic .NET Dim cg As NetCodeGroup = New NetCodeGroup( _ New PublisherMembershipCondition( _ X509Certificate.CreateFromCerFile("Publisher.cer"))) The FileCodeGroup constructor takes an IMembershipCondition and a System.Security.Permissions.FileIOPermissionAccess argument. The FileIOPermissionAccess specifies the type of file access permission the FileCodeGroup should include in its dynamically generated permission set; see Table 8-6 for possible values. Details of the FileIOPermission class are in Chapter 7.
The following code creates a FileCodeGroup that allows all code signed with the publisher certificate contained in the Publisher.cer file to write to the directory from which it was loaded: # C# FileCodeGroup cg = new FileCodeGroup( new PublisherMembershipCondition( X509Certificate.CreateFromCertFile("Publisher.cer")), FileIOPermissionAccess.Write ); # Visual Basic .NET Dim cg As FileCodeGroup = New FileCodeGroup( _ New PublisherMembershipCondition( _ X509Certificate.CreateFromCertFile("Publisher.cer")), FileIOPermissionAccess.Write) 8.2.2 Programming Policy LevelsThe .NET class library contains the System.Security.Policy.PolicyLevel class to represent all security policy levels: enterprise, machine, user, and application domain. The PolicyLevel class is a container for the component elements we described in "Security Policy Levels": fully trusted assemblies, named permission sets, and code groups. You cannot create new PolicyLevel objects using constructors. To manipulate the enterprise, machine, or user policy levels, you must obtain a reference to the desired level using the static methods of the System.Security.SecurityManager class, which we discuss in the next section. To create a new application domain policy level, use the static PolicyLevel.CreateAppDomainLevel factory method, which we demonstrate in Section 8.2.4. Table 8-7 summarizes the members of the PolicyLevel class. None of the members is protected with code-access permissions. .NET protects the methods you use to obtain and update policy levels, not the methods that manipulate them once you have a reference.
8.2.2.1 Managing fully trusted assembliesThe PolicyLevel class represents fully trusted assemblies by maintaining a list of StrongNameMembershipCondition objects configured to match the strong names of the trusted assembly. You can manage the fully trusted assembly list by providing StrongName or StrongNameMembershipCondition objects to the AddFullTrustAssembly and RemoveFullTrustAssembly methods. The read-only FullTrustAssemblies property gets a System.Collections.IList containing the list of fully trusted assemblies. Example 8-1 creates a StrongNameMembershipCondition object to add an entry to the fully trusted assembly list for the HelloWorld assembly. Example 8-1. Managing a fully trusted assembly# C# // Create a byte array containing the strong name public key // data. byte[] publickey = { 0, 36, 0, 0, 4, 128, 0, 0, 148, 0, 0, 0, 6, 2, 0, 0, 0, 36, 0, 0, 82, 83, 65, 49, 0, 4, 0, 0, 1, 0, 1, 0, 169, 206, 164, 8, 66, 197, 231, 138, 148, 74, 99, 125, 171, 203, 120, 143, 240, 155, 104, 138, 4, 123, 15, 55, 85, 255, 183, 20, 111, 10, 217, 58, 127, 15, 236, 86, 16, 121, 222, 35, 161, 14, 122, 246, 85, 226, 162, 221, 46, 215, 161, 151, 183, 38, 31, 150, 198, 119, 109, 94, 11, 65, 208, 33, 122, 172, 106, 62, 192, 4, 35, 255, 220, 10, 43, 90, 92, 183, 29, 136, 57, 235, 30, 5, 127, 72, 210, 108, 215, 226, 65, 197, 184, 28, 129, 184, 191, 211, 159, 69, 8, 84, 116, 65, 186, 179, 35, 116, 174, 223, 167, 217, 116, 8, 178, 232, 213, 155, 172, 87, 181, 187, 61, 43, 133, 105, 10, 187 }; // Create a StrongNamePublicKeyBlob object from the // public key byte array. StrongNamePublicKeyBlob blob = new StrongNamePublicKeyBlob(publickey); // Create a Version object based on the assembly version // number Version version = new Version("1.1578.0.0"); // Create the new StrongNameMembershipCondition StrongNameMembershipCondition mc = new StrongNameMembershipCondition (blob, "HelloWorld", version); // Create a new application domain policy level PolicyLevel p = PolicyLevel.CreateAppDomainLevel( ); // Add the StrongNameMembershipCondition to the fully trusted // assembly list p.AddFullTrustAssembly(mc); # Visual Basic .NET ' Create a byte array containing the strong name public key ' data. Dim publickey( ) As Byte = { 0, 36, 0, 0, 4, 128, 0, 0, 148, _ 0, 0, 0, 6, 2, 0, 0, 0, 36, 0, 0, 82, 83, 65, 49, 0, 4, 0, _ 0, 1, 0, 1, 0, 169, 206, 164, 8, 66, 197, 231, 138, 148, 74, _ 99, 125, 171, 203, 120, 143, 240, 155, 104, 138, 4, 123, 15, _ 55, 85, 255, 183, 20, 111, 10, 217, 58, 127, 15, 236, 86, 16, _ 121, 222, 35, 161, 14, 122, 246, 85, 226, 162, 221, 46, 215, _ 161, 151, 183, 38, 31, 150, 198, 119, 109, 94, 11, 65, 208, _ 33, 122, 172, 106, 62, 192, 4, 35, 255, 220, 10, 43, 90, 92, _ 183, 29, 136, 57, 235, 30, 5, 127, 72, 210, 108, 215, 226, 65, _ 197, 184, 28, 129, 184, 191, 211, 159, 69, 8, 84, 116, 65, 186, _ 179, 35, 116, 174, 223, 167, 217, 116, 8, 178, 232, 213, 155, _ 172, 87, 181, 187, 61, 43, 133, 105, 10, 187} ' Create a StrongNamePublicKeyBlob object from the ' publickey byte array. Dim blob As StrongNamePublicKeyBlob = New StrongNamePublicKeyBlob(publickey) ' Create a Version object based on the assembly version ' number Dim version As Version = New Version("1.1578.0.0") ' Create the new StrongNameMembershipCondition Dim mc As StrongNameMembershipCondition = _ New StrongNameMembershipCondition(blob,"HelloWorld",version) ' Create a new application domain policy level Dim p As PolicyLevel = PolicyLevel.CreateAppDomainLevel( ) ' Add the StrongNameMembershipCondition to the fully trusted ' assembly list p.AddFullTrustAssembly(mc) 8.2.2.2 Managing named permission setsAs described in Section 8.1.1, each policy level contains a default set of named permissions sets. These default sets cannot be deleted, and, apart from the Everything group, you cannot edit them. To manage a policy level's named permission sets use the AddNamedPermissionSet and RemoveNamedPermissionSet methods. AddNamedPermissionSet takes a NamedPermissionSet argument, whereas RemoveNamedPermissionSet can take either a NamedPermissionSet or a String containing the name of the NamedPermissionSet to remove. You can also change the permission set of an existing NamedPermissionSet without having to add and remove it by calling the ChangeNamedPermissionSet method and passing it the name of the NamedPermissionSet to change, and a PermissionSet containing the new set of permissions. The GetNamedPermissionSet method returns a NamedPermissionSet with the specified name, and the NamedPermissionSets property gets an IList containing the set of NamedPermissionSet objects. Example 8-2 demonstrates the manipulation of named permission sets. Example 8-2. Manipulating named permission sets# C# // Create a new application domain policy level PolicyLevel p = PolicyLevel.CreateAppDomainLevel( ); // Get a copy of the default permission set named "Internet" and // call it "NewPermissionSet" NamedPermissionSet ps = p.GetNamedPermissionSet("Internet").Copy("NewPermissionSet"); // Add the new permission set p.AddNamedPermissionSet(ps); // Modify the permission set "NewPermissionSet" to grant unrestricted // access p.ChangeNamedPermissionSet("NewPermissionSet", new PermissionSet(PermissionState.Unrestricted)); // Remove the NewPermissionSet permission set p.RemoveNamedPermissionSet("NewPermissionSet"); # Visual Basic .NET ' Create a new application domain policy level Dim p As PolicyLevel = PolicyLevel.CreateAppDomainLevel( ) ' Get a copy of the default permission set named "Internet" and ' call it "NewPermissionSet" Dim ps As NamedPermissionSet = _ p.GetNamedPermissionSet("Internet").Copy("NewPermissionSet") ' Add the new permission set p.AddNamedPermissionSet(ps) ' Modify the permission set "NewPermissionSet" to grant unrestricted ' access p.ChangeNamedPermissionSet("NewPermissionSet", _ New PermissionSet(PermissionState.Unrestricted)) Console.WriteLine(p.ToXml( )) ' Remove the NewPermissionSet permission set p.RemoveNamedPermissionSet("NewPermissionSet") 8.2.2.3 Managing the code group treeYou get and set the root code group of the policy level's code group tree using the RootCodeGroup property (see Example 8-3). You must then use the methods and properties of CodeGroup, which we discussed in Section 8.2.1, to build and configure the tree hierarchy. The following code creates the policy level we used in Section 8.1.2 to demonstrate the policy resolution process (see Figure 8-4).
Example 8-3. Manipulating the code group tree of a policy level# C# // Create a new application domain policy level. PolicyLevel p = PolicyLevel.CreateAppDomainLevel( ); // Create the MyCompany named permission set as a copy of // the default LocalIntranet named permission set p.AddNamedPermissionSet( p.GetNamedPermissionSet("LocalIntranet").Copy("MyCompany") ); // Create the My_Site code group that matches all code // run from the "www.mysite.com" Site and grants it FullTrust. UnionCodeGroup MySite = new UnionCodeGroup( new SiteMembershipCondition("www.mysite.com"), new PolicyStatement(p.GetNamedPermissionSet("FullTrust")) ); MySite.Name = "My_Site"; // Create the Work_Site code group that matches all code // run from the "www.company.com" Site and grants it the // MyCompany, permission set. UnionCodeGroup WorkSite = new UnionCodeGroup( new SiteMembershipCondition("www.company.com"), new PolicyStatement(p.GetNamedPermissionSet("MyCompany")) ); WorkSite.Name = "Work_Site"; // Create the Internet_Code code group that matches all code // run from the Internet Zone and grants it Interent permissions. UnionCodeGroup Internet = new UnionCodeGroup( new ZoneMembershipCondition(SecurityZone.Internet), new PolicyStatement(p.GetNamedPermissionSet("Internet")) ); Internet.Name = "Internet_Code"; // Add the My_Site and Work_Site code groups as children of the // Internet code group Internet.AddChild(MySite); Internet.AddChild(WorkSite); // Create the My_Code code group that matches all code // run from the My_Computer Zone and grants it FullTrust. UnionCodeGroup MyCode = new UnionCodeGroup( new ZoneMembershipCondition(SecurityZone.MyComputer), new PolicyStatement(p.GetNamedPermissionSet("FullTrust")) ); MyCode.Name = "My_Code"; // Create the root UnionCodeGroup that matches all code, // but grants no permissions. UnionCodeGroup Root = new UnionCodeGroup( new AllMembershipCondition( ), new PolicyStatement(p.GetNamedPermissionSet("Nothing")) ); Root.Name = "All_Code"; // Add the My_Code and Internet_Code groups as children of the // Root code group Root.AddChild(MyCode); Root.AddChild(Internet); // Assign the code group tree to the PolicyLevel p.RootCodeGroup = Root; # Visual Basic .NET ' Create a new application domain policy level. Dim p As PolicyLevel = PolicyLevel.CreateAppDomainLevel( ) ' Create the MyCompany named permission set as a copy of ' the default LocalIntranet named permission set p.AddNamedPermissionSet( _ p.GetNamedPermissionSet("LocalIntranet").Copy("MyCompany")) ' Create the My_Site code group that matches all code ' run from the "www.mysite.com" Site and grants it FullTrust. Dim MySite As UnionCodeGroup = New UnionCodeGroup( _ New SiteMembershipCondition("www.mysite.com"), _ New PolicyStatement(p.GetNamedPermissionSet("FullTrust"))) MySite.Name = "My_Site" ' Create the Work_Site code group that matches all code ' run from the "www.company.com" Site and grants it the ' MyCompany, permission set. Dim WorkSite As UnionCodeGroup = New UnionCodeGroup( _ New SiteMembershipCondition("www.company.com"), _ New PolicyStatement(p.GetNamedPermissionSet("MyCompany"))) WorkSite.Name = "Work_Site" ' Create the Internet_Code code group that matches all code ' run from the Internet Zone and grants it Interent permissions. Dim Internet As UnionCodeGroup = New UnionCodeGroup( _ New ZoneMembershipCondition(SecurityZone.Internet), _ New PolicyStatement(p.GetNamedPermissionSet("Internet"))) Internet.Name = "Internet_Code" ' Add the My_Site and Work_Site code groups as children of the ' Internet code group Internet.AddChild(MySite) Internet.AddChild(WorkSite) ' Create the My_Code code group that matches all code ' run from the My_Computer Zone and grants it FullTrust. Dim MyCode As UnionCodeGroup = New UnionCodeGroup( _ New ZoneMembershipCondition(SecurityZone.MyComputer), _ New PolicyStatement(p.GetNamedPermissionSet("FullTrust"))) MyCode.Name = "My_Code" ' Create the root UnionCodeGroup that matches all code, ' but grants no permissions. Dim Root As UnionCodeGroup = New UnionCodeGroup( _ New AllMembershipCondition( ), _ New PolicyStatement(p.GetNamedPermissionSet("Nothing"))) Root.Name = "All_Code" ' Add the My_Code and Internet_Code groups as children of the ' Root code group Root.AddChild(MyCode) Root.AddChild(Internet) ' Assign the code group tree to the PolicyLevel p.RootCodeGroup = Root 8.2.3 Programming the Security ManagerThe System.Security.SecurityManager class contains a set of static members that provide access to critical security system functionality and data. Most members of SecurityManager require the caller to have the ControlPolicy permission; ControlPolicy is an element of System.Security.Permissions.SecurityPermission, which we discussed in Chapter 7. The SecurityManager functionality protected by ControlPolicy means that ControlPolicy is one of the highest trust permissions you can grant to code; you must have absolute confidence in the source and integrity of the code to which you grant the ControlPolicy permission. Table 8-8 summarizes the members of the SecurityManager class. The far right column titled "C" identifies those members that require the ControlPolicy permission.
The SecurityEnabled property is critical to the overall operation of the security system. SecurityEnabled is the master switch for code access security. Setting SecurityEnabled to false causes all demands for code-access or identity permissions to succeed; role-based security is not affected. This is effectively the same as giving all code FullTrust but has less overhead, because the runtime bypasses the security system when code makes security demands.
The CheckExecutionRights property controls whether the runtime enforces the Execution permission, which is also an element of the SecurityPermission class. When CheckExecutionRights is false, any code can run regardless of whether it has the Execution permission. The runtime will not store changes to the SecurityEnabled and CheckExecutionRights properties until you call the SavePolicy method. Even after calling SavePolicy, the effect on the current process is unpredictable, and only new processes will work reliably with the new settings: # C# // Turn on security, turn off execution checking, and save the settings SecurityManager.SecurityEnabled = true; SecurityManager.CheckExecutionRights = false; SecurityManager.SavePolicy( ); # Visual Basic.NET ' Turn on security, turn off execution checking, and save the settings SecurityManager.SecurityEnabled = True SecurityManager.CheckExecutionRights = False SecurityManager.SavePolicy( ) The reason you are most likely to be tempted to use both SecurityEnabled and CheckExecutionRights is performance. In an environment where security is not required, it makes sense to remove the overhead of runtime security checks, but the performance gains will vary greatly depending on the nature of the application. In addition, you should consider thoroughly what other factors are affecting application performance before you decide to turn off code-access security. The IsGranted, ResolvePolicy, and ResolvePolicyGroup members are useful for testing and debugging but are not as useful as they might initially seem. IsGranted only determines if the grant set of the calling code contains a specified permission you should not use it as a substitute for the normal security control statements we discussed in Chapter 7. ResolvePolicy and ResolvePolicyGroup both take an Evidence collection and run it through the policy resolution process. ResolvePolicy returns a grant set based on the evidence provided, and ResolvePolicyGroup returns the code groups to which the evidence qualifies for membership. Of more use is the PolicyHierarchy method that returns a System.Collections.IEnumerator containing PolicyLevel objects for each of the active policy levels. Stepping through the enumerator, you can obtain a PolicyLevel object representing one of the active policy levels, allowing you to edit its content. Use the PolicyLevel.Label property to identify the desired PolicyLevel: # C# // Get the enumeration of policy levels. IEnumerator e = SecurityManager.PolicyHierarchy( ); // Step through the PolicyLevel objects, find the User policy // and display it to the console. while(e.MoveNext( )) { PolicyLevel p = e.Current as PolicyLevel; if (p.Label == "User") { Console.WriteLine(p.ToXml( ).ToString( )); } } # Visual Basic .NET ' Get the enumeration of policy levels. Dim e As IEnumerator = SecurityManager.PolicyHierarchy( ) ' Step through the PolicyLevel objects, find the User policy ' and display it to the console. While e.MoveNext( ) Dim p As PolicyLevel = CType(e.Current,PolicyLevel) If p.Label = "User" Then Console.WriteLine(p.ToXml( ).ToString( )) End If End While After editing an active PolicyLevel, you must call SavePolicy or SavePolicyLevel method if you want to save the changes to disk so that they affect future processes. SavePolicy saves all policy levels and policy settings, but SavePolicyLevel saves the configuration of a specified PolicyLevel. SavePolicyLevel saves the specified PolicyLevel back to its source location, which is contained in the PolicyLevel.StoreLocation property. 8.2.4 Programming Application Domain PolicyApplication domain policy is the final layer of security policy evaluated when determining the code-access permissions to grant an assembly. Regardless of the permissions granted to an assembly based on the enterprise, machine, and user policies, you can further lock down its permissions using application domain policy. Application domain policy is resolved even if the assembly is a member of a LevelFinal code group in a higher policy level. You configure application domain policy by calling the AppDomain.SetAppDomainPolicy method and passing a configured PolicyLevel object. Application domain policy can be set only once per application domain setting it a second time results in a System.Security.Policy.PolicyException. Your code must have the ControlDomainPolicy permission to call the SetAppDomainPolicy method. ControlDomainPolicy is an element of SecurityPermission, which we discussed in Chapter 7: # C# public void SetAppDomainPolicy( PolicyLevel domainPolicy ); # Visual Basic .NET NotOverridable Public Sub SetAppDomainPolicy( _ ByVal domainPolicy As PolicyLevel ) Any assemblies loaded into an application domain before you configure the application domain policy are subject to the standard policy resolution results from the enterprise, machine, and user levels. Application domain policy affects only those assemblies loaded after it's set. This allows you to load assemblies that require higher trust, before locking down the application domain through the additional layer of policy. The need to implement application domain policy will not be an everyday occurrence, but when writing applications that are responsible for loading other code, the capability is invaluable. Example 8-4 uses the SetDomainPolicy method to create a policy level with the code group structure shown in Figure 8-10 and assign it to a specified application domain. The root code group matches all code but grants the Nothing permission set. The second layer of code groups determines the permissions to grant based on Url evidence of a loaded assembly. You grant assemblies loaded from the Trusted folder the FullTrust permission set, assemblies loaded from the Untrusted folder the Internet permission set, and assemblies loaded from the Demo the Execution permission set. You grant all other assemblies no permissions. Because we expect an assembly to have only one piece of Url evidence, we make the root code group of type FirstMatchCodeGroup, saving the runtime from performing unnecessary policy resolution processing. Figure 8-10. Sample application domain policyExample 8-4. Creating a policy level and assigning it to an application domain# C# private static void SetDomainPolicy(AppDomain domain) { // Create a new PolicyLevel for the application domain PolicyLevel policy = PolicyLevel.CreateAppDomainLevel( ); // Create a new FirstMatchCodeGroup that matches all code and // grants the named permission set "Nothing". policy.RootCodeGroup = new FirstMatchCodeGroup( new AllMembershipCondition( ), new PolicyStatement(policy.GetNamedPermissionSet("Nothing")) ); // Determine the base path name for the URL evidence String basePath = "file://" + AppDomain.CurrentDomain.BaseDirectory; // Create the code groups that apply to the assemblies loaded // from specific folders. Make each of the the code groups // Exclusive so that they set the absolut maximum permisisons a // plugin can have. policy.RootCodeGroup.AddChild(new UnionCodeGroup( new UrlMembershipCondition(basePath + "Trusted/*"), new PolicyStatement(policy.GetNamedPermissionSet("FullTrust"), PolicyStatementAttribute.Exclusive) )); policy.RootCodeGroup.AddChild(new UnionCodeGroup( new UrlMembershipCondition(basePath + "Untrusted/*"), new PolicyStatement(policy.GetNamedPermissionSet("Internet"), PolicyStatementAttribute.Exclusive) )); policy.RootCodeGroup.AddChild(new UnionCodeGroup( new UrlMembershipCondition(basePath + "Demo/*"), new PolicyStatement(policy.GetNamedPermissionSet("Execution"), PolicyStatementAttribute.Exclusive) )); // Assign the policy to the specified application domain. domain.SetAppDomainPolicy(policy); } # Visual Basic .NET Private Shared Sub SetDomainPolicy(ByVal domain As AppDomain) ' Create a new PolicyLevel for the application domain Dim policy As PolicyLevel = _ PolicyLevel.CreateAppDomainLevel( ) ' Create a new FirstMatchCodeGroup that matches all code and ' grants the named permission set "Nothing". policy.RootCodeGroup = New FirstMatchCodeGroup( _ New AllMembershipCondition( ), _ New PolicyStatement(policy.GetNamedPermissionSet("Nothing"))) ' Determine the base path name for the URL evidence Dim basePath As String = _ "file://" & AppDomain.CurrentDomain.BaseDirectory; ' Create the code groups that apply to the assemblies loaded ' from specific folders. Make each of the the code groups ' Exclusive so that they set the absolut maximum permisisons a ' plugin can have. policy.RootCodeGroup.AddChild(New UnionCodeGroup( _ New UrlMembershipCondition(basePath & "Trusted/*"), _ New PolicyStatement(policy.GetNamedPermissionSet("FullTrust"), _ PolicyStatementAttribute.Exclusive))) policy.RootCodeGroup.AddChild(New UnionCodeGroup( _ New UrlMembershipCondition(basePath & "Untrusted/*"), _ New PolicyStatement(policy.GetNamedPermissionSet("Internet"), _ PolicyStatementAttribute.Exclusive))) policy.RootCodeGroup.AddChild(New UnionCodeGroup( _ New UrlMembershipCondition(basePath & "Demo/*"), _ New PolicyStatement(policy.GetNamedPermissionSet("Execution"), _ PolicyStatementAttribute.Exclusive))) ' Assign the policy to the specified application domain. domain.SetAppDomainPolicy(policy) End Sub |