TokenGroup und MemberOf

Verschiedene Programme arbeiten mit Gruppenmitgliedschaften, um z.B. abhängig davon entsprechende Aktionen auszulösen. Über den Eintrag "MemberOf" eines Benutzers kann ein Programm aber nur die direkten Mitgliedschaften ermitteln. Oft ist es aber erforderlich die rekursive Mitgliedschaft zu ermitteln. Ein Programm könnte nun rekursiv die Mitgliedschaften durchlaufen, was aber lange dauert und viele LDAP-Anfragen liefert. Der DC selbst kann diese Funktion aber schon übernehmen und muss dies sowieso z.B.: für die Ermittlung von Berechtigungen machen. Und es ist bedeutend schneller diese Liste zu nutzen und bei Bedarf die SID auf den Namen aufzulösen als selbst rekursiv die Gruppenzugehörigkeit zu ermitteln.

Funktion von TokenGroup

Das LDAP-Feld "TokenGroup" kann wie jedes andere LDAP-Feld einfach gelesen werden. Da es aber normalerweise nicht in einer Standard Antwort enthalten ist, muss man dieses Feld explizit mit einem "GetEx" anfordern. Vorher macht sich der DC nicht die Mühe das Feld zu ermitteln. Als errechnetes Systemfeld kann es zudem nicht geschrieben werden. Die Basisfunktion unter VBScript könnte wie folgt aussehen:

Function GetTokenGroups(strdn)
   Dim adsObject
 
   Set adsObject = GetObject(strdn)
   adsObject.GetInfoEx Array("tokenGroups"), 0
   GetTokenGroups = adsObject.GetEx("tokenGroups")
End Function

Die gleiche Funktion in PowerShell sieht sehr ähnlich aus. Hier noch erweitert um die Ausgabe der Gruppen

$UserAccount = [ADSI]"LDAP://$Account"
$UserAccount.GetInfoEx(@("tokengroups"),0)
$groups = $UserAccount.Get("tokengroups")
write-host "Anzahl der Gruppen" $groups.count

Achtung:
Die Ausgaben sind nur dann akkurat, wenn Sie einen DC der Domäne nutzen, in der das Objekt angelegt ist. Dazu müssen Sie sich per LDAP verbinden. Wer einfach eine Suche per "GC" macht, erhält keine komplette Liste der Gruppen.

Will man noch mehr Details über die Gruppen selbst ermitteln, muss man aus der SID natürlich wieder eine Gruppe ableiten. Aber auch das ist recht einfach:

foreach($group in $groups) {
    $ID = New-Object System.Security.Principal.SecurityIdentifier($group,0)
    if($ID)
    {
        $groupname = $ID.Translate([System.Security.Principal.NTAccount])
        $groupname.Value
    }
}

Für die Tokengröße geht es aber auch einfacher, indem man die SID, welche man schon über die "Groups"-Auflistung erhält, zum Binden an den DC verwendet.

foreach($group in $groups) {
    $strSID = [System.BitConverter]::ToString($group).replace("-","")
    $grp = [ADSI]"LDAP://<SID=$strSID>"
    Write-Host "Group: " $grp.sAMAccountName
}

Achtung:
Wer in seinen AD-Konten noch SID-History-Einträge von vergangenen Migrationen hat, kann durchaus hier noch SIDs finden, die per LDAP nicht mehr aufzulösen sind.

Über das Property Grouptype der Gruppe bekommt man dann schnell heraus, ob es sich um eine lokale, globale oder universeller Gruppe handelt.

if ($grp.groupType.psbase.value -band 1){
	Write-Host " BuildIn" -NoNewline	
}
if ($grp.groupType.psbase.value -band 2){
	Write-Host " Global" -NoNewline	
}
if ($grp.groupType.psbase.value -band 4){
	Write-Host " Local" -NoNewline	
}
if ($grp.groupType.psbase.value -band 8){	
	Write-Host " universal" -NoNewline
}
if ($grp.groupType.psbase.value -band 0x80000000){	
	Write-Host " Securitygroup" -NoNewline
}
else {	
	Write-Host " Distributiongroup" -NoNewline
}

Dann muss man nur noch anhand der Tabelle die Token-Größe aufaddieren.

1200 für die Verwaltungsinformation
+ 40 * Anzahl der Mitgliedschaften in Domainlokalen Gruppen
+ 40 * Anzahl der Mitgliedschaften in universellen Gruppen anderer Domains + 40 * Anzahl der SIDs in der SIDHistory
+  8 * Anzahl der Mitgliedschaften in globalen Securitygruppen
+  8 * Anzahl der Mitgliedschaften in universellen Gruppen der eigenen Domain
=======================
Gesamtgröße des Tokens

Das bedeutet, dass man für universelle Gruppen der gleichen Domain oder globale Gruppen nur 8 Byte addieren muss und ansonsten immer 40 Bytes addiert. Das ganze dann in ein einfaches PowerShell-Skript verpackt finden Sie hier zum Download.

Dump-Ticketsize
PowerShell-Script zum Auslesen der Gruppenmitgliedschaften und Kerberos Ticketgröße

Sie können es einfach in einer PowerShell aufrufen. Wenn Sie die Ausgabe als CSV-Datei haben wollen, dann sind sie nur einen Export-CSV davon entfernt.

[PS]C:\>.\dumptokensize.ps1 | export-csv -path tokensize.csv

Welche Größe Sie als "kritisch" ansehen, hängt natürlich davon ab, an welche Stellen Sie schon verschiedene Grenzen angehoben haben. Details hierzu finden Sie auf Kerberos Ticketsize.

Limits mit Windows 2008

Als weiteren Schutz gegen DoS Attacken sind in Windows 2008 und 2008R2 einige Grenzwerte "hard" einprogrammiert, mit denen eine Software bei der Abfrage umgehen muss

Alternative mit LDAP_MATCHING_RULE_IN_CHAIN

Die Nutzung der TokenGroup liefert natürlich alle Sicherheitsgruppen, da nur diese eine SID haben. Einfache "Verteiler" sind nicht erfasst und fehlen daher in der Liste der Mitglieder. Über eine besondere Option bei der Suche können Sie zwar nicht die Liste Gruppen ermitteln, in der ihr Objekt enthalten ist, aber Sie können sehr wohl prüfen, ob jemand in einer Gruppe enthalten ist. Dazu gibt man den Wert ":1.2.840.113556.1.4.1941:" in der LDAP-Suche hinter dem Feld an, welches für das explizit angegebene Objekt gelesen werden soll.

$objDomain = New-Object System.DirectoryServices.DirectoryEntry("LDAP://rootDSE")
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = "LDAP://$($objDomain.rootDomainNamingContext)"
$objSearcher.PageSize = 1000


$objSearcher.Filter = "(memberof:1.2.840.113556.1.4.1941:=cn=User1,dc=msxfaq,dc=net)"
$objSearcher.SearchScope = "Subtree"
$objSearcher.PropertiesToLoad.Add("Name") | out-null
$colResults = $objSearcher.FindAll()
foreach ($objResult in $colResults) {
   $objItem = $objResult.Properties
   $objItem.name
}

Allerdings ist diese LDAP-Operation für einen Domain Controller sehr aufwändig. Wenn Sie nun also für jedes Objekte nacheinander so die Mitgliedschaften ermitteln wollen, dann sollten Sie besser den umgekehrten Weg gehen und die Mitgliedschaften der Gruppen selbst auslesen und aufaddieren. Um aber mal schnell ein einzelnes Objekte zu prüfen, kann man diesen einfacheren Weg nutzen.

Weitere Links