Cover Image for Resolving nested AD groups completely over LDAP

Resolving nested AD groups completely over LDAP

The member attribute only returns direct members. How to resolve nested groups in Active Directory with the chain matching rule, what is missing for large and primary groups, and how it works in OpenLDAP.

5 minActive Directory

A group in Active Directory has three entries. The application reads the member attribute, finds three people and is done. In reality seven people belong to it, because two of the three entries are groups themselves. Nobody notices until a policy fails to apply to four people.

The problem with member

A group's member attribute lists its direct members: users, contacts, computers and other groups. It resolves nothing. Nesting is the norm in grown directories, for example department groups inside site groups inside a group for all staff.

The chain rule in Active Directory

Active Directory has its own matching rule for chains, LDAP_MATCHING_RULE_IN_CHAIN with the identifier 1.2.840.113556.1.4.1941. It follows a link across any number of levels.

All users who are direct or indirect members of a group:

ldapsearch -H ldaps://dc1.company.local -b "DC=company,DC=local" \
  "(&(objectCategory=person)(objectClass=user)(memberOf:1.2.840.113556.1.4.1941:=CN=FieldSales,OU=Groups,DC=company,DC=local))" \
  mail displayName

The other way round, the same rule on member returns all groups a user belongs to directly or indirectly. For a single user the constructed attribute tokenGroups is faster, read with scope base on the user object.

In PowerShell, Get-ADGroupMember -Identity FieldSales -Recursive does the same.

Three traps

Large groups

If a group has more members than the domain controller returns in one response, 1500 by default, member stays incomplete. The rest comes through range retrieval with member;range=0-1499, then member;range=1500-2999 and so on, until the response ends with an asterisk. The chain rule in the filter avoids this, because it searches for users instead of reading values.

The primary group

Users whose primaryGroupID points to a group are not in its member. For Domain Users that usually means everyone. The chain rule does not find them either. If you need primary groups, also search for primaryGroupID with the group's RID.

Members from other domains

Members from a trusted forest show up as objects under ForeignSecurityPrincipals with their SID. Their address lives in the other directory.

The chain rule is an Active Directory feature. OpenLDAP, 389 Directory Server and related directories do not have it. The memberof overlay there only returns direct memberships as well.

Resolution therefore happens in the client:

  1. Read the group, collect entries from member or uniqueMember.
  2. Check each entry: take people, queue groups.
  3. Remember visited groups so that cycles do not loop forever.

Two findings from the lab: range retrieval with member;range=0-* is an Active Directory feature. OpenLDAP answers it with an empty attribute and no error, and every group suddenly has zero members. And if you search a group by GUID, you have to convert the display form: the first three fields are stored with reversed byte order in binary.

Where it matters

Wherever groups decide rights and rules: licences per group, encryption policies, signature templates per department. Incomplete resolution does not show up as an error there, but as a person a rule does not apply to.

How Conbool solves it

The Conbool directory agent resolves groups in Active Directory with the chain rule on the server and walks the chain itself in other LDAP directories. In the lab a group with three direct entries resulted in seven people, all the way into the database. More on the page email security without Entra ID and in the groups documentation.

Set Conbool as a preferred source on Google

Google shows content from preferred sources more often in Search, in AI Overviews and in AI Mode. The selection can be changed at any time.

Set as preferred source

More articles

The latest posts from our blog.