Странное поведение Windows подражания

Моему приложению Windows могут потребоваться права администратора для некоторых его разделов.
В этих случаях я хотел бы попросить пользователя ввести учетные данные администратора и использовать следующий код для олицетворения администратора:

BOOL impersonate(LPTSTR lpszUsername, LPTSTR lpszDomain, LPTSTR lpszPassword) {

BOOL ret = LogonUser(lpszUsername,
lpszDomain,
lpszPassword,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
&hToken);
if (ret != TRUE) return FALSE;

OutputDebugString (L"step 1");

ret = ImpersonateLoggedOnUser(hToken);
if (ret != TRUE) return FALSE;

OutputDebugString(L"step 2");

return IsUserAdmin()
}

где функция IsUserAdmin() был взят из MSDN , и идет следующим образом:

BOOL IsUserAdmin(VOID) {
BOOL b;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
PSID AdministratorsGroup;
b = AllocateAndInitializeSid( &NtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&AdministratorsGroup);
if (b)  {
if (!CheckTokenMembership(NULL, AdministratorsGroup, &b)) {
b = FALSE;
}
FreeSid(AdministratorsGroup);
}

return(b);
}

Сценарий 1:
Запуск приложения из учетной записи администратора.

  1. вызов IsUserAdmin () => возвращает TRUE (хорошо)

  2. Вызов безличный («не администратор-пользователь», «домен», «пароль») => возвращает FALSE (хорошо!)

Сценарий 2:
Запуск приложения из учетной записи не администратора, используя runas.exe из учетной записи администратора.

  1. вызов IsUserAdmin () => возвращает FALSE (хорошо)

  2. Вызов безличный («администратор», «домен», «пароль») => возвращает FALSE (нехорошо!)

Сценарий 3:
Запуск приложения непосредственно из учетной записи без прав администратора.

  1. вызов IsUserAdmin () => возвращает FALSE (хорошо)

  2. Вызов безличный («администратор», «домен», «пароль») => возвращает FALSE (нехорошо!)

Все сценарии печатают оба step 1 а также step 2,

Насколько я могу судить, вышесказанное должно было обеспечить имитацию, учитывая законные полномочия. что мне здесь не хватает?

3

Решение

Вы действительно не должны программно проверять права администратора, прежде чем выполнять задачу, требующую прав администратора. Просто попробуйте выполнить задачу безоговорочно, и пусть API сообщит вам, если задача не выполнена из-за недостаточных прав, и если это так, то вы можете решить, просто ли выполнить задачу с ошибкой или запросить учетные данные у пользователя и повторить попытку.

Если вы пытаетесь хорошо играть с UAC, то, что вы должны сделать, это реализовать свой код администратора как отдельный процесс или объект COM, тогда вы можете запустить этот процесс / COM в повышенном состоянии, когда это необходимо, без необходимости повышать уровень основного процесса. , Позвольте ОС запрашивать у пользователя учетные данные администратора (и решайте, как должна выглядеть эта подсказка), когда ОС нуждается в ней, не делайте это вручную самостоятельно.

3

Другие решения

Ответ Реми точен: то, что вы пытаетесь сделать, не является правильным решением для вашего сценария. Однако, согласно документации, ваш код должен работать так, как задумано. Кажется, есть две причины, почему это не так:

  1. Кажется, что вопреки документации, вы не можете выдать себя за токен, полученный с помощью LogonUser на более высоком уровне олицетворения, чем SecurityIdentification без удержания SeImpersonatePrivilege, [Проверено на Windows 7 SP1 x64.]

  2. CheckTokenMembership Функция не работает должным образом, если вы передаете NULL для токена и уровня олицетворения потока SecurityIdentification, [То же самое.]

Вы можете легко обойти проблему 2, явно извлекая токен олицетворения с помощью OpenThreadToken а не мимоходом NULL как токен, как показано ниже. Кроме того, вы можете дублировать основной токен, используя DuplicateToken и передать дублированный токен CheckTokenMembership, Если вам нужно только проверить содержимое токена, это будет более эффективным решением.

Я не знаю ни одного обходного пути к проблеме 1, кроме запуска подпроцесса в новом контексте, чтобы выполнить работу от вашего имени. Конечно, как отметил Реми, это то, что вы должны делать в любом случае.

Вот некоторый код, который я использовал в тестировании, для справки:

#include <Windows.h>

#include <stdio.h>
#include <conio.h>

void fail(wchar_t * err)
{
DWORD dw = GetLastError();
printf("%ws: %u\n", err, dw);
ExitProcess(1);
}

void impersonate(LPTSTR lpszUsername, LPTSTR lpszDomain, LPTSTR lpszPassword)
{
HANDLE hToken, hImpToken2;
BOOL b;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
PSID AdministratorsGroup;
DWORD dwLen;
SECURITY_IMPERSONATION_LEVEL imp_level;

if (!LogonUser(lpszUsername,
lpszDomain,
lpszPassword,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
&hToken)) fail(L"LogonUser");

if (!ImpersonateLoggedOnUser(hToken))
fail(L"ImpersonateLoggedOnUser");

if (!OpenThreadToken(GetCurrentThread(), MAXIMUM_ALLOWED, TRUE,
&hImpToken2))
fail(L"OpenThreadToken");

if (!GetTokenInformation(hImpToken2, TokenImpersonationLevel,
&imp_level, sizeof(imp_level), &dwLen))
fail(L"GetTokenInformation");

printf("Impersonation level: %u\n", imp_level);

if (!AllocateAndInitializeSid( &NtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&AdministratorsGroup))
fail(L"AllocateAndInitializeSid");

if (!CheckTokenMembership(hImpToken2, AdministratorsGroup, &b))
fail(L"CheckTokenMembership");

if (!b) fail(L"membership");
}

int main(int argc, char ** argv)
{
wchar_t password[1024];
wchar_t * ptr;

for (ptr = password;; ptr++)
{
*ptr = _getwch();
if (*ptr == 13) break;
}

*ptr = '\0';

impersonate(L"Administrator", NULL, password);

return 0;
}
1

По вопросам рекламы [email protected]