5

I'm trying to write an LDAP query which will discover if a user is a member of a group which matches a wildcard query and I'm trying to use the LDAP_MATCHING_RULE_IN_CHAIN OID to do this. I'm basically following example 2 on this page:

http://support.microsoft.com/kb/914828

I've found that this method works well within a domain i.e. if user1 is in group1 and group1 is in group2 then I can write a query matching "*2" and the LDAP query will find the nested relationship and match the user against the group.

However, now I've been asked to support relationships between domains in the same forest. So now I've got:

  • user1 is a member of group1 in domain 1
  • group1 in domain 1 is a member of group2 in domain 2

And I want to be able to match user1 against group2.... I can't work out how to make LDAP_MATCHING_RULE_IN_CHAIN do this:

I've tried setting the base of the query to the following:

  1. Domain 1, but this just returns groups in domain 1
  2. The parent domain of domain 1 and domain 2, but this returns no results.
  3. The GC, found by querying "rootDSE" property but this just returns groups inside the domain 1 (which is the GC server)

Anyone know how I can make this work?

Benj
  • 31,668
  • 17
  • 78
  • 127
  • When you searched the via global catalog, was your base object path in the form "GC://DC=rootdomain,DC=..."? If your base object was the root domain of the forest, and had the "GC://" prefix, and you didn't get the expected results, then my guess is that cross domain searches do not work for this OID. – Artomegus Oct 04 '11 at 15:30
  • Hmm, I think I might have figured why I can't search the GC properly. Microsoft say that the GC doesn't contain information about domain local groups.... Hence LDAP_MATCHING_RULE_IN_CHAIN can't follow the chain if there's a DMG involved... bummer. – Benj Oct 04 '11 at 17:09
  • I don't know if comments works this way but i'd like to attract @Brian Desmond attention to this question. – JPBlanc Oct 12 '11 at 04:15
  • Do you test the last example I post ? – JPBlanc Oct 24 '11 at 03:38
  • I haven't been able to since I've lost access to the test environment with multiple domains. However, I have researched the method and it looks like other people have used this successfully to do cross domain nested group searches. Thanks! Now to figure out how it can be done with c++.... – Benj Oct 24 '11 at 14:55

1 Answers1

3

As far as I understand, one way of doing that is :

  1. From the RootDSE, look for the configuration NamingContext.
  2. In the configuration NamingContext looking for objects of class crossRef with an attribute nETBIOSName existing.
  3. From these entries use the algorithm you are discribing by using dnsRoot and nCName attributs. A working forest DNS allows you to join a domain controler of dnsRoot. nCName allows to search from the root.

Be careful to do this as a member of the enterpreise administrators group.

Here is an example of the code.

/* Retreiving RootDSE
 */
string ldapBase = "LDAP://WM2008R2ENT:389/";
string sFromWhere = ldapBase + "rootDSE";
DirectoryEntry root = new DirectoryEntry(sFromWhere, "dom\\jpb", "PWD");
string configurationNamingContext = root.Properties["configurationNamingContext"][0].ToString();

/* Retreiving the root of all the domains
 */
sFromWhere = ldapBase + configurationNamingContext;
DirectoryEntry deBase = new DirectoryEntry(sFromWhere, "dom\\jpb", "PWD");

DirectorySearcher dsLookForDomain = new DirectorySearcher(deBase);
dsLookForDomain.Filter = "(&(objectClass=crossRef)(nETBIOSName=*))";
dsLookForDomain.SearchScope = SearchScope.Subtree;
dsLookForDomain.PropertiesToLoad.Add("nCName");
dsLookForDomain.PropertiesToLoad.Add("dnsRoot");

SearchResultCollection srcDomains = dsLookForDomain.FindAll();

foreach (SearchResult aSRDomain in srcDomains)
{
  /* For each root look for the groups containing my user
   */
  string nCName = aSRDomain.Properties["nCName"][0].ToString();
  string dnsRoot = aSRDomain.Properties["dnsRoot"][0].ToString();

  /* To find all the groups that "user1" is a member of :
   * Set the base to the groups container DN; for example root DN (dc=dom,dc=fr) 
   * Set the scope to subtree
   * Use the following filter :
   * (member:1.2.840.113556.1.4.1941:=cn=user1,cn=users,DC=x)
   */
  /* Connection to Active Directory
   */
  sFromWhere = "LDAP://" + dnsRoot + "/" + nCName;
  deBase = new DirectoryEntry(sFromWhere, "dom\\jpb", "PWD");

  DirectorySearcher dsLookFor = new DirectorySearcher(deBase);
  // you cancomplete the filter here  (&(member:1.2.840.113556.1.4.1941:=CN=user1 Users,OU=MonOu,DC=dom,DC=fr)(cn=*2)
  dsLookFor.Filter = "(member:1.2.840.113556.1.4.1941:=CN=user1 Users,OU=MonOu,DC=dom,DC=fr)";
  dsLookFor.SearchScope = SearchScope.Subtree;
  dsLookFor.PropertiesToLoad.Add("cn");

  SearchResultCollection srcGroups = dsLookFor.FindAll();

  foreach (SearchResult srcGroup in srcGroups)
  {
    Console.WriteLine("{0}", srcGroup.Path);
  }
}

This is just a proof of concept, you have to complete with :

using using(){} form for disposing DirectoryEntry objects

Exception management


Edited (2011-10-18 13:25)

Your comment about the way you solve the problem can be found in a method given in System.DirectoryServices.AccountManagement Namespace. It's a kind of recursive solution. This time, I test with a user belonging to group1 (in an other domain) which belongs to group2 (in a third domain) and it seems to work.

/* Retreiving a principal context
 */
Console.WriteLine("Retreiving a principal context");
PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, "WM2008R2ENT:389", "dc=dom,dc=fr", "jpb", "PWD");


/* Look for all the groups a user belongs to
 */
UserPrincipal aUser = UserPrincipal.FindByIdentity(domainContext, "user1");
PrincipalSearchResult<Principal> a =  aUser.GetAuthorizationGroups();

foreach (GroupPrincipal gTmp in a)
{
  Console.WriteLine(gTmp.Name);    
}
JPBlanc
  • 70,406
  • 17
  • 130
  • 175
  • Thanks for this, I'm not in a position to test it now but I'll give it a good test tomorrow. I'm actually not convinced it will work. I did try something very similar, iterating over the domains in the forest performing the LDAP_MATCHING_RULE_IN_CHAIN over each one. However, it appeared to be the case that this wasn't enough. It was almost like one search had to be able to match the entire nested relationship and encompass every group therin or it wouldn't work at all... – Benj Oct 17 '11 at 19:11
  • At the moment, I test on a VM infrstructure, and it really found every groups in which the user is enrolled (local, gobal, universel)directly or indirectly. – JPBlanc Oct 18 '11 at 02:11
  • This is a neat way of finding all the domains to search but the individual group search suffers from the same problem my existing code suffers from. I.e. it will find nested relationships where the user and the nested groups are in the same domain but if user 1 is a member of group 1 in domain 1 and group 1 is a member of group 2 in domain 2. It won't resolve that user 1 is a member of group 2. – Benj Oct 18 '11 at 09:15
  • Sorry for the lost of time, I effectively don't reproduce exactly your question in my VMs. – JPBlanc Oct 18 '11 at 09:21
  • I have kind of found a way of doing what I want but it'd not ideal. It involves LDAP searching each domain for groups matching the query, getting their SIDs and comparing those with the SIDs obtained from the users local security token. This approach works but it has a few limitations and I'd prefer to do the whole thing with LDAP if possible. – Benj Oct 18 '11 at 09:25
  • I modify my answer to add a solution. As far as LDAP is concerned we are speaking about a search different naming contexts wich can be done having referals and chasing referal. In the case of forest it's perhaps possible using [Phantom Root](http://msdn.microsoft.com/en-us/library/aa366988(VS.85).aspx), but I think you need to program base level protocol in C++. – JPBlanc Oct 18 '11 at 12:06