I'm working on a project which requires delegation in a double-hop scenario. We have a desktop client, connecting to a WCF service using a net.tcp binding, connecting to a SQL database on another server. Our goal is to use the user's credentials to access the SQL database.
Both the WCF service and SQL database are running under the same domain user, which has delegation enabled for the SQL database. The instructions here have been followed, with no success.
Now, some details recorded in our logs:
The login used on the SQL database appears as the user the WCF service is running under, and uses Kerberos.
The login used on the WCF server appears as the client's user, but uses NTLM.
Using either [OperationBehavior(Impersonation = ImpersonationOption.Allowed)] or using (ServiceSecurityContext.Current.WindowsIdentity.Impersonate()) results in commands being run as the client, on the WCF server. This leads me to believe impersonation is working fine.
So, what could be causing the first hop to fall back to NTLM? We suspect it's a SPN issue, but we've registered the SPNs of both the WCF service and SQL service to the shared domain user. Also, as per the instructions listed above, we've set the SQL service as trusted for delegation on the domain user.
We've used EndpointIdentity.CreateSpnIdentity on the WCF service to set the SPN, and this is the SPN we've registered to the domain user.
Any suggestions? Thanks in advance!
Edit:
We've found something that may have been an issue - We had not used EndpointIdentity.CreateSpnIdentity on the client. After setting this we receive the error
“call to SSPI failed” with an inner exception of “target principle name is incorrect”. But the SPN we've set in the client and the server match, and both match the hostname of the service. If we set both the client and the server SPN to something completely different, or if the client's specified SPN does not match the server's SPN, authentication falls back to NTLM as it did before. We've done research into the error, but cannot find its cause. Any suggestions?
We've also performed packet captures of both cases - falling back to NTLM and when we receive the "call to SSPI failed" error. In both cases, similar packets are sent and received until, on one, NTLM is mentioned. On the other, a "TURN CHANNEL" packet is sent from the client to the server. The packets contain nothing human readable except the IP address of the server until either NTLM is mentioned, and the username and computer names are sent, or the "TURN CHANNEL" packet is sent, which contains what appears to be the SPN, and possibly the hostname. There doesn't appear to be any human readable error codes or error messages. Any suggestions on what to look for in the packets?
We found our error - the client was creating the connection using the IP address of the server. After switching the IP to a fully qualified domain name, the first hop consistently authenticates using Kerberos.
The IP address resolves to the same string we used in both SPNs, but I suppose the client checks if the connection string matches the portion of the SPN following the slash before performing any other checks.
We tested our results using both Network Service and our domain user, and as long as the SPN was registered to the computer or user respectively, there were no issues.
Hopefully this answer will save others some time and hassle!
Additional Note: While this enabled Kerberos authentication for all connections, we later discovered that this was unnecessary in our situation. Part of our connection to the database was not inside the impersonation using block, which was causing the table read to fail. We have since removed all delegation and SPN related code, and the database connection continued to work correctly. Our first hop is using NTLM. We're not exactly sure how the credentials are being used at the SQL server, as our connection appears to be exactly what is described as the double hop scenario, which should require Kerberos and delegation, but it's hard to argue with what's working. I suspect it may have something to do with the note located under delegation in this document:
When a client authenticates to the front-end service using a user name and password that correspond to a Windows account on the back-end service, the front-end service can authenticate to the back-end service by reusing the client’s user name and password. This is a particularly powerful form of identity flow, because passing user name and password to the back-end service enables the back-end service to perform impersonation, but it does not constitute delegation because Kerberos is not used. Active Directory controls on delegation do not apply to user name and password authentication.
If anyone has any other suggestions for the reason it's working, I'd love to hear them. However, I don't feel it's worth opening another question for.
Related
I'm doing my school project and in my case, I have a client and 2 WCF cloud service in Azure cloud and the first service then needs to call another service. The client (caller) need to call the WCF service and verify the caller identity without Login, and what way can I use in this case, My idea is to use SSL Authentication or IP to verify the caller identity and is this method is correct or any suggestion method to this case?
There are multiple options for authentication. As you indicated you can use a SSL certificate to validate that the client is who you think they are (preferably SHA2 or above).
You can also white list by IP as you also mentioned. This could cause problems later if the there are multiple clients or their IP changes.
With WCF you can also use a Custom User Name and Password Validator where the client passes a user name and password in the request.
I think unless you have the option to use windows auth, tokens would be the other option, that is more complicated though. Using SSL or User Name Validator are probably the easiest to set up.
Let's assume that the client wants to authenticate himself to a HTTP proxy. The proxy is configured with kerberos, and has clearly the service name HTTP/proxy.foo.bar set in it's configs. How does the client know which service name to request the ticket to ? Does it request the ticket to the domain name he's making request to (in this case it is proxy.foo.bar indeed), or does it receive the name in the authentication sequence, in a 407 reply in this case (which doest contain the negotiate challenge, but I just don't know if there's a way to look into it) ?
I'm trying to debug the kerberos errors on a proxy which suddenly stopped authenticating some clients. The thing is, that looking in the Wireshark, I see that the client is requesting a ticket not for a service name configured on a proxy (same name he's instructed to use), HTTP/proxy.foo.bar, but for a name that the proxy IP resolves to, HTTP/host.foo.bar (well, at least it's the name that the proxy resolves to, may be though the client gets it some other way), and TGS just cannot find one, thus an error happens.
So you’ve got two questions in here (you didn't ask how to actually solve the problem, to do that more details would be needed - see comments).
You asked "The proxy is configured with kerberos, and has clearly the service name HTTP/proxy.foo.bar set in it's configs. How does the client know which service name to request the ticket to?"
A. It works pretty much like this. The client types in a URL in the web browser or clicks on a hyperlink. It looks up the IP host in DNS domain which matches the host name in the URL. Then it goes to that IP host, looking for the service defined in the URL, in this case it is the HTTP service. If it receives an HTTP 401 Negotiate challenge (it's 401, not 407) from the web server, due to it being Kerberos-protected, it goes to its KDC and requests a Kerberos service ticket for HTTP/proxy.foo.bar, zips back to proxy.foo.bar and presents the ticket to that host for the HTTP service running on it. The host validates this ticket and if all is well and the client web browser renders the HTML. You've seen the Kerberos ticket ticket when you ran klist on the client. I don't have any web references for you, this is all off the top of my head.
You also asked “Does it request the ticket to the domain name he's making request to (in this case it is proxy.foo.bar indeed), or does it receive the name in the authentication sequence, in a 407 reply in this case (which doest contain the negotiate challenge, but I just don't know if there's a way to look into it) ?”
A. Your question was a bit hard to follow but if I am understanding you correctly, the answer is the web client requests a ticket as a result of the HTTP 401 Negotiate authentication challenge from the web server (see above).
There’s many diagrams sequencing this process on the web, including here: http://www.zeroshell.org/kerberos/Kerberos-operation/
First of all sorry for my English, its not my native language. I will try to describe my problem as much as I can.
I searched for a long time on the Internet for a solution where I can create a wcf service that can respond to requests of my clients with username and password required but without creating a certificate, using https or anything else that require a special configuration on the machine where my windows service will be installed.
Here is my point: I want to deploy an application to a lot of my customers. This application will have mobile devices and a server which will give some information to mobile device with the help of wcf. Each customer will have a server and many devices. I don't want that anyone on the web can have access to these information and for this reason, I must implement an authentication procedure with username and password for each request but I don't want to be forced to install a certificate, activate some https port on each machine when I sell a new copy of my application.
I've read that with wcf 4.0, there is a built-in system that can encrypt data and for this reason, I don't want the overhead of implementing anything else if possible.
My question is: Is that possible to have a secure solution considering my requirements and if yes, how can I do that?
If I really must create a certificate and use IIS, https or any other secure solution, it is possible to automate these things in a package that will be installed in a single click wizard into each server machine of my customers?
Thank you in advance for your time.
By default WCF doesnt allow transport of username credentials over http and hence have to use certificates to secure your transport layer. But if you are sure that you are fine with sending username credentials over the http channel then you can have a look at ClearUsernameBinding which gives you the flexibility of sending username credentials over http channel (consider the fact that someone can intercept your transport channel to get access to the credentials)
Also if you want to use certificates that have to be installed you can achieve that writing some code in c# and include that as part of your installation from your package. You can also configure everything from an msi like creating a virtual directory, deploying the application,etc..
what you are probably looking for is one of the wcf bindings that has message level security. You can put the user name and password into this message and not worry about them going across an http wire unencrypted(ie custom authentication). The defaults for WCF send user name and password as part of the http request in the header this is why it wants https.
We have a webserver in a DMZ hosting an IIS website. The website talks to a middleware machine in a private domain, which is hosting WCF services.
When the webserver tries to communicate with the middleware machine via a TCP binding, we get the following error message,
The server has rejected the client credentials. The logon attempt
failed.
The website uses an AppPool account from the private domain (DMZ trusts private domain). Forms and Anonymous authentication are enabled.
My question is, would the middleware server be able to authenticate valid credentials (hopefully the appPool credentials), even through they originated from an untrusted domain (dmz)??
#marc_s is right in his comment. It's not just a WCF thing though. it's security in general.
Just look at it from a conceptual point: "authenticate valid credentials" is not what is happening. The middleware service gets credentials. The question is then if those credentials are valid or not. In order to determine validity it needs to known it or ask something that it trust to validate it for him. Asking an untrusted party doesn't work since you can't determine if the answer you get is a valid answer or not. In your case there is no place to determine if passed credentials are valid or just a random token.
If you want to allow unvalidated credentials to pass you should really remove the authentication/authorisation all together.
My scenario:
Many WCF clients which are in environments outside of my control
Server will either be mine OR in an environment outside of my control
So worst case the client and the server is outside of my control. More specifically, I might assume that someone hosting this code could try to maliciously impersonate either the server or the client (the server being more likely). However, the client needs to verify that the server is my code and the server needs to verify that the client is my code. I've seen all the recommendations to use certificates; however, is that an option given my scenario?
One approach I've considered is to write an IClientMessageInspector and an IDispatchMessageInspector to set and verify a custom SOAP header on both sides. I would create an HMAC signature based on a secret key contained within the source code (assume I have a way to keep this hidden) and then verify the digest based on the message body.
I feel like this would work; however, I feel like there might be something more out-of-the-box that I'm missing here. Am I close, way off track? Thanks for any guidance!
Certificates are definitely the way to go in your situation.
Your server will easily be authenticated by clients because it will provide a certificate known to each client, SSL is a good option here.
The server will also be able to authenticate clients by requesting that every client should provide a certificate (server can check for a specific issuer of the certificate - your own issuer in that case).
Now you just need to correctly manage/secure your certificate server to make sure that it won't be compromised.
I don't think there is anything out of the box to do this, simply because it is an unusual requirement for the server to verify that the code on the client calling the service is authorized code.
Generally, it is sufficient to establish trust as follows:
Server has a certificate and service uses SSL - this way clients are confident that they are connecting to the correct server machine.
Clients provide authentication details (eg username/password, certificate etc) to the server so the server knows the connecting client can be trusted.
You are attempting to go the extra step to verify that not only are the users/machines verified, but also that the code running is verified - this is simply overkill. If the code running is not verified, either:
One of the machines has been compromised, in which case you have bigger issues to worry about.
One of your users has written code against your service and is using it 'illegally'. This should not be a problem if your service only allows authorized users to perform 'dangerous' operations.