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

牧云@^-^@ 提交于 2019-12-11 06:36:11

问题


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(
    CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE *pcpgsr,
    CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION *pcpcs,
    PWSTR *ppwszOptionalStatusText,
    CREDENTIAL_PROVIDER_STATUS_ICON *pcpsiOptionalStatusIcon
)
{
    UNREFERENCED_PARAMETER(ppwszOptionalStatusText);
    UNREFERENCED_PARAMETER(pcpsiOptionalStatusIcon);

    HRESULT hr;

    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;
        *pcpgsr = CPGSR_RETURN_CREDENTIAL_FINISHED;
    }

    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;
    WCHAR szPin[] = CS_TEST_PIN;
    ULONG ulPinByteLen = (ULONG)(wcslen(szPin) * sizeof(WCHAR));
    WCHAR szUserName[] = CS_TEST_USERNAME;
    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;
    KERB_CERTIFICATE_LOGON *pKerbCertLogon;
    KERB_SMARTCARD_CSP_INFO *pKerbCspInfo;
    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) +
        ulCspDataLen;

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

回答1:


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.

Also sometimes (depending on CREDENTIAL_PROVIDER_USAGE_SCENARIO) you should use KERB_CERTIFICATE_UNLOCK_LOGON instead of KERB_CERTIFICATE_LOGON.



来源:https://stackoverflow.com/questions/38398905/ksp-key-storage-provider-not-being-loaded-at-logon-via-a-credential-provider

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!