How can I check if an NTAccount object is a group or a user?
When working with access rules returned
GetAccessRules(True, True, GetType(System.Security.Principal.NTAccount))
how can I tell if the NTAccount object is a link in each rule by a user account or a group?
Update:
I was able to solve it in the following way. Note that the purpose of this code is to return True
if NTAccount
is a group, False
otherwise, or if an error occurs during validation.
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 ok for most server \ account objects, but it fails for local group objects on EMC Celerra NAS servers. I am trying to use the NetUserGetInfo / NetLocalGroupGetInfo Win API calls to see if this will work, but I cannot get them to work correctly. See NetUserGetInfo / NetLocalGroupGetInfo returning error 1722 for details .
source to share
The answer to this question really was to use the Windows API ( 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
source to share
I faced the same problem as yours. After doing some research all over the world, I finally found a decent solution to solve this problem. The idea is to use WinAPI LookupAccountSid. I've tested this for both domain and local accounts. Domain \ User, NT Authority \ System, MachineName \ Administrators will work. Please note that I am not posting any exceptions. You can judge which control procedure is appropriate for 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);
source to share