1

我正在创建一个 Windows 凭据提供程序,以使用本文所述的证书登录到 Windows 域。这意味着创建一个自定义 KSP,在创建身份验证包时将由 LsaLogonUser 调用。

我设法创建了自定义 KSP 并在直接调用 LsaLogonUser 的独立应用程序中成功对其进行了测试。基本上创建身份验证包并将其传递给 LsaLogonUser,加载 KSP,调用一堆函数并验证用户在状态码和加载的用户配置文件上返回成功结果。

但是,当我在凭据上的 GetSerialization 期间使用相同的身份验证包时,KSP 甚至不会加载,并且我得到 ReportResult(NTSTATUS ntsStatus, NTSTATUS ntsSubstatus, ....) 报告的 0xc000000d(参数不正确) nts 状态。

这是我在测试中使用的 GetSerialization 代码:

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

我的问题是为什么在直接从 LsaLogonUser 调用身份验证包时加载 KSP,而不是在 Windows 登录期间从凭据提供程序调用。

InitialiseKerbCertificateLogon 代码为:

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

1 回答 1

4

来自微软关于KERB_CERTIFICATE_LOGON结构的文档:

存储在 UNICODE_STRING 类型成员中的指针是相对于结构的开头的,不是绝对内存指针。

但是文档并没有告诉您CspData指针也应该相对于结构的开头......

当您LsaLogonUser使用绝对内存指针调用数据时,它会起作用,当指针是相对时也是如此。当数据被序列化时,仅适用于所有相对指针。

有时(取决于 CREDENTIAL_PROVIDER_USAGE_SCENARIO)您应该使用 KERB_CERTIFICATE_UNLOCK_LOGON 而不是 KERB_CERTIFICATE_LOGON。

于 2016-08-16T09:27:44.390 回答