Monday, May 21, 2012

Kerberos Token Size Script

As Active Directory infrastructures age and groups accumulate on user accounts, a users Kerberos token size increases. When the Kerberos token grows beyond the maximum allowed size, authentication problems abound and there are all manner of unfortunate side-effects.

There are several great KB articles that go into to detail about the Kerberos token size problem and potential resolutions. Two of the better ones are:
  http://support.microsoft.com/kb/327825
  http://blogs.technet.com/b/surama/archive/2009/04/06/kerberos-authentication-problem-with-active-directory.aspx
So I'm not going to rehash the details of these articles, but instead focus on identifying whether or not you have a Kerberos token size problem.

To answer that question, we'll turn to PowerShell. From KB327825, we know that we can estimate the token size using the following formula:
    TokenSize = 2*(1200 + 40d + 8s)

d = # of domain local groups + # of universal groups outside the user's domain + # of groups in SID history.
s = # of security global groups + # of universal groups in a user's domain
1200 = Estimated value for ticket overhead.
2* = When users authenticate to a domain controller, Microsoft recommends that you double the estimate.

In most cases I've dealt with, group accumulation was the primary culprit, so to simplify the script we'll leave out the SID History and external domains.

This script takes a user account as an argument, and then looks at its group membership to create an estimated token size:


# Estimates Kerberos Tickets Size based upon the formula here:
# http://support.microsoft.com/kb/327825
#   TokenSize = 1200 + 40d + 8s
#   This formula uses the following values:
#   d: The number of domain local groups a user is a member of plus the number of universal groups outside the user's account domain plus the number of groups represented in security ID (SID) history.
#   s: The number of security global groups that a user is a member of plus the number of universal groups in a user's account domain.
#   1200: The estimated value for ticket overhead. This value can vary depending on factors such as DNS domain name length, client name, and other factors.
#   In scenarios in which delegation is used (for example, when users authenticate to a domain controller), Microsoft recommends that you double the token size.
# Created By: Jeremy Jackson (aisforaction@gmail.com)


if (!$Args[0]) {
write-host "Usage: .\UserTokenEstimate.ps1 <username>"
break
}


$username=$Args[0]
$groupfile = $username + "_groups.txt"
$tokenfile = $username + "_tokeninfo.txt"


function global:Count-Groups {


param($CN)


$LDAP="LDAP://"+$CN

write-host $CN
$CN  | out-file $groupfile -append

$user = [adsi]$LDAP
$groupSearcher = New-Object DirectoryServices.DirectorySearcher($user)
$groupSearcher.PageSize = 1000
$results = $groupSearcher.FindOne()


if ($results.properties.item("groupType") -eq "-2147483646") {$Type="Global"}
if ($results.properties.item("groupType") -eq "-2147483644") {$Type="DomainLocal"}
if ($results.properties.item("groupType") -eq "-2147483640") {$Type="Universal"}


if ($Groups -notcontains $CN) {
$script:Groups += $CN
if ($Type -eq "Global") {$script:GlobalGroups += $CN}
if ($Type -eq "DomainLocal") {$script:DomainLocalGroups += $CN}
if ($Type -eq "Universal") {$script:UniversalGroups += $CN}
}


foreach ($Group in $results.properties.item("memberOf")) {
if ($Groups -notcontains $Group) {
Count-Groups($Group)
}
}
}


function global:Estimate-Token {


param($Name,[string]$ADSPath)

$ADSPath = $ADSPath.Replace("LDAP://","")

Set-Variable -Name Groups -Value @() -Scope Script
Set-Variable -Name GlobalGroups -Value @() -Scope Script
Set-Variable -Name DomainLocalGroups -Value @() -Scope Script
Set-Variable -Name UniversalGroups -Value @() -Scope Script


Count-Groups $ADSPath


$TokenSize =  1200 + ($DomainLocalGroups.Count * 40) + ($UniversalGroups.Count * 40) + ($GlobalGroups.Count * 8)
$DCTokenSize = $TokenSize * 2


[string]$TokenInfo = $Name + "," + $TokenSize + "," + $DCTokenSize + "," + $Groups.Count + "," + $DomainLocalGroups.Count  + "," + $UniversalGroups.Count + "," +  $GlobalGroups.Count + "," + $ADSPath

write-host $TokenSize : $ADSPath

$TokenInfo.Replace(" ","") | out-file $tokenfile -append


}


$Header = "Name,Token Size,Double Token Size,Groups,Domain Local Grops,Universal Groups,Global Groups"
$Header | out-file $tokenfile -append


$strFilter = "(&(objectCategory=person)(objectClass=user)(name=$username))"


$objDomain = New-Object System.DirectoryServices.DirectoryEntry


$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.PageSize = 1000
$objSearcher.Filter = $strFilter
$objSearcher.SearchScope = "Subtree"


$objSearcher.PropertiesToLoad.Add("name")


$colResults = $objSearcher.FindAll()


foreach ($objResult in $colResults) {
$objItem = $objResult.Properties
Estimate-Token $objItem.name $objItem.adspath
}

No comments:

Post a Comment