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: