Introduction to CAS
Code Access Security is a mechanism that is built into the .NET Framework to limit access to resources for managed code. A resource can be anything that might need to be secured, such as a file on disk, a CD-ROM drive, a TCP/IP connection, a database connection, or any number of things.
CAS defines standard permissions and permission sets that apply to all activities and all code run within the .NET Framework. When code is written with CAS in mind, code can request permissions from the system based on the activities that it needs to perform. It can also list the permissions that it would like to have but are not required for proper operation. Administrators can configure a security policy that dictates which sets of code (called code groups) have which permissions.
This chapter covers permissions and code groups and requesting permissions later. For now, it might be helpful if you knew how the .NET Framework authorized code. The code authorization process determines the permissions granted to a piece of code that is running.
To do this, the .NET Framework iterates through all the methods in the call stack and compares the permissions that have been granted to each item on the call stack with the permissions being demanded by those items. Obviously, if an item is demanding more permission than it has been granted, a security exception will occur and the item will be unable to complete its process.
If a semi-trusted (or untrusted) assembly makes a call to a fully trusted assembly without some additional precaution, the untrusted assembly could take advantage of the second assembly's security clearance and potentially cause severe damage to the system. To counteract this, .NET assigns permissions to individual callers on the call stack. Each caller has its own set of permissions. When the highly trusted code requests a permission, the stack is walked backward and the aggregate of permissions is examined. If one of the previous callers has been denied that permission, the execution will fail even if the most recent caller has been granted that permission. Figure 34.1 supplies a visual overview of this process.
Figure 34.1. Illustration of call-stack based permission resolution.
From the figure, you can see that there are three different assemblies, each with a different trust level (set of permissions). When Assembly 1 initiates Action A, whenever subsequently executed code demands permissions, those permissions will be resolved against the trust level of Assembly 1. Even if the request for permission occurs in Assembly 3 (a fully trusted assembly), the stack-walk that the .NET Framework performs will reveal that the call was initiated by a caller with little or no permissions granted and the call will more than likely fail. Action B is initiated by a semi-trusted assembly and will have more permissions than Action A, but less than Action C, because Action C is initiated by an assembly that has been fully trusted.
All of this complexity and depth in resolving permissions associated with code is designed to prevent your code from being abused by malicious code. The idea is that if someone writes malicious code that attempts to use an API that can destroy files, the malicious code will be unable to do so, even though that API has full permissions on the file system. This is possible because the stack of execution calls contains the permission information associated with the malicious code (hopefully untrusted) and can react accordingly.
Before discussing the permissions themselves, let's summarize the way that CAS works with assemblies. When an assembly is loaded, evidence such as the identity, public key, filename, version number, and zone (Internet, Intranet, so on) is combined with the configurable policy (you will see this policy later in the chapter) to produce a set of permissions that have been granted to the assembly. This set of permissions is then assigned to each caller on the stack to allow CAS to determine what callers from the assembly can and cannot do.
Using Code Access Security Permissions
By now you're probably wondering what a permission is. A permission is a discrete unit of ability. When code has a permission, it means that the code will be allowed to perform the task associated with the permission. In the list of permissions that you will see later, all the permissions refer to very specific actions to be taken by code when permission has been granted.
Code Access Permissions
Code access permissions are designed to protect resources and operations from unauthorized code and inappropriate, potentially harmful use. Each of the permissions in Table 34.1 grants the privilege to access a protected resource or to perform a particular secured task.
When protecting your code from unauthorized use, it is often necessary to require that only calling code with a certain identity be given execute access to your code. For this purpose, and many others, there are identity permissions. Identity permissions are designed to represent various pieces of identity-related evidence that is associated with an assembly. Table 34.2 is a list of the identity permissions in the .NET Framework.
Role-Based Security Permissions
There is only one role-based security permission class within CAS: PrincipalPermission. This is an identity-style permission. Instead of checking for the identity of the calling code, it checks the identity of the current security context, allowing code to demand identity and role membership of the current security context.