How To Determine Whether a Thread Is Running in User Context of Local Administrator Account (118626)
The information in this article applies to:
- Microsoft Win32 Application Programming Interface (API), when used with:
- the operating system: Microsoft Windows NT
- the operating system: Microsoft Windows 2000
- the operating system: Microsoft Windows XP
This article was previously published under Q118626 SUMMARY To determine whether a thread is running under a local
administrator account, you have to examine the access token that is associated
with the thread. This article describes how to do this.
With Windows
2000 and later versions, you can use the CheckTokenMembership() API instead of the steps described in this article. For
additional information, see the Microsoft Platform SDK documentation.
MORE INFORMATION By default, the token that is associated with a thread is
that of its containing process. This "user context" is superceded by any token
that is attached directly to the thread. Therefore, to determine a thread's
user context, you should first attempt to obtain a token for the thread with
the OpenThreadToken function. If this method fails and the GetLastError function reports ERROR_NO_TOKEN, then you can obtain the token
for the process with the OpenProcessToken function. After you obtain the token of the current
user, you can use the AccessCheck function to detect whether the user is an administrator. To do
this, follow these steps:
- Create a security identifier (SID) for the local
administrator group by using the AllocateAndInitializeSid function.
- Construct a new security descriptor (SD) with a
Discretionary Access Control List (DACL) that contains an Access Control Entry
(ACE) for the administrator group's SID.
- Call AccessCheck with the token of the current user and the newly constructed SD
to detect whether the user is an administrator.
The following sample code uses the functions that are mentioned
earlier in this article to test whether the current thread is running as a user
who is an administrator on the local computer. Sample code
#include <windows.h>
#include <stdio.h>
#include <lmcons.h>
BOOL IsCurrentUserLocalAdministrator(void);
void main(int argc, char **argv)
{
if (IsCurrentUserLocalAdministrator())
printf("You are an administrator\n");
else
printf("You are not an administrator\n");
}
/*-------------------------------------------------------------------------
-
IsCurrentUserLocalAdministrator ()
This function checks the token of the calling thread to see if the caller
belongs to the Administrators group.
Return Value:
TRUE if the caller is an administrator on the local machine.
Otherwise, FALSE.
--------------------------------------------------------------------------*
/
BOOL IsCurrentUserLocalAdministrator(void)
{
BOOL fReturn = FALSE;
DWORD dwStatus;
DWORD dwAccessMask;
DWORD dwAccessDesired;
DWORD dwACLSize;
DWORD dwStructureSize = sizeof(PRIVILEGE_SET);
PACL pACL = NULL;
PSID psidAdmin = NULL;
HANDLE hToken = NULL;
HANDLE hImpersonationToken = NULL;
PRIVILEGE_SET ps;
GENERIC_MAPPING GenericMapping;
PSECURITY_DESCRIPTOR psdAdmin = NULL;
SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY;
/*
Determine if the current thread is running as a user that is a member
of
the local admins group. To do this, create a security descriptor
that
has a DACL which has an ACE that allows only local aministrators
access.
Then, call AccessCheck with the current thread's token and the
security
descriptor. It will say whether the user could access an object if
it
had that security descriptor. Note: you do not need to actually
create
the object. Just checking access against the security descriptor
alone
will be sufficient.
*/
const DWORD ACCESS_READ = 1;
const DWORD ACCESS_WRITE = 2;
__try
{
/*
AccessCheck() requires an impersonation token. We first get a
primary
token and then create a duplicate impersonation token. The
impersonation token is not actually assigned to the thread, but is
used in the call to AccessCheck. Thus, this function itself never
impersonates, but does use the identity of the thread. If the
thread
was impersonating already, this function uses that impersonation
context.
*/
if (!OpenThreadToken(GetCurrentThread(), TOKEN_DUPLICATE|TOKEN_QUERY,
TRUE, &hToken))
{
if (GetLastError() != ERROR_NO_TOKEN)
__leave;
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_DUPLICATE|TOKEN_QUERY, &hToken))
__leave;
}
if (!DuplicateToken (hToken, SecurityImpersonation,
&hImpersonationToken))
__leave;
/*
Create the binary representation of the well-known SID that
represents the local administrators group. Then create the
security
descriptor and DACL with an ACE that allows only local admins
access.
After that, perform the access check. This will determine whether
the current user is a local admin.
*/
if (!AllocateAndInitializeSid(&SystemSidAuthority, 2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0, &psidAdmin))
__leave;
psdAdmin = LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
if (psdAdmin == NULL)
__leave;
if (!InitializeSecurityDescriptor(psdAdmin,
SECURITY_DESCRIPTOR_REVISION))
__leave;
// Compute size needed for the ACL.
dwACLSize = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) +
GetLengthSid(psidAdmin) - sizeof(DWORD);
pACL = (PACL)LocalAlloc(LPTR, dwACLSize);
if (pACL == NULL)
__leave;
if (!InitializeAcl(pACL, dwACLSize, ACL_REVISION2))
__leave;
dwAccessMask= ACCESS_READ | ACCESS_WRITE;
if (!AddAccessAllowedAce(pACL, ACL_REVISION2, dwAccessMask,
psidAdmin))
__leave;
if (!SetSecurityDescriptorDacl(psdAdmin, TRUE, pACL, FALSE))
__leave;
/*
AccessCheck validates a security descriptor somewhat; set the
group
and owner so that enough of the security descriptor is filled out
to
make AccessCheck happy.
*/
SetSecurityDescriptorGroup(psdAdmin, psidAdmin, FALSE);
SetSecurityDescriptorOwner(psdAdmin, psidAdmin, FALSE);
if (!IsValidSecurityDescriptor(psdAdmin))
__leave;
dwAccessDesired = ACCESS_READ;
/*
Initialize GenericMapping structure even though you
do not use generic rights.
*/
GenericMapping.GenericRead = ACCESS_READ;
GenericMapping.GenericWrite = ACCESS_WRITE;
GenericMapping.GenericExecute = 0;
GenericMapping.GenericAll = ACCESS_READ | ACCESS_WRITE;
if (!AccessCheck(psdAdmin, hImpersonationToken, dwAccessDesired,
&GenericMapping, &ps, &dwStructureSize, &dwStatus,
&fReturn))
{
fReturn = FALSE;
__leave;
}
}
__finally
{
// Clean up.
if (pACL) LocalFree(pACL);
if (psdAdmin) LocalFree(psdAdmin);
if (psidAdmin) FreeSid(psidAdmin);
if (hImpersonationToken) CloseHandle (hImpersonationToken);
if (hToken) CloseHandle (hToken);
}
return fReturn;
}
Modification Type: | Minor | Last Reviewed: | 7/1/2004 |
---|
Keywords: | kbAPI kbhowto kbKernBase kbSecurity KB118626 |
---|
|