Not passing Credentials to WCF Service resulting in a 401 - c#

I'm tearing my hair out on this one, I have a WCF service that I can call through the browser and it works fine, when I call it from the web application with the below method I get a (401) Unauthorized error. And the service does not get called. What's more, when I run my web application from my local machine (debug mode using IIS Express) pointed at my dev server (IIS7) it works but when I deploy my web application to the dev server and point it to the dev server services it fails wit the 401 error. I think this is something to do with IIS7 but I'm not 100% sure and help would be super useful.
I have looked online for the answers but thus far the best I have found is this.
My service call is as follows:
var request = (HttpWebRequest) WebRequest.Create(url);
request.Method = "GET";
request.ContentType = "application/json; charset=utf-8";
request.AuthenticationLevel = AuthenticationLevel.MutualAuthRequested;
request.Credentials = CredentialCache.DefaultCredentials;
WebResponse responce = request.GetResponse();
Stream reader = responce.GetResponseStream();
var sReader = new StreamReader(reader);
string outResult = sReader.ReadToEnd();
sReader.Close();
var result = (T) JsonConvert.DeserializeObject(outResult, typeof (T));
return result;
My configuration for the service looks like this :
<service name="RGMPServices.Householding.Services.AccountService" behaviorConfiguration="Default">
<endpoint address="" kind="webHttpEndpoint" endpointConfiguration="SecuredHttpEndpointBinding" contract="RGMPServices.Householding.Contracts.IAccountService" />
</service>
<service name="RGMPServices.Householding.Services.HouseholdService" behaviorConfiguration="Default">
<endpoint address="" kind="webHttpEndpoint" endpointConfiguration="SecuredHttpEndpointBinding" contract="RGMPServices.Householding.Contracts.IHouseholdService" />
</service>
<service name="RGMPServices.Householding.Services.UserService" behaviorConfiguration="Default">
<endpoint address="" kind="webHttpEndpoint" endpointConfiguration="SecuredHttpEndpointBinding" contract="RGMPServices.Householding.Contracts.IUserService" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="webBehaviour">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="Default">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint name="SecuredHttpEndpointBinding" helpEnabled="true" automaticFormatSelectionEnabled="true">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
</standardEndpoint>
</webHttpEndpoint>
</standardEndpoints>
I have put some logging on the client service call, just before I call the service, the response is:
DEBUG 2013-10-01 13:15:13,569 452ms ServiceGetSingle - Passing Login: MYLANDOMAIN\MYLANUSERNAME
ERROR 2013-10-01 13:15:13,631 514ms ServiceGetSingle - ERROR Calling ServiceGetSingle with user credentials login: MYLANDOMAIN\MYLANUSERNAME
System.Net.WebException: The remote server returned an error: (401) Unauthorized.
at System.Net.HttpWebRequest.GetResponse()
at Householding.Common.ServiceHelper.ServiceGetSingle[T](String url)
The code looks like:
logger.Debug("Passing Login: "
+ System.Security.Principal.WindowsIdentity.GetCurrent().Name)
Even when I set the AppPool for my website to my domain account it is still not authorising me to access the WCF Service, but again: it's working for the browser. So weird!

It seems likely you're a victim of the double-hop issue when using Integrated Windows Authentication (IWA) and Kerberos. The first hop is from your browser to the web application; the second hop is from your web application to the WCF service.
Here are some resources that explain the issue more fully, and may offer a solution:
IIS, Windows Authentication and the Double Hop issue
Using Integrated Windows Authentication (IWA) in a Distributed Application Architecture
Understanding Kerberos Double Hop
You can configure Active Directory to support Kerberos delegation (usually the infrastructure guys don't like this), or you could turn off impersonation and use a "service" account for the web application and IIS app pool that can authenticate with the WCF service on behalf of the end user.

What are the default credentials on the Dev server? Try doing a log right there and see what you get.
This is what I suspect: Running locally, the credentials are YOUR windows creds. When you call the dev server from dev, the credentials would be whatever account the website is running under. If that particular account doesn't have access, then it would blow up.

How they said before, this looks like an impersonation problem.
Have you tried to start the client program with "run as" to change the credentials?
Additionally you can change this line of code
request.Credentials = CredentialCache.DefaultCredentials;
to
request.Credentials = new NetworkCredential("MyUsername", "MyPassword");
And see if it works. Also you need to create the account "MyUserName" with "MyPassword" on the web server to make it work.

These errors can be caused when the authenticated user does not have access to the physical path where the WCF service is hosted. On the dev server, open up IIS Manager and navigate to the virtual directory for the service. On the right in the Actions bar, click on "Basic Settings". Below the "Physical Path" text box, click "Connect as...". Choose "Specific User" and try setting it to a user account that you know has rights to the physical folder on the dev server. Typically, this would be a service account whose password does not expire.

When running from the browser, the browser is sending your auth credentials. Also, iis express will run as the logged in user, so this is also sending your credentials. Iis is different, it will be running as a local account. Even if you have authentication on your front end iis, that will not be passed to the backend. Windows impersonation tokens are limited in the number of hops allowed, usually 0. This is done to prevent exactly what you are doing.
If you want front end authentication to flow to the backend then you should probably do the authentication yourself and grab user/pass on the way through. Alternatively, if you do the authentication yourself you can create an impersonation token that allows a hop to another machine and it should work.

Related

TLS Fatal: Handshake Failure

I need to connect with an external service, and I have a problem with client authentication. The service requires a certificate, username and password with a request.
I'm using Windows Server 2008 R2.
I've received the PKCS#7 package with the certificates and imported:
SSL certificate (with only public key) to LocalComputer/Personal
Intermediate CA and Root CA to LocalComputer/TrustedRootCertificationAuthorities
I've enabled the TLS 1.0, 1.1, 1.2 clients in the Windows Register:
Windows Register
I'm trying to connect to the server both using the WCF client and web browsers (IE and Chrome).
WCF client (.NET 4.6.1):
App.config:
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_ICalculator" >
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="endpointCredentialBehavior">
<clientCredentials>
<clientCertificate findValue="<thumbprint>"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindByThumbprint" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
Program.cs:
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12;
using (var client = new ServiceClient())
{
client.ClientCredentials.UserName.UserName = "username";
client.ClientCredentials.UserName.Password = "pwd";
client.Open();
var response = client.DoSth();
}
And I get an error:
"Could not establish secure channel for SSL/TLS with authority ...".
Internet Explorer shows that:
IE error
Chrome shows that:
Chrome error
I also tried to debug it via Wireshark. For me, it is suspect that the 'Certificate' message from the client doesn't contain any certificate (neither using the WCF client or a web browser). Should the certificate be added here, and if yes what could be causing a problem with it?
Wireshark packages
I know that there are a lot of articles about TLS and authentication on stackoverflow and Google, but I went through a lot of them and didn't find any info about what I'm doing wrong.
We had better not specify the TLS version, configure your code to let the OS decide on. The specific TLS version used in the communication will be determined by both the server and the client environment.
https://learn.microsoft.com/en-us/dotnet/framework/network-programming/tls
Transport security mode requires that the trust relationship between the server and the client should be established before the communication.
In my opinion, the error typically indicates that the client doesn’t install the server certificate properly.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/transport-security-with-certificate-authentication
Only the server side add Servicemetadata behavior, we can access the service definition(WSDL).
Usually the client provides a certificate that is sufficient to indicate its identity and there is no need to provide a username/password again. Just like the generated client configuration, we only need to provide a certificate. How the service is specifically called, we must know the configuration of the server or the WSDL file.
Feel free to let me know if there is anything I can help with.

How to publish a WCF Rest Webservice?

My App.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="WcfJsonRestService.Service1">
<endpoint address="http://localhost/webservice"
binding="webHttpBinding"
contract="WcfJsonRestService.IService1"/>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior>
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
I published my webservice to the http://localhost/webservice target but when I openhttp://localhost/webservice/ in my browser I get this:
localhost - /webservice/
[To Parent Directory]
11/9/2015 10:30 AM <dir> bin
11/9/2015 11:23 AM 58 WcfJsonRestService.Service1.svc
11/9/2015 11:22 AM 481 Web.config
This works well in debug mode. I just do http://localhost/webservice/getUsers and I receive a list of users. The problem occurs when I publish the webservice to the IIS because http://localhost/webservice/getUsers returns HTTP Error 404.0 - Not Found.
Any suggestions?
You can try this.
You can configure anonymous authentication by using the default
anonymous user account (IUSR), or you can set up a local user account
for anonymous users.
In Features View of IIS Manager, double-click Authentication.
On the Authentication page, select Anonymous Authentication.
In the Actions pane, click Edit to set the security principal (user credentials) under which anonymous users will connect to the site.
In the Edit Anonymous Authentication Credentials dialog box, select one of the following options:
If you want to configure a specific user account that IIS uses to access your site or application, select Specific user. Then click Set to open the Set Credentials dialog box, and enter a user name and password for the identity. Then click OK.
If you want IIS processes to run by using the account that is currently specified on the property page for the application pool, select Application pool identity. By default, this identity is the IUSR account. (Important: If you use the IUSR account, you grant anonymous users all the internal network access associated with that account.)
Click OK to close the Edit Anonymous Authentication Credentials dialog box.
You can use this link as reference.

Impersonate User in SharePoint 2010 web part to call WCF service

I have a Web Part where I am trying to call a WCF Service as the current logged in user. The Web Part work locally when debugging in Visual Studio 2013 - calling the external WCF service. I have configured Kerberos for the WCF Service as well as the SharePoint site and fee like that is all correct. I can call the WCF Service correctly from another Kerberos enabled web app fine as well as from a console app.
Now I'd like to roll this to a SharePoint farm. However after I add the solution to the farm and activate the feature on the sharepoint site...I get an error when I try to add the web part to a page. The error comes up in a box that says "The caller was not authenticated by the service.". The error comes from the actualy method call.
My web part code is as such:
WorksiteService.iWorksiteServiceClient wss = new WorksiteService.iWorksiteServiceClient("WSHttpBinding_iWorksiteService");
wss.ClientCredentials.Windows.ClientCredential = System.Net.CredentialCache.DefaultNetworkCredentials;
wss.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
WorkSiteDocument[] wd = wss.GetDocumentsByAlias2("test");
The web.config in for the sharepoint site has this for the endpoint:
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_iWorksiteService" />
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://myservicerd.xyz.com/WorkSiteService.svc" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_iWorksiteService" contract="WorksiteService.iWorksiteService" name="WSHttpBinding_iWorksiteService">
<identity>
<servicePrincipalName value="myservicerd.xyz.com" />
</identity>
</endpoint>
</client>
</system.serviceModel>
Any ideas on why I get that error? I'm assuming I'm not getting the current user's credentials for some reason.There is nothing in the event or sharepoint logs of the sharepoint server or wcf server. Any ideas on how I can troubleshoot this?Thank you for any assistance you can provide.
I'm not a Sharepoint Expert, but from a Kerberos perspective I would suggest to check the delegation settings in Active Directory just as Marek suggested.
In a double-hop scenario like this, the 'man' in the middle must be permitted to take on the identity of the client.
The question remains who the 'man' in the middle is, which depends on the IIS configuration.
If the IIS is configured for kernel-mode authentication the middle identity is probably the computer account if the server IIS is installed on.
Otherwise I would assume that the identity that needs delegation rights should be the identity of the application pool the web part runs in.
If you determined the identity, go to the appropriate Active Directory account (delegation tab) and select 'Account is trusted for delegation'.
Maybe this blog article can shed some light on this: http://tekaris.com/blog/2013/04/05/http-400-bad-request-kerberos-authentication-with-iis-2/

Problem authenticating with WCF service cross domain

I'm new to WCF and its security so was looking for some help on a problem I'm having.
I have a WCF service that to be deployed to every machine in a small domain. The WCF service is to be hosted in a Windows Service since it has methods in it that need to be invoked elevated. The service is very draft at the moment (it exposes meta-data - but I believe this is to be turned off later - is this possible?). It uses the net.tcp binding - The App.Config of the service is shown below:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service behaviorConfiguration="SvcBehavior" name="Microsoft.ServiceModel.Samples.Svc">
<endpoint address="" binding="netTcpBinding"
contract="Microsoft.ServiceModel.Samples.ISvc" />
<endpoint address="mex" binding="mexTcpBinding" bindingConfiguration=""
contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://*:8100/ServiceModelSamples/service" />
</baseAddresses>
<timeouts closeTimeout="00:10:00" openTimeout="00:10:00" />
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="SvcBehavior">
<serviceMetadata httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
For a client, I have a Winforms C# application. The application serves 2 functions, it pulls static information from the service and then gives the user an option to authenticate and call the administrative methods. Therefore, the authentication is done at client level.
I am using Channelfactory to connect to the service since the hostname is variable (user is prompted for it on client start-up) and the app.config file for the client is shown below - its virtually empty:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
</bindings>
<client />
</system.serviceModel>
</configuration>
The channelfactory code is:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(Namespace = "http://Microsoft.ServiceModel.Samples", ConfigurationName = "ISvc")]
ChannelFactory<ISvc> myChannelFactory = new ChannelFactory<ISvc>(new NetTcpBinding(), "net.tcp://" + HostName + ":8100/ServiceModelSamples/service");
public static ISvc client = null;
I have an interface that descrives the methods in the service as well.
The main problem I am having is this. Say there is Machine1 and Machine2 running on domainA. Lets assume that Machine1 hosts the service and Machine2 runs the client.
When I connect to the service using a domain account domainA\User, the service works fine but say I have a local user on Machine2 and want to connect to the service as Machine2\LocalUser, I get the following error message:
The server has rejected the client credentials.
I have tried experimenting with setting the security mode to none (not something im keen on doing) but then I get an error saying the client and service have a configuration mismatch.
Is there something I can do to fix this? Also, what would happen if the service running on domainA\Machine1 was called by a user from another domain - say domainB\User2 ?
Thanks in advance - Id appreciate some insight into this!
Chada
If you turn off security on the service you must turn in off on the client as well. If you configure client in code you must set the same configuration for new instance of NetTcpBinding as you do on the service in configuration file.
When Windows security is used (default for NetTcpBinding) MachineB cannot authenticate local accounts from MachineA (there were some tricks with duplicate local users but I don't know if they work with WCF as well). That is why you have a domain. Cross domain windows authentication requires trust between domains.
If you need to authenticate cross domain users or local users you cannot use Windows authentication. There are other authentication mechanism but they require configuring certificate (at least on the service) and use either client certificate or user name and password for client authentication.

Problem with hosting WCF service in a Windows Service

I have a WCF service that is hosted inside a Windows Service. The Windows Service has a OnStart method like this:
protected override void OnStart(string[] args)
{
serviceHost = new ServiceHost(typeof (RouterService));
serviceHost.Open();
}
It's quite minimal now, since I'm trying to find the problem. Since I have not put any error handling here, any exceptions at this point should have stopped the service from starting up properly. It would start, and then automatically stop again immediately.
My config file for the WCF service looks like this:
<?xml version="1.0"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
<system.serviceModel>
<services>
<service name="WcfService.RouterService"
behaviorConfiguration="serviceBehavior" >
<endpoint address="RouterService" contract="WcfService.IRouterService" binding="wsHttpBinding" bindingConfiguration="NoSecurity" />
<endpoint address="mex" contract="IMetadataExchange" binding="mexHttpBinding" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8000"/>
</baseAddresses>
</host>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="NoSecurity">
<security mode="None" />
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="serviceBehavior" >
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
In addition, I have added the NETWORK SERVICE user (who the service is running as to be able to listen to Http at the specified port:
netsh http add urlacl url=http://+:8000/ user="NETWORK SERVICE"
Now, in the client, I choose Add Service Reference. I can find the server, and VS also manage to create the configuration and make the proxy files. Then inside the client, I make an instance of the proxy, and open it. Everything still fine. But upon calling the first method on proxy, I get the following exception:
The HTTP service located at http://localhost:8000/RouterService is too busy.
I know that my client is the only thing that is trying to connect to this service, so why do I get this error message then? And how can I fix this? It does not time out when it tries to call the method, it throws the exception almost immediately.
EDIT
Ok, now I found out that my TestServer (not the Windows Client one, but a console application) also gives the same error. Earlier today it didn't, and neither the WCF service or TestServer are changed.
Then I tried changing the port to 8080 instead of 8000, and it worked. Then I tried the same for the Windows Service, and that also worked (after running the netsh http command on the new port)
So something has happened to http on port 8000 for some reason. I have tried restarting the system also of course.
This is puzzling me, so if anyone has any idea what is going on here I would appreciate it.
Ok, after a long time of trying to figure out what happened here, I found the solution.
After I created the windows service, and tried to start it I couldn't because I had to register the NETWORK SERVICE user to listen on http post 8000 first. I therefore ran this command:
netsh http add urlacl url=http://+:8000/RouterService user="NETWORK SERVICE"
It gave a successful result, but then I tried starting the service, and got the same error message. Then I ran the same command without RouterService specified:
netsh http add urlacl url=http://+:8000/ user="NETWORK SERVICE"
It gave a successful result, and now my windows service started up without any problems, but I got the problems mentioned in the question above.
It seems that the first call to netsh causes all of these problems. I tried removing it again with this command:
netsh http delete urlacl url=http://+:8000/RouterService
And then everything worked perfectly.
A bitch to debug this since the exception had nothing to do with the actual problem, so I hope this question can save someone else a couple of hours :)
Following answer of #oyvind-knobloch-brathen consider to use well known SID strings instead of "user" command line key, this will give you cross culture portability (I experienced problems on French-localized Win7 machine). For more information see SID strings. For your case this will be:
netsh http add urlacl url=http://+:8000/ sddl="O:NS"
a link to SDDL on MSDN.

Categories