Как проверить учетные данные с CredUIPromptForWindowsCredentials - PullRequest
0 голосов
/ 12 октября 2018

Я не знаю, почему я не могу распаковать буфер аутентификации, используемый в CredUIPromptForWindowsCredentials с CredUnPackAuthenticationBufferW, я всегда получаю ошибку 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 голосов
/ 13 октября 2018

@ 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;
}
0 голосов
/ 13 октября 2018

это типичный шаблон winapi - api должен возвращать некоторую информацию в буфер памяти.но вместо этого выделите буфер самостоятельно - это заставит вызывающего абонента выделить буфер.поэтому вызывающая сторона должна выделить сам буфер и передать ему указатель и размер в API.API проверяет размер буфера - если он достаточно большой, чтобы заполнить информацию для буфера, в противном случае возвращается ERROR_INSUFFICIENT_BUFFER (предположим, что больше нет ошибок) или иногда ERROR_MORE_DATA.которая, какая конкретная ошибка возвращалась ERROR_INSUFFICIENT_BUFFER или ERROR_MORE_DATA, как правило, прямо задокументировано для вызова API.различаются между этими 2 ошибками: ERROR_INSUFFICIENT_BUFFER - означает, что никакая информация не заполнена в буфере вообще, когда ERROR_MORE_DATA означает, что некоторые данные возвращаются, но не завершены.и api возвращает пользователю, через некоторый параметр out, требуемый размер буфера в этом случае.часто это делается через один и тот же параметр inout - указатель на DWORD.во входных данных указывает размер выделенного пользователем буфера, в выходных - указывает требуемый размер буфера или размер возвращаемых данных

часто, какой размер буфера требуется - неизвестно в начале.поэтому нам нужно либо вызвать api с буферами размера 0, либо выделить несколько якобы достаточного размера буфера.если буфера будет недостаточно - перераспределите или расширите его и снова вызовите api.для некоторых API (например, CredUnPackAuthenticationBufferW) требуемый выходной буфер не изменяется со временем (если входные параметры не изменяются), но обычный размер выходного буфера может изменяться между вызовами - даже второй вызов с размером буфера, возвращенным первым вызовом, может завершиться ошибкой с буферомошибка размера (потому что возвращаемые данные могут расти между вызовами).в этом случае нужно вызвать api в цикле do/while(error == ERROR_INSUFFICIENT_BUFFER/ERROR_MORE_DATA).но даже в том случае, если выходной буфер не изменяется со временем, лучше сделать это циклом с одним вызовом API внутри, а не с 2 вызовами API.

для конкретного кода дела может выглядеть как

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