简体   繁体   English

Windows冒充的奇怪行为

[英]Odd behavior of windows Impersonation

My windows application may require administrative privileges for some of it's sections. 我的Windows应用程序可能需要某些部分的管理权限。 For those cases, I'd like to ask the user for an administrator credentials, and use the following code to impersonate the administrator: 对于这些情况,我想询问用户管理员凭据,并使用以下代码来模拟管理员:

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()
}

where the function IsUserAdmin() has been taken from MSDN , and goes as follow: 函数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);
}

Scenario 1: 场景1:
Running the application from an administrator account. 从管理员帐户运行应用程序。

  1. calling IsUserAdmin() => returns TRUE (good) 调用IsUserAdmin()=>返回TRUE (好)

  2. calling impersonate ("non-admin-user" , "domain", "password" ) => returns FALSE (good!) 调用impersonate(“非管理员用户”,“域名”,“密码”)=>返回FALSE (好!)

Scenario 2: 场景2:
Running the application from a non-administrator account, using runas.exe from an administrator account. 使用管理员帐户中的runas.exe从非管理员帐户运行应用程序。

  1. calling IsUserAdmin() => returns FALSE (good) 调用IsUserAdmin()=>返回FALSE (好)

  2. calling impersonate ("administrator" , "domain", "password" ) => returns FALSE (not good!) 调用impersonate(“administrator”,“domain”,“password”)=>返回FALSE (不好!)

Scenario 3: 场景3:
Running the application directly from a non-administrator account. 直接从非管理员帐户运行应用程序。

  1. calling IsUserAdmin() => returns FALSE (good) 调用IsUserAdmin()=>返回FALSE (好)

  2. calling impersonate ("administrator" , "domain", "password" ) => returns FALSE (not good!) 调用impersonate(“administrator”,“domain”,“password”)=>返回FALSE (不好!)

All scenarios print both step 1 and step 2 . 所有方案都打印step 1step 2

As far as I can tell, the above should have assure impersonation, given the a legitimate credentials. 据我所知,鉴于合法的证书,上述内容应该保证冒充。 what am I missing here? 我在这里想念的是什么?

You really should not be programmably checking for admin rights before doing a task that requires admin rights. 在执行需要管理员权限的任务之前,您实际上不应该以可编程方式检查管理员权限。 Just attempt the task unconditionally and let the API tell you if the task failed due to insufficient rights, and if so then you can decide whether to just fail the task with an error or prompt the user for credentials and try the task again. 只需无条件地尝试该任务,并让API告诉您任务是否由于权限不足而失败,如果是,那么您可以决定是否仅使用错误使任务失败,或者提示用户输入凭据并再次尝试该任务。

If you are trying to play nice with UAC, what you are supposed to do is implement your admin code as a separate process or COM object, then you can run that process/COM in an elevated state when needed without having to elevate your main process. 如果你想与UAC玩得很好,你应该做的是将管理代码作为一个单独的进程或COM对象实现,然后你可以在需要时以升级状态运行该进程/ COM,而不必提升你的主进程。 Let the OS prompt the user for admin credentials (and decide how that prompt should look) when the OS needs it, don't do it manually yourself. 当操作系统需要时,让操作系统提示用户提供管理员凭据(并确定该提示应该如何显示),不要自己手动执行。

Remy's answer is spot on, what you're trying to do is not the correct solution for your scenario. Remy的回答很明显,你要做的不是你的场景的正确解决方案。 However, according to the documentation your code should work as intended. 但是,根据文档,您的代码应该按预期工作。 There appears to be two reasons why it doesn't: 似乎有两个原因:

  1. It seems that, contrary to the documentation, you cannot impersonate a token obtained via LogonUser at a higher impersonation level than SecurityIdentification without holding SeImpersonatePrivilege . 与文档相反,您似乎无法在没有保留SeImpersonatePrivilege情况下模拟比SecurityIdentification更高的模拟级别通过LogonUser获取的令牌。 [Tested on Windows 7 SP1 x64.] [在Windows 7 SP1 x64上测试。]

  2. The CheckTokenMembership function does not work properly if you pass NULL for the token and the thread's impersonation level is SecurityIdentification . 如果为令牌传递NULL并且线程的模拟级别为SecurityIdentification ,则CheckTokenMembership函数无法正常工作。 [ditto.] [同上。]

You can work around problem 2 easily enough by explicitly extracting the impersonation token using OpenThreadToken rather than passing NULL as the token, as shown below. 通过使用OpenThreadToken显式提取模拟令牌而不是将NULL作为令牌传递,您可以轻松地解决问题2,如下所示。 Alternately, you could duplicate the primary token using DuplicateToken and pass the duplicated token to CheckTokenMembership . 或者,您可以使用DuplicateToken主令牌,并将复制的令牌传递给CheckTokenMembership If all you want is to check the contents of the token, that would be a more efficient solution. 如果你想要的只是检查令牌的内容,那将是一个更有效的解决方案。

I am not aware of any workaround to problem 1, other than launching a subprocess in the new context to do the work on your behalf. 我不知道问题1的任何解决方法,除了在新上下文中启动子进程以代表您完成工作。 Of course, as Remy pointed out, this is what you should be doing anyway. 当然,正如雷米指出的那样,这就是你应该做的事情。

Here is some code I used in testing, for reference: 以下是我在测试中使用的一些代码,供参考:

#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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM