Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I check if an NTAccount object represents a Group or a User?

When working with the access rules returned by

GetAccessRules(True, True, GetType(System.Security.Principal.NTAccount))

how can I tell if the NTAccount object referenced in each rule is a user account or a group?

Update:

I was able to solve this as follows. Note, the intent of this code is to return True if the NTAccount is a group, and False otherwise or if an error occurs during checking.

Is there a better way to do this?

Public Function IsGroup(ByVal account As NTAccount) as Boolean  
    Dim samAccountName as string = account.Value  
    Dim accountNameParts() As String = samAccountName.Split("\")  
    If accountNameParts.Count() = 2 Then  
        Dim principalContext As PrincipalContext  
        Try  
            principalContext = New PrincipalContext(ContextType.Domain, accountNameParts(0))  
        Catch  
            Try  
                principalContext = New PrincipalContext(ContextType.Machine, accountNameParts(0))  
            Catch  
                principalContext = Nothing  
            End Try  
        End Try  
        If Not principalContext Is Nothing Then  
            Dim principal As Principal  
            principal = principal.FindByIdentity(principalContext, _samAccountName)  
            If Not principal Is Nothing then   
                return TypeOf principal Is GroupPrincipal  
            End If  
        End If  
    End If  
    Return False  
End Function

Another update:

The above solution was okay for most server\account objects, but it fails for local group objects on the EMC Celerra NAS servers we have. I'm trying to use the NetUserGetInfo/NetLocalGroupGetInfo Win API calls to see if that will work, but I can't get them to work properly. See NetUserGetInfo/NetLocalGroupGetInfo returning error 1722 for more details.

like image 435
Andrew Cooper Avatar asked Aug 12 '10 04:08

Andrew Cooper


2 Answers

I ran into the same problem you have. After doing some research everywhere I finally found a decent solution to solve this: The idea is to use WinAPI LookupAccountSid. I have tested this against both domain and local accounts. Domain\User, NT Authority\System, MachineName\Administrators will all work. Note that I dont put any exception handling there. You can judge what kinda handling is acceptable to your scenario. To get the exception, you can use System.Runtime.InteropServices.Marshal.GetLastWin32Error()

public enum PrincipalType
{
    User,
    Group
}
public bool TryGetPrincipalType(string domainQualifiedName, out PrincipalType type)
{
        var name = new StringBuilder();
        var cchName = (uint) name.Capacity;
        var referencedDomainName = new StringBuilder();
        var cchReferencedDomainName = (uint) referencedDomainName.Capacity;
        SID_NAME_USE sidType;

        var account = new NTAccount(domainQualifiedName);

        var id = new SecurityIdentifier(account.Translate(typeof (SecurityIdentifier)).Value);
        var sidBuffer = new byte[id.BinaryLength];
        id.GetBinaryForm(sidBuffer, 0);

        if (LookupAccountSid(null, sidBuffer, name, ref cchName, referencedDomainName,
                             ref cchReferencedDomainName, out sidType))
        {
            switch (sidType)
            {
                case SID_NAME_USE.SidTypeGroup:
                case SID_NAME_USE.SidTypeWellKnownGroup:
                case SID_NAME_USE.SidTypeAlias:
                    type = PrincipalType.Group;
                    return true;
                case SID_NAME_USE.SidTypeUser:
                    type = PrincipalType.User;
                    return true;
            }
        }
        type = default(PrincipalType);
        return false;
    }
private enum SID_NAME_USE
{
    SidTypeUser = 1,
    SidTypeGroup,
    SidTypeDomain,
    SidTypeAlias,
    SidTypeWellKnownGroup,
    SidTypeDeletedAccount,
    SidTypeInvalid,
    SidTypeUnknown,
    SidTypeComputer
}
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool LookupAccountSid(
        string lpSystemName,
        [MarshalAs(UnmanagedType.LPArray)] byte[] Sid,
        StringBuilder lpName,
        ref uint cchName,
        StringBuilder ReferencedDomainName,
        ref uint cchReferencedDomainName,
        out SID_NAME_USE peUse);
like image 81
Terry Tsay Avatar answered Oct 07 '22 09:10

Terry Tsay


The answer to this was indeed to use the Windows APIs (NetUserGetInfo and NetLocalGroupGetInfo).

Class NetApi
    Private Declare Unicode Function NetUserGetInfo Lib "Netapi32.dll" ( _
         ByVal ServerName As String, _
         ByVal UserName As String, _
         ByVal level As Integer, _
         ByRef BufPtr As IntPtr) As Integer

    Private Declare Unicode Function NetLocalGroupGetInfo Lib "Netapi32.dll" ( _
         ByVal ServerName As String, _
         ByVal GroupName As String, _
         ByVal level As Integer, _
         ByRef BufPtr As IntPtr) As Integer

    Declare Unicode Function NetApiBufferFree Lib "netapi32.dll" _
    (ByRef buffer As IntPtr) As Long

    Public Shared Function PrincipalIsGroup(ByVal MachineName As String, ByVal AccountName As String) As String
        If String.IsNullOrEmpty(MachineName) Then
            Throw New ArgumentException("MachineName is Required")
        End If
        If String.IsNullOrEmpty(AccountName) Then
            Throw New ArgumentException("AccountName is Required")
        End If
        Dim returnValue As String = "NotFound"
        Dim bufPtr As IntPtr
        Dim lngReturn As Integer = NetLocalGroupGetInfo("\\" & MachineName, AccountName, 0, bufPtr)
        Call NetApiBufferFree(bufPtr)
        bufPtr = IntPtr.Zero
        If lngReturn = 0 Then
            returnValue = "True"
        Else
            lngReturn = NetUserGetInfo("\\" & MachineName, AccountName, 0, bufPtr)
            Call NetApiBufferFree(bufPtr)
            bufPtr = IntPtr.Zero
            If lngReturn = 0 Then
                returnValue = "False"
            End If
        End If
        Return returnValue
    End Function
End Class
like image 35
Andrew Cooper Avatar answered Oct 07 '22 10:10

Andrew Cooper