【问题标题】:Odd behavior of windows Impersonationwindows模拟的奇怪行为
【发布时间】:2013-12-27 03:20:58
【问题描述】:

我的 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()has been taken from 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. 调用 impersonate ("non-admin-user" , "domain", "password" ) => 返回 FALSE(好!)

场景 2:
从非管理员帐户运行应用程序,使用管理员帐户的runas.exe

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

  2. 调用 impersonate ("administrator" , "domain", "password" ) => 返回 FALSE(不好!)

场景 3:
从非管理员帐户直接运行应用程序。

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

  2. 调用 impersonate ("administrator" , "domain", "password" ) => 返回 FALSE(不好!)

所有场景都打印step 1step 2

据我所知,鉴于合法凭据,上述内容应确保模拟。我在这里错过了什么?

【问题讨论】:

  • 您说impersonate() 返回 FALSE,但您没有指出它到底在哪里失败。 impersonate() 有 4 个故障点可能导致它返回 FALSE - 如果 LogonUser() 失败,如果 ImpersonateLoggedOnUser() 失败,如果 AllocateAndInitializeSid() 失败,或者如果 CheckTokenMembership() 失败。你需要调试你的代码并找出到底是什么失败了。

标签: c++ windows winapi authentication impersonation


【解决方案1】:

在执行需要管理员权限的任务之前,您真的不应该以编程方式检查管理员权限。只需无条件地尝试该任务,并让 API 告诉您任务是否由于权限不足而失败,如果是,您可以决定是仅因错误而使任务失败,还是提示用户输入凭据并再次尝试该任务。

如果您想很好地使用 UAC,您应该将管理代码实现为单独的进程或 COM 对象,然后您可以在需要时以提升状态运行该进程/COM,而无需提升你的主要过程。让操作系统在操作系统需要时提示用户输入管理员凭据(并决定提示的外观),不要自己手动操作。

【讨论】:

    【解决方案2】:

    雷米的回答是正确的,您尝试做的不是您的场景的正确解决方案。但是,根据文档,您的代码应该按预期工作。它没有出现的原因似乎有两个:

    1. 看来,与文档相反,如果不持有SeImpersonatePrivilege,则无法以比SecurityIdentification 更高的模拟级别模拟通过LogonUser 获得的令牌。 [在 Windows 7 SP1 x64 上测试。]

    2. 如果您为令牌传递NULL 并且线程的模拟级别为SecurityIdentification,则CheckTokenMembership 函数无法正常工作。 [同上]

    您可以通过使用OpenThreadToken 显式提取模拟令牌而不是将NULL 作为令牌传递来轻松解决问题2,如下所示。或者,您可以使用DuplicateToken 复制主令牌并将复制的令牌传递给CheckTokenMembership。如果您只想检查令牌的内容,那将是一个更有效的解决方案。

    我不知道问题 1 的任何解决方法,除了在新上下文中启动一个子进程来代表您完成工作。当然,正如 Remy 指出的那样,无论如何这都是你应该做的。

    这是我在测试中使用的一些代码,供参考:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-06-24
      • 1970-01-01
      • 2021-03-04
      • 1970-01-01
      • 2012-08-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多