Search code examples
windowswinapiusergroups

Why does the WinAPI function ::NetUserGetLocalGroups() not return all groups?


I have a user DOMAIN\User.Name that is according to whoami /GROUPS in somewhat over 200 windows domain groups.

When I try do determine all groups for this user using the WinAPI function ::NetUserGetLocalGroups() I get only 27 groups for said user:

#include <windows.h> 
#include <lm.h>
#include <string>

int main()
{
  //query the domain server for the groups of the user
  LPGROUP_USERS_INFO_0 pBuf = NULL;
  DWORD dwLevel = 0;
  DWORD dwPrefMaxLen = MAX_PREFERRED_LENGTH;
  DWORD dwEntriesRead = 0;
  DWORD dwTotalEntries = 0;
  NET_API_STATUS nStatus = MAX_NERR;

  std::wstring serverName(L"\\\\MyDomainNameDns");
  std::wstring userName(L"DOMAIN\\user.name");

  nStatus = ::NetUserGetLocalGroups(serverName.c_str(),
    userName.c_str(),
    dwLevel,
    LG_INCLUDE_INDIRECT,
    reinterpret_cast<PBYTE*>(&pBuf),
    dwPrefMaxLen,
    &dwEntriesRead,
    &dwTotalEntries);
}

dwTotalEntries is 27 and nStatus is 0, so it's not that I fetch only a part of all groups. Also I tried using ::NetUserGetGroups() instead but had no success either.

NOTE: Using .NET functionality System.Security.Principal.WindowsIdentity.GetCurrent().Groups indeed does give me all groups.

What could be the reason NetUserGetLocalGroups does not return get all groups as whoami or .NET / WindowsIdentity? What could be the difference between the groups returned/not returned?


Solution

  • After quite some more digging I found the following statement in a windows user group discussion:

    Actually, NetUserGetLocalGroups / NetUserGetGroups do not handle nested or universal groups in windows 2000 native domains. Enumerating the SIDs from the token is the only reliable way.

    So, in other words, use ::NetUserGetLocalGroups / NetUserGetGroups if you want direct group membership. If you want to know the group ownership in order to find out whether a user has the right to do something, use the token / SID way (e.g. using ::OpenProcessToken, ::GetTokenInformation and ::LookupAccountSid).

    Example

    A user is in the (domain local) group Employee. All employees have keycard access to the building, so the Employee group is am member of the HasBuildingAccess (domain local) group.

    If you want to know, whether a user is (directly) in the Employee group, use ::NetUserGetLocalGroups. It contains the Employee group, but not the HasBuildingAccess group.

    If you want to know however, whether a user has the right to access the building, enumerate the SIDs from the token. It will contain the HasBuildingAccess group.