Сообщений 13    Оценка 159 [+1/-0]         Оценить  
Система Orphus

Как узнать, есть ли у пользователя права администратора?

Автор: Игорь Вартанов
Опубликовано: 01.02.2002
Исправлено: 13.03.2005
Версия текста: 0.99

Демонстрационное приложение к ОБЩИМ СООБРАЖЕНИЯМ
Демонстрационное приложение к СПЕЦИАЛЬНЫМ СООБРАЖЕНИЯМ

В связи с распространением Windows NT/2000/XP перед разработчиком может встать вопрос адаптации работы приложения применительно к указанным операционным системам. И немаловажным аспектом работы приложения будет являться его осведомленность о наличии необходимых прав, поскольку указанные системы активно используют разграничение прав для пользователей.

Общие соображения

Чтобы узнать, есть ли у пользователя права администратора, программа должна определить, принадлежит ли пользователь к группе "Администраторы" на данной рабочей станции.

В первую очередь для этого необходимо создать идентификатор безопасности группы "Администраторы". Во времена былинные (до выхода Windows2000) после этого необходимо было получить токен процесса, а у токена запросить принадлежность его (а точнее пользователя, от имени которого выполняется процесс), к группам. Затем, перебирая полученные идентификаторы безопасности групп процесса, последовательно сравнить их с указанным идентификатором группы "Администраторы".

#pragma comment(lib, "advapi32")

BOOL IsUserAdmin( PBOOL pbAdmin )
{
#if WINVER < 0x0500
    HANDLE hAccessToken       = NULL;
    PBYTE  pInfoBuffer        = NULL;
    DWORD  dwInfoBufferSize   = 1024;    // начальный размер буфера
    PTOKEN_GROUPS ptgGroups   = NULL;
#endif
    PSID   psidAdministrators = NULL;
    SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
    BOOL   bResult = FALSE;
    
    __try
    {
        // инициализация идентификатора безопасности
        if( !AllocateAndInitializeSid( 
            &siaNtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID,
            DOMAIN_ALIAS_RID_ADMINS, 0,0,0,0,0,0, &psidAdministrators ) )
                __leave;
        
#if WINVER < 0x0500
        // код для предшественников Windows2000

        // откроем токен процесса
        if( !OpenProcessToken( GetCurrentProcess(),TOKEN_QUERY,&hAccessToken ) )
            __leave;

        do  // выделение буфера для запрошенной из токена информации
        {
            if( pInfoBuffer )
                delete pInfoBuffer;
            pInfoBuffer = new BYTE[dwInfoBufferSize];
            if( !pInfoBuffer )
                __leave;
            SetLastError( 0 );
            if( !GetTokenInformation( 
                    hAccessToken, 
                    TokenGroups, pInfoBuffer,
                    dwInfoBufferSize, &dwInfoBufferSize ) &&
                ( ERROR_INSUFFICIENT_BUFFER != GetLastError() )
               )
                __leave;
            else
                ptgGroups = (PTOKEN_GROUPS)pInfoBuffer;
        }
        while( GetLastError() ); // если была ошибка, значит начального размера недостаточно
        
        // переберем идентификаторы безопасности процесса в поисках необходимого нам
        for( UINT i = 0; i < ptgGroups->GroupCount; i++ )
        {
            if( EqualSid(psidAdministrators,ptgGroups->Groups[i].Sid) )
            {
                *pbAdmin = TRUE;
                bResult  = TRUE;
                __leave;
            }
        } 
#else
        // код для Windows2000
        bResult = CheckTokenMembership( NULL, psidAdministrators, pbAdmin );            
#endif
    }

    __finally
    {
#if WINVER < 0x0500
        if( hAccessToken )
            CloseHandle( hAccessToken );
        if( pInfoBuffer )
            delete pInfoBuffer;
#endif
        if( psidAdministrators )
            FreeSid( psidAdministrators );
    }
    
    return bResult;
}

Внимательный читатель наверняка заметил, что версия указанного кода для Windows2000 гораздо более "худосочная" - дело в том, что CheckTokenMembership() самостоятельно открывает токен доступа и выясняет, включен ли в токен указанный идентификатор безопасности, т.е. выполняет все те процедуры, которые на предыдущих версиях WindowsNT мы должны были реализовать своими силами.

ПРИМЕЧАНИЕ
IsUserAdmin.exe (релиз-версия) в демонстрационном проекте скомпилирован под Windows2000. Чтобы выполнить код в WindowsNT, необходимо изменить в настройках проекта директиву препроцессора WINVER на 0x0400 и пересобрать проект с новыми настройками.

Специальные соображения

Для программирующего на C/C++ приведенный код не представляет собой ничего особенного. И в то же время программист, работающий в среде, предположим, Visual Basic получит немалую головную боль при попытке реализовать вышеприведенный код. Однако, об этих программистах фирма Microsoft уже позаботилась (хотя, возможно, не все об этом догадываются :-) ).

Дело в том, что системная библиотека setupapi.dll уже содержит реализацию приведенной функции, мало того, она имеет то же название, но несколько иной прототип:

BOOL WINAPI IsUserAdmin();

Объявление этой функции на VB будет выглядеть следующим образом:

Declare Function IsUserAdmin Lib "setupapi.dll" () As Long

Надеюсь, для программирующих в других средах, впрочем, так же как и для C/C++ программистов, не составит труда самостоятельно воспользоваться приведенной информацией.


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
    Сообщений 13    Оценка 159 [+1/-0]         Оценить