The SAFER Software Restriction Policies (SRPs) are a huge step in the right direction in the battle against malicious mobile code; however, they are not a panacea. One of the key problems of SRP rules is that they are not granular enough. Only a complete process can be blocked—it is impossible to block a tiny portion of code executing in the context of an application process. Also, using SRPs you either disallow or allow a program to run, and you cannot restrict access to only a limited set of system resources.
The solution to this granularity problem is the Common Language Runtime (CLR) and its CAS technology. The CLR is the execution (run time) environment driving the .NET framework. CAS allows for highly granular code access control enforcement. One of the key features of CAS will be its ability to give different levels of trust to the same piece of code depending on factors such as the calling process and the machine where the code is executed. One of the interesting trust levels CAS will support is “sandboxing”—a code isolation technology that has proven effective in the JAVA world. Unlike SAFER, MS promised to make the CLR work on non- Windows XP platforms such as Windows 98, ME, and NT.
Another fundamental feature of CAS is its code-centric access control enforcement approach. Whereas the classic Windows OS and COM+ access control models enforce the security policy based on the identity of a user, CAS uses the identity of a piece of code. Limiting a piece of code’s capabilities by identifying the code and enforcing access control is currently the best way to protect against malicious mobile code execution. Because access control settings are linked to pieces of code, the engine that executes the code can enforce the settings independently of the user or service requesting access to the code.
The CAS approach also separates the definition of access control settings at development time from code execution and the application of security settings at run time. Finally, CAS also creates one of the foundations for Web services security: Applications that are possibly using components spread all over the Internet need an efficient code-centric access control enforcement mechanism.
In the following sections we focus on the administrative aspects of CAS. We explain the key CAS concepts of which every Windows Server 2003 administrator and architect should be aware. This section can also serve as an introduction to CAS for developers. It does not, however, address the programmatic nuts and bolts of CAS.
The .NET term for a logical unit of code is an assembly. A .NET Framework–based application can call one or more assemblies. Developers compile assemblies into platform-independent Intermediate Language (IL). At run time, the engine that compiles and executes the assemblies—the .NET run time or the Common Language Runtime (CLR)—uses a just-in-time (JIT) compilation process to convert IL into platform-dependent native code. The CLR also includes the subsystem that enforces CAS-based access control.
Code that takes full advantage of the CLR’s features (e.g., CAS, JIT compilation, .NET class libraries) is called managed code. Older Win32 COM+ code that uses the legacy COM run time environment is called unmanaged code. Although administrators cannot use the .NET Frame- work’s CAS mechanism to control unmanaged code, they can use the Windows Server 2003 SAFER feature (explained previously) to protect against the execution of legacy malicious mobile code.
Keep in mind that an application cannot take advantage of CAS unless the developer built the application using the .NET Framework building blocks and features mentioned earlier. Also, the platform executing the application code must have the .NET Framework installed. Microsoft bundles the .NET Framework (including the CLR and CAS support) with the Windows Server 2003 Server OS and also makes it available as an add-on component for XP, Windows 2000 (requires Service Pack 2—SP2), Windows NT 4.0 (requires SP6a), Windows ME, and Windows 98. You can download the .NET Framework add-on component from the Microsoft Developer Network (MSDN) Web site at http://msdn.microsoft.com.
CAS is built around the concept of evidence-based access control. This basically means that a piece of code is only allowed to do certain things with certain system resources after it has provided certain “evidence” to the CLR engine executing the code.
You can look at evidence as the equivalent of an identity in the classical Windows authorization model. What a piece of code is exactly allowed to do with a system’s resources is nailed down in a policy. A policy links evidence to system resource permissions. These are the three key concepts of CAS: policy, evidence, and permissions. In the following sections we explore these three concepts in greater detail.
Security policies are probably the most important element of the .NET Framework’s CAS. Policy evaluation is a core task of the CLR’s CAS sub- system. A CAS policy links up “evidence” with “permissions;” in other words, it tells what a particular piece of code is allowed or not allowed to do. “Evidence” and “permissions” will be explained later, but let’s first have a look at “policy.”
In the .NET Framework, policies can be defined on four different levels. The policy levels are differentiated by (1) the persons that can define and administer the policies, (2) the assemblies to which the policies apply, and(3) the order in which they are evaluated by the CAS engine. Table 11.1 lists these four policy levels and their characteristics. In what follows I will focus on the enterprise, machine, and user policies, because typically the.NET application developer, not the systems or domain administrator, defines and configures application domain policies programmatically.
Policy Type | Administered By | Applies To | Evaluation Order |
---|---|---|---|
Enterprise | Administrator | All managed code running on machines that are part of an Active Directory domain or forest infrastructure | 1 |
Machine | Administrator | All managed code running a specific computer | 2 |
User | Administrator or user | All managed code running in the context of a specific user’s logon session | 3 |
Application Domain | Application Developer | All managed code part of a specific application domain | 4 |
Evidence equals an assembly’s identity. The CLR’s CAS subsystem uses an assembly’s evidence and security policy to determine the permissions that the assembly receives. By default, CAS supports the evidence types listed in Table 11.2. An assembly’s evidence can be based on where the assembly is located (URL, Site, Zone, or Application Directory), who created it (Publisher and Strong name), or its content (hash). You can also add your own application-specific evidence types.
Evidence Type | Comments |
---|---|
URL | The URL or file system location from which the assembly originates. An example is http://www.compaq.com/products. |
Site | The site from which the assembly originates. An example is www.compaq.com. |
Zone | The zone from which the assembly originates. The zone concept is similar to the one used in Internet Explorer. |
Application directory | The application’s installation directory. |
Hash | The cryptographic hash value of an assembly. |
Authenticode digital signature | The Authenticode digital signature of the assembly. |
Strong name | The strong name of the assembly. This is a special kind of digital signature. Key elements in a strong name are the assembly’s name, version, and public key. |
Evidence is linked to assemblies using the concept of “code groups.” A code group is a collection of assemblies that have identical evidence. Assemblies that are within the same code group receive identical permissions.
Every policy level has a code group hierarchy. In a code group hierarchy, a parent code group can have multiple child code groups. If an assembly’s evidence matches the evidence of multiple code groups within the same policy level, the assembly will be a member of multiple code groups.
The .NET Framework comes with a set of predefined code groups and hierarchies (see Table 11.3), but you can also define your own. If, for example, you build a Web application for your intranet users and the application has specific CAS security requirements, you probably want to create a new child code group called “MyWebApplication” underneath the LocalIntranet_Zone code group.
Enterprise Policy Code Groups |
---|
All_Code |
Machine Policy Code Groups |
All_Code My_Computer_Zone Microsoft_Strong_Name ECMA_Strong_Name LocalIntranet_Zone Internet_Zone Restricted_Zone Trusted_Zone |
User Policy Code Groups |
All_Code |
Permissions determine how an assembly can use protected system resources or what protected operations the assembly can perform. Examples are writing to a particular registry key or accessing certain performance counters on the machine that executes the code. As you can see in Table 11.4, the CLR’s CAS mechanism lets you define very granular assembly permissions. Table11.4 lists the protected system resources and operations for which you can define permissions. As with evidence and code groups, you can extend the CAS permissions and add your own application-specific permissions.
Permission Resources (Permission Class Name) | Comments |
---|---|
Environment Variables (EnvironmentPermission) | Used to grant/deny assemblies read and/or write access to system environment variables |
File Dialog (FileDialogPermission) | Used to grant/deny assemblies access to the open and/or save file dialog boxes |
File IO (FileIOPermission) | Used to grant/deny assemblies read/write/append/path discovery access to files and directories |
Isolated Storage File (IsolatedStorageFilePermission) | Used to grant/deny assemblies access to file-based isolated storage—can also be used to set disk quotas |
Reflection (ReflectionPermission) | Used to grant/deny assemblies permissions to discover certain information about other assemblies at run time |
Registry (RegistryPermission) | Used to grant/deny assemblies read, write and/or create access to registry keys |
Security (SecurityPermission) | Used to grant/deny assemblies security permissions (for example, allow policy control, allow calls to unmanaged assemblies, and so forth) |
User Interface (UiPermission) | Used to grant/deny assemblies access to user interface elements like Windows, events, and the clipboard |
DNS (DnsPermission) | Used to grant/deny assemblies access to DNS for name resolution |
Printing (PrintingPermission) | Used to grant/deny assemblies access to printers |
Event Log (EventLogPermission) | Used to grant/deny assemblies browse, instrument or audit access to the event logs |
Socket Access (SocketPermission) | Used to let assemblies accept or deny access to certain connections based on IP addresses, ports, TCP, or UDP use |
Web Access ( WebPermission) | Used to let assemblies accept or deny access to certain Web connections based on Web addresses |
Performance Counter (PerformanceCounterPermission) | Used to grant/deny assemblies access to performance counters |
Directory Services (DirectoryServicesPermission) | Used to grant/deny assemblies browse or write access to directory service paths |
Message Queue (MessageQueuePermission) | Used to grant/deny assemblies browse, peek, send, receive or administer access to message queues |
Service Controller (ServiceControllerPermission) | Used to grant/deny assemblies control or browse access to services |
OLE DB (OleDBPermission) | Used to grant/deny assemblies access to OLE DB providers |
SQL Client (SqlClientPermission) | Used to grant/deny assemblies access to Microsoft SQL Servers using ADO.NET |
A very interesting permission is the Isolated Storage File permission, which refers to a special file storage system that you can define on top of the regular file system. The permission’s main characteristic is isolation: It lets an assembly store data and makes sure that the assembly cannot affect other assembly or system data.
To facilitate permissions management, permissions can be grouped into permission sets, which you can link to code groups. As for code groups, there is a set of predefined permission sets for every security policy level. Table 11.5 lists the preconfigured permission sets that come with the .NET Framework.
Permission Set | Comment |
---|---|
Nothing | No permissions |
Execution | Permissions to run |
Internet | Default permission set for code originating from the Internet |
LocalIntranet | Default permission set for code originating from within the intranet |
Everything | All permissions, with the exception of the permission to skip verification |
FullTrust | Full access to all resources |
SkipVerification | Grants right to bypass the verification |
A higher level of security and ease of administration rarely go hand in hand, and CAS security policy configuration is no exception. To configure the different policy levels, code groups, and permission sets, you can use the Microsoft Management Console (MMC) .NET Framework Configuration snap-in (mscorcfg.msc), or you can use the CAS Policy tool (caspol.exe), a command-line utility. The .NET Framework Configuration snap-in is available on any machine that has the .NET framework installed. The CAS policy tool comes with the .NET Framework software development kit (SDK).
An application developer can configure CAS policies and properties programmatically. Administrators can always override specific application domain-level permissions by explicitly denying them at a higher policy level (remember the CAS policy evaluation order mentioned earlier).
If you use the .NET Framework Configuration snap-in, you will set security policies from the Runtime Security Policy container, as Figure 11.6 shows. Each Runtime Security Policy container has three subcontainers: code groups, permission sets, and policy assemblies. The policy assemblies’ subcontainer can contain custom code that the CLR’s CAS subsystem uses to evaluate security policy—this custom code can define things such as custom CAS evidence or permissions.
Figure 11.6: .NET Framework Configuration tool and Security Policy containers.
To view a code group’s description, right-click the code group you want to view, and open the Properties dialog box (as illustrated in Figure 11.7). Click the General tab to see a description of the code group. The second tab, “Membership Condition,” shows the evidence used to uniquely identify the code group, and the third tab, “Permission Set,” shows the permissions that are available to the code group.
Figure 11.7: Code group properties.
A .NET Framework–enabled machine stores the CAS security policy configuration in XML documents. The enterprise policy XML configuration files (enterprisesec.config) and machine policy XML configuration files (security.config) are stored in the .NET run time installation directory. The user policy XML configuration file (also named security.config) is kept in the user’s profile directory. The only exception to this is application domain policy definitions; they are embedded in the assemblies themselves.
Administrators can distribute new CAS security policies to the enterprise by generating a Windows Installer (.msi) file for a given security policy level. They can also change individual policy settings using caspol.exe. If administrators want to use caspol.exe to change multiple policy settings, they can bundle different calls to the tool in a script or batch file.
In this section we look in more detail at how the CAS policy settings are evaluated. Given the different policy levels and multiple code groups, this is a rather complex topic.
Figure 11.8 shows the reference code group hierarchy that we use to explain policy evaluation. In this example there is a code group hierarchy defined for the enterprise, machine, and user policy levels. The assembly that we are evaluating has the necessary evidence to make it a member of all code groups that have a dark gray rectangle. On the machine policy level, for example, our assembly is a member of the “Internet,” “www.hp.com,” and “www.hp.com/dev” code groups.
Figure 11.8: CAS policy evaluation order.
CAS policy evaluation happens when the assembly is loaded into the run time by a CLR component that is commonly referred to as the policy evaluator. Remember that the ultimate goal of the CAS policy evaluation is to determine the permissions a particular assembly will receive. During the policy evaluation process, the CAS policy evaluator will first evaluate the enterprise-level policies, then the machine-level policies followed by the user-level policies, and finally the application domain-level policies. Within each policy level the policy evaluator will start with evaluating the root code group and will then walk down the tree to evaluate all underlying child code groups. If an assembly is not a member of a parent code group, none of its child groups will be evaluated.
At a given policy level, the effective permissions that are granted to an assembly is the union of the permissions granted to the different code groups the assembly is a member of. In our example on the machine policy level, the permissions granted our test assembly will be the sum of the permissions granted to the “Internet,” “www.hp.com,” and “www.hp.com/dev” code groups.
When the CAS policy evaluator is finished evaluating permissions on the different policy levels, it takes the intersection of the permissions granted on these levels to determine the final set of permissions that will be granted to the assembly. After all, this looks relatively simple: On the policy level, the policy evaluator performs a union operation; to determine the final set of permissions it uses an intersection operation between the results of the different policy levels. All of this is summarized in Figure 11.9.
Figure 11.9: Default CAS policy evaluation process.
In Figure 11.9, a code group hierarchy is defined on the four CAS policy levels: enterprise, machine, user, and application domain. On each policy level the CAS engine calculates the sum of permissions granted in the different code groups that apply to the assembly. For example, on the enterprise policy level, this is the union of the permission granted to the A, B, C, and D code groups. The final set of permissions granted to the assembly is the intersection of the results of the different policy levels.
A code group’s properties contain two ways to change the default policy evaluation process, as outlined in Figure 11.9. You can do so using the checkboxes located at the bottom of the “General” tab in the code group’s properties (as illustrated in Figure 11.7). Their effect is illustrated in Figures11.10 and 11.11.
The first option, This policy level will only have the permissions from the permission set associated with this code group, makes membership in a particular code group an assembly’s primary code group membership. If the assembly is a member of other code groups located at the same policy level, the CAS policy evaluator will ignore these code groups. This option is known as the Exclusive code group attribute. At a given policy level, only a single code group can have the Exclusive attribute set. Figure 11.10 shows the effect of the Exclusive attribute.
Figure 11.10: Effect of the “Exclusive” code group attribute on CAS security policy evaluation.
The second option, Policy levels below this level will not be evaluated, limits assembly code-group–membership evaluation to the security policy level of a given code group. If you select this option, known as the LevelFinal code group attribute, the CAS policy evaluator will not evaluate member- ship of code groups lower in the security policy hierarchy. Figure 11.11 shows the effect of the LevelFinal attribute: In this example the user policy level is ignored.
Figure 11.11: Effect of the “LevelFinal” code group attribute on CAS security policy evaluation.
In the previous section we looked at how CAS policy is evaluated; in this section we look at how it is enforced. One of the key features of CAS policy enforcement is its ability to protect against luring attacks. A luring attack occurs when an untrusted piece of code calls on the services of another piece of code to elevate its privileges. In other words, the trusted code has privileges that the untrusted code does not have, and the untrusted code tries to get these by calling on the services of the trusted code.
Figure 11.12 shows how CAS policy enforcement works under normal circumstances. Everything starts off when assembly A1 is loaded on the CLR stack for execution. Assembly A1 calls on the services of assembly A2, which calls assembly A3; the latter finally calls assembly A4, which contains a method that needs a permission P.
Figure 11.12: Normal CAS stack walk behavior.
In the COM+ world the COM execution engine would have checked whether the security principal in whose security context assembly A4 was executing had sufficient permissions. In the .NET world the CLR will check whether the code itself has sufficient permissions, not the security principle in whose security context the code is executing. Remember from the previous sections that thanks to code evidence we can now uniquely identify a piece of code.
The great thing about CAS policy enforcement is that not only will the permissions of assembly A4 be checked, but also the ones of all other assemblies that called on the services of assembly A4. This explains the CAS capability to protect against luring attacks. When assembly A4 demands a permission P, the CLR CAS subsystem will start a process that is known as a “security stack walk.” During this stack walk, the CLR will check whether all calling assemblies have been granted the same permission P as requested by assembly A4. If this is not the case for one of the calling assemblies on the stack, the CLR will generate a security exception and the stack walk will terminate. To check the permissions for the calling assemblies, the CLR will let the CAS policy evaluator subsystem perform a CAS policy evaluation for all calling assemblies. The policy evaluation process was explained in detail earlier.
In the example in Figure 11.12, everything turns out fine for assembly A4: Assembly A4 itself and all the calling assemblies have the permission P, and thus assembly A4 is effectively granted permission P.
Figure 11.13 illustrates a luring attack and how it is blocked by the CLR. In the example assembly A2 is granted a set of permissions G2 that does not contain the permission P requested by assembly A4. Assembly A2 tries to elevate his or her privileges by calling on A3, who then calls on A4. Thanks to the security stack walk, we can block assembly A2’s luring attack.
Figure 11.13: Normal CAS stack walk behavior: protection against luring attack.
Developers can add certain operations to their code to modify the CLR’s security stack walk behavior. These operations are methods that are defined on all the CAS permission classes and permissions sets. They are also referred to as stack walk modifiers. Next we will illustrate the two most important stack walk modifiers: the Assert and Deny methods.
When the Assert method is used for a particular permission, the assembly will get this permission independently of the permissions granted to the calling assemblies. In other words, in this case the CLR is not interested in the outcome of the rest of the security stack walk. The Assert method is a dangerous option: When application developers use it, it is very important that they check the trustworthiness of the calling assemblies on the stack in their code. In the example in Figure 11.14, permission P is set to Assert in assembly A3. As a consequence the CLR will not even call on the policy evaluator to check assembly A3’s permissions: It will simply terminate the stack walk and grant assembly A4 permission P.
Figure 11.14: CAS stack walk behavior with the “Assert” stack walk modifier.
The Deny method is the opposite of the Assert method: Whereas the use of the Assert method can terminate the stack walk with a successful out- come, the use of the Deny method can terminate the stack walk with a negative outcome or, in other words, with the generation of a security exception. In the example in Figure 11.15, permission P is set to Deny in assembly A2. As a consequence, the CLR will not even call on the policy evaluator to check assembly A2’s permissions: It will simply terminate the stack walk and generate a security exception.
Figure 11.15: CAS stack walk behavior with the “Deny” stack walk modifier.