710 lines
23 KiB
C++
710 lines
23 KiB
C++
//
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// Helper functions for copying parameters and packaging the buffer
|
|
// for GetSerialization.
|
|
|
|
|
|
#include "helpers.h"
|
|
#include <intsafe.h>
|
|
|
|
//
|
|
// Copies the field descriptor pointed to by rcpfd into a buffer allocated
|
|
// using CoTaskMemAlloc. Returns that buffer in ppcpfd.
|
|
//
|
|
HRESULT FieldDescriptorCoAllocCopy(
|
|
_In_ const CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR &rcpfd,
|
|
_Outptr_result_nullonfailure_ CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR **ppcpfd
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
*ppcpfd = nullptr;
|
|
DWORD cbStruct = sizeof(**ppcpfd);
|
|
|
|
CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR *pcpfd = (CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR*)CoTaskMemAlloc(cbStruct);
|
|
if (pcpfd)
|
|
{
|
|
pcpfd->dwFieldID = rcpfd.dwFieldID;
|
|
pcpfd->cpft = rcpfd.cpft;
|
|
pcpfd->guidFieldType = rcpfd.guidFieldType;
|
|
|
|
if (rcpfd.pszLabel)
|
|
{
|
|
hr = SHStrDupW(rcpfd.pszLabel, &pcpfd->pszLabel);
|
|
}
|
|
else
|
|
{
|
|
pcpfd->pszLabel = nullptr;
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*ppcpfd = pcpfd;
|
|
}
|
|
else
|
|
{
|
|
CoTaskMemFree(pcpfd);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Coppies rcpfd into the buffer pointed to by pcpfd. The caller is responsible for
|
|
// allocating pcpfd. This function uses CoTaskMemAlloc to allocate memory for
|
|
// pcpfd->pszLabel.
|
|
//
|
|
HRESULT FieldDescriptorCopy(
|
|
_In_ const CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR &rcpfd,
|
|
_Out_ CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR *pcpfd
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR cpfd;
|
|
|
|
cpfd.dwFieldID = rcpfd.dwFieldID;
|
|
cpfd.cpft = rcpfd.cpft;
|
|
cpfd.guidFieldType = rcpfd.guidFieldType;
|
|
|
|
if (rcpfd.pszLabel)
|
|
{
|
|
hr = SHStrDupW(rcpfd.pszLabel, &cpfd.pszLabel);
|
|
}
|
|
else
|
|
{
|
|
cpfd.pszLabel = nullptr;
|
|
hr = S_OK;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*pcpfd = cpfd;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// This function copies the length of pwz and the pointer pwz into the UNICODE_STRING structure
|
|
// This function is intended for serializing a credential in GetSerialization only.
|
|
// Note that this function just makes a copy of the string pointer. It DOES NOT ALLOCATE storage!
|
|
// Be very, very sure that this is what you want, because it probably isn't outside of the
|
|
// exact GetSerialization call where the sample uses it.
|
|
//
|
|
HRESULT UnicodeStringInitWithString(
|
|
_In_ PWSTR pwz,
|
|
_Out_ UNICODE_STRING *pus
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
if (pwz)
|
|
{
|
|
size_t lenString = wcslen(pwz);
|
|
USHORT usCharCount;
|
|
hr = SizeTToUShort(lenString, &usCharCount);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
USHORT usSize;
|
|
hr = SizeTToUShort(sizeof(wchar_t), &usSize);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = UShortMult(usCharCount, usSize, &(pus->Length)); // Explicitly NOT including NULL terminator
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pus->MaximumLength = pus->Length;
|
|
pus->Buffer = pwz;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// The following function is intended to be used ONLY with the Kerb*Pack functions. It does
|
|
// no bounds-checking because its callers have precise requirements and are written to respect
|
|
// its limitations.
|
|
// You can read more about the UNICODE_STRING type at:
|
|
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/unicode_string.asp
|
|
//
|
|
static void _UnicodeStringPackedUnicodeStringCopy(
|
|
__in const UNICODE_STRING& rus,
|
|
__in PWSTR pwzBuffer,
|
|
__out UNICODE_STRING *pus
|
|
)
|
|
{
|
|
pus->Length = rus.Length;
|
|
pus->MaximumLength = rus.Length;
|
|
pus->Buffer = pwzBuffer;
|
|
|
|
CopyMemory(pus->Buffer, rus.Buffer, pus->Length);
|
|
}
|
|
|
|
//
|
|
// Initialize the members of a KERB_INTERACTIVE_UNLOCK_LOGON with weak references to the
|
|
// passed-in strings. This is useful if you will later use KerbInteractiveUnlockLogonPack
|
|
// to serialize the structure.
|
|
//
|
|
// The password is stored in encrypted form for CPUS_LOGON and CPUS_UNLOCK_WORKSTATION
|
|
// because the system can accept encrypted credentials. It is not encrypted in CPUS_CREDUI
|
|
// because we cannot know whether our caller can accept encrypted credentials.
|
|
//
|
|
HRESULT KerbInteractiveUnlockLogonInit(
|
|
_In_ PWSTR pwzDomain,
|
|
_In_ PWSTR pwzUsername,
|
|
_In_ PWSTR pwzPassword,
|
|
_In_ CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus,
|
|
_Out_ KERB_INTERACTIVE_UNLOCK_LOGON *pkiul
|
|
)
|
|
{
|
|
KERB_INTERACTIVE_UNLOCK_LOGON kiul;
|
|
ZeroMemory(&kiul, sizeof(kiul));
|
|
|
|
KERB_INTERACTIVE_LOGON *pkil = &kiul.Logon;
|
|
|
|
// Note: this method uses custom logic to pack a KERB_INTERACTIVE_UNLOCK_LOGON with a
|
|
// serialized credential. We could replace the calls to UnicodeStringInitWithString
|
|
// and KerbInteractiveUnlockLogonPack with a single cal to CredPackAuthenticationBuffer,
|
|
// but that API has a drawback: it returns a KERB_INTERACTIVE_UNLOCK_LOGON whose
|
|
// MessageType is always KerbInteractiveLogon.
|
|
//
|
|
// If we only handled CPUS_LOGON, this drawback would not be a problem. For
|
|
// CPUS_UNLOCK_WORKSTATION, we could cast the output buffer of CredPackAuthenticationBuffer
|
|
// to KERB_INTERACTIVE_UNLOCK_LOGON and modify the MessageType to KerbWorkstationUnlockLogon,
|
|
// but such a cast would be unsupported -- the output format of CredPackAuthenticationBuffer
|
|
// is not officially documented.
|
|
|
|
// Initialize the UNICODE_STRINGS to share our username and password strings.
|
|
HRESULT hr = UnicodeStringInitWithString(pwzDomain, &pkil->LogonDomainName);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = UnicodeStringInitWithString(pwzUsername, &pkil->UserName);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = UnicodeStringInitWithString(pwzPassword, &pkil->Password);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Set a MessageType based on the usage scenario.
|
|
switch (cpus)
|
|
{
|
|
case CPUS_UNLOCK_WORKSTATION:
|
|
pkil->MessageType = KerbWorkstationUnlockLogon;
|
|
hr = S_OK;
|
|
break;
|
|
|
|
case CPUS_LOGON:
|
|
pkil->MessageType = KerbInteractiveLogon;
|
|
hr = S_OK;
|
|
break;
|
|
|
|
case CPUS_CREDUI:
|
|
pkil->MessageType = (KERB_LOGON_SUBMIT_TYPE)0; // MessageType does not apply to CredUI
|
|
hr = S_OK;
|
|
break;
|
|
|
|
default:
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// KERB_INTERACTIVE_UNLOCK_LOGON is just a series of structures. A
|
|
// flat copy will properly initialize the output parameter.
|
|
CopyMemory(pkiul, &kiul, sizeof(*pkiul));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// WinLogon and LSA consume "packed" KERB_INTERACTIVE_UNLOCK_LOGONs. In these, the PWSTR members of each
|
|
// UNICODE_STRING are not actually pointers but byte offsets into the overall buffer represented
|
|
// by the packed KERB_INTERACTIVE_UNLOCK_LOGON. For example:
|
|
//
|
|
// rkiulIn.Logon.LogonDomainName.Length = 14 -> Length is in bytes, not characters
|
|
// rkiulIn.Logon.LogonDomainName.Buffer = sizeof(KERB_INTERACTIVE_UNLOCK_LOGON) -> LogonDomainName begins immediately
|
|
// after the KERB_... struct in the buffer
|
|
// rkiulIn.Logon.UserName.Length = 10
|
|
// rkiulIn.Logon.UserName.Buffer = sizeof(KERB_INTERACTIVE_UNLOCK_LOGON) + 14 -> UNICODE_STRINGS are NOT null-terminated
|
|
//
|
|
// rkiulIn.Logon.Password.Length = 16
|
|
// rkiulIn.Logon.Password.Buffer = sizeof(KERB_INTERACTIVE_UNLOCK_LOGON) + 14 + 10
|
|
//
|
|
// THere's more information on this at:
|
|
// http://msdn.microsoft.com/msdnmag/issues/05/06/SecurityBriefs/#void
|
|
//
|
|
|
|
HRESULT KerbInteractiveUnlockLogonPack(
|
|
_In_ const KERB_INTERACTIVE_UNLOCK_LOGON &rkiulIn,
|
|
_Outptr_result_bytebuffer_(*pcb) BYTE **prgb,
|
|
_Out_ DWORD *pcb
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
|
|
const KERB_INTERACTIVE_LOGON *pkilIn = &rkiulIn.Logon;
|
|
|
|
// alloc space for struct plus extra for the three strings
|
|
DWORD cb = sizeof(rkiulIn) +
|
|
pkilIn->LogonDomainName.Length +
|
|
pkilIn->UserName.Length +
|
|
pkilIn->Password.Length;
|
|
|
|
KERB_INTERACTIVE_UNLOCK_LOGON *pkiulOut = (KERB_INTERACTIVE_UNLOCK_LOGON*)CoTaskMemAlloc(cb);
|
|
if (pkiulOut)
|
|
{
|
|
ZeroMemory(&pkiulOut->LogonId, sizeof(pkiulOut->LogonId));
|
|
|
|
//
|
|
// point pbBuffer at the beginning of the extra space
|
|
//
|
|
BYTE *pbBuffer = (BYTE*)pkiulOut + sizeof(*pkiulOut);
|
|
|
|
//
|
|
// set up the Logon structure within the KERB_INTERACTIVE_UNLOCK_LOGON
|
|
//
|
|
KERB_INTERACTIVE_LOGON *pkilOut = &pkiulOut->Logon;
|
|
|
|
pkilOut->MessageType = pkilIn->MessageType;
|
|
|
|
//
|
|
// copy each string,
|
|
// fix up appropriate buffer pointer to be offset,
|
|
// advance buffer pointer over copied characters in extra space
|
|
//
|
|
_UnicodeStringPackedUnicodeStringCopy(pkilIn->LogonDomainName, (PWSTR)pbBuffer, &pkilOut->LogonDomainName);
|
|
pkilOut->LogonDomainName.Buffer = (PWSTR)(pbBuffer - (BYTE*)pkiulOut);
|
|
pbBuffer += pkilOut->LogonDomainName.Length;
|
|
|
|
_UnicodeStringPackedUnicodeStringCopy(pkilIn->UserName, (PWSTR)pbBuffer, &pkilOut->UserName);
|
|
pkilOut->UserName.Buffer = (PWSTR)(pbBuffer - (BYTE*)pkiulOut);
|
|
pbBuffer += pkilOut->UserName.Length;
|
|
|
|
_UnicodeStringPackedUnicodeStringCopy(pkilIn->Password, (PWSTR)pbBuffer, &pkilOut->Password);
|
|
pkilOut->Password.Buffer = (PWSTR)(pbBuffer - (BYTE*)pkiulOut);
|
|
|
|
*prgb = (BYTE*)pkiulOut;
|
|
*pcb = cb;
|
|
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// This function packs the string pszSourceString in pszDestinationString
|
|
// for use with LSA functions including LsaLookupAuthenticationPackage.
|
|
//
|
|
static HRESULT _LsaInitString(
|
|
__out PSTRING pszDestinationString,
|
|
__in PCSTR pszSourceString
|
|
)
|
|
{
|
|
size_t cchLength = strlen(pszSourceString);
|
|
USHORT usLength;
|
|
HRESULT hr = SizeTToUShort(cchLength, &usLength);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pszDestinationString->Buffer = (PCHAR)pszSourceString;
|
|
pszDestinationString->Length = usLength;
|
|
pszDestinationString->MaximumLength = pszDestinationString->Length+1;
|
|
hr = S_OK;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Retrieves the 'negotiate' AuthPackage from the LSA. In this case, Kerberos
|
|
// For more information on auth packages see this msdn page:
|
|
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/msv1_0_lm20_logon.asp
|
|
//
|
|
HRESULT RetrieveNegotiateAuthPackage(_Out_ ULONG *pulAuthPackage)
|
|
{
|
|
HRESULT hr;
|
|
HANDLE hLsa;
|
|
|
|
NTSTATUS status = LsaConnectUntrusted(&hLsa);
|
|
if (SUCCEEDED(HRESULT_FROM_NT(status)))
|
|
{
|
|
ULONG ulAuthPackage;
|
|
LSA_STRING lsaszKerberosName;
|
|
_LsaInitString(&lsaszKerberosName, NEGOSSP_NAME_A);
|
|
|
|
status = LsaLookupAuthenticationPackage(hLsa, &lsaszKerberosName, &ulAuthPackage);
|
|
if (SUCCEEDED(HRESULT_FROM_NT(status)))
|
|
{
|
|
*pulAuthPackage = ulAuthPackage;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_NT(status);
|
|
}
|
|
LsaDeregisterLogonProcess(hLsa);
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_NT(status);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Return a copy of pwzToProtect encrypted with the CredProtect API.
|
|
//
|
|
// pwzToProtect must not be NULL or the empty string.
|
|
//
|
|
static HRESULT _ProtectAndCopyString(
|
|
_In_ PCWSTR pwzToProtect,
|
|
_Outptr_result_nullonfailure_ PWSTR *ppwzProtected
|
|
)
|
|
{
|
|
*ppwzProtected = nullptr;
|
|
|
|
// pwzToProtect is const, but CredProtect takes a non-const string.
|
|
// So, make a copy that we know isn't const.
|
|
PWSTR pwzToProtectCopy;
|
|
HRESULT hr = SHStrDupW(pwzToProtect, &pwzToProtectCopy);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// The first call to CredProtect determines the length of the encrypted string.
|
|
// Because we pass a NULL output buffer, we expect the call to fail.
|
|
//
|
|
// Note that the third parameter to CredProtect, the number of characters of pwzToProtectCopy
|
|
// to encrypt, must include the NULL terminator!
|
|
DWORD cchProtected = 0;
|
|
if (!CredProtectW(FALSE, pwzToProtectCopy, (DWORD)wcslen(pwzToProtectCopy)+1, nullptr, &cchProtected, nullptr))
|
|
{
|
|
DWORD dwErr = GetLastError();
|
|
|
|
if ((ERROR_INSUFFICIENT_BUFFER == dwErr) && (0 < cchProtected))
|
|
{
|
|
// Allocate a buffer long enough for the encrypted string.
|
|
PWSTR pwzProtected = (PWSTR)CoTaskMemAlloc(cchProtected * sizeof(wchar_t));
|
|
if (pwzProtected)
|
|
{
|
|
// The second call to CredProtect actually encrypts the string.
|
|
if (CredProtectW(FALSE, pwzToProtectCopy, (DWORD)wcslen(pwzToProtectCopy)+1, pwzProtected, &cchProtected, nullptr))
|
|
{
|
|
*ppwzProtected = pwzProtected;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
CoTaskMemFree(pwzProtected);
|
|
|
|
dwErr = GetLastError();
|
|
hr = HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
|
|
CoTaskMemFree(pwzToProtectCopy);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// If pwzPassword should be encrypted, return a copy encrypted with CredProtect.
|
|
//
|
|
// If not, just return a copy.
|
|
//
|
|
HRESULT ProtectIfNecessaryAndCopyPassword(
|
|
_In_ PCWSTR pwzPassword,
|
|
_In_ CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus,
|
|
_Outptr_result_nullonfailure_ PWSTR *ppwzProtectedPassword
|
|
)
|
|
{
|
|
*ppwzProtectedPassword = nullptr;
|
|
|
|
HRESULT hr;
|
|
|
|
// ProtectAndCopyString is intended for non-empty strings only. Empty passwords
|
|
// do not need to be encrypted.
|
|
if (pwzPassword && *pwzPassword)
|
|
{
|
|
// pwzPassword is const, but CredIsProtected takes a non-const string.
|
|
// So, ake a copy that we know isn't const.
|
|
PWSTR pwzPasswordCopy;
|
|
hr = SHStrDupW(pwzPassword, &pwzPasswordCopy);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
bool bCredAlreadyEncrypted = false;
|
|
CRED_PROTECTION_TYPE protectionType;
|
|
|
|
// If the password is already encrypted, we should not encrypt it again.
|
|
// An encrypted password may be received through SetSerialization in the
|
|
// CPUS_LOGON scenario during a Terminal Services connection, for instance.
|
|
if (CredIsProtectedW(pwzPasswordCopy, &protectionType))
|
|
{
|
|
if (CredUnprotected != protectionType)
|
|
{
|
|
bCredAlreadyEncrypted = true;
|
|
}
|
|
}
|
|
|
|
// Passwords should not be encrypted in the CPUS_CREDUI scenario. We
|
|
// cannot know if our caller expects or can handle an encryped password.
|
|
if (CPUS_CREDUI == cpus || bCredAlreadyEncrypted)
|
|
{
|
|
hr = SHStrDupW(pwzPasswordCopy, ppwzProtectedPassword);
|
|
}
|
|
else
|
|
{
|
|
hr = _ProtectAndCopyString(pwzPasswordCopy, ppwzProtectedPassword);
|
|
}
|
|
|
|
CoTaskMemFree(pwzPasswordCopy);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = SHStrDupW(L"", ppwzProtectedPassword);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Unpack a KERB_INTERACTIVE_UNLOCK_LOGON *in place*. That is, reset the Buffers from being offsets to
|
|
// being real pointers. This means, of course, that passing the resultant struct across any sort of
|
|
// memory space boundary is not going to work -- repack it if necessary!
|
|
//
|
|
void KerbInteractiveUnlockLogonUnpackInPlace(
|
|
_Inout_updates_bytes_(cb) KERB_INTERACTIVE_UNLOCK_LOGON *pkiul,
|
|
DWORD cb
|
|
)
|
|
{
|
|
if (sizeof(*pkiul) <= cb)
|
|
{
|
|
KERB_INTERACTIVE_LOGON *pkil = &pkiul->Logon;
|
|
|
|
// Sanity check: if the range described by each (Buffer + MaximumSize) falls within the total bytecount,
|
|
// we can be pretty confident that the Buffers are actually offsets and that this is a packed credential.
|
|
if (((ULONG_PTR)pkil->LogonDomainName.Buffer + pkil->LogonDomainName.MaximumLength <= cb) &&
|
|
((ULONG_PTR)pkil->UserName.Buffer + pkil->UserName.MaximumLength <= cb) &&
|
|
((ULONG_PTR)pkil->Password.Buffer + pkil->Password.MaximumLength <= cb))
|
|
{
|
|
pkil->LogonDomainName.Buffer = pkil->LogonDomainName.Buffer
|
|
? (PWSTR)((BYTE*)pkiul + (ULONG_PTR)pkil->LogonDomainName.Buffer)
|
|
: nullptr;
|
|
|
|
pkil->UserName.Buffer = pkil->UserName.Buffer
|
|
? (PWSTR)((BYTE*)pkiul + (ULONG_PTR)pkil->UserName.Buffer)
|
|
: nullptr;
|
|
|
|
pkil->Password.Buffer = pkil->Password.Buffer
|
|
? (PWSTR)((BYTE*)pkiul + (ULONG_PTR)pkil->Password.Buffer)
|
|
: nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Use the CredPackAuthenticationBuffer and CredUnpackAuthenticationBuffer to convert a 32 bit WOW
|
|
// cred blob into a 64 bit native blob by unpacking it and immediately repacking it.
|
|
//
|
|
HRESULT KerbInteractiveUnlockLogonRepackNative(
|
|
_In_reads_bytes_(cbWow) BYTE *rgbWow,
|
|
_In_ DWORD cbWow,
|
|
_Outptr_result_bytebuffer_(*pcbNative) BYTE **prgbNative,
|
|
_Out_ DWORD *pcbNative
|
|
)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
PWSTR pszDomainUsername = nullptr;
|
|
DWORD cchDomainUsername = 0;
|
|
PWSTR pszPassword = nullptr;
|
|
DWORD cchPassword = 0;
|
|
|
|
*prgbNative = nullptr;
|
|
*pcbNative = 0;
|
|
|
|
// Unpack the 32 bit KERB structure
|
|
CredUnPackAuthenticationBufferW(CRED_PACK_WOW_BUFFER, rgbWow, cbWow, pszDomainUsername, &cchDomainUsername, nullptr, nullptr, pszPassword, &cchPassword);
|
|
if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
|
|
{
|
|
pszDomainUsername = (PWSTR) LocalAlloc(0, cchDomainUsername * sizeof(wchar_t));
|
|
if (pszDomainUsername)
|
|
{
|
|
pszPassword = (PWSTR) LocalAlloc(0, cchPassword * sizeof(wchar_t));
|
|
if (pszPassword)
|
|
{
|
|
if (CredUnPackAuthenticationBufferW(CRED_PACK_WOW_BUFFER, rgbWow, cbWow, pszDomainUsername, &cchDomainUsername, nullptr, nullptr, pszPassword, &cchPassword))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = GetLastError();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Repack native
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
CredPackAuthenticationBufferW(0, pszDomainUsername, pszPassword, *prgbNative, pcbNative);
|
|
if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
|
|
{
|
|
*prgbNative = (BYTE*) LocalAlloc(LMEM_ZEROINIT, *pcbNative);
|
|
if (*prgbNative)
|
|
{
|
|
if (CredPackAuthenticationBufferW(0, pszDomainUsername, pszPassword, *prgbNative, pcbNative))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
LocalFree(*prgbNative);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LocalFree(pszDomainUsername);
|
|
if (pszPassword)
|
|
{
|
|
SecureZeroMemory(pszPassword, cchPassword * sizeof(wchar_t));
|
|
LocalFree(pszPassword);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// Concatonates pwszDomain and pwszUsername and places the result in *ppwszDomainUsername.
|
|
HRESULT DomainUsernameStringAlloc(
|
|
_In_ PCWSTR pwszDomain,
|
|
_In_ PCWSTR pwszUsername,
|
|
_Outptr_result_nullonfailure_ PWSTR *ppwszDomainUsername
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
*ppwszDomainUsername = nullptr;
|
|
size_t cchDomain = wcslen(pwszDomain);
|
|
size_t cchUsername = wcslen(pwszUsername);
|
|
// Length of domain, 1 character for '\', length of Username, plus null terminator.
|
|
size_t cbLen = sizeof(wchar_t) * (cchDomain + 1 + cchUsername +1);
|
|
PWSTR pwszDest = (PWSTR)HeapAlloc(GetProcessHeap(), 0, cbLen);
|
|
if (pwszDest)
|
|
{
|
|
hr = StringCbPrintfW(pwszDest, cbLen, L"%s\\%s", pwszDomain, pwszUsername);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*ppwszDomainUsername = pwszDest;
|
|
}
|
|
else
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, pwszDest);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT SplitDomainAndUsername(_In_ PCWSTR pszQualifiedUserName, _Outptr_result_nullonfailure_ PWSTR *ppszDomain, _Outptr_result_nullonfailure_ PWSTR *ppszUsername)
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
*ppszDomain = nullptr;
|
|
*ppszUsername = nullptr;
|
|
PWSTR pszDomain;
|
|
PWSTR pszUsername;
|
|
const wchar_t *pchWhack = wcschr(pszQualifiedUserName, L'\\');
|
|
const wchar_t *pchEnd = pszQualifiedUserName + wcslen(pszQualifiedUserName) - 1;
|
|
|
|
if (pchWhack != nullptr)
|
|
{
|
|
const wchar_t *pchDomainBegin = pszQualifiedUserName;
|
|
const wchar_t *pchDomainEnd = pchWhack - 1;
|
|
const wchar_t *pchUsernameBegin = pchWhack + 1;
|
|
const wchar_t *pchUsernameEnd = pchEnd;
|
|
|
|
size_t lenDomain = pchDomainEnd - pchDomainBegin + 1; // number of actual chars, NOT INCLUDING null terminated string
|
|
pszDomain = static_cast<PWSTR>(CoTaskMemAlloc(sizeof(wchar_t) * (lenDomain + 1)));
|
|
if (pszDomain != nullptr)
|
|
{
|
|
hr = StringCchCopyN(pszDomain, lenDomain + 1, pchDomainBegin, lenDomain);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
size_t lenUsername = pchUsernameEnd - pchUsernameBegin + 1; // number of actual chars, NOT INCLUDING null terminated string
|
|
pszUsername = static_cast<PWSTR>(CoTaskMemAlloc(sizeof(wchar_t) * (lenUsername + 1)));
|
|
if (pszUsername != nullptr)
|
|
{
|
|
hr = StringCchCopyN(pszUsername, lenUsername + 1, pchUsernameBegin, lenUsername);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*ppszDomain = pszDomain;
|
|
*ppszUsername = pszUsername;
|
|
}
|
|
else
|
|
{
|
|
CoTaskMemFree(pszUsername);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
CoTaskMemFree(pszDomain);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
return hr;
|
|
} |