
| Last Modification: August 12, 2000 |
How to determine if the user has administrator privileges?
Given the complexity of the Windows NT security model
and it's capabilities for discreationary access, this is not a very simple
question to answer. Over time, several methods have appeared, some of which have
been suggested by Microsoft in the Knowledge Base.
The first one was to check if the user was a member of the local Administrators group. This is easily done by building a SID for the group (it's a well-known sid after all) and checking the groups in the user's access token against it. Something like this:
#define BUFF_SIZE 1024
BOOL IsAdmin() const
{
HANDLE hToken = NULL;
PSID pAdminSid = NULL;
BYTE buffer[BUFF_SIZE];
PTOKEN_GROUPS pGroups = (PTOKEN_GROUPS)buffer;
DWORD dwSize; // buffer size
DWORD i;
BOOL bSuccess;
SID_IDENTIFIER_AUTHORITY siaNtAuth = SECURITY_NT_AUTHORITY;
// get token handle
if ( !OpenProcessToken ( GetCurrentProcess ( ), TOKEN_QUERY, &hToken ) )
return false;
bSuccess = GetTokenInformation ( hToken, TokenGroups,
(LPVOID)pGroups, BUFF_SIZE,
&dwSize );
CloseHandle ( hToken );
if ( !bSuccess )
return false;
if ( !AllocateAndInitializeSid ( &siaNtAuth, 2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0,0,0,0,0,0, &pAdminSid ) )
return false;
bSuccess = FALSE;
for ( i = 0; (i < pgroups->GroupCount) && !bSuccess; i++ )
{
if ( EqualSid ( pAdminSid, pGroups->Groups[i].Sid ) )
bSuccess = TRUE;
}
FreeSid ( pAdminSid );
return bSuccess;
}
This can be easily modified to check against the thread token to deal with impersonation, or use NetUserGetGroups() instead to deal with any user name. It has, however, one serious drawback: It will only tell you if the user has administrative privileges locally, but cannot be used to check for domain admin.
More recently, Knowledge Base article Q118626 suggested using a variation of this approach
involving the creation of an ACL with the local admin SID, and then using the AccessCheck()
API to see if the user's access token is granted access to this ACL. The downside is that it's slightly more
complicated, and requires that you have an access token for the user, which is not always possible.
Both methods, however, are not foolproof, for reasons we'll see below. So, another approaach was conceived. This methods is fairly simple, and consists in trying to perform an operation that normally only an administrator can do, like opening and locking the service database:
isAdmin = false ;
SC_HANDLE h = OpenSCManager( NULL, NULL, SC_MANAGER_LOCK ) ;
if ( h )
{
SC_LOCK lock = LockServiceDatabase( h ) ;
if ( lock )
{
UnlockServiceDatabase( lock ) ;
isAdmin = TRUE ;
}
else
{
DWORD lastErr = GetLastError() ;
switch( lastErr )
{
case ERROR_SERVICE_DATABASE_LOCKED: // Should only get this error if we would have
isAdmin = TRUE ; break ; // otherwise succeeded.
case ERROR_ACCESS_DENIED:
case ERROR_INVALID_HANDLE:
default: break ;
}
}
CloseServiceHandle( h ) ;
}
This approach, however, can get you into the same trouble as the first two: It's possible for a user to not be an administrator of the machine/domain, not have access to service database, and still have the required permissions and privileges to perform a given operation. When possible, it's much better to try the operation and be ready to deal with an access denied error, than trying to guess if the user has/hasn't the necessary access to do it.
References and samples: