如何使用CredUIPromptForWindowsCredentials验证凭据

时间:2018-10-12 20:02:26

标签: c++ winapi msdn

我不知道为什么无法使用CredUnPackAuthenticationBufferW解包CredUIPromptForWindowsCredentials中使用的身份验证缓冲区,我总是收到ERROR_INSUFFICIENT_BUFFER错误。 感谢您的帮助。

std::wstring caption = L"Caption";
std::wstring msg= L"Msg";
CREDUI_INFOW credui = {};
credui.cbSize = sizeof(credui);
credui.hwndParent = nullptr;
credui.pszMessageText = msg.c_str();
credui.pszCaptionText = caption.c_str();
credui.hbmBanner = nullptr;

ULONG  authPackage = 0;
LPVOID outCredBuffer = nullptr;
ULONG  outCredSize = 0;
BOOL   save = false;

LPWSTR pszUserName = nullptr;
DWORD pcchlMaxUserName = 0;
LPWSTR pszDomainName = nullptr;
DWORD pcchMaxDomainName = 0;
LPWSTR pszPassword = nullptr;
DWORD pcchMaxPassword = 0;

DWORD result = CredUIPromptForWindowsCredentialsW(&credui,
                                                0,
                                                &authPackage,
                                                nullptr,
                                                0,
                                                &outCredBuffer,
                                                &outCredSize,
                                                &save,
                                                CREDUIWIN_ENUMERATE_ADMINS);


std::cout <<CredUnPackAuthenticationBufferW(CRED_PACK_PROTECTED_CREDENTIALS
                                ,outCredBuffer
                                ,outCredSize
                                ,pszUserName
                                ,&pcchlMaxUserName
                                ,pszDomainName
                                ,&pcchMaxDomainName
                                ,pszPassword
                                ,&pcchMaxPassword) << std::endl;

std::cout << GetLastError() << std::endl; // out put 122 == ERROR_INSUFFICIENT_BUFFER 

2 个答案:

答案 0 :(得分:1)

这是典型的winapi模式-api必须在内存缓冲区中返回一些信息。而是自己分配缓冲区-它使调用者有义务分配缓冲区。 因此,调用者必须自己分配缓冲区,并将其指针和大小传递给api。 api检查缓冲区大小-如果它有足够大的填充信息来缓冲,否则返回ERROR_INSUFFICIENT_BUFFER(假设没有其他错误)或有时返回ERROR_MORE_DATA。哪个具体错误导致了ERROR_INSUFFICIENT_BUFFERERROR_MORE_DATA通常直接针对api调用记录。这2个错误之间的区别:ERROR_INSUFFICIENT_BUFFER-表示ERROR_MORE_DATA表示没有数据填充到缓冲区,而CredUnPackAuthenticationBufferW表示返回了一些数据,但不完整。 在这种情况下,api通过一些out参数返回给用户所需的缓冲区大小。通常,这是通过相同的inout参数-指向DWORD的指针完成的。在输入中指定用户分配的缓冲区的大小,在输出中-指定所需的缓冲区大小或返回数据的大小

经常需要哪个缓冲区大小-开始时未知。因此,我们需要先使用0个大小的缓冲区调用api,或者分配一些假定足够的缓冲区大小。如果缓冲区不足,请重新分配或扩展缓冲区,然后再次调用api。对于某些api(例如do/while(error == ERROR_INSUFFICIENT_BUFFER/ERROR_MORE_DATA)),所需的输出缓冲区不会随时间变化(如果输入参数未更改),但通常的输出缓冲区大小可能在两次调用之间发生变化-即使第二次调用的缓冲区大小由第一次调用返回,也会失败缓冲区大小错误(因为在两次调用之间返回的数据可能会增长)。在这种情况下,需要在ULONG cred() { CREDUI_INFO ci = { sizeof(ci) }; BOOL bSave = FALSE; PVOID pvOutAuthBuffer; ULONG ulOutAuthBufferSize; ULONG ulAuthPackage = 0; ULONG dwError = CredUIPromptForWindowsCredentials( &ci, NOERROR, &ulAuthPackage, 0, 0, &pvOutAuthBuffer, &ulOutAuthBufferSize, &bSave, CREDUIWIN_ENUMERATE_ADMINS ); if (dwError == NOERROR) { ULONG cchUserName = 0; ULONG cchPassword = 0; ULONG cchDomain = 0; static volatile UCHAR guz = 0; PWSTR stack = (PWSTR)alloca(guz); PWSTR szUserName = 0, szPassword = 0, szDomainName = 0; ULONG cchNeed, cchAllocated = 0; do { if (cchAllocated < (cchNeed = cchUserName + cchPassword + cchDomain)) { szUserName = (PWSTR)alloca((cchNeed - cchAllocated) * sizeof(WCHAR)); cchAllocated = (ULONG)(stack - szUserName); szPassword = szUserName + cchUserName; szDomainName = szPassword + cchPassword; } dwError = CredUnPackAuthenticationBuffer( CRED_PACK_PROTECTED_CREDENTIALS, pvOutAuthBuffer, ulOutAuthBufferSize, szUserName, &cchUserName, szDomainName, &cchDomain, szPassword, &cchPassword) ? NOERROR : GetLastError(); if (dwError == NOERROR) { DbgPrint("%S@%S %S\n", szDomainName, szUserName, szPassword); break; } } while (dwError == ERROR_INSUFFICIENT_BUFFER); CoTaskMemFree(pvOutAuthBuffer); } return dwError; } 循环中调用api。但是即使输出缓冲区不随时间变化,我们最好还是在内部使用单个api调用而不是2个api调用循环。

具体案例代码看起来像

MySkill look up code {XXX_ONE} {XXX_TWO} {XXX_THREE} {XXX_FOUR} {XXX_FIVE} {XXX_SIX}

答案 1 :(得分:0)

@RbMm-你是对的!我用LogonUser对其进行了测试,并且效果很好。谢谢。 对于现成的解决方案,我得到了:

bool Authenticate_ADMIN_User(std::wstring caption, std::wstring msg, int maxReAsks = 0)
{
    CREDUI_INFOW credui   = {};
    credui.cbSize         = sizeof(credui);
    credui.hwndParent     = nullptr;
    credui.pszMessageText = msg.c_str();
    credui.pszCaptionText = caption.c_str();
    credui.hbmBanner      = nullptr;

    ULONG  authPackage   = 0,
           outCredSize   = 0;
    LPVOID outCredBuffer = nullptr;
    BOOL   save          = false;

    DWORD err   = 0;
    int   tries = 0;

    bool reAsk = false;

    do
    {
      tries++;

      if(CredUIPromptForWindowsCredentialsW(&credui,
                                         err,
                                         &authPackage,
                                         nullptr,
                                         0,
                                         &outCredBuffer,
                                         &outCredSize,
                                         &save,
                                         CREDUIWIN_ENUMERATE_ADMINS)

              != ERROR_SUCCESS)
          return false;


      ULONG cchUserName = 0;
      ULONG cchPassword = 0;
      ULONG cchDomain   = 0;
      ULONG cchNeed, cchAllocated = 0;

      static volatile UCHAR guz = 0;

      PWSTR stack = (PWSTR)alloca(guz);
      PWSTR szUserName = nullptr, szPassword = nullptr, szDomainName = nullptr;

      BOOL ret;

      do{
          if (cchAllocated < (cchNeed = cchUserName + cchPassword + cchDomain))
          {
              szUserName = (PWSTR)alloca((cchNeed - cchAllocated) * sizeof(WCHAR));
              cchAllocated = (ULONG)(stack - szUserName);
              szPassword = szUserName + cchUserName;
              szDomainName = szPassword + cchPassword;
          }

          ret = CredUnPackAuthenticationBuffer(
              CRED_PACK_PROTECTED_CREDENTIALS , outCredBuffer, outCredSize, szUserName, &cchUserName,
              szDomainName, &cchDomain, szPassword,
              &cchPassword);

      }while(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER);


        SecureZeroMemory(outCredBuffer, outCredSize);
        CoTaskMemFree(outCredBuffer);

        HANDLE handle = nullptr;

        if (LogonUser(szUserName, 
                      szDomainName,
                      szPassword,
                      LOGON32_LOGON_INTERACTIVE, 
                      LOGON32_PROVIDER_DEFAULT,
                      &handle)) 
        {

          CloseHandle(handle);
          return true;
        }

        else
        {
          err = ERROR_LOGON_FAILURE;
          reAsk = true;
        }


    }while(reAsk && tries < maxReAsks);

    return false;
}