Abel Pereira Abel Pereira - 1 year ago 70
C++ Question

KSP (Key Storage Provider) not being loaded at logon via a Credential Provider

I am creating a Windows Credential Provider to logon into a Windows domain using certificates as described on this article.
This implies creating a custom KSP that will be called by LsaLogonUser when creating an authentication package.

I manage to create the custom KSP and tested it successfully in a standalone app that calls LsaLogonUser directly.
Basically creating the authentication package and pass it to LsaLogonUser, loaded the KSP, called a bunch of functions and authenticated the user returning a success result on the status code and the user profile loaded.

However, when I use the same authentication package during the GetSerialization on a credential, the KSP doesn't even loads and I get a 0xc000000d (The parameter is incorrect) reported by ReportResult(NTSTATUS ntsStatus, NTSTATUS ntsSubstatus, ....) on the ntsStatus.

This is the code for GetSerialization I am using on my testing:

HRESULT AOCredential::GetSerialization(
PWSTR *ppwszOptionalStatusText,


ULONG ulAuthPackage;
hr = RetrieveKerberosAuthPackage(&ulAuthPackage);

if (SUCCEEDED(hr))
InitialiseKerbCertificateLogon(&pcpcs->rgbSerialization, &pcpcs->cbSerialization); // this package actually worked when calling LsaLogonUser function directly (the KSP gets loaded and the authentication succeeds)

pcpcs->ulAuthenticationPackage = ulAuthPackage;
pcpcs->clsidCredentialProvider = CLSID_CallsignProvider;

return hr;

My question is why the KSP is getting loaded when the authentication package is called from LsaLogonUser directly, but not from the Credential Provider during the windows logon.

The InitialiseKerbCertificateLogon code is:

void InitialiseKerbCertificateLogon(PWSTR domain, PWSTR username, LPBYTE* authInfo, ULONG *authInfoLength)
WCHAR szCardName[] = L"";
WCHAR szContainerName[] = L"Default";
WCHAR szReaderName[] = L"";
WCHAR szCspName[] = CS_KSP_NAME;
ULONG ulPinByteLen = (ULONG)(wcslen(szPin) * sizeof(WCHAR));
ULONG ulUserByteLen = (ULONG)(wcslen(szUserName) * sizeof(WCHAR));
WCHAR szDomainName[] = CS_TEST_DOMAIN;
ULONG ulDomainByteLen = (ULONG)(wcslen(szDomainName) * sizeof(WCHAR));
LPBYTE pbAuthInfo = NULL;
ULONG ulAuthInfoLen = 0;
LPBYTE pbDomainBuffer, pbUserBuffer, pbPinBuffer;
LPBYTE pbCspData;
LPBYTE pbCspDataContent;

ULONG ulCspDataLen = (ULONG)(sizeof(KERB_SMARTCARD_CSP_INFO) - sizeof(TCHAR) +
(wcslen(szCardName) + 1) * sizeof(WCHAR) +
(wcslen(szCspName) + 1) * sizeof(WCHAR) +
(wcslen(szContainerName) + 1) * sizeof(WCHAR) +
(wcslen(szReaderName) + 1) * sizeof(WCHAR));

ulAuthInfoLen = sizeof(KERB_CERTIFICATE_LOGON) +
ulDomainByteLen + sizeof(WCHAR) +
ulUserByteLen + sizeof(WCHAR) +
ulPinByteLen + sizeof(WCHAR) +

pbAuthInfo = (LPBYTE)CoTaskMemAlloc(ulAuthInfoLen);
ZeroMemory(pbAuthInfo, ulAuthInfoLen);

pbDomainBuffer = pbAuthInfo + sizeof(KERB_CERTIFICATE_LOGON);
pbUserBuffer = pbDomainBuffer + ulDomainByteLen + sizeof(WCHAR);
pbPinBuffer = pbUserBuffer + ulUserByteLen + sizeof(WCHAR);
pbCspData = pbPinBuffer + ulPinByteLen + sizeof(WCHAR);

memcpy(pbDomainBuffer, szDomainName, ulDomainByteLen);
memcpy(pbUserBuffer, szUserName, ulUserByteLen);
memcpy(pbPinBuffer, szPin, ulPinByteLen);

pKerbCertLogon = (KERB_CERTIFICATE_LOGON*)pbAuthInfo;

pKerbCertLogon->MessageType = KerbCertificateLogon;
pKerbCertLogon->DomainName.Length = (USHORT)ulDomainByteLen;
pKerbCertLogon->DomainName.MaximumLength = (USHORT)(ulDomainByteLen + sizeof(WCHAR));
pKerbCertLogon->DomainName.Buffer = (PWSTR)pbDomainBuffer;
pKerbCertLogon->UserName.Length = (USHORT)ulUserByteLen;
pKerbCertLogon->UserName.MaximumLength = (USHORT)(ulUserByteLen + sizeof(WCHAR));
pKerbCertLogon->UserName.Buffer = (PWSTR)pbUserBuffer;
pKerbCertLogon->Pin.Length = (USHORT)ulPinByteLen;
pKerbCertLogon->Pin.MaximumLength = (USHORT)(ulPinByteLen + sizeof(WCHAR));
pKerbCertLogon->Pin.Buffer = (PWSTR)pbPinBuffer;

pKerbCertLogon->CspDataLength = ulCspDataLen;
pKerbCertLogon->CspData = pbCspData;

pKerbCspInfo = (KERB_SMARTCARD_CSP_INFO*)pbCspData;
pKerbCspInfo->dwCspInfoLen = ulCspDataLen;
pKerbCspInfo->MessageType = 1;
pKerbCspInfo->KeySpec = AT_KEYEXCHANGE;
pKerbCspInfo->nCardNameOffset = 0;
pKerbCspInfo->nReaderNameOffset = pKerbCspInfo->nCardNameOffset + (ULONG)wcslen(szCardName) + 1;
pKerbCspInfo->nContainerNameOffset = pKerbCspInfo->nReaderNameOffset + (ULONG)wcslen(szReaderName) + 1;
pKerbCspInfo->nCSPNameOffset = pKerbCspInfo->nContainerNameOffset + (ULONG)wcslen(szContainerName) + 1;

pbCspDataContent = pbCspData + sizeof(KERB_SMARTCARD_CSP_INFO) - sizeof(TCHAR);
memcpy(pbCspDataContent + (pKerbCspInfo->nCardNameOffset * sizeof(WCHAR)), szCardName, wcslen(szCardName) * sizeof(WCHAR));
memcpy(pbCspDataContent + (pKerbCspInfo->nReaderNameOffset * sizeof(WCHAR)), szReaderName, wcslen(szReaderName) * sizeof(WCHAR));
memcpy(pbCspDataContent + (pKerbCspInfo->nContainerNameOffset * sizeof(WCHAR)), szContainerName, wcslen(szContainerName) * sizeof(WCHAR));
memcpy(pbCspDataContent + (pKerbCspInfo->nCSPNameOffset * sizeof(WCHAR)), szCspName, wcslen(szCspName) * sizeof(WCHAR));

*authInfo = pbAuthInfo;
*authInfoLength = ulAuthInfoLen;

Answer Source

The problem is probably in your function InitializeKerbCertificateLogon - but you didn't give us a source code for it.

From Microsoft's docs about KERB_CERTIFICATE_LOGON structure:

The pointers stored in the members of UNICODE_STRING type are relative to the beginning of the structure and are not absolute memory pointers.

But documentation does not tell you that CspData pointer should also be relative to the beginning of the structure...

When you call LsaLogonUser with data with absolute memory pointers it will work, also when pointers are relative. When data is serialized will only work with all pointers relative.