The solution is using the AuthzAPI. The following code loads the user groups for a given user SID. The results are the same that the user.GetAuthorizationGroups()
call retrieves:
static List<string> GetSidGroupsFromSid(sbyte[] sid_bytes)
{
List<string> result = new List<string>();
IntPtr clientContext = IntPtr.Zero;
IntPtr resourceManager = IntPtr.Zero;
IntPtr buffer = IntPtr.Zero;
LUID unusedLuid = new LUID();
bool success;
try
{
success = AuthzInitializeResourceManager(
(int)AuthzResourceManagerFlags.NO_AUDIT,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
string.Empty,
out resourceManager);
ThrowLastError(success);
success = AuthzInitializeContextFromSid(
(int)AuthzContextFlags.NONE,
sid_bytes, resourceManager,
IntPtr.Zero, unusedLuid,
IntPtr.Zero,
out clientContext);
ThrowLastError(success);
int pSizeRequired = 0;
success = AuthzGetInformationFromContext(
clientContext,
(int)AuthContextInformation.AuthzContextInfoGroupsSids,
0,
out pSizeRequired,
IntPtr.Zero);
if (!success && pSizeRequired > 0 && Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER)
{
buffer = Marshal.AllocHGlobal(pSizeRequired);
success = AuthzGetInformationFromContext(clientContext, 2, pSizeRequired, out pSizeRequired, buffer);
ThrowLastError(success);
TOKEN_GROUPS groups = ((TOKEN_GROUPS)Marshal.PtrToStructure(buffer, typeof(TOKEN_GROUPS)));
IntPtr ptr = new IntPtr(buffer.ToInt64() + (long)Marshal.SizeOf(typeof(TOKEN_GROUPS)) - (long)Marshal.SizeOf(typeof(IntPtr)));
for (int index = 0; index < groups.groupCount; ++index)
{
SID_AND_ATTR currentSid = (SID_AND_ATTR)Marshal.PtrToStructure(ptr, typeof(SID_AND_ATTR));
ptr = new IntPtr(ptr.ToInt64() + (long)Marshal.SizeOf(typeof(SID_AND_ATTR)));
string sidString = "";
NetWorkAPI.ConvertSidToStringSid(currentSid.pSid, ref sidString);
result.Add(sidString);
}
}
}
finally
{
if (clientContext != IntPtr.Zero)
{
success = AuthzFreeContext(clientContext);
ThrowLastError(success);
}
if (resourceManager != IntPtr.Zero)
{
success = AuthzFreeResourceManager(resourceManager);
ThrowLastError(success);
}
if (buffer != IntPtr.Zero)
{
Marshal.FreeHGlobal(buffer);
}
}
return result;
}
static void ThrowLastError(bool success)
{
if (success)
return;
int err = Marshal.GetLastWin32Error();
Win32Exception win32Exception = new Win32Exception(err);
throw new Exception("Authz error " + err + ": " + win32Exception.Message);
}
const int ERROR_INSUFFICIENT_BUFFER = 122;
[Flags]
enum AuthzResourceManagerFlags : int
{
NONE = 0,
NO_AUDIT = 0x1,
INITIALIZE_UNDER_IMPERSONATION = 0x2,
VALID_INIT_FLAGS = (NO_AUDIT | INITIALIZE_UNDER_IMPERSONATION),
};
[Flags]
enum AuthzContextFlags : int
{
NONE = 0,
SKIP_TOKEN_GROUPS = 0x2,
REQUIRE_S4U_LOGON = 0x4,
COMPUTE_PRIVILEGES = 0x8
};
[StructLayout(LayoutKind.Sequential)]
struct LUID
{
public uint LowPart;
public int HighPart;
};
enum AuthContextInformation : int
{
AuthzContextInfoUserSid = 1,
AuthzContextInfoGroupsSids = 2
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
sealed class TOKEN_GROUPS
{
public int groupCount;
public IntPtr groups = IntPtr.Zero;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
sealed class SID_AND_ATTR
{
public IntPtr pSid = IntPtr.Zero;
public int attrs;
}
[DllImport(
"authz.dll",
CharSet = CharSet.Unicode,
CallingConvention = CallingConvention.StdCall,
SetLastError = true)]
static extern bool AuthzInitializeResourceManager(
int flags,
IntPtr pfnAccessCheck,
IntPtr pfnComputeDynamicGroups,
IntPtr pfnFreeDynamicGroups,
string name,
out IntPtr rm);
[DllImport(
"authz.dll",
CharSet = CharSet.Unicode,
CallingConvention = CallingConvention.StdCall,
SetLastError = true)]
static extern bool AuthzInitializeContextFromSid(
int Flags,
sbyte[] userSid,
IntPtr AuthzResourceManager,
IntPtr pExpirationTime,
LUID Identitifier,
IntPtr DynamicGroupArgs,
out IntPtr pAuthzClientContext);
[DllImport(
"authz.dll",
CharSet = CharSet.Unicode,
CallingConvention = CallingConvention.StdCall,
SetLastError = true)]
static extern bool AuthzGetInformationFromContext(
IntPtr hAuthzClientContext,
int InfoClass,
int BufferSize,
out int pSizeRequired,
IntPtr Buffer);
[DllImport(
"authz.dll",
CharSet = CharSet.Unicode,
CallingConvention = CallingConvention.StdCall)]
static extern bool AuthzFreeContext(
IntPtr AuthzClientContext);
[DllImport(
"authz.dll",
CharSet = CharSet.Unicode,
CallingConvention = CallingConvention.StdCall)]
static extern bool AuthzFreeResourceManager(IntPtr rm);