The caller (application) determines the maximum token size for a security package during initial setup by calling the QuerySecurityPackageInfo() function for a specific security package, and using the returned cbMaxToken value.
It is based on this value that the application then initializes the buffer pointers and sizes in the buffer description.
NOW, as you may know, the default maximum token size for the Kerberos authentication package (i.e. Kerberos SSP) was 8000 bytes in Windows 2000 and is 12,000 bytes in Windows Server 2003/8.
As a result, when the caller (application) calls QuerySecurityPackageInfo() function to determine the maximum token size for the Kerberos package, the cbMaxToken value that is returned to the user is 8000 bytes in Windows 2000 and 12,000 bytes in Windows Server 2003/8. It is based on this return value that the caller initializes buffer pointers and sizes.
The client and server then each call AcquireCredentialsHandle(), following which the client calls InitializeSecurityContext() and the server calls AcquireSecurityContext(). Under the hood, the Kerberos SSP on the client's machine takes care of requesting a session ticket from the KDC and sending it across to the server, and the Kerberos SSP on the server's machine takes care of receiving and decrypting the ticket. If all's well thus far, the server can then call ImpersonateSecurityContext().
Under the hood, during the above, when the KDC receives the session ticket request, it generates and inserts the list of SIDS of all the global, universal and domain local security groups that the user is a member of directly or indirectly, (plus SIDHistory) in the user's Privilege Attribute Certificate (PAC), which is stored in the Authorization-Data (Optional) field of the service ticket that the KDC issues to the client for submission to the server. (As you may know, strictly speaking, it copies the PAC from the client's TGT into the service ticket's PAC, then adds the list of all DLGs to which the user directly or indirectly belongs, to it.)
Then, at the server side, when the Kerberos SSP receives and decrypts the service ticket, it verifies/validates the PAC and then generates and return an output security token via the output buffer that was initialized and provided by the caller (application).
Now, remember that the size of this output buffer was based on the value of the maximum token size provided to the caller when it called the QuerySecurityPackageInfo() function to query the cbMaxToken size for the Kerberos package.
If the user belongs to a large enough number of groups, the amount of memory needed for the output security token (containing the SIDs of all the security groups to which the user belongs) will exceed the amount of space allocated for the output buffer, and thus the authentication request will fail, and as a result, the user will not be able to successfully logon.
In essence, the crux of the issue is that the amount of memory (default maximum token size) that the caller ends up allocating for the output buffer could be insufficient to hold the resulting output security token when the number of SIDs is large enough, and this can cause the authentication request to fail, resulting in a logon failure.
An Example of Kerberos Token Bloat
This issue is perhaps best illustrated with an example, so I’ll share an example with you which covers 2 scenarios –
- A domain user account holder U from domain A logs to a machine Ma, wherein this machine Ma also belongs to the domain A (i.e. is domain-joined to domain A)
- This same user U then proceeds to use a client-server application with the server component hosted on a machine Mb, wherein machine Mb is domain-joined to domain B
Scenario 1: Interactive Logon to a machine on Domain A
Here is (roughly) what happens when domain user account A attempts to interactively logon to domain-joined machine Ma–
- Domain user account holder U invokes the Secure Attention Sequence (SAS) on the machine by pressing Alt-Ctrl-Del.
- Upon invocation of the SAS, Winlogon switches to the logon desktop, and dispatches GINA to collect the logon data.
- GINA collects and returns the user’s logon data to Winlogon, which then sends the data to the LSA by calling LsaLogonUser.
- The LSA immediately converts user’s plaintext password to a secret key by passing it through a one-way hashing function.
- The LSA then creates pre-authentication data by encrypting a timestamp with the secret key derived from the user’s password.
- The LSA then invokes the Kerberos SSP and sends a KRB_AS_REQ message (which includes this pre-authentication data) to the user’s KDC authentication service.
- The KDC’s authentication service uses the user’s identity (UPN) contained in the KRB_AS_REQ to locate the user in its account database (Active Directory).
- The KDC then uses the account’s hashed password to attempt to decrypt the pre-authentication data.
- If the KDC is able to successfully decrypt the pre-authentication data, it then proceeds to evaluate the timestamp. If the timestamp passes the test, the KDC can authenticate the user.
- If the user is authenticated, the KDC then identifies all the universal and global groups to which the user belongs (directly or indirectly) and packages them in a PAC.
- The KDC then builds a TGT for the user, inserts this PAC in it, and replies back with a KRB_AS_REP message, which includes this TGT for the user.
- The LSA then sends a KRB_TGS_REQ message to the KDC’s ticket-granting service to request a session ticket for the user for admission to this computer.
- Since this computer belongs to the same domain, the KDC then identifies all the domain local security groups (in this domain) to which the user belongs (directly or indirectly.)
- It then copies the data from the PAC stored in the TGT into a new PAC created for this session ticket, and it adds all the identified domain local security groups to this PAC.
- It then creates a service ticket good for admission to this computer, inserts the new PAC in it, and replies back with a KRB_TGS_REP message, which contains this service ticket.
- Upon receipt of the user’s session ticket for this computer, the LSA decrypts it with the computer’s secret key, and extracts the PAC (which contains the user’s authorization data.)
- It then queries the local Security Accounts Manager (SAM) database to determine if the user is a member of any machine local security groups (directly or indirectly.)
- If it finds any local security groups that the user might be a member of, it then adds this list of groups taken from the PAC.
- It then creates an access token for this user and passes it back to Winlogon. (Technically it creates a logon session, and the access token is associated with the logon session.)
- Winlogon then creates a window station and several desktop objects, attaches the user’s access token, and starts the shell process designated for this user.
The user’s access token is subsequently inherited by any application process that he starts during the logon session.
The key points to note here are that during the creation of the PAC for the session ticket –
- The Global and Universal Group memberships were taken from the PAC in the TGT
- Only those Domain Local Groups that belong to the computer’s domain (and of which the user is a member directly or indirectly) were included.
Now, let’s assume that the user was a member of 10 universal groups, 15 global groups and 25 domain local groups from the computer’s domain (which in this case is the same as the user’s domain).
In this case, the PAC in this Session Ticket contained 50 security groups in all, and that the resulting Kerberos token size would have been approximately 1200 + (40*25) + (8*(15+10)) bytes = 2400 bytes, based on Microsoft's recommended formula, details of which are provided below.
Since 2400 bytes is well below the 12000 byte MaxTokenSize limit, the user did not experience any issues with the logon.
Scenario 2: Network logon to a machine on Domain B
Now let us assume that immediately after logging on, the user U launches the client side of a client-server application, the server side of which is running on a domain joined machine Mb in domain B (i.e. the machine is joined to the domain B.)
Assume that this client-server application use SSPI for network authentication, prefer to use Kerberos, and that the client knows the SPN of the server-side of the application.
Here is (roughly) what happens under the hood in this scenario –
- The user launches the client side of the application.
- The client side of the application establishes a socket connection with the server
- The client and server sides each proceed to call QuerySecurityPackageInfo() to determine the max token size for Kerberos
- Then the client and server sides each proceed to call AcquireCredentialsHandle() passing in the name of the Kerberos package, to acquire a handle to their pre-existing credentials
- Then, the client calls InitializeSecurityContext() to give the SSP a chance to prepare an outgoing token (; this token is not to be confused with a “Windows access token”)
- The client then transmits the resulting token to the server
- When the server receives this token, it calls AcceptSecurityContext() to pass the token to its SSP. (This function may be called two times.)
- Upon a successful final pass of the AcceptSecurityContext() function on the server side, the system will generate a new network logon session for the client (i.e. the user)
- The server-side will then call ImpersonateSecurityContext() to ask the SSP to place a token for the client’s logon session (on the server)
- Finally when done, the server-side will call RevertSecurityContext() to discontinue the impersonation of the caller
Now, in case you find yourself wondering where the necessary Kerberos related exchanges are in the description above, the answer is that they are automatically handled by the SSP between steps 5 – 8 above since the SSP abstracts protocol specific details.
As it pertains to Kerberos, here’s (roughly) what happens under the hood between steps 5 through 8 above –
- The client sends a KRB_TGS_REQ to the client’s KDC client, presenting its TGT, an authenticator, and the name of the target server (the Server Principal Name or SPN.)
- The client’s KDC uses the SPN to determine that it is belongs to another realm, so it creates a TGT that the client can submit to a KDC in the server’s domain for requesting the service ticket
- Before returning this TGT to the user, the KDC copies the PAC from the initial TGT into this TGT
- The client then sends a KRB_TGS_REQ to the server’s KDC, presenting the new TGT, an authenticator, and the name of the target server (the Server Principal Name or SPN.)
- The server’s KDC verifies the TGT (since it was signed by the client’s KDC, and these two Kerberos realm KDCs trust each other) and proceeds to generate a service ticket to the client for the server
- Prior to the generation of the service ticket, the server’s KDC identifies all the domain local security groups from the server’s domain to which the user belongs (directly or indirectly.)
- It then copies the data from the PAC stored in the presented TGT into a new PAC created for this session ticket, and it adds all the domain local security groups identified above to this PAC.
- It then creates a service ticket good for admission to the server, inserts this new PAC in it, and replies back with a KRB_TGS_REP message, which contains this service ticket.
- Upon receipt of the user’s session ticket for the server, the client sends a KRB_AP_REQ message along with the ticket and a new authenticator, to the target server, requesting access.
- The server decrypts the ticket, validates the authenticator, and and extracts the PAC (which contains the user’s authorization data.)
- It then queries the local Security Accounts Manager (SAM) database to determine if the user is a member of any machine local security groups (directly or indirectly.)
- If it finds any local security groups that the user might be a member of, it then adds this list of groups taken from the PAC.
- It then creates an access token for this user. (Technically it creates a network logon session, and the access token is associated with this network logon session.)
Now, above we had assumed that the user was a member of 10 universal groups, 15 global groups and 25 domain local groups from the user’s domain (which in this case is the same as the user’s domain).
Now, let us also assume that this user also happens to be a member of 300 domain local security groups in the server’s domain.
In this case, the PAC in this Session Ticket will contain 325 security groups in all, and the resulting Kerberos token size would be approximately 1200 + (40*300) + (8*(15 + 10)) bytes = 13500 bytes.
Since 13500 bytes is above the 12000 byte MaxTokenSize limit, the user will actually not be able to logon and the Kerberos SSP will most likely throw an error during the AcceptSecurityContext() call on the server.
The end result will be that the user will not be able to successfully authenticate to the server, and if this server was running Windows Server 2008, you might see the following message in the Kerberos event log –
Event Details
Product: Windows Operating System
ID: 6
Source: Microsoft-Windows-Security-Kerberos
Version: 6.0
Symbolic Name: KERBEVT_INSUFFICIENT_TOKEN_SIZE
Message:
The Kerberos SSPI package generated an output token of size 34BC bytes, which was too large to fit in the token buffer of size 2EE0 bytes, provided by process id 0. The output SSPI token being too large is probably the result of the user U being a member of a large number of groups.It is recommended to minimize the number of groups a user belongs to. If the problem can not be corrected by reduction of the group memberships of this user, please contact your system administrator to increase the maximum token size, which in term is configured machine-wide via the following registry value: HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Kerberos\Parameters\MaxTokenSize.
Potentially Impacted Services in Windows
It is worth noting that in a Microsoft Windows Server environment, many distributed services use SSPI to access the Kerberos protocol. Here is a partial list of the various ways in which the Kerberos protocol is used for authentication -
- Print spooler services
- CIFS/SMB remote file access
- LDAP queries to the Active Directory
- Distributed file system management and referrals
- IPsec host-to-host security authority authentication
- Reservation requests for network Quality of Service
- Intranet authentication to Internet Information Services
- Remote server or workstation management using authenticated RPC
- Certificate requests to Certificate Services for domain users and computers