Оценка 54 Оценить ![]() ![]() ![]() ![]() ![]() ![]()
|
Возможность успешного выполнения некоторых операций в Windows NT зависит от того, имеет ли вызывающий пользователь некоторую привилегию или набор привилегий. Например, чтобы завершить работу системы, необходимо иметь привилегию SeShutdownPrivilege. Хотя это обязанность операционной системы - проверять наличие необходимых привилегий, приложение может делать это, чтобы более тонко настроить свой пользовательский интерфейс. Например, оно может запретить команду завершения работы системы, если у пользователя нет соответствующей привилегии, вместо того, чтобы сообщать ему об этом, когда он попытается воспользоваться этой командой.
Список привилегий, предоставленных пользователю, хранится в системной базе данных учетных записей. При входе пользователя в систему, этот список заносится в токен пользователя, откуда его легко получить с помощью функции GetTokenInformation:
BOOL GetTokenInformation(
HANDLE TokenHandle, // описатель токена
TOKEN_INFORMATION_CLASS InformationClass, // тип информации
PVOID TokenInformation, // выходной буфер
DWORD TokenInformationLength, // размер буфера
PDWORD ReturnLength // требуемый размер буфера
);
|
Эта функция позволяет получить различную информацию о токене пользователя, параметр InformationClass указывает требуемый вид информации. Нас интересует список привилегий, поэтому мы будем указывать в этом параметре значение TokenPrivileges. Функция IsPrivilege, приведенная в листинге 1, определяет наличие указанной привилегии с использованием GetTokenInformation.
BOOL IsPrivilege(
IN PCTSTR pszPrivilegeName // имя привилегии
)
{
_ASSERTE(pszPrivilegeName != NULL);
LUID Luid;
HANDLE hToken;
DWORD cbNeeded;
PTOKEN_PRIVILEGES pPriv;
// получаем идентификатор привилегии
if (!LookupPrivilegeValue(NULL, pszPrivilegeName, &Luid))
return FALSE;
// получаем токен текущего потока
if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken))
{
if (GetLastError() != ERROR_NO_TOKEN)
return FALSE;
// получаем токен процесса
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
return FALSE;
}
// определяем размер буфера, необходимый для получения
// всех привилегий
if (!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &cbNeeded))
{
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
DWORD dwError = GetLastError();
CloseHandle(hToken);
return SetLastError(dwError), FALSE;
}
}
// выделяем память для выходного буфера
pPriv = (PTOKEN_PRIVILEGES)_alloca(cbNeeded);
_ASSERTE(pPriv != NULL);
// получаем список привилегий
if (!GetTokenInformation(hToken, TokenPrivileges, pPriv, cbNeeded,
&cbNeeded))
{
DWORD dwError = GetLastError();
CloseHandle(hToken);
return SetLastError(dwError), FALSE;
}
CloseHandle(hToken);
// проходим по списку привилегий и проверяем, есть ли в нем
// указанная привилегия
for (UINT i = 0; i < pPriv->PrivilegeCount; i++)
{
if (pPriv->Privileges[i].Luid.LowPart == Luid.LowPart &&
pPriv->Privileges[i].Luid.HighPart == Luid.HighPart)
return TRUE;
}
SetLastError(ERROR_SUCCESS);
return FALSE;
}
|
Своим первым действием, функция IsPrivilege получает идентификатор привилегии по ее имени. Хотя привилегии идентифицируются текстовыми строками, при загрузке системы каждой привилегии сопоставляется 64-битный идентификатор, используемый в дальнейшем для сравнения привилегий. Функция LookupPrivilegeValue позволяет получить по имени привилегии ее идентификатор.
Затем функция открывает описатель токена, представляющего текущего пользователя, учитывая при этом возможность имперсонации. Механизм имперсонации в Windows NT позволяет некоторому потоку процесса временно выполнять действия от лица другого пользователя, нежели пользователя изначально создавшего процесс. Поэтому функция сначала пытается открыть токен, связанный с текущим потоком, так как в случае имперсонации этот токен будет представлять имперсонируемого пользователя. Если же поток не имеет ассоциированного с ним токена, функция использует OpenProcessToken, чтобы получить токен, назначенный процессу.
После того, как описатель токена получен, функция вызывает GetTokenInformation дважды: первый раз для того, чтобы определить требуемый размер буфера, и второй раз - чтобы заполнить буфер списком привилегий. В заключение, функция проходит по списку в поиске идентификатора привилегии, указанной в качестве параметра функции.
Как известно, даже если привилегия присутствует в токене пользователя, она может находится в двух состояниях: включенном (enabled) или выключенном (disabled). Некоторые функции Win32 API требуют, чтобы привилегия была явно включена приложением с помощью AdjustTokenPrivileges, некоторые же самостоятельно включают необходимые привилегии. Если необходимо определить, включена ли некоторая привилегия в токене пользователя, для этого можно воспользоваться функцией PrivilegeCheck, что иллюстрируется функцией IsPrivilegeEnabled, приведенной в листинге 2.
BOOL IsPrivilegeEnabled(
IN PCTSTR pszPrivilegeName
)
{
_ASSERTE(pszPrivilegeName != NULL);
HANDLE hToken;
PRIVILEGE_SET PrivSet;
BOOL bEnabled = FALSE;
PrivSet.PrivilegeCount = 1;
PrivSet.Control = 0;
PrivSet.Privilege[0].Attributes = 0;
// получаем идентификатор привилегии
if (!LookupPrivilegeValue(NULL, pszPrivilegeName,
&PrivSet.Privilege[0].Luid))
return FALSE;
// получаем токен текущего потока
if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken))
{
if (GetLastError() != ERROR_NO_TOKEN)
return FALSE;
// получаем токен процесса
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
return FALSE;
}
// проверяем статус привилегии
if (!PrivilegeCheck(hToken, &PrivSet, &bEnabled))
{
DWORD dwError = GetLastError();
CloseHandle(hToken);
return SetLastError(dwError), FALSE;
}
CloseHandle(hToken);
SetLastError(0);
return bEnabled;
}
|
Применение описанных в этой статье методов демонстрирует тестовое приложение PrivChk. Это консольное приложение, которое, будучи запущенным без параметров, выдает список привилегий текущего пользователя. Если же в качестве параметра указано имя конкретной привилегии, программа отображает ее состояние, используя функции IsPrivilege и IsPrivilegeEnabled.
Оценка 54 Оценить ![]() ![]() ![]() ![]() ![]() ![]()
|