0
votes

I’m developping an « auto-connect » mode between a WPF application and a Windows service throw WCF. I want to allow only trusted domain users to connect to a particular WCF service, without the need of windows credentials (the user is already logged on his windows session). This service will next log the user to the application.

I’ve created a WCF service with Security mode to « Transport » and Transport.ClientCredentialType to « Windows » (same binding on both client & server). There are other WCF services running in the same application, with security mode set to « None ».

When I run the windows service on a domain machine under LocalService account, and run the client on another machine under a domain user account, I can get the user Windows Identity « DOMAIN\user » on the server side in the OperationContext object. I see that Kerberos is used, and I think there is some kind of impersonation, wich is ok.

But when I run the windows service on a Workgroup machine (out of domain), where no domain user is known, the OperationContext WindowsIdentity is set to « MACHINE_NAME\Administrator » (the local session created on the server). There is no error, no exception on the service call, and I can’t figure out why. The IsAuthenticated property on WindowsIdentity object is always set to true.

I’ve already tested with security mode to « Message ».

I wanted to know if there is a possibility to tell WCF to reject ALL connections that are not truly authentified ?

UPDATE: here some informations that might help:

  • Client app is a WPF application
  • Server is a classic Windows service, running under LOCAL SERVICE
  • Bindings are net.tcp
  • Client proxy classes are generated via svcutil.exe
  • Everything is done by code, no config file
  • Here is the code creating a ServiceClient:
 public static TClient Create<TClient, TChannel>(params IContractBehavior[] behaviors)
          where TChannel : class
          where TClient : ClientBase<TChannel>
        {
            var typeOfClient = typeof(TClient);
            var ctor = typeOfClient.GetConstructor(new[] { _bindingType, _endpointAddressType });
            if (ctor == null)
            {
                throw new Exception($"{typeOfClient} has no constructor taking 2 parameters ({_bindingType},{_endpointAddressType})");
            }
            var address = getClientBaseEndPointAddress(typeof(TChannel));
            var binding = getBinding<TClient>(address.Uri.Scheme);
            var clt = (TClient)ctor.Invoke(new object[] { binding, address });
            foreach (var behavior in behaviors)
            {
                clt.ChannelFactory.Endpoint.Contract.Behaviors.Add(behavior);
            }
            manageDataContractResolver<TClient, TChannel>(clt);
            clt.setOperationTimeout(binding.SendTimeout);
            return clt;
        }
  • getBinding method is shared by Client & Server app:
   var ret = new NetTcpBinding
   {
      MaxBufferSize = bc.MessageSize,
      MaxBufferPoolSize = bc.MessageSize,
      ...        
   };

   // Secured or not
   if (secured)
   {
      ret.Name = "Default_Secured_Binding";
      ret.Security.Mode = SecurityMode.Transport;
      ret.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;      
    }
    else
    {
       ret.Security.Mode = SecurityMode.None;
       ret.Name = "Default_Binding";
    }

I thought WCF would reject any request that are not fully authenticated, but I can't get any "authentication error".

Let me know if you need more information.

1

1 Answers

0
votes

If the client does not provide a windows credential as the client windows credential, then the current computer user’s credential will be used to be authenticated. The condition for authentication is that this credential can be logged on to the server (windows user)
How does the client call the service, client proxy class or ChannelFactory? Here is the approach to set up the credential on the client side.

ServiceReference2.Service1Client client = new ServiceReference2.Service1Client();
            //if we don't set up this, The computer user credentials that are currently running the program will be sent to the server.
            client.ClientCredentials.Windows.ClientCredential.UserName = "administrator";
            client.ClientCredentials.Windows.ClientCredential.Password = "123456";

provided that the server side configuration is below,

  <system.serviceModel>
    <services>
      <service name="WcfService1.Service1">
        <endpoint address="" binding="wsHttpBinding" contract="WcfService1.IService1" bindingConfiguration="mybinding">
        </endpoint>
        <endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange"></endpoint>
      </service>
    </services>
    <bindings>
      <wsHttpBinding>
        <binding name="mybinding">
          <security mode="Transport">
            <transport clientCredentialType="Windows"></transport>
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>

Please post your complete server configuration and the client configuration(system.servicecmodel section) for call, I would try to locate the problem.
Feel free to let me know if there is anything I can help with.