How to overcome the error "SID member error failed" when checking if a user is a member of a group?

We have a process that requires checking whether a particular user is a member of the local administrators group.

The code that validates is as follows:

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 a group has accounts (for example, domain accounts) that have been deleted, we get a PrincipalOperationException and the message "An error occurred while enumerating the group membership (1332). Member SID could not be resolved."

Is there a way to overcome this without: a) Removing manually orphaned security identifiers from group b) Do not ignore it?

thanks

+5
source share
4 answers

This is based on my finding in http://www.seirer.net/blog/2013/9/12/how-to-deal-with-localized-or-renamed-administrators-in-net , written by Michael Seirer. He tried to get the SID of the local administrator account, and all we need are the names in this group. The cause of the error is "Member SID cannot be resolved." because there are accounts that are no longer recognized in Active Directory - probable relics pointing to remote user accounts. You can either do what Microsoft says and just delete them, and hope that your application never crashes (although it will be the next time the account is deleted, which is in this group of "Administrators") , or solve it forever with this code, which 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> members of the local Administrators group on the local machine. You can even change Environment.MachineName as any computer name in your domain if you do not want a local machine.

Then you can iterate over the list to see if they are 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 } 
+2
source

One way to avoid mistakes is to go the other way. Instead of checking if the user is a member of a group, first download all the groups and check the list for your target group. One drawback: slower ....

 var groups = UserPrincipal.Current.GetAuthorizationGroups(); var found = groups.FirstOrDefault(principal => principal.Name == "Administrators"); var isMemberOfAdminGroup = found != null; 

Thanks arus for your help :)

0
source
 public static bool UserHasLocalAdminPrivledges(this UserPrincipal up) { SecurityIdentifier id = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null); return up.GetAuthorizationGroups().Any(g => g.Sid == id) } 
0
source

There are three possible solutions (all untested, using the latter for all domain groups):
1) Download the group and list the participants yourself
2) Download the base group object and use the ["Members"] properties, which is a list of SIDs.
3) Use the GetAuthorizationGroups () of the user (who will also use your indirect groups, the Service-Account must be a member of the "Windows Authorization Groups" and "PreWindows 2000 Comaptible ....") and use the list of groups to search for your Admin group.

-1
source

Source: https://habr.com/ru/post/1441820/


All Articles