SharePoint Security and .NET Impersonation

By Jay Nathan


Microsoft Windows SharePoint Services and SharePoint Portal Server 2003 have become very popular in recent years and have helped many businesses create an infrastructure for sharing and collaborating internal information. Out of the box, SharePoint offers an abundance of functionality that spans many vertical markets and divisions within an organization. From a development perspective, the SharePoint Services platform provides an IT organization the unique opportunity to focus almost exclusively on providing specific domain logic and value-added functionality to the businesses they support. During the process of extending SharePoint it is imperative to understand the SharePoint security model. Several techniques can be applied that will provide the optimum combination of ease of development, deployment, functionality and security. This article explains the fundamentals of SharePoint security under IIS’s Windows Authentication mode (SharePoint’s Domain Account mode). Custom Web parts will be used to demonstrate SharePoint’s security infrastructure, and techniques will be taught for ensuring that an implementation is as secure as possible while providing a clean and consistent user experience.



SharePoint Authentication


To authenticate its users, SharePoint has the ability to run under any of the modes that are available in IIS. These include Anonymous, Basic, Integrated Windows or Certificates Authentication (over SSL). This article assumes that SharePoint will be running under Integrated Windows Authentication mode, which is common for internal scenarios where SharePoint is configured for the domain user account mode. Since SharePoint is built upon ASP.NET, it is configured in much the same way as a normal ASP.NET application. The following XML element which defines the authentication mode for a SharePoint installation can be found in the web.config file in the root of a SharePoint installation (c:\inetpub\wwwroot on my server):


<authentication mode=”Windows” />

The <authentication> configuration element tells ASP.NET which mechanism should be used to authenticate users to the site. It is important to note that in addition to this piece of ASP.NET configuration, the SharePoint IIS Web application (Default Web Site on my server) is also configured to authenticate via Windows. The available authentication modes in ASP.NET are Windows, Forms, Passport or None and should not be confused with the authentication modes of IIS.




Figure 1: IIS 6.0 Authentication Methods dialog box for the SharePoint web application


Authorization


Perhaps the most obvious use of security in SharePoint is to regulate the actions and access rights of individual users and groups of users. Integrated Windows Authentication provides a high level of security while maintaining an authentication and authorization structure that lives in harmony with a typical Windows domain.


Individual domain users and groups can be given access rights to SharePoint sites and resources by being added to SharePoint site groups. A SharePoint site group defines a role such as “Readers”, which allows read-only access to a given site. In addition to Readers, there are several more site groups built into SharePoint including Contributors, Web Designers and Administrators. By adding domain users and groups to SharePoint site groups, the authorization structure of SharePoint security begins to take shape. It is also possible to create custom site groups to encapsulate different combinations of permissions.


As an example of typical authorization structure, consider the following: I create one domain group for my Accounting department and assign it to both the Readers and Contributors site groups on the SharePoint Accounting Web site. I do the same thing for my HR department and its SharePoint site, but I only add my HR domain group to the Readers site group of the Accounting Web site. This allows me to implicitly control membership to SharePoint content based on membership in divisional domain groups. HR has read-only access to content on the Accounting site and Account has read-only access to the HR site. Security can get much more complex than this, but this demonstrates the capabilities of SharePoint authorization as it relates to the Windows domain.


Impersonation



To make Windows security integration possible, SharePoint utilizes .NET impersonation. .NET Impersonation allows an application to run under the context of the client accessing an application. With ASP.NET impersonation, IIS is responsible for authenticating users against the domain and passing to ASP.NET an authenticated token, which can then act on behalf of the client. ASP.NET impersonation can be defined implicitly through configuration settings, or, as covered later in this article, explicitly through code. A close look at the web.config file for a SharePoint site reveals the implicit impersonation configuration for a SharePoint installation running under Integrated Windows Authentication mode:

<identity impersonate=”true” />


This setting instructs ASP.NET to implicitly act on the behalf of the client who is accessing SharePoint.


The Problem



The time will come during a SharePoint customization project where a developer will want .NET code to perform some action that all users of the SharePoint site do not have the permission to perform. For example, accessing and displaying data from lists on other SharePoint sites and virtual servers (through the SharePoint object model), accessing Active Directory, or pushing files to a non-public shared drive. A developer may even need to access data from a SQL Server instance that requires Windows Authentication. Some users will possess the necessary permissions to perform these operations while others won’t, and we certainly do not want to grant these permissions to everyone.


In all cases where the client does not possess the proper permissions to perform any of the actions listed above, SharePoint will cause Internet Explorer to prompt the user for login credentials. To avoid this usability interruption and facilitate smooth access to privileged operations and resources, a custom impersonation solution must be implemented. The code performing these actions must be run under the context of an account that has permission to perform the necessary tasks. After the operations are performed, the security principal should revert back to the client’s domain account which SharePoint was originally impersonating.



Figure 2 – Internet Explorer’s network login dialog box


The Impersonator Class



There are several steps involved with performing explicit impersonation, which I have wrapped up into a class called Impersonator (download below). The steps for impersonation are as follows:



  1. Authenticate a valid account which possesses the proper permissions to perform the necessary operations
  2. Create a new System.Security.Principal.WindowsIdentity instance that represents the account

  3. Begin impersonating the new Windows identity
  4. Perform actions that require higher permission level
  5. Stop impersonating and revert back to the client’s identity


The goal of wrapping these steps into one class is to write the following simple code to begin impersonation, execute code under the context of a different domain user account, and revert back to the original security context of the client:


Impersonator i = new Impersonator(“SharePointRead”, “MARINER”,
“password123”).Impersonate();

// Code requiring higher permissions…

i.Undo();


Note: Instead of hard-coding the account credentials, they should probably be stored in the SharePoint’s web.config file or a SQL Server table.


The first step is to authenticate the user account that has permission to perform the necessary operation. One way of doing this is by utilizing the Win32 LogonUser API call. To make a Win32 call from ASP.NET, we can make use of P/Inoke with the following method declaration:


[DllImport(“advapi32.dll”, SetLastError=true)]
private static extern bool LogonUser(string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);


The LogonUser API function attempts to authenticate an account to a Windows domain with the security credentials that are passed to it. Notice that in the DllImport attribute declaration we are specifying the name of the library in which the function exists and are setting the SetLastError property value equal to true. If the logon is successful LogonUser returns true and also passes a token handle back to the caller that represents the authenticated user to impersonate. This handle is stored in an IntPtr object when returned to a .NET application. If the operation fails, LogonUser returns false and the error code can be retrieved with the following line of code:

int errorCode = Marshal.GetLastWin32Error();

Marshal.GetLastWin32Error() is a static method that retrieves the last error thrown by an unmanaged P/Invoke call whose SetLastError value is set to true in the DllImport statement.


Now that we have a token representing our authenticated user, an instance of WindowsIdentity can be created. WindowsIdentity implements the System.Security.Principal.IIdentity interface and is used to represent a Windows user in a .NET application. A WindowsIdentity instance representing the impersonation principal that is needed can be created by simply passing the token handle that was received from LogonUser to the WindowsIdentity constructor:


WindowsIdentity winIdentity = new WindowsIdentity(handle);

The Impersonator class has a public method called Impersonate() which wraps a call to the WindowsIdentity.Impersonate() method. WindowsIdentity.Impersonate() returns a System.Security.Principal.WindowsImpersonationContext instance, which is stored as a private member of the Impersonator class. The WindowsImpersonationContext instance represents a Windows user before impersonation begins and allows us to stop impersonation by calling its Undo() method. The Impersonator.Undo() method wraps the call to WindowsImpersonationContext.Undo(). Consider the following code extract from the Impersonator class:

private WindowsImpersonationContext impersonationContext = null;

public void Impersonate()
{
//Logon() returns the WindowsImpersonationContext instance needed
this.impersonationContext = this.Logon().Impersonate();
}

public void Undo()
{
this.impersonationContext.Undo();
}


Please refer to the accompanying C# files for the complete Impersonator class source code and an example of its use.

Deployment and Code Access Security



.NET Code Access Security (CAS) provides a security model that restricts the operations that can be performed and resources that can be accessed by managed code. The security rules imposed by CAS are applied based on a combination of facts about the origin of code and its runtime environment, not the account under which the code is executing. The full extent of CAS is well beyond the scope of this article, but do note that in order for managed code to make unmanaged calls though P/Invoke and manipulate the principal security object within ASP.NET, as the Impersonator class does, SharePoint’s security policy must be altered a bit.


For the sake of this article, let’s assume that we need to deploy a Web part that utilizes the Impersonator class. Our code is compiled into an assembly named WebParts.dll, and it will be deployed to the bin directory of the root SharePoint installation (c:\inetpub\wwwroot\bin on my server). After deploying the Web part via stsadm.exe several changes need to be made to the file that defines the security context for Windows SharePoint Services. The default policy file, wss_minimaltrust.config, for WSS can be found in C:\Program Files\Common Files\Microsoft Shared\web server extensions\60\CONFIG. You will want to make a copy of this file and call it wss_customtrust.config.


To enable the use of unmanaged code and to exercise control over the ASP.NET security principal, the following changes will have to be made to the SharePoint security policy:



  1. Add appropriate SecurityClass references to the <SecurityClasses> element of the policy file

  2. Create a custom permission set to which our Web part assembly will belong
  3. Apply the custom permission set to our assembly by creating custom code group and assigning our assembly to it
  4. Update WSS’s web.config file to point at the new security policy


The security permission classes required for our Impersonator code to run under ASP.NET are EnvironmentPermission and SecurityPermission. SecurityPermission is referenced in the policy file by default, so the only reference we need to add is for the EnvironmentPermission. The following XML fragment should be added somewhere within the <SecurityClasses> element of wss_customtrust.config:


<SecurityClass Name=”EnvironmentPermission”
Description=”System.Security.Permissions.EnvironmentPermission, mscorlib,
Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″/>

Next, a new permission set must be added to the custom policy file. This permission set defines a group of permissions that will eventually allow our Web part’s code to perform COM interoperability and manipulate the application’s security principal object. For these two security privileges, we add the UnmanagedCode and ControlPrincipal flags to the SecurityPermission definition and set the Unrestricted property of the EnvironmentPermission class equal to true:


<PermissionSet
class=”NamedPermissionSet”
version=”1″
Name=”WebPartCustomTrust”>
<IPermission
class=”SecurityPermission”
version=”1″
Flags=”Execution,UnmanagedCode,ControlPrincipal”
/>
<IPermission
class=”EnvironmentPermission”
version=”1″
Unrestricted=”true”
/>
</PermissionSet>

Place this PermissionSet definition as a child under the <NamedPermissionSets> element in the wss_customtrust.config policy file.


Now we can create a code group called WebPartCustomTrust to which the permission set above will be applied. To determine which assemblies will belong this code group, the UrlMembershipCondition is used. In our case membership will be granted exclusively to the Mariner.WebParts.dll assembly (look at the Url attribute). The following snippet should be added to the policy file as a child of the first <CodeGroup> element (code groups are hierarchical).


<CodeGroup class=”UnionCodeGroup”
version=”1″
PermissionSetName=”WebPartCustomTrust”>
<IMembershipCondition
class=”UrlMembershipCondition”
version=”1″
Url=”$AppDirUrl$/bin/Mariner.WebParts.dll” />
</CodeGroup>

Save the changes to wss_customtrust.config.


Finally, two small changes need to be made to WSS’s web.config file to point it at the new security policy. Under the <SecurityPolicy> element, a new <trustLevel> element must be configured to take advantage of the policy file that we created. The new <trustLevel> element creates a reference to wss_customtrust.config and will looks like this:


<trustLevel name=”WSS_Custom” policyFile=”C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\60\config\wss_customtrust.config” />


In the <trust> element of the web.config file, change the level attribute to “WSS_Custom”, thereby pointing it at the policy reference we just created. Save the web.config file.


<trust level=”WSS_Custom” originUrl=”” />

At this point all of the necessary configuration pieces are in place to impersonate a high-privileged user in a very secure way in SharePoint. Running iisreset from the command line will ensure that the new policy is applied to the SharePoint application.


Conclusion


SharePoint and ASP.NET take advantage of .NET Impersonation to manage resource access and operations privileges. When implementing custom components that require access to restricted resources, implicit impersonation must be used. By wrapping all of the steps in to the easy-to-use Impersonator class, impersonation can be accomplished in a tidy manner within code. The final step is to properly configure the SharePoint application for secure access to the extended functionality that can be found in ASP.NET. When you develop components for SharePoint remember that it is simply a robust ASP.NET application. SharePoint’s is easily extensible as long as an understanding of its architecture exists.


References

CloseHandle (kernel32.dll):

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/closehandle.asp


ASP.NET Impersonation:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconaspnetimpersonation.asp


Managing Users and Cross-site Groups:

http://www.microsoft.com/resources/documentation/wss/2/all/adminguide/en-us/stsf02.mspx


SecurityPermissionFlag Enumeration:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemsecuritypermissionssecuritypermissionflagclasstopic.asp


EnvironmentPermission Class

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemSecurityPermissionsEnvironmentPermissionClassTopic.asp


Packaging and Deploying Web Parts for Microsoft Windows SharePoint Services

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odc_sp2003_ta/html/sharepoint_deployingwebparts.asp


About the Author



Jay Nathan is a Senior Consultant at Mariner, a business intelligence consulting firm headquartered in Charlotte, NC. He is a Microsoft Certified Professional and has been a Consultant, Solutions Architect and Project Team Lead delivering solutions to the power generation, telecommunications and healthcare industries. Jay’s areas of expertise lie in the Microsoft .NET technologies, SQL Server, and BizTalk Server and are augmented by experience in database design, ETL and data conversion, collaborative, and reporting solutions development. Jay is currently working with Microsoft tools such as SharePoint Portal Services, Reporting Services, Analysis Services and Microsoft CRM. He can be reached at jnathan@mariner-usa.com or visit his blog.

About the Company



Mariner provides specialized technology products and services that help its clients increase productivity and improve decision making by applying software engineering best practices and principles to dynamic, business intelligence, data warehousing and collaborative solutions. Mariner, a certified Microsoft Gold Partner and a Business Objects Gold Partner, serves a client list that includes Lash Group, National Gypsum, US Bioservices, Transamerica, Blue Cross Blue Shield of NC, Siemens, Bank of America, Mecklenburg County and Russell Corporation. Charlotte, NC-based Mariner Helps Companies Realize their Visions with Technology. www.mariner-usa.com.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read