How to use ADSI/LDAP API’s for querying active directory using credentials derived from a Smart Card

There are no ADSI/LDAP API’s that eventually could directly use the Smart Card Credentials.

We could follow the steps below:

1. Get the user credentials by reading in the certificate from the Smart card.

2. Call LogonUser() to get the user’s token.

3. Use this token to impersonate the user.

4. Under the impersonated user’s context, perform the ADSI/LDAP operations which will eventually run under the impersonated user.

In short after you have impersonated a user, the calling thread runs under the security context of this user and you need not have to pass any further credentials to the ADSI/LDAP API’s.

Please note: Calling LogonUser with marshaled certificate credentials requires that the smart card certificate be located in the personal certificate store otherwise LogonUser would fail with error 1312 (ERROR_NO_SUCH_LOGON_SESSION).

Example:

#include "stdafx.h"

#include <Iads.h>

#include <Adshlp.h>

#define MAX_CERT_SIMPLE_NAME_STR 1000

int SmartCardLogon (TCHAR * pPIN);

int _tmain(int argc, _TCHAR* argv[])

{

    if (argc != 2)

    {

        _tprintf(_T ("\nUSAGE: %ls PIN \n"), argv[0]);

        _tprintf(_T ("Example: \"%ls 1234 \"\n\n"), argv[0]);

        return 1;

    }

    SmartCardLogon(argv[1]);

    return 0;

}

int SmartCardLogon (TCHAR * pPIN)

{

    HANDLE hToken;

    HCRYPTPROV hProv;

    HCRYPTKEY hKey;

    HCERTSTORE hStoreHandle = NULL;

    HCRYPTHASH hHash;

    BOOL fStatus;

    BOOL fSave = FALSE;

    BOOL bStatus;

    SCARDCONTEXT hSC;

    OPENCARDNAME_EX dlgStruct;

    CERT_CREDENTIAL_INFO certInfo;

    WCHAR szReader[256];

    WCHAR szCard[256];

    WCHAR pProviderName[256];

    LONG lReturn;

    DWORD lStatus;

    DWORD cchProvider = 256;

    DWORD dwCertLen;

    DWORD dwLogonCertsCount = 0;

    DWORD dwHashLen = CERT_HASH_LENGTH;

    BYTE* pCertBlob;

    PCCERT_CONTEXT pCertContext = NULL;

    LPTSTR szMarshaledCred = NULL;

    // Establish a context.

    // It will be assigned to the structure's hSCardContext field.

    lReturn = SCardEstablishContext(

        SCARD_SCOPE_USER,

        NULL,

        NULL,

        &hSC );

    if ( SCARD_S_SUCCESS != lReturn )

    {

        _tprintf(_T("Failed SCardEstablishContext\n"));

        return 1;

    }

    // Initialize the structure.

    memset(&dlgStruct, 0, sizeof(dlgStruct));

    dlgStruct.dwStructSize = sizeof(dlgStruct);

    dlgStruct.hSCardContext = hSC;

    dlgStruct.dwFlags = SC_DLG_FORCE_UI;

    dlgStruct.lpstrRdr = szReader;

    dlgStruct.nMaxRdr = 256;

    dlgStruct.lpstrCard = szCard;

    dlgStruct.nMaxCard = 256;

    dlgStruct.lpstrTitle = L"My Select Card Title";

    // Display the select card dialog box.

    lReturn = SCardUIDlgSelectCard(&dlgStruct);

    if ( SCARD_S_SUCCESS != lReturn )

    {

        _tprintf(_T("Failed SCardUIDlgSelectCard - %x\n"), lReturn);

    }

    else

    {

        _tprintf(_T("Reader: %S\nCard: %S\n"), szReader, szCard);

    }

    lStatus = SCardGetCardTypeProviderName(

        dlgStruct.hSCardContext, // SCARDCONTEXT hContext,

        dlgStruct.lpstrCard, // LPCTSTR szCardName,

        SCARD_PROVIDER_CSP, // DWORD dwProviderId,

        pProviderName, // LPTSTR szProvider,

        &cchProvider // LPDWORD* pcchProvider

        );

    _tprintf(_T("SCardGetCardTypeProviderName returned: %u (a value of 0 is success)\n"), lStatus);

    if ( SCARD_S_SUCCESS != lReturn )

    {

        _tprintf(_T("Failed SCardGetCardTypeProviderName - %u\n"), lStatus );

    }

    else

    {

        _tprintf(_T("Provider name: %ls.\n"), pProviderName);

    }

    fStatus = CryptAcquireContext(

        &hProv, // HCRYPTPROV* phProv,

        NULL, // LPCTSTR pszContainer,

        pProviderName, // LPCTSTR pszProvider,

        PROV_RSA_FULL, // DWORD dwProvType,

        0 // DWORD dwFlags

        );

    if (!fStatus)

    {

        _tprintf(_T("CryptAcquireContext failed: 0x%x\n"), GetLastError());

        return 1;

    }

    else

    {

        _tprintf(_T("CryptAcquireContext succeeded.\n"));

    }

    fStatus = CryptGetUserKey(

        hProv, // HCRYPTPROV hProv,

        AT_KEYEXCHANGE, // DWORD dwKeySpec,

        &hKey // HCRYPTKEY* phUserKey

        );

    if (!fStatus)

    {

        _tprintf(_T("CryptGetUserKey failed: 0x%x\n"), GetLastError());

        return 1;

    }

    else

    {

        _tprintf(_T("CryptGetUserKey succeeded.\n"));

    }

    dwCertLen = 0;

    fStatus = CryptGetKeyParam(

        hKey, // HCRYPTKEY hKey,

        KP_CERTIFICATE, // DWORD dwParam,

        NULL, // BYTE* pbData,

        &dwCertLen, // DWORD* pdwDataLen,

        0 // DWORD dwFlags

        );

    if (!fStatus)

    {

        _tprintf(_T("CryptGetUserKey failed: 0x%x\n"), GetLastError());

        return 1;

    }

    else

    {

        _tprintf(_T("CryptGetUserKey succeeded.\n"));

    }

    _tprintf(_T("dwCertLen: %u\n"), dwCertLen);

    pCertBlob = (BYTE*) malloc(dwCertLen);

    fStatus = CryptGetKeyParam(

        hKey, // HCRYPTKEY hKey,

        KP_CERTIFICATE, // DWORD dwParam,

        pCertBlob, // BYTE* pbData,

        &dwCertLen, // DWORD* pdwDataLen,

        0 // DWORD dwFlags

        );

    if (!fStatus)

    {

        _tprintf(_T("CryptGetUserKey failed: 0x%x\n"), GetLastError());

        return 1;

    }

    else

    {

        _tprintf(_T("CryptGetUserKey succeeded.\n"));

    }

    pCertContext = CertCreateCertificateContext(

        PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,

        pCertBlob,

        dwCertLen);

    if(pCertContext)

    {

        // print some info

        TCHAR Issuer[MAX_CERT_SIMPLE_NAME_STR + 1];

        TCHAR Subject[MAX_CERT_SIMPLE_NAME_STR + 1];

        CertNameToStr(X509_ASN_ENCODING,

            &pCertContext->pCertInfo->Subject,

            CERT_SIMPLE_NAME_STR,

            Subject,

            MAX_CERT_SIMPLE_NAME_STR);

        CertNameToStr(X509_ASN_ENCODING,

            &pCertContext->pCertInfo->Issuer,

            CERT_SIMPLE_NAME_STR,

            Issuer,

            MAX_CERT_SIMPLE_NAME_STR);

        _tprintf(_T("Subject:%s\n"), Subject);

        _tprintf(_T("Issuer:%s\n"), Issuer);

        ZeroMemory(&certInfo, sizeof(certInfo));

        certInfo.cbSize = sizeof(certInfo);

        // compute the SHA-1 hash of the certificate

        bStatus = CryptAcquireContext(&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);

        if (bStatus)

        {

            bStatus = CryptCreateHash(hProv, CALG_SHA1, NULL, 0, &hHash);

            if (bStatus)

            {

                bStatus = CryptHashData(hHash, pCertContext->pbCertEncoded, pCertContext->cbCertEncoded, 0);

                if (bStatus)

                {

                    bStatus = CryptGetHashParam(hHash, HP_HASHVAL, certInfo.rgbHashOfCert, &dwHashLen, 0);

                }

                CryptDestroyHash(hHash);

            }

            CryptReleaseContext(hProv, 0);

        }

        if (bStatus)

        {

            bStatus = CredMarshalCredential(CertCredential, &certInfo, &szMarshaledCred);

            if (bStatus)

            {

                //make sure your account has enough privilage to execute LogonUser

                bStatus = LogonUser(szMarshaledCred,

                    NULL,

                    pPIN,

                    LOGON32_LOGON_NETWORK,

                    LOGON32_PROVIDER_DEFAULT,

                    &hToken);

                if (bStatus)

                {

                    _tprintf(_T("LogonUser success - test passed\n"));

                    //

                    // impersonate the user we just got a token for

                    //

                    if (ImpersonateLoggedOnUser(hToken))

                    {

                        TCHAR szName[255];

                        DWORD cbName=255;

                        //

                        // indicate who we logged on, to verify success

                        //

                        GetUserName(szName, &cbName);

                        _tprintf(_T("Successfully logged on user = %s\n"), szName);

                        // AD specific calls

                        HRESULT hr;

                        IADsUser *padsRoot1;

                        hr = CoInitialize(NULL);

                        //Adding code for the AdSystemInfo to check the current user from thread

                        IADsADSystemInfo *pSys;

                        hr = CoCreateInstance(CLSID_ADSystemInfo,

                            NULL,

                            CLSCTX_INPROC_SERVER,

                            IID_IADsADSystemInfo,

                            (void**)&pSys);

                        BSTR bstr;

                        hr = pSys->get_UserName(&bstr);

                        if (SUCCEEDED(hr)) {

                            _tprintf(_T("User: %S\n"), bstr);

                            SysFreeString(bstr);

                        }

                        //Finished AdSystemInfo

                        hr = ADsOpenObject(

                            L"LDAP://DC=FAREAST,DC=CORP,DC=MICROSOFT,DC=COM",

                            NULL,

                            NULL,

                            ADS_SECURE_AUTHENTICATION,

                            IID_IADs,

                            (LPVOID*)&padsRoot1

                            );

                        CoUninitialize();

                        // End

                        RevertToSelf();

                    }

                    CloseHandle(hToken);

                }

                else

                {

                    _tprintf(_T("LogonUser failed with error %d\n"), GetLastError());

                }

                CredFree(szMarshaledCred);

            }

            else

            {

                _tprintf(_T("CredMarshalCredential failed with error 0x%.8X\n"), GetLastError());

            }

        }

        else

        {

            _tprintf(_T("Failed to compute logon certificate hash\n"));

        }

        CertFreeCertificateContext(pCertContext);

    }

    return 0;

}

References:

https://msdn.microsoft.com/en-us/library/aa379479(VS.85).aspx

https://msdn.microsoft.com/en-us/library/aa376033(VS.85).aspx

https://msdn.microsoft.com/en-us/library/aa379886(VS.85).aspx

https://msdn.microsoft.com/en-us/library/aa378184(VS.85).aspx

https://msdn.microsoft.com/en-us/library/aa378612(v=VS.85).aspx

https://msdn.microsoft.com/en-us/library/aa746340(VS.85).aspx

https://msdn.microsoft.com/en-us/library/aa772238(VS.85).aspx

-Shamik