SharePoint: Facts and Troubleshooting the Claims To Windows Token Service (C2WTS)

Facts:

1. The Claims to Windows Token Service (from here on denoted as "C2WTS") is only used when SharePoint needs to get data from an external system that does not understand claims.  Examples include (but are not limited to):

  • SQL Server Reporting Services (SSRS)
  • Excel Web Services (EWS)
  • PowerPivot / Power View
  • Business Data Connectivity (BDC) / Business Connectivity Services (BCS) / External Content Types (ECT) / External Lists.
  • Other SharePoint "BI" stuff.
  • Custom Code that does user impersonation.

Normal SharePoint stuff (browsing sites, working in native lists / libraries, etc) does not use the C2WTS.

-- I've seen many cases where we've spent a lot of time configuring the C2WTS to try to fix some authentication or permissions issue, when in fact, the C2WTS doesn't even come into play in that scenario.

 

2. The C2WTS can only be accessed on the local server, so when you are using it,  it must be running on all of your WFEs and any other server that will access the external data.

 

3. It only works for Windows-Claims web apps.  It does not work for Trusted Provider / SAML claims (ADFS, Ping, SiteMinder, etc).  Even if you pass UPN as one of your claims, it will not work.

See this: https://technet.microsoft.com/en-us/library/ee806870.aspx#section2

Excerpt: "It is important to understand that these service applications can use the C2WTS only if the incoming authentication method is either Windows claims or Windows classic mode. Service applications that are accessed through web applications and that use Security Assertion Markup Language (SAML) claims or forms-based authentication claims do not use the C2WTS.Therefore, they cannot translate claims to Windows credentials."?

 

4. This is not necessarily an "error":

 11/09/2017 17:55:14.15 w3wp.exe (0x1A88) 0x2978 SharePoint Foundation Claims Authentication bz7l Medium SPSecurityContext: Could not retrieve a valid windows identity for username 'CONTOSO\User1' with UPN 'User1@contoso.com'. UPN is required when Kerberos constrained delegation is used. Exception: System.ServiceModel.FaultException`1[System.ServiceModel.ExceptionDetail]: WTS0003: The caller is not authorized to access the service. (Fault Detail is equal to An ExceptionDetail, likely created by IncludeExceptionDetailInFaults=true, whose value is: System.UnauthorizedAccessException: WTS0003: The caller is not authorized to access the service.
 at Microsoft.IdentityModel.WindowsTokenService.CallerSecurity.CheckCaller(WindowsIdentity callerIdentity)
 at Microsoft.IdentityModel.WindowsTokenService.S4UServiceContract.PerformLogon(Func`1 logonOperation, Int32 pid)
 at SyncInvokeUpnLogon(Object , Object[] , Object[] )
 at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
 at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
 at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
 at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc& rpc)
 at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet))..

Many times we see that in the ULS logs because there is some custom code in the master page or in a web part that calls an "impersonate" method, which results in calling into the Claims to Windows Token Service (C2WTS).  But C2WTS may not be required for that code to work, so even though it throws the above error, it causes no actual problem.

Before digging into this “error”, you must ask yourself: “Does what I’m troubleshooting even use the C2WTS?” (see Fact #1 above).

 

Proper Permission Configuration:

Most C2WTS issues I see are a problem with configuring the service, more specifically with properly configuring permissions for the account that runs it.

Here's a summary of how you should configure it:

Permissions Check list:
Reference here: https://technet.microsoft.com/en-us/library/hh231678(v=sql.110).aspx

Note: The end-to-end configuration for C2WTS will include setting up delegation on the C2WTS account.  These steps vary depending on what you're trying to get C2WTS to pull data from, and therefore are beyond the scope of this post.  However, here's an example of setting it up for SSRS.

For Users calling the service

(any user browsing the SharePoint page, web part, SSRS report, Excel spreadsheet, etc that connects to the back-end service that needs C2WTS)
○ Edit c2wtshost.exe.config at C:\Program Files\Windows Identity Foundation\v3.5 and make sure all users have access to call it.  -- Adding "NT AUTHORITY\Authenticated Users" to the allowed callers list ought to take care of that.
<allowedCallers>
<clear/>
<add value="NT AUTHORITY\Network Service" />
<add value="NT AUTHORITY\Local Service" />
<add value="NT AUTHORITY\System" />
<add value="NT AUTHORITY\Authenticated Users" />
</allowedCallers>

For the account running the C2WTS service (in services.msc):

○ On the SharePoint boxes running C2WTS:
§ Add to the local Administrators group
§ Add to the local security policy (Start > Administrative Tools > Local Security Policy > Local Policies > User Rights Assignment),
□ Act as part of the operating system
□ Impersonate a client after authentication
□ Log on as a service

○ On the domain(s):
§ Needs to have the “Read tokenGroupsGlobalAndUniversal” permission to all the users in the domain.
§ To give this permission, we need to add it to the “Windows Authorization Access Group” builtin domain group, which should have this permission to all domain accounts by default.
§ The account needs these permissions on every domain that contains the users that are calling the service.  So if you have a number of domain / forest trusts, and users from these other domains are browsing your SharePoint sites and using stuff that invokes the C2WTS, then you need to add your C2WTS service account to the “Windows Authorization Access Group” in every on of those domains.

Other notes for configuring C2WTS:

  • This service can only be accessed locally, so it needs to be running on all the machines in the farm that require it.  For example, in the case of SSRS, it needs to be running on the servers running the SSRS service.  Because it may not immediately be clear where you need the service in each scenario, it may be best to just run it on all servers in the farm.
  • Keep in mind that the c2wtshost.exe.config edit, and the local security policy changes listed above need to be made on every server running C2WTS.

Troubleshooting:

If you have configured permissions properly as described above but are still getting C2WTS errors, you can use the C2WTS tester tool:
https://blogs.msdn.com/b/rodneyviana/archive/2011/07/19/troubleshooting-claims-to-windows-nt-token-service-c2wts-in-sharepoint-2010-may-be-difficult-if-you-don-t-know-where-to-start.aspx

It’s called “c2WTSTest.zip” and you can download it here:

https://github.com/rodneyviana/blogdemos/blob/master/c2WTSTest.zip

-- Here's what the C2WTS tester tool looks like:
-By default, the tool runs as the logged-on user, but you can run it as another user by simply supplying a user name and password.
Note: This account is the user calling the service, which is different from account running the service.
-You also specify the UPN to try to convert to a Windows token.  This can be the UPN for the user running the tool, or some other UPN altogether.
-Here's what a successful run of the tool looks like:

-- As you can see it checks that the caller has permission per the "allowedCallers" tag in the c2wtshost.exe.config file, that the user can log in, and that the service account has permission to create the Windows token.

-- And here's an example of a failure:

Error Text:
***** c2WTS could not provide a valid Windows Token. Reason: Access is denied.

Server stack trace:
at System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter)
at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
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)
at Microsoft.IdentityModel.WindowsTokenService.S4UClient.IS4UService_dup.UpnLogon(String upn, Int32 pid)
at Microsoft.IdentityModel.WindowsTokenService.S4UClient.<>c__DisplayClass1.<UpnLogon>b__0(IS4UService_dup channel)
at Microsoft.IdentityModel.WindowsTokenService.S4UClient.CallService(Func`2 contractOperation)
at c2WTSTest.Form1.button2_Click(Object sender, EventArgs e)

-- It can be useful to take a Netmon (Network Monitor 3.4) trace while running the tester tool.
-The C2WTS uses Kerberos calls, so if you filter like tcp.port == 88, then you will see the request.

Here's an example of a failing sequence:

-- We can see it fails with “KDC_ERR_C_PRINCIPAL_UNKNOWN” which means “Client not found in Kerberos database".  The “client” in this case is the user we tried to generate a token for.

-- See: https://support.microsoft.com/kb/2009157

As specified above, the service account running the C2WTS needs to have the “Read tokenGroupsGlobalAndUniversal” permission to all the users in the domain (or at least all users that want to be able to invoke it and render their favorite SSRS reports).

To give this permission, we need to add it to the “Windows Authorization Access Group” builtin domain group.
By default the “Windows Authorization Access Group” has the “Read tokenGroupsGlobalAndUniversal” permission to all accounts in the domain.  If that has been removed that for some reason, we’ll need to add it or the equivalent permissions back.One way to check permissions in AD is to go to Active Directory Users and Computers | Properties for a user | Security | Advanced | Effective Access.  Choose the C2WTS service account  and make sure it has (at least) the “Read tokenGroupsGlobalAndUniversal” permission.

Here's an example where my service account (m1garandservice) does not have the correct permission to the User1 user account:

-- In this case, I reproduced this behavior with an explicit deny on the Read tokenGroupsGlobalandUniversal permission for the User1 account, but out in the wild, you'd likely see this because the C2WTS service account is not in the “Windows Authorization Access Group” for all applicable domains -- have I mentioned that's an important permission yet??

More from Rodney on C2WTS: https://blogs.msdn.com/b/rodneyviana/archive/2014/03/21/verifying-whether-the-broken-piece-is-c2wts-or-active-directory.aspx
- At its heart, C2WTS just calls:
WindowsIdentity s4u = new WindowsIdentity(upn);
WindowsImpersonationContext wic = s4u.Impersonate();- You can use his S4ULogonViaDotNet tool to test core functionality.

Full SharePoint ULS log example for the Access Denied error:

SPSecurityContext.WindowsIdentity: Could not retrieve a valid windows identity for NTName='CONTOSO\User1', UPN='user1@contoso.com'. UPN is required when Kerberos constrained delegation is used. Exception: System.ServiceModel.Security.SecurityAccessDeniedException: Access is denied.
Server stack trace:
at System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter)
at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
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)
at Microsoft.IdentityModel.WindowsTokenService.S4UClient.IS4UService_dup.UpnLogon(String upn, Int32 pid)
at Microsoft.IdentityModel.WindowsTokenService.S4UClient.<>c__DisplayClass1.<UpnLogon>b__0(IS4UService_dup channel)
at Microsoft.IdentityModel.WindowsTokenService.S4UClient.CallService(Func`2 contractOperation)
at Microsoft.SharePoint.SPSecurityContext.GetWindowsIdentity().Throwing Microsoft.ReportingServices.Diagnostics.Utilities.ClaimsToWindowsTokenException: , Microsoft.ReportingServices.Diagnostics.Utilities.ClaimsToWindowsTokenException: Cannot convert claims identity to windows token. ---> System.InvalidOperationException: Could not retrieve a valid Windows identity. ---> System.ServiceModel.Security.SecurityAccessDeniedException: Access is denied.
Server stack trace:
at System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter)
at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
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)
at Microsoft.IdentityModel.WindowsTokenService.S4UClient.IS4UService_dup.UpnLogon(String upn, Int32 pid)
at Microsoft.IdentityModel.WindowsTokenService.S4UClient.<>c__DisplayClass1.<UpnLogon>b__0(IS4UService_dup channel)
at Microsoft.IdentityModel.WindowsTokenService.S4UClient.CallService(Func`2 contractOperation)
at Microsoft.SharePoint.SPSecurityContext.GetWindowsIdentity()
--- End of inner exception stack trace ---
at Microsoft.SharePoint.SPSecurityContext.GetWindowsIdentity()
at Microsoft.ReportingServices.ServiceRuntime.WcfUserContext.GetWindowsIdentity()
--- End of inner exception stack trace ---;