Spot the Defect: Modifying the Security Policy in Code

Modifying the CLR's security policy can be done in your code by interacting with the SecurityManager object.  Specifically, you can access the PolicyHierarchy method which will expose an enumerator over the policy levels, and the SavePolicy method, which will commit your changes to the policy.  However, using these methods can be counter-intuitive sometimes.  Here's a sample method with a common problem in it -- if you can spot the defect, leave a comment, I'll provide answers tomorrow.

For some hints, the problem is not:

  • The policy changes won't affect the running code
  • If the policy is not setup close to the default, the method may not work

Here's the code:

 1   /// <summary>
 2   ///      Modify the security policy to give an assembly FullTrust from the LocalIntranet
 3   /// </summary>
 4   /// <remarks>
 5   ///      This method modifies the machine policy level and adds a code group that
 6   ///      matches the given application's key and name but does not take into account
 7   ///      version information.
 8   ///
 9   ///      If the policy is set up in a way that is different from the default CLR security policy,
10   ///      this method may not produce the intended results. Assuming a default security policy, the
11   ///      goal of this method is to produce a policy that looks like this:
12   ///
13   ///      Machine level:
14   ///        1. All code: Nothing
15   ///          1.1 Zone - MyComputer: FullTrust
16   ///            1.1.1 StrongName - MS Key: FullTrust
17   ///            1.1.2 StrongName - ECMA Key: FullTrust
18   ///          1.2 Zone - Intranet: LocalIntranet
19   ///            1.2.1 All code: Same site Web
20   ///            1.2.2 All code: Same directory FileIO
21   ///            1.2.3 StrongName - appKey, assemblyName: FullTrust
22   ///          1.3 Zone - Internet: Internet
23   ///            1.3.1 - All code: Same site Web
24   ///          1.4 Zone - Untrusted: Nothing
25   ///          1.5 Zone - Trusted: Internet
26   ///            1.5.1 All code: Same site Web
27   /// </remarks>
28   /// <param name="assemblyName">assembly to grant FullTrust form the Intranet</param>
29   /// <param name="appKey">Key of the assembly</param>
30   /// <exception cref="ArgumentNullException">
31   ///      If <param name="assemblyName"/> or <param name="appKey"/> are null
32   /// </exception>
33   /// <exception cref="ArgumentOutOfRangeException">
34   ///      If <param name="assemblyName"/> is empty
35   /// </exception>
36   /// <returns>true if the policy was modified, false otherwise</returns>
37  public static bool TrustIntranetAssembly(string assemblyName, StrongNamePublicKeyBlob appKey)
38  {
39    Debug.Assert(assemblyName != null && assemblyName != String.Empty, "assemblyName cannot be empty");
40    Debug.Assert(appKey != null, "appKey cannot be null");
41
42    if(assemblyName == null)
43      throw new ArgumentNullException("assemblyName");
44    if(assemblyName == String.Empty)
45      throw new ArgumentOutOfRangeException("assemblyName", assemblyName, "assemblyName cannot be empty");
46    if(appKey == null)
47      throw new ArgumentNullException("appKey");
48
49    bool addedGroup = false;
50
51    IEnumerator policyEnumerator = SecurityManager.PolicyHierarchy();
52
53     // iterate over each policy level until we find the machine level group
54    while(policyEnumerator.MoveNext())
55    {
56      PolicyLevel policyLevel = policyEnumerator.Current as PolicyLevel;
57      Debug.Assert(policyLevel != null, "Unexpected object in policy enumerator");
58
59      if(policyLevel.Label.Equals("Machine"))
60      {
61         // get a list of all the code groups on the machine level
62        CodeGroup root = policyLevel.RootCodeGroup;
63        IList children = root.Children;
64
65         // iterate over each child until we find the LocalIntranet zone
66        for(int i = 0; i < children.Count; i++)
67        {
68          CodeGroup currentGroup = children[i] as CodeGroup;
69          Debug.Assert(currentGroup != null, "Unexpected object in code group's children");
70
71          if(currentGroup.Name.Equals("LocalIntranet_Zone"))
72          {
73             // create permission objects that can be used to give the application FullTrust
74            IMembershipCondition membershipCondition = new StrongNameMembershipCondition(appKey, assemblyName, null);
75            PermissionSet permissionSet = new PermissionSet(PermissionState.Unrestricted);
76            PolicyStatement statement = new PolicyStatement(permissionSet);
77
78             // now combine these objects into a CodeGroup
79            CodeGroup appCodeGroup = new UnionCodeGroup(membershipCondition, statement);
80            appCodeGroup.Description = String.Format("Allow {0} to run off the Intranet with FullTrust", assemblyName);
81            appCodeGroup.Name = assemblyName;
82
83             // and make this CodeGroup a child of the Intranet zone
84            currentGroup.AddChild(appCodeGroup);
85            addedGroup = true;
86            break;
87          }
88        }
89        break;
90      }
91    }
92
93     // Commit the changes to the policy
95    if(addedGroup)
95      SecurityManager.SavePolicy();
96
97    return addedGroup;
98  }