Chapter 11: Code Access Security (CAS) (Expert WSS 3.0 and MOSS 2007 Programming)

Summary:This article is an excerpt from Expert WSS 3.0 and MOSS 2007 Programming, by Dr. Shahram Khosravi, from Wrox (ISBN 978-0470381373, copyright Wrox 2008, all rights reserved). No part of this chapter may be reproduced, stored in a retrieval system, or transmitted in any form or by any means—electronic, electrostatic, mechanical, photocopying, recording, or otherwise—without the prior written permission of the publisher, except in the case of brief quotations embodied in critical articles or reviews.

Contents

Download the sample code that accompanies this book

Overview

Role-based security works by using the roles in which an authenticated user is to authorize what the user can and cannot access. Such a user- centric authorization mechanism assumes that as long as the user is authorized everything should be working as it was configured. This overlooks the fact that a trustworthy user normally executes pieces of code coming from different sources. These pieces of code execute under the trustworthy user's security context. Some of these pieces of code could be malicious, and others might have bugs that could be used by malicious code to mount an attack. Still others may have bugs that could cause the code to do things that it is not authorized to do. The security problems in these situations do not stem from the fact that the user is untrustworthy or malicious. The security problems arise from the code itself. Code Access Security (CAS) authorizes code, not users, and determines whether a piece of code has the permission to access a protected resource or perform a protected operation. If the code does not have the required permission, the runtime's security system will not allow the code to access the protected resource or perform the protected operation. Another related issue is the fact that users do need to run code that originates from untrustworthy sources. Code Access Security enables administrators to establish a security context or sandbox where code that originates from variety of sources can execute without compromising the security of the system. In other words, CAS is a solution that prevents untrustworthy code from performing privileged actions.

Code Access Security provides the following important benefits among many others:

  • Prevents malicious code from accessing protected resources and performing protected operations

  • Prevents malicious code from using trustworthy code to access protected resources and operations

  • Prevents a bug in trustworthy code from getting unauthorized access to protected resources and operations

  • Enables users to execute code originating from a variety of untrustworthy sources within a security sandbox established by the local security policy

  • Allows you to trust code to varying degrees based on the identity of the code, which also includes the location where the code originated. In other words, the trust is not all or nothing. There are degrees of trust based on the identity of the code. This minimizes the amount of code that you have to fully trust.

  • Minimizes the likelihood that malicious code can take advantage of the vulnerabilities of your trustworthy code because you can explicitly specify not only what permissions your code must be granted to operate properly but also what permission your code should never be granted. This reduces your liability.

As you'll see in this chapter, CAS plays a fundamental role in SharePoint Web part solutions. This chapter provides in-depth coverage of CAS and shows how to take advantage of it in your own code.

What CAS Provides

Code Access Security (CAS) enables you to do the following:

  • Define individual permissions or permission sets for accessing various protected resources and operations.

  • Define code groups where each code group is assigned a specific permission set. Each code group explicitly specifies a code membership condition that the given code must meet in order to be a member of the group. All code members of a code group have the permissions specified in the permission set assigned to the code group.

  • Request the runtime security system to grant your assembly specified permissions or permission sets. Keep in mind that an assembly is the unit of security in the .NET Framework. The runtime grants permission to assemblies, not individual classes, methods, events, or properties in the assembly. As such, security requests are always made at the assembly level. Keep in mind that just because an assembly makes a security request does not mean that the runtime security system will grant the assembly the requested permissions. The runtime security system uses the local security policy to determine whether to grant the assembly the requested permissions.

  • As you'll see later in this chapter, an assembly can make the following three types of security requests:

    • Minimal. An assembly makes a minimal security request to let the runtime know that the assembly cannot operate properly without requested permissions. This enables the runtime to determine whether to load the assembly into memory. The runtime will not load the assembly if the local security policy does not allow the assembly to have the requested permissions. This is a great way to catch problems at load time as opposed to runtime. As such, your Web part assembly should explicitly make minimal security requests for permissions that they have to have to operate properly. This will enable you to catch the security problems at load time.

    • Optional. An assembly makes an optional security request to let the runtime know that the assembly can still operate properly without the requested permissions. The runtime will still load the assembly even if the local security policy does not allow the assembly to have the requested optional permissions. When the assembly finally attempts to access the protected resource or operation for which it does not have the permission, the runtime will throw a security exception. As a best practice, your code must catch these security exceptions and handle them accordingly.

    • Refuse. An assembly makes a refuse security request to let the runtime know that the assembly must never be granted the specified permissions. This enables you to prevent malicious code from taking advantage of the vulnerabilities of your code to access protected resources or operations. It also prevents bugs in your code to cause it to access these resources or operations to do things that it should not do. The runtime will not grant your code the permissions even if the local security policy allows your code to have these permissions.

  • Grant your assemblies the minimum permissions they need to operate properly. As mentioned earlier, an assembly can request the runtime to be granted specified permissions or permission sets. The runtime uses the local security policy to determine whether to grant the assembly the requested permissions. Therefore, if you know that your assembly cannot operate properly without the specified permissions, you can use Code Access Security to add entries for your assembly to the local security policy so your assembly is granted the minimum permissions it needs to operate properly. As you'll see later, the local security policy grants permissions to code groups. In other words, the unit of security at the local security policy level is a code group, not an assembly. That said, you could set the code membership condition of a code group so that the code group only contains a single assembly. As a matter of fact, this is how SharePoint creates the code groups that it needs to add to the local security policy from the solution package. This is discussed further later in this book.

  • Demand the callers of your classes and class members to have specific permissions or permission sets before they can call into your classes and their members. This reduces your liability by preventing less trusted code to use your code to access protected resources or operations for which they are not granted the required permissions. At runtime, when a piece of code attempts to access your code, the runtime security system walks the call stack and compares the granted permissions of each caller in the stack to the demanded permissions. If even one caller in the call stack does not have the demanded permissions, the runtime security system throws a security exception and prevents the caller from accessing your code.

It is very important to realize that every piece of managed code goes through the runtime security system's security checks. It is ultimately the local security policy on the local machine that determines which permissions an assembly is granted. Because different machines are bound to have different local security policies you can never know whether your assembly will be granted the permissions that it needs to operate properly. The same assembly that runs perfect on the development box may fail measurably on the deployment box because these two computers have two different local security policies.

You should not assume that the local security policy will grant the permissions that your assembly needs. As a best practice, you should always target for the minimum trust level. The minimum trust level in WSS is known as WSS_Minimal. You should never assume that the deployment box will have the WSS_Medium trust level, which is the next trust level after the WSS_Minimal trust level. If your assembly must have certain permissions, you should explicitly add entries for your assembly to the local security policy when deploying your assembly.

Security Syntax

As discussed earlier, your code needs to make security requests to be granted specific permissions and to make security demands to its caller to have or not have specific permissions. This means that your code needs to interact with the runtime security system. The .NET Framework provides two types of security syntax that your code can use to interact with the runtime security system, as discussed in the following sections.

Declarative Security Syntax

Every permission or permission set is represented by a security metadata attribute with which you can annotate your assemblies, classes, and class members to make a security request or demand. For example, the following permissions are represented by the SharePointPermissionAttribute metadata attribute:

  • Permission to access the SharePoint object model

  • Permission to impersonate

  • Permission to save to database on Get requests

As you can see, a security metadata attribute could represent more than one permission. A security metadata attribute normally exposes properties. You must set these properties to specify which of the permissions that the security metadata attribute represents you're interested in. For example, the SharePointPermission metadata attribute exposes the following Boolean properties:

  • ObjectModel. Set this property to true to make a security request or demand for accessing the SharePoint object model.

  • Impersonate. Set this property to true to make a security request or demand for impersonation.

  • UnSafeSaveOnGet. Set this property to true to make a security request or demand for saving to database on Get requests.

Every security metadata attribute also takes a SecurityAction enumeration value as its argument. Pass the appropriate SecurityAction enumeration value into a security metadata attribute to specify the type of security call. Listing 11-1 presents the definition of the SecurityAction enumeration type.

Listing 11-1: The SecurityAction enumeration type

[Serializable, ComVisible(true)]
public enum SecurityAction
{
   Assert = 3,
   Demand = 2,
   Deny = 4,
   InheritanceDemand = 7,
   LinkDemand = 6,
   PermitOnly = 5,
   RequestMinimum = 8,
   RequestOptional = 9,
   RequestRefuse = 10
}

Here are the descriptions of these enumeration values:

  • Assert. Use this SecurityAction enumeration value to tell the runtime that if the callers of your code have the permissions represented by the security metadata attribute they should be allowed to access your code regardless of whether callers higher in the call stack have these permissions. This is used in what is known as security overrides, which are rarely used and are not covered in this book.

  • Demand. Use this SecurityAction enumeration value to tell the runtime to walk the call stack to ensure that all callers in the stack have the permissions represented by the security metadata attribute. The runtime will throw an exception and deny access to your code if even one caller in the call stack does not have these permissions.

  • Deny. Use this SecurityAction enumeration value to tell the runtime to deny access to the resources or operations (specified by the security metadata attribute) to the caller even if the caller has been granted the permissions represented by the security metadata attribute.

  • InheritanceDemand. Annotate your class with a security metadata attribute and pass this SecurityAction enumeration value into the attribute to tell the runtime that a class must have the permissions represented by the security metadata attribute to inherit from your class. Or annotate a virtual member of your class with a security metadata attribute and pass this SecurityAction enumeration value into the attribute to tell the runtime that a subclass must have the permissions represented by the security metadata attribute to override this method.

  • LinkDemand. Use this SecurityAction enumeration value to tell the runtime that the immediate caller must have the permissions represented by the security metadata attribute. The main difference between Demand and LinkDemand is that contrary to Demand, LinkDemand does not cause the runtime to walk the call stack.

  • PermitOnly. Use this SecurityAction enumeration value to tell the runtime that only resources and operations specified by this security metadata attribute can be accessed. The runtime will not allow access to any other resources and operations even if the code is granted the permissions to do so.

  • RequestMinimum. Use this SecurityAction enumeration value to tell the runtime that your assembly must be granted the permissions represented by this security metadata attribute to operate properly. The runtime will not load your assembly if the local security policy does not grant your assembly the requested permissions.

  • RequestOptional. Use this SecurityAction enumeration value to tell the runtime that your assembly can still operate properly even if the permissions represented by this security metadata attribute are not granted. The runtime grants your assembly these optional permissions if the local security policy allows it. However, if the local security policy does not allow your assembly to have these optional permissions, the runtime will still load your assembly but it will throw a security exception if your assembly attempts to access the protected resources or operations for which it is not granted the required permissions. As a best practice, your code should catch these security exceptions and handle them accordingly.

  • This SecurityAction enumeration value has a side effect. Because you use this SecurityAction enumeration value to specify optional permissions, the runtime assumes that your assembly does not need other permissions. By using this SecurityAction enumeration value, you're indirectly telling the runtime that your assembly must not be granted permissions that are not specified in this security metadata attribute.

  • RequestRefuse. Use this SecurityAction enumeration value to tell the runtime that your assembly must never be granted the permissions represented by this security metadata attribute even if the local security policy allows your assembly to have these permissions.

You can use the declarative security syntax to make both security requests and security demands.

You can also use this syntax to make security overrides. Security overrides are rarely used and are beyond the scope of this book.

Security requests are always made at the assembly level. Here is an example:

[assembly : SharePointPermission(SecurityAction.RequestMinimum, ObjectModel=true,
Impersonate=true, UnSafeSaveOnGet=true)]

This assembly-level security metadata attribute requests the runtime to grant the assembly the permission to access the SharePoint object model, the permission to impersonate, and the permission to save to the database on the Get requests. Note that the SecurityAction.RequestMinimum enumeration value has been passed into this attribute to tell the runtime that this assembly will not operate properly without these three permissions. This means that the runtime will not load this assembly into memory if the local security policy does not allow the assembly to be granted these three permissions.

Where should assembly-level security metadata attributes go? You have two options. You can add this on top of any source code file in your project. Or you can add it to the AssemblyInfo.cs file of your project. The AssemblyInfo.cs file is the recommended place for assembly-level attributes.

Imperative Security Syntax

Every permission or permission set is represented by a permission class. The imperative security syntax can be used to make security demands and overrides but not requests. Security requests can only be made at the assembly level using assembly-level security metadata attributes.

Because imperative security demands are made within your managed code, the first order of business is to determine where to place this security demand in your code. The runtime will not allow the code following a security demand to execute if the code does not have the permissions that the permission object represents. Therefore, you should insert the security demand right before the code that you want to protect. Here is an example:

public void MyMethod()
{
   ...
   SharePointPermission perm =
           new SharePointPermission(PermissionState.Unrestricted);
   perm.ObjectModel = true;
   perm.Demand();
   SPWeb site = SPContext.Current.Web;
   ...
}

The code that follows the call into the Demand method will be executed only if all the callers in the call stack are granted the permission to access the SharePoint object model. Note that this security demand has no impact on the code that comes before the call into the Demand method. The situation would be different if you were to use the declarative security syntax to annotate MyMethod with the SharePointPermissionAttribute metadata attribute:

   [SharePointPermission(SecurityAction.Demand, ObjectModel=true, Unrestricted=true)]
public void MyMethod()
{
   ...
   SPWeb site = SPContext.Current.Web;
   ...
}

In this case, the whole MyMethod method including the code that comes before the call into the SPContext.Current.Web and the code that comes after it is protected. In other words, only if all the callers in the call stack are granted the permission to access the SharePoint object model would the runtime allow the access to this method. The imperative security syntax allows more granular control over what gets protected. For example, in this case you may not need to protect the code that comes before the SPContext.Current.Web.

These steps need to be taken in order to use the imperative security syntax:

  • Identify the code that you need to protect.

  • Identify the permission class that represents the permissions that you want to demand the callers to have.

  • Instantiate an instance of the permission class right before the code that you want to protect.

  • Set the appropriate properties of this permission object. Recall that a permission object may represent more than one permission. You need to set the appropriate properties of the permission object to specify which permissions your callers must have to access the code.

  • Invoke the Demand method on the permission object. The call into the Demand method causes the runtime security check to occur where the runtime walks the call stack. What happens to the code that follows the call into the Demand method depends on two factors. One factor is whether or not the runtime security check fails. If it does, the runtime will throw an exception. Another factor is whether you have encapsulated the call into the Demand method in a try block and whether you're catching the security exception that the runtime raises. If you do indeed catch the exception and handle it accordingly, the code following the call into the Demand method could run as is.

GAC versus Bin

Any assembly deployed to the Global Assembly Cache (GAC) executes with full trust. Any assembly deployed to the local bin directory of a SharePoint web application executes within the security context (also known as sandbox) established by the local code access security policy. As such, the code that runs from the local bin directory of a web application is only allowed to access resources and perform operations for which it has been granted the required permissions. Follow these best code access security practices in your own applications:

  • Deploy your Web part assemblies to the local bin directory of your web application as opposed to the GAC to ensure that your Web parts only access protected resources and perform protected operations for which they have been granted permissions.

  • If you have one or more Web parts that require more permissions than others, compile these Web parts in their own assembly and grant this assembly higher permissions as opposed to putting all your Web parts in one assembly and granting this big assembly higher permissions. Keep in mind that assembly is the unit of security.

  • If you have an assembly that requires a higher level of trust, you should grant that assembly the permission it needs as opposed to raising the local trust level because raising the local trust level raises the trust level for all assemblies in the bin directory. This will all be clear by the end of this chapter.

  • As you have seen throughout this book, certain SharePoint components such as feature receivers and event receivers must be deployed to the GAC. If your SharePoint solution contains these components plus Web parts, you should compile your Web parts in a separate assembly so you can deploy them to the local bin directory of your web application where you can benefit from the local code access security policy.

  • Your clients should be able to deploy your Web part assemblies to the bin directory of their web applications where the WSS_Minimal trust level is used. If you have a Web part assembly that requires more permissions, you must explicitly grant these permissions through custom code access security settings. This will enable your assembly to run under the WSS_Minimal trust level. Your assembly should not expect the trust level to be higher than WSS_Minimal.

Security Demands

If your code accesses a protected resource or operation and if you're concerned that unauthorized code may use your code to access this protected resource or operation, your code should demand its callers to have or not have a specific permission or permission set to call your code. Note that you could make a security demand for callers not to have a specific permission or set of permissions. For example, if your code reads a file but does not update it and if you're concerned that a caller may use your code to update the file, you can demand the caller not to have the write access to the file to reduce your liability.

A security demand can be applied to:

  • A class to demand that the callers have the specified permissions to perform the following tasks:

    • Instantiate the class.

    • Access the members of the class. You can apply a security demand to a class to protect all members of the class.

    • Inherit from the class.

  • A method in a class to demand the callers to have the specified permissions to perform the following tasks:

    • Call the method (Demand or LinkDemand)

    • Override the method (InheritanceDemand)

  • A property in a class to demand the callers to have the specified permissions to perform the following tasks:

    • Call the getter of the property

    • Call the setter of the property

    • Override the property

    • An event in a class.

Note that you cannot use security demands to protect assemblies or fields in a class. As you can see, if you have a field in a class that you need to protect, you should turn it into a property. If you apply a security demand to a class, it applies to all members of the class. If you apply a security demand to a member of a class, it only applies to that member and will override the security demands applied to the class. Here is an example:

[SharePointPermission(SecurityAction.Demand, ObjectModel=true)]
public class MyClass
{
   [SharePointPermission(SecurityAction.LinkDemand, Impersonate=true)]
   public void MyMethod()
   {
     ...
   }
   public void MyOtherMethod()
   {
     ...
   }
   public string MyProperty
   {
     get { ... }
     set { ... }
   }
}

In this example, all callers of the MyOtherMethod and MyProperty members in the call stack are demanded to have permission to access the SharePoint object model. This does not apply to the callers of the MyMethod method because this method specifies its own security requirements, which overrides the security requirements specified at the class level. MyMethod demands its immediate callers to have the permission to impersonate. It does not demand them to have the permission to access the SharePoint object model.

In general there are three types of security demands that are discussed in the following sections: Demand, LinkDemand, and InheritanceDemand. All security demands cause the runtime to walk the call stack to perform security checks on all callers in the stack. The only exception to this rule is LinkDemand, which causes the runtime to perform a security check only on the immediate caller.

Demand

This type of security demand causes the runtime security system to walk the call stack and check the granted permissions of all callers to ensure that every single caller in the call stack has the demanded permission. This helps prevent luring attacks, which happen when a less-trusted code calls highly trusted code to access a protected resource or operation for which the less-trusted code does not have the demanded permission. Here is an example. Method A calls Method B, which demands its caller to have permission Perm 1. Because Method A has been granted the Perm 1 permission it can access Method B. Now imagine a Method C, which has not been granted the Perm 1 permission, calls Method A, which in turn calls Method B. Because the runtime security system walks the call stack, it finds out that Method C has not been granted the permission. As such, the runtime security system does not allow Method A to call into Method B and raises the SecurityException exception.

There are two ways to make a security demand: declarative and imperative. The declarative approach requires you to take these steps to secure a class, a method in a class, an event in a class, or a property in a class:

  1. Annotate the class, method, event, or property with the permission metadata attribute that represents the permission that you want to demand the callers to have or not to have.

  2. Pass the enumeration value of SecurityAction. Demand as an argument into this permission metadata attribute.

  3. Set the appropriate properties of the permission metadata attribute. A permission metadata attribute may represent a bunch of permissions where each permission is associated with a property. You need to set these properties to specify which permissions you want to demand.

The imperative approach can only be used inside a method or property. It cannot be used on a class or a member as a whole.

Most .NET classes make the appropriate security demands. As such, you should not make the same security demands to protect access to these .NET classes. Doing so would trigger redundant call stack walks. You should make security demands to protect custom resources and operations. For example, the SPWebApplication class is annotated with two instances of the SharePointPermission metadata attribute:

[SharePointPermission(SecurityAction.InheritanceDemand, ObjectModel=true),
SharePointPermission(SecurityAction.LinkDemand, ObjectModel=true)]
public class SPWebApplication : SPPersistedUpgradableObject,
                 IBackupRestore, IMigratable
{
   ...
}

The first permission metadata attribute demands the subclasses of the SPWebApplication class to have the permission to access the SharePoint object model. The second permission metadata attribute demands the immediate callers of this class to have the permission to access the SharePoint object model. If your code accesses the SPWebApplication class, you do not need to make the same security demands. Note that the SPWebApplication class only demands its immediate callers to have the permission to access the SharePoint object model. It does not demand other callers higher in the call stack to have the same permission. If your code is running in the environment where you're concerned about luring attacks where less trusted code may use your code to access the SPWebApplication class, you can make a security demand for all callers in the stack using the SecurityAction.Demand option.

Making a link demand does not cause a call stack walk. The actual security check is performed at the just-in-time compilation phase where the direct links to your code such as method calls are examined to determine whether the direct caller has the specified permissions. If not, a security exception is raised at load time.

Inheritance Demand

An inheritance demand can only be specified declaratively and only applies to classes and nonstatic class members. When an attempt is made to execute a class that inherits from a class protected by an inheritance demand, the runtime performs a security check to determine whether the derived class has been granted the demanded permissions. If not, the runtime will not allow the derived class to run.

Security Requests

As a best practice, your assembly should make security requests for the permissions that it needs. Security requests must be made at the assembly level using the declarative syntax. They cannot be made at the class or class member level. They cannot be made using the imperative approach either. The compiler stores the security requests made through these assembly-level permission metadata attributes into the assembly manifest. At load time, the runtime accesses this metadata and uses the local security policy to determine whether to grant your assembly the requested permissions.

Technically speaking your assembly does not have to make security requests. However, doing so provides the following important benefits among many others:

  • The assembly that makes security requests for the minimum permissions that it needs to operate properly will not run unless it is granted these minimum permissions. If the assembly is running, it has been granted these permissions. This means that you don't have to litter your code with unnecessary security checks to determine whether your code has been granted these permissions. This also means that you don't have to litter your code with unnecessary catch blocks that handle security exceptions because the runtime allows your code to execute only after it is granted these minimum permissions.

  • If the assembly that explicitly requests the permissions that it needs and the permissions that it must not be granted is misused by malicious code or misbehaves due to a bug, it cannot perform unauthorized access to the protected resources and operations because it simply does not have the permissions to do so.

  • The assembly that explicitly requests the permissions that it needs makes it possible for the administrators to add the required entries to the local security policy to grant the assembly these minimum permissions if they choose to do so. Otherwise, there would be no way for administrators to know what permissions the assembly needs to operate properly.

Keep in mind that making a security request has absolutely no effect on whether the runtime will grant the specified permissions to the assembly. It can only make the runtime not to grant permissions. It is only the local security policy that determines whether an assembly should be granted the requested permissions.

In general there are three types of security requests: minimum, optional, and refuse. These security request types are discussed in the following sections.

Minimum

Pass the SecurityAction.RequestMinimum enumeration value into an assembly-level permission metadata attribute to tell the runtime that the assembly needs the permissions that the permission metadata attribute represents to run. The runtime will not run the assembly if the assembly cannot be granted these minimum permissions.

One of the great things about the SecurityAction.RequestMinimum option is that your assembly will still be granted all the permissions that the local security policy allows your assembly to have even though your assembly did not specifically request all of these permissions. In other words, this option does not deprive your assembly from getting all the permissions that the local security policy allows your assembly to have. It can only specify which permissions are required at the minimum.

Here is an example:

[assembly : SharePointPermission(SecurityAction.RequestMinimum, ObjectModel=true)]

Optional

Pass the SecurityAction.RequestOptional enumeration value into an assembly-level permission metadata attribute to tell the runtime that your assembly needs the permissions that the permis sion metadata attribute represents but it can run without them. At load time, the runtime grants these permissions to your assembly if the local security policy allows your assembly to have these permissions. However, the runtime will allow your assembly to run even if your assembly is not granted these optional permissions. When your assembly finally attempts to access the protected resources or operations for which it was not granted the optional permissions, the runtime will throw a security exception. Because your assembly specifies these permissions as optional and consequently tells the runtime to allow your assembly to run even when it is not granted these permissions, you have to use catch blocks in your code to catch the security exceptions that the runtime throws. This is very different from the SecurityAction.RequestMinimum option where you don't need to litter your code with such catch blocks because the runtime will not allow your code to run if it is not granted the requested permissions.

One downside of the SecurityAction.RequestOptional option is that it implicitly tells the runtime not to grant your assembly those permissions that are not explicitly requested even though the local security policy allows your assembly to have those permissions. When you use the SecurityAction .RequestOptional option to specify the optional permissions, you should also use the Security Action.RequestMinimum option to specify the required permissions. Otherwise, your assembly will only be granted the requested optional permissions if the local security policy allows your assembly to have these permissions.

Here is an example of the optional security request:

[assembly : SharePointPermission(SecurityAction.RequestOptional, ObjectModel=true)]

Refuse

Pass the SecurityAction.RequestRefuse enumeration value into an assembly-level permission metadata attribute to tell the runtime that your assembly must not be granted the permissions that the permission metadata attribute represents. The runtime will not grant your assembly these permissions even if the local security policy allows your assembly to have these permissions.

Here is the idea behind the SecurityAction.RequestRefuse option. As discussed earlier, it is the local security policy that determines what permissions an assembly can be granted. As such, your assembly may be granted permissions that it does not need. Malicious code can easily exploit these unnecessary permissions to do damage through your code. As a best practice, you should identify the permissions that your assembly does not need and that malicious code could exploit and use the SecurityAction .RequestRefuse option to tell the runtime that your assembly mustn't be granted these permissions.

Here is an example of the SecurityAction.RequestRefuse option:

[assembly : SharePointPermission(SecurityAction.RequestRefuse, ObjectModel=true)]

So far we've discussed how to use the three types of security requests, that is, minimum, optional, and refuse, to make security requests for individual permissions. Another option is to make these three types of security requests for what is known as named permission sets. As the name suggests, a named permission set is a named set of permissions. The .NET Framework comes with these standard named permission sets: Nothing, Execution, FullTrust, Internet, LocalIntranet, and SkipVerification. You must use the PermissionSetAttribute assembly-level permission metadata attribute to make a security request for a named permission set. Here is an example:

[assembly : PermissionSetAttribute(SecurityAction.RequestMinimum,Name="Execution")]

As you can see, the PermissionSetAttribute permission metadata attribute supports a property named Name that must be set to the name of the named permission set. This property can only be assigned the name of a standard named permission set.

Granting Permissions

The previous two sections discussed security demands and requests. As you saw, an assembly should make security requests for minimum permissions that it needs to run, optional permissions, and permissions that it must never be granted. Security requests for minimum and optional permissions do not influence the runtime in any way to give the assembly the requested permissions. It is the local security policy that determines what permissions can be granted to an assembly. The local security policy is implemented in configuration files known as policy files, which are located in the following folder in the file system of each front-end web server:

Local_Drive:\Program Files\Common Files\microsoft shared\Web Server Extensions\12\CONFIG

Each policy file specifies a set of permissions that each code group can be granted. As the name suggests, a code group specifies a group of code. This enables system administrators to group code based on some grouping criteria and grant each group a specific set of permissions. To help you understand the structure of a policy file, examine wss_minimaltrust.config, which is one of the policy files that comes with a typical WSS installation. This policy file, like any other policy file, is located in the 12\CONFIG folder. Listing 11-2 presents this policy file.

Listing 11-2: The wss_minimaltrust.config policy file

<configuration>
  <mscorlib>
    <security>
      <policy>
        <PolicyLevel version="1">
          <SecurityClasses>
            <SecurityClass
            Name="AllMembershipCondition"
            Description="System.Security.Policy.AllMembershipCondition, mscorlib,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
            <SecurityClass
            Name="AspNetHostingPermission"
            Description="System.Web.AspNetHostingPermission, System,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
            <SecurityClass
            Name="FirstMatchCodeGroup"
            Description="System.Security.Policy.FirstMatchCodeGroup, mscorlib,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
            <SecurityClass
            Name="NamedPermissionSet"
            Description="System.Security.NamedPermissionSet"/>
            <SecurityClass
            Name="SecurityPermission"
            Description="System.Security.Permissions.SecurityPermission, mscorlib,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
            <SecurityClass
            Name="StrongNameMembershipCondition"
            Description="System.Security.Policy.StrongNameMembershipCondition,
mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
            <SecurityClass
            Name="UnionCodeGroup"
            Description="System.Security.Policy.UnionCodeGroup, mscorlib,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
            <SecurityClass
            Name="UrlMembershipCondition"
            Description="System.Security.Policy.UrlMembershipCondition, mscorlib,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
            <SecurityClass
            Name="WebPartPermission"
            Description="Microsoft.SharePoint.Security.WebPartPermission,
Microsoft.SharePoint.Security, Version=12.0.0.0, Culture=neutral,
PublicKeyToken=71e9bce111e9429c"/>
            <SecurityClass
            Name="ZoneMembershipCondition"
            Description="System.Security.Policy.ZoneMembershipCondition, mscorlib,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
          </SecurityClasses>
          <NamedPermissionSets>
            <PermissionSet
            class="NamedPermissionSet"
            version="1"
            Unrestricted="true"
            Name="FullTrust"
            Description="Allows full access to all resources" />
            <PermissionSet
            class="NamedPermissionSet"
            version="1"
            Name="Nothing"
            Description="Denies all resources, including the right to execute" />
            <PermissionSet
            class="NamedPermissionSet"
            version="1"
            Name="SPRestricted">
              <IPermission
              class="AspNetHostingPermission"
              version="1"
              Level="Minimal" />
              <IPermission
              class="SecurityPermission"
              version="1"
              Flags="Execution" />
              <IPermission class="WebPartPermission"
              version="1"
              Connections="True" />
            </PermissionSet>
          </NamedPermissionSets>
          <CodeGroup
          class="FirstMatchCodeGroup"
          version="1"
          PermissionSetName="Nothing">
            <IMembershipCondition
            class="AllMembershipCondition"
            version="1" />
            <CodeGroup
            class="UnionCodeGroup"
            version="1"
            PermissionSetName="FullTrust">
              <IMembershipCondition
              class="UrlMembershipCondition"
              version="1"
              Url="$AppDirUrl$/_app_bin/*" />
            </CodeGroup>
            <CodeGroup
            class="UnionCodeGroup"
            version="1"
            PermissionSetName="SPRestricted">
              <IMembershipCondition
              class="UrlMembershipCondition"
              version="1"
              Url="$AppDirUrl$/*" />
            </CodeGroup>
            <CodeGroup
            class="UnionCodeGroup"
            version="1"
            PermissionSetName="FullTrust">
              <IMembershipCondition
              class="UrlMembershipCondition"
              version="1"
              Url="$CodeGen$/*" />
            </CodeGroup>
            <CodeGroup class="UnionCodeGroup" version="1" PermissionSetName="Nothing">
              <IMembershipCondition
              class="ZoneMembershipCondition"
              version="1"
              Zone="MyComputer" />
              <CodeGroup
              class="UnionCodeGroup"
              version="1"
              PermissionSetName="FullTrust"
              Name="Microsoft_Strong_Name"
              Description="This code group grants code signed with the Microsoft strong name full trust. ">
                <IMembershipCondition
                class="StrongNameMembershipCondition"
                version="1"
PublicKeyBlob="002400000480000094000000060200000024000052534131000400000100010007D1FA5
7C4AED9F0A32E84AA0FAEFD0DE9E8FD6AEC8F87FB03766C834C99921EB23BE79AD9D5DCC1DD9AD23613
2102900B723CF980957FC4E177108FC607774F29E8320E92EA05ECE4E821C0A5EFE8F1645C4C0C93C1A
B99285D622CAA652C1DFAD63D745D6F2DE5F17E5EAF0FC4963D261C8A12436518206DC093344D5AD293"/>
              </CodeGroup>
              <CodeGroup
              class="UnionCodeGroup"
              version="1"
              PermissionSetName="FullTrust"
              Name="Ecma_Strong_Name"
              Description="This code group grants code signed with the ECMA strong name full trust. ">
                <IMembershipCondition
                class="StrongNameMembershipCondition"
                version="1"
                PublicKeyBlob="00000000000000000400000000000000" />
              </CodeGroup>
            </CodeGroup>
          </CodeGroup>
        </PolicyLevel>
      </policy>
    </security>
  </mscorlib>
</configuration>

As you can see from Listing 11-2, the code access security policy is specified in an element named <PolicyLevel>, which contains three child elements named <SecurityClasses>, <NamedPermissionSets>, and <CodeGroup>:

<PolicyLevel version="1">
  <SecurityClasses>
     ...
  </SecurityClasses>
  <NamedPermissionSets>
     ...   </NamedPermissionSets>
  <CodeGroup>
     ...
  </CodeGroup>
</PolicyLevel>

The <SecurityClasses> element is where the security classes are declared. Each security class is declared in a separate <SecurityClass>child element of the <SecurityClasses>element. The <SecurityClass>supports two attributes named Name and Description. The Name attribute provides a friendly name for the security class and the Description attribute contains the fully assembly namespace – qualified name of the security class. The rest of the policy file uses this friendly name to refer to the security class. For example, the following <SecurityClass> declares the AllMembershipCondition security class:

<SecurityClasses>
  <SecurityClass
  Name="AllMembershipCondition"
  Description="System.Security.Policy.AllMembershipCondition, mscorlib,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
     ...
</SecurityClasses>

The <NamedPermissionSets> element is where the permission sets for code groups are defined. Each named permission set is defined in a separate <PermissionSet>element. This element supports the following important attributes:

  • class. This specifies a security class for the permission set. This is basically the friendly name of a security class declared in the <SecurityClasses>element. Set this to NamedPermissionSet because we're defining a named permission set.

  • version. This specifies a version for the named permission set.

  • Name. This specifies a name for the permission set. As you'll see later, the web.config file of a SharePoint web application uses this name to reference the permission set.

  • Description. This specifies a short description for the permission set.

The <PermissionSet>element supports zero or more <IPermission>child elements. Each <IPermission>child element defines a permission. The <IPermission>child element supports the following attributes:

  • class. This specifies the security class that represents the permission. This is the friendly name of a security class declared in the <SecurityClasses>element.

  • version. This specifies a version for the permission.

As Listing 11-2 shows, the wss_minimaltrust.config file defines a permission set named SPRestricted as follows:

<PermissionSet
class="NamedPermissionSet"
version="1"
Name="SPRestricted">

  <IPermission
  class="AspNetHostingPermission"
  version="1"
  Level="Minimal" />

  <IPermission
  class="SecurityPermission"
  version="1"
  Flags="Execution" />

  <IPermission class="WebPartPermission"
  version="1"
  Connections="True" />

</PermissionSet>

As you can see, the SPRestricted permission set contains three permissions, which are represented by the AspNetHostingPermission, SecurityPermission, and WebPartPermission security classes. The Level property on the AspNetHostingPermission security class is set to Minimal to grant the respective code groups the minimal level of ASP.NET hosting permission. The Flags property on the SecurityPermission security class is set to Execution to grant the respective code groups the permission to execute. The Connections property on the WebPartPermission security class is set to True to grant the respective code group the permission for Web part to Web part connections.

As Listing 11-2 shows, the <CodeGroup> element is the root of a hierarchy of code groups. Each code group in this hierarchy is represented by a separate <CodeGroup>element. The <CodeGroup>element supports the following attributes:

  • class. This specifies the security class that determines the type of code group. This is the friendly name of a security class declared in the <SecurityClasses>element.

  • version. This specifies a version for the code group.

  • PermissionSetName. This specifies a permission set for the code group. This is the value of the Name attribute of a <PermissionSet>child element in the <PermissionSets>element. This means that the code group is granted the permissions specified in the permission set.

The <CodeGroup>element supports a single <IMembershipCondition> child element and one or more <CodeGroup>child elements. The <IMembershipCondition>element specifies the condition that given code must meet to be a member of the code group. The permission set whose name is specified in the PermissionSetName attribute is granted to all members of the code group.

As mentioned, code groups form a hierarchy of code groups with a single root code group. The CLR begins with the top of the code group hierarchy. The root code group hierarchy in Listing 11-2 is the FirstMatchCodeGroup code group. Note that this code group uses the AllMembershipCondition membership condition. This condition matches every code group, hence the name FirstMatchCodeGroup. The PermissionSetName attribute on the root code group is set to Nothing, which is the name of the permission set that grants no permissions to the code group, not even the permission to execute. This means that every code group originally has no permissions.

<CodeGroup
class="FirstMatchCodeGroup"
version="1"
PermissionSetName="Nothing">
  <IMembershipCondition
  class="AllMembershipCondition"
  version="1" />

The root code group contains four UnionCodeGroup code groups. The first UnionCodeGroup code group uses the UrlMembershipCondition membership condition with the Url property value of $AppDirUrl$/_app_bin/*. This basically specifies that any code that originates from the _app_bin folder of the web application is a member of this code group. This folder is located in the root directory of each web application, which is normally the following folder in the file system of each front-end web server in the server farm:

Local_Drive:\inetpub\wwwroot\wss\VirtualDirectories\WebApplicationName

The $AppDirUrl$ maps to this root folder. If you check out the _app_bin folder, you'll see that it contains the following assemblies:

  • Microsoft.Office.DocumentManagement.Pages.dll

  • Microsoft.Office.officialfileSoap.dll

  • Microsoft.Office.Policy.Pages.dll

  • Microsoft.Office.SlideLibrarySoap.dll

  • Microsoft.Office.Workflow.Pages.dll

  • Microsoft.Office.WorkflowSoap.dll

  • Microsoft.SharePoint.ApplicationPages.dll

The code that these assemblies contain is a member of this UnionCodeGroup code group. As Listing 11-2 shows, the PermissionSetName attribute on this code group is set to FullTrust. This means that the members of this code group, that is, the preceding assemblies, are fully trusted. The _app_bin folder is for SharePoint infrastructural assemblies. You should not add custom assemblies to this folder.

<CodeGroup
class="UnionCodeGroup"
version="1"
PermissionSetName="FullTrust">
  <IMembershipCondition
  class="UrlMembershipCondition"
  version="1"
  Url="$AppDirUrl$/_app_bin/*" />
</CodeGroup>

The second UnionCodeGroup code group that the root code group contains uses the UrlMembershipCondition membership condition with the URL property value of $AppDirUrl$/*. This basically specifies that any code that originates from the root folder of the web application is a member of this code group. As Listing 11-2 shows, the PermissionSetName attribute on this code group is set to SPRestricted. This means that the members of this code group have the permissions specified in the SPRestricted permission set discussed earlier.

<CodeGroup
class="UnionCodeGroup"
version="1"
PermissionSetName="SPRestricted">
  <IMembershipCondition
  class="UrlMembershipCondition"
  version="1"
  Url="$AppDirUrl$/*" />
</CodeGroup>

Note that the members of the first UnionCodeGroup code group are also the members of the second UnionCodeGroup code group. This means that the members of the first UnionCodeGroup code group have the permissions that the members of the second code group have plus the permissions that their own code group grants them.

The third UnionCodeGroup code group that the root code group contains uses the UrlMembershipCondition membership condition with the URL attribute value of $CodeGen$/*. This basically specifies that any code that originates from this folder is a member of this group. As Listing 11-2 shows, the PermissionSetName attribute on this code group is set to FullTrust. This means that the members of this code group are fully trusted.

<CodeGroup
class="UnionCodeGroup"
version="1"
PermissionSetName="FullTrust">
  <IMembershipCondition
  class="UrlMembershipCondition"
  version="1"
  Url="$CodeGen$/*" />
</CodeGroup>

The fourth UnionCodeGroup code group that the root code group contains uses the ZoneMembershipCondition membership condition with the Zone attribute value of MyComputer. This basically specifies that any code that originates from this zone is a member of this group. As Listing 11-2 shows, the PermissionSetName attribute on this code group is set to Nothing. This means that the members of this code group have no permissions, not even the permission to execute.

<CodeGroup class="UnionCodeGroup" version="1" PermissionSetName="Nothing">
  <IMembershipCondition
  class="ZoneMembershipCondition"
  version="1"
  Zone="MyComputer" />

This code group contains two child code groups. The first child code group is UnionCodeGroup that uses the StrongNameMembershipCondition membership condition with the specified PublicKeyBlob property. This public key blob is basically the public key blob of assemblies signed with the Microsoft strong name.

This basically specifies that any code in any assembly signed with this strong name is a member of this code group. As Listing 11-2 shows, the PermissionSetName attribute on this code group is set to FullTrust. This means that the members of this code group are fully trusted. Keep in mind that the runtime traverses the code group hierarchy from top to bottom. This means that the code that belongs to this code group first passes the membership condition of the parent code group where the code gets no permissions at all (recall that the PermissionSetName attribute on the parent code group is set to Nothing). The code then passes the membership condition of this code group where it is granted full trust:

     <CodeGroup
     class="UnionCodeGroup"
     version="1"
     PermissionSetName="FullTrust"
     Name="Microsoft_Strong_Name"
     Description="This code group grants code signed with the Microsoft strong name full trust. ">
       <IMembershipCondition
       class="StrongNameMembershipCondition"
       version="1"
PublicKeyBlob="002400000480000094000000060200000024000052534131000400000100010007D1FA
57C4AED9F0A32E84AA0FAEFD0DE9E8FD6AEC8F87FB03766C834C99921EB23BE79AD9D5DCC1DD9AD23613
2102900B723CF980957FC4E177108FC607774F29E8320E92EA05ECE4E821C0A5EFE8F1645C4C0C93C1AB
99285D622CAA652C1DFAD63D745D6F2DE5F17E5EAF0FC4963D261C8A12436518206DC09334D5AD293"/>
     </CodeGroup>

The second child code group is a UnionCodeGroup code group that uses the StrongNameMembershipCondition membership condition with the specified PublicKeyBlob property. This public key blob is basically the public key blob of assemblies signed with the ECMA strong name. This basically specifies that any code in any assembly signed with this strong name is a member of this code group. As Listing 11-2 shows, the PermissionSetName attribute on this code group is set to FullTrust. This means that the members of this code group are fully trusted:

     <CodeGroup
     class="UnionCodeGroup"
     version="1"
     PermissionSetName="FullTrust"
     Name="Ecma_Strong_Name"
     Description="This code group grants code signed with the ECMA strong name full trust. ">
       <IMembershipCondition
       class="StrongNameMembershipCondition"
       version="1"
       PublicKeyBlob="00000000000000000400000000000000" />
     </CodeGroup>

As you can see, the CLR walks the code group hierarchy specified in the policy file from top to bottom to determine the code groups to which a given code belongs. Because each code group is granted a specified permission set, the CLR easily figures out the permission set granted to the code. The code can only have the permissions specified in the local policy file. The CLR will not grant the code any more permissions than specified in this file even if the code has requested these extra permissions.

The security policy file is only half the story. The second half is in the web.config file of the web application. As Listing 11-3 shows, this web.config file contains a configuration section named <securityPolicy>, which contains one or more <trustLevel> child elements.

Listing 11-3: The portion of the web.config file

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<configuration>
  <system.web>
    <securityPolicy>
      <trustLevel name="WSS_Medium" policyFile="C:\Program Files\Common Files\
Microsoft Shared\Web Server Extensions\12\config\wss_mediumtrust.config" />
     <trustLevel name="WSS_Minimal" policyFile="C:\Program Files\Common Files\
Microsoft Shared\Web Server Extensions\12\config\wss_minimaltrust.config" />
     <trustLevel name="WSS_Custom" policyFile="C:\Program Files\Common Files\
Microsoft Shared\Web Server Extensions\12\config\wss_custom_wss_mediumtrust
.config" />
    </securityPolicy>
  </system.web>
  <trust level="WSS_Minimal" originUrl="" />
</configuration>

As the name suggests, each <trustLevel> element defines a trust level. The <trustLevel>element supports the following two attributes:

  • name. This specifies a unique name for the trust level.

  • policyFile. This specifies the full folder path to the policy file for the trust level.

As Listing 11-3 shows, the web.config file defines several trust levels. The question is which trust level is used. The answer lies in the <trust>element of the web.config file:

<trust level="WSS_Minimal" originUrl="" />

The <trust> element specifies the trust level that is used. This element supports an attribute named level, which is set to the name of the trust level. By default, this is set to the WSS_Minimal trust level for WSS installations. As Listing 11-3 shows, the wss_minimaltrust.config policy file discussed earlier in this section is the policy file for this trust level.

Following the discussions of this section, you should be able to implement your own custom security policy file to grant the assemblies that contain your custom Web parts the permissions that the custom Web parts need to operate properly. For example, if your custom Web parts need to access the SharePoint object model, the default WSS_Minimal trust level will not permit it. Therefore, you need to take these steps to grant your custom Web parts the required permission:

  • Follow the discussions of this section to implement a policy file that grants your custom Web parts the permission to access the SharePoint object model.

  • Add a new <trustLevel>element to the web.config file of the web application.

  • Pick a unique name for your custom trust level and assign it to the name attribute on this <trustLevel> element.

  • Set the policyFile attribute on this <trustLevel>element to the full folder path to your custom policy file.

  • Assign the name of your custom trust level to the level attribute on the <trust>element.

Because this approach requires you to modify the web.config file, you should use the SPWebConfigModification class in managed code to modify this file as discussed earlier in this book. This ensures that these changes are propagated to:

  • The web.config files of all IIS web sites, which map to the web application, in all existing front-end web servers in the server farm.

  • The web.config files of all IIS web sites, which map to the web application, in all front-end web servers that will be added in the future.

The next chapter discusses the SharePoint deployment, where you learn how to add code access security entries to a solution manifest to have SharePoint automatically create the appropriate policy file for you. This saves you from having to modify the web.config file and create the policy file yourself. As such, you should always add your code access security settings to the solution manifest as opposed to creating the policy file yourself. The discussions of this section should put you in a much better position to understand the structure of the code access security entries that you need to add to a solution manifest.

AllowPartiallyTrustedCallers Attribute

When you strong-name an assembly, .NET automatically places a LinkDemand for FullTrust on every public or protected method on every publicly accessible class in the assembly. This means that the CLR will only allow fully trusted callers to call into the public or protected methods of the publicly accessible classes in the assembly. As discussed earlier, all assemblies in the bin directory of a SharePoint web application execute in partially trusted mode. If any of these assemblies attempts to call into a strong named assembly, the CLR will raise an exception. Because strong-named assemblies are normally installed in the GAC, this protects these shared assemblies from malicious code.

If you want to enable partially trusted code to access your Web part and if you're strong-naming the assembly that contains your Web part, you'll need to add the AllowPartiallyTrustedCallersAttribute assembly-level attribute:

 [assembly: System.Security.AllowPartiallyTrustedCallers()]

This instructs the compiler not to place the LinkDemand for FullTrust on the public or protected methods of the publicly accessible classes in the assembly. Keep in mind that this introduces a security hole in your application because you're allowing partially trusted callers to call into your assembly. If you choose to do so, you must take extra precaution to ensure that your assembly does not contain a code that introduces security risks. Also keep in mind that the AllowPartiallyTrusedCallersAttribute is only applicable when you're strong-naming the assembly. This attribute will have no effect if you're not strong-naming your assembly.

Summary

This chapter provided in-depth coverage of Code Access Security to set the stage for the next chapter, where you learn how to add code access security entries to your solution manifest to grant your Web part assemblies the permissions they need to function properly.

Additional Resources

For more information, see the following resources: