Professional ASP.NET Security - Jeff Ferguson
.pdfCode Access Security
Rogue |
not among Gl |
Assembly |
|
Al calls into A2
\
Trusted Assembly For each Assembly in the upper call stack, CLR checks if
FileAccess permission is
FileStream Assembly |
permissions |
^^
Demands for FileAccess permission
In this diagram Gl, G2, and G3 are the permissions each assembly has been granted by security policies at load time, and the large arrow on the right the direction of the stack walk.
Evidences
Now that we've provided the big picture, let's investigate the details, starting with CAS evidences.
Evidences provide information about an assembly identity. They can be split into two main categories: assembly inner properties (such as assembly Strong Name or Publisher) and assembly origin information (such as URL, Site, Zone). The former information is fixed, while the latter can vary. In the following table, we can see a list of the .NET Framework built-in evidences.
Evidence |
Description |
Evidence Type |
|
|
|
Application |
Base path for probing private assemblies |
Assembly origin |
Directory |
|
|
Hash |
Cryptographic hash such as SHA1 |
Within assembly |
Publisher |
Authenticode signature |
Within assembly |
Site |
Site of origin, such as |
Assembly origin |
|
http://www.microsoft.com |
|
Strong Name |
Cryptographically strong name of the |
Within assembly |
|
assembly (generated by sn. exe tool) |
|
URL |
URL of origin |
Assembly origin |
Zone |
Zone of origin, such as Internet Zone |
Assembly origin |
|
|
|
Some of these evidences may not be available for some assemblies, such as the Publisher or the Strong Name.
315
Note that some evidences are stronger than others, depending on how easily they can be tampered with. For instance, Strong Name evidence is much stronger (expecially if using 1024-bit keys) than URL evidence, since DNS and web sites can be hijacked.
The evidence entries shown above are defined as built-in for the following reasons:
Q They are automatically evaluated and attached to the assembly by the CLR loader when referenced for the first time.
Q The default policy configuration set up when the. NET framework is installed is based upon such evidences.
Each built-in evidence is represented by a specific class in the BCL (there is a Site class, a Url class, and so on). Each built-in evidence class has a companion "membership condition" class (such as the ZoneMembershipCondition class, StrongNameMembershipCondition class, and so on). Membership condition classes are contacted by the policy evaluator during the permission granting process.
We need to introduce other concepts before being able examine how they are used in any detail; we encounter more details when we come to look at CAS policies in more detail, later in this chapter.
Application, Domain, and Assembly Evidence
So far we have looked at assembly permissions and evidences. Some CAS permissions are also applicable to application domains as a whole, determining what the application domain is allowed to do (such as creating another application domain). For this reason, the CLR determines and attaches evidences to application domains as well.
When an application domain or an assembly is loaded automatically by the CLR, we have no way to modify the assigned evidence set. However, the CLR exposes methods that enable us to create application domains or load assemblies explicitly. In this case, we are given the opportunity to modify the evidence set (adding new evidences or overriding evidences provided by the CLR).
The BCL provides different overloaded methods to create an Application Domain. If we use the basic method signature, that takes only the domain friendly name, the CLR copies the caller domain evidence to the newly created one:
AppDomain myappdomain = AppDomain.CreateDomainf"myappdomain");
However, other overloaded methods enable us to specify explicitly what evidence must be associated with the new application domain. In the following sample, we override the Zone evidence setting to Internet.
//create |
a new evidence |
set |
object |
initialized with the |
evidence |
values |
of |
|||
//the |
current |
Application |
domain |
|
|
|
|
|||
Evidence |
ev |
= AppDomain.CurrentDomain.Evidence; |
|
|
|
|||||
Zone |
z |
= |
new Zone(SecurityZone.Internet); |
|
|
|
||||
//Add or |
override |
( i f |
yet |
existing) |
the newly created |
evidence |
to the |
evidence |
||
set |
|
|
|
|
|
|
|
|
|
|
ev.AddHost(z); |
|
|
|
|
|
|
|
|
AppDomain myappdomain = AppDomain.CreateDomain{"myappdomain", ev);
316
Code Access Security
Almost the same applies when explicitly loading an assembly into an application domain (using Load, LoadFrom, ExecuteAssembly, and so on). When only the overloaded method that gets the assembly name is used, the CLR loader automatically provides proper evidences to the assembly.
There are other overloaded methods that let us specify the evidence that must be assigned to the loaded assembly. Note that there is a subtle difference here. The evidence provided by the loader is attached to the assembly in any case (this doesn't happen when creating an application domain unless we use the simplest CreateDomain overloaded method, which gets only the assembly friendly name). Assembly evidences that are explicitly specified are added (if they do not already exist), or overriden in the case of those provided by the CLR.
In the following sample, the Zone evidence provided by the CLR (Zone=Internet or Zone=Intranet, since the assembly is loaded from a URL) is overridden with a Zone=MyComputer evidence. These few lines of code have a big impact on CAS Security. The permission the policy evaluator will assign to the assembly will be FullTrust.
Zone z = new Zone(SecurityZone.MyComputer); ev.AddHost(z);
myappdomain.ExecuteAssembly( "http://serverl/cas/ConsoleApplication2.exe", ev);
A host must have been granted a specific permission by the CLR (SecurityPermissionAttribute class with ControlEvidence property set to True) to be able to assign evidences to other application domains or loaded assemblies. Application domains with such permission are commonly referred as trusted domain hosts.
Custom Evidence
The Evidence infrastructure is completely extensible, since any serializable object can be provided as an Evidence entry. We can programmatically provide custom evidences when loading assemblies or application domains explicitly, as we saw above. To have the CLR load a custom evidence automatically, as happens for built-in evidences, we must embed our custom evidence definition as a resource of the assembly using the assembly linker tool (al. exe) specifying the file containing the evidence definition with the / e switch.
Note, however, that a generic serializable object is of no use until we define and register a companion Membership Condition class in our security policy. Membership condition classes are required to implement the IMembershipCondition interface.
We will look in detail later in the section - when we look at security policies - at the steps required to register a custom evidence class in the CAS policy configuration.
Runtime Hosts
Since, at present, the windows operating system has no built-in knowledge of the .NET runtime, a few bits of unmanaged code are required to load and start up the CLR in the process before any .NET code can be run (note that when the CLR is loaded it automatically creates a default application). Any unmanaged code that loads and bootstraps the CLR is called a Runtime host.
317
The .NET Framework comes with three built-in runtime hosts: one to run .NET applications in the Windows shell, a second one that hosts ASP.NET applications, and the last one to let us run .NET applications with IE.
The unmanaged API that loads the CLR runtime is exposed as a COM interface. This means that the .NET Framework enables us to write our custom runtime host in unmanaged code, even in VB6, with few lines of code. To do this, we need to reference mscoree . tlb and ms cor lib. tlb and then write something such as:
clr = new mscoree.CorRuntimeHost,• clr.start;
appdomain = new mscorlib.AppDomain; clr.GetDefaultDomain appdomain; appdomain.ExecuteAssembly_2(<assemblypath>);
If unmanaged code could inject evidences or any security-related settings into a .NET application, this would open a can of worms, making all CAS infrastructure useless. Fortunately this is not the case. Although mscorlib. tlb exposes the Evidence interface, and all other CAS related interfaces, such interfaces have no methods defined, thus making the CLR sealed to unmanaged exploits (there is actually no clear reason why these interfaces are present at all).
Code Access Security Permissions
CAS permissions define what an assembly is or is not allowed to do. The .NET Framework Base Class Library comes with a list of built-in permission classes, one for each resource or service it provides access to. Each assembly of the BCL providing access to a specific resource protects it by putting proper security demands on the corresponding permission object each time the resource is requested. This is done in order to guarantee that all the assemblies in the call stack have been granted the demanded permission by CAS security policies.
This following table shows the built-in permissions in the .NET Framework (the meanings are self explanatory for most of the permissions types).
Permission Classes
System.DirectoryServices.DirectoryServicesPermission
System.NET.DnsPermission
System.EnvironmentPermission
System.Diagnostics.EventLogPermission
System.Security.Permissions.FileDialogPermission
System.Security.Permissions.FilelOPermission
System.Security.Permissions.IsolatedStorageFilePermission
System.Security.Permissions.IsolatedStoragePermission
System.Messaging.MessageQueuePermission
318
As we can see, permission classes provide fine-grained control for different system resources. Some of these permissions map closely to the ones provided by the underlying operating system (FilelOPermission), while others are higher-level permissions that map closely to common application tasks (WebPermission, OleDbPermission, and so on).
All permission classes must implement the IPermission, ISecurityEncodable, IStackWalk, and lUnrestrictedPermission interfaces (all defined in the System. Security namespace). In reality, all built-in permissions objects do not explicitly implement all four interfaces, they inherit from the System. Security .CodeAccesPermission class instead. This class provides an implementation of the first three interfaces so that only the lUnrestrictedPermission interface has to be implemented explicitly. Note, however, that some methods of the CodeAccessPermission class must be overridden by each permission class. This is because their implementation depends on the behavior of specific permission classes. We will examine these methods when we come to look at the custom permission classes.
To provide declarative support via attributes for security demands and requests, each permission class has associated with it an attribute type counterpart class. For instance, the name of the corresponding attribute class for the FilelOPermission class is FilelOPermissionAttribute.
While the behavior of some permission types can be simply expressed with an all or nothing choice, most permissions need to expose more granular and specific semantics to allow effective control of the protected resource (for instance read, write, or append permissions).
To achieve this, each permission class specializes its semantics from the base CodeAccessPermission class by providing different overloaded constructors and specific properties or methods. All or nothing permission types simply expose a constructor that takes a parameter of type PermissionState whose value can be None or Unrestricted.
An example of an all or nothing permission class is the DnsPermission class, which controls rights to access Domain Name Systems (DNS) servers on the network.
[Serializable] public DnsPermission (PermissionState state);
An example of a more complex permission class is the FilelOPermission class. Since this needs to provide more powerful semantics, it exposes both different overloaded constructors and specific methods. These accept as parameters an ad hoc enumerator, specifying what action is granted permission, and another parameter specifying the list of files or directories the permission applies to. Here we show one of the overloaded constructors of this class and one of its specific methods that can be used to set its state:
[Serializable] public FilelOPermission (
FilelOPermissionAccess access, string [] pathList
[Serializable] public void AddPathList
( FilelOPermissionAccess
320
Code Access Security
Note that even complex permission classes must define the basic constructor that gets a permissionsState parameter.
Custom permission classes
.NET built-in permissions are designed to cover most application requirements. There are cases though, where you might need to implement CAS permissions on application-specific resources or services. This can be done thanks to the extensibility of the permission infrastructure.
In the same way as built-in permission classes, a custom permission is a class that inherits from the SecurityPermission class and implements the lUnrestrictedPermission interface.
To implement a specific behavior, a permission class is required to override the following methods of the SecurityPermission base class: Copy, Intersect, IsSubsetOf, FromXml, and ToXml. The implementation of Copy, FromXml, and ToXml is boiler plate code. These must operate in the following ways:
Q The Copy method must return an exact copy of the object instance.
Q The FromXml method is called by the CLR. A SecurityElement (a class implementing a lightweight version of the XML DOM) is passed as a parameter, and is used by the method implementation to set the state of the permission object.
Q The ToXml method is called by the CLR to get the state of the permission class back, in the form of a SecurityElement object.
More interesting are the IsSubsetOf and the Intersect methods. These are constructed in the following way:
public abstract bool
IsSubsetOf( IPermission target );
public abstract IPermission Intersect) IPermission target );
The CLR calls into the SecurityPermission class to process, respectively, Request and Demand calls.
According to the security permission object semantics, these methods must return true or false, comparing the status of the permission object against another instance of the same class that is passed in as a method parameter (see the method signature, above).
The implementation of all or nothing permission objects is straightforward, but can become quite complex, depending on the semantics of the implemented permissions. We will investigate the steps required to register a custom permission class in the CAS policy configuration a little later, when we look at security policies in more detail.
Remember that we need to implement an attribute type counterpart class if declarative use of the class is needed (for instance, to make a security Request). A custom permission class is available, within MyCustomPermission project, with the code download for this book.
321
Identity Permissions
Strictly speaking, identity permissions don't pertain to the CAS model. Nevertheless, we'll discuss them a little here, as they can help to secure an application in conjunction with CAS. The type of security provided by Identity permissions is commonly named Controlled Sharing.
Identity permission objects are created by the CLR and attached to an assembly when it is loaded. They are configured by the CLR in order to match the values of the assembly evidence. The built-in assembly evidences mapped to identity permissions are: Siteldentity, Publisherldentity, StrongNameldentity, Urlldentity and Zoneldentity.
Identity permissions can be used for security requests and demands. Suppose, for instance, that company X wants to make sure that no one else uses functionality implemented in the assemblies it has developed. This is easily done by placing a security demand on the strong name identity permission. Since only Company X holds the public-private key pair required to strongly sign assemblies with the demanded strong name, no one else will be able to place a security demand on the strong name identity permission.
Of course, a strong name identity permission demand is ignored by the CLR if the call is issued by an assembly not signed with the demanded strong name.
To get the result we want, we also need to define such classes as sealed or, more likely, place an inheritance demand, in addition to the strong name identity permission demand, in order to guarantee that such classes are not exploited by inheriting from them (we will look at some security implications of inheritance later in this chapter).
Note that identity permissions do not extend CAS policy permissions but intersect with them, so that they can only restrict but not extend permissions granted by CAS policies. For instance, suppose we have to extend the permissions of an assembly when downloaded from the Internet if it has a particular Strong Name. In such situations, identity permissions is not the way to go. To get the desired result we must appropriately customize CAS polices. We'll look at how to do this in the next section.
Code Access Security Policies
We have now investigated in some detail assembly evidence and CAS permissions. With a solid understanding of these two concepts, we can examine the toughest part of the CAS infrastructure: security policies and the security policies evaluator.
Security policies grant a set of permissions to an assembly, according to its evidence, when it is loaded into an application domain. CAS defines three distinct security policy levels:
Q Enterprise (to be deployed around the enterprise by administrators via msi files using the Group Policy snap-in or System Management Server (SMS)
Q
Machine Q
User
Additionally, CAS allows a trusted host to programmatically define an application-specific policy, and inject it into a newly created application domain. This approach will be used, for example, by the next version of SQL Server, to tighten the security on assemblies that run within the application domain, which is set up by the database runtime host.
322
Code Access Security
Unless specified explicitly, the final permissions granted by the CAS infrastructure to an assembly will be those granted in the intersection of the permissions acquired within each policy evaluation, plus the application domain policy, if defined:
Application
Domain
Machine" |
User |
Allowed |
|
|
Permissions |
Enterprise
The three security policy configurations, for Enterprise, Machine, and User, are stored in XML files:
Enterprise policy is defined in:
<WinDir>\Microsoft.NET\Framework\<version>\config\enterprisesec.config
Machine policy is defined in:
<WinDir>\Microsoft.NET\Framework\<version>\config\security.config
User policy is defined in:
<Documents and Settings Path>\<Username>\Application DataXMicrosoft\CLR Security Config\<version>\security.config
Being XML-based, security policies can be manually edited, but this isn 't recommended, unless you really know what you 're doing.
The BCL provides a set of classes that expose the policy structure as an object model. This object model lets us programmatically access and modify the three policies configuration and create application-specific ones. We'll examine this shortly.
The .NET Framework provides a couple of tools to edit policies. There is a UI, MMC-based, tool called Microsoft .NET Framework Configuration, which is accessible from Start/Settings/Administrative Tools, from which we can manage the three policy levels here: