Active Directory has a global catalog (GC), which contains a partial replica of all objects in the directory. It also contains partial replicas of the schema and configuration containers. One or more domain controllers in a domain can hold a copy of the global catalog.
The global catalog holds a replica of every object in Active Directory, but with only a small number of their attributes. The attributes in the global catalog are those most frequently used in search operations, such as a user’s first and last names, login names, and so on. The global catalog attributes also include those required to locate a full replica of the object. The global catalog allows users to quickly find objects of interest without knowing what domain
holds them and without requiring a contiguous extended namespace in the enterprise; that is, you can search the entire forest.
The following sample code demonstrates a way to check if a caller is in a perticular security group. It searches the global catalog to obtain the group’s SID(Security Id), and then call CheckTokenMembership to find out if the caller is in that group. It is very useful when a service process need to check a caller’s membership for authentication or authorization.
In real applications, you may need to cache the SIDs of the groups for better performance since searching the global catalog is fast, but not that fast.
using System; using System.IO; using System.Text; using System.Collections; using System.DirectoryServices; using System.Security.Principal; using System.Runtime.InteropServices; namespace IsInGroup { /// /// Summary description for Class1. /// class Class1 { [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)] static extern int CheckTokenMembership(int TokenHandle, byte[] PSID, out bool IsMember); [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)] static extern bool IsValidSid(byte[] PSID); public static string GetCallerName() { WindowsPrincipal principal = new WindowsPrincipal(WindowsIdentity.GetCurrent()); return principal.Identity.Name.Split('\\')[1]; } /// /// The main entry point for the application. /// static void Main(string[] args) { string groupName = "administrators"; //get current caller's information string user = GetCallerName(); //search global catalog and get SID of the group Byte[] sid = null; DirectoryEntry entry = new DirectoryEntry("GC:"); IEnumerator ie = entry.Children.GetEnumerator(); ie.MoveNext(); DirectorySearcher ds = new DirectorySearcher((DirectoryEntry)ie.Current); string sam = "(sAMAccountName=" + groupName + ")"; ds.Filter = "(&(|" + sam + ")(objectClass=group))"; using (SearchResultCollection resColl = ds.FindAll()) { if (resColl.Count > 0) { ResultPropertyCollection resultPropColl = resColl[0].Properties; string name = (string)resultPropColl["name"][0]; string adsPath = (string)resultPropColl["adspath"][0]; sid = (byte[])resultPropColl["objectsid"][0]; if (sid == null || !IsValidSid(sid)) { System.Console.WriteLine("Invalid SID for the group."); return; } else { System.Console.WriteLine("Group found: " + name); System.Console.WriteLine("Group ADS path: " + adsPath); } } else { System.Console.WriteLine("Required info was not found in the GC."); return; } } bool bIsMember = false; if (CheckTokenMembership(0, sid, out bIsMember) == 0) { System.Console.WriteLine("Failed to call CheckTokenMembership."); } else { System.Console.WriteLine(user + (bIsMember ? " is a member of " : " isn't a member of ") + groupName); } } } }