Subtle issues when using predefined accounts

The code below has a small problem - can you spot quickly?

    const MAX_TEXT_BUFFER = 1024;
BYTE pbSid[SECURITY_MAX_SID_SIZE];
PSID pSid = (PSID)pbSid;
WCHAR wszDomainName[MAX_TEXT_BUFFER];
SID_NAME_USE snUse;

DWORD cbSid = SECURITY_MAX_SID_SIZE;
DWORD dwDomainNameSize = MAX_TEXT_BUFFER;
LPWSTR pwszUserName = L"NT AUTHORITY\\Local Service";
if (!LookupAccountName( NULL, pwszUserName,
pSid, &cbSid,
wszDomainName, &dwDomainNameSize,
&snUse))
{
// Error
throw(E_UNEXPECTED);
}

And, another puzzle. What's wrong with the code below?

    DWORD cEntriesRead = 0, cEntriesTotal = 0;
LPBYTE pbBuffer = NULL;
DWORD_PTR ResumeHandle = NULL;
NET_API_STATUS status =
NetLocalGroupGetMembers(
NULL,
"BUILTIN\\Backup Operators",
0,
&pbBuffer,
MAX_PREFERRED_LENGTH,
&cEntriesRead,
&cEntriesTotal,
&ResumeHandle
);
if (status != NERR_Success)
{
// Error
throw(E_UNEXPECTED);
}

 

Solution: In both cases, the problem is that the strings above are in localized format. This code will fail on a German build, for example. The solution, at least for Local Service and Network Service accounts is to use the no-localized forms (which in this case is the same as the english form but without the spaces in the user name). The table below shows these names in localized format, and the corresponding aliases in the non-localized format.

Localized form (English) Localized form (German) Non-Localized form SID WELL_KNOWN_SID_TYPE
NT AUTHORITY\Local Service NT-AUTORIT─T\LOKALER DIENST NT AUTHORITY\Localservice S-1-5-19 WinLocalServiceSid
NT AUTHORITY\Network Service NT-AUTORIT─T\NETZWERKDIENST NT AUTHORITY\Networkservice S-1-5-20 WinNetworkServiceSid
BUILTIN\Administrators VORDEFINIERT\Administratoren n\a S-1-5-32-544 WinBuiltinAdministratorsSid
BUILTIN\Backup Operators VORDEFINIERT\Sicherungs-Operatoren n\a S-1-5-32-551 WinBuiltinBackupOperatorsSid

We note that the Administrators and Backup Operators groups do not have a non-localized alias. This might be a little problematic but you can always use the predefined SID through the CreateWellKnownSid with one of the WELL_KNOWN_SID_TYPE enumeration values.

There are two more subtle problems with the second code above that is querying for Backup Operators.

1) First, the code will fail on Windows XP Home Edition, since on this SKU we do not have a backup operators group!

2) Second, this code might potentially fail on Domain Controllers because on a DC the local groups are simply aliases on the domain groups used by this machine (in the sense that BUILTIN\Administrators is simply an alias to the Domain Admins group). Even if you are running as Local SYSTEM, if you try to enumerate members of this group you might get a failure if the administrators specifically set an Deny ACL to your user account (remember that in a DC you can set ACLs on groups, as opposed to a non-DC case where any local user can enumerate the content of any local group.

[Update] I mentioned above that you can access some predefined groups only through their SIDs. This might be problematic in VBScript, but not hard. I also added here a script that would return the name of the Administrators script on a machine, regardless of the language settings.

 ' Get the Administrators account name
Dim strQuery
strQuery = "select * from Win32_Account where SID='S-1-5-32-544' and localAccount=TRUE"
Dim objSet
set objSet = GetObject("winmgmts:").ExecQuery(strQuery)

 Dim obj, Account
for each obj in objSet
set Account = obj
exit for
next

 wscript.echo "- Set user name = .\" & Account.Name & " "