Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to overcome the "The member's SID could not be resolved" error when checking if user is a member of group?

We have a process that requires to check whether a particular user is a member of local Administrators group.

The code that checks that looks like the following:

using (PrincipalContext context = new PrincipalContext(ContextType.Machine, null))
{
    UserPrincipal user = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, sUserName);
    if (user != null)
    {
         SecurityIdentifier adminsGroupSID = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null);
         GroupPrincipal group = GroupPrincipal.FindByIdentity(context, IdentityType.Sid, adminsGroupSID.Value);
         if (group != null)
         {
             if (user.IsMemberOf(group))
                 return 0;
         }
    }
}

When the group has accounts (e.g. domain accounts) that were removed, we're getting a PrincipalOperationException and a message "An error (1332) occurred while enumerating the group membership. The member's SID could not be resolved."

Is there any way to overcome this without: a) Removing manually the orphaned SIDs from the group b) Not ignoring it?

Thanks

like image 689
Denis Mazourick Avatar asked Oct 24 '12 13:10

Denis Mazourick


1 Answers

This is based heavily on my finding at http://www.seirer.net/blog/2013/9/12/how-to-deal-with-localized-or-renamed-administrators-in-net written by Michael Seirer. He was attempting to get the SID of the local Admin account, while all we need are the names in that group. The reason for the error "The member's SID could not be resolved." is because there are accounts that are no longer recognized in Active Directory - likely relics that point to deleted user accounts. You can either do what Microsoft says and just delete them and hope your app never crashes again (though it will, the next time an account is deleted that is sitting in that Administrators group), or solve it permanently with this code I slightly modified from Mike.

using System.DirectoryServices;
using System.Collections;
using System.Runtime.InteropServices;

[DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool ConvertSidToStringSid(IntPtr pSid, out string strSid);

private static string GetTextualSID(DirectoryEntry objGroup)
{
    string sSID = string.Empty;
    byte[] SID = objGroup.Properties["objectSID"].Value as byte[];
    IntPtr sidPtr = Marshal.AllocHGlobal(SID.Length);
    sSID = "";
    System.Runtime.InteropServices.Marshal.Copy(SID, 0, sidPtr, SID.Length);
    ConvertSidToStringSid((IntPtr)sidPtr, out sSID);
    System.Runtime.InteropServices.Marshal.FreeHGlobal(sidPtr);
    return sSID; 
}


public static List<string> GetLocalAdministratorsNames()
{
    List<string> admins = new List<string>();
    DirectoryEntry localMachine = new DirectoryEntry("WinNT://" + Environment.MachineName);
    string adminsSID = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null).ToString();

    string localizedAdmin = new System.Security.Principal.SecurityIdentifier(adminsSID).Translate(typeof(System.Security.Principal.NTAccount)).ToString();

    localizedAdmin = localizedAdmin.Replace(@"BUILTIN\", "");

    DirectoryEntry admGroup = localMachine.Children.Find(localizedAdmin, "group");
    object adminmembers = admGroup.Invoke("members", null);

    DirectoryEntry userGroup = localMachine.Children.Find("users", "group");
    object usermembers = userGroup.Invoke("members", null);

    //Retrieve each user name.
    foreach (object groupMember in (IEnumerable)adminmembers)
    {
        DirectoryEntry member = new DirectoryEntry(groupMember);

        string sidAsText = GetTextualSID(member);
        admins.Add(member.Name);            
    }
    return admins;
}

It will return a List<string> of members of the local Administrators group on the local machine. You can even alter Environment.MachineName to be any computer name in your domain, if you don't want the local machine.

Then you can iterate the list to see if they're in it:

private static bool isAdmin(string user)
{
    //string user = @"DOMAIN\doej";
    user = user.Split(@'\')[1];

    List<string> admins = GetLocalAdministratorsNames();
    foreach (string s in admins)
    {
        if (s == user)
            return true; // admin found
    }
    return false;  // not an admin
}
like image 65
vapcguy Avatar answered Nov 15 '22 11:11

vapcguy