我不知道为什么无法使用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
答案 0 :(得分:1)
这是典型的winapi模式-api必须在内存缓冲区中返回一些信息。而是自己分配缓冲区-它使调用者有义务分配缓冲区。
因此,调用者必须自己分配缓冲区,并将其指针和大小传递给api。
api检查缓冲区大小-如果它有足够大的填充信息来缓冲,否则返回ERROR_INSUFFICIENT_BUFFER
(假设没有其他错误)或有时返回ERROR_MORE_DATA
。哪个具体错误导致了ERROR_INSUFFICIENT_BUFFER
或ERROR_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;
}