How to convert WindowsIdentity to NetworkCredential?

How do we convert WindowsIdentity to NetworkCredential ? I am testing my WCF service to make sure that anonymous callers are blocked. For this, I want to do something like:

 myProxy.ClientCredentials.Windows.ClientCredential = foo(WindowsIdentity.GetAnonymous()); 

where foo is the method that converts a WindowsIdentity to NetworkCredential

+4
source share
3 answers

Answering my question:
Converting a WindowsIdentity to NetworkCredential not possible. To check if anonymous subscribers are blocked, issue the current stream from the session zero token , and then call the WCF service. Note: do not use WindowsIdentity.GetAnonymous . This method is useless (suppose it was improperly implemented and has never been fixed). Code to impersonate the current thread with a zero session token (error handling is not performed):

  public static class Helper { [DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] private static extern IntPtr GetCurrentThread(); [DllImport("advapi32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] private static extern bool ImpersonateAnonymousToken(IntPtr handle); public static void ImpersonateAnonymousUser() { ImpersonateAnonymousToken(GetCurrentThread()); } } static string ToString(IIdentity identity) { return string.Format("{0} {1} {2}", identity.Name, identity.IsAuthenticated, identity.AuthenticationType); } static void Main(string[] args) { Console.WriteLine(ToString(WindowsIdentity.GetCurrent())); Helper.ImpersonateAnonymousUser(); Console.WriteLine(ToString(WindowsIdentity.GetCurrent())); } 

Output:

 my machine\me True NTLM NT AUTHORITY\ANONYMOUS LOGON False 

In response to Edmund's comment, setting proxy.ClientCredentials.Windows.ClientCredential to null will not do the indentation of making the request an anonymous user. Here is my complete test code and its output:

Service Code:

 public class Service1 : IService1 { // note that if client is not authenticated, this code will never get a chance to execute // exception will happen before that // therefore there is no need to decorate this method with a // [PrincipalPermission(SecurityAction.Demand, Authenticated=true)] attribute public string GetData() { try { var identity = Thread.CurrentPrincipal.Identity; return string.Concat(identity.Name, ",", identity.IsAuthenticated, ",", identity.AuthenticationType); } catch (Exception e) { return string.Concat(e.Message, "\\r\\n", e.StackTrace); } } } 

Service Configuration:

 <services> <service name="WcfService1.Service1"> <host> <baseAddresses> <add baseAddress="http://mymachine/Service1/" /> </baseAddresses> </host> <endpoint address="Service1" binding ="customBinding" bindingConfiguration="myHttpBinding" contract="WcfService1.IService1"> </endpoint> </service> </services> <bindings> <customBinding> <binding name="myHttpBinding"> <reliableSession/> <binaryMessageEncoding /> <httpTransport maxBufferSize="2147483647" maxReceivedMessageSize="2147483647" authenticationScheme="IntegratedWindowsAuthentication" /> </binding> </customBinding> </bindings> 

Client Code:

 static void MakeRequest() { try { using (var svc = new Service1Client()) { Console.WriteLine(svc.GetData()); } } catch (Exception e) { Console.WriteLine(e.Message); Console.WriteLine(e.StackTrace); } } static void Test3() { Console.WriteLine("using {0}", ToString(WindowsIdentity.GetCurrent())); MakeRequest(); Console.WriteLine(); Console.WriteLine("setting svc.ClientCredentials.Windows.ClientCredential to null..."); try { using (var svc = new Service1Client()) { svc.ClientCredentials.Windows.ClientCredential = null; Console.WriteLine(svc.GetData()); } } catch (Exception e) { Console.WriteLine(e.Message); Console.WriteLine(e.StackTrace); } Console.WriteLine(); ImpersonateAnonymousUser(); Console.WriteLine("using {0}", ToString(WindowsIdentity.GetCurrent())); MakeRequest(); Console.WriteLine(); } 

Client Configuration:

 <bindings> <customBinding> <binding name="CustomBinding_IService1"> <reliableSession /> <binaryMessageEncoding /> <httpTransport authenticationScheme="Negotiate" /> </binding> </customBinding> </bindings> <client> <endpoint address="mymachine/Service1/Service1.svc/Service1" binding="customBinding" bindingConfiguration="CustomBinding_IService1" contract="ServiceReference1.IService1" name="CustomBinding_IService1"> <identity> <servicePrincipalName value="host/mymachine" /> </identity> </endpoint> </client> <behaviors> <endpointBehaviors> <!-- this setting protects the client by prohibiting the service to assume identity of client via imperonation and/or delegation and then doing bad things --> <behavior name="ImpersonationBehavior"> <clientCredentials> <windows allowedImpersonationLevel="Identification"/> </clientCredentials> </behavior> </endpointBehaviors> </behaviors> 

Output:

 using mymachine\me True Negotiate mymachine\me,True,Negotiate setting svc.ClientCredentials.Windows.ClientCredential to null... mymachine\me,True,Negotiate using NT AUTHORITY\ANONYMOUS LOGON False The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state. Server stack trace: at System.ServiceModel.Channels.CommunicationObject.Close(TimeSpan timeout) Exception rethrown at [0]: at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage req Msg, IMessage retMsg) at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgDa ta, Int32 type) at System.ServiceModel.ICommunicationObject.Close(TimeSpan timeout) at System.ServiceModel.ClientBase`1.System.ServiceModel.ICommunicationObject. Close(TimeSpan timeout) at System.ServiceModel.ClientBase`1.Close() at System.ServiceModel.ClientBase`1.System.IDisposable.Dispose() at TestClient.Program.MakeRequest() 
+3
source

Generally speaking, foo does not exist. Because NetworkCredential is a wider entity, which is WindowsIdentity. That is, I can use NetworkCredential where I need WindowsIdentity, but not vice versa.

The reason is security.

NetworkCredential means that you are allowed to use this identifier and use the AUTHORITIES associated with it on the machine OTHER.

It means that

  • You have user credentials, not just his identity.
  • This credential set is suitable for impersonation, not just local access.

I assume the credentials came from another machine (due to WCF). Again, for security reasons, this network account was converted to LocalCredential when switching to this computer (unless it is a matter of delegation, not impersonation). GAVE client machine has the right to use its credentials on the server and only on the server.

If you want to return NetworkCredential, you need to do something called delegation, because of what is called a Multi-Hop problem. This includes Kerberos, the three-headed evil dog of the underworld. You do not want to deal with Kerberos.

Generally speaking, by default, WCF proxies do not send credentials with their calls. Usually you need to install ClientCredentials or install

 proxy.UseDefaultCredentials = true 

It does not provide either normal or no credentials, and therefore an anonymous authorizer.

+3
source

We have a set of tests that we do, and we use this helper method:

 public static ResponseType CallService(RequestType requestBody, NetworkCredential credential) { ResponseType response; using (var channelFactory = new ChannelFactory<IMyService>("BasicHttpBinding_IMyService")) { channelFactory.Credentials.Windows.ClientCredential = credential; var client = channelFactory.CreateChannel(); var request = new MyRequest() { MyService = requestBody }; response = client.MyService(request).MyResponse1; ((IClientChannel)client).Close(); channelFactory.Close(); } return response; } 

use in tests:

 var requestBody = GetRequestBody();//another helper method var credential = new NetworkCredential { Domain = "MyDomain", UserName = "MyUsername", Password = "MyPassword" }; var response = CallService(requestBody, credential); //Assert various user credentials var response = CallService(requestBody, null); //Assert anonymous 
0
source

All Articles