Neeraj Barnwal
Neeraj Barnwal

Reputation: 91

How to check whether a Windows user has admin privileges in C?

Is there any way of conditionally checking (strictly in C) whether a Windows user has administrator privileges or not?

I wish to do this programmatically (not simply telling the user to "Run as Administrator").

Upvotes: 5

Views: 6472

Answers (3)

Stéphane
Stéphane

Reputation: 20350

A slightly different (and shorter? easier?) method adapted from some hints on MSDN:

PSID administrators_group = NULL;
SID_IDENTIFIER_AUTHORITY nt_authority = SECURITY_NT_AUTHORITY;
BOOL result = AllocateAndInitializeSid( &nt_authority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &administrators_group);
BOOL is_user_admin = FALSE;
if (result)
{
    CheckTokenMembership(NULL, administrators_group, &is_user_admin);
    FreeSid(administrators_group);
}

if (is_user_admin)
{
    // do something here for admin users...
}

Upvotes: 6

Jerry Coffin
Jerry Coffin

Reputation: 490178

There are a couple of fundamentally different ways. The most common is, unfortunately, rather tedious. It involves finding the current user's SID, then finding the groups to which he belongs, and then finding whether one of them is the Administrators group:

#include <windows.h> 
#include <vector>

bool is_administrator() { 
    HANDLE access_token; 
    DWORD buffer_size = 0; 
    PSID admin_SID; 
    TOKEN_GROUPS *group_token = NULL; 
    SID_IDENTIFIER_AUTHORITY NT_authority = SECURITY_NT_AUTHORITY; 

    if (!OpenProcessToken(GetCurrentProcess(),TOKEN_READ,&access_token)) 
        return false; 

    GetTokenInformation( 
        access_token, 
        TokenGroups, 
        group_token, 
        0, 
        &buffer_size 
        ); 

    std::vector<char> buffer(buffer_size);

    group_token = 
        reinterpret_cast<TOKEN_GROUPS*>(&buffer[0]); 

    bool succeeded = GetTokenInformation( 
        access_token, 
        TokenGroups, 
        group_token, 
        buffer_size, 
        &buffer_size 
        ); 

    CloseHandle(access_token); 
    if (!succeeded) 
        return false;

    if (!AllocateAndInitializeSid( 
        &NT_authority, 
        2, 
        SECURITY_BUILTIN_DOMAIN_RID, 
        DOMAIN_ALIAS_RID_ADMINS, 
        0,0,0,0,0,0, 
        &admin_SID 
        )) 
    {
        return false; 
    }

    bool found=false; 
    for(int i=0; !found && i < group_token->GroupCount; i++) 
        found = EqualSid(admin_SID,group_token->Groups[i].Sid); 
    FreeSid(admin_SID); 
    return found; 
} 

There's another way that's a bit simpler though:

bool is_administrator() 
{ 
        bool result; 
        DWORD rc; 
        wchar_t user_name[256]; 
        USER_INFO_1 *info; 
        DWORD size = sizeof( user_name ); 
        GetUserNameW( user_name, &size); 
        rc = NetUserGetInfo( NULL, user_name, 1, (byte **) &info ); 
        if ( rc != NERR_Success ) 
                return false; 
        result = info->usri1_priv == USER_PRIV_ADMIN; 
        NetApiBufferFree( info ); 
        return result; 
} 

In either case, if you have a domain, things can be a little hairy, because a particular user might be an administrator on the local machine, but not on the domain, or vice versa. Finding the info doesn't necessarily change much, but you may have to think a bit to figure out what you really want.

Edit: As @Benj pointed out, the first method could really use a bit of updating. While I already fixed the obvious leak it had, it's still a huge, monolithic function with no exception safety, and generally rather outdated coding style. Perhaps a small update is in order:

#include <windows.h> 
#include <vector>
#include <algorithm>

class sid {
    PSID s;
public:
    sid(SID_IDENTIFIER_AUTHORITY auth, std::vector<DWORD> sub_auths) {
        DWORD count = sub_auths.size();
        sub_auths.resize(7, DWORD());

        if (!AllocateAndInitializeSid( 
            &auth,
            count, 
            sub_auths[0], sub_auths[1], sub_auths[2], sub_auths[3],
            sub_auths[4], sub_auths[5], sub_auths[6], sub_auths[7],
            &s 
            )) 
        {
            throw std::runtime_error("Unable to allocate Admin SID");
        }
    }

    sid(PSID const &p=NULL) : s(p) {}
    bool operator==(sid const &r) const { return EqualSid(s, r.s); }
};

class access_token {
    HANDLE token;
public:
    access_token(HANDLE PID=GetCurrentProcess(), DWORD access=TOKEN_READ) {
        if (!OpenProcessToken(PID, access, &token))
            throw std::runtime_error("Unable to open process token");
    }
    operator HANDLE() { return token; }
    ~access_token() { CloseHandle(token); }
};

std::vector<sid> get_group_sids() {
    DWORD buffer_size = 0; 
    TOKEN_GROUPS *group_token = NULL; 
    std::vector<sid> groups;
    access_token token;

    GetTokenInformation(token, TokenGroups, group_token, 0, &buffer_size);

    std::vector<char> buffer(buffer_size);

    group_token = reinterpret_cast<TOKEN_GROUPS*>(&buffer[0]);

    if (GetTokenInformation(token, TokenGroups, group_token, buffer_size, &buffer_size))
        for (int i=0; i<group_token->GroupCount; i++)
            groups.push_back(group_token->Groups[i].Sid);
    return groups;
}

bool is_administrator() {
    std::vector<sid> groups = get_group_sids();

    SID_IDENTIFIER_AUTHORITY NT_authority = SECURITY_NT_AUTHORITY;
    std::vector<DWORD> sub_auths;

    sub_auths.push_back(SECURITY_BUILTIN_DOMAIN_RID);
    sub_auths.push_back(DOMAIN_ALIAS_RID_ADMINS);

    sid admin_SID(NT_authority, sub_auths);

    return std::find(groups.begin(), groups.end(), admin_SID) != groups.end();
}

#ifdef TEST
#include <iostream>
#include <iomanip>

int main() {
    std::cout << std::boolalpha << is_administrator() << "\n";
}

#endif

Upvotes: 7

Benj
Benj

Reputation: 32398

Sure you need to call:

OpenThreadToken()

To get the user's token.

GetTokenInformation(hToken, TokenGroups, NULL, 0, &dwNeeded)

To get the size of the token group information (and allocate enough space)

GetTokenInformation(hToken, TokenGroups, pTokenGroups, dwSize, &dwNeeded)

To get the local groups

AllocateAndInitializeSid(&SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidAdministrators);

To get the SID of the Administrator

EqualSid()

To compare that SID with the SIDs in your local groups.

Upvotes: 3

Related Questions