Why does Principal.IsMemberOf() return a false negative for some groups?

2.4k views Asked by At

Why does Principal.IsMemberOf(GroupPrincipal) (MSDN) return a false negative for the Domain Computers group in the following test?

[TestMethod]
public void DomainComputerTest()
{
    var distinguishedName = "CN=MyMachine,DC=SomeDomain,DC=local";
    using( var pc = new PrincipalContext( ContextType.Domain, "SomeDomain.local", "UserName", "Password" ) )
    using( var computer = ComputerPrincipal.FindByIdentity( pc, IdentityType.DistinguishedName, distinguishedName ) )
    {
        Assert.IsNotNull( computer );
        // Get the groups for the computer.
        var groups = computer.GetGroups().Cast<GroupPrincipal>();
        foreach( var group in groups )
        {
            // Immediately turn around and test that the computer is a member of the groups it returned.
            Assert.IsTrue( computer.IsMemberOf( group ), "Computer is not member of group {0}", group.Name );
        }
    }
}

Result Message: Assert.IsTrue failed. Computer is not member of 'Domain Computers' group

The computer is indeed a member of the "Domain Computers" group, which the 'GetGroups()' method returned correctly. In fact, if you attempt to add the computer to the group, a PrincipalExistsException is thrown.

I can reproduce the exact same behavior with users and the "Domain Users" group. Is this because the groups are the Principal groups? Is it because these are "default" groups?

Edit to add: We're using .NET 4.5.1.

4

There are 4 answers

1
Josh On BEST ANSWER

For other developers that find this, here's what I ended up doing. This is a simplified version of the code I deployed, but in summary, I'm assuming the issue is the Primary Group relationship. This may not be correct, but it is working for us for now.

You can get the DirectoryEntry instance for a ComputerPrincipal like this.

var entry = (DirectoryEntry)computerPrincipal.GetUnderlyingObject();

And I'm using this extension method to check for the primary group relationship.

public static bool IsPrimaryGroupFor( this GroupPrincipal group, DirectoryEntry target )
{
    // .Value will return an int like "123", which is the last part of the group's SID
    var id = target.Properties[ "primaryGroupID" ].Value.ToString();
    // strip the account domain SID from the group SID.
    var groupId = group.Sid.Value.Remove( 0, group.Sid.AccountDomainSid.Value.Length + 1 );

    // If the 
    return id.Equals( groupId, StringComparison.OrdinalIgnoreCase );
}

We're syncing AD group memberships, so we discovered this issue in code similar to the following;

public void AddComputerToGroups( ComputerPrincipal computer, ICollection<GroupPrincipal> groups )
{
    var directoryEntry = (DirectoryEntry)computer.GetUnderlyingObject();

    foreach( var principal in groups.Where(g=> !computer.IsMemberOf(g) )
    {
        principal.Members.Add( computer );
        principal.Save(); // Exception thrown because computer already existed in the primary group.
    }
}
2
TyCobb On

This appears to be an issue that has always existed.

Every question I found with your same issue was unanswered. I could not find anything that said this was filed as a bug, but appears to have existed since .NET 3.5.

I attempted to get your example to return true (of course changed the information to be for my work's domain). No matter what, it returned false. Decompiling the Principal class in dotPeek yielded only speculation. Having gone through and set up Visual Studio to allow stepping into the .NET framework code was a bust because it will not step into the necessary methods. I can step into other .NET framework code, but nothing on Principal. Not sure if this is related the methods being tagged with SecurityCriticalAttribute or not. Would love confirmation on that.

My suggestion to you is to file this as a bug and in the mean time go a different route to determine if the computer is a member. In my test, group.Members did contain the computer.

I experienced the same issue running .NET 3.5, 4.0, 4.5, 4.5.1

Here are some references:

Work around -- GroupPrincipal.IsMemberOf always returns false

A 2010 MSDN Blog entry with a comment that had the same issue as you.

NOTE: I normally wouldn't answer like this, but because every question I found with this issue had 0 answers or a workaround, I thought it would be beneficial for future readers to actually see some sort of an "answer" on this problem.

0
Paul Dolphin On

This can also happen if the group you are testing has too many members. IsMemberOf only appears to check the first 1000 or so entries. To get round this scenario check whether group.Members contains the user/computer.

0
Robert Kaucher On

So, I believe that you might be experiencing the same issue that I did here but for a computer account and not a user account.

Principal.IsInRole("AD Group Name") always returns false, no exceptions thrown

Verify that the account that is executing the code has read permissions to both the Computers and Users containers in the domain that the computer causing the error/experiencing the problem is registered to.