Code Access Security and SharePoint 2007 Web Parts
Code Access Security (CAS) is an important technology for both SharePoint administrators and Web Part developers. Those wishing to deploy Web Parts in their Microsoft SharePoint environment will use it to help maintain a secure SharePoint portal. This is true whether the components to be deployed are developed internally or obtained from a third-party. This article is for IT administrators, SharePoint site architects, and Web Part developers who want to gain a better understanding of how to use CAS in their environments to ensure Web Part security. It will cover the basics of CAS technology and detail how to support CAS in your Web Part deployments.
Background
While CAS has been around since ASP.NET 1.0, it meant very little to developers and administrators until the release of ASP.NET 1.1. This is because, prior to the later version, everything ran with unrestricted permissions under what is known as “full trust”. The concept of role based security was essentially in play as code permissions would defer to the permissions of the user running the program. CAS became truly useful with the advent of ASP.NET 1.1 due to the capability to configure an application to run in a partially trusted environment. Code could then be given specific permissions to control what type of actions it was able to perform and what resources it could access. This was enforced irrespective of the permissions of the user running the program. There have been some minor changes to CAS with the latest releases of the .NET Framework, but for all intensive purposes what held true for v1.1 continues in v2.0+.
Why should I care about this?
The ability to limit the reach of applications through the concept of CAS is a powerful tool for both administrators and developers. Yes, “developers”. That was not a typo. Meeting the demands of defining the appropriate CAS level for an application should originate from the developer - not system or site administrators. Developers are the group that is in the best position to identify the minimum set of permissions that their code will need. Turning one’s code loose on a server and allowing it to run with full trust is not only bad practice, it also robs a developer of some important benefits. Let’s say an issue arises indicating a corrupt system registry on a server that your Web Part was deployed to earlier in the week. With a custom CAS policy in place, you can quickly point out how your Web Part is configured to run without permissions to access the registry or write to the file system. This can save you from the uncomfortable and sometimes impossible task of explaining to IT why you believe your Web Part was not at fault. Simply point them to the CAS permissions definition that your code runs under. Developers can also benefit from CAS by using it as a mechanism to find bugs. Turning on or off specific permissions can help identify where a problem is occurring. The benefits of CAS for the enterprise and its system and SharePoint site administrators are numerous. It is an absolute must for those wishing to safely run code from a third-party on their servers. Internal developers can also do strange things in their code too, and although malicious intent may not be there, buggy code can also create problems. CAS ensures that code can only do certain things and can prevent a process from being usurped by another process that may have a more wicked intent.
Trust Levels
There are five default ASP.NET trust levels defined in the .NET Framework: Full, High, Medium, Low, and Minimal. As mentioned above, the Full trust level grants applications unrestricted code access permissions. Web applications are given full trust by default, but these permissions can be customized to establish the application in a partial trust mode. Microsoft SharePoint introduced two new trust levels named WSS_Medium and WSS_Minimal. They are extensions of the ASP.NET trust levels Medium and Minimal respectively. New virtual servers extended with SharePoint are given a default trust level of WSS_Minimal, thus making SharePoint a partial trust application by default. How do the SharePoint default trust levels differ from their ASP.NET counterparts? Before we answer that question, let’s take a quick look at where trust levels are configured.
Each trust level maps to a specific XML policy file that lists the set of permissions and code groups defined for the named trust level. The default policy files are located in %windir%/Microsoft.NET/ Framework/<version>/CONFIG. SharePoint-specific policy files are located in the %Program Files%/Common Files/Microsoft Shared/web server extensions/12/CONFIG directory for SharePoint 2007 (WSS v3.0 / MOSS).
The trust element of the machine.config or the web.config files controls whether CAS is enabled for a Web application. The level attribute of the trust element specifies a named trustLevel that is defined in the securityPolicy section. Each trustLevel element contains a policyFile attribute pointing to the policy file associated with that named trust level. The default policy files can be copied and edited to create new custom policies that are targeted to give applications the specific permissions they need to run.
<system.web>
<securityPolicy>
<trustLevel name="WSS_Medium" policyFile="C:/Program Files/Common Files/Microsoft Shared/Web Server Extensions/60/config/wss_mediumtrust.config" />
<trustLevel name="WSS_Minimal" policyFile="C:/Program Files/Common Files/Microsoft Shared/1Web Server Extentions/60/config/wss_minimaltrust.config" />
</securityPolicy>
<trust level="WSS_Minimal" originUrl=””/>
</system.web>
So back to the initial question that started this discourse on policy files. How do the SharePoint trust levels differ from the default ASP.NET levels? Well, the WSS_Minimal level extends the default ASP.NET Minimal by adding the permission to allow Web Part connections to be made. The WSS_Medium level inherits all the rights of its ASP.NET counterpart with the addition that both Web Part connections and SharePoint Object Model permissions are granted. I could spend the entire article diving into the specific permissions that can be granted through a policy file, but since this topic has been covered well by other sources (see the references links at the end of this article) we will leave with the basics outlined and talk about the options for configuring trust in a SharePoint environment.
A mentioned above, a default trust level of WSS_Minimal is applied to all new virtual servers extended by SharePoint. Any Web Parts that are deployed to a virtual server directory are affected by the trust level set in the web.config file for that virtual server. As a result, any Web Parts deployed to this directory operate with significant limitations. The .NET Common Language Runtime (CLR) will throw an exception whenever the Web Part you are installing attempts to access an unauthorized resource. When a Web Part fails to run due to a security issue, you will find a message like this in the server event log file:
In order to deploy Web Parts that are able to access resources that require a higher trust level, you have three options: Raise the trust level for the entire virtual server, Deploy your Web Parts to the Global Assembly Cache (GAC), or Create your own custom policy.
Web Part Deployment Options
The first option, raising the trust level for the entire virtual server, is very easy to implement. As detailed above, simply edit the web.config file for the virtual server and change the trust level from its WSS_Minimal default to another named trust level and perform an IIS Reset. The biggest drawback of this method is that the trust level setting made at this level is put into effect for all assemblies running in the /bin for that virtual server. If you wish to deploy your Web Part in an environment that you do not control, then it is quite possible that the virtual server trust level will be adjusted at some point and your component will stop working.
Installing your Web Part into the GAC is another option. The GAC resides in the /windows/assembly directory on the server, and any assemblies that are placed in this directory are available to all virtual servers and applications running WSS. Assemblies in the GAC will run at a Full trust level. In addition, assemblies residing in the GAC are not affected by the trust level of the assemblies installed in the virtual server /inetpub/wwwroot/wss/VirtualDirectories/<port number> directory. Routine GAC installations are not an appropriate option in a production environment. It is less secure because it potentially grants a higher level of permission to your assemblies across a larger scope than might be necessary.
The final and recommended option is to create a custom policy for your Web Part. Your assembly will then run with a targeted set of permissions, defined in the policy, which meets its requirements. By providing a custom policy with your component, you can ensure that your code will run regardless of the trust level configured at the virtual server level. It will be limited only by the logged in user’s permissions. Systems administrators can also be assured your Web Part will only access the resources and capabilities defined in the security policy. We will highlight how to define a custom CAS policy in the sample that follows.
A Simple Web Part Deployment Sample
The good news is that if you are a Web Part developer wondering where the meat of this article is as it relates to you, you have reached that point now. In this section, we will run through the creation of two Web Parts. The first project is a simple Web Part that displays a greeting to the logged in user. The second one we create will display special text to the user informing them of their SharePoint rights. We will retrieve these rights using the SharePoint Object Model and throughout the sample show how to use CAS.
All project samples are contained in the zip file referenced by the article. The project source code was created with Visual Studio 2005 using the Web Part Project template. Consult the readme.txt file contained in the archive for additional instructions.
First we start by creating two Web Part classes; here is the code listing:
marginwidth="3" marginheight="3" src="http://store.bamboosolutions.com/kb/attachments/CASArticle.Listing1.htm" frameborder="0" width="800" height="200">
Compile these sample classes into an assembly named “Bamboo.WebParts.Samples.dll “. Our next step is to deploy our Web Parts using the stsadm.exe utility. To do this we need to build a cabinet file with the .wsp extension that includes: manifest.xml, a web part definitions xml file (.webpart), and our sample assembly dll.
The sample Manifest.xml we will use in our example is:
<Solution xmlns="http://schemas.microsoft.com/sharepoint/" SolutionId="F11A6018-158F-42e0-B1D7-BD1118DD4506">
<Assemblies>
<Assembly Location="Bamboo.WebParts.Samples.dll" DeploymentTarget="WebApplication">
<SafeControls>
<SafeControl
Assembly="Bamboo.WebParts.Samples,Version=1.0.0.0,Culture=neutral,
PublicKeyToken=9f4da00116c38ec5" Namespace="BambooDemo" TypeName="*" Safe="True" />
</SafeControls>
</Assembly>
</Assemblies>
<DwpFiles>
<DwpFile Location="Simple.webpart"/>
<DwpFile Location="SimpleCAS.webpart"/>
</DwpFiles>
</Solution>
Finally, we deploy our web parts using stsadm.exe utility on SharePoint site running Windows SharePoint Services v3.0 with minimum trust (wss_minimal). After deployment with stsadm.exe, add the Web Parts to a page.
The first Web Part shows a nice greeting message:
The second Web Part on the other hand displays an error message as soon as we add it to a page:
Error: Request for the permission of type 'Microsoft.SharePoint.Security.SharePointPermission, Microsoft.SharePoint.Security, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' failed.
The default trust level on our test site is set to wss_minimal which does not grant us permissions to use the SharePoint Object Model from the code.
There are three different ways to get over this permission problem: we can add our assembly to the GAC to run under full trust, change our web.config and set the trust level to Full, or create a custom trust level by modifying wss_minimaltrust.config with just enough trust to get our code working. For all the reasons mentioned above in the prior section, Web Part Deployment Options, the third option is the recommended approach we will take to resolve our problem.
They are two ways to create custom trust config file. We can either edit an existing config file directly or include the CAS permissions information in our deployment. The second method is the easier one and enables the config file to be updated automatically. It also eliminates potential manual entry errors that might crash our site. Let’s return to our manifest.xml file and include a new section for CodeAccessSecurity permissions. The updated manifest is listed below:
marginwidth="3" marginheight="3" src="http://store.bamboosolutions.com/kb/attachments/CASArticle.Listing2.htm" frameborder="0" width="800" height="200">
Save the new manifest.xml, rebuild the project to create a new wsp file, and then redeploy the Web Part using stsadm.exe and the – allowCASPolicies option. This option was not available in earlier releases of SharePoint. The new Code Access Security section will cause the creation of the “NamedPermissionSet” class which includes the permissions that our second Web Part needs to run successfully. Now when we run our sample page the second Web Part shows the following:
In the screen shot, I am logged in as Admin so I see text letting me know I have access to both the Site Admin Area and Web Admin Area.
Determining CAS Permissions
How do you determine what permissions your assembly needs to run properly? Your first source of information should be the MSDN documentation. In a perfect world, it will clearly state what permission a specific method demands. Unfortunately, based on our experience, you will find that this is not always the case. The online documentation is getting better in regards to CAS, but it’s not covered 100%. For now, let’s assume we were very lucky and could find all the permission listings we needed for our code in the documentation. Once you know what permissions are required, the next task is to come up with the XML representation of them so that they can be defined in the CodeAccessSecurity section of the manifext.xml.
The IPermission interface is the serialized representation of the permission so you can build the Permission in code and then call ToXml on the permission to get the xml. Below is an example of how to get the xml representation of the SecurityPermission permission with the flags allowing UnmanagedCode, ControlPrincipal, and ControlThread.
SecurityPermissionFlag flags = SecurityPermissionFlag.UnmanagedCode |
SecurityPermissionFlag.ControlPrincipal |
SecurityPermissionFlag.ControlThread;
SecurityPermission sp = new SecurityPermission( flags );
Console.WriteLine( sp.ToXml() );
The above code will provide you with the needed CAS xml:
<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlThread, ControlPrincipal" />
That is our tidy scenario where we are able to find the permissions our code needs in the documentation. Based on our real-world experience, the process is actually much dirtier and involves a trial and error approach. We do this by deploying the assembly and then testing it to catch all security demands. In .NET v1.1 this was a pretty painful experience since the exception information was very limited once you caught the SecurityException that resulted. The good news is that it’s much easier in .NET v2.0. The SecurityException is very rich and carries a lot of additional information about the demand that failed. The new SecurityException adds several more properties, which together allow developers to figure out what code was causing a security problem. The new properties are:
Name | Type | Description |
Action | SecurityAction | the SecurityAction that failed the security check |
Demanded | object | the permission, permission set, or permission sets that were demanded and triggered the exception |
DenySetInstance | object | if a Deny stack frame caused the security exception to fail, then this property will contain that set, otherwise it will be null. |
FailedAssemblyInfo | AssemblyName | AssemblyName of the assembly that caused the security check to fail |
FirstPermissionThatFailed | IPermission | the first permission in failing PermissionSet (or PermissionSetCollection) that did not pass the security check |
Method | MethodInfo | the method that the failed assembly was in when it encountered the security check that triggered the exception. If a PermitOnly or Deny stack frame failed, this will contain the method that put the PermitOnly or Deny frame on the stack. |
PermitOnlySetInstance | object | if the stack frame that caused the security exception had a PermitOnly permission set, this property will contain it, otherwise it will be null |
Url | string | URL of the assembly that failed the security check |
Zone | SecurityZone | Zone of the assembly that failed the security check |
To obtain this rich exception information from your tests, you need the following SecurityPermissions: ControlPolicy and ControlEvidence. Simply grant the assembly you are testing these permissions. If the security exception goes away, then you know it was one of those permissions you needed. Otherwise, you will now have a very rich exception that you can examine to see exactly what was demanded.
try
{
// Code that throws
}
catch( System.Security.SecurityException x )
{
System.Diagnostics.Trace.WriteLine( x.ToString() );
throw;
}
The output looks like this:
marginwidth="3" marginheight="3" src="http://store.bamboosolutions.com/kb/attachments/CASArticle.Listing3.htm" frameborder="0" width="800" height="200">
As you can see, you get the exact IPermission node that you have to add to your CAS policy:
<IPermission class="System.Net.WebPermission, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1">
<ConnectAccess>
<URI uri="http://xxxx/yyyy/.asmx"/>
</ConnectAccess>
</IPermission>
After adding this permission, you will need to run the code again until you have found all the permissions that your code needs. The enhancements in .NET v2.0 make it a whole lot easier than it was before.
About the Authors
Wes Bryan is a Senior Product Manager for Bamboo Solutions, a provider of SharePoint Web Parts and custom SharePoint development services. Two members of Bamboo’s engineering staff, Jonas Nilsson and Muhammad Piracha , also provided content for this article. Wes, Jonas, and Muhammad have worked together at Bamboo for over 7 years. Much of the SharePoint experience they gather comes from their shared experiences with customers implementing portal solutions and Web Parts. Wes can be reached via email.
References
Find Out What's New with Code Access Security in the .NET Framework 2.0
http://msdn.microsoft.com/msdnmag/issues/05/11/CodeAccessSecurity/default.aspx
.NET Security Blog - Whidbey's New SecurityException
http://blogs.msdn.com/shawnfa/archive/2004/07/30/202468.aspx
Microsoft Windows SharePoint Services and Code Access Security:
Using Wppackager to Package and Deploy Web Parts for Microsoft SharePoint Products and Technologies:
Code Access Security policies within SharePoint V3:
http://www.bluedoglimited.com/SharePointThoughts/ViewPost.aspx?ID=249
Code Access Security (CAS) and Design Patterns:
http://www.codeproject.com/gen/design/CASDesignPatterns.asp
Why is Code Access Security important?
http://www.bluedoglimited.com/SharePointThoughts/ViewPost.aspx?id=99
Code Access Security: When Role-based Security Isn’t Enough
http://www.devx.com/security/Article/31259/0/page/1