Sharepoint Single Sign-On, Impersonation and the Double-Hop Problem

How do you overcome the identity double hop problem?

Windows credentials can only make one “hop” between machines on a network. The first hop is from the user’s browser to the web server; from here, to get to another machine on your network, a second hop is required.

There are two ways to work around this problem: 1) establish a delegation relationship between the web server and the other network machine, and configure the AD domain to allow Kerberos Protocol Transition, or 2) use the Win32 LogonUser API to switch to the user’s identity on the web server before making that single hop out to the other network machine.

Sharepoint Single Sign-On uses the second approach. The first approach is complex and probably akin to using a sledge hammer to crack a nut.

The great thing about the Sharepoint SSO service is that when creating Enterprise Application Definition’s, you can decide what credential fields are stored, so you can store, obviously, User names and Passwords, DB connection strings, Domain names and, well, other stuff you can put in a string.

So, as an example, a web part needs to collect information from a network machine to display in it’s UI to a user. You have two choices here, either the resource access needs to be done under the security context of the user (impersonation model), or, the resource access can be done under the security context of some ad-hoc user account (the trusted sub-system model).

In summary the webpart will retrieve security credentials from SSO, create an impersonation security context with those credentials using the LogonUser API, perform the resource access and then undo the impersonation.

We can overcome the double-hop problem using Sharepoint SSO while fulfilling both security models;

1. Trusted Sub-system Model

Create an SSO Enterprise Application Definition of type Group – all users will access network resources using the same credentials:

MbosDoDefSSO Display Name: Mbos ESB Domain Access
Type: Group

Username (Unmasked) = esbprocess
Password (Masked) = *****
Domain (Unmasked) = MIT

2. Impersonation Model

Create an SSO Enterprise Application Definition of type Individual – all users will access network resources using their own credentials:
MbosLoDefSSO Display Name: Mbos ESB LogData Access
Type: Individual

Username (Unmasked) = hardingp
Password (Masked) = *****
Domain (Unmasked) = MIT

First you’ll need to configure Sharepoint SSO, try google or this post here.

So assuming that you’ve configured SSO and set up these EAD’s, the next requirement is some code to do the impersonation which you can write, find on google or copy this one here.

Finally, you’ll want some code to get credentials from SSO, which I’ve reproduced below.

One thing to note, is that if you create an EAD of type Individual (Windows Authentication), when you call ISsoProvider.GetCredentials, a SingleSignonException exception will be generated if SSO doesn’t have credentials stored for the calling user, for the EAD.

In this case, you can make a call to ISsoProvider.GetCredentialManagementUrl to get the credential management URL to allow the user to enter their SSO credentials (for this EAD).

Putting these pieces together, accessing network resources either on behalf of the calling user, or as an ad-hoc user, can be accomplished as shown below;
view source
01 // get sso creds
02 var ssoApp = SharepointSSO.GetEnterpriseApplication(C_SSO_EadId);
04 using (new Network.Impersonator(ssoApp.Fields["Username"], ssoApp.Fields["Domain"], ssoApp.Fields["Password"],
06 Network.LogonProvider.LOGON32_PROVIDER_WINNT50))
07 {
08 // perform your network resource access here
10 }

Sharepoint Single Sign-On accessor code.
view source
01 using System;
02 using System.Collections.Generic;
03 using System.Runtime.InteropServices;
04 using Microsoft.SharePoint.Portal.SingleSignon;
06 namespace Tools.Sharepoint.SSO
07 {
08 class SSOApplication
09 {
10 public IDictionary Infomation
11 { get; set; }
13 public IDictionary Fields
14 { get; set; }
15 }
17 class SharepointSSO
18 {
19 private static string ConvertSecureStringToString(System.Security.SecureString pValue)
20 {
21 IntPtr lValuePointer = IntPtr.Zero;
22 string lValueAsString;
23 try
24 {
25 lValuePointer = Marshal.SecureStringToBSTR(pValue);
26 lValueAsString = Marshal.PtrToStringBSTR(lValuePointer);
27 }
28 catch (Exception ex)
29 {
30 lValueAsString = ex.Message;
31 }
32 finally
33 {
34 if (lValuePointer != IntPtr.Zero)
35 Marshal.ZeroFreeBSTR(lValuePointer);
36 }
37 return lValueAsString;
38 }
40 public static SSOApplication GetEnterpriseApplication(string eadID)
41 {
42 if (string.IsNullOrEmpty(eadID)) throw new ArgumentException("an EAD ID is required", "eadID");
44 var ssoProvider = SsoProviderFactory.GetSsoProvider();
45 var ssoCreds = ssoProvider.GetCredentials(eadID);
46 var ssoApp = ssoProvider.GetApplicationInfo(eadID);
47 var ssoFields = ssoProvider.GetApplicationFields(eadID);
49 var creds = new SSOApplication
50 {
51 Infomation = new Dictionary(),
52 Fields = new Dictionary()
53 };
54 creds.Infomation["ID"] = ssoApp.ApplicationName;
55 creds.Infomation["Display Name"] = ssoApp.ApplicationFriendlyName;
56 creds.Infomation["Contact Name"] = ssoApp.ContactName;
57 creds.Infomation["Type"] = ssoApp.Type.ToString();

More Here