Моему приложению 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:
Запуск приложения из учетной записи администратора.
вызов IsUserAdmin () => возвращает TRUE
(хорошо)
Вызов безличный («не администратор-пользователь», «домен», «пароль») => возвращает FALSE
(хорошо!)
Сценарий 2:
Запуск приложения из учетной записи не администратора, используя runas.exe
из учетной записи администратора.
вызов IsUserAdmin () => возвращает FALSE
(хорошо)
Вызов безличный («администратор», «домен», «пароль») => возвращает FALSE
(нехорошо!)
Сценарий 3:
Запуск приложения непосредственно из учетной записи без прав администратора.
вызов IsUserAdmin () => возвращает FALSE
(хорошо)
Вызов безличный («администратор», «домен», «пароль») => возвращает FALSE
(нехорошо!)
Все сценарии печатают оба step 1
а также step 2
,
Насколько я могу судить, вышесказанное должно было обеспечить имитацию, учитывая законные полномочия. что мне здесь не хватает?
Вы действительно не должны программно проверять права администратора, прежде чем выполнять задачу, требующую прав администратора. Просто попробуйте выполнить задачу безоговорочно, и пусть API сообщит вам, если задача не выполнена из-за недостаточных прав, и если это так, то вы можете решить, просто ли выполнить задачу с ошибкой или запросить учетные данные у пользователя и повторить попытку.
Если вы пытаетесь хорошо играть с UAC, то, что вы должны сделать, это реализовать свой код администратора как отдельный процесс или объект COM, тогда вы можете запустить этот процесс / COM в повышенном состоянии, когда это необходимо, без необходимости повышать уровень основного процесса. , Позвольте ОС запрашивать у пользователя учетные данные администратора (и решайте, как должна выглядеть эта подсказка), когда ОС нуждается в ней, не делайте это вручную самостоятельно.
Ответ Реми точен: то, что вы пытаетесь сделать, не является правильным решением для вашего сценария. Однако, согласно документации, ваш код должен работать так, как задумано. Кажется, есть две причины, почему это не так:
Кажется, что вопреки документации, вы не можете выдать себя за токен, полученный с помощью LogonUser
на более высоком уровне олицетворения, чем SecurityIdentification
без удержания SeImpersonatePrivilege
, [Проверено на Windows 7 SP1 x64.]
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;
}