Can't connect to sql in wcf
Hi, i keep getting:
Login failed for user ''. The user....
Currently i'm hosting my wcf application within IIS (it worked fine when i self hosted it in an app running as me).
So i tried to impersonate my user account in the web config (just to see), but that didn't work either.
In IIS 5 how do i host WCF and make it run as a specified domain account? (So i can windows auth to my sql server) (is this bad practice? should i manually be telling it when/who to login to sql as?)
In IIS 6?
in IIS 7?
ALSO:
How do we properly specify (windows based) client security in our service if we're hosting it anonymously in IIS? I want to certain OU groups in AD to have access to this service, currently having it anonymous allows everyone to do what it use to do as a web service (it's a port from a web service we have running) I understand theres a lot more levels of security in WCF but there is NO documentation in the SDK for how to properly use it!
thanks, i did try that and it worked in IIS...however it's not working again now when self hosted.
interestingly, i'm able to get past my PrinciplePersmission attributes that require the principle to be me, yet when it goes to log into Sql it's sending NT AUTHORITY\anonymous user through the wire.
so here's the client... which before even started requires i be authenticated and ensures i'm running as me...
static void Main(string[] args){
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);WindowsIdentity ident = WindowsIdentity.GetCurrent();WindowsPrincipal principal = new WindowsPrincipal(ident);PrincipalPermission pp = new PrincipalPermission("SRG\\jpeckham","SRG\\Domain Users",true);pp.Demand();
if (principal.IsInRole("SRG\\Domain Users")){
System.Diagnostics.
Debug.WriteLine(string.Format("RUNNING AS: {0}", ident.Name));try{
StarSearcherServiceClient service = new StarSearcherServiceClient();service.ChannelFactory.Credentials.Windows.AllowedImpersonationLevel =
System.Security.Principal.
TokenImpersonationLevel.Impersonation;(..)
so after this all happens it calls the service method, which looks like this:
[
OperationBehavior(Impersonation = ImpersonationOption.Required)][
PrincipalPermission(SecurityAction.Demand, Role = "SRG\\Domain Users")]GetCodesByTypeResponse IStarSearcherService.GetCodesByType(GetCodesByTypeRequest Request){
(..)
It gets this far without any errors and starts executing into the data access layer, then it fails authenticating to SQL and says "LOGIN failed for NT AUTHORITY\Anonymous User"
how is it getting past the PrincipalPermission attribute if it's operating as Anonymous User?
why does the EXACT same code work in IIS but not self hosted?
... oh and heres the config file info
<
system.serviceModel><
bindings><
netTcpBinding><
binding name="tcpSecured"><
security mode="Transport" /></
binding></
netTcpBinding></
bindings><
behaviors><
serviceBehaviors><
behavior name="MetaDataBehavior"><
serviceDebug includeExceptionDetailInFaults="true" /><
serviceAuthorization impersonateCallerForAllOperations="false" /><
serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost:8100/starsearcher/wsdl"/></
behavior></
serviceBehaviors></
behaviors><
services><
service behaviorConfiguration="MetaDataBehavior" name="SRG.Services.StarSearcher.ServiceImplementation.ServiceImplementation"><
endpoint address="" binding="netTcpBinding" bindingConfiguration="tcpSecured"name="netTcpStarSearcher" contract="SRG.Services.StarSearcher.ServiceContracts.IStarSearcherService" /><
endpoint address="mex" binding="mexTcpBinding" bindingConfiguration=""name="mexTcp" contract="IMetadataExchange" /><
host><
baseAddresses><
add baseAddress="net.tcp://localhost:8000/StarSearcherService" /></
baseAddresses></
host></
service></
services></
system.serviceModel>
I tried <serviceAuthorization impersonateCallerForAllOperations="true" /> also...
Ok, first of all theres iconsistencies in both of those links and in what i've been successful with.
The first link says that only transport level security can be impersonated, which is untrue, i was using message level in my wshttp binding and impersonating fine through IIS.
The Second link says that to get impersonation working you have to set the Windows client credential impersonation level... which is also untrue, i had to set the impersonation level of the channel factory to get it to work at all. (see http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1285198&SiteID=1)
I have to get this figured out so, what i intend to do today is take a simple 1 method service that accesses my sql database and try it in every level of security and impersonation to see what actually works, i'll try to keep you posted on how that goes.
Now, in response to your question, my client credential type is windows, i used vs2005 extensions to generate a service reference each time i changed any of the host config. so my client config looks like this:
<security mode="Transport">
<
transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" /><
message clientCredentialType="Windows" /></
security>the code in my client looks liek this:
Client service = new Client();service.ChannelFactory.Credentials.Windows.AllowedImpersonationLevel =
System.Security.Principal.
TokenImpersonationLevel.Impersonation;service.ClientCredentials.Windows.AllowedImpersonationLevel =
TokenImpersonationLevel.Impersonation;
i've tried this:
Client service = new Client();
service.ChannelFactory.Credentials.Windows.AllowedImpersonationLevel =
System.Security.Principal.
TokenImpersonationLevel.Impersonation;
i've tried this:
Client service = new Client();
service.ClientCredentials.Windows.AllowedImpersonationLevel =
TokenImpersonationLevel.Impersonation;
i've tried this
Client service = new Client();
in my client before i do anything, i first added this:
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal); didn't help so i added this:
WindowsIdentity ident = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(ident);System.Diagnostics.
Debug.WriteLine(string.Format("RUNNING AS: {0}", ident.Name));
it was saying it was SRG\jpeckham, but just to be sure i added this:
PrincipalPermission pp = new PrincipalPermission("SRG\\jpeckham","SRG\\Domain Users",true);
pp.Demand();
i still didn't think it was working properly so i added this:
WindowsIdentity.Impersonate(ident.Token);
well i'll start a simple 1 method service that hits my db and mess around with it some more today, i honestly don't even need this current service i'm working to impersonate, i just want to know how to do it properly since so far no example has worked for me out of the box.
ok... here is what i did.
I started a new service using the service factory (microsoft patterns and practices guidance automation toolkit).
I put in 1 method that calls to my sql database (on a dev machine seperate from my service host/dev machine)
here's my Operation Contract implementation:
[
OperationBehavior(Impersonation=ImpersonationOption.Required)][
PrincipalPermission(SecurityAction.Demand,Role="SRG\\Domain Users")]public GetFolderNameByOfficeCodeResponse GetFolderNameByOfficeCode(GetFolderNameByOfficeCodeRequest request){
GetFolderNameByOfficeCodeResponse response = new GetFolderNameByOfficeCodeResponse();GetFolderNameByOfficeCodeLogic bll = new GetFolderNameByOfficeCodeLogic();string result = bll.GetFolderNameByOfficeCode(request.OfficeCode);response.FolderName = result;
return response;}
Nothing real fancy going on in the GetFolderNameByOfficeCode() method, it just uses the repository to create a single object from the database then return a string property from that object.
Works perfectly fine when i Host it in IIS with THIS config:
<
system.serviceModel><
bindings/><
behaviors><
serviceBehaviors><
behavior name="Srg.Services.PayBill.ServiceImplementation.PayBillService_Behavior"><
serviceDebug includeExceptionDetailInFaults="true"/><
serviceMetadata httpGetEnabled="true"/></
behavior></
serviceBehaviors></
behaviors><
services><
service behaviorConfiguration="Srg.Services.PayBill.ServiceImplementation.PayBillService_Behavior" name="Srg.Services.PayBill.ServiceImplementation.PayBillService"><
endpoint binding="wsHttpBinding" bindingNamespace="http://Srg.Services.PayBill.ServiceContracts/2007/02" contract="Srg.Services.PayBill.ServiceContracts.IPayBillService"/><
endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/></
service></
services></
system.serviceModel>
Now i created a new WPF application to host the service in and setup endpoints for wsHTTP and netTcp, the config looks like this:
<
system.serviceModel><
behaviors><
serviceBehaviors><
behavior name="NewBehavior"><
serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost:9001/meta" /><
serviceDebug includeExceptionDetailInFaults="true" /></
behavior></
serviceBehaviors></
behaviors><
bindings><
netTcpBinding><
binding name="netTcpBinding" /></
netTcpBinding><
wsHttpBinding><
binding name="wsHttpBinding" /></
wsHttpBinding></
bindings><
services><
service behaviorConfiguration="NewBehavior" name="Srg.Services.PayBill.ServiceImplementation.PayBillService"><
host><
baseAddresses><
add baseAddress="net.tcp://localhost:9000/paybill"/><
add baseAddress="http://localhost:9001"/></
baseAddresses></
host><
endpoint address="" binding="netTcpBinding"bindingConfiguration="" name="netTcp" contract="Srg.Services.PayBill.ServiceContracts.IPayBillService" /><
endpoint address="paybill" binding="wsHttpBinding"bindingConfiguration="" name="wsHttp" contract="Srg.Services.PayBill.ServiceContracts.IPayBillService" /><
endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /></
service></
services></
system.serviceModel>The pertinent code looks like this:
private void StartService(){
_host =
new ServiceHost(typeof(PayBillService));_host.Faulted +=
new EventHandler(_host_Faulted);_host.Open();
}
Then i made a client for the IIS hosted app, then i copied that code and modified the service reference and code for the self hosted app.
The IIS client config looks like this:
<?
xml version="1.0" encoding="utf-8" ?><
configuration><
system.serviceModel><
bindings><
wsHttpBinding><
binding name="WSHttpBinding_IPayBillService" closeTimeout="00:01:00"openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"maxBufferPoolSize="524288" maxReceivedMessageSize="65536"messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"allowCookies="false"><
readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"maxBytesPerRead="4096" maxNameTableCharCount="16384" /><
reliableSession ordered="true" inactivityTimeout="00:10:00"enabled="false" /><
security mode="Message"><
transport clientCredentialType="Windows" proxyCredentialType="None"realm="" /><
message clientCredentialType="Windows" negotiateServiceCredential="true"algorithmSuite="Default" establishSecurityContext="true" /></
security></
binding></
wsHttpBinding></
bindings><
client><
endpoint address="http://localhost/services/paybill/PayBillService.svc"binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IPayBillService"contract="Srg.Services.PayBill.Client.localhost.IPayBillService"name="WSHttpBinding_IPayBillService"><
identity><
userPrincipalName value="mymachinenameremoved\ASPNET" /></
identity></
endpoint></
client></
system.serviceModel></
configuration>
The self hosted appconfig looks like this:
<?
xml version="1.0" encoding="utf-8" ?><
configuration><
system.serviceModel><
bindings><
netTcpBinding><
binding name="netTcp" closeTimeout="00:01:00" openTimeout="00:01:00"receiveTimeout="00:10:00" sendTimeout="00:01:00" transactionFlow="false"transferMode="Buffered" transactionProtocol="OleTransactions"hostNameComparisonMode="StrongWildcard" listenBacklog="10"maxBufferPoolSize="524288" maxBufferSize="65536" maxConnections="10"maxReceivedMessageSize="65536"><
readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"maxBytesPerRead="4096" maxNameTableCharCount="16384" /><
reliableSession ordered="true" inactivityTimeout="00:10:00"enabled="false" /><
security mode="Transport"><
transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" /><
message clientCredentialType="Windows" /></
security></
binding></
netTcpBinding><
wsHttpBinding><
binding name="wsHttp" closeTimeout="00:01:00" openTimeout="00:01:00"receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false"transactionFlow="false" hostNameComparisonMode="StrongWildcard"maxBufferPoolSize="524288" maxReceivedMessageSize="65536"messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"allowCookies="false"><
readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"maxBytesPerRead="4096" maxNameTableCharCount="16384" /><
reliableSession ordered="true" inactivityTimeout="00:10:00"enabled="false" /><
security mode="Message"><
transport clientCredentialType="Windows" proxyCredentialType="None"realm="" /><
message clientCredentialType="Windows" negotiateServiceCredential="true"algorithmSuite="Default" establishSecurityContext="true" /></
security></
binding></
wsHttpBinding></
bindings><
client><
endpoint address="net.tcp://localhost:9000/paybill" binding="netTcpBinding"bindingConfiguration="netTcp" contract="Srg.Services.PayBill.Client.localhost.IPayBillService"name="netTcp"><
identity><
userPrincipalName value="myemailremoved@myemailremoved.com" /></
identity></
endpoint><
endpoint address="http://localhost:9001/paybill" binding="wsHttpBinding"bindingConfiguration="wsHttp" contract="Srg.Services.PayBill.Client.localhost.IPayBillService"name="wsHttp"><
identity><
userPrincipalName value="myemailremoved" /></
identity></
endpoint></
client></
system.serviceModel></
configuration>
Then the fun part happened, i ran the IIS client, it worked 100% no issues, connected, retrieved the string and shown in messagebox.
However, i ran the TCP client next, it failed stating Sql connection login failed for user NT AUTHORITY\Anonymous logon.
Tried the Wshttp self hosted service, same problem.
The interesting bit is that it gets past the impersonation required attribute, and it gets past the PrinciplePermission attribute, and i was just curious about it so i put in WindowsIdentity.GetCurrent().Name and printed it... it came out as the correct identity! So what the heck is going on here?