<Background>
Many people have stumbled across the IIS Double-Hop issue when trying to access Active Directory information on an ASP.NET page.
In short - when we access an ASP.NET page, IIS can impersonate our user credentials for code execution (if set in the web.config). On Windows networks, impersonated credentials can't be used to access network resources - this prevents a web-server from maliciously or accidently using the credentials of a user who innocently logged on to it to access protected network resources.
The problem is that when we try to access the Active Directory for legitimate purposes (to display the current user's details, for instance) while impersonating the user (as SPS does by default) we can't pass the credentials on and our AD query uses the understandbly-limited Anonymous Logon account.
The suggested workaround is to supply teh DirectoryServices request with a username and password so as not to use the impersonated credentials - but this, of course, opens up a whole set of problems relating to security and maintainability.
</Background>
<Solution>
This workaround will work only if the IIS worker process is running under a domain account.
Under IIS 6 - this is the user specified in the Identity tab of the Application Pool.
Under IIS 5 - this is the user specified in the Identity tab of the COM+ application that is generated for the IIS application.
Since our problem arises from having our code run in an impersonation context, the solution is simply to undo the impersonation so that the executing identity isn't the connecting user, but the user who started the process. Since we don't have any convenient .NET WindowsImpersonationContext object we can Undo(), we'll simply have to go deeper into Win32 code:
[DllImport("advapi32.dll")]
private static extern int RevertToSelf();
This very simple function (no parameters - no marshalling! Yipee!) will cancel the current impersonation session and return the actual running user - our IIS WP identity.
It would be prudent to re-impersonate the connected user after our code runs:
WindowsIdentity connectedUser = WindowsIdentity.GetCurrent();
RevertToSelf();
// DirectoryServices code here.
connectedUser.Impersonate()
If this revert/reimpersonate code is something we'll be running a lot, it would be nice to wrap this in a nice disposable object so we can wrap our code in a using() block:
using (new UnImpersonator())
{
// Code running as the Worker Process identity.
}
This is similar to the ImpersonationContext class I described here - the constructor will save the current Identity in a member and call Revert, and the Dispose() will re-impersonate the user.
</Solution>