Hallo, hier ist mal wieder Andy aus dem Domain Team. Ich bin letztens mal wieder auf ein eigentlich banales Problem gestoßen, für das es aber nach meinen Recherchen keine fertige Lösung gibt.
Im Active Directory ist es möglich Gruppen in Gruppen zu verschachteln. Aber was passiert wenn eine Gruppe A Mitglied der Gruppe B ist und die Gruppe B ist Mitglied der Gruppe A. Eine klassische Endlosschleife, wenn man versucht die effektiven Benutzer einer Benutzergruppe zu analysieren.
LSASS.EXE (Local Security Authority Subsystem Service) von Windows besitzt intern verschiedene Methoden um solche Schleifen zu erkennen und aufzulösen, jedoch liefert der LSASS Prozess keine Informationen zurück, wenn eine Schleife entdeckt wurde und eine "Group Loop“ abgebrochen wurde. So macht LSASS aus dem Umstand selbst keine Endlosschleife, welche zu dauerhaft 100% CPU Last führen würde, da es sich die Gruppenmitgliedschaften beim ersten Durchlauf merkt und bei einem Vergleich im nächsten Lauf dann nicht weiter verfolgt. So wird auch verhindert, daß später das Token mit duplizierten Gruppen SIDs an die Kerberos Ticket oder sogar LSA Limits gebracht würde. Nähere Informationen hierzu gibt es in dem Whitepaper AccessTokenLimitation.doc auf http://www.microsoft.com/downloads/en/details.aspx?FamilyID=22dd9251-0781-42e6-9346-89d577a3e74a&DisplayLang=en.

In Anmeldescripts hingegen, die einfach nur dem nächsten MemberOf folgen, kann eine solche "Group Loop" schnell zu unangenehm hoher CPU Last von LSASS auf den Domain Controllern führen. Solche Schleifen bei jeder Useranmeldung zu vermeiden, macht also durchaus Sinn.

Die Schleife zu erkennen ist ja noch relative einfach, wenn es sich nur um 2 Gruppen handelt. Aber was passiert in sehr komplexen Umgebungen, in der die Verschachtelung von Gruppen über viele Ebenen hinweg passiert. Hier manuell eine Schleife zu entdecken ist fast unmöglich.
In C# war das Problem schnell gelöst. Jedoch stellte sich die Frage wie kann man diese Fragestellung in einem Powershell Script lösen. Also warum nicht die Stärken von C# in der Powershell nutzen.

Also den C# Code in einer Datei abspeichern mit dem C# Compiler im .NET Framework kompilieren und eine DLL Assembly erstellen (der C# Code für die DLL searchgrouploop.dll ist als Attachment dem Blog beigefügt, ohne dem geht es leider nicht).

Das Kompilieren des Codes erfolgt mit dem Befehl:
csc /target:library searchgrouploop.cs

Jetzt noch die DLL in die Powershell importieren:
Import-Module <Path>\searchgrouploop.dll
und dann damit das Active Directory nach looped groups durchsuchen.

Das passende Powershell Script hierzu könnte dann so aussehen:

$objSL = New-Object groupld.Loopdetect
[int] $errConnect = 0
[string]$errMsg = ""
if ($objSL.Connect([ref] $errConnect,[ref]$errMsg))
{
   if ($objSL.GetGroups([ref] $strGroups,[ref] $errConnect, [ref] $errMsg))
   {
      foreach ($strDN in $strGroups)
      {
         [string]$strLoopGroup=""
         [string]$strLoopSource=""
         if ($objSL.SearchLoop($strDN, "",[ref] $strLoopGroup, [ref] $strLoopSource)) 
         {
            write-host "Group loop found " $strLoopGroup " in " $strloopSource"
         }
      }
   }
   else
   {
       write-host "Can't enumerate domain groups error (" $errConnect ") " $errMsg
    }
}
else
{
   write-host "Can't connect to LDAP Server error (" $errConnect ") " $errMsg 
}

Ich hoffe trotz des Ausfluges ins Coding was das ein nützlicher Beitrag. Natürlich gilt wie immer bei solchen Themen, bitte ausgiebig testen. Script und Code verstehen sich als reine Samples. Microsoft übernimmt keinen Support oder entsprechende Gewährleistung.
Gutes Gelingen, Andy