The Double-Hop Problem
The double-hop problem will usually only be an issue to those of you who write some sort of web-based code (a web application or web service) that uses impersonation. As a .NET programmer I must confess I did not realise that impersonation was built into Windows. Like many others I know, I thought of it as a feature of the .NET framework. It turns out however that impersonation is something that Windows implements at an operating system level and its also possible to use it when working with Windows API or any other method of programming windows that will allow you direct/indirect access to the Windows API.
Having made that preamble, lets move on to the problem I encountered. I was tasked with showing a list of some documents stored in a sharepoint server in our application. For various reasons, I decided to write my own set of web services and host them on the sharepoint server. These web services would interact with sharepoint (using its class libraries) on my web application's behalf and bring back the data I needed. I also needed to enforce security where in only users who had access to a document library (roughly sharepoint's equivalent of a windows folder whose security permissions can be controlled) would be able to see the documents in it.
Now sharepoint (or rather its class libraries) make this thing simple because you can just set a flag telling it to only show you document libraries which the current user has access to. The only thing you need to get this to work is to impersonate the currently logged on user in the process you are using to access sharepoint (ASPNET in this case because I was using web services). Fair enough. I turned on impersonation in my web application as well as the web services application hosted on the sharepoint server. I also turned off all everything but integrated windows authentication on both my web application and web services. Now I was sure that I would get a set of valid windows credentials whenever a user logged on to my web application. I thought I could safely pass that on to my web services which could get me the documents the user was allowed to see. I was wrong. Before I describe the problem, lets take a look at what my application looked like at this point:
While I was testing this solution on my computer everything worked exactly as I thought it would. The web application would impersonate the user (me because I was testing with localhost as the web server) and pass it on to my sharepoint web service. Even when I tried accessing the application using "Run as..." with the browser, everything worked as expected. Users were only ever shown files they had access to. However, as soon as I delivered the application to quality control they came back with a problem. They said the web page that called my sharepoint web services kept coming back with the error 401: Unauthorized. For the longest time I couldn't figure it out.
I decided to recreate the testing set up and test the problem. This was when I realised that the web application (or rather windows) simply refused to forward impersonated credentials to the web service. And if it did, windows wouldn't carry them across properly, so the web application calling the web service wouldn't even get across the windows authentication in IIS. The reason for this was the so called double-hop issue. Basically, what this means is that although windows will allow you to use impersonated credentials to access local resources, it won't allow you to delegate the impersonated credentials to another remote resource (such as our web service). The reason the whole thing worked when I was testing it was because in my case only a single hop was involved because my web server and user agent were on the same machine. So the only single hop was from the client/web server machine to the sharepoint server.
According to a Microsoft KB article: The double-hop issue is when the ASPX page tries to use resources that are located on a server that is different from the IIS server.
I dug a little deeper and realized that the double-hop issue is actually something inherent to the default NTLM authentication scheme that Windows Server OSs use. Apparently, you can get around the problem and use proper delegation if you set up your network to use Kerberos and set up the web server in question as trusted for delegation.
I was pretty convinced this wasn't something our client would be willing to do, so I ended up using a custom permissioning scheme whose rules were defined in our web application rather than using permissions set up on the sharepoint server. The actual access to sharepoint happens using a set of (super-user) credentials stored in the web application's configuration. This has the disadvantage that if the permissions are set up differently on sharepoint and our application then a user who doesn't actually have access to a sharepoint document library might be able to access it using our application. Having said that however, I would treat that as a set up issue and leave it to the system administrator to keep the two permission systems in sync. For now, this is about the best and most low impact way I can see of solving the problem. Of course, I could choose to pick up the sharepoint permissions and use those to enforce security, but that would amount to reimplementing the whole windows security system as well as sharepoint's role based security mechanism. Something which given the time I couldn't be bothered with.
Incidentally, this problem will also affect you if you are trying to access the Active Directory using impersonated credentials. That is unless your web server lives on your primary domain controller, which in itself is a pretty bad idea.