Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get user's groups using the Network API

I'm using an ActiveDirectory server to query the groups that a user belongs to. I would like to get all the groups that a user belongs to, but using the Network Management funcions of the Network API.

I realized that already exist a function called NetUserGetGroups, but unfortunately, this function does not include the groups that a member indirect belongs to.

Example

For example, if I have the following structure:

MyGroup1
   |_ MyGroup2
          |_ MyUser

Using NetUserGetGroups call will return:

MyGroup2

Using System.DirectoryServices.AccountManagement, will return:

    public static List<string> GetUserGroupsAD(string dc, string userName)
    {
        var result = new List<string>();
        try
        {
            using (var context = new PrincipalContext(
                   ContextType.Domain, dc.Replace("\\", "")))
            {
                var user = UserPrincipal.FindByIdentity(context, userName);
                var groups = user.GetAuthorizationGroups();
                foreach (Principal p in groups2)
                    result.Add(p.Name);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("An error happened in GetUserGroups", ex);
        }

        return result;
    }

This code returns:

MyGroup1 <-- this is what I need, loading the indirect groups!
MyGroup2
(and others)

The problem is that I'm using .NET2, and I cannot get access to the System.DirectoryServices. I only have access to the NetworkAPI.

My question

Someone knows how could I implement the user.GetAuthorizationGroups() call, using the NetworkAPI?

like image 914
Daniel Peñalba Avatar asked Mar 13 '14 13:03

Daniel Peñalba


1 Answers

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);
like image 191
Daniel Peñalba Avatar answered Sep 19 '22 12:09

Daniel Peñalba