How to serialize credentials in smart card credential provider for a domain account for logon and unlock?

628 views Asked by At

I am building a credential provider which works same like windows smart card credential provider i.e this works only with domain accounts. I am facing an issue when passing the credentials to Negotiate SSP and I am using microsoft base smart card crypto provider as CSP. I am getting The parameter is incorrect error on lock screen after entering pin.

GetSerialization

HRESULT CCredential::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;
    WCHAR dmz[244] = L"demodomain";

        PWSTR pwzProtectedPin;

        hr = ProtectIfNecessaryAndCopyPassword(_rgFieldStrings[SFI_PIN], _cpus, _dwFlags, &pwzProtectedPin);

        if (SUCCEEDED(hr))
        {
            KERB_CERTIFICATE_UNLOCK_LOGON kiul;

            // Initialize kiul with weak references to our credential.
            hr = UnlockLogonInit(dmz, _rgFieldStrings[SFI_USERNAME], pwzProtectedPin, _cpus, &kiul);

            if (SUCCEEDED(hr))
            {
                PBASE_SMARTCARD_CSP_INFO pCspInfo = _pContainer->GetCSPInfo();
                if (pCspInfo)
                {
                    CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcp;

                    hr = UnlockLogonPack(kiul, pCspInfo, &pcpcs->rgbSerialization, &pcpcs->cbSerialization);
                    _pContainer->FreeCSPInfo(pCspInfo);

                    if (SUCCEEDED(hr))
                    {
                        ULONG ulAuthPackage;

                        hr = RetrieveNegotiateAuthPackage(&ulAuthPackage);
                        if (SUCCEEDED(hr))
                        {
                            pcpcs->ulAuthenticationPackage = ulAuthPackage;
                            pcpcs->clsidCredentialProvider = CLSID_CProvider;

                            // At this point the credential has created the serialized credential used for logon
                            // By setting this to CPGSR_RETURN_CREDENTIAL_FINISHED we are letting logonUI know
                            // that we have all the information we need and it should attempt to submit the 
                            // serialized credential.
                            *pcpgsr = CPGSR_RETURN_CREDENTIAL_FINISHED;
                        }
                        else
                        {
                            PrintLn(WINEVENT_LEVEL_WARNING, L"RetrieveNegotiateAuthPackage not SUCCEEDED hr=0x%08x", hr);
                        }
                    }
                    else
                    {
                        PrintLn(WINEVENT_LEVEL_WARNING, L"UnlockLogonPack not SUCCEEDED hr=0x%08x", hr);
                    }
                }
                else
                {
                    PrintLn(WINEVENT_LEVEL_WARNING, L"pCspInfo NULL");
                }
            }
            else
            {
                PrintLn(WINEVENT_LEVEL_WARNING, L"UnlockLogonInit not SUCCEEDED hr=0x%08x", hr);
            }
            CoTaskMemFree(pwzProtectedPin);
        }
        else
        {
            PrintLn(WINEVENT_LEVEL_WARNING, L"ProtectIfNecessaryAndCopyPassword not SUCCEEDED hr=0x%08x", hr);
        }
    
    if (!SUCCEEDED(hr))
    {
        PrintLn(WINEVENT_LEVEL_WARNING, L"not SUCCEEDED hr=0x%08x", hr);
    }
    else
    {
        PrintLn(WINEVENT_LEVEL_WARNING, L"OK");
    }
    return hr;
}

UnlockLogonInit

HRESULT UnlockLogonInit(
                                       PWSTR pwzDomain,
                                       PWSTR pwzUsername,
                                       PWSTR pwzPin,
                                       CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus,
                                       KERB_CERTIFICATE_UNLOCK_LOGON* pkiul
                                       )
{
    UNREFERENCED_PARAMETER(cpus);
    KERB_CERTIFICATE_UNLOCK_LOGON kiul;
    ZeroMemory(&kiul, sizeof(kiul));

    KERB_CERTIFICATE_LOGON* pkil = &kiul.Logon;

    HRESULT hr = UnicodeStringInitWithString(pwzDomain, &pkil->LogonDomainName);

    if (SUCCEEDED(hr))
    {
        hr = UnicodeStringInitWithString(pwzUsername, &pkil->UserName);

        if (SUCCEEDED(hr))
        {
            hr = UnicodeStringInitWithString(pwzPin, &pkil->Pin);
            
            if (SUCCEEDED(hr))
            {
                // Set a MessageType based on the usage scenario.
                pkil->MessageType = KerbCertificateLogon; //13
                pkil->CspDataLength = 0;
                pkil->CspData = NULL;
                pkil->Flags = 0;

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

UnlockLogonPack

HRESULT UnlockLogonPack(
                                       const KERB_CERTIFICATE_UNLOCK_LOGON& rkiulIn,
                                       const PBASE_SMARTCARD_CSP_INFO pCspInfo,
                                       BYTE** prgb,
                                       DWORD* pcb
                                       )
{
    HRESULT hr;

    const KERB_CERTIFICATE_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->Pin.Length +
        pCspInfo->dwCspInfoLen;


    KERB_CERTIFICATE_UNLOCK_LOGON* pkiulOut = (KERB_CERTIFICATE_UNLOCK_LOGON*)CoTaskMemAlloc(cb);

    if (pkiulOut)
    {
        ZeroMemory(&pkiulOut->LogonId, sizeof(LUID));

        //
        // point pbBuffer at the beginning of the extra space
        //
        BYTE* pbBuffer = (BYTE*)pkiulOut + sizeof(*pkiulOut);


        KERB_CERTIFICATE_LOGON* pkilOut = &pkiulOut->Logon;

        pkilOut->MessageType = pkilIn->MessageType;
        pkilOut->Flags = pkilIn->Flags;

        //
        // 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->Pin, (PWSTR)pbBuffer, &pkilOut->Pin);
        pkilOut->Pin.Buffer = (PWSTR)(pbBuffer - (BYTE*)pkiulOut);
        pbBuffer += pkilOut->Pin.Length;

        pkilOut->CspData = (PUCHAR) (pbBuffer - (BYTE*)pkiulOut);
        pkilOut->CspDataLength = pCspInfo->dwCspInfoLen;

        memcpy(pbBuffer,pCspInfo,pCspInfo->dwCspInfoLen);

        *prgb = (BYTE*)pkiulOut;
        *pcb = cb;

        hr = S_OK;
    }
    else
    {
        hr = E_OUTOFMEMORY;
    }

    return hr;
}

_KERB_SMARTCARD_CSP_INFO Structure and GetCSPInfo


// based on _KERB_SMARTCARD_CSP_INFO 
typedef struct _BASE_SMARTCARD_CSP_INFO 
{
  DWORD dwCspInfoLen;
  DWORD MessageType;
  union {
    PVOID ContextInformation;
    ULONG64 SpaceHolderForWow64;
  } ;
  DWORD flags;
  DWORD KeySpec;
  ULONG nCardNameOffset;
  ULONG nReaderNameOffset;
  ULONG nContainerNameOffset;
  ULONG nCSPNameOffset;
  TCHAR bBuffer[sizeof(DWORD)];
} BASE_SMARTCARD_CSP_INFO, 
 *PBASE_SMARTCARD_CSP_INFO;

PBASE_SMARTCARD_CSP_INFO CContainer::GetCSPInfo()
{
//szreaderName, szCardname, szproviderName, szContainerName are initialized with respective values in constructor
    _ASSERTE( _CrtCheckMemory( ) );
    DWORD dwReaderLen = (DWORD) _tcslen(_szReaderName)+1;
    DWORD dwCardLen = (DWORD) _tcslen(_szCardName)+1;
    DWORD dwProviderLen = (DWORD) _tcslen(_szProviderName)+1;
    DWORD dwContainerLen = (DWORD) _tcslen(_szContainerName)+1;
    DWORD dwBufferSize = dwReaderLen + dwCardLen + dwProviderLen + dwContainerLen;

    PBASE_SMARTCARD_CSP_INFO pCspInfo = (PBASE_SMARTCARD_CSP_INFO) BASEAlloc(sizeof(BASE_SMARTCARD_CSP_INFO)+dwBufferSize*sizeof(TCHAR));
    if (!pCspInfo) return NULL;
    //ZeroMemory(pCspInfo);
    memset(pCspInfo,0,sizeof(BASE_SMARTCARD_CSP_INFO));
    pCspInfo->dwCspInfoLen = sizeof(BASE_SMARTCARD_CSP_INFO)+dwBufferSize*sizeof(TCHAR);
    pCspInfo->MessageType = 1;
    pCspInfo->KeySpec = _KeySpec;
    pCspInfo->nCardNameOffset = ARRAYSIZE(pCspInfo->bBuffer);
    pCspInfo->nReaderNameOffset = pCspInfo->nCardNameOffset + dwCardLen;
    pCspInfo->nContainerNameOffset = pCspInfo->nReaderNameOffset + dwReaderLen;
    pCspInfo->nCSPNameOffset = pCspInfo->nContainerNameOffset + dwContainerLen;
    memset(pCspInfo->bBuffer,0,sizeof(pCspInfo->bBuffer));
    _tcscpy_s(&pCspInfo->bBuffer[pCspInfo->nCardNameOffset] ,dwBufferSize + 4 - pCspInfo->nCardNameOffset, _szCardName);
    _tcscpy_s(&pCspInfo->bBuffer[pCspInfo->nReaderNameOffset] ,dwBufferSize + 4 - pCspInfo->nReaderNameOffset, _szReaderName);
    _tcscpy_s(&pCspInfo->bBuffer[pCspInfo->nContainerNameOffset] ,dwBufferSize + 4 - pCspInfo->nContainerNameOffset, _szContainerName);
    _tcscpy_s(&pCspInfo->bBuffer[pCspInfo->nCSPNameOffset] ,dwBufferSize + 4 - pCspInfo->nCSPNameOffset, _szProviderName);
    _ASSERTE( _CrtCheckMemory( ) );
    return pCspInfo;
}

I don't understand where I am doing wrong, been stuck here for a while now. Any help would be appreciated.

0

There are 0 answers