Home > Articles > Programming > Windows Programming

Exploring the CLR

  • Print
  • + Share This
This chapter is from the book

Code Access Security

In the modern computing environment, you may receive code from several different sources: from your local hard drive, from your corporate intranet, from a network drive, from email, or from the Internet. Depending on where the code originates from and other properties of the code, such as who authored the code, you may want to assign different permissions to the code. For instance, if the code originated from your hard drive, you may want to allow it to do just about anything. If it originated from your local intranet or from a network drive on your corporate intranet, you may not want to prevent the code from writing to your local files, but you may allow it to read your files. If the code came from the Internet, you probably would want to prevent it from doing most things.

The Code Access Security functionality in .NET provides you with the capability to define, in a very precise way, the permissions that code will be granted based on evidence that includes (among other things) the location where the code originated from. The basic idea is that certain code groups with membership conditions are based on evidence that the CLR can gather about the assembly. This evidence includes the zone, site, or URL where the assembly originated from, who published the assembly, the strong name of the assembly, the hash of the assembly, or whether the assembly came from the same directory or a child directory of its client application. Each code group has a permission set associated with it. A permission set is a named set of permissions that you can grant as a single unit to a code group. Some of the individual permissions that comprise a permission set include the following:

  • Whether the code can read or write files

  • Whether the code can read or write environment variables

  • Whether the code can access isolated storage, or access the local DNS server, or open a window on the user interface of the client

  • Whether the code is allowed to print

If an assembly matches the membership condition for a code group, it is granted all of the permissions in the permission set associated with that code group.

When the CLR loads an assembly, it gathers the evidence associated with the assembly, that is, where the code originated from (local hard disk, intranet, Internet, and so forth), its publisher, hash and strong name, and so forth and presents this evidence to the security policy in effect on the machine. The security policy is the set of rules that the CLR uses to determine which permissions to grant to an assembly based on its presented evidence as shown in Figure 3–17.

The steps that the CLR performs to determine the permissions that it will grant to an assembly are as follows:

  1. Gather the evidence associated with the assembly.

  2. Present this evidence to the security policy that is in effect on the machine.

  3. The security policy determines the list of matching code groups for the assembly and, from that, determines the list of permission sets to grant to the assembly.

    Figure 17Figure 3–17 Security policy.


  4. The CLR grants the union of the permissions in the matched permission sets to the assembly.

The assembly may affect this final set of granted permissions by refusing certain permissions and requesting minimum and optional permissions, but I discuss this more when I drill-down deeper into security policy later in this chapter.

Now that you have a high-level idea of how permissions are granted to an assembly, let's discuss how those permissions used? All of the .NET Framework class library methods that access protected resources demand permission to use the resource before they actually use it. For instance, before it executes any of the code in an assembly, the CLR demands permission to execute code. When you open a disk file, the class library method that you use will demand FileIO permission. Any action that may have security implications, such as attempting to read or write environment variables or the registry, to open a UI window, or to execute unmanaged code will lead to a permission demand. Each type of permission has a class associated with it in the System.Security namespace. Each of these permission classes derives from a class called System.Security.CodeAccessPermission. The CodeAccessPermission class contains a method called Demand. This is the method that the CLR and the .NET Framework class library call to demand a particular permission. When you demand a permission, the CLR will walk upward through the current call stack, and, if any of the callers in the stack have not been granted the requested permission, it will throw a security exception. This stack walk is a critical part of code access security. It prevents a "Trojan horse" attack, where an assembly that has not been granted a particular permission can cause damage simply by loading another piece of code that does the damage on its behalf. If the Trojan horse assembly calls a method in a dangerous assembly, the stack walk will wind its way back up to the original caller (the Trojan horse), and, if this assembly was not granted the permission to perform the operation, the CLR will throw a security exception. One exception to this behavior (which is important given that this book talks about COM Interop and using unmanaged code) is that code access security does not affect managed code. Therefore, a Trojan horse assembly can simply call unmanaged code (either a Windows API method or a COM object) to do its dirty work. Fortunately, before the CLR makes a call to unmanaged code, it will demand the permission to execute unmanaged code. If this permission has not been granted, a security exception will be thrown. Therefore, if you are worried about Trojan horse programs like this, you can simply not grant the execute unmanaged code permission to any assemblies. In fact, by default, only code that originates from your local hard drive is allowed to execute unmanaged code anyway. Code that comes from either the Internet or your local intranet will not be able to execute unmanaged code unless you explicitly allow it to.

The preceding explanation was a very high-level view. Let's look at code groups and permission sets and security policies at a slightly lower level, and then I will conclude with an example.

Code Groups

A code group is comprised of a membership condition and a permission set. If an assembly matches the membership condition for the code group, it is granted the permissions in the code group's associated permission set. When you install the .NET Framework, a number of code groups are defined for you already, and you can create additional ones. To see the code groups that currently reside on your machine, we'll use the .NET Framework Configuration Tool, which is a convenient snap-in for the Microsoft Management Console (MMC).

NOTE

You can also view and edit the code groups on your machine using the Code Access Security Policy tool (caspol.exe), which is a command-line tool that is included with the .NET Framework SDK. The .NET Framework Configuration Tool is much easier to use, so I have used it throughout this chapter.

To view the code groups on your machine in the .NET Framework Configuration Tool, perform the following steps:

  1. Select Programs | Administrative Tools | Microsoft .NET Framework Configuration from the Start menu. (You will see the window shown in Figure 3–18.)

    Figure 18Figure 3–18 The .NET Framework Configuration Tool.

  2. Open the Code Groups node under Runtime Security Policy | Machine | Code Groups as shown in Figure 3–19.

    Figure 19Figure 3–19 Viewing code groups.


Notice that three levels of code groups are displayed in the .NET Framework Configuration Tool: Enterprise, Machine, and User. I discuss these various levels and the hierarchical structuring of the code groups more when I talk about security policy. For now, let's looks at the machine node. The root of the code group tree contains a code group called All_Code that all assemblies will match. If you want to apply a set of permissions to all assemblies (for a given policy level), you can put the permission in the permission set associated with the All_Code code group. Notice that all of the code groups directly beneath the All_Code code group are based on zones. In other words, the membership condition for the code group is that the assembly must have originated from the specified zone. (See the sidebar in this chapter about zones to learn how zones are created and edited.) The Intranet_Same_Site_Access, Intranet_Same_Directory_Access, and Trusted_Same_Site_Access code groups are custom code groups with custom permission sets that allow a component downloaded from a trusted or intranet site to connect back to the Web site where it originated from (Intranet_Same_Site_Access and Trusted_Same_Site_Access) and allow a component downloaded from an intranet site to access the directory that it originated from (Intranet_Same_Directory_Access). You cannot edit these code groups with the .NET Framework Configuration Tool, but, fortunately, you can edit all the others.

Zones

Zones are managed within Internet Explorer. To view the zones that currently reside on your machine, perform the following steps:

  1. Start Internet Explorer.

  2. Select Tools | Internet Options. The Internet Options dialog will appear.

  3. Click the Security tab. Your window should look as shown in Figure 3–20.

    Figure 20Figure 3–20 The Internet Options Dialog.


For all zones except the Internet zone, you can select the zone and then click the Sites button to add or remove sites from that site. If you use the Intranet zone, you can choose which sites you consider to be on your Intranet. The Trusted and Restricted zones are simply lists that you can add sites to. If you trust that a site will not damage your computer you can add it to the Trusted list. Or if you believe that a site may damage your computer you can add it to the Restricted list. The Internet zone consists of all sites that are not in any of the other zones. You can assign permissions to web sites in each of these zones.

You can view and edit the membership condition and permission sets associated with a code group by right-clicking the code group and selecting Context | Properties. You will then see the code properties dialog as shown in Figure 3–21 for the Local Intranet. Zone.

Figure 21Figure 3–21 The General tab of the code group properties dialog.


On the General tab, notice that each code group has a name and a description. I will explain the two checkboxes on this page when I talk about security policy later in this chapter. The Membership Condition tab is how you specify the criterion that the CLR should use to determine if an assembly is a member of the code group. The Membership Condition tab of the properties dialog is shown in Figure 3–22.

Figure 22Figure 3–22 The Membership Condition tab of the code group properties dialog.


The controls that you see on this window are a function of the condition type that you select for the code group. The default condition type is Zone, and, when you select this condition type, you will be presented with a dropdown that allows you to select the zone for the code group as shown in Figure 3–22. If you selected Publisher as your condition type instead, you will see controls that allow you to enter a digital signature for a publisher as shown in Figure 3–23.

Figure 23Figure 3–23 The Membership Condition tab of the code group properties dialog with Publisher selected for the condition type.


You can choose the condition type from the top-most dropdown. The possible options are shown in Table 3–14:

Table 3–14 Available condition types for code group membership conditions

Condition Type

Returns True if . . .

All

For all assemblies

Application Directory

If an assembly originates from the same directory or a child directory of the running application

Hash

If the hash of the assembly matches the one specified

Publisher

If the digital signature matches the one specified

Site

If the assembly comes from the specified site

Strong Name

If the assembly matches the one specified

URL

If the assembly originates from the specified URL

Zone

If the assembly originates from the selected zone

Custom

If the assembly matches the custom membership condition specified. You cannot add code groups with custom membership conditions with the graphical tool.


Permission Sets

After an assembly matches the membership condition for a code group, it is granted the permissions in the permission set associated with the code group. Permission sets allow you to group permissions together so that you can add them as a single unit to a code group. When you install the .NET Framework, it will create the permission sets shown in Figure 3–24 on your machine.

Figure 24Figure 3–24 Permission sets.


You can also add your own permission sets. Each permission set contains a list of permissions. The set of individual permissions that permission sets are built from is shown in Table 3–15.

Table 3–15 Available permissions to add to a permission set

Permission

Description

Directory Services

A list of active directory paths. For each path, you can specify whether you can just browse or also write to the directory, or you can specify unrestricted access.

DNS

You can choose whether an assembly has unrestricted access to DNS or no access whatsoever to DNS.

Event Logs

You can grant Browse, Instrument, or Audit permissions for the Event Logs on a list of machines, or you can specify unrestricted access.

Environment Variables

You can grant read and/or write access to a list of environment variables, or you can specify unrestricted access.

File IO

You can grant read, write, and append privileges to a list of directory paths, or you can specify unrestricted access.

File Dialog

You can grant access to the Open, Save, or Open and Save dialogs, or you can specify unrestricted access.

Isolated Storage

You can grant access to file-based isolated storage with a specified disk quota and usage, or you can grant unrestricted access to file-based storage.

MessageQueue

You can grant access to a specific queue or to all queues.

Custom

You can grant access to a custom permission that you must specify using an XML schema. You cannot add custom code groups with the .NET Framework Configuration Tool.

OLEDB

You can grant access to specific OLEDB providers or unrestricted access to all OLEDB providers.

Performance Counters

You can grant browse or instrument access to a list of performance counters or unrestricted access to all performance counters.

Printing

You can specify All Printing, Safe Printing, Default Printing, or unrestricted access to printing.

Registry

You can specify Read, Write, or Create access to a list or registry keys or unrestricted access to the registry.

Reflection

You can specify that an assembly can read member (methods, properties, and so forth) and/or type (classes) information and/or that the assembly is allowed to emit metadata, or you can specify unrestricted access to reflection.

Security

You can specify whether an assembly should (or should not) be granted any of a number of permissions including:

Enable Assembly Execution
Allow Calls to Unmanaged assemblies
Skip Verification
Enable Thread Control
Enable Remoting Configuration

Service Controller

You can specify browse or control access to a list of services on specified machines or unrestricted access to all services.

Socket Access

You can allow access to a particular end point, that is, host/port/direction using TCP or UDP or unrestricted access to all sockets.

SQL Client

You can allow access to Microsoft SQL Servers through ADO.NET or unrestricted access to Microsoft SQL Server.

Web Access

You can grant accept or connect privileges to a list of hosts or unrestricted access to all Web sites.

User Interface

You can grant access to UI windows and/or the clipboard, or you may grant unrestricted access to all user interface elements.


The Security permission is perhaps the most interesting because you must grant the Enable Assembly Execution permission for an assembly to run at all, and you must grant the Allow Calls to Unmanaged assemblies permission in order to use unmanaged code through COM Interop.

Permission Sets are a convenience that frees you from the need to add each of these permissions individually to your code groups. You simply compile these permissions into permission sets, and then you can assign these permission sets to a code group.

Security Policy

Simply saying that an assembly is granted the permissions in the permission sets associated with the code groups that it matches is actually an oversimplification. In reality, the mapping from a set of matched code groups to a list of permissions that the assembly is granted is quite complicated. This mapping of matched code groups to permissions is called security policy.

I mentioned earlier that there are four levels of code groups. Three of these levels are visible in the .NET Framework Configuration Tool: Enterprise, Machine, and User. You can see these in Figure 3–25.

Figure 25Figure 3–25 The three code group levels visible in the .NET Framework Configuration Tool.


The fourth level is the Application Domain level, which you can set programmatically. In Figure 3–25, you can also see that, within each level, the code groups are laid out in a hierarchy as shown in Figure 3–26.

Figure 26Figure 3–26 Code group hierarchy.

When the CLR loads an assembly, it will walk the code group hierarchy once for each of the four levels. It determines the set of permissions at each level as the union of the permissions in the permission sets associated with the matched code groups at that level. After the CLR determines the permissions granted for a particular level, it then calculates the permissions granted from security policy as the intersection of the permissions granted at each level as shown in Figure 3–27.

Figure 27Figure 3–27 Granted permissions.

If you are not familiar with set theory, this means that, in order for a particular permission, let's say File IO, to be granted to an assembly, it must be a part of the calculated permissions for each level. Therefore, by default, only the Machine level is relevant because the Enterprise and User levels initially have the All_Code code group associated with them, and the permission set associated with the All_Code code group is Full Trust, that is, all permissions are granted. A system administrator can use the Enterprise or User Permissions then to prevent certain permissions from being granted even if they would ordinarily be granted based on the code groups and permission sets at one of the other levels.

At each level, the walk of the code group hierarchy begins at the All_Code code group, which matches every assembly.

NOTE

The All_Code code group is a convenient place to put permissions that you would like every assembly to have. By default, this code group does not have a permission set associated with it.

If an assembly's evidence matches the membership condition for a code group, the CLR will also check the children of the code group for matches. For example, consider that I modified the default hierarchy shown in Figure 3–26 so that I added two custom code groups. The membership condition for each of these code groups uses the Site condition with the specified site for one of the code groups being the Human Resources (HR) server and other site being the Accounting server on the corporate LAN. The code group hierarchy at the Machine level will now look as shown in Figure 3–28.

Based on this hierarchy, a Microsoft .NET-based application loaded from your local hard drive will match the following code groups at the Machine Level: All Code, My Computer zone, and Microsoft strong name. A .NET application downloaded from the HR server on your corporate intranet will match the following code groups: All Code, Local intranet zone, HR server, Intranet same site access, and Intranet same directory access.

Figure 28Figure 3–28 A modified code group hierarchy.

NOTE

You can alter the way the tree is traversed. You can do this using the Exclusive and LevelFinal attributes on a code group. If the Exclusive attribute is applied to a code group at a policy level, the CLR will grant the permissions in that code group for the policy level and ignore the other code groups at that policy level. You can obviously only have one code group at a given policy level with the Exclusive attribute. The policy levels are arranged in a hierarchy with Enterprise at the highest level, following by machine, user, and then AppDomain. If the LevelFinal attribute is applied to a code group at a policy level, as long as the assembly matches a code group at that policy level, no code groups at a lower level will be checked.

After the CLR determines the permissions at each level by traversing the code group hierarchy at each level, it calculates the granted permissions by taking the intersection of the permissions granted at each of the four levels. This total is called the permissions from policy and is still not the final set of permissions that the CLR will grant to the assembly. An assembly is allowed to refuse permissions. In general, an assembly should not accept more permissions than it needs. An assembly can also request the minimum set of permissions that it requires to run. If the assembly is not granted at least the minimum set of permissions, it will fail to load. This prevents the CLR from loading an assembly and then failing with a security exception every time the assembly tries to do anything. An assembly can also request optional permissions. These are permissions that the assembly can run without, but can take advantage of if they are available. The CLR will not grant more privileges than the set of minimum plus optional permissions. The final formula for the set of granted permissions is as follows:

Pgranted=Pmin + (Poptional _ Ppolicy) - Prefused

If you are not familiar with set theory, this formula reads in English: the set of granted permissions Pgranted equals the minimum requested permissions (Pmin) plus the set of permissions that are both declared optional permissions by the assembly (Poptional) and granted by policy (Ppolicy), minus the list of permissions that are explicitly refused by the assembly (Prefused).

Example

I think a lot of these concepts can be made clearer with an example. Earlier in this chapter when I talked about assembly binding, I showed you an example application, which you can download from the Web site, called the AssemblyMetaDataViewer, which you can see here in Figure 3–29.

Figure 29Figure 3–29 The assembly metadata viewer.

This application loads assembly files, so it will call .NET Framework class library methods that will demand the file IO permission. Make sure the multifile assembly that you built in this chapter is loaded into the GAC by performing the following steps:

  1. Open a Visual Studio .NET command prompt by selecting Programs | Microsoft Visual Studio .NET | Visual Studio .NET Tools | Visual Studio .NET Command Prompt from the Start menu.

  2. Navigate to where you built the multifile assembly.

  3. Enter "gacutil –i multifile.dll" at the command prompt.

Now run the AssemblyMetaDataViewer application. Enter "multifile" into the Assembly Name textbox and click the Load button. The tool calls the Load method in the System.Reflection.Assembly class, and then it displays the location that it loaded the assembly from. Running the tool from your hard drive verifies that the AssemblyMetaDataViewer is allowed to open and read an assembly file when you run it from your local machine.

Let's try running the AssemblyMetaDataViewer application by downloading it from the intranet. First, create an IIS virtual directory by performing the following steps:

  1. Select Programs | Administrative Tools | Computer Management from the Start menu. The computer management MMC console will appear.

  2. Open the Services and Applications node.

  3. Open the Internet Information Services node.

  4. Right-click on Default Web site.

  5. Select New | Virtual Directory from the console. You should see Step 1 of the Virtual Directory Creation Wizard.

  6. Click the Next button. You should see Step 2 of the Virtual Directory Creation Wizard.

  7. Enter "TestSecurity" in the Alias field.

  8. Click the Next button. You should see Step 3 of the Virtual Directory Creation Wizard, which allows you to specify where the physical directory is.

  9. Click the Browse button and navigate to where the AssemblyMetaDataViewer executable resides on your hard drive.

  10. Click the Next button. You should see the access permissions step of the Virtual Directory Creation Wizard.

  11. Accept the defaults for the access permissions and click the Next button.

  12. Click Finish on the last page of the wizard.

Now, open Internet Explorer and navigate to the following URL:

http://localhost/testsecurity/assemblymetadataviewer.exe

The assemblymetadataviewer application should appear. Now enter "multifile" again and click the Load button. You should see the error message shown in Figure 3–30.

What happened? First, let's go back to the .NET Framework Configuration Tool and see if we can figure out which code groups the assembly will match when we access it from the intranet. The .NET Framework Configuration Tool is shown in Figure 3–31.

At the machine level (remember this is the only level that matters by default because the other levels grant everything), the assembly will match the All Code and Intranet zone groups. The All Code code group has the Nothing permission set associated with it, and the LocalIntranet_Zone code group has the Local Intranet permission set associated with it. Figure 3–32 shows the list of permissions in the Local Intranet permission set.

Figure 30Figure 3–30 The security error when running over the intranet.

Figure 31Figure 3–31 The three code group levels visible in the .NET Framework Configuration Tool.


Figure 32Figure 3–32 Permissions in the Local intranet permission set.


Notice that this permission set does not include the FileIO permission that you need to use the AssemblyMetaDataViewer. So how do you fix this? To state this more specifically, how can you allow the assemblymetadataviewer application to run in an Intranet environment? You have a number of choices. You could add the FileIO permission to the Local intranet permission set. This has the side effect of granting the FileIO permission to all assemblies that originate from you Intranet. A better choice would be to add a new code group beneath the Local intranet code group with a URL membership condition. We can set the granted permissions associated with this code group to allow the FileIO permission only to assemblies that originate from the URL of the metadataviewer application. Using this approach also allows me to demonstrate how to create a new code group and a new permission set. To add the code group and new permission set, perform the following steps:

  1. In the .NET Framework Configuration Tool, right-click the LocalIntranet_Zone code group (a context menu will appear) as shown in Figure 3–33.

    Figure 33Figure 3–33 The context menu for a code group.


  2. Select New from the context menu. The first page of the Create Code Group wizard will appear as shown in Figure 3–34.

    Figure 34Figure 3–34 The first page of the Create Code Group wizard.


  3. Enter the name "allow_metadataviewer" (or any name you want) for the code group, enter a description (if you want to, this is not required), and click the Next button. The second page of the Create Code Group wizard will appear as shown in Figure 3–35.

    Figure 35Figure 3–35 The second page of the Create Code Group wizard.


  4. Select URL for the Condition Type, enter "http://localhost/testsecurity/assemblymetadataviewer.exe" for the URL (or whatever is the URL where you deployed the metadataviewer), and click the Next button. The third page of the Create Code Group wizard will appear as shown in Figure 3–36.

    Figure 36Figure 3–36 The third page of the Create Code Group wizard.


  5. Click the Create a new permission set radio button and click the Next button. The first page of the Create Permission Set wizard will appear as shown in Figure 3–37.

    Figure 37Figure 3–37 The first page of the Create Permission Set wizard.


  6. Enter the name "allow_metadataviewer" (or any name you want), (optionally) add a description, and then click the Next button. The second page of the Create Permission Set wizard will appear as shown in Figure 3–38.

    Figure 38Figure 3–38 The second page of the Create Permission Set wizard.


  7. Select File IO from the list of available permissions and click the Add >> button. The configuration dialog for the File IO permission will appear as shown in Figure 3–39.

    Figure 39Figure 3–39 The configuration dialog for the File IO permission.


  8. We'll keep things simple and just click the Grant assemblies unrestricted access to the filesystem checkbox and then click the OK button.

  9. You should now be back on the second page of the Create Permission Set wizard (see Figure 3–38). Click the Next button on this page and then click the Finish button on the final page of the Wizard.

    The membership condition for the allow_metadataviewer code group is: all assemblies that originate from URL http://localhost/testsecurity/assemblymetadataviewer.exe, and the permission set granted to an assembly that matches this membership condition contains the File IO permission. The metadataviewer will obviously match the membership condition for this group and therefore will be granted the permission. Try running the metadataviewer again by pointing Internet Explorer at the following URL:

    http://localhost/testsecurity/assemblymetadataviewer.exe

    You should now be able to enter "multifile" (or any other assembly name for that matter) and load the assembly without problem.

    Now if you are following along with this example, you are probably thinking that it would be too much trouble for someone to perform these steps manually for each machine on a corporate LAN that needs to use an application. That's where the Code Access Security Policy tool (caspol.exe) comes in handy. Caspol.exe is a command-line tool that is included with the .NET Framework SDK that allows you to perform all the steps I just performed with the .NET Framework Configuration Tool in a command-line environment. You can create scripts that perform the same steps that I did manually and then run the caspol.exe utility on those scripts to apply your Code Access Security Settings to a machine. You could even do this automatically when a user logs in. See the .NET Framework SDK documentation to learn more about the Code Access Security Policy tool..

  • + Share This
  • 🔖 Save To Your Account