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

Update: 3/18/24 — Added fact: the C2WTS is deprecated in SharePoint Server Subscription Edition.

Update: 3/31/22 — Added a reference to a related post from my colleague Mike: Unable to start the C2WTS

Facts:

1. In SharePoint Server Subscription Edition (SPSE), the C2WTS has been deprecated. The service still shows up in Central Administration, but the backing Windows service is not installed. Trying to start the service in Central Admin will result in this error: SPWindowsTokenServiceInstance: could not find c2wtshost.exe.config. Please edit the configuration manually.

SPSE replaced Windows Identity Foundation (WIF) 3.5 with WIF 4.5, in which the Claims to Windows Token Service (c2wts) and its associated classes in the Microsoft.IdentityModel.WindowsTokenService namespace are removed. See Guidelines for Migrating an Application Built Using WIF 3.5 to WIF 4.5 for more info. In other words, SPSE no longer uses the C2WTS. If you have custom solutions that depend on it, you may have some adjustments to make.

2. In SharePoint 2019 and below, 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 of features that can be configured to use C2WTS 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 those scenarios.

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

4. Out of the box, 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.”

** I found that certain custom claim providers may also contain the custom code necessary to make C2WTS work with their SAML claims. For example, the Okta farm-level solution contains such code. In that case, you must be passing UPN as one of the claims, and you must have custom farm-level property “MapUpnToWindowsUser” set. Reference.

5. 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.

Many times we see the above in the SharePoint 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 #2 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

By “calling the service”, I mean any user browsing the SharePoint page, web part, SSRS report, Excel spreadsheet, etc that connects to some back-end service that utilizes the C2WTS. These users directly call the service and need permission to do so.
Edit c2wtshost.exe.config at C:\Program Files\Windows Identity Foundation\v3.5\ and make sure all users have access to call the service.  — 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 one 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, you may choose to just run it on all servers in the farm.

Keep in mind that the c2wtshost.exe.config file edit, and the local security policy changes listed above need to be made on every server running C2WTS.

If you stop the C2WTS and start it again in Central Administration | Manage Services on Server, that will cause the c2wtshost.exe.config file to revert to its out-of-box version, and your users won’t be allowed access anymore. If you want to restart the service, just restart the Windows service within services.msc:

Important: If the Claims to Windows Token Service won’t start at all, you may have a problem within the c2wtshost.exe.config file. See this post from my colleague Mike Lee: Unable to start the 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://rodneyviana.com/troubleshooting-claims-to-windows-nt-token-service-c2wts-in-sharepoint-2010-may-be-difficult-if-you-dont-know-where-to-start/

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: http://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, or whatever you’re using C2WTS for).

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 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://rodneyviana.com/verifying-whether-the-broken-piece-is-c2wts-or-active-directory/

– 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 —;

One Comment

Add a Comment