views:

48

answers:

4

We have a .NET 3.5 Web Service (not WCF) running under IIS. It must use identity impersonate="true" and Integrated Windows authentication in order to authenticate to third-party software. In addition, it connects to a SQL Server database using ADO.NET and SQL Server Authentication (specifying a fixed User ID and Password in the connection string).

Everything worked fine until the database was moved from SQL Server A to SQL Server B. (Neither was the same as the web server.) Then the Web Service would throw the following exception:

A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server)

This error only occurs if identity impersonate is true in the Web.config.

Again, the connection string hasn't changed and it specifies the user. I have tested the connection string and it works, both under the impersonated account and under the service account (and from both the remote machine and the server).

What needs to be changed to get this to work with impersonation?

EDIT:

Remus Rusanu pointed us in the right direction. It came down to Kerberos - the SPNs weren't set up for the new server. See also asp.net via kerberos integrated windows authentication to sql server. Thank you!

A: 

are you using impersonation in the web service itself?

Impersonation in web services operates on a different layer than IIS does. To get from client to web service, you can have identity=impersonate off and get the user token from ServiceSecurityContext, even with Anonymous mode on.

To impersonate that token in the web service, get the WindowsCredential from ServiceSecurityContext and call the credential.Impersonate() method in a using statement, placing your connection to the database inside the using block.

public class HelloService : IHelloService
{
    [OperationBehavior]
    public string Hello(string message)
    {
        WindowsIdentity callerWindowsIdentity =
        ServiceSecurityContext.Current.WindowsIdentity;
        if (callerWindowsIdentity == null)
        {
            throw new InvalidOperationException
           ("The caller cannot be mapped to a WindowsIdentity");
        }
        using (callerWindowsIdentity.Impersonate())
        {
            // Access a file as the caller.
        }
        return "Hello";
    }
}

Also, if you need another leg in the process (i.e. the back-end service is on another server), you will need to use Delegation to propagate the credentials. You may also do this declaratively. See this article for full details: http://msdn.microsoft.com/en-us/library/ms730088.aspx

Lerxst
I'm using impersonation set up through IIS and the Web.config for this service. While potentially your technique would allow us to impersonate when calling the third-party software and not while calling ADO.NET, it would significantly complicate the code (which worked before the SQL Server was moved). We're also not using WCF, so the delegation link does not appear to be relevant.
TrueWill
I wasn't aware that you were not using WCF, or that the SQL server was never on the same box. The code I posted is what is needed for WCF, as the impersonation is not done through IIS. Sorry for the confusion, and thanks for the clarification
Lerxst
Thanks - I added a note to the original post to clarify.
TrueWill
A: 

Since it worked when the SQL Server was on the same box, it could be connected to transactions.

It may be that it is trying to use the the MSDTC, and the impersonated users are lacking some rights.

Another thing that you could try is to log on to the web server with a user that you are trying to impersonate, and see if you can then connect to the sql server.

Shiraz Bhaiji
I edited the question to clarify it. Neither SQL Server (old or new) was on the same box as the web server.
TrueWill
+2  A: 

When using impersonation and accessing a resource on a different host, delegation occurs (what the laymen call 'two hops'). Since delegation is restricted by default, authentication fails, unless constrained delegation is explicitly enabled.

But wait, you says, I use SQL Authentication and SQL authentication is not an NTLM/Kerberos 'resource'. True, says I, but you also use NAMED PIPES and named pipes are an NTLM/Kerberos resource, therefore delegation does occur.

See How to: Configure Client Protocols to make sure SQL Server is listening on TCP and Configuring Client Network Protocols for how to force the client to choose a specific protocol (ie. not try named pipe first). You can also force TCP by simply appending tcp: in front of the server name in the connection string.

Remus Rusanu
If I append "tcp:" I get: "(provider: TCP Provider, error: 0 - No connection could be made because the target machine actively refused it.)" I tried this on my local machine, where it will work if I take the "tcp:" off.
TrueWill
Your server is not configured to listen on TCP, or is not listening on the default port
Remus Rusanu
A: 

If Delegation is your problem, see this article for enabling constrained delegation http://msdn.microsoft.com/en-us/library/ms730088.aspx

go to the very bottom to see how to set up an account for constrained delegation. I know its a WCF article, but the process for setting up an account to use delegation should be the same.

for even more details, go here http://technet.microsoft.com/en-us/library/cc739587(WS.10).aspx

Alternatively, have your SQL server enable TCP access and access it that way, as Remus explained.

Lerxst