Eagle Eyed reader Jens Geyer sent me an email yesterday asking:
There's a KB article, where there is an example code how to determine if the current process/thread is running from admin account. The sample code changed in the newest revision of that KB article.
The old version was testing the group list of the token, whether one of the groups is the predefined admin group. THe new code does something different: They create a SD, with a DACL containing exactly one Access-Allowed-ACE with the Admins Group SID. Then an AccessCheck() is performed on this SD to determine the function result.
My question now is: What is the reason behind the change? Whats wrong with the old code?
Unfortunately the KB article does not mention anything about the reasons. I hope, you can help or at least know someone, who can.
Humorously enough, I wrote the sample code for the original KB article many, many years ago, someone picked up the code and added it to a KB article.
In pseudo-code, the sample did:
foreach (groupSid in groups)
if (groupSid == BuiltinAdministratorsSid)
In other words, it iterated over the token's groups and looked to see if the group was active in the token.
This worked great at the time it was written (NT4), but by the time that Windows 2000 came out, the sample ceased to be accurate. The thing that broke the sample was the addition of the concept of a "Restricted Token". Essentially a restricted token is a token which has had several of the groups and privileges disabled in the token. Vista's UAC feature is based on restricted tokens, as were Windows XP's Safer APIs.
The problem with the previous code is that it didn't take restricted tokens into account. Fortunately the AccessCheck function does. So the KB people updated the sample code to reflect this change - first off they pointed to the CheckTokenMembership API, and for those users that can't use that API, they put up a replacement version of the function in the original KB (in pseudo-code):
psidAdministrators = AllocateAndInitializeSid(...);
pACL = new BYTE[ACLSize];
AddAccessAllowedACE(pACL, READ_ACCESS, psidAdministrators);
return (AccessCheck(psd, htoken, READ_ACCESS));
And now you know "The Rest of the Story" 🙂