Windows 10 version 2004 Smart Card authentication problem with custom CP's

Greg_N_Huntsville 21 Reputation points
2020-11-13T14:19:37.333+00:00

Windows 10 version 2004 and 20H2 both have an issue with custom Credential Providers that worked fine in versions prior to 2004. After packaging the credentials in GetSerialization and handing them off to Lsass, ReportResult returns "An invalid parameter was passed to a service or function". VMware states that it's a known limitation in Windows 10 version 2004 1 but I can't find any acknowledgement from Microsoft on this issue. Does anyone know anything about this?

I originally posted feedback for the Insider Build, but never received anything back.
Also see the 2 post below.

Windows development | Windows API - Win32
Windows for business | Windows Client for IT Pros | Devices and deployment | Configure application groups
Windows for business | Windows Client for IT Pros | User experience | Other
{count} votes

Accepted answer
  1. Drake Wu - MSFT 996 Reputation points
    2020-12-17T09:52:45.277+00:00

    Hi,@Greg_N_Huntsville Could you please check if the following sample is helpful to you?
    CSampleCredential.cpp:

    //  
    // 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) 2006 Microsoft Corporation. All rights reserved.  
    //  
    //  
    #ifndef WIN32_NO_STATUS  
    #include <ntstatus.h>  
    #define WIN32_NO_STATUS  
    #endif  
    #include <unknwn.h>  
    //#include <tchar.h>  
    #include "CSampleCredential.h"  
    #include "guid.h"  
      
      
    #include <wincred.h>  
      
      
      
       
    /*  
    #define UNICODE  
    #define _UNICODE  
    #define SECURITY_WIN32  
    #include <windows.h>  
    #include <credentialprovider.h>  
    #include <ntsecapi.h>  
    #include <tchar.h>  
    #include <security.h>  
    #include <intsafe.h>  
    #include <wincrypt.h>  
    #include <stdio.h>  
      
    */  
      
      
      
      
      
    void mylog (CHAR * lpFmt, ...)  
    {  
      va_list arglist;  
      CHAR szTemp [1024];  
      HANDLE hLogFile;  
      DWORD dwNumberOfCharactersWritten=0,dwNumberOfBytesWritten=0;  
      SYSTEMTIME systemtime;  
      
      GetLocalTime (&systemtime);  
      dwNumberOfCharactersWritten = wsprintfA (szTemp, "[PID: %u](%02u/%02u/%02u %02u:%02u:%02u:%03u) - ", GetCurrentProcessId(), systemtime.wMonth,   systemtime.wDay, systemtime.wYear, systemtime.wHour, systemtime.wMinute, systemtime.wSecond, systemtime.wMilliseconds);  
      va_start(arglist, lpFmt);  
      dwNumberOfCharactersWritten += wvsprintfA (szTemp + dwNumberOfCharactersWritten, lpFmt, arglist);  
      va_end(arglist);  
      dwNumberOfCharactersWritten += wsprintfA (szTemp + dwNumberOfCharactersWritten, "\r\n");  
      WriteConsoleA (GetStdHandle (STD_OUTPUT_HANDLE), szTemp, lstrlenA (szTemp), &dwNumberOfCharactersWritten, 0);  
      hLogFile = CreateFileA ("c:\\logfile.txt", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,   NULL);       
      SetFilePointer (hLogFile, 0, 0, FILE_END);   
      WriteFile (hLogFile, szTemp, dwNumberOfCharactersWritten * sizeof(CHAR), &dwNumberOfBytesWritten, NULL);  
      FlushFileBuffers (hLogFile);  
      CloseHandle (hLogFile);  
    } // log  
      
      
    #define ALIGN_LPVOID sizeof(LPVOID)  
    #define ROUND_UP_COUNT(Count,Pow2) \  
            ( ((Count)+(Pow2)-1) & (~(((LONG)(Pow2))-1)) )  
      
      
    typedef enum _KERB_SMARTCARD_CSP_INFO_TYPE {  
        LogonInfo2 = 1,  
        KERB_SMARTCARD_CSP_INFO_MAX_TYPE = 10,  
    } KERB_SMARTCARD_CSP_INFO_TYPE, *PKERB_SMARTCARD_CSP_INFO_TYPE;  
      
    /*  
    typedef enum _KERB_LOGON_SUBMIT_TYPE {  
        KerbInteractiveLogon = 2,  
        KerbSmartCardLogon = 6,  
        KerbWorkstationUnlockLogon = 7,  
        KerbSmartCardUnlockLogon = 8,  
        KerbProxyLogon = 9,  
        KerbTicketLogon = 10,  
        KerbTicketUnlockLogon = 11,  
        KerbS4ULogon = 12,  
        KerbCertificateLogon = 13,   
        KerbCertificateS4ULogon = 14,  
        KerbCertificateUnlockLogon = 15,  
    } KERB_LOGON_SUBMIT_TYPE, *PKERB_LOGON_SUBMIT_TYPE;  
    */  
      
      
      
    typedef struct _KERB_SMARTCARD_CSP_INFO {  
      DWORD dwCspInfoLen;  
      DWORD MessageType;  
      union {  
        PVOID ContextInformation; // 4 bytes.  
        ULONG64 SpaceHolderForWow64; // 8 bytes.  
         //LONG64 SpaceHolderForWow64; // <-- adding this back goes from 36 to 48 bytes for the structure.   
      };  
      DWORD flags;  
      DWORD KeySpec;  
      ULONG nCardNameOffset;  
      ULONG nReaderNameOffset;  
      ULONG nContainerNameOffset;  
      ULONG nCSPNameOffset;  
      WCHAR bBuffer[1]; // TCHAR=WCHAR= 2bytes, 4 total for this array.  
    } KERB_SMARTCARD_CSP_INFO, *PKERB_SMARTCARD_CSP_INFO;  
      
    /*  
    typedef struct _KERB_CERTIFICATE_LOGON {  
        KERB_LOGON_SUBMIT_TYPE MessageType; // KerbCertificateLogon  
        LSA_UNICODE_STRING DomainName; // OPTIONAL, if supplied, used to locate the account forest  
        LSA_UNICODE_STRING UserName;   // OPTIONAL, if supplied, used to locate the account  
        LSA_UNICODE_STRING Pin;  
        ULONG Flags;               // additional flags  
        ULONG CspDataLength;  
        PUCHAR CspData;            // contains the smartcard CSP data  
    } KERB_CERTIFICATE_LOGON, *PKERB_CERTIFICATE_LOGON;  
      
    typedef struct _KERB_CERTIFICATE_UNLOCK_LOGON {  
        KERB_CERTIFICATE_LOGON Logon;  
        LUID LogonId;  
    } KERB_CERTIFICATE_UNLOCK_LOGON, *PKERB_CERTIFICATE_UNLOCK_LOGON;  
    */  
      
    //   
    // This function packs the string pszSourceString in pszDestinationString  
    // for use with LSA functions including LsaLookupAuthenticationPackage.  
    //  
    /*  
    HRESULT LsaInitString(PSTRING pszDestinationString, PCSTR pszSourceString)  
    {  
        USHORT usLength;  
        HRESULT hr = SizeTToUShort(strlen(pszSourceString), &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(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;  
    }  
    */  
      
      
      
    void hexlog(CHAR * lpFmt, ...)  
    {  
        va_list arglist;  
        CHAR szTemp [1024];  
        HANDLE hLogFile;  
        DWORD dwNumberOfCharactersWritten=0,dwNumberOfBytesWritten=0;  
        //SYSTEMTIME systemtime;  
      
        va_start(arglist, lpFmt);  
        dwNumberOfCharactersWritten += wvsprintfA (szTemp + dwNumberOfCharactersWritten, lpFmt, arglist);  
        va_end(arglist);  
        hLogFile = CreateFileA ("c:\\logfile.txt", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,   NULL);       
        SetFilePointer (hLogFile, 0, 0, FILE_END);   
        WriteFile (hLogFile, szTemp, dwNumberOfCharactersWritten * sizeof(CHAR), &dwNumberOfBytesWritten, NULL);  
        FlushFileBuffers (hLogFile);  
        CloseHandle (hLogFile);  
      
    } // log  
      
       
      
      
    /*  
    void hexlog(CHAR * lpFmt, ...)  
    {  
      
        va_list arglist;  
        CHAR szTemp [1024];  
        DWORD dwNumberOfCharactersWritten=0,dwNumberOfBytesWritten=0;  
      
        va_start(arglist, lpFmt);  
        dwNumberOfCharactersWritten += wvsprintfA (szTemp + dwNumberOfCharactersWritten, lpFmt, arglist);  
        va_end(arglist);  
      
     printf("%s", szTemp);  
      
    } // log  
      
    */  
      
    /*  
    void hexlog(CHAR * lpFmt, ...)  
    {  
      
    } // log  
    */  
      
      
    void HexDump (BYTE * pBuf, DWORD dwLen)  
    {  
        DWORD i, u, v;  
        BYTE * p;  
      
        hexlog("\r\n");  
      
        p = pBuf;  
        i = u = v = 0;  
        for (u=0; u<(dwLen/16); u++)  
        {  
            for (v=0; v<16; v++)  
            {  
                hexlog("%.2x ", p[i]);  
                i++;  
            }  
      
            hexlog("   ");  
      
            i -= 16;  
            for (v=0; v<16; v++)  
            {  
                if ((p[i] > 32) && (p[i] < 127))  
                    hexlog("%c", p[i]);  
                else  
                    hexlog(".");  
                i++;  
            }  
      
            hexlog("\r\n");  
        }  
      
        for (u=0; u<(dwLen%16); u++)  
            hexlog("%.2x ", p[i+u]);  
      
        for (u=0; u<(((16-(dwLen%16))*3)+3); u++)  
            hexlog(" ");  
      
        for (u=0; u<(dwLen%16); u++)  
            if ((p[i+u] > 32) && (p[i+u] < 127))  
                hexlog("%c", p[i+u]);  
            else  
                hexlog(".");  
      
        hexlog("\r\n");  
        hexlog("\r\n%u total bytes.\r\n\r\n", dwLen);  
      
    } // HexDump  
      
      
      
      
      
    //STDMETHODIMP  
    HRESULT EncryptPin(__in LPWSTR rgwszPin,   
                        __deref_out LPWSTR * ppwszEncryptedPin,  
                        __out DWORD *pcbPin)  
    {  
        CRED_PROTECTION_TYPE ProtectionType;  
        BOOLEAN bEnc                = TRUE;  
        DWORD dwSts                 = ERROR_SUCCESS;  
        HRESULT hr                  = S_OK;  
        DWORD cchPin                = 0;  
        DWORD cchEncPin             = 0;  
        DWORD cbPin                 = 0;  
        DWORD dwEncLen              = 0;  
        LPWSTR pwszEncPin           = NULL;  
      
        hr = ULongAdd(lstrlenW(rgwszPin), 1, &cchPin);  
        if (FAILED(hr))  
        {  
            return hr;  
        }  
          
        if(!CredIsProtectedW(rgwszPin,  
                            &ProtectionType  
                            ))  
        {  
            hr = HRESULT_FROM_WIN32(GetLastError());  
            return hr;  
        }  
      
        if(CredUnprotected != ProtectionType)  
        {  
            bEnc = FALSE;  
        }  
      
        if(bEnc)  
        {  
            if(!CredProtectW(FALSE,  
                             rgwszPin,  
                             cchPin,  
                             NULL,  
                             &cchEncPin,  
                             NULL))  
            {  
                dwSts = GetLastError();  
                if (ERROR_INSUFFICIENT_BUFFER == dwSts)  
                {  
                    hr = ULongAdd(cchEncPin, 1, &cbPin);  
                    if (SUCCEEDED(hr))  
                    {  
                        hr = ULongMult(cbPin, sizeof(WCHAR), &cbPin);  
                    }  
                    if (SUCCEEDED(hr))  
                    {  
     pwszEncPin = (WCHAR *)malloc(cbPin);  
                    }  
                    if (SUCCEEDED(hr))  
                    {  
                        cchEncPin = cbPin /sizeof(WCHAR);  
                        if (!CredProtectW(FALSE,  
                                    rgwszPin,  
                                    cchPin,  
                                    pwszEncPin,  
                                    &cchEncPin,  
                                    NULL))  
                        {  
                            hr = HRESULT_FROM_WIN32(GetLastError());  
     mylog("CredProtectW failed: %u\r\n", GetLastError());  
                        }  
     else  
     mylog("CredProtectW succeeded.\r\n");  
      
                    }  
                }  
                else  
                {  
                    hr = HRESULT_FROM_WIN32(dwSts);  
                }  
            }else  
            {  
                // We expect this call to fail  
                hr = E_UNEXPECTED;   
            }  
      
            if (SUCCEEDED(hr))  
            {  
                dwEncLen = cchEncPin * sizeof(WCHAR);   
            }  
        }  
    /*  
        else  
        {  
            hr = ULongMult(cchPin, sizeof(WCHAR), &cbPin);  
            if (SUCCEEDED(hr))  
            {  
     pwszEncPin = (WCHAR *)malloc(cbPin);  
      
            }  
            if (SUCCEEDED(hr))  
            {  
                memcpy(pwszEncPin,rgwszPin,cbPin);  
      
                // Explicit lack of NULL  
                dwEncLen = cbPin - sizeof(WCHAR);  
            }  
        }  
    */  
      
        //if (SUCCEEDED(hr))  
        //{  
            *pcbPin = dwEncLen;  
            *ppwszEncryptedPin = pwszEncPin;  
            pwszEncPin = NULL;  
        //}  
      
        //if (pwszEncPin)  
        //    free(pwszEncPin);  
      
      
        //return hr;  
        return S_OK;  
    }  
      
      
      
      
    HRESULT DoCredSerialization(  
        __out CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE* pcpgsr,  
        __out CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs,  
     PWSTR pwszPin, PCWSTR pwszCardName, PCWSTR pwszReaderName, PCWSTR pwszContainerName, PCWSTR pwszProviderName  
     )  
    {  
        HRESULT hr;  
        //ISmartcardError *psce    = NULL;  
      
        //PCWSTR pwszCardName      = L"Axalto Cryptoflex .NET"; //_psrd->GetCardName(); 22chars=23*2=46bytes  
        //PCWSTR pwszReaderName    = L"Gemplus USB Smart Card Reader 0"; //_psrd->GetReaderName(); 31chars=32*2=64bytes  
        //PCWSTR pwszContainerName = L"le-MSSmartcardUser-1c926350-2652--33341"; //_psrcd->GetContainer(); 39chars=40*2=80bytes  
        //PCWSTR pwszProviderName  = L"Microsoft Base Smart Card Crypto Provider"; //_psrd->GetProviderName(); 41chars=42*2=84bytes  
        LPWSTR pwszEncryptedPin  = NULL;  
        DWORD  dwEncLen          = 0;  
        DWORD  dwLogonInfoLen    = 0;  
     /*  
     BYTE bPin[] =   
    {0x40, 0x00, 0x40, 0x00, 0x44, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x67, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x6e, 0x00, 0x50, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x52, 0x00, 0x45, 0x00, 0x45, 0x00, 0x79, 0x00, 0x43, 0x00, 0x69, 0x00, 0x49, 0x00, 0x71, 0x00, 0x44, 0x00, 0x38, 0x00, 0x51, 0x00, 0x4a, 0x00, 0x6d, 0x00, 0x53, 0x00, 0x52, 0x00, 0x78, 0x00, 0x73, 0x00, 0x61, 0x00, 0x6a, 0x00, 0x79, 0x00, 0x51, 0x00, 0x37, 0x00, 0x52, 0x00, 0x39, 0x00, 0x44, 0x00, 0x4e, 0x00, 0x70, 0x00, 0x4f, 0x00, 0x43, 0x00, 0x35, 0x00, 0x32, 0x00, 0x45, 0x00, 0x00, 0x00}; // 116 bytes.  
    */  
      
     hr = EncryptPin(pwszPin, &pwszEncryptedPin, &dwEncLen);  
     if (SUCCEEDED(hr))  
     {  
     mylog("EncryptPin succeeded.\r\n");  
     HexDump((BYTE *)pwszEncryptedPin, dwEncLen);  
     }  
     else  
     {  
     mylog("EncryptPin failed.\r\n");  
     return hr;  
     }  
      
      
    // pin value: 40 00 40 00 44 00 07 00 08 00 0c 00 0a 00 0d 00 67 00 41 00 41 00 41 00 41 00 41 00 6e 00 50 00 41 00 41 00 41 00 41 00 41 00 41 00 41 00 41 00 41 00 52 00 45 00 45 00 79 00 43 00 69 00 49 00 71 00 44 00 38 00 51 00 4a 00 6d 00 53 00 52 00 78 00 73 00 61 00 6a 00 79 00 51 00 37 00 52 00 39 00 44 00 4e 00 70 00 4f 00 43 00 35 00 32 00 45 00 00 00     
      
      
    /*  
    0d 00 00 00 00 00 00 00 28 00 00 00 00 00 00 00    ........(.......  
    28 00 00 00 74 00 74 00 28 00 00 00 00 00 00 00    (...t.t.(.......  
    40 01 00 00 9c 00 00 00 40 00 40 00 44 00 07 00    @.......@.@.D...  
    08 00 0c 00 0a 00 0d 00 67 00 41 00 41 00 41 00    ........g.A.A.A.  
    41 00 41 00 6e 00 50 00 41 00 41 00 41 00 41 00    A.A.n.P.A.A.A.A.  
    41 00 41 00 41 00 41 00 77 00 74 00 75 00 4a 00    A.A.A.A.w.t.u.J.  
    72 00 4a 00 62 00 79 00 74 00 47 00 6b 00 45 00    r.J.b.y.t.G.k.E.  
    62 00 52 00 64 00 49 00 6d 00 65 00 23 00 46 00    b.R.d.I.m.e.#.F.  
    43 00 46 00 73 00 64 00 51 00 72 00 6c 00 79 00    C.F.s.d.Q.r.l.y.  
    4d 00 4b 00 74 00 76 00 46 00 00 00 40 01 00 00    M.K.t.v.F...@...  
    01 00 00 00 00 00 00 00 a8 b1 f6 00 15 00 01 00    ................  
    01 00 00 00 00 00 00 00 17 00 00 00 3a 00 00 00    ............:...  
    62 00 00 00 41 00 78 00 61 00 6c 00 74 00 6f 00    b...A.x.a.l.t.o.  
    20 00 43 00 72 00 79 00 70 00 74 00 6f 00 66 00    ..C.r.y.p.t.o.f.  
    6c 00 65 00 78 00 20 00 2e 00 4e 00 45 00 54 00    l.e.x.....N.E.T.  
    00 00 4f 00 4d 00 4e 00 49 00 4b 00 45 00 59 00    ..O.M.N.I.K.E.Y.  
    20 00 41 00 47 00 20 00 53 00 6d 00 61 00 72 00    ..A.G...S.m.a.r.  
    74 00 20 00 43 00 61 00 72 00 64 00 20 00 52 00    t...C.a.r.d...R.  
    65 00 61 00 64 00 65 00 72 00 20 00 55 00 53 00    e.a.d.e.r...U.S.  
    42 00 20 00 30 00 00 00 6c 00 65 00 2d 00 4d 00    B...0...l.e.-.M.  
    53 00 53 00 6d 00 61 00 72 00 74 00 63 00 61 00    S.S.m.a.r.t.c.a.  
    72 00 64 00 55 00 73 00 65 00 72 00 2d 00 37 00    r.d.U.s.e.r.-.7.  
    32 00 36 00 37 00 32 00 62 00 39 00 62 00 2d 00    2.6.7.2.b.9.b.-.  
    30 00 30 00 61 00 64 00 2d 00 2d 00 33 00 36 00    0.0.a.d.-.-.3.6.  
    37 00 33 00 34 00 00 00 4d 00 69 00 63 00 72 00    7.3.4...M.i.c.r.  
    6f 00 73 00 6f 00 66 00 74 00 20 00 42 00 61 00    o.s.o.f.t...B.a.  
    73 00 65 00 20 00 53 00 6d 00 61 00 72 00 74 00    s.e...S.m.a.r.t.  
    20 00 43 00 61 00 72 00 64 00 20 00 43 00 72 00    ..C.a.r.d...C.r.  
    79 00 70 00 74 00 6f 00 20 00 50 00 72 00 6f 00    y.p.t.o...P.r.o.  
    76 00 69 00 64 00 65 00 72 00 00 00                v.i.d.e.r...  
    476 total bytes.  
    [PID: 4396]  
      
      
      
      
      
      
    */  
      
      
      
      
      
        // size of KERB_CERTIFICATE_UNLOCK_LOGON or KERB_CERTIFICATE_LOGON  
        //const size_t cbKERB_CERTIFICATE_UNLOCK_LOGON = sizeof(KERB_CERTIFICATE_UNLOCK_LOGON);  
        const size_t cbKERB_CERTIFICATE_LOGON        = sizeof(KERB_CERTIFICATE_LOGON);  
        
      
        PCWSTR pwszDomainHint                        = NULL;  
        PCWSTR pwszUserNameHint                      = NULL;  
        size_t cbDomainHint                          = 0;  
        size_t cbUserNameHint                        = 0; //wcslen(_rgwszUserNameHint) * sizeof(WCHAR); // mjs  
      
        // size of KERB_SMARTCARD_CSP_INFO  
        //const size_t cbKerbSCCSPInfo    = offsetof(KERB_SMARTCARD_CSP_INFO, bBuffer);  
        const size_t cbKerbSCCSPInfo = sizeof(KERB_SMARTCARD_CSP_INFO) - (sizeof(WCHAR)*2);  
        size_t cbCardName           = (wcslen(pwszCardName) + 1) * sizeof(WCHAR);  
        size_t cbReaderName         = (wcslen(pwszReaderName) + 1) * sizeof(WCHAR);  
        size_t cbContainerName      = (wcslen(pwszContainerName) + 1) * sizeof(WCHAR);  
        size_t cbProviderName       = (wcslen(pwszProviderName) + 1) * sizeof(WCHAR);  
     mylog("%u %u %u %u %u\n", sizeof(KERB_SMARTCARD_CSP_INFO) /*cbKerbSCCSPInfo*/, cbCardName, cbReaderName, cbContainerName, cbProviderName);  
      
        size_t cbCspDataLength = 0x140; //cbKerbSCCSPInfo + cbCardName + cbReaderName + cbContainerName + cbProviderName;  
     mylog("%u %u %u %u %u\n", cbKerbSCCSPInfo, cbCardName, cbReaderName, cbContainerName, cbProviderName);  
      
        size_t cb = ROUND_UP_COUNT(cbKERB_CERTIFICATE_LOGON, ALIGN_LPVOID) +  
      ROUND_UP_COUNT(dwEncLen, ALIGN_LPVOID) +   
             ROUND_UP_COUNT(cbDomainHint, ALIGN_LPVOID) +  
             ROUND_UP_COUNT(cbUserNameHint, ALIGN_LPVOID) +  
             cbCspDataLength;  
      
        BYTE* rgbBase;  
     rgbBase = (BYTE *)CoTaskMemAlloc(cb); //(BYTE *)malloc(cb);  
      
      
      
        //KERB_INTERACTIVE_UNLOCK_LOGON* pkiulOut = (KERB_INTERACTIVE_UNLOCK_LOGON*)CoTaskMemAlloc(cb);  
      
      
        memset(rgbBase, 0, cb);  
     mylog("cb: %u\n", cb);  
      
        //if (SUCCEEDED(hr))  
        //{  
     // mylog("EncryptPin succeeded.\r\n");  
      
      
            memset(rgbBase, 0, cb - cbCspDataLength);  
            ULONG NegoPackageId;  
      
     hr = RetrieveNegotiateAuthPackage(&NegoPackageId);  
            if (SUCCEEDED(hr))  
            {  
     mylog("RetrieveNegotiateAuthPackage succeeded.\r\n");  
     BYTE* rgb = rgbBase;  
      
                KERB_CERTIFICATE_LOGON* pkscl = reinterpret_cast<KERB_CERTIFICATE_LOGON*>(rgb);  
                pkscl->Flags = 0;  
                pkscl->MessageType =  (KERB_LOGON_SUBMIT_TYPE)13; //KerbCertificateLogon = 13  
                rgb += ROUND_UP_COUNT(cbKERB_CERTIFICATE_LOGON, ALIGN_LPVOID);  
      
                pkscl->DomainName.Length = (USHORT)cbDomainHint;  
                pkscl->DomainName.MaximumLength = (USHORT)cbDomainHint;  
                pkscl->DomainName.Buffer = (PWSTR)(rgb - rgbBase); // offset relative to rgbBase (for serialization)  
                CopyMemory(rgb, pwszDomainHint, cbDomainHint);  
                rgb += ROUND_UP_COUNT(cbDomainHint,ALIGN_LPVOID);  
      
                pkscl->UserName.Length = (USHORT)cbUserNameHint;  
                pkscl->UserName.MaximumLength = (USHORT)cbUserNameHint;  
                pkscl->UserName.Buffer = (PWSTR)(rgb - rgbBase); // offset relative to rgbBase (for serialization)  
                CopyMemory(rgb, pwszUserNameHint, cbUserNameHint);  
                rgb += ROUND_UP_COUNT(cbUserNameHint, ALIGN_LPVOID);  
      
                pkscl->Pin.Length = (USHORT)dwEncLen;  
                pkscl->Pin.MaximumLength = (USHORT)dwEncLen;  
                pkscl->Pin.Buffer = (PWSTR)(rgb - rgbBase); // offset relative to rgbBase (for serialization)  
                CopyMemory(rgb, pwszEncryptedPin, dwEncLen);  
                rgb += ROUND_UP_COUNT(dwEncLen, ALIGN_LPVOID);  
      
                pkscl->CspDataLength = (ULONG)cbCspDataLength;  
                pkscl->CspData = (PUCHAR)(rgb - rgbBase); // offset relative to rgbBase (for serialization)  
      
                dwLogonInfoLen = pkscl->CspDataLength;  
      
                KERB_SMARTCARD_CSP_INFO* pli = reinterpret_cast<KERB_SMARTCARD_CSP_INFO*>(rgb);  
                //pli->dwLogonInfoLen = dwLogonInfoLen;  
                pli->dwCspInfoLen = dwLogonInfoLen;  
                pli->MessageType = LogonInfo2;  
      
                pli->flags = 0;  
                pli->KeySpec = AT_KEYEXCHANGE; //_psrcd->GetKeySpec();  
      
                pli->nCardNameOffset = 0;  
                pli->nReaderNameOffset = pli->nCardNameOffset + (ULONG)(cbCardName / sizeof(WCHAR));  
                pli->nContainerNameOffset = pli->nReaderNameOffset + (ULONG)(cbReaderName / sizeof(WCHAR));  
                pli->nCSPNameOffset = pli->nContainerNameOffset + (ULONG)(cbContainerName / sizeof(WCHAR));  
      
                CopyMemory(&pli->bBuffer[pli->nCardNameOffset], pwszCardName, cbCardName);  
                CopyMemory(&pli->bBuffer[pli->nReaderNameOffset], pwszReaderName, cbReaderName);  
                CopyMemory(&pli->bBuffer[pli->nContainerNameOffset], pwszContainerName, cbContainerName);  
                CopyMemory(&pli->bBuffer[pli->nCSPNameOffset], pwszProviderName, cbProviderName);  
                rgb += dwLogonInfoLen;  
      
                pcpcs->ulAuthenticationPackage = NegoPackageId;  
                pcpcs->clsidCredentialProvider = CLSID_SmartcardCredentialProvider;  
                pcpcs->cbSerialization = (ULONG)cb;  
                pcpcs->rgbSerialization = rgbBase;  
      
                //if (SUCCEEDED(hr))  
                //{  
                    *pcpgsr = CPGSR_RETURN_CREDENTIAL_FINISHED;  
                //}  
            }  
     else  
     mylog("RetrieveNegotiateAuthPackage failed.\r\n");  
      
        //}  
      
    //Ret:  
         
        //if (pwszEncryptedPin)  
        //    free(pwszEncryptedPin);  
      
        return hr;  
    }  
      
      
      
      
      
      
      
    // CSampleCredential ////////////////////////////////////////////////////////  
      
    CSampleCredential::CSampleCredential():  
        _cRef(1),  
        _pCredProvCredentialEvents(NULL)  
    {  
        DllAddRef();  
      
        ZeroMemory(_rgCredProvFieldDescriptors, sizeof(_rgCredProvFieldDescriptors));  
        ZeroMemory(_rgFieldStatePairs, sizeof(_rgFieldStatePairs));  
        ZeroMemory(_rgFieldStrings, sizeof(_rgFieldStrings));  
    }  
      
    CSampleCredential::~CSampleCredential()  
    {  
        if (_rgFieldStrings[SFI_PASSWORD])  
        {  
            // CoTaskMemFree (below) deals with NULL, but StringCchLength does not.  
            size_t lenPassword;  
            HRESULT hr = StringCchLengthW(_rgFieldStrings[SFI_PASSWORD], 128, &(lenPassword));  
            if (SUCCEEDED(hr))  
            {  
                SecureZeroMemory(_rgFieldStrings[SFI_PASSWORD], lenPassword * sizeof(*_rgFieldStrings[SFI_PASSWORD]));  
            }  
            else  
            {  
                // TODO: Determine how to handle count error here.  
            }  
        }  
        for (int i = 0; i < ARRAYSIZE(_rgFieldStrings); i++)  
        {  
            CoTaskMemFree(_rgFieldStrings[i]);  
            CoTaskMemFree(_rgCredProvFieldDescriptors[i].pszLabel);  
        }  
      
        DllRelease();  
    }  
      
    // Initializes one credential with the field information passed in.  
    // Set the value of the SFI_USERNAME field to pwzUsername.  
    // Optionally takes a password for the SetSerialization case.  
    HRESULT CSampleCredential::Initialize(  
        CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus,  
        const CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR* rgcpfd,  
        const FIELD_STATE_PAIR* rgfsp,  
        PCWSTR pwzUsername,  
        PCWSTR pwzPassword  
        )  
    {  
        HRESULT hr = S_OK;  
      
        _cpus = cpus;  
      
        // Copy the field descriptors for each field. This is useful if you want to vary the   
        // field descriptors based on what Usage scenario the credential was created for.  
        for (DWORD i = 0; SUCCEEDED(hr) && i < ARRAYSIZE(_rgCredProvFieldDescriptors); i++)  
        {  
            _rgFieldStatePairs[i] = rgfsp[i];  
            hr = FieldDescriptorCopy(rgcpfd[i], &_rgCredProvFieldDescriptors[i]);  
        }  
      
        // Initialize the String values of all the fields.  
        if (SUCCEEDED(hr))  
        {  
            hr = SHStrDupW(pwzUsername, &_rgFieldStrings[SFI_USERNAME]);  
        }  
        if (SUCCEEDED(hr))  
        {  
            hr = SHStrDupW(pwzPassword ? pwzPassword : L"", &_rgFieldStrings[SFI_PASSWORD]);  
        }  
        if (SUCCEEDED(hr))  
        {  
            hr = SHStrDupW(L"Submit", &_rgFieldStrings[SFI_SUBMIT_BUTTON]);  
        }  
      
        return S_OK;  
    }  
      
    // LogonUI calls this in order to give us a callback in case we need to notify it of anything.  
    HRESULT CSampleCredential::Advise(  
        ICredentialProviderCredentialEvents* pcpce  
        )  
    {  
        if (_pCredProvCredentialEvents != NULL)  
        {  
            _pCredProvCredentialEvents->Release();  
        }  
        _pCredProvCredentialEvents = pcpce;  
        _pCredProvCredentialEvents->AddRef();  
        return S_OK;  
    }  
      
    // LogonUI calls this to tell us to release the callback.  
    HRESULT CSampleCredential::UnAdvise()  
    {  
        if (_pCredProvCredentialEvents)  
        {  
            _pCredProvCredentialEvents->Release();  
        }  
        _pCredProvCredentialEvents = NULL;  
        return S_OK;  
    }  
      
    // LogonUI calls this function when our tile is selected (zoomed).  
    // If you simply want fields to show/hide based on the selected state,  
    // there's no need to do anything here - you can set that up in the   
    // field definitions.  But if you want to do something  
    // more complicated, like change the contents of a field when the tile is  
    // selected, you would do it here.  
    HRESULT CSampleCredential::SetSelected(BOOL* pbAutoLogon)    
    {  
        *pbAutoLogon = FALSE;    
      
        return S_OK;  
    }  
      
    // Similarly to SetSelected, LogonUI calls this when your tile was selected  
    // and now no longer is. The most common thing to do here (which we do below)  
    // is to clear out the password field.  
    HRESULT CSampleCredential::SetDeselected()  
    {  
           HRESULT hr = S_OK;  
        if (_rgFieldStrings[SFI_PASSWORD])  
        {  
            // CoTaskMemFree (below) deals with NULL, but StringCchLength does not.  
            size_t lenPassword;  
            hr = StringCchLengthW(_rgFieldStrings[SFI_PASSWORD], 128, &(lenPassword));  
            if (SUCCEEDED(hr))  
            {  
                SecureZeroMemory(_rgFieldStrings[SFI_PASSWORD], lenPassword * sizeof(*_rgFieldStrings[SFI_PASSWORD]));  
              
                CoTaskMemFree(_rgFieldStrings[SFI_PASSWORD]);  
                hr = SHStrDupW(L"", &_rgFieldStrings[SFI_PASSWORD]);  
            }  
          
            if (SUCCEEDED(hr) && _pCredProvCredentialEvents)  
            {  
                _pCredProvCredentialEvents->SetFieldString(this, SFI_PASSWORD, _rgFieldStrings[SFI_PASSWORD]);  
            }  
        }  
      
        return hr;  
    }  
      
    // Gets info for a particular field of a tile. Called by logonUI to get information to   
    // display the tile.  
    HRESULT CSampleCredential::GetFieldState(  
        DWORD dwFieldID,  
        CREDENTIAL_PROVIDER_FIELD_STATE* pcpfs,  
        CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE* pcpfis  
        )  
    {  
        HRESULT hr;  
      
        // Validate paramters.  
        if ((dwFieldID < ARRAYSIZE(_rgFieldStatePairs)) && pcpfs && pcpfis)  
        {  
            *pcpfs = _rgFieldStatePairs[dwFieldID].cpfs;  
            *pcpfis = _rgFieldStatePairs[dwFieldID].cpfis;  
      
            hr = S_OK;  
        }  
        else  
        {  
            hr = E_INVALIDARG;  
        }  
        return hr;  
    }  
      
    // Sets ppwsz to the string value of the field at the index dwFieldID.  
    HRESULT CSampleCredential::GetStringValue(  
        DWORD dwFieldID,   
        PWSTR* ppwsz  
        )  
    {  
        HRESULT hr;  
      
        // Check to make sure dwFieldID is a legitimate index.  
        if (dwFieldID < ARRAYSIZE(_rgCredProvFieldDescriptors) && ppwsz)   
        {  
            // Make a copy of the string and return that. The caller  
            // is responsible for freeing it.  
            hr = SHStrDupW(_rgFieldStrings[dwFieldID], ppwsz);  
        }  
        else  
        {  
            hr = E_INVALIDARG;  
        }  
      
        return hr;  
    }  
      
    // Gets the image to show in the user tile.  
    HRESULT CSampleCredential::GetBitmapValue(  
        DWORD dwFieldID,   
        HBITMAP* phbmp  
        )  
    {  
        HRESULT hr;  
        if ((SFI_TILEIMAGE == dwFieldID) && phbmp)  
        {  
            HBITMAP hbmp = LoadBitmap(HINST_THISDLL, MAKEINTRESOURCE(IDB_TILE_IMAGE));  
            if (hbmp != NULL)  
            {  
                hr = S_OK;  
                *phbmp = hbmp;  
            }  
            else  
            {  
                hr = HRESULT_FROM_WIN32(GetLastError());  
            }  
        }  
        else  
        {  
            hr = E_INVALIDARG;  
        }  
      
        return hr;  
    }  
      
    // Sets pdwAdjacentTo to the index of the field the submit button should be   
    // adjacent to. We recommend that the submit button is placed next to the last  
    // field which the user is required to enter information in. Optional fields  
    // should be below the submit button.  
    HRESULT CSampleCredential::GetSubmitButtonValue(  
        DWORD dwFieldID,  
        DWORD* pdwAdjacentTo  
        )  
    {  
        HRESULT hr;  
      
        // Validate parameters.  
        if ((SFI_SUBMIT_BUTTON == dwFieldID) && pdwAdjacentTo)  
        {  
            // pdwAdjacentTo is a pointer to the fieldID you want the submit button to appear next to.  
            *pdwAdjacentTo = SFI_PASSWORD;  
            hr = S_OK;  
        }  
        else  
        {  
            hr = E_INVALIDARG;  
        }  
        return hr;  
    }  
      
    // Sets the value of a field which can accept a string as a value.  
    // This is called on each keystroke when a user types into an e
    

3 additional answers

Sort by: Most helpful
  1. Teemo Tang 11,466 Reputation points
    2020-11-16T02:39:02.18+00:00

    I only find out this Microsoft article about Custom credential providers
    https://learn.microsoft.com/en-us/troubleshoot/windows-client/user-profiles-and-logon/custom-credential-providers-dont-load-first-logon
    Your question is related to development, if you have feedback on Feedback Hub, you’d better wait for reply there or ask for help from MSDN or github.
    The reason why we recommend posting appropriately is you will get the most qualified pool of respondents, and other partners who read the forums regularly can either share their knowledge or learn.
    Thanks for your understanding and cooperating.

    -------------------------------------------------------------------------------------

    If the Answer is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    0 comments No comments

  2. Greg_N_Huntsville 21 Reputation points
    2020-11-16T12:13:33.137+00:00

    The MSDN forum site is where I started to post the question, but saw the notice below. Previously I posted questions in the Windows Desktop Development / Security forums which are no longer there.

    The following forum(s) have migrated to Microsoft Q&A: All English Windows and Windows phone apps, Microsoft Azure, SharePoint, Windows Desktop Development, Visual FoxPro, Internet of Things, Visual Studio Development, Windows Forms, SQL Server, Power BI forums, Microsoft Partner Center API, Windows Presentation Foundation (WPF), Bing Ads API: Development, Microsoft Graph, Small Basic!
    Visit Microsoft Q&A to post new questions.

    0 comments No comments

  3. Teemo Tang 11,466 Reputation points
    2020-11-18T07:44:31.717+00:00

    I have add a tag called winapi-sdk, which is corresponding to previous Windows Desktop development, let's wait the reply from other engineers

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.