Managing Security Policy with Code Groups


Security becomes a major challenge when dealing with mobile code and third-party components. For example, you probably want to restrict macros and assembly components from accessing anything other than the document or application that contains them. You may even want to protect your system from bugs in software from trusted vendors . To handle these situations, CAS allows security policy to be enforced on assemblies according to established rules of code group membership. This means that security decisions can be applied to individual assemblies that are used in combination within an application such that each assembly is treated in a specific and appropriate manner according to the trust level of the code group to which it belongs.

It would be too difficult to manage security policy on an assembly-by-assembly basis. Since there could potentially be thousands of assemblies on a given machine, it would be far too labor- intensive to fiddle with them all individually. Instead, you manage code groups by specifying the criteria that is used to determine code group membership for entire categories of assemblies. You can think of a code group as a set of assemblies that represent a common degree of trust. Then, security policy is defined in terms of the permission set granted to each specific code group rather than to each individual assembly.

The Basic Concepts of Security Policy Management

There are three configurable security policy levels: Enterprise, Machine, and User. Enterprise is the highest level, and User is the lowest level. There is also a fourth security policy level, which is the lowest , known as the AppDomain level, [15] but it is only programmatically managed within a given application and not administratively managed. Each level is used to define code access security policy, but lower security policy levels cannot grant more permission than that defined by higher security policy levels. Each policy level contains a hierarchy of code groups. Each code group specifies a membership condition that determines which assemblies belong to that particular code group. Strangely, it is possible to define membership conditions that result in an assembly falling into more than one code group.

[15] An application domain, often referred to as an AppDomain, provides CLR-controlled memory isolation within a program. It is similar in concept to an operating system process in that it provides safety through isolation, but it is much more lightweight and scalable. Multiple AppDomains can exist within a single operating system process. As you may have guessed, only verifiably type-safe code can be managed within an AppDomain. AppDomain-level policy is not configurable, but instead is established by calling the AppDomain.SetAppDomainPolicy method, which is effective only if the SecurityPermission has been granted.

Each code group has a named permission set associated with it. A permission set specifies the actions that the CLR will permit code in that code group to perform. As you can guess from its name , a permission set contains a set of permissions. Each of these permissions defines exactly what resource access or program action is being permitted. Permissions are determined for an assembly at each policy level, and the resulting permissions are determined by evaluating the intersection of the permissions for policy levels.

Ultimately, permissions are determined according to code group membership, which in turn is based on host and assembly evidence. Therefore, you can think of security policy as a mapping from security evidence to a permission set. By manipulating permission sets and code groups membership criteria, the administrator can determine what permissions are ultimately granted to individual assemblies. The .NET platform provides the following two tools for managing .NET security policy:

  • The .NET Framework Configuration tool, Mscorcfg.msc

  • The code access security policy utility, Caspol .exe

The .NET Framework Configuration tool can be used to administer local machine security policy, and it can also be used to create policy deployment packages that target the enterprise using SMS (System Management Server) and Active Directory Group Policy. The Caspol command-line utility is not as easy to use manually, but it is more suitable for automated scripting of policy administration.

Using the .NET Framework Configuration Tool

The .NET Framework Configuration tool runs as an MMC (Microsoft Management Console) snap-in. This tool is used for many configuration purposes, but in this chapter we focus on using it for .NET security policy configuration. You can start this tool by double-clicking on the Mscorcfg.msc file, or you can select Start Settings Control Panel menu, and then select Administrative Tools Microsoft .NET Framework Configuration.

Within this tool, you open the Runtime Security Policy node, as shown in Figure 8-1. As you can see, there are three subnodes named Enterprise, Machine, and User, which are the three configurable security policy levels we talked about earlier. Under each of those policy levels, there are three subnodes named Code Groups, Permission Sets, and Policy Assemblies. Each of these subnodes contains further subnodes, which we discuss in more detail shortly. For now, it is sufficient to understand that the Code Groups node contains criteria for categorizing groups of assemblies with common security requirements, the Permission Sets node contains named sets of security permissions, and the Policy Assemblies node contains fully trusted assemblies that implement classes that define security objects that represent code group membership conditions and code access permissions.

Figure 8-1. Runtime security policy in the Microsoft .NET Framework Configuration tool.

graphics/08fig01.jpg

Actually, policy assemblies are very special. Policy assemblies define permissions and membership conditions that are used by the security engine during permission evaluation. If you add a custom permission or custom membership condition to your security policy, you must first add the assembly implementing the custom security object and all the assemblies it depends on to the list of policy assemblies. The policy system will then grant the required full trust to those assemblies for use in evaluating the security policy that is defined using those custom security objects. Policy assemblies must be fully trusted, digitally signed, and deployed in the global assembly cache before they can be added to the list of policy assemblies. Since policy assemblies enter the inner sanctum of security management, it is obvious that you must be absolutely certain that they are completely trustworthy, or your entire security infrastructure will be compromised!

The highest of the configurable security policy levels is the Enterprise level, which is used to define policy for an entire organization. The second security policy level is the Machine level, which defines policy for all code that is run on the local computer. The third level is User, which defines security policy for the currently logged-on user. Table 8-1 shows the purpose of each of these three policy levels.

Table 8-1. Configurable Security Policy Levels

Configurable security policy level

Purpose

Enterprise

Defines security policy for an entire enterprise.

Machine

Defines security policy for code on the local computer.

User

Defines security policy for the currently logged-on user.

At runtime, when the CLR loads an assembly, it first determines which code group (or groups) the assembly belongs to according to the supporting evidence provided by the assembly, and then it evaluates the appropriate security policy according to its code group membership. The security policy is determined by evaluating all of the three security policy levels, and the result is formed by the intersection of the permissions granted by all three levels. In other words, it requires a unanimous vote so that if an assembly lacks a given permission at any of these levels, then it is not granted that permission.

Figure 8-2 shows the hierarchy of code groups defined under the machine level. At each code group node, you can edit the code group membership properties, and you can also create child code groups. The hierarchy allows you to loosely define group membership near the root node, and as you go further down the tree, you typically define more restrictive membership constraints. Every code group has a single permission set associated with it, and it also has a set of criteria that is used to determine if any given assembly belongs to that code group.

Figure 8-2. Machine code groups.

graphics/08fig02.jpg

If you select a particular code group, and then click on the Edit Code Group Properties link, you will see the Code Group Properties dialog. With this dialog, you can edit the code group name, description, membership condition, and permission set. This is shown in Figures 8-3, 8-4, and 8-5 for the preexisting My_Computer_Zone Code Group.

Figure 8-3. Code Group Properties general tab.

graphics/08fig03.gif

Figure 8-4. Code Group Properties membership condition tab.

graphics/08fig04.gif

Figure 8-5. Code Group Properties permission set tab.

graphics/08fig05.gif

As we have just seen, every code group has a single permission set. However, a single permission set can contain a number of permissions. A single permission represents the right to take some action or access some resource in some way. There are several preexisting permission sets, and it turns out that the .NET Framework Configuration tool also allows you to construct your own new named permission sets.

There are also several preexisting code group membership conditions that are provided by the .NET Framework. Just as was the case for code access permissions, each of the code group membership conditions is also implemented as a class defined in a policy assembly. Also, just as you can create your own custom code access permission classes, you can also create your own code group membership condition classes. The key concept here is simply that a code group membership condition determines whether the membership condition is satisfied by an assembly's evidence. Table 8-2 shows the name and purpose of the prebuilt code group membership conditions along with the evidence used in determining membership.

Table 8-2. Code Group Membership Criteria

Code group

Evidence used in making membership determination

All code

Matches all code regardless of evidence.

Application directory

Application installation directory.

Hash

MD5 or SHA1 hash.

Publisher

Public key of digital signature (X.509 certificate).

Site

Web or FTP site hostname.

Strong name

Digital signature.

URL

The entire originating URL.

Zone

The originating zone (MyComputer, LocalIntranet, Internet, Restricted, or Trusted).

THE PINVOKE EXAMPLE: DEFINING A NEW CODE GROUP

There are several general-purpose predefined code groups, such as My_Computer_Zone and LocalIntranet_Zone, with associated default permission sets. However, there are times when you need to modify existing code groups or define your own new code groups. Let's go through the steps of a typical example where the .NET Framework Configuration Tool is used to define a new code group with specific code access permissions.

The PInvoke example is a simple program that attempts to call into the two Win32 APIs GetComputerName and GetLastError . If you run this program under the original default security policy, you will see that it works properly even though it takes the risk of calling into unmanaged code. This is because it is run on the local machine, and, according to default security policy, the MyComputer security zone is fully trusted, as shown in Figure 8-6

Figure 8-6. My_Computer_Zone is fully trusted.

graphics/08fig06.gif

We will soon see how we can change this so that the PInvoke program is no longer permitted to call into unmanaged code, but, first, let's look at the source code for the program. As you can see, there is no hint of any security- related code whatsoever. This is because we are not interested in security programming at this point. Instead, we want to focus on the administrative business of managing security policy. When you run this program under default security policy, it simply displays the name of the local machine.

 //PInvoke.cs using System; using System.Text; using System.Runtime.InteropServices; using System.Security; public class Test {  [DllImport("kernel32.dll", CharSet=CharSet.Ansi)]  public static extern bool  GetComputerName  (       StringBuilder name, out uint buffer);  [DllImport("kernel32.dll")]  public static extern uint  GetLastError  ();    public static int Main(string[] args)    {       bool result = true;       uint error = 0;       StringBuilder name = new StringBuilder(128);       uint length = 128;       Console.WriteLine(          "Attempting to call GetComputerName");       result =  GetComputerName  (name, out length);       if (result == true)          Console.WriteLine(             "GetComputerName returned: " +             name);       else       {          error =  GetLastError  ();          Console.WriteLine(             "Error! GetComputerName  returned: " +             error);       }       Console.Write("Press Enter to exit...");       Console.Read();       return 0;    } } 

Here is the output from the program. Note that this is before we make any changes to the default security policy. Naturally, the name of your machine will appear differently.

 Attempting to call GetComputerName GetComputerName returned: HPDESKTOP 

Of course, the PInvoke program does not actually do any real damage; otherwise , you would not want to try running it as a demonstration example! But let's pretend for a moment that you are an administrator and you do not have its source code. Since you do not know what it might attempt to do, you might want to configure security such that it cannot do any harm.

To do this, you first define a new code group with membership criteria that includes the PInvoke.exe assembly. Start the .NET Framework Configuration Tool, and then open the Code Groups node under the Machine policy level. You could have just as easily chosen the Enterprise or User level, but in this example you will be working under the Machine policy level. Under the Code Groups node, click on the All_Code node and then click on the Add a Child Code Group link. [16] In the resulting Create Code Group wizard, enter the name My_Own_Zone and a description for the new zone, as shown in Figure 8-7.

[16] We will be adding and changing several aspects of security policy in this section. This can mess things up quite badly if you are not careful. Fortunately, once you are finished experimenting with security policy, you can revert everything back to its original default. To do this, simply right-click on the Runtime Security Policy node, and select Reset All from the context menu. Of course, if you have previously made great effort to establish an intricate policy that you would like to keep intact, then you might want to carefully back out of your changes manually rather than revert back to the defaults. Alternatively, you may want to construct a batch file that uses Caspol.exe to automatically reestablish your baseline security policy configuration.

Figure 8-7. Creating My_Own_Zone step 1.

graphics/08fig07.gif

Click Next to go to the next step of the wizard to choose the code membership condition. In the condition type combo box, choose URL, and in the URL field, enter file://C:/OI/NetSecurity/Chap8/PInvoke/bin/Debug/* , as shown in Figure 8-8. This will make any assembly found in that directory a member of the My_Own_Zone code group.

Figure 8-8. Creating My_Own_Zone step 2.

graphics/08fig08.gif

Click Next to proceed to the third step of the wizard, where you will assign a permission set to the new code group. In the Use existing permission set combo box, select the Execution permission, as shown in Figure 8-9.

Figure 8-9. Creating My_Own_Zone step 3.

graphics/08fig09.gif

Finish the wizard by clicking Next and Finish. The new code group will then appear in the .NET Configuration Tool, as shown in Figure 8-10. Notice that the name My_Own_Zone appears in the code group hierarchy, and the description and membership condition appear in the right-hand pane.

Figure 8-10. My_Own_Zone created.

graphics/08fig10.jpg

Now, click on the Edit Code Group Properties link to see the My_Own_ Zone code group properties dialog. On the General tab, select the checkbox that specifies that this policy level will only have the permissions from the permission set associated with this code group, as shown in Figure 8-11

Figure 8-11. My_Own_Zone properties general tab.

graphics/08fig11.gif

On the Permission Set tab, you can see what permissions are granted by the Execute permission set. As you can see in Figure 8-12, the Execute permission set contains a single permission named Security.

Figure 8-12. My_Own_Zone properties permission set tab.

graphics/08fig12.gif

If you now select the Security permission in the list box, and then click on the View Permission button, you can see the details of this Security permission. Figure 8-13 shows that this permission allows code execution, but it disallows every other possible aspect of the Security permission. [17] In particular, notice that the permission to call unmanaged code is not granted. Since the permission set contains no permissions other than the Security permission, it is clear that the My_Own_Zone code group is rather severely restricted.

[17] If you take a look at the documentation for the SecurityPermission class, you will see each of these Security permission aspects in the form of the Flags property, which takes on values defined by the SecurityPermissionFlag enumeration.

Figure 8-13. My_Own_Zone permission viewer.

graphics/08fig13.gif

Next, close the Permission Viewer window and click OK to close the Code Group Properties dialog. Now that you have finished creating your own code group and established its membership criteria and associated permission set, you can try running the PInvoke program again. Previously, this program simply displayed the current machine name. But now, it throws an exception, as shown in the following output. There is no point in trying to catch this exception within the PInvoke program's Main method, since it is actually thrown when an attempt is made to call the Main method. This is because of the DllImport attribute that is applied to the Main method.

 Unhandled Exception: System.Security.SecurityException:   System.Security.Permissions.SecurityPermission    at Test.Main(String[] args) The state of the failed permission was:   <IPermission class=    "System.Security.Permissions.SecurityPermission,     mscorlib, Version=1.0.3300.0, Culture=neutral,     PublicKeyToken=b77a5c561934e089"              version="1"              Flags="UnmanagedCode"/> 
DEFINING A NEW PERMISSION SET

We have just seen how to add a new code group and associate it with a permission set. However, we just used a predefined permission set named Execution, which has a rigidly defined set of permissions contained within it. Recall that the Execution permission set contains a single permission named Security, and that permission has only the one enabled capability to execute code. What if you want a Security permission that will enable code execution and thread control but disable all other possibilities, such as calling unmanaged code and application domain creation? You may even want to define a new permission set that contains a combination of permissions, such as Security, User Interface, and File IO permissions, each with a customized set of options enabled. For these situations, you need to create a new permission set. [18]

[18] In fact, you can go one step further by programmatically defining a completely customized permission class that controls access to application-specific functionality. This involves several steps, including the definition of a class derived from CodeAccessPermission in an assembly that must be digitally signed, deployed to the global assembly cache, and fully trusted. An XML representation of the permission is also required, and the resulting assembly is then added under the Policy Assemblies node in the .NET Framework configuration tool. Once that is all completed, the new permission may be added to a permission set and used in configuring code group security policy.

To define a new permission set, start the .NET Framework Configuration Tool and open the desired policy level. In this example choose the Machine policy level, and then open the corresponding Permission Sets node. Click on the Create New Permission Set link in the right-hand pane, and in the resulting Create Permission Set wizard, enter the name MyPermissionSet and provide a description for the new permission set, as shown in Figure 8-14

Figure 8-14. Create MyPermissionSet step 1.

graphics/08fig14.gif

Click Next to move to the second step of the wizard, where you construct the permission set contents. You select permission items in the left list box, and click the Add button to move them to the right list box. Figure 8-15 shows what this dialog looks like before any permission has been added.

Figure 8-15. Create MyPermissionSet step 2.

graphics/08fig15.gif

For each of the items that you move to the right list box, you are provided a permission-specific dialog where you can make detailed decisions about exactly what options the permission entails. This is shown in Figure 8-16 in the case of the File IO permission and in Figure 8-17 in the case of the Security permission.

Figure 8-16. File IO permission settings.

graphics/08fig16.gif

Figure 8-17. Security permission settings.

graphics/08fig17.gif

After you have added the desired permissions to your new permission set, you can click Finish to close the Create Permission Set wizard. The final result is shown in Figure 8-18

Figure 8-18. MyPermissionSet created.

graphics/08fig18.gif

Now that you have created the new permission set, you can associate it with a code group in the same way we saw earlier with a predefined permission set. In particular, you should notice that the newly created permission set will now automatically appear in the Create Code Group wizard and the Edit Code Group Properties dialog that we saw earlier.

Using the Caspol.exe Utility

There is a command-line alternative to the .NET Configuration tool, named Caspol.exe . [19] Generally, people prefer to use a nice GUI interface, especially when first learning about the concepts. However, command-line tools are more useful when batch file scripts and programs are used to automate the configuration process.

[19] Caspol stands for code access security policy.

The command-line options supported by Caspol are numerous , and we will not go into them in exhaustive detail here. Instead, let's just look at a few examples of the types of work that can be done with this utility. Table 8-3 shows some of the more commonly used command-line options. Abbreviations for command-line arguments and target operands are not shown here. Please see the Caspol documentation for further details.

Table 8-3. The Caspol Command-Line Options

Caspol command line

Purpose

caspol -help

Display the Caspol command-line documentation.

caspol -machine

Make subsequent commands act on the machine level.

caspol -user

Make subsequent commands act on the user level.

caspol -enterprise

Make subsequent commands act on the enterprise level.

caspol -addgroup ...

Add code group to policy level.

caspol -remgroup ...

Remove code group to policy level.

caspol -listgroups

List code groups.

caspol -addpset ...

Add named permission set to policy level.

caspol -rempset ...

Remove a named permission set from the policy level.

caspol -listpset

List permission sets.

caspol -addfulltrust ...

Add full-trust assembly to policy level.

caspol -remfulltrust ...

Remove a full-trust assembly from the policy level.

caspol -listfulltrust

List full-trust assemblies.

caspol -resolvegroup ...

List code groups that assembly belongs to.

caspol -security ...

Turn security on or off.

caspol -reset

Reset a policy level to its default state.



.NET Security and Cryptography
.NET Security and Cryptography
ISBN: 013100851X
EAN: 2147483647
Year: 2003
Pages: 126

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net