I’m having a problem at work in that our app which uses WCF for SSO is failing when using selective authentication for a domain trust when communicating with the other domain. This is on Server 2k8R2 machines, at a full 2k8R2 functional level for both of their domains (this is a test system, because we have a customer that wants to deploy this type of thing).
Basically, we have two domains, call them A and B. When we do a full two-way EXTERNAL (not forest) trust between the domains, the application works fine (after putting the users in appropriate groups on the other domain of course). Then we flipped the relationship from “Domain-wide” authentication to “Selective Authentication.” According to some doc we’ve read, we need to add the users to each computer’s entry in AD directly, and give them the “Allowed to authenticate” permission.
It didn’t work.
Further, then we saw somewhere ELSE that implied that we had to also give them the same permissions on the DCs themselves. So that was done. Again, failure.
The exception thrown by the application is as follows (I dumped it to a file)
A call to SSPI failed, see inner exception. Stacktrace:
Server stack trace:
at System.ServiceModel.Channels.WindowsStreamSecurityUpgradeProvider.WindowsStreamSecurityUpgradeInitiator.OnInitiateUpgrade(Stream stream, SecurityMessageProperty& remoteSecurity)
at System.ServiceModel.Channels.StreamSecurityUpgradeInitiatorBase.InitiateUpgrade(Stream stream)
at System.ServiceModel.Channels.ConnectionUpgradeHelper.InitiateUpgrade(StreamUpgradeInitiator upgradeInitiator, IConnection& connection, ClientFramingDecoder decoder, IDefaultCommunicationTimeouts defaultTimeouts, TimeoutHelper& timeoutHelper)
at System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.SendPreamble(IConnection connection, ArraySegment`1 preamble, TimeoutHelper& timeoutHelper)
at System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.DuplexConnectionPoolHelper.AcceptPooledConnection(IConnection connection, TimeoutHelper& timeoutHelper)
at System.ServiceModel.Channels.ConnectionPoolHelper.EstablishConnection(TimeSpan timeout)
at System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.CallOnceManager.CallOnce(TimeSpan timeout, CallOnceManager cascade)
at System.ServiceModel.Channels.ServiceChannel.EnsureOpened(TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
(A couple of calls in our own code below here)
InnerException: System.Security.Authentication.AuthenticationException: A call to SSPI failed, see inner exception. ---> System.ComponentModel.Win32Exception: Unknown error (0xc0000413)
--- End of inner exception stack trace ---
at System.Net.Security.NegoState.ProcessAuthentication(LazyAsyncResult lazyResult)
at System.Net.Security.NegotiateStream.AuthenticateAsClient(NetworkCredential credential, String targetName, ProtectionLevel requiredProtectionLevel, TokenImpersonationLevel allowedImpersonationLevel)
at System.ServiceModel.Channels.WindowsStreamSecurityUpgradeProvider.WindowsStreamSecurityUpgradeInitiator.OnInitiateUpgrade(Stream stream, SecurityMessageProperty& remoteSecurity)
So, basically that’s it. Some type of “AuthorizationException” is occurring. As I said, it doesn’t happen when it’s a domain-wide two-way trust, so we think we’re missing some setting for what to “re-enable” to make this work. Does anybody know trusts well enough to point us in the right direction for what to enable for selective authentication and make SSO/SSPI work for this? It DID work already on the domain-wide, but what do we have to open up to make it work for selective (and preferrably for ONLY the users we want it to work for)?
Thanks.
After a call to Microsoft, this was resolved: we hadn’t put the “Allowed to Authenticate” permission on a number of the users running services on the host machines. The documentation all referred to having to give your clients that permission on the machines you are accessing (including the DC in some cases), but said nothing about also having to add your remote clients to the “security” tab of the host user running the services.
So I’ll summarize with some contrived user, domain, and machine names, as well as one actual service account which exists by default.
“A” domain: The domain where the client is trying to connect from
“B” domain: This is the domain where a machine is hosting the service being connected to by a user in domain A.
Client@A: user from the A domain connecting to a service in the B domain.
ServiceAccount@B: user from the B domain that is hosting the WCF service that Client@A is connecting to.
krbtgt@B: this is a built-in user who’s description is “Key Distribution Center Service Account”. It will be under Active Directory Users and Computers under “Users” as long as you have “Advanced Features” enabled under the “view” menu. It won’t show up at all unless you have done that.
B-DC: The domain controller of domain B
B-host: The host machine for the WCF service we’re connecting to.
So, to let Client@A connect to the WCF service being run on B-host by user ServiceAccount@B by using SSPI/Windows authentication, on an external trust with Selective Authentication, you need to do the following:
NOW it should work. It’s weird you need all that just to run a windows-authenticated (so not certificates) WCF connection across the boundary like that, but there you go. You need to add your remote user to FOUR different objects in AD to get it to work. Presumably adding the remote user to some domain-local groups in domain B that had the right permissions would also work, but that’s not tested yet.
Edit: and then get krbtgt overwritten by the PDC Emulator with the rights on AdminSDHolder in under an hour. Every hour.
Basically, krbtgt is super-protected. You have to alter AdminSDHolder, and it propogates out a lot. The command to add JUST “allowed to authenticate” is:
Look up the “dsacls” tool if you want more info on what it does. It’s pretty handy for both altering the access control lists, and printing them out. Better than the GUI in fact.
Anyways, this actually works now. There’s the FULL answer.