diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bfaaa3bc..6c226e525 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,6 +109,12 @@ message("FREERDP_VERSION=${FREERDP_VERSION_FULL}") set(FREERDP_INCLUDE_DIR "include/freerdp${FREERDP_VERSION_MAJOR}/") +option(WITH_SMARTCARD_EMULATE "Emulate smartcards instead of redirecting readers" OFF) +if (WITH_SMARTCARD_EMULATE) + add_definitions(-DWITH_SMARTCARD_EMULATE) + find_package(ZLIB REQUIRED) +endif() + option(WITH_FREERDP_DEPRECATED "Build FreeRDP deprecated symbols" OFF) if (WITH_FREERDP_DEPRECATED) add_definitions(-DWITH_FREERDP_DEPRECATED) diff --git a/channels/smartcard/client/CMakeLists.txt b/channels/smartcard/client/CMakeLists.txt index ef8908713..717a7608f 100644 --- a/channels/smartcard/client/CMakeLists.txt +++ b/channels/smartcard/client/CMakeLists.txt @@ -27,9 +27,10 @@ set(${MODULE_PREFIX}_SRCS add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DeviceServiceEntry") - - -target_link_libraries(${MODULE_NAME} winpr freerdp) +target_link_libraries(${MODULE_NAME} winpr freerdp ${OPENSSL_LIBRARIES}) +if (WITH_SMARTCARD_EMULATE) + target_link_libraries(${MODULE_NAME} ZLIB::ZLIB) +endif() set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client") diff --git a/channels/smartcard/client/smartcard_main.c b/channels/smartcard/client/smartcard_main.c index fba610f62..d37ede6e4 100644 --- a/channels/smartcard/client/smartcard_main.c +++ b/channels/smartcard/client/smartcard_main.c @@ -36,6 +36,14 @@ #define CAST_FROM_DEVICE(device) cast_device_from(device, __FUNCTION__, __FILE__, __LINE__) +#if defined(WITH_SMARTCARD_EMULATE) +#include +#define str(x) #x +#define wrap(smartcard, fkt, ...) Emulate_##fkt(smartcard->emulation, ##__VA_ARGS__) +#else +#define wrap(smartcard, fkt, ...) fkt(__VA_ARGS__) +#endif + static SMARTCARD_DEVICE* sSmartcard = NULL; static SMARTCARD_DEVICE* cast_device_from(DEVICE* device, const char* fkt, const char* file, @@ -152,7 +160,7 @@ SMARTCARD_CONTEXT* smartcard_context_new(SMARTCARD_DEVICE* smartcard, SCARDCONTE if (!pContext->IrpQueue) { WLog_ERR(TAG, "MessageQueue_New failed!"); - goto error_irpqueue; + goto fail; } pContext->thread = CreateThread(NULL, 0, smartcard_context_thread, pContext, 0, NULL); @@ -160,14 +168,12 @@ SMARTCARD_CONTEXT* smartcard_context_new(SMARTCARD_DEVICE* smartcard, SCARDCONTE if (!pContext->thread) { WLog_ERR(TAG, "CreateThread failed!"); - goto error_thread; + goto fail; } return pContext; -error_thread: - MessageQueue_Free(pContext->IrpQueue); -error_irpqueue: - free(pContext); +fail: + smartcard_context_free(pContext); return NULL; } @@ -179,15 +185,20 @@ void smartcard_context_free(void* pCtx) return; /* cancel blocking calls like SCardGetStatusChange */ - SCardCancel(pContext->hContext); - SCardReleaseContext(pContext->hContext); + wrap(pContext->smartcard, SCardCancel, pContext->hContext); - if (MessageQueue_PostQuit(pContext->IrpQueue, 0) && - (WaitForSingleObject(pContext->thread, INFINITE) == WAIT_FAILED)) - WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", GetLastError()); + if (pContext->IrpQueue) + { + if (MessageQueue_PostQuit(pContext->IrpQueue, 0)) + { + if (WaitForSingleObject(pContext->thread, INFINITE) == WAIT_FAILED) + WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", GetLastError()); - CloseHandle(pContext->thread); - MessageQueue_Free(pContext->IrpQueue); + CloseHandle(pContext->thread); + } + MessageQueue_Free(pContext->IrpQueue); + } + wrap(pContext->smartcard, SCardReleaseContext, pContext->hContext); free(pContext); } @@ -226,9 +237,9 @@ static void smartcard_release_all_contexts(SMARTCARD_DEVICE* smartcard) hContext = pContext->hContext; - if (SCardIsValidContext(hContext) == SCARD_S_SUCCESS) + if (wrap(smartcard, SCardIsValidContext, hContext) == SCARD_S_SUCCESS) { - SCardCancel(hContext); + wrap(smartcard, SCardCancel, hContext); } } @@ -278,8 +289,11 @@ static UINT smartcard_free_(SMARTCARD_DEVICE* smartcard) Queue_Free(smartcard->CompletedIrpQueue); if (smartcard->StartedEvent) - SCardReleaseStartedEvent(); + wrap(smartcard, SCardReleaseStartedEvent); +#if defined(WITH_SMARTCARD_EMULATE) + Emulate_Free(smartcard->emulation); +#endif free(smartcard); return CHANNEL_RC_OK; } @@ -290,7 +304,6 @@ static UINT smartcard_free_(SMARTCARD_DEVICE* smartcard) */ static UINT smartcard_free(DEVICE* device) { - UINT error; SMARTCARD_DEVICE* smartcard = CAST_FROM_DEVICE(device); if (!smartcard) @@ -309,7 +322,7 @@ static UINT smartcard_free(DEVICE* device) if (MessageQueue_PostQuit(smartcard->IrpQueue, 0) && (WaitForSingleObject(smartcard->thread, INFINITE) == WAIT_FAILED)) { - error = GetLastError(); + DWORD error = GetLastError(); WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error); return error; } @@ -780,6 +793,11 @@ UINT DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints) goto fail; } +#if defined(WITH_SMARTCARD_EMULATE) + smartcard->emulation = Emulate_New(smartcard->rdpcontext->settings); + if (!smartcard->emulation) + goto fail; +#endif if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman, &smartcard->device))) { WLog_ERR(TAG, "RegisterDevice failed!"); diff --git a/channels/smartcard/client/smartcard_main.h b/channels/smartcard/client/smartcard_main.h index 4d543d120..c6476cf5b 100644 --- a/channels/smartcard/client/smartcard_main.h +++ b/channels/smartcard/client/smartcard_main.h @@ -34,6 +34,10 @@ #include "smartcard_operations.h" +#if defined(WITH_SMARTCARD_EMULATE) +#include +#endif + #define TAG CHANNELS_TAG("smartcard.client") typedef struct _SMARTCARD_DEVICE SMARTCARD_DEVICE; @@ -59,6 +63,9 @@ struct _SMARTCARD_DEVICE wListDictionary* rgOutstandingMessages; rdpContext* rdpcontext; wLinkedList* names; +#if defined(WITH_SMARTCARD_EMULATE) + SmartcardEmulationContext* emulation; +#endif }; SMARTCARD_CONTEXT* smartcard_context_new(SMARTCARD_DEVICE* smartcard, SCARDCONTEXT hContext); diff --git a/channels/smartcard/client/smartcard_operations.c b/channels/smartcard/client/smartcard_operations.c index f13b6ca53..446715de8 100644 --- a/channels/smartcard/client/smartcard_operations.c +++ b/channels/smartcard/client/smartcard_operations.c @@ -40,6 +40,15 @@ #include "smartcard_operations.h" #include "smartcard_main.h" +#if defined(WITH_SMARTCARD_EMULATE) +#include + +#define str(x) #x +#define wrap(smartcard, fkt, ...) Emulate_##fkt(smartcard->emulation, ##__VA_ARGS__) +#else +#define wrap(smartcard, fkt, ...) fkt(__VA_ARGS__) +#endif + static LONG smartcard_call_to_operation_handle(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPERATION* operation) { @@ -256,7 +265,8 @@ static LONG smartcard_EstablishContext_Call(SMARTCARD_DEVICE* smartcard, EstablishContext_Return ret = { 0 }; IRP* irp = operation->irp; EstablishContext_Call* call = &operation->call.establishContext; - status = ret.ReturnCode = SCardEstablishContext(call->dwScope, NULL, NULL, &hContext); + status = ret.ReturnCode = + wrap(smartcard, SCardEstablishContext, call->dwScope, NULL, NULL, &hContext); if (ret.ReturnCode == SCARD_S_SUCCESS) { @@ -315,7 +325,7 @@ static LONG smartcard_ReleaseContext_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPERATION* operation) { Long_Return ret = { 0 }; - ret.ReturnCode = SCardReleaseContext(operation->hContext); + ret.ReturnCode = wrap(smartcard, SCardReleaseContext, operation->hContext); if (ret.ReturnCode == SCARD_S_SUCCESS) { @@ -354,7 +364,7 @@ static LONG smartcard_IsValidContext_Call(SMARTCARD_DEVICE* smartcard, { Long_Return ret = { 0 }; - ret.ReturnCode = SCardIsValidContext(operation->hContext); + ret.ReturnCode = wrap(smartcard, SCardIsValidContext, operation->hContext); smartcard_trace_long_return(smartcard, &ret, "IsValidContext"); return ret.ReturnCode; } @@ -383,7 +393,8 @@ static LONG smartcard_ListReaderGroupsA_Call(SMARTCARD_DEVICE* smartcard, DWORD cchGroups = 0; IRP* irp = operation->irp; cchGroups = SCARD_AUTOALLOCATE; - ret.ReturnCode = SCardListReaderGroupsA(operation->hContext, (LPSTR)&mszGroups, &cchGroups); + ret.ReturnCode = + wrap(smartcard, SCardListReaderGroupsA, operation->hContext, (LPSTR)&mszGroups, &cchGroups); ret.msz = (BYTE*)mszGroups; ret.cBytes = cchGroups; @@ -393,7 +404,7 @@ static LONG smartcard_ListReaderGroupsA_Call(SMARTCARD_DEVICE* smartcard, return status; if (mszGroups) - SCardFreeMemory(operation->hContext, mszGroups); + wrap(smartcard, SCardFreeMemory, operation->hContext, mszGroups); return ret.ReturnCode; } @@ -427,8 +438,8 @@ static LONG smartcard_ListReaderGroupsW_Call(SMARTCARD_DEVICE* smartcard, irp = operation->irp; cchGroups = SCARD_AUTOALLOCATE; - status = ret.ReturnCode = - SCardListReaderGroupsW(operation->hContext, (LPWSTR)&mszGroups, &cchGroups); + status = ret.ReturnCode = wrap(smartcard, SCardListReaderGroupsW, operation->hContext, + (LPWSTR)&mszGroups, &cchGroups); ret.msz = (BYTE*)mszGroups; ret.cBytes = cchGroups * sizeof(WCHAR); @@ -441,7 +452,7 @@ static LONG smartcard_ListReaderGroupsW_Call(SMARTCARD_DEVICE* smartcard, return status; if (mszGroups) - SCardFreeMemory(operation->hContext, mszGroups); + wrap(smartcard, SCardFreeMemory, operation->hContext, mszGroups); return ret.ReturnCode; } @@ -557,8 +568,8 @@ static LONG smartcard_ListReadersA_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_O IRP* irp = operation->irp; ListReaders_Call* call = &operation->call.listReaders; cchReaders = SCARD_AUTOALLOCATE; - status = ret.ReturnCode = SCardListReadersA(operation->hContext, (LPCSTR)call->mszGroups, - (LPSTR)&mszReaders, &cchReaders); + status = ret.ReturnCode = wrap(smartcard, SCardListReadersA, operation->hContext, + (LPCSTR)call->mszGroups, (LPSTR)&mszReaders, &cchReaders); if (call->mszGroups) { @@ -582,7 +593,7 @@ static LONG smartcard_ListReadersA_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_O } if (mszReaders) - SCardFreeMemory(operation->hContext, mszReaders); + wrap(smartcard, SCardFreeMemory, operation->hContext, mszReaders); if (status != SCARD_S_SUCCESS) return status; @@ -718,8 +729,8 @@ static LONG smartcard_ListReadersW_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_O string.bp = call->mszGroups; cchReaders = SCARD_AUTOALLOCATE; - status = ret.ReturnCode = - SCardListReadersW(operation->hContext, string.wz, (LPWSTR)&mszReaders.pw, &cchReaders); + status = ret.ReturnCode = wrap(smartcard, SCardListReadersW, operation->hContext, string.wz, + (LPWSTR)&mszReaders.pw, &cchReaders); if (call->mszGroups) { @@ -736,7 +747,7 @@ static LONG smartcard_ListReadersW_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_O status = smartcard_pack_list_readers_return(smartcard, irp->output, &ret, TRUE); if (mszReaders.pb) - SCardFreeMemory(operation->hContext, mszReaders.pb); + wrap(smartcard, SCardFreeMemory, operation->hContext, mszReaders.pb); if (status != SCARD_S_SUCCESS) return status; @@ -749,7 +760,7 @@ static LONG smartcard_IntroduceReaderGroupA_Call(SMARTCARD_DEVICE* smartcard, { Long_Return ret = { 0 }; ContextAndStringA_Call* call = &operation->call.contextAndStringA; - ret.ReturnCode = SCardIntroduceReaderGroupA(operation->hContext, call->sz); + ret.ReturnCode = wrap(smartcard, SCardIntroduceReaderGroupA, operation->hContext, call->sz); log_status_error(TAG, "SCardIntroduceReaderGroupA", ret.ReturnCode); if (call->sz) { @@ -766,7 +777,7 @@ static LONG smartcard_IntroduceReaderGroupW_Call(SMARTCARD_DEVICE* smartcard, { Long_Return ret = { 0 }; ContextAndStringW_Call* call = &operation->call.contextAndStringW; - ret.ReturnCode = SCardIntroduceReaderGroupW(operation->hContext, call->sz); + ret.ReturnCode = wrap(smartcard, SCardIntroduceReaderGroupW, operation->hContext, call->sz); log_status_error(TAG, "SCardIntroduceReaderGroupW", ret.ReturnCode); if (call->sz) { @@ -783,7 +794,8 @@ static LONG smartcard_IntroduceReaderA_Call(SMARTCARD_DEVICE* smartcard, { Long_Return ret = { 0 }; ContextAndTwoStringA_Call* call = &operation->call.contextAndTwoStringA; - ret.ReturnCode = SCardIntroduceReaderA(operation->hContext, call->sz1, call->sz2); + ret.ReturnCode = + wrap(smartcard, SCardIntroduceReaderA, operation->hContext, call->sz1, call->sz2); log_status_error(TAG, "SCardIntroduceReaderA", ret.ReturnCode); free(call->sz1); call->sz1 = NULL; @@ -799,7 +811,8 @@ static LONG smartcard_IntroduceReaderW_Call(SMARTCARD_DEVICE* smartcard, { Long_Return ret = { 0 }; ContextAndTwoStringW_Call* call = &operation->call.contextAndTwoStringW; - ret.ReturnCode = SCardIntroduceReaderW(operation->hContext, call->sz1, call->sz2); + ret.ReturnCode = + wrap(smartcard, SCardIntroduceReaderW, operation->hContext, call->sz1, call->sz2); log_status_error(TAG, "SCardIntroduceReaderW", ret.ReturnCode); free(call->sz1); call->sz1 = NULL; @@ -815,7 +828,7 @@ static LONG smartcard_ForgetReaderA_Call(SMARTCARD_DEVICE* smartcard, { Long_Return ret = { 0 }; ContextAndStringA_Call* call = &operation->call.contextAndStringA; - ret.ReturnCode = SCardForgetReaderA(operation->hContext, call->sz); + ret.ReturnCode = wrap(smartcard, SCardForgetReaderA, operation->hContext, call->sz); log_status_error(TAG, "SCardForgetReaderA", ret.ReturnCode); if (call->sz) { @@ -832,7 +845,7 @@ static LONG smartcard_ForgetReaderW_Call(SMARTCARD_DEVICE* smartcard, { Long_Return ret = { 0 }; ContextAndStringW_Call* call = &operation->call.contextAndStringW; - ret.ReturnCode = SCardForgetReaderW(operation->hContext, call->sz); + ret.ReturnCode = wrap(smartcard, SCardForgetReaderW, operation->hContext, call->sz); log_status_error(TAG, "SCardForgetReaderW", ret.ReturnCode); if (call->sz) { @@ -849,7 +862,8 @@ static LONG smartcard_AddReaderToGroupA_Call(SMARTCARD_DEVICE* smartcard, { Long_Return ret = { 0 }; ContextAndTwoStringA_Call* call = &operation->call.contextAndTwoStringA; - ret.ReturnCode = SCardAddReaderToGroupA(operation->hContext, call->sz1, call->sz2); + ret.ReturnCode = + wrap(smartcard, SCardAddReaderToGroupA, operation->hContext, call->sz1, call->sz2); log_status_error(TAG, "SCardAddReaderToGroupA", ret.ReturnCode); free(call->sz1); call->sz1 = NULL; @@ -865,7 +879,8 @@ static LONG smartcard_AddReaderToGroupW_Call(SMARTCARD_DEVICE* smartcard, { Long_Return ret = { 0 }; ContextAndTwoStringW_Call* call = &operation->call.contextAndTwoStringW; - ret.ReturnCode = SCardAddReaderToGroupW(operation->hContext, call->sz1, call->sz2); + ret.ReturnCode = + wrap(smartcard, SCardAddReaderToGroupW, operation->hContext, call->sz1, call->sz2); log_status_error(TAG, "SCardAddReaderToGroupW", ret.ReturnCode); free(call->sz1); call->sz1 = NULL; @@ -881,7 +896,8 @@ static LONG smartcard_RemoveReaderFromGroupA_Call(SMARTCARD_DEVICE* smartcard, { Long_Return ret = { 0 }; ContextAndTwoStringA_Call* call = &operation->call.contextAndTwoStringA; - ret.ReturnCode = SCardRemoveReaderFromGroupA(operation->hContext, call->sz1, call->sz2); + ret.ReturnCode = + wrap(smartcard, SCardRemoveReaderFromGroupA, operation->hContext, call->sz1, call->sz2); log_status_error(TAG, "SCardRemoveReaderFromGroupA", ret.ReturnCode); free(call->sz1); call->sz1 = NULL; @@ -897,7 +913,8 @@ static LONG smartcard_RemoveReaderFromGroupW_Call(SMARTCARD_DEVICE* smartcard, { Long_Return ret = { 0 }; ContextAndTwoStringW_Call* call = &operation->call.contextAndTwoStringW; - ret.ReturnCode = SCardRemoveReaderFromGroupW(operation->hContext, call->sz1, call->sz2); + ret.ReturnCode = + wrap(smartcard, SCardRemoveReaderFromGroupW, operation->hContext, call->sz1, call->sz2); log_status_error(TAG, "SCardRemoveReaderFromGroupW", ret.ReturnCode); free(call->sz1); call->sz1 = NULL; @@ -916,8 +933,8 @@ static LONG smartcard_LocateCardsA_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_O LocateCardsA_Call* call = &operation->call.locateCardsA; IRP* irp = operation->irp; - ret.ReturnCode = SCardLocateCardsA(operation->hContext, call->mszCards, call->rgReaderStates, - call->cReaders); + ret.ReturnCode = wrap(smartcard, SCardLocateCardsA, operation->hContext, call->mszCards, + call->rgReaderStates, call->cReaders); log_status_error(TAG, "SCardLocateCardsA", ret.ReturnCode); ret.cReaders = call->cReaders; ret.rgReaderStates = NULL; @@ -965,8 +982,8 @@ static LONG smartcard_LocateCardsW_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_O LocateCardsW_Call* call = &operation->call.locateCardsW; IRP* irp = operation->irp; - ret.ReturnCode = SCardLocateCardsW(operation->hContext, call->mszCards, call->rgReaderStates, - call->cReaders); + ret.ReturnCode = wrap(smartcard, SCardLocateCardsW, operation->hContext, call->mszCards, + call->rgReaderStates, call->cReaders); log_status_error(TAG, "SCardLocateCardsW", ret.ReturnCode); ret.cReaders = call->cReaders; ret.rgReaderStates = NULL; @@ -1026,13 +1043,13 @@ static LONG smartcard_ReadCacheA_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPE } if (autoalloc) - ret.ReturnCode = SCardReadCacheA(operation->hContext, call->Common.CardIdentifier, - call->Common.FreshnessCounter, call->szLookupName, - (BYTE*)&ret.pbData, &ret.cbDataLen); + ret.ReturnCode = wrap(smartcard, SCardReadCacheA, operation->hContext, + call->Common.CardIdentifier, call->Common.FreshnessCounter, + call->szLookupName, (BYTE*)&ret.pbData, &ret.cbDataLen); else - ret.ReturnCode = SCardReadCacheA(operation->hContext, call->Common.CardIdentifier, - call->Common.FreshnessCounter, call->szLookupName, - ret.pbData, &ret.cbDataLen); + ret.ReturnCode = + wrap(smartcard, SCardReadCacheA, operation->hContext, call->Common.CardIdentifier, + call->Common.FreshnessCounter, call->szLookupName, ret.pbData, &ret.cbDataLen); if ((ret.ReturnCode != SCARD_W_CACHE_ITEM_NOT_FOUND) && (ret.ReturnCode != SCARD_W_CACHE_ITEM_STALE)) { @@ -1043,7 +1060,7 @@ static LONG smartcard_ReadCacheA_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPE status = smartcard_pack_read_cache_return(smartcard, irp->output, &ret); if (autoalloc) - SCardFreeMemory(operation->hContext, ret.pbData); + wrap(smartcard, SCardFreeMemory, operation->hContext, ret.pbData); else free(ret.pbData); if (status != SCARD_S_SUCCESS) @@ -1058,26 +1075,14 @@ static LONG smartcard_ReadCacheW_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPE ReadCache_Return ret = { 0 }; ReadCacheW_Call* call = &operation->call.readCacheW; IRP* irp = operation->irp; - BOOL autoalloc = (call->Common.cbDataLen == SCARD_AUTOALLOCATE); - if (!call->Common.fPbDataIsNULL) - { - ret.cbDataLen = call->Common.cbDataLen; - if (!autoalloc) - { - ret.pbData = malloc(ret.cbDataLen); - if (!ret.pbData) - return SCARD_F_INTERNAL_ERROR; - } - } - if (autoalloc) - ret.ReturnCode = SCardReadCacheW(operation->hContext, call->Common.CardIdentifier, - call->Common.FreshnessCounter, call->szLookupName, - (BYTE*)&ret.pbData, &ret.cbDataLen); - else - ret.ReturnCode = SCardReadCacheW(operation->hContext, call->Common.CardIdentifier, - call->Common.FreshnessCounter, call->szLookupName, - ret.pbData, &ret.cbDataLen); + if (!call->Common.fPbDataIsNULL) + ret.cbDataLen = SCARD_AUTOALLOCATE; + + ret.ReturnCode = + wrap(smartcard, SCardReadCacheW, operation->hContext, call->Common.CardIdentifier, + call->Common.FreshnessCounter, call->szLookupName, (BYTE*)&ret.pbData, &ret.cbDataLen); + if ((ret.ReturnCode != SCARD_W_CACHE_ITEM_NOT_FOUND) && (ret.ReturnCode != SCARD_W_CACHE_ITEM_STALE)) { @@ -1087,10 +1092,9 @@ static LONG smartcard_ReadCacheW_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPE free(call->Common.CardIdentifier); status = smartcard_pack_read_cache_return(smartcard, irp->output, &ret); - if (autoalloc) - SCardFreeMemory(operation->hContext, ret.pbData); - else - free(ret.pbData); + + wrap(smartcard, SCardFreeMemory, operation->hContext, ret.pbData); + if (status != SCARD_S_SUCCESS) return status; @@ -1102,9 +1106,9 @@ static LONG smartcard_WriteCacheA_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OP Long_Return ret = { 0 }; WriteCacheA_Call* call = &operation->call.writeCacheA; - ret.ReturnCode = SCardWriteCacheA(operation->hContext, call->Common.CardIdentifier, - call->Common.FreshnessCounter, call->szLookupName, - call->Common.pbData, call->Common.cbDataLen); + ret.ReturnCode = wrap(smartcard, SCardWriteCacheA, operation->hContext, + call->Common.CardIdentifier, call->Common.FreshnessCounter, + call->szLookupName, call->Common.pbData, call->Common.cbDataLen); log_status_error(TAG, "SCardWriteCacheA", ret.ReturnCode); free(call->szLookupName); free(call->Common.CardIdentifier); @@ -1119,9 +1123,9 @@ static LONG smartcard_WriteCacheW_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OP Long_Return ret = { 0 }; WriteCacheW_Call* call = &operation->call.writeCacheW; - ret.ReturnCode = SCardWriteCacheW(operation->hContext, call->Common.CardIdentifier, - call->Common.FreshnessCounter, call->szLookupName, - call->Common.pbData, call->Common.cbDataLen); + ret.ReturnCode = wrap(smartcard, SCardWriteCacheW, operation->hContext, + call->Common.CardIdentifier, call->Common.FreshnessCounter, + call->szLookupName, call->Common.pbData, call->Common.cbDataLen); log_status_error(TAG, "SCardWriteCacheW", ret.ReturnCode); free(call->szLookupName); free(call->Common.CardIdentifier); @@ -1138,7 +1142,7 @@ static LONG smartcard_GetTransmitCount_Call(SMARTCARD_DEVICE* smartcard, GetTransmitCount_Return ret = { 0 }; IRP* irp = operation->irp; - ret.ReturnCode = SCardGetTransmitCount(operation->hCard, &ret.cTransmitCount); + ret.ReturnCode = wrap(smartcard, SCardGetTransmitCount, operation->hCard, &ret.cTransmitCount); log_status_error(TAG, "SCardGetTransmitCount", ret.ReturnCode); status = smartcard_pack_get_transmit_count_return(smartcard, irp->output, &ret); if (status != SCARD_S_SUCCESS) @@ -1167,12 +1171,12 @@ static LONG smartcard_GetReaderIcon_Call(SMARTCARD_DEVICE* smartcard, IRP* irp = operation->irp; ret.cbDataLen = SCARD_AUTOALLOCATE; - ret.ReturnCode = SCardGetReaderIconW(operation->hContext, call->szReaderName, - (LPBYTE)&ret.pbData, &ret.cbDataLen); + ret.ReturnCode = wrap(smartcard, SCardGetReaderIconW, operation->hContext, call->szReaderName, + (LPBYTE)&ret.pbData, &ret.cbDataLen); log_status_error(TAG, "SCardGetReaderIconW", ret.ReturnCode); free(call->szReaderName); status = smartcard_pack_get_reader_icon_return(smartcard, irp->output, &ret); - SCardFreeMemory(operation->hContext, ret.pbData); + wrap(smartcard, SCardFreeMemory, operation->hContext, ret.pbData); if (status != SCARD_S_SUCCESS) return status; @@ -1187,8 +1191,8 @@ static LONG smartcard_GetDeviceTypeId_Call(SMARTCARD_DEVICE* smartcard, GetDeviceTypeId_Call* call = &operation->call.getDeviceTypeId; IRP* irp = operation->irp; - ret.ReturnCode = - SCardGetDeviceTypeIdW(operation->hContext, call->szReaderName, &ret.dwDeviceId); + ret.ReturnCode = wrap(smartcard, SCardGetDeviceTypeIdW, operation->hContext, call->szReaderName, + &ret.dwDeviceId); log_status_error(TAG, "SCardGetDeviceTypeIdW", ret.ReturnCode); free(call->szReaderName); @@ -1222,8 +1226,8 @@ static LONG smartcard_GetStatusChangeA_Call(SMARTCARD_DEVICE* smartcard, LPSCARD_READERSTATEA rgReaderState = NULL; IRP* irp = operation->irp; GetStatusChangeA_Call* call = &operation->call.getStatusChangeA; - ret.ReturnCode = SCardGetStatusChangeA(operation->hContext, call->dwTimeOut, - call->rgReaderStates, call->cReaders); + ret.ReturnCode = wrap(smartcard, SCardGetStatusChangeA, operation->hContext, call->dwTimeOut, + call->rgReaderStates, call->cReaders); log_status_error(TAG, "SCardGetStatusChangeA", ret.ReturnCode); ret.cReaders = call->cReaders; ret.rgReaderStates = NULL; @@ -1286,8 +1290,8 @@ static LONG smartcard_GetStatusChangeW_Call(SMARTCARD_DEVICE* smartcard, LPSCARD_READERSTATEW rgReaderState = NULL; IRP* irp = operation->irp; GetStatusChangeW_Call* call = &operation->call.getStatusChangeW; - ret.ReturnCode = SCardGetStatusChangeW(operation->hContext, call->dwTimeOut, - call->rgReaderStates, call->cReaders); + ret.ReturnCode = wrap(smartcard, SCardGetStatusChangeW, operation->hContext, call->dwTimeOut, + call->rgReaderStates, call->cReaders); log_status_error(TAG, "SCardGetStatusChangeW", ret.ReturnCode); ret.cReaders = call->cReaders; ret.rgReaderStates = NULL; @@ -1346,7 +1350,7 @@ static LONG smartcard_Cancel_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPERATI { Long_Return ret = { 0 }; - ret.ReturnCode = SCardCancel(operation->hContext); + ret.ReturnCode = wrap(smartcard, SCardCancel, operation->hContext); log_status_error(TAG, "SCardCancel", ret.ReturnCode); smartcard_trace_long_return(smartcard, &ret, "Cancel"); return ret.ReturnCode; @@ -1379,9 +1383,9 @@ static LONG smartcard_ConnectA_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPERA call->Common.dwPreferredProtocols = SCARD_PROTOCOL_Tx; } - ret.ReturnCode = - SCardConnectA(operation->hContext, (char*)call->szReader, call->Common.dwShareMode, - call->Common.dwPreferredProtocols, &hCard, &ret.dwActiveProtocol); + ret.ReturnCode = wrap(smartcard, SCardConnectA, operation->hContext, (char*)call->szReader, + call->Common.dwShareMode, call->Common.dwPreferredProtocols, &hCard, + &ret.dwActiveProtocol); smartcard_scard_context_native_to_redir(smartcard, &(ret.hContext), operation->hContext); smartcard_scard_handle_native_to_redir(smartcard, &(ret.hCard), hCard); @@ -1422,9 +1426,9 @@ static LONG smartcard_ConnectW_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPERA call->Common.dwPreferredProtocols = SCARD_PROTOCOL_Tx; } - ret.ReturnCode = - SCardConnectW(operation->hContext, (WCHAR*)call->szReader, call->Common.dwShareMode, - call->Common.dwPreferredProtocols, &hCard, &ret.dwActiveProtocol); + ret.ReturnCode = wrap(smartcard, SCardConnectW, operation->hContext, (WCHAR*)call->szReader, + call->Common.dwShareMode, call->Common.dwPreferredProtocols, &hCard, + &ret.dwActiveProtocol); smartcard_scard_context_native_to_redir(smartcard, &(ret.hContext), operation->hContext); smartcard_scard_handle_native_to_redir(smartcard, &(ret.hCard), hCard); @@ -1457,8 +1461,9 @@ static LONG smartcard_Reconnect_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPER Reconnect_Return ret = { 0 }; IRP* irp = operation->irp; Reconnect_Call* call = &operation->call.reconnect; - ret.ReturnCode = SCardReconnect(operation->hCard, call->dwShareMode, call->dwPreferredProtocols, - call->dwInitialization, &ret.dwActiveProtocol); + ret.ReturnCode = + wrap(smartcard, SCardReconnect, operation->hCard, call->dwShareMode, + call->dwPreferredProtocols, call->dwInitialization, &ret.dwActiveProtocol); log_status_error(TAG, "SCardReconnect", ret.ReturnCode); status = smartcard_pack_reconnect_return(smartcard, irp->output, &ret); if (status != SCARD_S_SUCCESS) @@ -1486,7 +1491,7 @@ static LONG smartcard_Disconnect_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPE Long_Return ret = { 0 }; HCardAndDisposition_Call* call = &operation->call.hCardAndDisposition; - ret.ReturnCode = SCardDisconnect(operation->hCard, call->dwDisposition); + ret.ReturnCode = wrap(smartcard, SCardDisconnect, operation->hCard, call->dwDisposition); log_status_error(TAG, "SCardDisconnect", ret.ReturnCode); smartcard_trace_long_return(smartcard, &ret, "Disconnect"); @@ -1513,7 +1518,7 @@ static LONG smartcard_BeginTransaction_Call(SMARTCARD_DEVICE* smartcard, { Long_Return ret = { 0 }; - ret.ReturnCode = SCardBeginTransaction(operation->hCard); + ret.ReturnCode = wrap(smartcard, SCardBeginTransaction, operation->hCard); log_status_error(TAG, "SCardBeginTransaction", ret.ReturnCode); smartcard_trace_long_return(smartcard, &ret, "BeginTransaction"); return ret.ReturnCode; @@ -1540,7 +1545,7 @@ static LONG smartcard_EndTransaction_Call(SMARTCARD_DEVICE* smartcard, Long_Return ret = { 0 }; HCardAndDisposition_Call* call = &operation->call.hCardAndDisposition; - ret.ReturnCode = SCardEndTransaction(operation->hCard, call->dwDisposition); + ret.ReturnCode = wrap(smartcard, SCardEndTransaction, operation->hCard, call->dwDisposition); log_status_error(TAG, "SCardEndTransaction", ret.ReturnCode); smartcard_trace_long_return(smartcard, &ret, "EndTransaction"); return ret.ReturnCode; @@ -1565,8 +1570,8 @@ static LONG smartcard_State_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPERATIO State_Return ret = { 0 }; IRP* irp = operation->irp; ret.cbAtrLen = SCARD_ATR_LENGTH; - ret.ReturnCode = SCardState(operation->hCard, &ret.dwState, &ret.dwProtocol, (BYTE*)&ret.rgAtr, - &ret.cbAtrLen); + ret.ReturnCode = wrap(smartcard, SCardState, operation->hCard, &ret.dwState, &ret.dwProtocol, + (BYTE*)&ret.rgAtr, &ret.cbAtrLen); log_status_error(TAG, "SCardState", ret.ReturnCode); status = smartcard_pack_state_return(smartcard, irp->output, &ret); @@ -1608,9 +1613,9 @@ static LONG smartcard_StatusA_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPERAT cchReaderLen = SCARD_AUTOALLOCATE; status = ret.ReturnCode = - SCardStatusA(operation->hCard, call->fmszReaderNamesIsNULL ? NULL : (LPSTR)&mszReaderNames, - &cchReaderLen, &ret.dwState, &ret.dwProtocol, - cbAtrLen ? (BYTE*)&ret.pbAtr : NULL, &cbAtrLen); + wrap(smartcard, SCardStatusA, operation->hCard, + call->fmszReaderNamesIsNULL ? NULL : (LPSTR)&mszReaderNames, &cchReaderLen, + &ret.dwState, &ret.dwProtocol, cbAtrLen ? (BYTE*)&ret.pbAtr : NULL, &cbAtrLen); log_status_error(TAG, "SCardStatusA", status); if (status == SCARD_S_SUCCESS) @@ -1627,7 +1632,7 @@ static LONG smartcard_StatusA_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPERAT status = smartcard_pack_status_return(smartcard, irp->output, &ret, FALSE); if (mszReaderNames) - SCardFreeMemory(operation->hContext, mszReaderNames); + wrap(smartcard, SCardFreeMemory, operation->hContext, mszReaderNames); if (status != SCARD_S_SUCCESS) return status; @@ -1668,8 +1673,9 @@ static LONG smartcard_StatusW_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPERAT ret.cBytes = SCARD_AUTOALLOCATE; status = ret.ReturnCode = - SCardStatusW(operation->hCard, call->fmszReaderNamesIsNULL ? NULL : (LPWSTR)&mszReaderNames, - &ret.cBytes, &ret.dwState, &ret.dwProtocol, (BYTE*)&ret.pbAtr, &cbAtrLen); + wrap(smartcard, SCardStatusW, operation->hCard, + call->fmszReaderNamesIsNULL ? NULL : (LPWSTR)&mszReaderNames, &ret.cBytes, + &ret.dwState, &ret.dwProtocol, (BYTE*)&ret.pbAtr, &cbAtrLen); log_status_error(TAG, "SCardStatusW", status); if (status == SCARD_S_SUCCESS) { @@ -1687,7 +1693,7 @@ static LONG smartcard_StatusW_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPERAT return status; if (mszReaderNames) - SCardFreeMemory(operation->hContext, mszReaderNames); + wrap(smartcard, SCardFreeMemory, operation->hContext, mszReaderNames); return ret.ReturnCode; } @@ -1728,8 +1734,8 @@ static LONG smartcard_Transmit_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPERA ret.pioRecvPci = call->pioRecvPci; ret.ReturnCode = - SCardTransmit(operation->hCard, call->pioSendPci, call->pbSendBuffer, call->cbSendLength, - ret.pioRecvPci, ret.pbRecvBuffer, &(ret.cbRecvLength)); + wrap(smartcard, SCardTransmit, operation->hCard, call->pioSendPci, call->pbSendBuffer, + call->cbSendLength, ret.pioRecvPci, ret.pbRecvBuffer, &(ret.cbRecvLength)); log_status_error(TAG, "SCardTransmit", ret.ReturnCode); @@ -1770,8 +1776,8 @@ static LONG smartcard_Control_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPERAT return SCARD_E_NO_MEMORY; ret.ReturnCode = - SCardControl(operation->hCard, call->dwControlCode, call->pvInBuffer, call->cbInBufferSize, - ret.pvOutBuffer, call->cbOutBufferSize, &ret.cbOutBufferSize); + wrap(smartcard, SCardControl, operation->hCard, call->dwControlCode, call->pvInBuffer, + call->cbInBufferSize, ret.pvOutBuffer, call->cbOutBufferSize, &ret.cbOutBufferSize); log_status_error(TAG, "SCardControl", ret.ReturnCode); status = smartcard_pack_control_return(smartcard, irp->output, &ret); @@ -1833,7 +1839,8 @@ static LONG smartcard_GetAttrib_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPER pbAttr = autoAllocate ? (LPBYTE) & (ret.pbAttr) : ret.pbAttr; } - ret.ReturnCode = SCardGetAttrib(operation->hCard, call->dwAttrId, pbAttr, &cbAttrLen); + ret.ReturnCode = + wrap(smartcard, SCardGetAttrib, operation->hCard, call->dwAttrId, pbAttr, &cbAttrLen); log_status_error(TAG, "SCardGetAttrib", ret.ReturnCode); ret.cbAttrLen = cbAttrLen; @@ -1841,7 +1848,7 @@ static LONG smartcard_GetAttrib_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPER call->cbAttrLen); if (autoAllocate) - SCardFreeMemory(operation->hContext, ret.pbAttr); + wrap(smartcard, SCardFreeMemory, operation->hContext, ret.pbAttr); else free(ret.pbAttr); return status; @@ -1852,8 +1859,8 @@ static LONG smartcard_SetAttrib_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPER Long_Return ret = { 0 }; SetAttrib_Call* call = &operation->call.setAttrib; - ret.ReturnCode = - SCardSetAttrib(operation->hCard, call->dwAttrId, call->pbAttr, call->cbAttrLen); + ret.ReturnCode = wrap(smartcard, SCardSetAttrib, operation->hCard, call->dwAttrId, call->pbAttr, + call->cbAttrLen); log_status_error(TAG, "SCardSetAttrib", ret.ReturnCode); free(call->pbAttr); smartcard_trace_long_return(smartcard, &ret, "SetAttrib"); @@ -1890,7 +1897,7 @@ static LONG smartcard_AccessStartedEvent_Call(SMARTCARD_DEVICE* smartcard, WINPR_UNUSED(operation); if (!smartcard->StartedEvent) - smartcard->StartedEvent = SCardAccessStartedEvent(); + smartcard->StartedEvent = wrap(smartcard, SCardAccessStartedEvent); if (!smartcard->StartedEvent) status = SCARD_E_NO_SERVICE; @@ -2065,8 +2072,8 @@ static LONG smartcard_LocateCardsByATRA_Call(SMARTCARD_DEVICE* smartcard, CopyMemory(&(states[i].rgbAtr), &(call->rgReaderStates[i].rgbAtr), 36); } - status = ret.ReturnCode = - SCardGetStatusChangeA(operation->hContext, 0x000001F4, states, call->cReaders); + status = ret.ReturnCode = wrap(smartcard, SCardGetStatusChangeA, operation->hContext, + 0x000001F4, states, call->cReaders); log_status_error(TAG, "SCardGetStatusChangeA", status); for (i = 0; i < call->cAtrs; i++) diff --git a/client/Windows/resource/FreeRDP.ico b/client/Windows/resource/FreeRDP.ico index 0864d1cda..0f35c685e 100644 Binary files a/client/Windows/resource/FreeRDP.ico and b/client/Windows/resource/FreeRDP.ico differ diff --git a/include/freerdp/emulate/scard/smartcard_emulate.h b/include/freerdp/emulate/scard/smartcard_emulate.h new file mode 100644 index 000000000..be1b7b385 --- /dev/null +++ b/include/freerdp/emulate/scard/smartcard_emulate.h @@ -0,0 +1,352 @@ +/** + * WinPR: Windows Portable Runtime + * Smart Card API emulation + * + * Copyright 2021 Armin Novak + * Copyright 2021 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SMARTCARD_EMULATE_PRIVATE_H +#define WINPR_SMARTCARD_EMULATE_PRIVATE_H + +#include +#include + +#include +#include + +typedef struct smartcard_emulation_context SmartcardEmulationContext; + +FREERDP_API SmartcardEmulationContext* Emulate_New(const rdpSettings* settings); +FREERDP_API void Emulate_Free(SmartcardEmulationContext* context); + +FREERDP_API BOOL Emulate_IsConfigured(SmartcardEmulationContext* context); + +FREERDP_API LONG WINAPI Emulate_SCardEstablishContext(SmartcardEmulationContext* smartcard, + DWORD dwScope, LPCVOID pvReserved1, + LPCVOID pvReserved2, + LPSCARDCONTEXT phContext); + +FREERDP_API LONG WINAPI Emulate_SCardReleaseContext(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext); + +FREERDP_API LONG WINAPI Emulate_SCardIsValidContext(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext); + +FREERDP_API LONG WINAPI Emulate_SCardListReaderGroupsA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPSTR mszGroups, + LPDWORD pcchGroups); + +FREERDP_API LONG WINAPI Emulate_SCardListReaderGroupsW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPWSTR mszGroups, + LPDWORD pcchGroups); + +FREERDP_API LONG WINAPI Emulate_SCardListReadersA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR mszGroups, + LPSTR mszReaders, LPDWORD pcchReaders); + +FREERDP_API LONG WINAPI Emulate_SCardListReadersW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR mszGroups, + LPWSTR mszReaders, LPDWORD pcchReaders); + +FREERDP_API LONG WINAPI Emulate_SCardListCardsA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCBYTE pbAtr, + LPCGUID rgquidInterfaces, DWORD cguidInterfaceCount, + CHAR* mszCards, LPDWORD pcchCards); + +FREERDP_API LONG WINAPI Emulate_SCardListCardsW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCBYTE pbAtr, + LPCGUID rgquidInterfaces, DWORD cguidInterfaceCount, + WCHAR* mszCards, LPDWORD pcchCards); + +FREERDP_API LONG WINAPI Emulate_SCardListInterfacesA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szCard, + LPGUID pguidInterfaces, + LPDWORD pcguidInterfaces); + +FREERDP_API LONG WINAPI Emulate_SCardListInterfacesW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szCard, + LPGUID pguidInterfaces, + LPDWORD pcguidInterfaces); + +FREERDP_API LONG WINAPI Emulate_SCardGetProviderIdA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szCard, + LPGUID pguidProviderId); + +FREERDP_API LONG WINAPI Emulate_SCardGetProviderIdW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szCard, + LPGUID pguidProviderId); + +FREERDP_API LONG WINAPI Emulate_SCardGetCardTypeProviderNameA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, + LPCSTR szCardName, DWORD dwProviderId, + CHAR* szProvider, + LPDWORD pcchProvider); + +FREERDP_API LONG WINAPI Emulate_SCardGetCardTypeProviderNameW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, + LPCWSTR szCardName, + DWORD dwProviderId, WCHAR* szProvider, + LPDWORD pcchProvider); + +FREERDP_API LONG WINAPI Emulate_SCardIntroduceReaderGroupA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, + LPCSTR szGroupName); + +FREERDP_API LONG WINAPI Emulate_SCardIntroduceReaderGroupW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, + LPCWSTR szGroupName); + +FREERDP_API LONG WINAPI Emulate_SCardForgetReaderGroupA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szGroupName); + +FREERDP_API LONG WINAPI Emulate_SCardForgetReaderGroupW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szGroupName); + +FREERDP_API LONG WINAPI Emulate_SCardIntroduceReaderA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szReaderName, + LPCSTR szDeviceName); + +FREERDP_API LONG WINAPI Emulate_SCardIntroduceReaderW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPCWSTR szDeviceName); + +FREERDP_API LONG WINAPI Emulate_SCardForgetReaderA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szReaderName); + +FREERDP_API LONG WINAPI Emulate_SCardForgetReaderW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szReaderName); + +FREERDP_API LONG WINAPI Emulate_SCardAddReaderToGroupA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szReaderName, + LPCSTR szGroupName); + +FREERDP_API LONG WINAPI Emulate_SCardAddReaderToGroupW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPCWSTR szGroupName); + +FREERDP_API LONG WINAPI Emulate_SCardRemoveReaderFromGroupA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, + LPCSTR szReaderName, + LPCSTR szGroupName); + +FREERDP_API LONG WINAPI Emulate_SCardRemoveReaderFromGroupW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, + LPCWSTR szReaderName, + LPCWSTR szGroupName); + +FREERDP_API LONG WINAPI Emulate_SCardIntroduceCardTypeA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szCardName, + LPCGUID pguidPrimaryProvider, + LPCGUID rgguidInterfaces, + DWORD dwInterfaceCount, LPCBYTE pbAtr, + LPCBYTE pbAtrMask, DWORD cbAtrLen); + +FREERDP_API LONG WINAPI Emulate_SCardIntroduceCardTypeW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szCardName, + LPCGUID pguidPrimaryProvider, + LPCGUID rgguidInterfaces, + DWORD dwInterfaceCount, LPCBYTE pbAtr, + LPCBYTE pbAtrMask, DWORD cbAtrLen); + +FREERDP_API LONG WINAPI Emulate_SCardSetCardTypeProviderNameA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, + LPCSTR szCardName, DWORD dwProviderId, + LPCSTR szProvider); + +FREERDP_API LONG WINAPI Emulate_SCardSetCardTypeProviderNameW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, + LPCWSTR szCardName, + DWORD dwProviderId, + LPCWSTR szProvider); + +FREERDP_API LONG WINAPI Emulate_SCardForgetCardTypeA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szCardName); + +FREERDP_API LONG WINAPI Emulate_SCardForgetCardTypeW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szCardName); + +FREERDP_API LONG WINAPI Emulate_SCardFreeMemory(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPVOID pvMem); + +FREERDP_API HANDLE WINAPI Emulate_SCardAccessStartedEvent(SmartcardEmulationContext* smartcard); + +FREERDP_API void WINAPI Emulate_SCardReleaseStartedEvent(SmartcardEmulationContext* smartcard); + +FREERDP_API LONG WINAPI Emulate_SCardLocateCardsA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR mszCards, + LPSCARD_READERSTATEA rgReaderStates, + DWORD cReaders); + +FREERDP_API LONG WINAPI Emulate_SCardLocateCardsW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR mszCards, + LPSCARD_READERSTATEW rgReaderStates, + DWORD cReaders); + +FREERDP_API LONG WINAPI Emulate_SCardLocateCardsByATRA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, + LPSCARD_ATRMASK rgAtrMasks, DWORD cAtrs, + LPSCARD_READERSTATEA rgReaderStates, + DWORD cReaders); + +FREERDP_API LONG WINAPI Emulate_SCardLocateCardsByATRW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, + LPSCARD_ATRMASK rgAtrMasks, DWORD cAtrs, + LPSCARD_READERSTATEW rgReaderStates, + DWORD cReaders); + +FREERDP_API LONG WINAPI Emulate_SCardGetStatusChangeA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, DWORD dwTimeout, + LPSCARD_READERSTATEA rgReaderStates, + DWORD cReaders); + +FREERDP_API LONG WINAPI Emulate_SCardGetStatusChangeW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, DWORD dwTimeout, + LPSCARD_READERSTATEW rgReaderStates, + DWORD cReaders); + +FREERDP_API LONG WINAPI Emulate_SCardCancel(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext); + +FREERDP_API LONG WINAPI Emulate_SCardConnectA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szReader, + DWORD dwShareMode, DWORD dwPreferredProtocols, + LPSCARDHANDLE phCard, LPDWORD pdwActiveProtocol); + +FREERDP_API LONG WINAPI Emulate_SCardConnectW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szReader, + DWORD dwShareMode, DWORD dwPreferredProtocols, + LPSCARDHANDLE phCard, LPDWORD pdwActiveProtocol); + +FREERDP_API LONG WINAPI Emulate_SCardReconnect(SmartcardEmulationContext* smartcard, + SCARDHANDLE hCard, DWORD dwShareMode, + DWORD dwPreferredProtocols, DWORD dwInitialization, + LPDWORD pdwActiveProtocol); + +FREERDP_API LONG WINAPI Emulate_SCardDisconnect(SmartcardEmulationContext* smartcard, + SCARDHANDLE hCard, DWORD dwDisposition); + +FREERDP_API LONG WINAPI Emulate_SCardBeginTransaction(SmartcardEmulationContext* smartcard, + SCARDHANDLE hCard); + +FREERDP_API LONG WINAPI Emulate_SCardEndTransaction(SmartcardEmulationContext* smartcard, + SCARDHANDLE hCard, DWORD dwDisposition); + +FREERDP_API LONG WINAPI Emulate_SCardCancelTransaction(SmartcardEmulationContext* smartcard, + SCARDHANDLE hCard); + +FREERDP_API LONG WINAPI Emulate_SCardState(SmartcardEmulationContext* smartcard, SCARDHANDLE hCard, + LPDWORD pdwState, LPDWORD pdwProtocol, LPBYTE pbAtr, + LPDWORD pcbAtrLen); + +FREERDP_API LONG WINAPI Emulate_SCardStatusA(SmartcardEmulationContext* smartcard, + SCARDHANDLE hCard, LPSTR mszReaderNames, + LPDWORD pcchReaderLen, LPDWORD pdwState, + LPDWORD pdwProtocol, LPBYTE pbAtr, LPDWORD pcbAtrLen); + +FREERDP_API LONG WINAPI Emulate_SCardStatusW(SmartcardEmulationContext* smartcard, + SCARDHANDLE hCard, LPWSTR mszReaderNames, + LPDWORD pcchReaderLen, LPDWORD pdwState, + LPDWORD pdwProtocol, LPBYTE pbAtr, LPDWORD pcbAtrLen); + +FREERDP_API LONG WINAPI Emulate_SCardTransmit(SmartcardEmulationContext* smartcard, + SCARDHANDLE hCard, LPCSCARD_IO_REQUEST pioSendPci, + LPCBYTE pbSendBuffer, DWORD cbSendLength, + LPSCARD_IO_REQUEST pioRecvPci, LPBYTE pbRecvBuffer, + LPDWORD pcbRecvLength); + +FREERDP_API LONG WINAPI Emulate_SCardGetTransmitCount(SmartcardEmulationContext* smartcard, + SCARDHANDLE hCard, LPDWORD pcTransmitCount); + +FREERDP_API LONG WINAPI Emulate_SCardControl(SmartcardEmulationContext* smartcard, + SCARDHANDLE hCard, DWORD dwControlCode, + LPCVOID lpInBuffer, DWORD cbInBufferSize, + LPVOID lpOutBuffer, DWORD cbOutBufferSize, + LPDWORD lpBytesReturned); + +FREERDP_API LONG WINAPI Emulate_SCardGetAttrib(SmartcardEmulationContext* smartcard, + SCARDHANDLE hCard, DWORD dwAttrId, LPBYTE pbAttr, + LPDWORD pcbAttrLen); + +FREERDP_API LONG WINAPI Emulate_SCardSetAttrib(SmartcardEmulationContext* smartcard, + SCARDHANDLE hCard, DWORD dwAttrId, LPCBYTE pbAttr, + DWORD cbAttrLen); + +FREERDP_API LONG WINAPI Emulate_SCardUIDlgSelectCardA(SmartcardEmulationContext* smartcard, + LPOPENCARDNAMEA_EX pDlgStruc); + +FREERDP_API LONG WINAPI Emulate_SCardUIDlgSelectCardW(SmartcardEmulationContext* smartcard, + LPOPENCARDNAMEW_EX pDlgStruc); + +FREERDP_API LONG WINAPI Emulate_GetOpenCardNameA(SmartcardEmulationContext* smartcard, + LPOPENCARDNAMEA pDlgStruc); + +FREERDP_API LONG WINAPI Emulate_GetOpenCardNameW(SmartcardEmulationContext* smartcard, + LPOPENCARDNAMEW pDlgStruc); + +FREERDP_API LONG WINAPI Emulate_SCardDlgExtendedError(SmartcardEmulationContext* smartcard); + +FREERDP_API LONG WINAPI Emulate_SCardReadCacheA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPSTR LookupName, + PBYTE Data, DWORD* DataLen); + +FREERDP_API LONG WINAPI Emulate_SCardReadCacheW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPWSTR LookupName, + PBYTE Data, DWORD* DataLen); + +FREERDP_API LONG WINAPI Emulate_SCardWriteCacheA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPSTR LookupName, + PBYTE Data, DWORD DataLen); + +FREERDP_API LONG WINAPI Emulate_SCardWriteCacheW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPWSTR LookupName, + PBYTE Data, DWORD DataLen); + +FREERDP_API LONG WINAPI Emulate_SCardGetReaderIconA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szReaderName, + LPBYTE pbIcon, LPDWORD pcbIcon); + +FREERDP_API LONG WINAPI Emulate_SCardGetReaderIconW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPBYTE pbIcon, LPDWORD pcbIcon); + +FREERDP_API LONG WINAPI Emulate_SCardGetDeviceTypeIdA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szReaderName, + LPDWORD pdwDeviceTypeId); +FREERDP_API LONG WINAPI Emulate_SCardGetDeviceTypeIdW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPDWORD pdwDeviceTypeId); + +FREERDP_API LONG WINAPI Emulate_SCardGetReaderDeviceInstanceIdA( + SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, LPCSTR szReaderName, + LPSTR szDeviceInstanceId, LPDWORD pcchDeviceInstanceId); + +FREERDP_API LONG WINAPI Emulate_SCardGetReaderDeviceInstanceIdW( + SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPWSTR szDeviceInstanceId, LPDWORD pcchDeviceInstanceId); +FREERDP_API LONG WINAPI Emulate_SCardListReadersWithDeviceInstanceIdA( + SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, LPCSTR szDeviceInstanceId, + LPSTR mszReaders, LPDWORD pcchReaders); +FREERDP_API LONG WINAPI Emulate_SCardListReadersWithDeviceInstanceIdW( + SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, LPCWSTR szDeviceInstanceId, + LPWSTR mszReaders, LPDWORD pcchReaders); +FREERDP_API LONG WINAPI Emulate_SCardAudit(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, DWORD dwEvent); + +#endif /* WINPR_SMARTCARD_EMULATE_PRIVATE_H */ diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index ee2a0679d..cc555b16f 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -674,6 +674,8 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL; #define FreeRDP_Password51Length (1281) #define FreeRDP_SmartcardLogon (1282) #define FreeRDP_PromptForCredentials (1283) +#define FreeRDP_SmartcardCertificate (1285) +#define FreeRDP_SmartcardPrivateKey (1286) #define FreeRDP_KerberosKdc (1344) #define FreeRDP_KerberosRealm (1345) #define FreeRDP_IgnoreCertificate (1408) @@ -1153,7 +1155,12 @@ struct rdp_settings ALIGN64 UINT32 Password51Length; /* 1281 */ ALIGN64 BOOL SmartcardLogon; /* 1282 */ ALIGN64 BOOL PromptForCredentials; /* 1283 */ - UINT64 padding1344[1344 - 1284]; /* 1284 */ + + /* Settings used for smartcard emulation */ + UINT64 padding1284[1285 - 1284]; /* 1284 */ + ALIGN64 char* SmartcardCertificate; /* 1285 */ + ALIGN64 char* SmartcardPrivateKey; /* 1286 */ + UINT64 padding1344[1344 - 1287]; /* 1287 */ /* Kerberos Authentication */ ALIGN64 char* KerberosKdc; /* 1344 */ diff --git a/libfreerdp/CMakeLists.txt b/libfreerdp/CMakeLists.txt index 7ce5dbf66..a940cd344 100644 --- a/libfreerdp/CMakeLists.txt +++ b/libfreerdp/CMakeLists.txt @@ -103,6 +103,7 @@ else() endif() set(${MODULE_PREFIX}_SUBMODULES + emu utils common gdi diff --git a/libfreerdp/common/settings_getters.c b/libfreerdp/common/settings_getters.c index 2e7d6cab6..061131813 100644 --- a/libfreerdp/common/settings_getters.c +++ b/libfreerdp/common/settings_getters.c @@ -2500,6 +2500,12 @@ const char* freerdp_settings_get_string(const rdpSettings* settings, size_t id) case FreeRDP_ShellWorkingDirectory: return settings->ShellWorkingDirectory; + case FreeRDP_SmartcardCertificate: + return settings->SmartcardCertificate; + + case FreeRDP_SmartcardPrivateKey: + return settings->SmartcardPrivateKey; + case FreeRDP_TargetNetAddress: return settings->TargetNetAddress; @@ -2729,6 +2735,12 @@ BOOL freerdp_settings_set_string_(rdpSettings* settings, size_t id, const char* case FreeRDP_ShellWorkingDirectory: return update_string(&settings->ShellWorkingDirectory, cnv.cc, len, cleanup); + case FreeRDP_SmartcardCertificate: + return update_string(&settings->SmartcardCertificate, cnv.cc, len, cleanup); + + case FreeRDP_SmartcardPrivateKey: + return update_string(&settings->SmartcardPrivateKey, cnv.cc, len, cleanup); + case FreeRDP_TargetNetAddress: return update_string(&settings->TargetNetAddress, cnv.cc, len, cleanup); diff --git a/libfreerdp/common/settings_str.c b/libfreerdp/common/settings_str.c index 3a05c7b3e..528900a75 100644 --- a/libfreerdp/common/settings_str.c +++ b/libfreerdp/common/settings_str.c @@ -369,6 +369,8 @@ static const struct settings_str_entry settings_map[] = { { FreeRDP_RemoteAssistanceSessionId, 7, "FreeRDP_RemoteAssistanceSessionId" }, { FreeRDP_ServerHostname, 7, "FreeRDP_ServerHostname" }, { FreeRDP_ShellWorkingDirectory, 7, "FreeRDP_ShellWorkingDirectory" }, + { FreeRDP_SmartcardCertificate, 7, "FreeRDP_SmartcardCertificate" }, + { FreeRDP_SmartcardPrivateKey, 7, "FreeRDP_SmartcardPrivateKey" }, { FreeRDP_TargetNetAddress, 7, "FreeRDP_TargetNetAddress" }, { FreeRDP_TransportDumpFile, 7, "FreeRDP_TransportDumpFile" }, { FreeRDP_Username, 7, "FreeRDP_Username" }, diff --git a/libfreerdp/core/test/settings_property_lists.h b/libfreerdp/core/test/settings_property_lists.h index f13099ec2..5e0d7a1c9 100644 --- a/libfreerdp/core/test/settings_property_lists.h +++ b/libfreerdp/core/test/settings_property_lists.h @@ -378,6 +378,8 @@ static const size_t string_list_indices[] = { FreeRDP_RemoteAssistanceSessionId, FreeRDP_ServerHostname, FreeRDP_ShellWorkingDirectory, + FreeRDP_SmartcardCertificate, + FreeRDP_SmartcardPrivateKey, FreeRDP_TargetNetAddress, FreeRDP_TransportDumpFile, FreeRDP_Username, diff --git a/libfreerdp/emu/CMakeLists.txt b/libfreerdp/emu/CMakeLists.txt new file mode 100644 index 000000000..290cd13a2 --- /dev/null +++ b/libfreerdp/emu/CMakeLists.txt @@ -0,0 +1,11 @@ +if (WITH_SMARTCARD_EMULATE) + list(APPEND EMULATE_SRCS + scard/smartcard_emulate.c + scard/FreeRDP.ico.h + scard/FreeRDP.ico.c + scard/smartcard_virtual_gids.h + scard/smartcard_virtual_gids.c + ) + freerdp_module_add(${EMULATE_SRCS}) + freerdp_library_add(ZLIB::ZLIB) +endif() diff --git a/libfreerdp/emu/scard/FreeRDP.ico.c b/libfreerdp/emu/scard/FreeRDP.ico.c new file mode 100644 index 000000000..8f324fbe3 --- /dev/null +++ b/libfreerdp/emu/scard/FreeRDP.ico.c @@ -0,0 +1,456 @@ +unsigned char resources_FreeRDP_ico[] = { + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x32, 0x1c, + 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, + 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x5c, 0x72, 0xa8, 0x66, 0x00, 0x00, 0x1b, 0xf9, 0x49, 0x44, 0x41, 0x54, 0x78, + 0xda, 0xed, 0xdd, 0x6f, 0x6c, 0x14, 0xe7, 0x9d, 0x07, 0xf0, 0x6f, 0x0c, 0x78, 0x6d, 0xcc, 0xe2, + 0x01, 0x83, 0x63, 0x5b, 0x38, 0x1e, 0x9a, 0x60, 0x72, 0x05, 0xea, 0x05, 0x5a, 0xe4, 0x52, 0x09, + 0x96, 0x9e, 0x9a, 0xca, 0x70, 0x4d, 0x8d, 0x7a, 0x49, 0xa9, 0x4e, 0xbe, 0x18, 0x89, 0xbc, 0x48, + 0x93, 0x55, 0xa0, 0xd0, 0xaa, 0x2f, 0x92, 0x90, 0xa4, 0x48, 0x77, 0xad, 0x40, 0xa4, 0x72, 0x68, + 0xa4, 0x96, 0xca, 0xe4, 0x56, 0xba, 0xd0, 0x5c, 0x2b, 0x53, 0x52, 0xb3, 0x4a, 0x4f, 0xed, 0x1a, + 0x4b, 0xc0, 0x8a, 0x62, 0xbc, 0xe6, 0x8f, 0xe2, 0x3f, 0xd1, 0x79, 0x89, 0x25, 0x6c, 0x99, 0x58, + 0x59, 0x77, 0x21, 0xb1, 0x9d, 0x2a, 0xbe, 0x17, 0x3b, 0x6b, 0x6c, 0xb3, 0x3b, 0x3b, 0x3b, 0x3b, + 0x33, 0xbf, 0x79, 0x66, 0x7e, 0x9f, 0x57, 0x80, 0xbd, 0xeb, 0xdf, 0x2c, 0x7e, 0xbe, 0xf3, 0x3c, + 0xcf, 0x3c, 0xf3, 0xcc, 0x43, 0x60, 0xae, 0xb4, 0xf3, 0x58, 0x50, 0x06, 0xe0, 0x07, 0x20, 0x03, + 0xa8, 0x03, 0x20, 0xcd, 0xfa, 0x72, 0x1c, 0x40, 0x0f, 0x80, 0x8e, 0x73, 0x07, 0x9b, 0x3a, 0xa8, + 0x6b, 0x65, 0xe6, 0x79, 0x88, 0xba, 0x00, 0x66, 0x1d, 0xa5, 0xd1, 0x37, 0x03, 0x78, 0x06, 0xc9, + 0x86, 0xaf, 0x45, 0x0c, 0xc0, 0x2f, 0xcf, 0x1d, 0x6c, 0x7a, 0x83, 0xba, 0x7e, 0x66, 0x3c, 0x0e, + 0x00, 0x17, 0x50, 0x1a, 0xfe, 0x61, 0x24, 0x1b, 0xbf, 0x5e, 0x67, 0xce, 0x1d, 0x6c, 0xda, 0x4d, + 0x7d, 0x2c, 0xcc, 0x58, 0x1c, 0x00, 0x0e, 0x66, 0x50, 0xc3, 0x9f, 0xed, 0xd4, 0xb9, 0x83, 0x4d, + 0x7b, 0xa9, 0x8f, 0x8b, 0x19, 0x87, 0x03, 0xc0, 0xa1, 0x76, 0x1e, 0x0b, 0xbe, 0x0a, 0xe0, 0x45, + 0xcc, 0x1d, 0xdb, 0x1b, 0x61, 0x07, 0xcf, 0x0b, 0x38, 0xc7, 0x42, 0xea, 0x02, 0x98, 0xb1, 0x76, + 0x1e, 0x0b, 0xfa, 0x00, 0xb4, 0x02, 0xf0, 0x99, 0xf4, 0x23, 0x5e, 0x04, 0xd0, 0x41, 0x7d, 0x9c, + 0xcc, 0x18, 0x05, 0xd4, 0x05, 0x30, 0xe3, 0x28, 0x67, 0xfd, 0x6e, 0x98, 0xd7, 0xf8, 0x81, 0xe4, + 0x95, 0x03, 0xe6, 0x10, 0x3c, 0x04, 0x70, 0x80, 0x9d, 0xc7, 0x82, 0x12, 0x80, 0x36, 0x58, 0xd7, + 0x38, 0x37, 0x9e, 0x3b, 0xd8, 0x14, 0xa5, 0x3e, 0x6e, 0x96, 0x3f, 0x1e, 0x02, 0x08, 0x4e, 0xe9, + 0xf2, 0x87, 0x61, 0xfc, 0x58, 0x5f, 0x8d, 0x95, 0x3f, 0x8b, 0x99, 0x88, 0x87, 0x00, 0x02, 0xdb, + 0x79, 0x2c, 0xd8, 0x8c, 0x64, 0x97, 0x5f, 0xa2, 0xae, 0x85, 0x89, 0x89, 0x03, 0x40, 0x50, 0x3b, + 0x8f, 0x05, 0x8f, 0x23, 0x39, 0xd9, 0xc7, 0x98, 0x6e, 0x3c, 0x04, 0x10, 0xd0, 0xce, 0x63, 0xc1, + 0x56, 0x18, 0x77, 0x6d, 0x9f, 0xb9, 0x18, 0x07, 0x80, 0x40, 0x94, 0xc9, 0xbe, 0x30, 0xcc, 0x9d, + 0xe5, 0x67, 0x2e, 0xc2, 0x43, 0x00, 0x41, 0xd8, 0xac, 0xf1, 0xc7, 0xa8, 0x0b, 0x60, 0xc6, 0xe0, + 0xcb, 0x80, 0x02, 0xb0, 0x59, 0xe3, 0xc7, 0xb9, 0x83, 0x4d, 0xfc, 0x7b, 0xe3, 0x10, 0xdc, 0x03, + 0xb0, 0x39, 0xbb, 0x35, 0x7e, 0xf0, 0x2a, 0x40, 0x47, 0xe1, 0x00, 0xb0, 0x31, 0x1b, 0x36, 0x7e, + 0x00, 0x88, 0x52, 0x17, 0xc0, 0x8c, 0xc3, 0x01, 0x60, 0x53, 0x36, 0x6d, 0xfc, 0x40, 0x72, 0xa3, + 0x10, 0xe6, 0x10, 0x1c, 0x00, 0x36, 0x64, 0xe3, 0xc6, 0x0f, 0xf0, 0x10, 0xc0, 0x51, 0x38, 0x00, + 0x6c, 0xc6, 0xe6, 0x8d, 0x3f, 0x7a, 0xee, 0x60, 0x53, 0x8c, 0xba, 0x08, 0x66, 0x1c, 0x0e, 0x00, + 0x1b, 0xb1, 0x79, 0xe3, 0x07, 0xf8, 0xec, 0xef, 0x38, 0x1c, 0x00, 0xf6, 0x62, 0xe6, 0x7d, 0xfc, + 0x46, 0x78, 0x9b, 0xba, 0x00, 0x66, 0x2c, 0x0e, 0x00, 0x9b, 0x50, 0x96, 0xf7, 0x36, 0x52, 0xd7, + 0xa1, 0x22, 0xc6, 0xb7, 0x00, 0x3b, 0x0f, 0x07, 0x80, 0x0d, 0x28, 0x37, 0xf6, 0x34, 0x53, 0xd7, + 0x91, 0xc5, 0x19, 0xea, 0x02, 0x98, 0xf1, 0x38, 0x00, 0x88, 0x29, 0xb7, 0xf4, 0xee, 0xa7, 0xae, + 0x43, 0x03, 0xee, 0xfe, 0x3b, 0x10, 0x2f, 0xe9, 0x24, 0xb4, 0xf3, 0x58, 0xb0, 0x11, 0xc9, 0x9d, + 0x7c, 0xec, 0x2e, 0x76, 0xee, 0x60, 0xd3, 0x6a, 0xea, 0x22, 0x98, 0xf1, 0xb8, 0x07, 0x40, 0x64, + 0xd6, 0xe6, 0x9d, 0x22, 0xf8, 0x25, 0x75, 0x01, 0xcc, 0x1c, 0x1c, 0x00, 0x04, 0x66, 0xed, 0xe1, + 0x27, 0x51, 0xd7, 0xa2, 0xd1, 0x29, 0xea, 0x02, 0x98, 0x39, 0x38, 0x00, 0x68, 0x84, 0xa1, 0xfd, + 0xd1, 0x5c, 0xd4, 0x4e, 0x9d, 0x3b, 0xd8, 0x14, 0xa7, 0x2e, 0x82, 0x99, 0x83, 0x03, 0xc0, 0x62, + 0xca, 0xe5, 0x3e, 0x1f, 0x75, 0x1d, 0x39, 0xe0, 0xc9, 0x3f, 0x07, 0xe3, 0x49, 0x40, 0x0b, 0xed, + 0x3c, 0x16, 0xdc, 0x0f, 0xe0, 0x38, 0x75, 0x1d, 0x39, 0xe0, 0xc9, 0x3f, 0x87, 0xe3, 0x1e, 0x80, + 0x45, 0x76, 0x1e, 0x0b, 0xfa, 0x21, 0x56, 0xe3, 0x07, 0x80, 0xd7, 0xa8, 0x0b, 0x60, 0xe6, 0xe2, + 0x00, 0xb0, 0x80, 0xf2, 0x90, 0x4e, 0x11, 0x2e, 0xf7, 0xcd, 0x16, 0x07, 0x2f, 0xfe, 0x71, 0x3c, + 0x0e, 0x00, 0x6b, 0x88, 0x34, 0xe3, 0x9f, 0xf2, 0x4b, 0x9e, 0xfc, 0x73, 0x3e, 0x0e, 0x00, 0x93, + 0x09, 0x38, 0xe9, 0x97, 0xf2, 0x06, 0x75, 0x01, 0xcc, 0x7c, 0x1c, 0x00, 0x26, 0x52, 0x96, 0xf9, + 0x36, 0x53, 0xd7, 0xa1, 0x03, 0x5f, 0xfa, 0x73, 0x09, 0x0e, 0x00, 0x93, 0x28, 0x2b, 0xfd, 0x44, + 0x9b, 0xf4, 0x4b, 0xe1, 0x95, 0x7f, 0x2e, 0xc1, 0x01, 0x60, 0x02, 0x65, 0xa5, 0x5f, 0x2b, 0xc4, + 0x1b, 0xf7, 0x03, 0xc9, 0x5d, 0x7f, 0xa2, 0xd4, 0x45, 0x30, 0x6b, 0x70, 0x00, 0x98, 0xe3, 0x38, + 0xc4, 0x1c, 0xf7, 0x03, 0x7c, 0xf6, 0x77, 0x15, 0x0e, 0x00, 0x83, 0x09, 0x3c, 0xee, 0x4f, 0x39, + 0x43, 0x5d, 0x00, 0xb3, 0x0e, 0x07, 0x80, 0x81, 0x94, 0xeb, 0xfd, 0xa2, 0x8e, 0xfb, 0x01, 0xe0, + 0x0c, 0x4f, 0xfe, 0xb9, 0x0b, 0x07, 0x80, 0xb1, 0x44, 0xbc, 0xde, 0x3f, 0x1b, 0xaf, 0xfb, 0x77, + 0x19, 0x0e, 0x00, 0x83, 0x28, 0xdb, 0x7a, 0xf9, 0xa8, 0xeb, 0xc8, 0x53, 0x07, 0x75, 0x01, 0xcc, + 0x5a, 0x1c, 0x00, 0x06, 0x50, 0xd6, 0xf9, 0xef, 0xa7, 0xae, 0x23, 0x4f, 0xdc, 0xfd, 0x77, 0x21, + 0x0e, 0x80, 0x3c, 0xcd, 0xda, 0xdc, 0x43, 0x74, 0x7f, 0xa4, 0x2e, 0x80, 0x59, 0x8f, 0x03, 0x20, + 0x7f, 0xa2, 0x5e, 0xef, 0x9f, 0xaf, 0x83, 0xba, 0x00, 0x66, 0x3d, 0x0e, 0x80, 0x3c, 0x28, 0x97, + 0xfc, 0x1a, 0xa9, 0xeb, 0x30, 0x00, 0x3f, 0xf2, 0xcb, 0xa5, 0x38, 0x00, 0x74, 0x72, 0xc0, 0x25, + 0xbf, 0xd9, 0x3a, 0xa8, 0x0b, 0x60, 0x34, 0x38, 0x00, 0xf4, 0x73, 0x4a, 0xd7, 0x1f, 0x00, 0xce, + 0x53, 0x17, 0xc0, 0x68, 0x70, 0x00, 0xe8, 0xa0, 0x6c, 0xed, 0xe5, 0xa7, 0xae, 0xc3, 0x40, 0x1d, + 0xd4, 0x05, 0x30, 0x1a, 0xbc, 0x27, 0x60, 0x8e, 0x94, 0xae, 0x7f, 0x37, 0x9c, 0x73, 0xf6, 0x8f, + 0x9e, 0x3b, 0xd8, 0xb4, 0x91, 0xba, 0x08, 0x46, 0x83, 0x7b, 0x00, 0xb9, 0x73, 0x52, 0xd7, 0x1f, + 0xe0, 0xb3, 0xbf, 0xab, 0x71, 0x00, 0xe4, 0xc0, 0x81, 0x5d, 0x7f, 0x80, 0xc7, 0xff, 0xae, 0xc6, + 0x01, 0xa0, 0x91, 0xd2, 0xf5, 0x3f, 0x4c, 0x5d, 0x87, 0x09, 0xa2, 0xd4, 0x05, 0x30, 0x3a, 0x1c, + 0x00, 0xda, 0x39, 0xad, 0xeb, 0x0f, 0x24, 0xf7, 0xfd, 0x8f, 0x51, 0x17, 0xc1, 0xe8, 0x70, 0x00, + 0x68, 0xa0, 0x2c, 0xf8, 0xf1, 0x53, 0xd7, 0x61, 0x82, 0x28, 0x75, 0x01, 0x8c, 0x16, 0x07, 0x40, + 0x16, 0xca, 0x5a, 0x7f, 0xa7, 0x2c, 0xf8, 0x99, 0xaf, 0x87, 0xba, 0x00, 0x46, 0x8b, 0x03, 0x20, + 0x3b, 0x27, 0x76, 0xfd, 0x53, 0x3a, 0xa8, 0x0b, 0x60, 0xb4, 0x38, 0x00, 0x54, 0x28, 0xb7, 0xf9, + 0x36, 0x52, 0xd7, 0x61, 0xa2, 0x28, 0x75, 0x01, 0x8c, 0x16, 0x2f, 0x04, 0xca, 0x40, 0xe9, 0xfa, + 0x77, 0x43, 0x9c, 0xc7, 0x78, 0xe7, 0x2a, 0x7e, 0xee, 0x60, 0xd3, 0x32, 0xea, 0x22, 0x18, 0x2d, + 0xee, 0x01, 0x64, 0xb6, 0x1f, 0xce, 0x6d, 0xfc, 0x00, 0x9f, 0xfd, 0x19, 0x38, 0x00, 0xd2, 0x52, + 0x1e, 0xea, 0xe1, 0xc4, 0x6b, 0xfe, 0xb3, 0x45, 0xa9, 0x0b, 0x60, 0xf4, 0x38, 0x00, 0xd2, 0x73, + 0xea, 0xac, 0xff, 0x6c, 0xb7, 0xa8, 0x0b, 0x60, 0xf4, 0x38, 0x00, 0xe6, 0x71, 0xf0, 0x35, 0xff, + 0xf9, 0xa2, 0xd4, 0x05, 0x30, 0x7a, 0x1c, 0x00, 0xb3, 0x38, 0xfc, 0x9a, 0xff, 0x7c, 0x51, 0xea, + 0x02, 0x18, 0xbd, 0x85, 0xd4, 0x05, 0xd8, 0xcc, 0x61, 0x38, 0xf7, 0x9a, 0xff, 0x1c, 0x8f, 0x7e, + 0x79, 0x4d, 0x5b, 0x20, 0x14, 0x49, 0xfd, 0xf5, 0x3c, 0x80, 0x37, 0x5a, 0x1a, 0xea, 0xe3, 0xd4, + 0x75, 0x31, 0x6b, 0xf1, 0x65, 0x40, 0x85, 0x32, 0xf1, 0xd7, 0x4d, 0x5d, 0x87, 0x15, 0x8a, 0x4b, + 0x8a, 0x51, 0x55, 0xb3, 0x6a, 0xfe, 0x3f, 0xc7, 0x01, 0x1c, 0x68, 0x69, 0xa8, 0x3f, 0x45, 0x5d, + 0x1f, 0xb3, 0x0e, 0x0f, 0x01, 0xee, 0x73, 0x4b, 0xd7, 0x1f, 0x05, 0x05, 0x0b, 0xd2, 0xfd, 0xb3, + 0x04, 0xa0, 0x35, 0x10, 0x8a, 0x34, 0x52, 0xd7, 0xc7, 0xac, 0xc3, 0x01, 0x00, 0x60, 0xe7, 0xb1, + 0x60, 0x23, 0xdc, 0x31, 0xf1, 0x07, 0x00, 0xf0, 0x14, 0x79, 0xd4, 0xbe, 0xdc, 0x1a, 0x08, 0x45, + 0x24, 0xea, 0x1a, 0x99, 0x35, 0x38, 0x00, 0x92, 0x5c, 0x73, 0xf6, 0x07, 0x80, 0x82, 0x05, 0xaa, + 0xff, 0xed, 0x12, 0xc4, 0x7f, 0xca, 0x11, 0xd3, 0xc8, 0xf5, 0x01, 0xb0, 0xf3, 0x58, 0xf0, 0x55, + 0x38, 0x7b, 0xc5, 0xdf, 0x03, 0x0a, 0xd5, 0x7b, 0x00, 0x00, 0xf0, 0x62, 0x20, 0x14, 0x91, 0xa9, + 0xeb, 0x64, 0xe6, 0x73, 0x75, 0x00, 0x28, 0x97, 0xfd, 0x5e, 0xa4, 0xae, 0xc3, 0x6a, 0x05, 0x05, + 0x59, 0xff, 0xdb, 0x25, 0x38, 0x7f, 0x25, 0x24, 0x83, 0xcb, 0x03, 0x00, 0xc9, 0xae, 0xbf, 0x44, + 0x5d, 0x84, 0xd5, 0x3c, 0xd9, 0x7b, 0x00, 0x00, 0xd0, 0xcc, 0xbd, 0x00, 0xe7, 0x73, 0x6d, 0x00, + 0x28, 0x7b, 0xfc, 0x35, 0x53, 0xd7, 0x61, 0x73, 0xad, 0xd4, 0x05, 0x30, 0x73, 0xb9, 0x36, 0x00, + 0xe0, 0xd2, 0x5f, 0x6e, 0x8d, 0x67, 0xff, 0x14, 0x7f, 0x20, 0x14, 0xf1, 0x53, 0xd7, 0xcc, 0xcc, + 0xe3, 0xca, 0x00, 0x50, 0x36, 0xfa, 0xf0, 0x53, 0xd7, 0x41, 0x21, 0xcb, 0x15, 0x80, 0x74, 0x5c, + 0x19, 0x94, 0x6e, 0xe1, 0xca, 0x00, 0x80, 0x8b, 0x27, 0xb8, 0x32, 0x2c, 0x02, 0x52, 0x23, 0x07, + 0x42, 0x91, 0x57, 0xa9, 0xeb, 0x66, 0xe6, 0x70, 0x5d, 0x00, 0xb8, 0x6d, 0xd1, 0xcf, 0x83, 0xa6, + 0xf5, 0xbc, 0xe8, 0x45, 0x5e, 0x1c, 0xe4, 0x4c, 0xae, 0x0b, 0x00, 0xb8, 0x6c, 0xd1, 0xcf, 0x7c, + 0xd7, 0x23, 0x5d, 0x48, 0xc4, 0xc7, 0x73, 0x7d, 0x99, 0x04, 0x97, 0x7f, 0x6e, 0x4e, 0xe5, 0xaa, + 0x00, 0x50, 0xee, 0xf5, 0x97, 0xa9, 0xeb, 0xa0, 0xf4, 0x8f, 0xa9, 0xcf, 0xd1, 0xd1, 0x16, 0xd2, + 0xf3, 0xd2, 0x66, 0x9e, 0x10, 0x74, 0x1e, 0x57, 0x05, 0x00, 0x5c, 0x3c, 0xf6, 0x4f, 0x49, 0xc4, + 0xc7, 0x71, 0x3b, 0x36, 0x84, 0xeb, 0x91, 0x2e, 0x3d, 0x2f, 0xe7, 0x5e, 0x80, 0xc3, 0xb8, 0x26, + 0x00, 0xf8, 0xec, 0x3f, 0xd7, 0x95, 0xf0, 0x05, 0x3d, 0x43, 0x01, 0x5f, 0x20, 0x14, 0xd9, 0x4f, + 0x5d, 0x3b, 0x33, 0x8e, 0x6b, 0x02, 0x00, 0x7c, 0xf6, 0x9f, 0x63, 0x6a, 0x62, 0x52, 0xef, 0x50, + 0xe0, 0x30, 0x4f, 0x08, 0x3a, 0x87, 0x2b, 0x02, 0x80, 0xcf, 0xfe, 0xf7, 0x25, 0x3e, 0xb9, 0x7f, + 0xd6, 0xbf, 0x1d, 0x1b, 0x42, 0xac, 0x77, 0x20, 0xd7, 0xb7, 0x90, 0xc0, 0x43, 0x01, 0xc7, 0x70, + 0x45, 0x00, 0x80, 0xcf, 0xfe, 0x33, 0xfe, 0xf1, 0xf9, 0xe7, 0x73, 0xfe, 0x1e, 0x6e, 0x0b, 0x61, + 0x6a, 0x62, 0x32, 0xd7, 0xb7, 0xe1, 0x09, 0x41, 0x87, 0x70, 0x7c, 0x00, 0xf0, 0xd9, 0x5f, 0xdd, + 0xd4, 0xc4, 0x24, 0x2e, 0x86, 0xfe, 0xaa, 0xe7, 0xa5, 0xbc, 0x42, 0xd0, 0x01, 0x1c, 0x1f, 0x00, + 0xe0, 0xb3, 0x7f, 0x56, 0x7d, 0xd1, 0x1b, 0xb8, 0x1d, 0x1b, 0xca, 0xf5, 0x65, 0xbc, 0x42, 0xd0, + 0x01, 0x1c, 0x1d, 0x00, 0x7c, 0xf6, 0xd7, 0xae, 0x43, 0xdf, 0x50, 0x80, 0x37, 0x0e, 0x11, 0x9c, + 0xa3, 0x03, 0x00, 0x7c, 0xf6, 0x9f, 0x43, 0xed, 0xb2, 0x5f, 0x22, 0x3e, 0xae, 0x67, 0x6d, 0x80, + 0x04, 0x9e, 0x10, 0x14, 0x9a, 0x63, 0x03, 0x40, 0xb9, 0xe3, 0x4f, 0xa6, 0xae, 0xc3, 0x4e, 0xfe, + 0x31, 0xf5, 0xb9, 0xea, 0xd7, 0xaf, 0x84, 0x2f, 0x60, 0x6c, 0x64, 0x34, 0xd7, 0xb7, 0x6d, 0xe4, + 0x9d, 0x84, 0xc5, 0xe5, 0xd8, 0x00, 0x00, 0x9f, 0xfd, 0x75, 0xd1, 0x39, 0x21, 0x78, 0x9c, 0xd7, + 0x06, 0x88, 0xc9, 0x91, 0x01, 0xa0, 0x3c, 0xe4, 0xc3, 0x4f, 0x5d, 0x87, 0x88, 0x74, 0x2e, 0x13, + 0x96, 0xc1, 0x3b, 0x09, 0x0b, 0xc9, 0x91, 0x01, 0x00, 0x17, 0x6e, 0xf4, 0x69, 0xa4, 0x2b, 0xe1, + 0x0b, 0x7a, 0x26, 0x04, 0x0f, 0x07, 0x42, 0x11, 0x1f, 0x75, 0xed, 0x2c, 0x37, 0x8e, 0x0b, 0x00, + 0xde, 0xeb, 0x2f, 0x7f, 0x53, 0x13, 0x93, 0x08, 0xeb, 0x5b, 0x26, 0xcc, 0x13, 0x82, 0x82, 0x71, + 0x5c, 0x00, 0x80, 0x1b, 0xbf, 0x21, 0x62, 0xbd, 0x03, 0x7a, 0xd6, 0x06, 0xf8, 0x03, 0xa1, 0x48, + 0x33, 0x75, 0xed, 0x4c, 0x3b, 0x27, 0x06, 0xc0, 0x33, 0xd4, 0x05, 0x38, 0x85, 0xce, 0x9b, 0x85, + 0x78, 0x42, 0x50, 0x20, 0x8e, 0x0a, 0x00, 0x5e, 0xf8, 0x63, 0xac, 0x44, 0x7c, 0x1c, 0x5d, 0x1d, + 0x17, 0x73, 0x7d, 0x99, 0x04, 0x1e, 0x0a, 0x08, 0xc3, 0x51, 0x01, 0x00, 0x3e, 0xfb, 0x1b, 0xee, + 0xda, 0xa5, 0x2b, 0x7a, 0xf6, 0x0d, 0xe0, 0x9b, 0x85, 0x04, 0xe1, 0x98, 0x00, 0x50, 0x26, 0xff, + 0xfc, 0xd4, 0x75, 0x38, 0x4d, 0x1e, 0xfb, 0x06, 0x70, 0x2f, 0x40, 0x00, 0x8e, 0x09, 0x00, 0xf0, + 0xe4, 0x9f, 0x69, 0x6e, 0xc7, 0x86, 0xf4, 0x4c, 0x08, 0xfa, 0xf8, 0x66, 0x21, 0xfb, 0x73, 0x52, + 0x00, 0x70, 0xf7, 0xdf, 0x44, 0x3a, 0x7b, 0x01, 0x7c, 0xb3, 0x90, 0xcd, 0x39, 0x22, 0x00, 0x78, + 0xdd, 0xbf, 0xf9, 0xf2, 0x98, 0x10, 0xe4, 0x7d, 0x03, 0x6c, 0xcc, 0x11, 0x01, 0x00, 0x3e, 0xfb, + 0x5b, 0xe2, 0xda, 0xa5, 0x2b, 0x7a, 0x56, 0x08, 0xfa, 0xf9, 0x66, 0x21, 0xfb, 0x72, 0x4a, 0x00, + 0x34, 0x52, 0x17, 0xe0, 0x06, 0x79, 0xec, 0x1e, 0xc4, 0x6b, 0x03, 0x6c, 0x4a, 0xf8, 0x00, 0x50, + 0x1e, 0xf5, 0x25, 0x51, 0xd7, 0xe1, 0x16, 0x7d, 0xd1, 0x1b, 0x7a, 0x2e, 0x0b, 0xca, 0xe0, 0x9b, + 0x85, 0x6c, 0x49, 0xf8, 0x00, 0x00, 0xb0, 0x9d, 0xba, 0x00, 0xb7, 0xc9, 0x63, 0x3b, 0x71, 0x99, + 0xba, 0x76, 0x36, 0x97, 0x13, 0x02, 0xa0, 0x91, 0xba, 0x00, 0xb7, 0xd1, 0x79, 0x59, 0x10, 0xe0, + 0x09, 0x41, 0xdb, 0x11, 0x3a, 0x00, 0x94, 0xfb, 0xfe, 0x65, 0xea, 0x3a, 0xdc, 0xa8, 0x2b, 0x7c, + 0x41, 0xcf, 0xcb, 0x78, 0x42, 0xd0, 0x66, 0x84, 0x0e, 0x00, 0xf0, 0xca, 0xbf, 0x9c, 0x2c, 0x2c, + 0x5c, 0x64, 0xd8, 0x7b, 0xdd, 0x8e, 0x0d, 0xe9, 0xd9, 0x3e, 0x0c, 0xe0, 0x09, 0x41, 0x5b, 0x11, + 0x3d, 0x00, 0x78, 0xfc, 0x9f, 0x03, 0xaf, 0x54, 0x6a, 0xe8, 0xfb, 0x5d, 0xbf, 0xa4, 0xeb, 0x01, + 0xa3, 0x32, 0x78, 0x42, 0xd0, 0x36, 0x44, 0x0f, 0x00, 0x3f, 0x75, 0x01, 0x6e, 0xa6, 0xf3, 0x8a, + 0x00, 0x90, 0x5c, 0x21, 0x28, 0x51, 0xd7, 0xcf, 0x04, 0x0e, 0x00, 0x65, 0xfc, 0x2f, 0x51, 0xd7, + 0xe1, 0x76, 0xfd, 0xd1, 0x9b, 0x7a, 0x5e, 0x26, 0x81, 0x7b, 0x01, 0xb6, 0x20, 0x6c, 0x00, 0x00, + 0xf0, 0x51, 0x17, 0xc0, 0x80, 0xbe, 0xee, 0x1b, 0x7a, 0x5f, 0xca, 0xbd, 0x00, 0x1b, 0x10, 0x39, + 0x00, 0x78, 0xfc, 0xaf, 0xc3, 0xc2, 0x45, 0xc6, 0x4d, 0x04, 0x02, 0xc9, 0x7b, 0x04, 0x74, 0x3c, + 0x61, 0x18, 0x48, 0xf6, 0x02, 0x9a, 0xa9, 0x3f, 0x0f, 0xb7, 0x13, 0x39, 0x00, 0x7c, 0xd4, 0x05, + 0x88, 0xc8, 0xbb, 0xcc, 0xd8, 0x89, 0x40, 0x00, 0xe8, 0xeb, 0xd6, 0x35, 0x0c, 0x00, 0x78, 0xf7, + 0x66, 0x72, 0x1c, 0x00, 0x2c, 0x6f, 0xb1, 0xde, 0x01, 0x3d, 0x37, 0x09, 0x01, 0xc9, 0x07, 0x8c, + 0xfa, 0xa9, 0xeb, 0x77, 0x33, 0x21, 0x03, 0x40, 0xb9, 0xfd, 0x97, 0xd9, 0xc8, 0xa0, 0xbe, 0x61, + 0x00, 0xc0, 0x77, 0x72, 0x92, 0x12, 0x32, 0x00, 0xc0, 0xab, 0xff, 0x74, 0x2b, 0x2e, 0x59, 0x6c, + 0xca, 0xfb, 0xc6, 0x3e, 0xf8, 0x50, 0xef, 0x4b, 0x1b, 0xa9, 0x3e, 0x0b, 0x26, 0x6e, 0x00, 0xd4, + 0x51, 0x17, 0x20, 0xaa, 0x22, 0xb3, 0x02, 0x40, 0xff, 0x30, 0x40, 0xe2, 0x61, 0x00, 0x1d, 0x51, + 0x03, 0xc0, 0x47, 0x5d, 0x00, 0x7b, 0x50, 0x1e, 0xc3, 0x80, 0xef, 0x52, 0xd7, 0xee, 0x56, 0xa2, + 0x06, 0x80, 0x4c, 0x5d, 0x80, 0xa8, 0x8a, 0x17, 0x9b, 0xd3, 0x03, 0x00, 0x80, 0xe1, 0x41, 0x5d, + 0x77, 0x08, 0x02, 0xbc, 0xa2, 0x93, 0x0c, 0x07, 0x80, 0xcb, 0x98, 0x35, 0x04, 0x00, 0xf2, 0xea, + 0x01, 0xf8, 0x28, 0x3e, 0x0b, 0x26, 0x60, 0x00, 0xf0, 0x15, 0x00, 0xfb, 0x9a, 0x9a, 0x98, 0xd4, + 0x7b, 0x87, 0x20, 0x78, 0x1e, 0x80, 0x86, 0x70, 0x01, 0x00, 0x5e, 0xff, 0x9f, 0x17, 0x33, 0x16, + 0x02, 0xcd, 0xa6, 0x73, 0xa3, 0x10, 0x80, 0x7b, 0x01, 0x24, 0x44, 0x0c, 0x00, 0x1f, 0x75, 0x01, + 0x22, 0x33, 0x7a, 0x29, 0xf0, 0x7c, 0x63, 0xc3, 0xfa, 0x7a, 0x00, 0x00, 0x6a, 0x2c, 0xff, 0x30, + 0x98, 0x90, 0x01, 0x60, 0xee, 0x29, 0xcc, 0x05, 0xcc, 0x0c, 0x81, 0x8f, 0x75, 0x0e, 0x01, 0xc0, + 0xc1, 0x4e, 0x42, 0xc4, 0x00, 0xf0, 0x51, 0x17, 0x20, 0x3a, 0x33, 0x87, 0x01, 0x7a, 0xe7, 0x00, + 0xc0, 0x43, 0x3b, 0x12, 0x22, 0x06, 0x80, 0x44, 0x5d, 0x00, 0x53, 0xa7, 0x33, 0x04, 0x7c, 0xd4, + 0x75, 0xbb, 0x91, 0x88, 0x01, 0xe0, 0xa3, 0x2e, 0x40, 0x74, 0xcb, 0xca, 0x57, 0x98, 0xfa, 0xfe, + 0x93, 0xfa, 0x56, 0x04, 0x32, 0x02, 0x22, 0x06, 0x00, 0xb3, 0xb9, 0x61, 0xfd, 0x57, 0x02, 0x98, + 0xc5, 0x84, 0x0a, 0x00, 0x5e, 0x03, 0x60, 0x8c, 0x65, 0x2b, 0xcd, 0xed, 0x01, 0x30, 0x71, 0x08, + 0x15, 0x00, 0x8c, 0x31, 0x63, 0x89, 0x16, 0x00, 0x12, 0x75, 0x01, 0x4e, 0x60, 0xf6, 0x1c, 0xc0, + 0xed, 0xc1, 0x8f, 0xa8, 0x0f, 0x91, 0x69, 0x24, 0x5a, 0x00, 0xf8, 0xa8, 0x0b, 0x70, 0x0a, 0xb3, + 0x17, 0x04, 0x31, 0x31, 0x88, 0x16, 0x00, 0xcc, 0x20, 0x66, 0x2f, 0x09, 0x66, 0x62, 0x10, 0x2d, + 0x00, 0xf8, 0xb7, 0xd6, 0x20, 0x66, 0xed, 0x0c, 0xc4, 0xc4, 0xb2, 0x90, 0xba, 0x80, 0x1c, 0xf9, + 0xa8, 0x0b, 0xb0, 0x9b, 0x82, 0x05, 0x05, 0xf0, 0x14, 0x79, 0x50, 0x50, 0xb0, 0x00, 0x9e, 0x22, + 0x0f, 0x00, 0x60, 0xe1, 0xa2, 0x85, 0x0f, 0x3c, 0x07, 0x70, 0xd1, 0xa2, 0x85, 0x73, 0xba, 0xfd, + 0x8f, 0x7e, 0x79, 0x0d, 0x12, 0xf1, 0x71, 0x24, 0xe2, 0x7f, 0x9f, 0xf3, 0x7d, 0x63, 0x23, 0xa3, + 0x33, 0x3b, 0xfb, 0xa4, 0xc6, 0xf2, 0xb9, 0xde, 0xe0, 0xa3, 0xf3, 0x11, 0x64, 0x1d, 0xb4, 0x9f, + 0xa4, 0x3b, 0x89, 0x16, 0x00, 0xae, 0xe5, 0x29, 0xf2, 0x60, 0xe1, 0xa2, 0x45, 0xf0, 0x14, 0x79, + 0x50, 0x58, 0x54, 0x38, 0xf3, 0xe7, 0x7c, 0x78, 0xa5, 0xd2, 0x07, 0x1a, 0x6b, 0x95, 0x5c, 0x3d, + 0xf3, 0xe7, 0xcd, 0xfe, 0xad, 0x33, 0x7f, 0x9e, 0x9a, 0x98, 0xc4, 0xc7, 0x23, 0xa3, 0x18, 0x1b, + 0x19, 0x45, 0xe2, 0x93, 0x71, 0x8c, 0x8d, 0x8c, 0x66, 0x0c, 0x06, 0x9d, 0xc3, 0x8b, 0xb8, 0x55, + 0x9f, 0x25, 0xbb, 0x8f, 0x03, 0xc0, 0x86, 0x0a, 0x16, 0x14, 0xa0, 0x78, 0xf1, 0x62, 0x14, 0x97, + 0x14, 0xa3, 0xb0, 0xc8, 0x83, 0xe2, 0xc5, 0xc5, 0xd4, 0x25, 0xa1, 0xb0, 0xc8, 0x83, 0x2a, 0xb9, + 0x7a, 0x4e, 0x40, 0x00, 0x98, 0x09, 0x82, 0xdb, 0x83, 0x43, 0xb8, 0x1d, 0xfb, 0x48, 0xef, 0xbe, + 0x80, 0x00, 0xd0, 0x43, 0x7d, 0x8c, 0x6e, 0x24, 0x5a, 0x00, 0xf8, 0xa9, 0x0b, 0x30, 0xc3, 0xec, + 0x06, 0x5f, 0xe2, 0x2d, 0x11, 0x6a, 0x86, 0xbe, 0xac, 0xa2, 0x1c, 0x65, 0x15, 0xe5, 0xd8, 0x50, + 0xbf, 0x19, 0x40, 0x32, 0x10, 0x0a, 0xf5, 0xf5, 0x4c, 0xa2, 0xd4, 0xc7, 0xe2, 0x46, 0xa2, 0x05, + 0x80, 0x63, 0x2c, 0x5c, 0xb4, 0x08, 0x25, 0xde, 0x12, 0x94, 0x2c, 0x5d, 0x62, 0x8b, 0x33, 0xbc, + 0x51, 0xca, 0x2a, 0xca, 0xf5, 0xbe, 0x34, 0x4a, 0x5d, 0xbb, 0x1b, 0x71, 0x00, 0x58, 0xc8, 0x53, + 0xe4, 0x81, 0x57, 0x5a, 0x2a, 0xdc, 0x59, 0xde, 0x02, 0xb1, 0x96, 0x86, 0xfa, 0x18, 0x75, 0x11, + 0x6e, 0xc4, 0x01, 0x60, 0xb2, 0xd4, 0x99, 0xde, 0x2b, 0x2d, 0xcd, 0x7b, 0xd2, 0xce, 0xc1, 0x3a, + 0xa8, 0x0b, 0x70, 0x2b, 0x61, 0x02, 0x60, 0xe7, 0xb1, 0xa0, 0x4c, 0x5d, 0x43, 0x2e, 0x4a, 0xbc, + 0x4b, 0xe0, 0x95, 0xbc, 0x28, 0xf1, 0x2e, 0xa1, 0x2e, 0x45, 0x04, 0x7f, 0xa4, 0x2e, 0xc0, 0xad, + 0x44, 0x5a, 0x08, 0x24, 0x53, 0x17, 0x90, 0x4d, 0xc1, 0x82, 0x02, 0x2c, 0x5f, 0x59, 0x86, 0x9a, + 0x35, 0x32, 0x2a, 0xaa, 0x2b, 0xb9, 0xf1, 0x6b, 0x77, 0x3c, 0x10, 0x8a, 0x34, 0x53, 0x17, 0xe1, + 0x46, 0x22, 0x05, 0x80, 0x6d, 0x2d, 0x5c, 0xb4, 0x08, 0xe5, 0x55, 0x0f, 0x63, 0xf5, 0xda, 0x47, + 0xb1, 0x6c, 0xe5, 0x72, 0x1e, 0xdf, 0xe7, 0x4e, 0x06, 0xd0, 0x1a, 0x08, 0x45, 0x06, 0x39, 0x08, + 0xac, 0xc5, 0x01, 0x90, 0x87, 0xe2, 0x92, 0x62, 0x54, 0xc9, 0xab, 0x50, 0xb3, 0x46, 0x86, 0x57, + 0x5a, 0x4a, 0x5d, 0x8e, 0x13, 0xc8, 0x48, 0x06, 0x41, 0x98, 0x9f, 0x13, 0x60, 0x0d, 0x61, 0xe6, + 0x00, 0xec, 0xa4, 0xb8, 0xa4, 0x18, 0xcb, 0x56, 0x96, 0x39, 0xea, 0xf2, 0x9d, 0xcd, 0xf8, 0x01, + 0xf8, 0x03, 0xa1, 0xc8, 0x29, 0x00, 0x07, 0x5a, 0x1a, 0xea, 0xe3, 0xd4, 0x05, 0x39, 0x15, 0xf7, + 0x00, 0x72, 0x90, 0x3a, 0xe3, 0x57, 0xd5, 0xac, 0xe2, 0xc6, 0x6f, 0x8d, 0x66, 0x00, 0x83, 0x81, + 0x50, 0xa4, 0x91, 0xba, 0x10, 0xa7, 0x12, 0x29, 0x00, 0x64, 0xaa, 0x1f, 0x9c, 0x1a, 0xe3, 0x73, + 0xc3, 0x27, 0x21, 0x01, 0x68, 0x0b, 0x84, 0x22, 0x6d, 0x81, 0x50, 0x44, 0xa2, 0x2e, 0xc6, 0x69, + 0x38, 0x00, 0x54, 0xcc, 0x9e, 0xd5, 0xe7, 0x31, 0x3e, 0xb9, 0x46, 0x70, 0x6f, 0xc0, 0x70, 0x22, + 0x05, 0x80, 0xa5, 0x4a, 0xbc, 0x4b, 0x50, 0xfd, 0xa5, 0x47, 0xb0, 0x6c, 0xe5, 0x72, 0xea, 0x52, + 0xd8, 0x7d, 0x12, 0x92, 0xbd, 0x81, 0xe3, 0xd4, 0x85, 0x38, 0x05, 0x07, 0xc0, 0x3c, 0x0b, 0x17, + 0x2d, 0x42, 0x95, 0xbc, 0x0a, 0x15, 0xd5, 0x95, 0x7c, 0x39, 0xcf, 0xbe, 0xf6, 0x07, 0x42, 0x91, + 0x6e, 0x1e, 0x12, 0xe4, 0x8f, 0x03, 0x60, 0x96, 0xe5, 0x2b, 0xcb, 0x50, 0xfd, 0xa5, 0x47, 0x78, + 0x9c, 0x2f, 0x06, 0x1f, 0x92, 0x43, 0x02, 0x1f, 0x75, 0x21, 0x22, 0xe3, 0x00, 0x40, 0xf2, 0x26, + 0x9d, 0x55, 0x4a, 0x77, 0xbf, 0x60, 0x01, 0x7f, 0x24, 0x02, 0x91, 0x00, 0x84, 0x79, 0xf1, 0x90, + 0x7e, 0xae, 0xff, 0x6d, 0x2f, 0x5d, 0x2e, 0xa1, 0xaa, 0x66, 0x15, 0xdf, 0xa8, 0x23, 0x2e, 0x09, + 0xc9, 0xc5, 0x43, 0xcd, 0xd4, 0x85, 0x88, 0xc8, 0xb5, 0x01, 0x50, 0xb0, 0xa0, 0x00, 0x55, 0xf2, + 0x2a, 0xac, 0xa8, 0x58, 0xc9, 0x67, 0x7d, 0x67, 0x68, 0x0d, 0x84, 0x22, 0xad, 0xd4, 0x45, 0x88, + 0xc6, 0x95, 0xbf, 0xf9, 0xc5, 0x25, 0xc5, 0xa8, 0x79, 0x6c, 0x35, 0x8f, 0xf5, 0x9d, 0xa7, 0x99, + 0x43, 0x20, 0x37, 0xae, 0x0b, 0x80, 0xe5, 0x2b, 0xcb, 0x50, 0x55, 0xb3, 0x8a, 0xcf, 0xfa, 0xce, + 0xc5, 0x21, 0x90, 0x03, 0xd7, 0xb4, 0x82, 0x82, 0x05, 0x05, 0xa8, 0xa8, 0xae, 0xe4, 0xeb, 0xfa, + 0xee, 0xc0, 0x21, 0xa0, 0x91, 0x2b, 0x02, 0xc0, 0x53, 0xe4, 0x41, 0x55, 0xcd, 0x2a, 0xbe, 0x3f, + 0xdf, 0x5d, 0x9a, 0x03, 0xa1, 0xc8, 0xab, 0xd4, 0x45, 0xd8, 0x9d, 0xe3, 0x03, 0xa0, 0xb8, 0xa4, + 0x98, 0x67, 0xf9, 0xdd, 0xeb, 0x30, 0x5f, 0x1d, 0x50, 0xe7, 0xe8, 0x00, 0xf0, 0x4a, 0x4b, 0x79, + 0xbc, 0xcf, 0x5a, 0x79, 0x6f, 0x81, 0xcc, 0x1c, 0xdb, 0x32, 0x96, 0xaf, 0x2c, 0x43, 0x79, 0xd5, + 0xc3, 0xd4, 0x65, 0x30, 0x7b, 0x68, 0xe3, 0x15, 0x83, 0xe9, 0x39, 0x32, 0x00, 0xca, 0xab, 0x1e, + 0xe6, 0xc9, 0x3e, 0x36, 0x9b, 0x84, 0x64, 0x4f, 0x40, 0xa2, 0x2e, 0xc4, 0x6e, 0x1c, 0x17, 0x00, + 0xe5, 0x55, 0x0f, 0xf3, 0xad, 0xbb, 0x2c, 0x1d, 0x1f, 0x00, 0xbe, 0x32, 0x30, 0x8f, 0xa3, 0x02, + 0x80, 0x1b, 0x3f, 0xcb, 0xa2, 0x31, 0x10, 0x8a, 0xec, 0xa7, 0x2e, 0xc2, 0x4e, 0x1c, 0x13, 0x00, + 0xdc, 0xf8, 0x99, 0x46, 0xc7, 0x79, 0x3e, 0xe0, 0x3e, 0x47, 0x04, 0x00, 0x37, 0x7e, 0x96, 0x23, + 0xde, 0x5e, 0x4c, 0x21, 0x7c, 0x00, 0x70, 0xe3, 0x67, 0x3a, 0xc8, 0x00, 0x0e, 0x53, 0x17, 0x61, + 0x07, 0x42, 0x07, 0x00, 0x37, 0x7e, 0x96, 0x87, 0xfd, 0xbc, 0x3e, 0x40, 0xe0, 0x00, 0xf0, 0x4a, + 0x4b, 0xb9, 0xf1, 0xb3, 0x7c, 0xb9, 0xfe, 0xd2, 0xa0, 0x90, 0x01, 0xe0, 0x95, 0x96, 0xf2, 0x22, + 0x1f, 0x66, 0x04, 0x19, 0xc0, 0x7e, 0xea, 0x22, 0x28, 0x09, 0x17, 0x00, 0x9e, 0x22, 0x0f, 0x56, + 0x3c, 0xbc, 0x92, 0xba, 0x0c, 0xe6, 0x1c, 0x87, 0x03, 0xa1, 0x88, 0x4c, 0x5d, 0x04, 0x15, 0x91, + 0x02, 0xa0, 0xa3, 0x60, 0x41, 0x01, 0xaf, 0xed, 0x67, 0x66, 0x70, 0xed, 0x02, 0x21, 0xa1, 0x5a, + 0x12, 0x37, 0x7e, 0x66, 0x12, 0xbf, 0x5b, 0x27, 0x04, 0x85, 0x69, 0x4d, 0x8f, 0x7e, 0x79, 0xcd, + 0x77, 0xf9, 0x96, 0x5e, 0x66, 0x22, 0x57, 0xf6, 0x02, 0x84, 0x08, 0x00, 0xe5, 0x9e, 0xee, 0xfd, + 0xd4, 0x75, 0x30, 0x47, 0x93, 0xdd, 0xb8, 0x77, 0x80, 0xed, 0x03, 0x40, 0x99, 0xa0, 0xe1, 0x47, + 0x41, 0x31, 0x2b, 0xb8, 0x6e, 0x71, 0x90, 0xed, 0x03, 0x00, 0x40, 0x1b, 0x92, 0xb7, 0x73, 0x32, + 0x66, 0x36, 0xd7, 0xf5, 0x02, 0x6c, 0x1d, 0x00, 0xca, 0x9e, 0x6e, 0x3e, 0xea, 0x3a, 0x98, 0xab, + 0xb8, 0xaa, 0x17, 0xf0, 0x10, 0x75, 0x01, 0x99, 0x28, 0x77, 0x6c, 0x75, 0x53, 0xd7, 0xc1, 0x5c, + 0x69, 0x6f, 0x4b, 0x43, 0xfd, 0x29, 0xea, 0x22, 0xac, 0x60, 0xe7, 0x1e, 0x80, 0x2b, 0x67, 0x65, + 0x99, 0x2d, 0xbc, 0x48, 0x5d, 0x80, 0x55, 0x6c, 0x19, 0x00, 0xdc, 0xf5, 0x67, 0xc4, 0x7c, 0x6e, + 0x59, 0x17, 0x60, 0xbb, 0x00, 0x50, 0x66, 0xfd, 0x5d, 0x35, 0x0e, 0x63, 0xb6, 0xf4, 0x0c, 0x75, + 0x01, 0x56, 0xb0, 0xdd, 0x1c, 0x40, 0x20, 0x14, 0x09, 0x03, 0xf0, 0x53, 0xd7, 0xc1, 0x9c, 0x65, + 0x6a, 0x62, 0x12, 0xd7, 0x23, 0x5d, 0x98, 0xfc, 0x6c, 0x02, 0x63, 0x23, 0xa3, 0xaa, 0xdf, 0x5b, + 0xb5, 0xfa, 0x11, 0x14, 0x16, 0x79, 0x30, 0x35, 0x31, 0xb9, 0xfa, 0xe2, 0xcf, 0x7f, 0x14, 0xa3, + 0xae, 0xdd, 0x4c, 0xb6, 0x0a, 0x80, 0x40, 0x28, 0xd2, 0x88, 0xe4, 0x65, 0x3f, 0xc6, 0x0c, 0xf5, + 0xfe, 0x3b, 0x67, 0x10, 0xeb, 0x1d, 0xc8, 0xf5, 0x65, 0x71, 0x00, 0x3b, 0xa6, 0x2e, 0xff, 0x21, + 0x4a, 0x5d, 0xbf, 0x59, 0xec, 0x36, 0x04, 0xe0, 0x05, 0x3f, 0xcc, 0x14, 0x53, 0x13, 0x13, 0x7a, + 0x5e, 0x26, 0xc1, 0xe1, 0x93, 0xd1, 0xb6, 0x09, 0x00, 0x65, 0xe2, 0x4f, 0xa6, 0xae, 0x83, 0xb1, + 0x79, 0x7c, 0x85, 0x5b, 0xbe, 0xd7, 0x4c, 0x5d, 0x84, 0x59, 0x6c, 0x11, 0x00, 0xca, 0xae, 0x2c, + 0xae, 0xb9, 0xf4, 0xc2, 0x84, 0xe3, 0xd8, 0x09, 0x41, 0x5b, 0x04, 0x00, 0x92, 0x37, 0xfa, 0x48, + 0xd4, 0x45, 0x30, 0xe7, 0xf2, 0x4a, 0xa5, 0xf9, 0xbc, 0xdc, 0x5f, 0xb8, 0xe5, 0x7b, 0x12, 0xf5, + 0x31, 0x98, 0x81, 0x3c, 0x00, 0xf8, 0xec, 0xcf, 0xac, 0x50, 0xb9, 0xba, 0x3a, 0xdf, 0xb7, 0xf0, + 0x53, 0x1f, 0x83, 0x19, 0xc8, 0x03, 0x00, 0x7c, 0xf6, 0x67, 0x16, 0x58, 0xeb, 0x5b, 0x8f, 0xc2, + 0xfc, 0xf6, 0x93, 0xf0, 0x51, 0x1f, 0x83, 0x19, 0x48, 0x03, 0x80, 0xcf, 0xfe, 0xcc, 0x4a, 0x3b, + 0x76, 0x37, 0xe4, 0xf3, 0xf2, 0x1a, 0xea, 0xfa, 0xcd, 0xb0, 0x90, 0xf8, 0xe7, 0xef, 0x87, 0x4b, + 0xce, 0xfe, 0x53, 0x13, 0x93, 0xf8, 0x78, 0x64, 0x14, 0x89, 0xf8, 0x38, 0xee, 0xc6, 0xff, 0x9e, + 0xf1, 0xfb, 0x96, 0x48, 0x4b, 0xe1, 0x95, 0x4a, 0xb1, 0xa2, 0xa2, 0x3c, 0xdf, 0x33, 0x16, 0x9b, + 0x47, 0x7e, 0x7c, 0x0d, 0xbe, 0xba, 0xe3, 0x1b, 0xb8, 0x12, 0xbe, 0xa0, 0xeb, 0xe5, 0xd4, 0xf5, + 0x9b, 0x81, 0x3a, 0x00, 0x1c, 0x3b, 0xbb, 0x3a, 0x35, 0x31, 0x89, 0xc1, 0xde, 0x01, 0x0c, 0x0f, + 0x0e, 0xe1, 0x76, 0x6c, 0x08, 0x89, 0xf8, 0x78, 0xce, 0xef, 0x51, 0x58, 0xe4, 0x41, 0x95, 0xfc, + 0x08, 0xaa, 0x56, 0x57, 0x43, 0x7e, 0xfc, 0xb1, 0x7c, 0x27, 0xb2, 0x18, 0x80, 0xcd, 0xfe, 0xad, + 0xa8, 0xf5, 0xad, 0x43, 0x57, 0xf8, 0xa2, 0xe6, 0xff, 0x97, 0xc2, 0x22, 0x0f, 0x1e, 0xdf, 0xb8, + 0x21, 0x7a, 0xe5, 0xf2, 0x1f, 0xa8, 0xcb, 0x37, 0x1c, 0xd9, 0x4a, 0x40, 0x65, 0xe3, 0x05, 0x47, + 0x2d, 0xb2, 0x48, 0x35, 0xfa, 0xfe, 0xee, 0x1b, 0xb8, 0x1d, 0x1b, 0x32, 0xfc, 0xfd, 0xcb, 0x2a, + 0xca, 0xb1, 0x76, 0xe3, 0x7a, 0x23, 0xc6, 0xb3, 0x4c, 0x91, 0xea, 0x99, 0x4d, 0x4d, 0x4c, 0x60, + 0x6c, 0xe4, 0xce, 0x9c, 0xaf, 0x55, 0xca, 0xd5, 0xf0, 0x14, 0x79, 0x50, 0x56, 0x51, 0x0e, 0x00, + 0xb1, 0x96, 0x86, 0xfa, 0xd5, 0xd4, 0xf5, 0x1a, 0x8d, 0x32, 0x00, 0x1c, 0xb3, 0xe6, 0x3f, 0x11, + 0x1f, 0x47, 0x7f, 0xf4, 0x26, 0xae, 0x5d, 0xba, 0x82, 0xa9, 0x89, 0x49, 0x4b, 0x7e, 0xe6, 0x5a, + 0xdf, 0x7a, 0x6c, 0xde, 0xb1, 0x95, 0x7b, 0x05, 0xd6, 0x5a, 0xdd, 0xd2, 0x50, 0x1f, 0xa3, 0x2e, + 0xc2, 0x48, 0x24, 0x01, 0xa0, 0xdc, 0xf1, 0x37, 0x48, 0x7d, 0xf0, 0xf9, 0x4a, 0xdd, 0x60, 0x62, + 0x65, 0xc3, 0x9f, 0x8f, 0x83, 0xc0, 0x52, 0x07, 0x5a, 0x1a, 0xea, 0xdf, 0xa0, 0x2e, 0xc2, 0x48, + 0x54, 0x73, 0x00, 0xc2, 0xcf, 0xfc, 0xc7, 0x7a, 0x07, 0x10, 0x6e, 0x0b, 0x91, 0x35, 0xfc, 0x94, + 0xbe, 0xe8, 0x0d, 0x0c, 0xf6, 0x0e, 0xe0, 0x2b, 0x5f, 0xff, 0x2a, 0x36, 0xfb, 0xb7, 0x52, 0x7f, + 0x2c, 0x4e, 0xb7, 0x1d, 0xc0, 0x1b, 0xd4, 0x45, 0x18, 0x89, 0x2a, 0x00, 0x9a, 0xa9, 0x0f, 0x5c, + 0xaf, 0xa9, 0x89, 0x49, 0x84, 0xdb, 0x42, 0x7a, 0xee, 0x2c, 0x33, 0xb5, 0xa6, 0x2b, 0xe1, 0x0b, + 0x18, 0xfc, 0x60, 0x00, 0xdf, 0xfe, 0x41, 0x23, 0xf7, 0x06, 0xcc, 0xe3, 0xa7, 0x2e, 0xc0, 0x68, + 0x96, 0x0f, 0x01, 0x44, 0xbe, 0xe5, 0x77, 0x6c, 0x64, 0x14, 0x67, 0x5b, 0x4f, 0x93, 0x9f, 0xf5, + 0xd5, 0x14, 0x16, 0x79, 0xb0, 0x63, 0x77, 0x03, 0xe4, 0xc7, 0xd7, 0x50, 0x97, 0xe2, 0x54, 0x1b, + 0x5b, 0x1a, 0xea, 0xa3, 0xd4, 0x45, 0x18, 0x85, 0x62, 0x21, 0xd0, 0x77, 0xa9, 0x0f, 0x5a, 0x8f, + 0xbe, 0xe8, 0x0d, 0xfc, 0xfe, 0xad, 0xb7, 0x6d, 0xdd, 0xf8, 0x81, 0x64, 0x6f, 0xe0, 0xfd, 0x77, + 0xce, 0xa0, 0xab, 0xe3, 0x22, 0x75, 0x29, 0x4e, 0xe5, 0xa3, 0x2e, 0xc0, 0x48, 0x14, 0x43, 0x80, + 0x46, 0xea, 0x83, 0xce, 0x55, 0x5f, 0xf4, 0x06, 0x3a, 0xda, 0x42, 0x86, 0xbc, 0xd7, 0xb6, 0x4d, + 0xeb, 0x00, 0x00, 0x35, 0x95, 0xe5, 0x90, 0xab, 0x92, 0x4f, 0x39, 0xee, 0xe9, 0x8f, 0x21, 0x9e, + 0xb8, 0x07, 0x00, 0xe8, 0xbc, 0x7a, 0xd3, 0x90, 0x9f, 0x73, 0x25, 0x7c, 0x01, 0x89, 0x4f, 0xc6, + 0xe1, 0xcf, 0x6f, 0xf5, 0x1b, 0x7b, 0xd0, 0x76, 0x00, 0xa7, 0xa8, 0x8b, 0x30, 0x8a, 0xa5, 0x01, + 0xa0, 0x74, 0xff, 0x25, 0xea, 0x83, 0xce, 0x45, 0x3e, 0x8d, 0x5f, 0xf2, 0x96, 0xe0, 0x3b, 0xdb, + 0xb6, 0x60, 0xfb, 0xe6, 0x75, 0xd8, 0xb6, 0x69, 0x1d, 0x6a, 0x2a, 0xb5, 0x3d, 0xd6, 0xfc, 0xd6, + 0xf0, 0x1d, 0x74, 0x5e, 0xbd, 0x89, 0xf3, 0x5d, 0x37, 0xf1, 0x5e, 0xe7, 0xe5, 0x99, 0x70, 0xd0, + 0x53, 0x3b, 0x00, 0x0e, 0x01, 0x63, 0xf9, 0xa8, 0x0b, 0x30, 0x92, 0xa5, 0x73, 0x00, 0x81, 0x50, + 0xa4, 0x15, 0x02, 0x4d, 0x00, 0xea, 0x6d, 0xfc, 0x4f, 0x6e, 0xdf, 0x82, 0xa6, 0x5d, 0x7e, 0x3c, + 0xb9, 0x7d, 0x8b, 0x21, 0x75, 0x9c, 0x3d, 0x7f, 0x19, 0x2d, 0xa7, 0xdb, 0x75, 0xf7, 0x0e, 0x36, + 0xd4, 0x6f, 0xc6, 0xd6, 0x86, 0x6f, 0x9a, 0xfa, 0x59, 0xb9, 0x49, 0x4b, 0x43, 0xbd, 0xad, 0xb6, + 0xd2, 0xcb, 0x87, 0xd5, 0x01, 0x30, 0x08, 0x41, 0xd6, 0x54, 0xdf, 0x8e, 0x0d, 0xe1, 0xbd, 0xd6, + 0xd3, 0x39, 0xbd, 0xa6, 0x69, 0xd7, 0x0e, 0xbc, 0xfc, 0xec, 0xd3, 0x9a, 0xcf, 0xf4, 0x59, 0x4d, + 0x4f, 0x61, 0x7a, 0x7a, 0x12, 0x98, 0xfe, 0x1c, 0xc0, 0x34, 0x6e, 0x8d, 0x7c, 0x82, 0x23, 0x27, + 0xff, 0x88, 0xe0, 0xb9, 0x4b, 0x39, 0xbf, 0x95, 0x7f, 0x77, 0x03, 0xd6, 0xfa, 0xd6, 0x5b, 0xf0, + 0xc9, 0xb9, 0xc2, 0x8e, 0x96, 0x86, 0xfa, 0x0e, 0xea, 0x22, 0x8c, 0x60, 0xd9, 0x24, 0xa0, 0xf2, + 0xa4, 0x1f, 0x99, 0xfa, 0x80, 0xb5, 0x48, 0x4e, 0xa4, 0x69, 0xbf, 0x50, 0x51, 0x57, 0x2b, 0xe3, + 0x7f, 0xdf, 0x7a, 0x0d, 0x27, 0x5f, 0x79, 0xde, 0xb0, 0xc6, 0x3f, 0xfd, 0xc5, 0x5d, 0x4c, 0x7f, + 0x91, 0x00, 0xa6, 0xa7, 0x00, 0x4c, 0x03, 0x00, 0x6a, 0x2a, 0x96, 0xe1, 0x37, 0x2f, 0x35, 0xe3, + 0xf2, 0xdb, 0x2f, 0xa1, 0xa6, 0x72, 0x45, 0x4e, 0xef, 0xd7, 0xd1, 0x16, 0xca, 0xba, 0x1b, 0x2e, + 0xd3, 0xcc, 0x47, 0x5d, 0x80, 0x51, 0xac, 0xbc, 0x0a, 0xe0, 0xa7, 0x3e, 0x58, 0xad, 0xde, 0x7f, + 0xa7, 0x4d, 0xf3, 0x6c, 0x7f, 0x60, 0xcf, 0x2e, 0x5c, 0x0e, 0x1e, 0x9d, 0x99, 0xdc, 0x33, 0xc4, + 0xf4, 0x04, 0x30, 0x9d, 0xf9, 0xe7, 0x7f, 0x65, 0x4d, 0x35, 0x2e, 0xbf, 0x7d, 0x18, 0x81, 0x3d, + 0xbb, 0x72, 0x3c, 0xae, 0x33, 0xb6, 0xbf, 0x8a, 0x21, 0x88, 0x3a, 0xea, 0x02, 0x8c, 0x62, 0x65, + 0x00, 0x6c, 0xa7, 0x3e, 0x58, 0x2d, 0xae, 0x47, 0xba, 0x34, 0xdd, 0xc8, 0x23, 0x79, 0x4b, 0xf0, + 0x3f, 0xbf, 0xf8, 0x09, 0x8e, 0x1e, 0xd8, 0x6b, 0x78, 0x0d, 0xd3, 0x5f, 0x7c, 0x96, 0xf5, 0x7b, + 0x4a, 0x97, 0x14, 0xe2, 0xe8, 0xfe, 0x7f, 0xc3, 0xc9, 0x57, 0x5e, 0x80, 0xe4, 0x2d, 0xd1, 0xf4, + 0xbe, 0x89, 0xf8, 0xb8, 0xde, 0x5b, 0x61, 0xd9, 0x5c, 0x32, 0x75, 0x01, 0x46, 0xe1, 0x1e, 0xc0, + 0x2c, 0x5a, 0x1b, 0x88, 0xe4, 0x2d, 0xc1, 0x9f, 0x7f, 0xf5, 0x9a, 0x61, 0x93, 0x7c, 0x29, 0xc1, + 0xf6, 0x0e, 0x3c, 0xf5, 0xe3, 0xff, 0x00, 0xf0, 0x85, 0xa6, 0xef, 0x9f, 0x9e, 0x9e, 0x42, 0xd3, + 0x2e, 0x3f, 0xfe, 0xfc, 0xab, 0xd7, 0x34, 0x87, 0x80, 0xd6, 0x80, 0x63, 0xaa, 0xfc, 0xd4, 0x05, + 0x18, 0xc5, 0x92, 0x00, 0x50, 0xc6, 0xff, 0x12, 0xf5, 0xc1, 0x66, 0xd3, 0x15, 0xbe, 0x98, 0xb5, + 0x8b, 0x9c, 0x6a, 0xfc, 0x75, 0xb5, 0xb2, 0x61, 0x3f, 0xf7, 0xd6, 0xf0, 0x1d, 0x7c, 0xeb, 0xb9, + 0xc3, 0xd8, 0xf7, 0xfa, 0x9b, 0x88, 0xdf, 0x4d, 0x68, 0x7e, 0xdd, 0xb5, 0xfe, 0xff, 0x43, 0xb0, + 0xbd, 0x03, 0x75, 0xb5, 0x72, 0x4e, 0x21, 0x70, 0x31, 0xf4, 0x57, 0x53, 0x3e, 0x3f, 0x37, 0x51, + 0x6e, 0x68, 0x13, 0x9e, 0x55, 0x3d, 0x00, 0x1f, 0xf5, 0x81, 0x66, 0x93, 0x88, 0x8f, 0xcf, 0x5c, + 0x37, 0xcf, 0xc4, 0x8c, 0xc6, 0xdf, 0xd3, 0x1f, 0xc3, 0x96, 0xa6, 0x43, 0x33, 0x97, 0xf8, 0x6e, + 0x0d, 0x8f, 0x69, 0x7e, 0x6d, 0xfc, 0xee, 0x3d, 0xec, 0x7b, 0xfd, 0x4d, 0xec, 0x7b, 0xfd, 0x44, + 0x4e, 0x21, 0x30, 0x36, 0x32, 0x9a, 0xf5, 0x58, 0x59, 0x56, 0x32, 0x75, 0x01, 0x46, 0xb0, 0x2a, + 0x00, 0x6c, 0x3f, 0xfe, 0xef, 0x0a, 0x67, 0x5f, 0x3a, 0xfb, 0x9b, 0x97, 0x9f, 0x37, 0xbc, 0xf1, + 0x3f, 0xf1, 0xc3, 0xc3, 0x73, 0x16, 0xfa, 0xdc, 0x1a, 0x1e, 0xc3, 0xb5, 0x01, 0x6d, 0x5d, 0xf4, + 0x6b, 0xfd, 0xc9, 0xef, 0x0b, 0xb6, 0x87, 0x67, 0x42, 0x40, 0xeb, 0x9c, 0x84, 0x96, 0xe3, 0x65, + 0xaa, 0xfc, 0xd4, 0x05, 0x18, 0xc1, 0xaa, 0x00, 0x90, 0xa9, 0x0f, 0x54, 0x8d, 0x96, 0xb3, 0x7f, + 0x60, 0xcf, 0x2e, 0x43, 0xc7, 0xfc, 0xf1, 0xc4, 0xbd, 0x07, 0x1a, 0x7f, 0xca, 0x91, 0xdf, 0xfe, + 0x49, 0xd3, 0x7b, 0xb4, 0xbc, 0xfb, 0x97, 0x99, 0x3f, 0x07, 0xdb, 0xc3, 0x08, 0xb6, 0x77, 0xa0, + 0x69, 0x97, 0x5f, 0xd3, 0xd5, 0x81, 0x44, 0x7c, 0x9c, 0xe7, 0x02, 0xf2, 0xe3, 0x88, 0x4d, 0x42, + 0xad, 0x0a, 0x00, 0x3f, 0xf5, 0x81, 0xaa, 0xe9, 0x8f, 0xaa, 0xaf, 0xb0, 0xcb, 0xe5, 0xcc, 0xaa, + 0xd5, 0xa1, 0xe3, 0xa7, 0x32, 0x2e, 0xf1, 0x3d, 0xdb, 0x19, 0xcd, 0x1a, 0x02, 0xcf, 0x1e, 0x39, + 0xf5, 0xc0, 0x70, 0xe1, 0xd0, 0xf1, 0x56, 0xc4, 0x13, 0xf7, 0xf0, 0xd2, 0xbe, 0xa7, 0x51, 0x53, + 0x59, 0x9e, 0xb5, 0x86, 0xeb, 0x97, 0xba, 0x0c, 0x3d, 0x26, 0x97, 0x91, 0xa9, 0x0b, 0x30, 0x82, + 0xe9, 0x01, 0xa0, 0x4c, 0x00, 0xda, 0x5a, 0x5f, 0xb7, 0xfa, 0xd9, 0xdf, 0xe8, 0xc6, 0x7f, 0x6b, + 0xf8, 0x0e, 0x82, 0xed, 0x61, 0xd5, 0xef, 0x39, 0xf2, 0xdb, 0xf7, 0xf0, 0xf4, 0x4f, 0xdf, 0x7a, + 0x60, 0x38, 0xd0, 0xd9, 0xdd, 0x8f, 0x27, 0x5e, 0x38, 0x96, 0x76, 0x35, 0x60, 0x3c, 0x71, 0x0f, + 0x47, 0x4e, 0xbe, 0x0b, 0xc9, 0x5b, 0x82, 0x93, 0xaf, 0x3c, 0x9f, 0xb5, 0x8e, 0x58, 0xef, 0x00, + 0xaf, 0x0b, 0xd0, 0xcf, 0x47, 0x5d, 0x80, 0x11, 0xac, 0xb8, 0x19, 0x48, 0xa2, 0x3e, 0x48, 0x35, + 0x63, 0xca, 0x56, 0xdd, 0x99, 0x3c, 0xb9, 0x7d, 0x8b, 0xb1, 0x8b, 0x7c, 0x00, 0xb4, 0x9c, 0xd6, + 0xd6, 0xc5, 0x3f, 0xdb, 0x19, 0xc5, 0xd9, 0xce, 0x28, 0x00, 0x60, 0xdb, 0xa6, 0x5a, 0x74, 0x5e, + 0xed, 0xcf, 0xfa, 0x9a, 0x60, 0x7b, 0x07, 0x8e, 0x1e, 0xd8, 0x8b, 0x6d, 0x9b, 0x92, 0x37, 0x20, + 0x65, 0xbb, 0x7f, 0x60, 0xb0, 0x77, 0x80, 0x97, 0x08, 0xeb, 0x23, 0x51, 0x17, 0x60, 0x04, 0x2b, + 0x86, 0x00, 0x7e, 0xea, 0x83, 0x54, 0x13, 0xeb, 0xfd, 0x50, 0xf5, 0xeb, 0x2f, 0xed, 0x7b, 0xda, + 0xf0, 0x9f, 0x79, 0xf6, 0xfc, 0xdf, 0x72, 0x7e, 0x8d, 0x96, 0xc6, 0x0f, 0x24, 0x7b, 0x01, 0xa9, + 0x46, 0xff, 0xf2, 0xb3, 0xd9, 0x6b, 0x8f, 0x7d, 0xf0, 0x61, 0xd6, 0xef, 0x61, 0xe9, 0x05, 0x42, + 0x11, 0x3f, 0x75, 0x0d, 0xf9, 0xb2, 0x22, 0x00, 0x6c, 0xbd, 0x3f, 0xd5, 0xed, 0xc1, 0x8f, 0x32, + 0x7e, 0x6d, 0xdb, 0xa6, 0x75, 0x86, 0xce, 0xfa, 0x03, 0xc9, 0x06, 0x7a, 0x6b, 0xd8, 0xdc, 0x35, + 0xf9, 0xa9, 0x00, 0x48, 0xf5, 0x02, 0x54, 0x8f, 0x3f, 0xf6, 0x91, 0x96, 0xb7, 0x64, 0xe9, 0xc9, + 0xd4, 0x05, 0xe4, 0xcb, 0x8a, 0x00, 0xf0, 0x51, 0x1f, 0xa4, 0x1a, 0xb5, 0x99, 0xf0, 0x7f, 0xff, + 0x97, 0x1d, 0x86, 0xff, 0xbc, 0x6b, 0x03, 0x31, 0xd3, 0x8f, 0x29, 0x76, 0xfb, 0xfe, 0xfe, 0xf6, + 0xd9, 0x8e, 0x61, 0x6a, 0x62, 0x92, 0x6f, 0x12, 0xd2, 0x4f, 0xa6, 0x2e, 0x20, 0x5f, 0x76, 0x78, + 0x38, 0x28, 0x19, 0xb5, 0xc6, 0x2f, 0x79, 0x4b, 0xd0, 0xb4, 0xcb, 0x4f, 0x5d, 0xa2, 0x2e, 0xb3, + 0x7b, 0x18, 0xdf, 0xd9, 0xf6, 0xb5, 0xac, 0xdf, 0xff, 0x31, 0x07, 0x80, 0x5e, 0xc2, 0x5f, 0x0a, + 0x74, 0xf5, 0x1c, 0x80, 0xda, 0xe4, 0x9f, 0xd1, 0x13, 0x7f, 0x56, 0x9a, 0xbd, 0x1a, 0x50, 0xf2, + 0x96, 0x64, 0x5d, 0xbf, 0xa0, 0xf6, 0xac, 0x42, 0xa6, 0x4a, 0xa6, 0x2e, 0x20, 0x5f, 0xae, 0xee, + 0x01, 0xa8, 0xfd, 0xe2, 0x9b, 0x15, 0x00, 0xa5, 0x4b, 0xb4, 0xad, 0xd7, 0xcf, 0xc7, 0xfc, 0x79, + 0x8b, 0xac, 0xf3, 0x00, 0x83, 0x3c, 0x0f, 0xa0, 0x93, 0x8f, 0xba, 0x80, 0x7c, 0xb9, 0x3a, 0x00, + 0x26, 0x3f, 0x9b, 0xc8, 0xf8, 0x35, 0xb3, 0x02, 0xa0, 0xae, 0x56, 0xd6, 0x7c, 0xd3, 0x8e, 0x5e, + 0xf3, 0x17, 0x01, 0x19, 0x3d, 0x91, 0xc9, 0x66, 0x48, 0xd4, 0x05, 0xe4, 0xcb, 0xd4, 0x00, 0xb0, + 0xfb, 0x65, 0x12, 0xb5, 0xc9, 0x2f, 0x33, 0x1b, 0x8d, 0xd9, 0xc3, 0x8b, 0xf9, 0xef, 0x2f, 0xf2, + 0x70, 0xc6, 0xee, 0x44, 0x58, 0xe8, 0xa6, 0xc6, 0xd5, 0x3d, 0x80, 0x4c, 0xb4, 0x2c, 0xa3, 0xcd, + 0x87, 0xd1, 0xfb, 0x08, 0xcc, 0x96, 0xcb, 0xee, 0xc3, 0x29, 0x3c, 0x09, 0x98, 0x17, 0x89, 0xba, + 0x80, 0x7c, 0x70, 0x00, 0xa4, 0x61, 0xd8, 0xa6, 0x9e, 0x19, 0x34, 0xed, 0xf2, 0x9b, 0x16, 0x32, + 0x99, 0x16, 0xff, 0xa8, 0xf5, 0x02, 0x78, 0x39, 0x70, 0x5e, 0x64, 0xea, 0x02, 0xf2, 0xc1, 0x01, + 0x40, 0x44, 0xcb, 0x5a, 0xfd, 0x5c, 0xe9, 0x5d, 0xb6, 0x5c, 0x58, 0xe4, 0xa1, 0xfe, 0x38, 0x44, + 0x26, 0x53, 0x17, 0x90, 0x0f, 0x0e, 0x00, 0x22, 0xdb, 0x36, 0xad, 0xcb, 0x79, 0x53, 0x4f, 0x35, + 0x35, 0x95, 0xe5, 0xf8, 0xcd, 0xcb, 0xfa, 0x42, 0x65, 0x45, 0x85, 0xb9, 0x43, 0x1e, 0x66, 0x5f, + 0xae, 0x0e, 0x80, 0x32, 0xe2, 0x5f, 0xfc, 0xa3, 0x07, 0xf6, 0xa2, 0x69, 0x57, 0xfe, 0xab, 0x0d, + 0x53, 0x1b, 0x94, 0x9a, 0x7d, 0x75, 0x81, 0xa5, 0x65, 0xfb, 0xcd, 0x6e, 0xd4, 0xb8, 0x3a, 0x00, + 0x3c, 0xc5, 0x45, 0xd4, 0x25, 0xe0, 0xe4, 0x2b, 0xcf, 0x6b, 0xba, 0x69, 0x27, 0x93, 0xba, 0x5a, + 0x19, 0x7d, 0x6d, 0xbf, 0xca, 0xeb, 0xaa, 0x45, 0x61, 0x11, 0xfd, 0xe7, 0xc0, 0x68, 0xb8, 0x3a, + 0x00, 0x96, 0x48, 0x4b, 0xa9, 0x4b, 0x00, 0x90, 0xbc, 0xe3, 0x30, 0xd7, 0x67, 0x0b, 0x48, 0xde, + 0x12, 0x1c, 0x3d, 0xb0, 0x17, 0x97, 0x83, 0x47, 0x35, 0x9d, 0xf9, 0x6f, 0x0d, 0xdf, 0xc9, 0xf8, + 0xb5, 0x15, 0x26, 0x5f, 0xf5, 0x70, 0x38, 0x99, 0xba, 0x80, 0x7c, 0x50, 0x3c, 0x1d, 0xd8, 0x36, + 0xbc, 0x52, 0xfa, 0x1b, 0x15, 0x8d, 0x7a, 0x42, 0x6f, 0x2e, 0x52, 0x4f, 0x17, 0xea, 0xe9, 0x8f, + 0x21, 0xd8, 0x1e, 0x46, 0xe7, 0xd5, 0x9b, 0xe8, 0xe9, 0x8f, 0xcd, 0xf9, 0x9e, 0x9a, 0xca, 0x72, + 0xd4, 0xd5, 0xca, 0x33, 0xcf, 0x1e, 0xcc, 0x85, 0xda, 0x1d, 0x88, 0x76, 0x09, 0x42, 0x41, 0xc9, + 0xd4, 0x05, 0xe4, 0xc3, 0xd5, 0x01, 0x50, 0x25, 0x57, 0x67, 0xfc, 0xda, 0xad, 0xe1, 0x3b, 0xa6, + 0x5f, 0x0e, 0x4c, 0xa7, 0xae, 0x56, 0x46, 0x5d, 0xad, 0xb1, 0x3b, 0x10, 0xcd, 0x0f, 0x92, 0xf9, + 0x78, 0x12, 0xd0, 0xbd, 0x5c, 0x1d, 0x00, 0x40, 0x32, 0x04, 0xd2, 0xdd, 0x15, 0xd8, 0xd3, 0x3f, + 0x48, 0x12, 0x00, 0x66, 0x50, 0xbb, 0x05, 0xb9, 0xb0, 0xc8, 0x43, 0x3e, 0x19, 0x6a, 0xb4, 0xd4, + 0xff, 0xe7, 0xb0, 0x86, 0x4d, 0x4f, 0x67, 0x1f, 0xbf, 0xda, 0x09, 0x41, 0x4d, 0x20, 0x14, 0x91, + 0x5b, 0x1a, 0xea, 0x63, 0xd4, 0xc7, 0xad, 0x07, 0x07, 0xc0, 0xea, 0x47, 0xd2, 0x06, 0x40, 0xe7, + 0xd5, 0x9b, 0xa6, 0xae, 0xd8, 0xb3, 0xd2, 0xf9, 0xae, 0xcc, 0x43, 0x9a, 0x2a, 0xf9, 0x11, 0xea, + 0xf2, 0xf2, 0x92, 0xda, 0xdd, 0x78, 0x78, 0x70, 0x08, 0x1f, 0x8f, 0x8c, 0x3e, 0xb0, 0xbc, 0xbb, + 0xa6, 0xb2, 0x0c, 0x35, 0x95, 0x65, 0xd8, 0xb6, 0x71, 0xed, 0x9c, 0x7f, 0x1f, 0xbf, 0xfb, 0x29, + 0x7a, 0x06, 0x86, 0xd0, 0x19, 0x9e, 0xbb, 0xd3, 0x92, 0x57, 0x2a, 0x45, 0x95, 0x5c, 0x8d, 0xad, + 0x0d, 0xdf, 0xcc, 0x65, 0x7d, 0x84, 0x0c, 0x20, 0x46, 0xfd, 0x59, 0xe8, 0x61, 0x76, 0x00, 0xc4, + 0xa9, 0x0f, 0x30, 0x1b, 0xf9, 0xf1, 0xc7, 0xd2, 0x3e, 0x0e, 0xec, 0xec, 0xf9, 0xbf, 0x99, 0xf2, + 0xdc, 0x3f, 0x0a, 0x6a, 0x73, 0x1a, 0xf2, 0x3f, 0x3d, 0x46, 0x5d, 0x5e, 0xce, 0xa6, 0x26, 0x26, + 0xd1, 0x17, 0xbd, 0x81, 0xbe, 0xee, 0x1b, 0x69, 0xef, 0xe7, 0x90, 0x96, 0x2c, 0xc6, 0x0b, 0xdf, + 0xff, 0x67, 0x34, 0xed, 0xfc, 0x3a, 0x6a, 0x2a, 0xcb, 0x54, 0xdf, 0x6b, 0xfc, 0xee, 0xa7, 0x08, + 0xb6, 0x5f, 0xc2, 0x91, 0xdf, 0xfe, 0x09, 0xf1, 0xbb, 0x9f, 0x2a, 0x5b, 0xc4, 0x8f, 0xe3, 0xe3, + 0x91, 0x51, 0xfc, 0xeb, 0x73, 0xcf, 0x50, 0x1f, 0xaa, 0xe9, 0x4c, 0x0d, 0x80, 0x96, 0x86, 0xfa, + 0x68, 0x20, 0x14, 0xa1, 0x3e, 0x46, 0x55, 0x65, 0x15, 0xe5, 0xf0, 0x4a, 0xa5, 0x0f, 0xec, 0x0d, + 0x70, 0x6b, 0x78, 0x14, 0x3d, 0xfd, 0x31, 0xe1, 0xef, 0xa4, 0xeb, 0xe9, 0x8f, 0xa9, 0x4e, 0x00, + 0xae, 0x7e, 0x7c, 0x0d, 0x75, 0x89, 0x9a, 0x25, 0xe2, 0xe3, 0xe8, 0x0a, 0x5f, 0x54, 0x7d, 0x86, + 0x43, 0xdd, 0x9a, 0x6a, 0xfc, 0xf9, 0xc4, 0x8f, 0x50, 0xba, 0x64, 0xb1, 0xa6, 0xf7, 0x2c, 0x55, + 0xc2, 0xa2, 0xa6, 0xb2, 0x0c, 0x4f, 0xfd, 0xf4, 0xad, 0x99, 0x7f, 0x1f, 0x1b, 0x19, 0x45, 0xac, + 0x77, 0x00, 0xb2, 0x40, 0x9f, 0x8f, 0x1e, 0xae, 0xbe, 0x0c, 0x98, 0xb2, 0x76, 0x63, 0xfa, 0x5d, + 0x71, 0xb3, 0x6d, 0xdd, 0x2d, 0x82, 0x96, 0xd3, 0xed, 0x99, 0x8f, 0xdb, 0xb7, 0x5e, 0x88, 0x65, + 0xc0, 0x89, 0xf8, 0x38, 0x3a, 0xda, 0x42, 0xf8, 0xef, 0xe3, 0xbf, 0xce, 0xfa, 0x00, 0x97, 0x77, + 0xff, 0xf3, 0x39, 0xcd, 0x8d, 0x7f, 0xb6, 0x52, 0xef, 0x83, 0xaf, 0x19, 0x1b, 0xb9, 0xa3, 0xf5, + 0xe5, 0x32, 0xf5, 0x67, 0xa4, 0x97, 0x15, 0x01, 0x10, 0xa7, 0x3e, 0xc8, 0x6c, 0x6a, 0x7d, 0xe9, + 0xaf, 0xbf, 0x07, 0xdb, 0x3b, 0x32, 0x3e, 0xbc, 0x43, 0x04, 0xf1, 0xc4, 0x3d, 0xd5, 0x10, 0xab, + 0xdd, 0x68, 0xff, 0xed, 0xc0, 0xbb, 0x3a, 0x2e, 0xe2, 0xf7, 0x6f, 0xbd, 0x6d, 0xea, 0xb3, 0x0c, + 0xc7, 0xef, 0x7e, 0x8a, 0x37, 0x7f, 0xf7, 0x97, 0x07, 0xfe, 0x3d, 0x87, 0xcb, 0xa3, 0x32, 0xc9, + 0x87, 0x63, 0x00, 0x2b, 0x02, 0x20, 0x4a, 0x7d, 0x90, 0xd9, 0x78, 0xa5, 0xd2, 0xb4, 0x7b, 0xe3, + 0xc7, 0x13, 0xf7, 0xf0, 0xe6, 0xef, 0xda, 0x75, 0xbc, 0xa3, 0x3d, 0xa8, 0xd5, 0x5e, 0x56, 0x51, + 0xae, 0x7b, 0xd6, 0xdb, 0x0a, 0x89, 0xf8, 0x38, 0xde, 0x6b, 0x3d, 0x8d, 0x2b, 0xe1, 0x0b, 0x39, + 0xdd, 0xad, 0x98, 0xee, 0x61, 0x2a, 0x99, 0x8c, 0xdf, 0xfd, 0x14, 0xc1, 0x73, 0x97, 0xb0, 0xe5, + 0x99, 0x23, 0x33, 0xcf, 0x5f, 0x98, 0xcd, 0xce, 0x9f, 0x8f, 0x51, 0x5c, 0x7f, 0x15, 0x20, 0x65, + 0xf3, 0x8e, 0xad, 0x69, 0xcf, 0x32, 0x2d, 0xa7, 0xdb, 0xf1, 0xc2, 0xf7, 0x77, 0x09, 0xb7, 0xce, + 0xfe, 0xd6, 0xf0, 0x1d, 0xd5, 0xee, 0xff, 0x86, 0xaf, 0x6f, 0xa6, 0x2e, 0x31, 0xa3, 0xb1, 0x91, + 0x51, 0x9c, 0x6d, 0x3d, 0xad, 0xeb, 0x36, 0xe5, 0x9e, 0x81, 0x21, 0x6c, 0x79, 0xe6, 0x08, 0xea, + 0xd6, 0x54, 0x63, 0xdb, 0xa6, 0x5a, 0xd4, 0x54, 0x94, 0xe1, 0x2b, 0xb5, 0xf7, 0x1b, 0xf2, 0xb5, + 0xfe, 0x21, 0xc4, 0xef, 0x7e, 0x86, 0xce, 0xee, 0x3e, 0xd5, 0x67, 0x2d, 0xa4, 0xe6, 0x86, 0x9c, + 0xce, 0x8a, 0x00, 0x38, 0x0f, 0x1b, 0x6f, 0x0c, 0x9a, 0x92, 0xea, 0x05, 0xcc, 0x0f, 0x81, 0x78, + 0xe2, 0x1e, 0x0e, 0x1d, 0x3f, 0x65, 0xca, 0xed, 0xbb, 0x66, 0x4a, 0x3d, 0x27, 0x30, 0x9d, 0x2a, + 0xb9, 0xda, 0xb6, 0x4f, 0x03, 0xea, 0x8b, 0xde, 0xc0, 0xc5, 0xd0, 0x5f, 0xf3, 0xde, 0xa3, 0xa0, + 0x67, 0x60, 0x08, 0x3d, 0x1a, 0x7b, 0x02, 0xe9, 0xd8, 0x39, 0x20, 0x8d, 0xc4, 0x93, 0x80, 0xb3, + 0x64, 0xba, 0xf6, 0x9b, 0x5a, 0x9a, 0x2b, 0x8a, 0xb3, 0xe7, 0x2f, 0xe3, 0xec, 0xf9, 0xcb, 0x19, + 0xbf, 0xbe, 0x79, 0xc7, 0x37, 0xa8, 0x4b, 0x4c, 0x6b, 0x6c, 0x64, 0x14, 0x1d, 0x6d, 0x21, 0xf2, + 0x0d, 0x4a, 0xd6, 0xfa, 0xd6, 0xdb, 0x36, 0x20, 0x8d, 0x66, 0x45, 0x00, 0x74, 0x50, 0x1f, 0xa4, + 0x56, 0x85, 0x45, 0x1e, 0x7c, 0x35, 0x43, 0xe3, 0x78, 0xea, 0x27, 0xbf, 0x10, 0x62, 0x42, 0xf0, + 0xd6, 0xf0, 0x1d, 0x3c, 0xfb, 0xb3, 0x13, 0x19, 0xbf, 0xbe, 0xa1, 0x7e, 0xb3, 0x2d, 0xc7, 0xb6, + 0x89, 0xf8, 0x38, 0xce, 0xb6, 0x9e, 0xa6, 0x2e, 0x03, 0x65, 0x15, 0xe5, 0xd8, 0xda, 0xf0, 0x4d, + 0xea, 0x32, 0x2c, 0xc3, 0x57, 0x01, 0xe6, 0xc9, 0xd4, 0x40, 0xe2, 0x89, 0x7b, 0x78, 0xe2, 0x87, + 0x87, 0x6d, 0x1d, 0x02, 0xf1, 0xc4, 0x3d, 0x3c, 0xf5, 0x93, 0x9f, 0x67, 0xac, 0xd1, 0x2b, 0x95, + 0x66, 0x0c, 0x38, 0x6a, 0xd7, 0x2f, 0x75, 0x91, 0x9f, 0xf9, 0xcb, 0x2a, 0xca, 0xf1, 0xe4, 0xde, + 0x3d, 0x7a, 0x2e, 0x8d, 0xd6, 0x91, 0x16, 0x9e, 0x07, 0xd3, 0x03, 0xa0, 0xa5, 0xa1, 0x3e, 0x4a, + 0x7d, 0x90, 0xb9, 0xfa, 0xf6, 0x0f, 0x76, 0xa7, 0xfd, 0x25, 0xe8, 0xe9, 0x8f, 0xe1, 0xd0, 0xf1, + 0x53, 0xd4, 0xe5, 0x65, 0xf4, 0xec, 0xcf, 0x4e, 0xa8, 0xde, 0xf8, 0xf3, 0xed, 0x1f, 0x34, 0xda, + 0xf6, 0xba, 0x3f, 0xf5, 0xe3, 0xc9, 0xf2, 0x68, 0xfc, 0x80, 0xc0, 0x1b, 0x83, 0x5a, 0x35, 0x07, + 0x10, 0xa5, 0x3e, 0xd0, 0x5c, 0x14, 0x16, 0x79, 0xf0, 0xe4, 0xde, 0x3d, 0x69, 0xbf, 0x16, 0x6c, + 0x0f, 0x63, 0xdf, 0xeb, 0x27, 0x72, 0x7c, 0x47, 0xf3, 0xed, 0x7b, 0xfd, 0x84, 0xea, 0xb8, 0xdf, + 0xbf, 0xbb, 0xc1, 0x71, 0x37, 0xfd, 0x18, 0xc5, 0x2b, 0x95, 0xe6, 0xd3, 0xf8, 0x85, 0xc6, 0x01, + 0x90, 0x41, 0x59, 0x45, 0x39, 0xfc, 0xbb, 0x1b, 0xd2, 0x7e, 0x2d, 0xd8, 0x1e, 0xc6, 0xb7, 0x9e, + 0xb3, 0xc7, 0x70, 0x20, 0x9e, 0xb8, 0x87, 0x2d, 0x4d, 0x87, 0x54, 0x17, 0xfc, 0x6c, 0xa8, 0xdf, + 0xec, 0x9a, 0x49, 0xad, 0x5c, 0x15, 0x16, 0x79, 0x6c, 0xdd, 0x33, 0x32, 0x9b, 0x55, 0x01, 0xd0, + 0x43, 0x7d, 0xa0, 0x7a, 0xac, 0xf5, 0xad, 0xcf, 0x18, 0x02, 0x9d, 0x57, 0x6f, 0xe2, 0x89, 0x1f, + 0x1e, 0xce, 0x7a, 0xaf, 0xbd, 0x99, 0x7a, 0xfa, 0x63, 0xd8, 0xd2, 0xf4, 0x63, 0xd5, 0x1a, 0xd6, + 0xfa, 0xd6, 0x0b, 0x31, 0xa9, 0x55, 0xb5, 0xda, 0xfa, 0xbb, 0x12, 0x53, 0x3d, 0x3d, 0x37, 0xf7, + 0x8c, 0xb8, 0x07, 0x90, 0x85, 0x5a, 0x08, 0x24, 0x1b, 0xe0, 0x21, 0x1c, 0x39, 0xf9, 0xae, 0xe5, + 0x75, 0x1d, 0x39, 0xf9, 0x2e, 0xb6, 0x34, 0x1d, 0x52, 0xbd, 0xd1, 0x47, 0xad, 0x76, 0xbb, 0xd9, + 0xec, 0xdf, 0x6a, 0xe9, 0xd5, 0x09, 0x83, 0x1b, 0x7f, 0xd4, 0xb2, 0xc2, 0x0d, 0xf6, 0x90, 0x55, + 0x3f, 0x28, 0x10, 0x8a, 0x4c, 0x53, 0x1f, 0x6c, 0x3e, 0xb2, 0x2d, 0x50, 0xa9, 0xab, 0x95, 0x71, + 0xf4, 0xc0, 0x5e, 0xd3, 0x1f, 0xc3, 0xd5, 0x79, 0xf5, 0x26, 0xf6, 0xbd, 0x7e, 0x42, 0xb5, 0xe1, + 0x03, 0xc9, 0x35, 0x0d, 0x1b, 0xea, 0xc5, 0x5b, 0xcc, 0xd2, 0x17, 0xbd, 0x81, 0xae, 0xf0, 0x45, + 0xd5, 0x27, 0x37, 0x1b, 0xe1, 0x3b, 0x7b, 0xf7, 0x18, 0x19, 0x38, 0xaf, 0xb5, 0x34, 0xd4, 0xbf, + 0x6a, 0xf6, 0x67, 0x63, 0x06, 0x2b, 0x03, 0xa0, 0x1b, 0x82, 0x3f, 0x4d, 0x75, 0x6c, 0x64, 0x14, + 0xef, 0xbf, 0x73, 0x26, 0xeb, 0x63, 0xc5, 0x03, 0x7b, 0x76, 0x19, 0xbe, 0x99, 0x48, 0xb0, 0xbd, + 0x03, 0xff, 0xf5, 0xa7, 0xec, 0x0b, 0x92, 0x0a, 0x8b, 0x3c, 0xd8, 0xb1, 0xbb, 0x41, 0xf8, 0xdb, + 0x58, 0xc7, 0x46, 0x46, 0x71, 0xfd, 0x52, 0x17, 0x06, 0x7b, 0x07, 0x0c, 0xbf, 0x3c, 0xe8, 0xdf, + 0xdd, 0x60, 0xf4, 0x9c, 0x08, 0x07, 0x40, 0x36, 0x81, 0x50, 0xe4, 0x38, 0x80, 0xfd, 0xd4, 0x07, + 0x9c, 0xaf, 0xa9, 0x89, 0x49, 0x84, 0xdb, 0x42, 0x88, 0xf5, 0x0e, 0xa8, 0x7e, 0x5f, 0x4d, 0x65, + 0x39, 0x9e, 0xdc, 0xfe, 0x35, 0xdd, 0x4f, 0xeb, 0x01, 0x92, 0x67, 0xfb, 0xb3, 0xe7, 0x2f, 0x6b, + 0xbe, 0x2b, 0xb1, 0x4a, 0xae, 0x86, 0x7f, 0x77, 0x83, 0xe3, 0xd6, 0xb0, 0xc7, 0x7a, 0x07, 0x10, + 0xfb, 0xe0, 0x43, 0x43, 0xc2, 0xc0, 0x84, 0xc6, 0x0f, 0x70, 0x00, 0x64, 0x17, 0x08, 0x45, 0x9a, + 0x01, 0xb4, 0x52, 0x1f, 0xb0, 0x51, 0x62, 0xbd, 0x03, 0xb8, 0x18, 0x0a, 0x6b, 0xee, 0xaa, 0x6e, + 0xdb, 0xb4, 0x0e, 0x75, 0xb5, 0xf2, 0xcc, 0xce, 0xbe, 0xe9, 0x74, 0x5e, 0xbd, 0x89, 0x78, 0xe2, + 0x1e, 0x7a, 0xfa, 0x63, 0x39, 0x2d, 0x3d, 0x4e, 0xad, 0x60, 0x14, 0xb1, 0xcb, 0x9f, 0xab, 0xb1, + 0x91, 0x51, 0xf4, 0x75, 0xdf, 0x40, 0xac, 0xf7, 0xc3, 0x9c, 0x87, 0x09, 0x26, 0x35, 0x7e, 0x40, + 0xe0, 0x00, 0xf8, 0x7f, 0x93, 0x99, 0x6d, 0x99, 0x94, 0x53, 0x36, 0xc5, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +unsigned int resources_FreeRDP_ico_len = 7240; diff --git a/libfreerdp/emu/scard/FreeRDP.ico.h b/libfreerdp/emu/scard/FreeRDP.ico.h new file mode 100644 index 000000000..ce6f97c1b --- /dev/null +++ b/libfreerdp/emu/scard/FreeRDP.ico.h @@ -0,0 +1,15 @@ +/* Generated from resources/FreeRDP.ico with xxd -i + * + * The icon must have the following properties: + * - resolution of 256x256 + * - no alpha + * - no alternate resolutions + */ + +#ifndef FREERDP_ICO_INTERNAL_ +#define FREERDP_ICO_INTERNAL_ + +extern const unsigned char resources_FreeRDP_ico[]; +extern const unsigned int resources_FreeRDP_ico_len; + +#endif /* FREERDP_ICO_INTERNAL_ */ diff --git a/libfreerdp/emu/scard/smartcard_emulate.c b/libfreerdp/emu/scard/smartcard_emulate.c new file mode 100644 index 000000000..d36c8598b --- /dev/null +++ b/libfreerdp/emu/scard/smartcard_emulate.c @@ -0,0 +1,2709 @@ +/** + * WinPR: Windows Portable Runtime + * Smart Card API emulation + * + * Copyright 2021 Armin Novak + * Copyright 2021 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "FreeRDP.ico.h" + +#include "smartcard_virtual_gids.h" + +#define MAX_CACHE_ITEM_SIZE 4096 +#define MAX_CACHE_ITEM_VALUES 4096 + +static CHAR g_ReaderNameA[] = { 'F', 'r', 'e', 'e', 'R', 'D', 'P', ' ', 'E', + 'm', 'u', 'l', 'a', 't', 'o', 'r', '\0', '\0' }; +static WCHAR g_ReaderNameW[] = { 'F', 'r', 'e', 'e', 'R', 'D', 'P', ' ', 'E', + 'm', 'u', 'l', 'a', 't', 'o', 'r', '\0', '\0' }; + +struct smartcard_emulation_context +{ + const rdpSettings* settings; + DWORD log_default_level; + wLog* log; + wHashTable* contexts; + wHashTable* handles; +}; + +#define MAX_EMULATED_READERS 1 +typedef struct +{ + ULONG readerState; + SCARD_READERSTATEA readerStateA[MAX_EMULATED_READERS]; + SCARD_READERSTATEW readerStateW[MAX_EMULATED_READERS]; + wHashTable* cards; + wArrayList* strings; + wHashTable* cacheA; + wHashTable* cacheW; + BOOL canceled; +} SCardContext; + +typedef struct +{ + union + { + void* pv; + CHAR* pc; + WCHAR* pw; + } szReader; + BOOL unicode; + BOOL transaction; + DWORD transmitcount; + DWORD dwShareMode; + DWORD dwActiveProtocol; + SCARDCONTEXT hContext; + SCARDHANDLE card; + vgidsContext* vgids; + size_t referencecount; +} SCardHandle; + +typedef struct +{ + DWORD freshness; + DWORD size; + char data[MAX_CACHE_ITEM_SIZE]; +} SCardCacheItem; + +static SCardHandle* find_reader(SmartcardEmulationContext* smartcard, const void* szReader, + BOOL unicode); + +static BOOL scard_status_transition(SCardContext* context) +{ + WINPR_ASSERT(context); + + switch (context->readerState) + { + default: + case 0: + { + SCARD_READERSTATEA* reader = &context->readerStateA[0]; + reader->szReader = g_ReaderNameA; + reader->dwEventState = SCARD_STATE_PRESENT; + reader->cbAtr = 5; + reader->rgbAtr[0] = 0x3B; + reader->rgbAtr[1] = 0x80; + reader->rgbAtr[2] = 0x80; + reader->rgbAtr[3] = 0x01; + reader->rgbAtr[4] = 0x01; + } + { + SCARD_READERSTATEW* reader = &context->readerStateW[0]; + reader->szReader = g_ReaderNameW; + reader->dwEventState = SCARD_STATE_PRESENT; + reader->cbAtr = 5; + reader->rgbAtr[0] = 0x3B; + reader->rgbAtr[1] = 0x80; + reader->rgbAtr[2] = 0x80; + reader->rgbAtr[3] = 0x01; + reader->rgbAtr[4] = 0x01; + } + context->readerState = 42; + break; + } + + return TRUE; +} + +static UINT32 scard_copy_strings(SCardContext* ctx, void* dst, UINT32 dstSize, const void* src, + UINT32 srcSize) +{ + WINPR_ASSERT(ctx); + WINPR_ASSERT(dst); + + if (dstSize == SCARD_AUTOALLOCATE) + { + void* tmp = malloc(srcSize); + memcpy(tmp, src, srcSize); + ArrayList_Append(ctx->strings, tmp); + *((void**)dst) = tmp; + return srcSize; + } + else + { + UINT32 min = MIN(dstSize, srcSize); + memcpy(dst, src, min); + return min; + } +} + +static void scard_context_free(void* context) +{ + SCardContext* ctx = context; + if (ctx) + { + HashTable_Free(ctx->cards); + ArrayList_Free(ctx->strings); + HashTable_Free(ctx->cacheA); + HashTable_Free(ctx->cacheW); + } + free(ctx); +} + +static BOOL char_compare(const void* a, const void* b) +{ + const CHAR* wa = a; + const CHAR* wb = b; + + if (!a && !b) + return TRUE; + if (!a || !b) + return FALSE; + return strcmp(wa, wb) == 0; +} + +static BOOL wchar_compare(const void* a, const void* b) +{ + const WCHAR* wa = a; + const WCHAR* wb = b; + + if (!a && !b) + return TRUE; + if (!a || !b) + return FALSE; + return _wcscmp(wa, wb) == 0; +} + +static SCardContext* scard_context_new(void) +{ + SCardContext* ctx = calloc(1, sizeof(SCardContext)); + if (!ctx) + return NULL; + + ctx->strings = ArrayList_New(FALSE); + if (!ctx->strings) + goto fail; + else + { + wObject* obj = ArrayList_Object(ctx->strings); + WINPR_ASSERT(obj); + obj->fnObjectFree = free; + } + + ctx->cacheA = HashTable_New(FALSE); + if (!ctx->cacheA) + goto fail; + else + { + wObject* key = HashTable_KeyObject(ctx->cacheA); + wObject* val = HashTable_ValueObject(ctx->cacheA); + WINPR_ASSERT(key); + WINPR_ASSERT(val); + + key->fnObjectEquals = char_compare; + key->fnObjectNew = (OBJECT_NEW_FN)_strdup; + key->fnObjectFree = free; + + val->fnObjectFree = free; + } + + ctx->cacheW = HashTable_New(FALSE); + if (!ctx->cacheW) + goto fail; + else + { + wObject* key = HashTable_KeyObject(ctx->cacheW); + wObject* val = HashTable_ValueObject(ctx->cacheW); + WINPR_ASSERT(key); + WINPR_ASSERT(val); + + key->fnObjectEquals = wchar_compare; + key->fnObjectNew = (OBJECT_NEW_FN)_wcsdup; + key->fnObjectFree = free; + + val->fnObjectFree = free; + } + + scard_status_transition(ctx); + return ctx; +fail: + scard_context_free(ctx); + return NULL; +} + +static void scard_handle_free(void* handle) +{ + SCardHandle* hdl = handle; + if (hdl) + { + free(hdl->szReader.pv); + vgids_free(hdl->vgids); + } + free(hdl); +} + +static SCardHandle* scard_handle_new(SmartcardEmulationContext* smartcard, SCARDCONTEXT context, + const void* name, BOOL unicode) +{ + SCardHandle* hdl; + + WINPR_ASSERT(smartcard); + + hdl = calloc(1, sizeof(SCardHandle)); + if (!hdl) + goto fail; + + /* ATTENTION: Do not use _strdup or _wcsdup! + * These strings are required to be double NULL terminated! + */ + if (unicode) + { + size_t s = _wcslen(name); + + hdl->szReader.pw = calloc(s + 2, sizeof(WCHAR)); + if (!hdl->szReader.pw) + goto fail; + memcpy(hdl->szReader.pv, name, s * sizeof(WCHAR)); + } + else + { + size_t s = strlen(name); + + hdl->szReader.pw = calloc(s + 2, sizeof(CHAR)); + if (!hdl->szReader.pw) + goto fail; + memcpy(hdl->szReader.pv, name, s * sizeof(CHAR)); + } + + if (!hdl->szReader.pv) + goto fail; + + hdl->vgids = vgids_new(); + if (!hdl->vgids) + goto fail; + + { + const char* pem = + freerdp_settings_get_string(smartcard->settings, FreeRDP_SmartcardCertificate); + const char* key = + freerdp_settings_get_string(smartcard->settings, FreeRDP_SmartcardPrivateKey); + const char* pin = NULL; + + if (freerdp_settings_get_bool(smartcard->settings, FreeRDP_PasswordIsSmartcardPin)) + pin = freerdp_settings_get_string(smartcard->settings, FreeRDP_Password); + + if (!vgids_init(hdl->vgids, pem, key, pin)) + goto fail; + } + + hdl->unicode = unicode; + hdl->hContext = context; + return hdl; + +fail: + scard_handle_free(hdl); + return NULL; +} + +static LONG scard_handle_valid(SmartcardEmulationContext* smartcard, SCARDHANDLE handle) +{ + SCardHandle* ctx; + + WINPR_ASSERT(smartcard); + + ctx = HashTable_GetItemValue(smartcard->handles, (const void*)handle); + if (!ctx) + return SCARD_E_INVALID_HANDLE; + + return SCARD_S_SUCCESS; +} + +static LONG scard_reader_name_valid_a(SmartcardEmulationContext* smartcard, SCARDCONTEXT context, + const char* name) +{ + size_t x; + SCardContext* ctx; + + WINPR_ASSERT(smartcard); + ctx = HashTable_GetItemValue(smartcard->contexts, (const void*)context); + + WINPR_ASSERT(name); + WINPR_ASSERT(ctx); + + for (x = 0; x < MAX_EMULATED_READERS; x++) + { + const SCARD_READERSTATEA* reader = &ctx->readerStateA[x]; + if (strcmp(reader->szReader, name) == 0) + return SCARD_S_SUCCESS; + } + + return SCARD_E_UNKNOWN_READER; +} + +static LONG scard_reader_name_valid_w(SmartcardEmulationContext* smartcard, SCARDCONTEXT context, + const WCHAR* name) +{ + size_t x; + SCardContext* ctx; + + WINPR_ASSERT(smartcard); + ctx = HashTable_GetItemValue(smartcard->contexts, (const void*)context); + + WINPR_ASSERT(name); + WINPR_ASSERT(ctx); + + for (x = 0; x < MAX_EMULATED_READERS; x++) + { + const SCARD_READERSTATEW* reader = &ctx->readerStateW[x]; + if (_wcscmp(reader->szReader, name) == 0) + return SCARD_S_SUCCESS; + } + + return SCARD_E_UNKNOWN_READER; +} + +/** + * Standard Windows Smart Card API + */ + +LONG WINAPI Emulate_SCardEstablishContext(SmartcardEmulationContext* smartcard, DWORD dwScope, + LPCVOID pvReserved1, LPCVOID pvReserved2, + LPSCARDCONTEXT phContext) +{ + LONG status = SCARD_E_NO_MEMORY; + SCardContext* ctx; + + WINPR_ASSERT(smartcard); + + ctx = scard_context_new(); + + WINPR_UNUSED(pvReserved1); + WINPR_UNUSED(pvReserved2); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardEstablishContext { dwScope: %s (0x%08" PRIX32 ")", + SCardGetScopeString(dwScope), dwScope); + + if (ctx) + { + SCARDCONTEXT context = { 0 }; + + winpr_RAND((BYTE*)&context, sizeof(SCARDCONTEXT)); + if (HashTable_Insert(smartcard->contexts, (const void*)context, ctx)) + { + *phContext = context; + status = SCARD_S_SUCCESS; + } + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardEstablishContext } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + if (status != SCARD_S_SUCCESS) + scard_context_free(ctx); + return status; +} + +LONG WINAPI Emulate_SCardReleaseContext(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext) +{ + LONG status; + SCardContext* value; + + WINPR_ASSERT(smartcard); + + value = HashTable_GetItemValue(smartcard->contexts, (const void*)hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardReleaseContext { hContext: %p", + (void*)hContext); + + if (value) + HashTable_Remove(smartcard->contexts, (const void*)hContext); + + status = SCARD_S_SUCCESS; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardReleaseContext } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardIsValidContext(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext) +{ + LONG status; + + WINPR_ASSERT(smartcard); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardIsValidContext { hContext: %p", + (void*)hContext); + + status = HashTable_Contains(smartcard->contexts, (const void*)hContext) + ? SCARD_S_SUCCESS + : SCARD_E_INVALID_HANDLE; + if (status == SCARD_S_SUCCESS) + { + SCardContext* value = HashTable_GetItemValue(smartcard->contexts, (const void*)hContext); + WINPR_ASSERT(value); /* Must be valid after Emulate_SCardIsValidContext */ + value->canceled = FALSE; + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardIsValidContext } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardListReaderGroupsA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPSTR mszGroups, + LPDWORD pcchGroups) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardListReaderGroupsA { hContext: %p", (void*)hContext); + + WINPR_UNUSED(mszGroups); + WINPR_UNUSED(pcchGroups); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardListReaderGroupsA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardListReaderGroupsW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPWSTR mszGroups, + LPDWORD pcchGroups) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardListReaderGroupsW { hContext: %p", (void*)hContext); + + WINPR_UNUSED(mszGroups); + WINPR_UNUSED(pcchGroups); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardListReaderGroupsW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardListReadersA(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, + LPCSTR mszGroups, LPSTR mszReaders, LPDWORD pcchReaders) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + if (!pcchReaders) + status = SCARD_E_INVALID_PARAMETER; + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardListReadersA { hContext: %p", + (void*)hContext); + + WINPR_UNUSED(mszGroups); /* Not required */ + + if (SCARD_S_SUCCESS == status) + { + SCardContext* value = + (SCardContext*)HashTable_GetItemValue(smartcard->contexts, (const void*)hContext); + WINPR_ASSERT(value); /* Must be valid after Emulate_SCardIsValidContext */ + + // TODO: If emulator not ready return SCARD_E_NO_READERS_AVAILABLE + + // TODO: argument mszGrous + + /* Return length only */ + if (!mszReaders) + *pcchReaders = ARRAYSIZE(g_ReaderNameA); + else + { + *pcchReaders = scard_copy_strings(value, mszReaders, *pcchReaders, g_ReaderNameA, + sizeof(g_ReaderNameA)); + } + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardListReadersA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardListReadersW(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, + LPCWSTR mszGroups, LPWSTR mszReaders, LPDWORD pcchReaders) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + if (!pcchReaders) + status = SCARD_E_INVALID_PARAMETER; + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardListReadersW { hContext: %p", + (void*)hContext); + + WINPR_UNUSED(mszGroups); /* Not required */ + + if (SCARD_S_SUCCESS == status) + { + SCardContext* value = HashTable_GetItemValue(smartcard->contexts, (const void*)hContext); + WINPR_ASSERT(value); /* Must be valid after Emulate_SCardIsValidContext */ + + // TODO: If emulator not ready return SCARD_E_NO_READERS_AVAILABLE + + // TODO: argument mszGrous + + /* Return length only */ + if (!mszReaders) + *pcchReaders = ARRAYSIZE(g_ReaderNameW); + else + { + *pcchReaders = scard_copy_strings(value, mszReaders, *pcchReaders, g_ReaderNameW, + sizeof(g_ReaderNameW)) / + sizeof(WCHAR); + } + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardListReadersW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardListCardsA(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, + LPCBYTE pbAtr, LPCGUID rgquidInterfaces, + DWORD cguidInterfaceCount, CHAR* mszCards, LPDWORD pcchCards) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardListCardsA { hContext: %p", + (void*)hContext); + + WINPR_UNUSED(pbAtr); + WINPR_UNUSED(rgquidInterfaces); + WINPR_UNUSED(cguidInterfaceCount); + WINPR_UNUSED(mszCards); + WINPR_UNUSED(pcchCards); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardListCardsA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardListCardsW(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, + LPCBYTE pbAtr, LPCGUID rgquidInterfaces, + DWORD cguidInterfaceCount, WCHAR* mszCards, LPDWORD pcchCards) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardListCardsW { hContext: %p", + (void*)hContext); + + WINPR_UNUSED(pbAtr); + WINPR_UNUSED(rgquidInterfaces); + WINPR_UNUSED(cguidInterfaceCount); + WINPR_UNUSED(mszCards); + WINPR_UNUSED(pcchCards); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardListCardsW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardListInterfacesA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szCard, + LPGUID pguidInterfaces, LPDWORD pcguidInterfaces) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardListInterfacesA { hContext: %p", + (void*)hContext); + + WINPR_UNUSED(szCard); + WINPR_UNUSED(pguidInterfaces); + WINPR_UNUSED(pcguidInterfaces); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardListInterfacesA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardListInterfacesW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szCard, + LPGUID pguidInterfaces, LPDWORD pcguidInterfaces) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardListInterfacesW { hContext: %p", + (void*)hContext); + + WINPR_UNUSED(szCard); + WINPR_UNUSED(pguidInterfaces); + WINPR_UNUSED(pcguidInterfaces); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardListInterfacesW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardGetProviderIdA(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, + LPCSTR szCard, LPGUID pguidProviderId) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardGetProviderIdA { hContext: %p", + (void*)hContext); + + WINPR_UNUSED(szCard); + WINPR_UNUSED(pguidProviderId); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardGetProviderIdA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardGetProviderIdW(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, + LPCWSTR szCard, LPGUID pguidProviderId) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardGetProviderIdW { hContext: %p", + (void*)hContext); + + WINPR_UNUSED(szCard); + WINPR_UNUSED(pguidProviderId); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardGetProviderIdW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardGetCardTypeProviderNameA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szCardName, + DWORD dwProviderId, CHAR* szProvider, + LPDWORD pcchProvider) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardGetCardTypeProviderNameA { hContext: %p", (void*)hContext); + + WINPR_UNUSED(szCardName); + WINPR_UNUSED(dwProviderId); + WINPR_UNUSED(szProvider); + WINPR_UNUSED(pcchProvider); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardGetCardTypeProviderNameA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardGetCardTypeProviderNameW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szCardName, + DWORD dwProviderId, WCHAR* szProvider, + LPDWORD pcchProvider) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardGetCardTypeProviderNameW { hContext: %p", (void*)hContext); + + WINPR_UNUSED(szCardName); + WINPR_UNUSED(dwProviderId); + WINPR_UNUSED(szProvider); + WINPR_UNUSED(pcchProvider); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardGetCardTypeProviderNameW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardIntroduceReaderGroupA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szGroupName) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardIntroduceReaderGroupA { hContext: %p", (void*)hContext); + + WINPR_UNUSED(szGroupName); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardIntroduceReaderGroupA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardIntroduceReaderGroupW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szGroupName) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardIntroduceReaderGroupW { hContext: %p", (void*)hContext); + + WINPR_UNUSED(szGroupName); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardIntroduceReaderGroupW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardForgetReaderGroupA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szGroupName) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardForgetReaderGroupA { hContext: %p", (void*)hContext); + + WINPR_UNUSED(szGroupName); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardForgetReaderGroupA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardForgetReaderGroupW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szGroupName) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardForgetReaderGroupW { hContext: %p", (void*)hContext); + + WINPR_UNUSED(szGroupName); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardForgetReaderGroupW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardIntroduceReaderA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szReaderName, + LPCSTR szDeviceName) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + if (status == SCARD_S_SUCCESS) + status = scard_reader_name_valid_a(smartcard, hContext, szReaderName); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardIntroduceReaderA { hContext: %p", + (void*)hContext); + + WINPR_UNUSED(szDeviceName); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardIntroduceReaderA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardIntroduceReaderW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPCWSTR szDeviceName) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + if (status == SCARD_S_SUCCESS) + status = scard_reader_name_valid_w(smartcard, hContext, szReaderName); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardIntroduceReaderW { hContext: %p", + (void*)hContext); + + WINPR_UNUSED(szDeviceName); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardIntroduceReaderW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardForgetReaderA(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, + LPCSTR szReaderName) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + if (status == SCARD_S_SUCCESS) + status = scard_reader_name_valid_a(smartcard, hContext, szReaderName); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardForgetReaderA { hContext: %p", + (void*)hContext); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardForgetReaderA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardForgetReaderW(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, + LPCWSTR szReaderName) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + if (status == SCARD_S_SUCCESS) + status = scard_reader_name_valid_w(smartcard, hContext, szReaderName); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardForgetReaderW { hContext: %p", + (void*)hContext); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardForgetReaderW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardAddReaderToGroupA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szReaderName, + LPCSTR szGroupName) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + if (status == SCARD_S_SUCCESS) + status = scard_reader_name_valid_a(smartcard, hContext, szReaderName); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardAddReaderToGroupA { hContext: %p", (void*)hContext); + + WINPR_UNUSED(szGroupName); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardAddReaderToGroupA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardAddReaderToGroupW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPCWSTR szGroupName) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + if (status == SCARD_S_SUCCESS) + status = scard_reader_name_valid_w(smartcard, hContext, szReaderName); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardAddReaderToGroupW { hContext: %p", (void*)hContext); + + WINPR_UNUSED(szGroupName); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardAddReaderToGroupW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardRemoveReaderFromGroupA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szReaderName, + LPCSTR szGroupName) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + if (status == SCARD_S_SUCCESS) + status = scard_reader_name_valid_a(smartcard, hContext, szReaderName); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardRemoveReaderFromGroupA { hContext: %p", (void*)hContext); + + WINPR_UNUSED(szGroupName); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardRemoveReaderFromGroupA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardRemoveReaderFromGroupW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPCWSTR szGroupName) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + if (status == SCARD_S_SUCCESS) + status = scard_reader_name_valid_w(smartcard, hContext, szReaderName); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardRemoveReaderFromGroupW { hContext: %p", (void*)hContext); + + WINPR_UNUSED(szGroupName); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardRemoveReaderFromGroupW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardIntroduceCardTypeA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szCardName, + LPCGUID pguidPrimaryProvider, LPCGUID rgguidInterfaces, + DWORD dwInterfaceCount, LPCBYTE pbAtr, + LPCBYTE pbAtrMask, DWORD cbAtrLen) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardIntroduceCardTypeA { hContext: %p", (void*)hContext); + + WINPR_UNUSED(szCardName); + WINPR_UNUSED(pguidPrimaryProvider); + WINPR_UNUSED(rgguidInterfaces); + WINPR_UNUSED(dwInterfaceCount); + WINPR_UNUSED(pbAtr); + WINPR_UNUSED(pbAtrMask); + WINPR_UNUSED(cbAtrLen); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardIntroduceCardTypeA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardIntroduceCardTypeW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szCardName, + LPCGUID pguidPrimaryProvider, LPCGUID rgguidInterfaces, + DWORD dwInterfaceCount, LPCBYTE pbAtr, + LPCBYTE pbAtrMask, DWORD cbAtrLen) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardIntroduceCardTypeW { hContext: %p", (void*)hContext); + + WINPR_UNUSED(szCardName); + WINPR_UNUSED(pguidPrimaryProvider); + WINPR_UNUSED(rgguidInterfaces); + WINPR_UNUSED(dwInterfaceCount); + WINPR_UNUSED(pbAtr); + WINPR_UNUSED(pbAtrMask); + WINPR_UNUSED(cbAtrLen); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardIntroduceCardTypeW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardSetCardTypeProviderNameA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szCardName, + DWORD dwProviderId, LPCSTR szProvider) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardSetCardTypeProviderNameA { hContext: %p", (void*)hContext); + + WINPR_UNUSED(szCardName); + WINPR_UNUSED(dwProviderId); + WINPR_UNUSED(szProvider); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardSetCardTypeProviderNameA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardSetCardTypeProviderNameW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szCardName, + DWORD dwProviderId, LPCWSTR szProvider) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardSetCardTypeProviderNameA { hContext: %p", (void*)hContext); + + WINPR_UNUSED(szCardName); + WINPR_UNUSED(dwProviderId); + WINPR_UNUSED(szProvider); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardSetCardTypeProviderNameW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardForgetCardTypeA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szCardName) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardForgetCardTypeA { hContext: %p", + (void*)hContext); + + WINPR_UNUSED(szCardName); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardForgetCardTypeA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardForgetCardTypeW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szCardName) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardForgetCardTypeW { hContext: %p", + (void*)hContext); + + WINPR_UNUSED(szCardName); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardForgetCardTypeW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardFreeMemory(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, + LPVOID pvMem) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardFreeMemory { hContext: %p", + (void*)hContext); + + if (status == SCARD_S_SUCCESS) + { + SCardContext* value = HashTable_GetItemValue(smartcard->contexts, (const void*)hContext); + WINPR_ASSERT(value); /* Must be valid after Emulate_SCardIsValidContext */ + + ArrayList_Remove(value->strings, pvMem); + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardFreeMemory } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +HANDLE WINAPI Emulate_SCardAccessStartedEvent(SmartcardEmulationContext* smartcard) +{ + HANDLE hEvent; + + WINPR_ASSERT(smartcard); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardAccessStartedEvent {"); + + /* Not required, return random */ + winpr_RAND((BYTE*)&hEvent, sizeof(hEvent)); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardAccessStartedEvent } hEvent: %p", + hEvent); + + return hEvent; +} + +void WINAPI Emulate_SCardReleaseStartedEvent(SmartcardEmulationContext* smartcard) +{ + WINPR_ASSERT(smartcard); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardReleaseStartedEvent {"); + + /* Not required, return not supported */ + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardReleaseStartedEvent }"); +} + +LONG WINAPI Emulate_SCardLocateCardsA(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, + LPCSTR mszCards, LPSCARD_READERSTATEA rgReaderStates, + DWORD cReaders) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardLocateCardsA { hContext: %p", + (void*)hContext); + + WINPR_UNUSED(mszCards); + WINPR_UNUSED(rgReaderStates); + WINPR_UNUSED(cReaders); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardLocateCardsA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardLocateCardsW(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, + LPCWSTR mszCards, LPSCARD_READERSTATEW rgReaderStates, + DWORD cReaders) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardLocateCardsW { hContext: %p", + (void*)hContext); + + WINPR_UNUSED(mszCards); + WINPR_UNUSED(rgReaderStates); + WINPR_UNUSED(cReaders); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardLocateCardsW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardLocateCardsByATRA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPSCARD_ATRMASK rgAtrMasks, + DWORD cAtrs, LPSCARD_READERSTATEA rgReaderStates, + DWORD cReaders) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardLocateCardsByATRA { hContext: %p", (void*)hContext); + + WINPR_UNUSED(rgAtrMasks); + WINPR_UNUSED(cAtrs); + WINPR_UNUSED(rgReaderStates); + WINPR_UNUSED(cReaders); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardLocateCardsByATRA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardLocateCardsByATRW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPSCARD_ATRMASK rgAtrMasks, + DWORD cAtrs, LPSCARD_READERSTATEW rgReaderStates, + DWORD cReaders) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardLocateCardsByATRW { hContext: %p", (void*)hContext); + + WINPR_UNUSED(rgAtrMasks); + WINPR_UNUSED(cAtrs); + WINPR_UNUSED(rgReaderStates); + WINPR_UNUSED(cReaders); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardLocateCardsByATRW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardGetStatusChangeA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, DWORD dwTimeout, + LPSCARD_READERSTATEA rgReaderStates, DWORD cReaders) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardGetStatusChangeA { hContext: %p", + (void*)hContext); + + if (dwTimeout == INFINITE) + dwTimeout = 60000; + + if (status == SCARD_S_SUCCESS) + { + const DWORD diff = 100; + size_t x; + size_t eventCount = 0; + SCardContext* value = HashTable_GetItemValue(smartcard->contexts, (const void*)hContext); + WINPR_ASSERT(value); /* Must be valid after Emulate_SCardIsValidContext */ + + status = SCARD_E_TIMEOUT; + do + { + for (x = 0; x < cReaders; x++) + { + size_t y; + LPSCARD_READERSTATEA out = &rgReaderStates[x]; + + for (y = 0; y < MAX_EMULATED_READERS; y++) + { + const LPSCARD_READERSTATEA in = &value->readerStateA[y]; + if (strcmp(out->szReader, in->szReader) == 0) + { + const SCardHandle* hdl = find_reader(smartcard, in->szReader, FALSE); + out->dwEventState = in->dwEventState; + if (hdl) + { + out->dwEventState |= SCARD_STATE_INUSE; + if (hdl->dwShareMode == SCARD_SHARE_EXCLUSIVE) + out->dwEventState |= SCARD_STATE_EXCLUSIVE; + } + + if ((out->dwEventState & SCARD_STATE_EMPTY) != + (out->dwCurrentState & SCARD_STATE_EMPTY)) + out->dwEventState |= SCARD_STATE_CHANGED; + if ((out->dwEventState & SCARD_STATE_PRESENT) != + (out->dwCurrentState & SCARD_STATE_PRESENT)) + out->dwEventState |= SCARD_STATE_CHANGED; + + out->cbAtr = in->cbAtr; + memcpy(out->rgbAtr, in->rgbAtr, out->cbAtr); + if (out->dwEventState & SCARD_STATE_CHANGED) + eventCount++; + } + } + } + if (value->canceled) + { + status = SCARD_E_CANCELLED; + break; + } + if (eventCount != 0) + { + status = SCARD_S_SUCCESS; + break; + } + Sleep(diff); + dwTimeout -= MIN(dwTimeout, diff); + } while (dwTimeout > 0); + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardGetStatusChangeA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardGetStatusChangeW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, DWORD dwTimeout, + LPSCARD_READERSTATEW rgReaderStates, DWORD cReaders) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardGetStatusChangeW { hContext: %p", + (void*)hContext); + + if (dwTimeout == INFINITE) + dwTimeout = 60000; + + if (status == SCARD_S_SUCCESS) + { + const DWORD diff = 100; + size_t x; + size_t eventCount = 0; + SCardContext* value = HashTable_GetItemValue(smartcard->contexts, (const void*)hContext); + WINPR_ASSERT(value); /* Must be valid after Emulate_SCardIsValidContext */ + + status = SCARD_E_TIMEOUT; + do + { + for (x = 0; x < cReaders; x++) + { + size_t y; + LPSCARD_READERSTATEW out = &rgReaderStates[x]; + + for (y = 0; y < MAX_EMULATED_READERS; y++) + { + const LPSCARD_READERSTATEW in = &value->readerStateW[y]; + if (_wcscmp(out->szReader, in->szReader) == 0) + { + const SCardHandle* hdl = find_reader(smartcard, in->szReader, TRUE); + out->dwEventState = in->dwEventState; + if (hdl) + { + out->dwEventState |= SCARD_STATE_INUSE; + if (hdl->dwShareMode == SCARD_SHARE_EXCLUSIVE) + out->dwEventState |= SCARD_STATE_EXCLUSIVE; + } + if ((out->dwEventState & SCARD_STATE_EMPTY) != + (out->dwCurrentState & SCARD_STATE_EMPTY)) + out->dwEventState |= SCARD_STATE_CHANGED; + if ((out->dwEventState & SCARD_STATE_PRESENT) != + (out->dwCurrentState & SCARD_STATE_PRESENT)) + out->dwEventState |= SCARD_STATE_CHANGED; + out->cbAtr = in->cbAtr; + memcpy(out->rgbAtr, in->rgbAtr, out->cbAtr); + + if (out->dwEventState & SCARD_STATE_CHANGED) + eventCount++; + } + } + } + if (value->canceled) + { + status = SCARD_E_CANCELLED; + break; + } + if (eventCount != 0) + { + status = SCARD_S_SUCCESS; + break; + } + Sleep(diff); + dwTimeout -= MIN(dwTimeout, diff); + } while (dwTimeout > 0); + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardGetStatusChangeW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardCancel(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardCancel { hContext: %p", + (void*)hContext); + + if (status == SCARD_S_SUCCESS) + { + SCardContext* value = HashTable_GetItemValue(smartcard->contexts, (const void*)hContext); + WINPR_ASSERT(value); + value->canceled = TRUE; + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardCancel } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); + + return status; +} + +SCardHandle* find_reader(SmartcardEmulationContext* smartcard, const void* szReader, BOOL unicode) +{ + SCardHandle* hdl = NULL; + UINT_PTR* keys = NULL; + size_t x, count; + + WINPR_ASSERT(smartcard); + count = HashTable_GetKeys(smartcard->handles, &keys); + for (x = 0; x < count; x++) + { + SCardHandle* cur = HashTable_GetItemValue(smartcard->handles, (const void*)keys[x]); + WINPR_ASSERT(cur); + + if (cur->unicode != unicode) + continue; + if (!unicode && (strcmp(cur->szReader.pc, szReader) != 0)) + continue; + if (unicode && (_wcscmp(cur->szReader.pw, szReader) != 0)) + continue; + hdl = cur; + break; + } + free(keys); + return hdl; +} + +static SCardHandle* reader2handle(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, + const void* szReader, BOOL unicode, DWORD dwShareMode, + SCARDHANDLE* phCard, DWORD dwPreferredProtocols, + LPDWORD pdwActiveProtocol) +{ + SCardHandle* hdl; + + WINPR_ASSERT(phCard); + + *phCard = 0; + if (Emulate_SCardIsValidContext(smartcard, hContext) != SCARD_S_SUCCESS) + return NULL; + + hdl = scard_handle_new(smartcard, hContext, szReader, unicode); + if (hdl) + { + winpr_RAND((BYTE*)&hdl->card, sizeof(hdl->card)); + hdl->dwActiveProtocol = SCARD_PROTOCOL_T1; + hdl->dwShareMode = dwShareMode; + + if (!HashTable_Insert(smartcard->handles, (const void*)hdl->card, hdl)) + { + scard_handle_free(hdl); + hdl = NULL; + } + else + { + if (pdwActiveProtocol) + { + if ((hdl->dwActiveProtocol & dwPreferredProtocols) == 0) + { + scard_handle_free(hdl); + hdl = NULL; + } + else + *pdwActiveProtocol = hdl->dwActiveProtocol; + } + if (hdl) + { + hdl->referencecount++; + *phCard = hdl->card; + } + } + } + WLog_Print(smartcard->log, smartcard->log_default_level, "{ %p }", (void*)*phCard); + return hdl; +} + +LONG WINAPI Emulate_SCardConnectA(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, + LPCSTR szReader, DWORD dwShareMode, DWORD dwPreferredProtocols, + LPSCARDHANDLE phCard, LPDWORD pdwActiveProtocol) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + if (!phCard || !pdwActiveProtocol) + status = SCARD_E_INVALID_PARAMETER; + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardConnectA { hContext: %p", + (void*)hContext); + + if (status == SCARD_S_SUCCESS) + { + if (!reader2handle(smartcard, hContext, szReader, FALSE, dwShareMode, phCard, + dwPreferredProtocols, pdwActiveProtocol)) + status = SCARD_E_NO_MEMORY; + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardConnectA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardConnectW(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, + LPCWSTR szReader, DWORD dwShareMode, DWORD dwPreferredProtocols, + LPSCARDHANDLE phCard, LPDWORD pdwActiveProtocol) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + if (!phCard || !pdwActiveProtocol) + status = SCARD_E_INVALID_PARAMETER; + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardConnectW { hContext: %p", + (void*)hContext); + + if (status == SCARD_S_SUCCESS) + { + if (!reader2handle(smartcard, hContext, szReader, TRUE, dwShareMode, phCard, + dwPreferredProtocols, pdwActiveProtocol)) + status = SCARD_E_NO_MEMORY; + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardConnectW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardReconnect(SmartcardEmulationContext* smartcard, SCARDHANDLE hCard, + DWORD dwShareMode, DWORD dwPreferredProtocols, + DWORD dwInitialization, LPDWORD pdwActiveProtocol) +{ + LONG status = scard_handle_valid(smartcard, hCard); + + if (!pdwActiveProtocol) + status = SCARD_E_INVALID_PARAMETER; + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardReconnect { hCard: %p", + (void*)hCard); + + if (status == SCARD_S_SUCCESS) + { + SCardHandle* hdl = HashTable_GetItemValue(smartcard->handles, (const void*)hCard); + WINPR_ASSERT(hdl); + + // TODO: Implement + hdl->dwShareMode = dwShareMode; + hdl->transaction = FALSE; + + *pdwActiveProtocol = hdl->dwActiveProtocol; + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardReconnect } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardDisconnect(SmartcardEmulationContext* smartcard, SCARDHANDLE hCard, + DWORD dwDisposition) +{ + LONG status = scard_handle_valid(smartcard, hCard); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardDisconnect { hCard: %p", + (void*)hCard); + + WINPR_UNUSED(dwDisposition); /* We just ignore this. All return values are static anyway */ + + if (status == SCARD_S_SUCCESS) + { + SCardHandle* hdl = HashTable_GetItemValue(smartcard->handles, (const void*)hCard); + WINPR_ASSERT(hdl); + + hdl->referencecount--; + if (hdl->referencecount == 0) + HashTable_Remove(smartcard->handles, (const void*)hCard); + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardDisconnect } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardBeginTransaction(SmartcardEmulationContext* smartcard, SCARDHANDLE hCard) +{ + LONG status = scard_handle_valid(smartcard, hCard); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardBeginTransaction { hCard: %p", + (void*)hCard); + + if (status == SCARD_S_SUCCESS) + { + SCardHandle* hdl = HashTable_GetItemValue(smartcard->handles, (const void*)hCard); + WINPR_ASSERT(hdl); + if (hdl->transaction) + status = SCARD_E_INVALID_VALUE; + else + hdl->transaction = TRUE; + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardBeginTransaction } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardEndTransaction(SmartcardEmulationContext* smartcard, SCARDHANDLE hCard, + DWORD dwDisposition) +{ + LONG status = scard_handle_valid(smartcard, hCard); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardEndTransaction { hCard: %p", + (void*)hCard); + + WINPR_UNUSED(dwDisposition); /* We just ignore this. All return values are static anyway */ + + if (status == SCARD_S_SUCCESS) + { + SCardHandle* hdl = HashTable_GetItemValue(smartcard->handles, (const void*)hCard); + WINPR_ASSERT(hdl); + if (!hdl->transaction) + status = SCARD_E_NOT_TRANSACTED; + else + hdl->transaction = FALSE; + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardEndTransaction } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardCancelTransaction(SmartcardEmulationContext* smartcard, SCARDHANDLE hCard) +{ + LONG status = scard_handle_valid(smartcard, hCard); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardCancelTransaction { hCard: %p", + (void*)hCard); + + if (status == SCARD_S_SUCCESS) + { + SCardHandle* hdl = HashTable_GetItemValue(smartcard->handles, (const void*)hCard); + WINPR_ASSERT(hdl); + if (!hdl->transaction) + status = SCARD_E_NOT_TRANSACTED; + else + hdl->transaction = FALSE; + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardCancelTransaction } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardState(SmartcardEmulationContext* smartcard, SCARDHANDLE hCard, + LPDWORD pdwState, LPDWORD pdwProtocol, LPBYTE pbAtr, + LPDWORD pcbAtrLen) +{ + LONG status = scard_handle_valid(smartcard, hCard); + + if (!pdwState || !pdwProtocol) + status = SCARD_E_INVALID_PARAMETER; + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardState { hCard: %p", + (void*)hCard); + + if (status == SCARD_S_SUCCESS) + { + SCardHandle* hdl = HashTable_GetItemValue(smartcard->handles, (const void*)hCard); + WINPR_ASSERT(hdl); + + if (pdwState) + *pdwState = SCARD_SPECIFIC; + if (pdwProtocol) + *pdwProtocol = SCARD_PROTOCOL_T1; + + if (pcbAtrLen) + { + size_t x; + SCardContext* ctx = + HashTable_GetItemValue(smartcard->contexts, (const void*)hdl->hContext); + WINPR_ASSERT(ctx); + + for (x = 0; x < MAX_EMULATED_READERS; x++) + { + const SCARD_READERSTATEA* readerA = &ctx->readerStateA[x]; + const SCARD_READERSTATEW* readerW = &ctx->readerStateW[x]; + if (hdl->unicode) + { + if (_wcscmp(readerW->szReader, hdl->szReader.pw) == 0) + { + *pcbAtrLen = scard_copy_strings(ctx, pbAtr, *pcbAtrLen, readerW->rgbAtr, + readerW->cbAtr); + } + } + else + { + if (strcmp(readerA->szReader, hdl->szReader.pc) == 0) + { + *pcbAtrLen = scard_copy_strings(ctx, pbAtr, *pcbAtrLen, readerA->rgbAtr, + readerA->cbAtr); + } + } + } + } + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardState } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardStatusA(SmartcardEmulationContext* smartcard, SCARDHANDLE hCard, + LPSTR mszReaderNames, LPDWORD pcchReaderLen, LPDWORD pdwState, + LPDWORD pdwProtocol, LPBYTE pbAtr, LPDWORD pcbAtrLen) +{ + LONG status = scard_handle_valid(smartcard, hCard); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardStatusA { hCard: %p", + (void*)hCard); + + if (status == SCARD_S_SUCCESS) + { + SCardContext* ctx; + SCardHandle* hdl = HashTable_GetItemValue(smartcard->handles, (const void*)hCard); + WINPR_ASSERT(hdl); + + ctx = HashTable_GetItemValue(smartcard->contexts, (const void*)hdl->hContext); + WINPR_ASSERT(ctx); + + if (pcchReaderLen) + *pcchReaderLen = + scard_copy_strings(ctx, mszReaderNames, *pcchReaderLen, hdl->szReader.pc, + (UINT32)strlen(hdl->szReader.pc) + 2); + + if (pdwState) + *pdwState = SCARD_SPECIFIC; + if (pdwProtocol) + *pdwProtocol = SCARD_PROTOCOL_T1; + + if (pcbAtrLen) + { + size_t x; + + for (x = 0; x < MAX_EMULATED_READERS; x++) + { + const SCARD_READERSTATEA* reader = &ctx->readerStateA[x]; + if (strcmp(reader->szReader, hdl->szReader.pc) == 0) + { + *pcbAtrLen = + scard_copy_strings(ctx, pbAtr, *pcbAtrLen, reader->rgbAtr, reader->cbAtr); + } + } + } + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardStatusA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardStatusW(SmartcardEmulationContext* smartcard, SCARDHANDLE hCard, + LPWSTR mszReaderNames, LPDWORD pcchReaderLen, LPDWORD pdwState, + LPDWORD pdwProtocol, LPBYTE pbAtr, LPDWORD pcbAtrLen) +{ + LONG status = scard_handle_valid(smartcard, hCard); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardStatusW { hCard: %p", + (void*)hCard); + + if (status == SCARD_S_SUCCESS) + { + SCardContext* ctx; + SCardHandle* hdl = HashTable_GetItemValue(smartcard->handles, (const void*)hCard); + WINPR_ASSERT(hdl); + + ctx = HashTable_GetItemValue(smartcard->contexts, (const void*)hdl->hContext); + WINPR_ASSERT(ctx); + + if (pcchReaderLen) + *pcchReaderLen = + scard_copy_strings(ctx, mszReaderNames, *pcchReaderLen, hdl->szReader.pw, + (UINT32)(_wcslen(hdl->szReader.pw) + 2) * sizeof(WCHAR)) / + sizeof(WCHAR); + + if (pdwState) + *pdwState = SCARD_SPECIFIC; + if (pdwProtocol) + *pdwProtocol = SCARD_PROTOCOL_T1; + + if (pcbAtrLen) + { + size_t x; + + for (x = 0; x < MAX_EMULATED_READERS; x++) + { + const SCARD_READERSTATEW* reader = &ctx->readerStateW[x]; + if (_wcscmp(reader->szReader, hdl->szReader.pw) == 0) + *pcbAtrLen = + scard_copy_strings(ctx, pbAtr, *pcbAtrLen, reader->rgbAtr, reader->cbAtr); + } + } + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardStatusW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardTransmit(SmartcardEmulationContext* smartcard, SCARDHANDLE hCard, + LPCSCARD_IO_REQUEST pioSendPci, LPCBYTE pbSendBuffer, + DWORD cbSendLength, LPSCARD_IO_REQUEST pioRecvPci, + LPBYTE pbRecvBuffer, LPDWORD pcbRecvLength) +{ + LONG status = scard_handle_valid(smartcard, hCard); + + if (!pioSendPci || !pbSendBuffer || !pbRecvBuffer || !pcbRecvLength) + status = SCARD_E_INVALID_PARAMETER; + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardTransmit { hCard: %p", + (void*)hCard); + + if (status == SCARD_S_SUCCESS) + { + BYTE* response = NULL; + DWORD responseSize = 0; + SCardHandle* hdl = HashTable_GetItemValue(smartcard->handles, (const void*)hCard); + WINPR_ASSERT(hdl); + + hdl->transmitcount++; + + if (!vgids_process_apdu(hdl->vgids, pbSendBuffer, cbSendLength, &response, &responseSize)) + status = SCARD_E_NO_SMARTCARD; + else + { + SCardContext* ctx = + HashTable_GetItemValue(smartcard->contexts, (const void*)hdl->hContext); + WINPR_ASSERT(ctx); + + *pcbRecvLength = + scard_copy_strings(ctx, pbRecvBuffer, *pcbRecvLength, response, responseSize); + free(response); + + /* Required */ + if (pioRecvPci) + pioRecvPci->dwProtocol = hdl->dwActiveProtocol; + } + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardTransmit } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardGetTransmitCount(SmartcardEmulationContext* smartcard, SCARDHANDLE hCard, + LPDWORD pcTransmitCount) +{ + LONG status = scard_handle_valid(smartcard, hCard); + + if (!pcTransmitCount) + status = SCARD_E_INVALID_PARAMETER; + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardGetTransmitCount { hCard: %p", + (void*)hCard); + + if (status == SCARD_S_SUCCESS) + { + SCardHandle* hdl = HashTable_GetItemValue(smartcard->handles, (const void*)hCard); + WINPR_ASSERT(hdl); + + *pcTransmitCount = hdl->transmitcount; + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardGetTransmitCount } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardControl(SmartcardEmulationContext* smartcard, SCARDHANDLE hCard, + DWORD dwControlCode, LPCVOID lpInBuffer, DWORD cbInBufferSize, + LPVOID lpOutBuffer, DWORD cbOutBufferSize, LPDWORD lpBytesReturned) +{ + LONG status = scard_handle_valid(smartcard, hCard); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardControl { hCard: %p", + (void*)hCard); + + if (status == SCARD_S_SUCCESS) + { + WINPR_UNUSED(dwControlCode); + WINPR_UNUSED(lpInBuffer); + WINPR_UNUSED(cbInBufferSize); + WINPR_UNUSED(lpOutBuffer); + WINPR_UNUSED(cbOutBufferSize); + WINPR_UNUSED(lpBytesReturned); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardControl } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardGetAttrib(SmartcardEmulationContext* smartcard, SCARDHANDLE hCard, + DWORD dwAttrId, LPBYTE pbAttr, LPDWORD pcbAttrLen) +{ + LONG status = scard_handle_valid(smartcard, hCard); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardGetAttrib { hCard: %p", + (void*)hCard); + + WINPR_UNUSED(dwAttrId); + WINPR_UNUSED(pbAttr); + WINPR_UNUSED(pcbAttrLen); + + /* Not required, return not supported */ + status = SCARD_F_INTERNAL_ERROR; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardGetAttrib } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardSetAttrib(SmartcardEmulationContext* smartcard, SCARDHANDLE hCard, + DWORD dwAttrId, LPCBYTE pbAttr, DWORD cbAttrLen) +{ + LONG status = scard_handle_valid(smartcard, hCard); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardSetAttrib { hCard: %p", + (void*)hCard); + + WINPR_UNUSED(dwAttrId); + WINPR_UNUSED(pbAttr); + WINPR_UNUSED(cbAttrLen); + + /* Not required, return not supported */ + status = SCARD_F_INTERNAL_ERROR; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardSetAttrib } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardUIDlgSelectCardA(SmartcardEmulationContext* smartcard, + LPOPENCARDNAMEA_EX pDlgStruc) +{ + LONG status; + + WINPR_ASSERT(smartcard); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardUIDlgSelectCardA {"); + + WINPR_UNUSED(pDlgStruc); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardUIDlgSelectCardA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardUIDlgSelectCardW(SmartcardEmulationContext* smartcard, + LPOPENCARDNAMEW_EX pDlgStruc) +{ + LONG status; + + WINPR_ASSERT(smartcard); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardUIDlgSelectCardW {"); + + WINPR_UNUSED(pDlgStruc); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardUIDlgSelectCardW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_GetOpenCardNameA(SmartcardEmulationContext* smartcard, + LPOPENCARDNAMEA pDlgStruc) +{ + LONG status; + + WINPR_ASSERT(smartcard); + + WLog_Print(smartcard->log, smartcard->log_default_level, "GetOpenCardNameA {"); + + WINPR_UNUSED(pDlgStruc); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "GetOpenCardNameA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_GetOpenCardNameW(SmartcardEmulationContext* smartcard, + LPOPENCARDNAMEW pDlgStruc) +{ + LONG status; + + WINPR_ASSERT(smartcard); + + WLog_Print(smartcard->log, smartcard->log_default_level, "GetOpenCardNameW {"); + + WINPR_UNUSED(pDlgStruc); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "GetOpenCardNameW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardDlgExtendedError(SmartcardEmulationContext* smartcard) +{ + LONG status; + + WINPR_ASSERT(smartcard); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardDlgExtendedError {"); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardDlgExtendedError } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardReadCacheA(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, + UUID* CardIdentifier, DWORD FreshnessCounter, LPSTR LookupName, + PBYTE Data, DWORD* DataLen) +{ + DWORD count = 0; + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + if (!CardIdentifier || !DataLen) + status = SCARD_E_INVALID_PARAMETER; + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardReadCacheA { hContext: %p", + (void*)hContext); + + if (DataLen) + { + count = *DataLen; + *DataLen = 0; + } + + if (status == SCARD_S_SUCCESS) + { + SCardCacheItem* data; + SCardContext* value = HashTable_GetItemValue(smartcard->contexts, (const void*)hContext); + WINPR_ASSERT(value); /* Must be valid after Emulate_SCardIsValidContext */ + + data = HashTable_GetItemValue(value->cacheA, LookupName); + if (!data) + status = SCARD_W_CACHE_ITEM_NOT_FOUND; + else if (data->freshness != FreshnessCounter) + status = SCARD_W_CACHE_ITEM_STALE; + else + *DataLen = scard_copy_strings(value, Data, count, data->data, data->size); + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardReadCacheA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardReadCacheW(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, + UUID* CardIdentifier, DWORD FreshnessCounter, LPWSTR LookupName, + PBYTE Data, DWORD* DataLen) +{ + DWORD count = 0; + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + if (!CardIdentifier || !DataLen) + status = SCARD_E_INVALID_PARAMETER; + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardReadCacheW { hContext: %p", + (void*)hContext); + + if (DataLen) + { + count = *DataLen; + *DataLen = 0; + } + + if (status == SCARD_S_SUCCESS) + { + SCardCacheItem* data; + SCardContext* value = HashTable_GetItemValue(smartcard->contexts, (const void*)hContext); + WINPR_ASSERT(value); /* Must be valid after Emulate_SCardIsValidContext */ + + data = HashTable_GetItemValue(value->cacheW, LookupName); + if (!data) + status = SCARD_W_CACHE_ITEM_NOT_FOUND; + else if (data->freshness != FreshnessCounter) + status = SCARD_W_CACHE_ITEM_STALE; + else + *DataLen = scard_copy_strings(value, Data, count, data->data, data->size); + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardReadCacheW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +static BOOL insert_data(wHashTable* table, DWORD FreshnessCounter, const void* key, + const PBYTE Data, DWORD DataLen) +{ + BOOL rc; + SCardCacheItem* item; + + WINPR_ASSERT(table); + WINPR_ASSERT(key); + + if (DataLen > MAX_CACHE_ITEM_SIZE) + return SCARD_W_CACHE_ITEM_TOO_BIG; + + if (HashTable_Count(table) > MAX_CACHE_ITEM_VALUES) + return SCARD_E_WRITE_TOO_MANY; + + item = HashTable_GetItemValue(table, key); + if (!item) + { + item = calloc(1, sizeof(SCardCacheItem)); + if (!item) + return SCARD_E_NO_MEMORY; + + rc = HashTable_Insert(table, key, item); + if (!rc) + { + free(item); + return SCARD_E_NO_MEMORY; + } + } + + if (item->freshness > FreshnessCounter) + return SCARD_W_CACHE_ITEM_STALE; + item->freshness = FreshnessCounter; + item->size = DataLen; + memcpy(item->data, Data, DataLen); + return SCARD_S_SUCCESS; +} + +LONG WINAPI Emulate_SCardWriteCacheA(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, + UUID* CardIdentifier, DWORD FreshnessCounter, LPSTR LookupName, + PBYTE Data, DWORD DataLen) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + if (!CardIdentifier) + status = SCARD_E_INVALID_PARAMETER; + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardWriteCacheA { hContext: %p", + (void*)hContext); + + if (status == SCARD_S_SUCCESS) + { + SCardContext* value = HashTable_GetItemValue(smartcard->contexts, (const void*)hContext); + WINPR_ASSERT(value); /* Must be valid after Emulate_SCardIsValidContext */ + + status = insert_data(value->cacheA, FreshnessCounter, LookupName, Data, DataLen); + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardWriteCacheA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardWriteCacheW(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, + UUID* CardIdentifier, DWORD FreshnessCounter, + LPWSTR LookupName, PBYTE Data, DWORD DataLen) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + if (!CardIdentifier) + status = SCARD_E_INVALID_PARAMETER; + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardWriteCacheW { hContext: %p", + (void*)hContext); + + if (status == SCARD_S_SUCCESS) + { + SCardContext* value = HashTable_GetItemValue(smartcard->contexts, (const void*)hContext); + WINPR_ASSERT(value); /* Must be valid after Emulate_SCardIsValidContext */ + + status = insert_data(value->cacheW, FreshnessCounter, LookupName, Data, DataLen); + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardWriteCacheW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardGetReaderIconA(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, + LPCSTR szReaderName, LPBYTE pbIcon, LPDWORD pcbIcon) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + if (!szReaderName || !pcbIcon) + status = SCARD_E_INVALID_PARAMETER; + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardGetReaderIconA { hContext: %p", + (void*)hContext); + + if (status == SCARD_S_SUCCESS) + status = scard_reader_name_valid_a(smartcard, hContext, szReaderName); + + if (status == SCARD_S_SUCCESS) + { + SCardContext* ctx = HashTable_GetItemValue(smartcard->contexts, (const void*)hContext); + WINPR_ASSERT(ctx); + + if (pbIcon) + *pcbIcon = scard_copy_strings(ctx, pbIcon, *pcbIcon, resources_FreeRDP_ico, + resources_FreeRDP_ico_len); + else + *pcbIcon = resources_FreeRDP_ico_len; + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardGetReaderIconA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardGetReaderIconW(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, + LPCWSTR szReaderName, LPBYTE pbIcon, LPDWORD pcbIcon) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + if (!szReaderName || !pcbIcon) + status = SCARD_E_INVALID_PARAMETER; + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardGetReaderIconW { hContext: %p", + (void*)hContext); + + if (status == SCARD_S_SUCCESS) + status = scard_reader_name_valid_w(smartcard, hContext, szReaderName); + + if (status == SCARD_S_SUCCESS) + { + SCardContext* ctx = HashTable_GetItemValue(smartcard->contexts, (const void*)hContext); + WINPR_ASSERT(ctx); + + if (pbIcon) + *pcbIcon = scard_copy_strings(ctx, pbIcon, *pcbIcon, resources_FreeRDP_ico, + resources_FreeRDP_ico_len); + else + *pcbIcon = resources_FreeRDP_ico_len; + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardGetReaderIconW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardGetDeviceTypeIdA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szReaderName, + LPDWORD pdwDeviceTypeId) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + if (!pdwDeviceTypeId) + status = SCARD_E_INVALID_PARAMETER; + + if (status == SCARD_S_SUCCESS) + status = scard_reader_name_valid_a(smartcard, hContext, szReaderName); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardGetDeviceTypeIdA { hContext: %p", + (void*)hContext); + + if (status == SCARD_S_SUCCESS) + { + *pdwDeviceTypeId = SCARD_READER_TYPE_USB; // SCARD_READER_TYPE_TPM + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardGetDeviceTypeIdA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardGetDeviceTypeIdW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPDWORD pdwDeviceTypeId) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + if (!pdwDeviceTypeId) + status = SCARD_E_INVALID_PARAMETER; + + if (status == SCARD_S_SUCCESS) + status = scard_reader_name_valid_w(smartcard, hContext, szReaderName); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardGetDeviceTypeIdW { hContext: %p", + (void*)hContext); + + if (status == SCARD_S_SUCCESS) + { + *pdwDeviceTypeId = SCARD_READER_TYPE_USB; // SCARD_READER_TYPE_TPM + } + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardGetDeviceTypeIdW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), + status); + + return status; +} + +LONG WINAPI Emulate_SCardGetReaderDeviceInstanceIdA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCSTR szReaderName, + LPSTR szDeviceInstanceId, + LPDWORD pcchDeviceInstanceId) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + if (status == SCARD_S_SUCCESS) + status = scard_reader_name_valid_a(smartcard, hContext, szReaderName); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardGetReaderDeviceInstanceIdA { hContext: %p", (void*)hContext); + + WINPR_UNUSED(szDeviceInstanceId); + WINPR_UNUSED(pcchDeviceInstanceId); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardGetReaderDeviceInstanceIdA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardGetReaderDeviceInstanceIdW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPWSTR szDeviceInstanceId, + LPDWORD pcchDeviceInstanceId) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + if (status == SCARD_S_SUCCESS) + status = scard_reader_name_valid_w(smartcard, hContext, szReaderName); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardGetReaderDeviceInstanceIdW { hContext: %p", (void*)hContext); + + WINPR_UNUSED(szDeviceInstanceId); + WINPR_UNUSED(pcchDeviceInstanceId); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardGetReaderDeviceInstanceIdW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardListReadersWithDeviceInstanceIdA(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, + LPCSTR szDeviceInstanceId, + LPSTR mszReaders, LPDWORD pcchReaders) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardListReadersWithDeviceInstanceIdA { hContext: %p", (void*)hContext); + + WINPR_UNUSED(szDeviceInstanceId); + WINPR_UNUSED(mszReaders); + WINPR_UNUSED(pcchReaders); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardListReadersWithDeviceInstanceIdA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardListReadersWithDeviceInstanceIdW(SmartcardEmulationContext* smartcard, + SCARDCONTEXT hContext, + LPCWSTR szDeviceInstanceId, + LPWSTR mszReaders, LPDWORD pcchReaders) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardListReadersWithDeviceInstanceIdW { hContext: %p", (void*)hContext); + + WINPR_UNUSED(szDeviceInstanceId); + WINPR_UNUSED(mszReaders); + WINPR_UNUSED(pcchReaders); + + /* Not required, return not supported */ + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardListReadersWithDeviceInstanceIdW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +LONG WINAPI Emulate_SCardAudit(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext, + DWORD dwEvent) +{ + LONG status = Emulate_SCardIsValidContext(smartcard, hContext); + + WINPR_UNUSED(dwEvent); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardAudit { hContext: %p", + (void*)hContext); + + // TODO: Implement + status = SCARD_E_UNSUPPORTED_FEATURE; + + WLog_Print(smartcard->log, smartcard->log_default_level, + "SCardAudit } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); + + return status; +} + +static BOOL context_equals(const void* pva, const void* pvb) +{ + const SCARDCONTEXT a = (const SCARDCONTEXT)pva; + const SCARDCONTEXT b = (const SCARDCONTEXT)pvb; + if (!a && !b) + return TRUE; + if (!a || !b) + return FALSE; + + return a == b; +} + +static BOOL handle_equals(const void* pva, const void* pvb) +{ + const SCARDHANDLE a = (const SCARDHANDLE)pva; + const SCARDHANDLE b = (const SCARDHANDLE)pvb; + if (!a && !b) + return TRUE; + if (!a || !b) + return FALSE; + + return a == b; +} + +SmartcardEmulationContext* Emulate_New(const rdpSettings* settings) +{ + SmartcardEmulationContext* smartcard; + + WINPR_ASSERT(settings); + + smartcard = calloc(1, sizeof(SmartcardEmulationContext)); + if (!smartcard) + goto fail; + + smartcard->settings = settings; + smartcard->log = WLog_Get("EmulateSCard"); + if (!smartcard->log) + goto fail; + smartcard->log_default_level = WLOG_TRACE; + + smartcard->contexts = HashTable_New(FALSE); + if (!smartcard->contexts) + goto fail; + else + { + wObject* obj = HashTable_KeyObject(smartcard->contexts); + WINPR_ASSERT(obj); + obj->fnObjectEquals = context_equals; + } + if (!smartcard->contexts) + goto fail; + else + { + wObject* obj = HashTable_ValueObject(smartcard->contexts); + WINPR_ASSERT(obj); + obj->fnObjectFree = scard_context_free; + } + + smartcard->handles = HashTable_New(FALSE); + if (!smartcard->handles) + goto fail; + else + { + wObject* obj = HashTable_KeyObject(smartcard->handles); + WINPR_ASSERT(obj); + obj->fnObjectEquals = handle_equals; + } + if (!smartcard->handles) + goto fail; + else + { + wObject* obj = HashTable_ValueObject(smartcard->handles); + WINPR_ASSERT(obj); + obj->fnObjectFree = scard_handle_free; + } + + return smartcard; + +fail: + Emulate_Free(smartcard); + return NULL; +} + +void Emulate_Free(SmartcardEmulationContext* context) +{ + if (!context) + return; + + HashTable_Free(context->handles); + HashTable_Free(context->contexts); + free(context); +} + +BOOL Emulate_IsConfigured(SmartcardEmulationContext* context) +{ + BOOL rc = FALSE; + vgidsContext* vgids; + + WINPR_ASSERT(context); + + vgids = vgids_new(); + if (vgids) + { + const char* pem = + freerdp_settings_get_string(context->settings, FreeRDP_SmartcardCertificate); + const char* key = + freerdp_settings_get_string(context->settings, FreeRDP_SmartcardPrivateKey); + const char* pin = NULL; + + if (freerdp_settings_get_bool(context->settings, FreeRDP_PasswordIsSmartcardPin)) + pin = freerdp_settings_get_string(context->settings, FreeRDP_Password); + + rc = vgids_init(vgids, pem, key, pin); + } + + vgids_free(vgids); + return rc; +} diff --git a/libfreerdp/emu/scard/smartcard_virtual_gids.c b/libfreerdp/emu/scard/smartcard_virtual_gids.c new file mode 100644 index 000000000..60c0a7a7c --- /dev/null +++ b/libfreerdp/emu/scard/smartcard_virtual_gids.c @@ -0,0 +1,1617 @@ +/** + * WinPR: Windows Portable Runtime + * Virtual GIDS implementation + * + * Copyright 2021 Martin Fleisz + * Copyright 2021 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "smartcard_virtual_gids.h" + +#define TAG CHANNELS_TAG("smartcard.vgids") + +#define VGIDS_EFID_MASTER 0xA000 +#define VGIDS_EFID_COMMON 0xA010 +#define VGIDS_EFID_CARDCF VGIDS_EFID_COMMON +#define VGIDS_EFID_CARDAPPS VGIDS_EFID_COMMON +#define VGIDS_EFID_CMAPFILE VGIDS_EFID_COMMON +#define VGIDS_EFID_CARDID 0xA012 +#define VGIDS_EFID_KXC00 VGIDS_EFID_COMMON +#define VGIDS_EFID_CURRENTDF 0x3FFF + +#define VGIDS_DO_FILESYSTEMTABLE 0xDF1F +#define VGIDS_DO_KEYMAP 0xDF20 +#define VGIDS_DO_CARDID 0xDF20 +#define VGIDS_DO_CARDAPPS 0xDF21 +#define VGIDS_DO_CARDCF 0xDF22 +#define VGIDS_DO_CMAPFILE 0xDF23 +#define VGIDS_DO_KXC00 0xDF24 + +#define VGIDS_CARDID_SIZE 16 +#define VGIDS_MAX_PIN_SIZE 127 + +#define VGIDS_DEFAULT_RETRY_COUNTER 3 + +#define VGIDS_KEY_TYPE_KEYEXCHANGE 0x9A +#define VGIDS_KEY_TYPE_SIGNATURE 0x9C + +#define VGIDS_ALGID_RSA_1024 0x06 +#define VGIDS_ALGID_RSA_2048 0x07 +#define VGIDS_ALGID_RSA_3072 0x08 +#define VGIDS_ALGID_RSA_4096 0x09 + +#define VGIDS_SE_CRT_AUTH 0xA4 +#define VGIDS_SE_CRT_SIGN 0xB6 +#define VGIDS_SE_CRT_CONF 0xB8 + +#define VGIDS_SE_ALGOID_CT_PAD_PKCS1 0x40 +#define VGIDS_SE_ALGOID_CT_PAD_OAEP 0x80 +#define VGIDS_SE_ALGOID_CT_RSA_1024 0x06 +#define VGIDS_SE_ALGOID_CT_RSA_2048 0x07 +#define VGIDS_SE_ALGOID_CT_RSA_3072 0x08 +#define VGIDS_SE_ALGOID_CT_RSA_4096 0x09 + +#define VGIDS_SE_ALGOID_DST_PAD_PKCS1 0x40 +#define VGIDS_SE_ALGOID_DST_RSA_1024 0x06 +#define VGIDS_SE_ALGOID_DST_RSA_2048 0x07 +#define VGIDS_SE_ALGOID_DST_RSA_3072 0x08 +#define VGIDS_SE_ALGOID_DST_RSA_4096 0x09 +#define VGIDS_SE_ALGOID_DST_ECDSA_P192 0x0A +#define VGIDS_SE_ALGOID_DST_ECDSA_P224 0x0B +#define VGIDS_SE_ALGOID_DST_ECDSA_P256 0x0C +#define VGIDS_SE_ALGOID_DST_ECDSA_P384 0x0D +#define VGIDS_SE_ALGOID_DST_ECDSA_P512 0x0E + +#define VGIDS_DEFAULT_KEY_REF 0x81 + +#define ISO_INS_SELECT 0xA4 +#define ISO_INS_GETDATA 0xCB +#define ISO_INS_GETRESPONSE 0xC0 +#define ISO_INS_MSE 0x22 +#define ISO_INS_PSO 0x2A +#define ISO_INS_VERIFY 0x20 + +#define ISO_STATUS_MORE_DATA 0x6100 +#define ISO_STATUS_VERIFYFAILED 0x6300 +#define ISO_STATUS_WRONGLC 0x6700 +#define ISO_STATUS_COMMANDNOTALLOWED 0x6900 +#define ISO_STATUS_SECURITYSTATUSNOTSATISFIED 0x6982 +#define ISO_STATUS_AUTHMETHODBLOCKED 0x6983 +#define ISO_STATUS_INVALIDCOMMANDDATA 0x6A80 +#define ISO_STATUS_FILENOTFOUND 0x6A82 +#define ISO_STATUS_INVALIDP1P2 0x6A86 +#define ISO_STATUS_INVALIDLC 0x6A87 +#define ISO_STATUS_REFERENCEDATANOTFOUND 0x6A88 +#define ISO_STATUS_SUCCESS 0x9000 + +#define ISO_AID_MAX_SIZE 16 + +#define ISO_FID_MF 0x3F00 + +struct vgids_ef +{ + UINT16 id; + UINT16 dirID; + wStream* data; +}; +typedef struct vgids_ef vgidsEF; + +struct vgids_se +{ + BYTE crt; /* control reference template tag */ + BYTE algoId; /* Algorithm ID */ + BYTE keyRef; /* Key reference */ +}; +typedef struct vgids_se vgidsSE; + +struct vgids_context +{ + UINT16 currentDF; + char* pin; + UINT16 curRetryCounter; + UINT16 retryCounter; + wStream* commandData; + wStream* responseData; + BOOL pinVerified; + vgidsSE currentSE; + + X509* certificate; + + RSA* publicKey; + RSA* privateKey; + + wArrayList* files; +}; + +/* PKCS 1.5 DER encoded digest information */ +#define VGIDS_MAX_DIGEST_INFO 7 + +static const BYTE g_PKCS1_SHA1[] = { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, + 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 }; +static const BYTE g_PKCS1_SHA224[] = { 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c }; +static const BYTE g_PKCS1_SHA256[] = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 }; +static const BYTE g_PKCS1_SHA384[] = { 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 }; +static const BYTE g_PKCS1_SHA512[] = { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 }; +static const BYTE g_PKCS1_SHA512_224[] = { 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, + 0x05, 0x05, 0x00, 0x04, 0x1c }; +static const BYTE g_PKCS1_SHA512_256[] = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, + 0x06, 0x05, 0x00, 0x04, 0x20 }; + +/* Helper struct to map PKCS1.5 digest info to OpenSSL EVP_MD */ +struct vgids_digest_info_map +{ + const BYTE* info; + size_t infoSize; + const EVP_MD* digest; +}; +typedef struct vgids_digest_info_map vgidsDigestInfoMap; + +/* MS GIDS AID */ +/* xx: Used by the Windows smart card framework for the GIDS version number. This byte must be set + * to the GIDS specification revision number which is either 0x01 or 0x02. + * yy: Reserved for use by the card application (set to 01) + */ +static const BYTE g_MsGidsAID[] = { + 0xA0, 0x00, 0x00, 0x03, 0x97, 0x42, 0x54, 0x46, 0x59, 0x02, 0x01 +}; + +/* GIDS APP File Control Parameter: + FD-Byte (82): 38 (not shareable-DF) + Sec Attr (8C): 03 30 30 Create/Delete File(03) Ext/User-Auth (30) +*/ +static const BYTE g_GidsAppFCP[] = { 0x62, 0x08, 0x82, 0x01, 0x38, 0x8C, 0x03, 0x03, 0x30, 0x30 }; +/* GIDS APP File Control Information: + AppID (4F, Len 0B): A0 00 00 03 97 42 54 46 59 02 01 + Discretionary DOs (73, Len 03): 40 01 C0 + Supported Auth Protocols (40, Len 01): C0 Mutual/External-Auth + */ +static const BYTE g_GidsAppFCI[] = { 0x61, 0x12, 0x4F, 0x0B, 0xA0, 0x00, 0x00, 0x03, 0x97, 0x42, + 0x54, 0x46, 0x59, 0x02, 0x01, 0x73, 0x03, 0x40, 0x01, 0xC0 }; + +/* +typedef struct _CARD_CACHE_FILE_FORMAT +{ + BYTE bVersion; // Cache version + BYTE bPinsFreshness; // Card PIN + WORD wContainersFreshness; + WORD wFilesFreshness; +} CARD_CACHE_FILE_FORMAT, *PCARD_CACHE_FILE_FORMAT; */ +static const BYTE g_CardCFContents[] = { 0x00, 0x00, 0x01, 0x00, 0x04, 0x00 }; + +/* {‘mscp’,0,0,0,0} */ +static const BYTE g_CardAppsContents[] = { 0x6d, 0x73, 0x63, 0x70, 0x00, 0x00, 0x00, 0x00 }; + +#pragma pack(push, 1) + +/* Type: CONTAINER_MAP_RECORD (taken from Windows Smart Card Minidriver Specification) + + This structure describes the format of the Base CSP's + container map file, stored on the card. This is wellknown + logical file wszCONTAINER_MAP_FILE. The file consists of + zero or more of these records. */ +#define MAX_CONTAINER_NAME_LEN 39 + +/* This flag is set in the CONTAINER_MAP_RECORD bFlags + member if the corresponding container is valid and currently + exists on the card. // If the container is deleted, its + bFlags field must be cleared. */ +#define CONTAINER_MAP_VALID_CONTAINER 1 + +/* This flag is set in the CONTAINER_MAP_RECORD bFlags + member if the corresponding container is the default + container on the card. */ +#define CONTAINER_MAP_DEFAULT_CONTAINER 2 + +struct vgids_container_map_entry +{ + WCHAR wszGuid[MAX_CONTAINER_NAME_LEN + 1]; + BYTE bFlags; + BYTE bReserved; + WORD wSigKeySizeBits; + WORD wKeyExchangeKeySizeBits; +}; +typedef struct vgids_container_map_entry vgidsContainerMapEntry; + +struct vgids_filesys_table_entry +{ + char directory[9]; + char filename[9]; + UINT16 pad0; + UINT16 dataObjectIdentifier; + UINT16 pad1; + UINT16 fileIdentifier; + UINT16 unknown; +}; +typedef struct vgids_filesys_table_entry vgidsFilesysTableEntry; + +struct vgids_keymap_record +{ + UINT32 state; + BYTE algid; + BYTE keytype; + UINT16 keyref; + UINT16 unknownWithFFFF; + UINT16 unknownWith0000; +}; +typedef struct vgids_keymap_record vgidsKeymapRecord; + +#pragma pack(pop) + +static vgidsEF* vgids_ef_new(vgidsContext* ctx, USHORT id) +{ + vgidsEF* ef = calloc(1, sizeof(vgidsEF)); + + ef->id = id; + ef->data = Stream_New(NULL, 1024); + if (!ef->data) + { + WLog_ERR(TAG, "Failed to create file data stream"); + goto create_failed; + } + Stream_SetLength(ef->data, 0); + + if (!ArrayList_Append(ctx->files, ef)) + { + WLog_ERR(TAG, "Failed to add new ef to file list"); + goto create_failed; + } + + return ef; + +create_failed: + free(ef); + return NULL; +} + +static BOOL vgids_write_tlv(wStream* s, UINT16 tag, const BYTE* data, DWORD dataSize) +{ + /* A maximum of 5 additional bytes is needed */ + if (!Stream_EnsureRemainingCapacity(s, dataSize + 5)) + { + WLog_ERR(TAG, "Failed to ensure capacity of DO stream"); + return FALSE; + } + + /* BER encoding: If the most-significant bit is set (0x80) the length is encoded in the + * remaining bits. So lengths < 128 bytes can be set directly, all others are encoded */ + if (tag > 0xFF) + Stream_Write_UINT16_BE(s, tag); + else + Stream_Write_UINT8(s, (BYTE)tag); + if (dataSize < 128) + { + Stream_Write_UINT8(s, (BYTE)dataSize); + } + else if (dataSize < 256) + { + Stream_Write_UINT8(s, 0x81); + Stream_Write_UINT8(s, (BYTE)dataSize); + } + else + { + Stream_Write_UINT8(s, 0x82); + Stream_Write_UINT16_BE(s, (UINT16)dataSize); + } + Stream_Write(s, data, dataSize); + Stream_SealLength(s); + return TRUE; +} + +static BOOL vgids_ef_write_do(vgidsEF* ef, UINT16 doID, const BYTE* data, DWORD dataSize) +{ + /* Write DO to end of file: 2-Byte ID, 1-Byte Len, Data */ + return vgids_write_tlv(ef->data, doID, data, dataSize); +} + +static BOOL vgids_ef_read_do(vgidsEF* ef, UINT16 doID, BYTE** data, DWORD* dataSize) +{ + /* Read the given DO from the file: 2-Byte ID, 1-Byte Len, Data */ + if (!Stream_SetPosition(ef->data, 0)) + { + WLog_ERR(TAG, "Failed to seek to front of file"); + return FALSE; + } + + /* Look for the requested DO */ + while (Stream_GetRemainingLength(ef->data) > 3) + { + BYTE len; + size_t curPos; + UINT16 doSize; + UINT16 nextDOID; + + curPos = Stream_GetPosition(ef->data); + Stream_Read_UINT16_BE(ef->data, nextDOID); + Stream_Read_UINT8(ef->data, len); + if ((len & 0x80)) + { + BYTE lenSize = len & 0x7F; + if (Stream_GetRemainingLength(ef->data) < lenSize) + { + WLog_ERR(TAG, "Remaining length is smaller than constructed tag length"); + return FALSE; + } + + switch (lenSize) + { + case 1: + Stream_Read_UINT8(ef->data, doSize); + break; + case 2: + Stream_Read_UINT16_BE(ef->data, doSize); + break; + default: + WLog_ERR(TAG, "Unexpected tag length %" PRIu8, lenSize); + return FALSE; + } + } + else + doSize = len; + + if (Stream_GetRemainingLength(ef->data) < doSize) + { + WLog_ERR(TAG, "Unexpected end for DO %04" PRIx16, nextDOID); + return FALSE; + } + + if (nextDOID == doID) + { + BYTE* outData; + + /* Include Tag and length in result */ + doSize += (UINT16)(Stream_GetPosition(ef->data) - curPos); + outData = malloc(doSize); + if (!outData) + { + WLog_ERR(TAG, "Failed to allocate output buffer"); + return FALSE; + } + + Stream_SetPosition(ef->data, curPos); + Stream_Read(ef->data, outData, doSize); + *data = outData; + *dataSize = doSize; + return TRUE; + } + else + { + /* Skip DO */ + Stream_SafeSeek(ef->data, doSize); + } + } + + return FALSE; +} + +static void vgids_ef_free(void* ptr) +{ + vgidsEF* ef = ptr; + if (ef) + { + Stream_Free(ef->data, TRUE); + free(ef); + } +} + +static BOOL vgids_prepare_fstable(const vgidsFilesysTableEntry* fstable, DWORD numEntries, + BYTE** outData, DWORD* outDataSize) +{ + /* Filesystem table: + BYTE unkonwn: 0x01 + Array of vgidsFilesysTableEntry + */ + DWORD i; + BYTE* data = malloc(sizeof(vgidsFilesysTableEntry) * numEntries + 1); + if (!data) + { + WLog_ERR(TAG, "Failed to allocate filesystem table data blob"); + return FALSE; + } + + *data = 0x01; + for (i = 0; i < numEntries; ++i) + memcpy(data + 1 + (sizeof(vgidsFilesysTableEntry) * i), &fstable[i], + sizeof(vgidsFilesysTableEntry)); + + *outData = data; + *outDataSize = sizeof(vgidsFilesysTableEntry) * numEntries + 1; + + return TRUE; +} + +static BOOL vgids_prepare_certificate(X509* cert, BYTE** kxc, DWORD* kxcSize) +{ + /* Key exchange container: + UINT16 compression version: 0001 + UINT16 source size + ZLIB compressed cert + */ + BYTE* i2dParam; + uLongf destSize; + wStream* s = NULL; + BYTE* certData = NULL; + BYTE* comprData = NULL; + int certSize = i2d_X509(cert, NULL); + if (certSize < 0) + { + WLog_ERR(TAG, "Failed to get certificate size"); + goto handle_error; + } + + certData = malloc(certSize); + comprData = malloc(certSize); + if (!certData || !comprData) + { + WLog_ERR(TAG, "Failed to allocate certificate buffer"); + goto handle_error; + } + + /* serialize certificate to buffer (out buffer pointer is modified so pass a copy!) */ + i2dParam = certData; + if (i2d_X509(cert, &i2dParam) < 0) + { + WLog_ERR(TAG, "Failed to encode X509 certificate to DER"); + goto handle_error; + } + + /* compress certificate data */ + destSize = certSize; + if (compress(comprData, &destSize, certData, certSize) != Z_OK) + { + WLog_ERR(TAG, "Failed to compress certificate data"); + goto handle_error; + } + + /* Write container data */ + s = Stream_New(NULL, destSize + 4); + Stream_Write_UINT16(s, 0x0001); + Stream_Write_UINT16(s, (UINT16)certSize); + Stream_Write(s, comprData, destSize); + Stream_SealLength(s); + + *kxc = Stream_Buffer(s); + *kxcSize = (DWORD)Stream_Length(s); + + Stream_Free(s, FALSE); + free(certData); + free(comprData); + return TRUE; + +handle_error: + Stream_Free(s, TRUE); + free(certData); + free(comprData); + return FALSE; +} + +static BYTE vgids_get_algid(vgidsContext* p_Ctx) +{ + int modulusSize = RSA_size(p_Ctx->privateKey); + switch (modulusSize) + { + case (1024 / 8): + return VGIDS_ALGID_RSA_1024; + case (2048 / 8): + return VGIDS_ALGID_RSA_2048; + case (3072 / 8): + return VGIDS_ALGID_RSA_3072; + case (4096 / 8): + return VGIDS_ALGID_RSA_4096; + default: + WLog_ERR(TAG, "Failed to determine algid for private key"); + break; + } + + return 0; +} + +static BOOL vgids_prepare_keymap(vgidsContext* context, BYTE** outData, DWORD* outDataSize) +{ + /* Key map record table: + BYTE unkonwn (count?): 0x01 + Array of vgidsKeymapRecord + */ + BYTE* data; + vgidsKeymapRecord record = { + 1, /* state */ + 0, /* algo */ + VGIDS_KEY_TYPE_KEYEXCHANGE, /* keytpe */ + (0xB000 | VGIDS_DEFAULT_KEY_REF), /* keyref */ + 0xFFFF, /* unknown FFFF */ + 0x0000 /* unknown 0000 */ + }; + + /* Determine algo */ + BYTE algid = vgids_get_algid(context); + if (algid == 0) + return FALSE; + + data = malloc(sizeof(record) + 1); + if (!data) + { + WLog_ERR(TAG, "Failed to allocate filesystem table data blob"); + return FALSE; + } + + *data = 0x01; + record.algid = algid; + memcpy(data + 1, &record, sizeof(record)); + + *outData = data; + *outDataSize = sizeof(record) + 1; + + return TRUE; +} + +static BOOL vgids_parse_apdu_header(wStream* s, BYTE* cla, BYTE* ins, BYTE* p1, BYTE* p2, BYTE* lc, + BYTE* le) +{ + if (Stream_GetRemainingLength(s) < 4) + { + WLog_ERR(TAG, "APDU header with less than 5 bytes"); + return FALSE; + } + + /* Read and verify APDU data */ + if (cla) + Stream_Read_UINT8(s, *cla); + else + Stream_Seek(s, 1); + if (ins) + Stream_Read_UINT8(s, *ins); + else + Stream_Seek(s, 1); + if (p1) + Stream_Read_UINT8(s, *p1); + else + Stream_Seek(s, 1); + if (p2) + Stream_Read_UINT8(s, *p2); + else + Stream_Seek(s, 1); + + /* If LC is requested - check remaining length and read as well */ + if (lc) + { + if (Stream_GetRemainingLength(s) < 1) + return FALSE; + + Stream_Read_UINT8(s, *lc); + if (Stream_GetRemainingLength(s) < *lc) + { + WLog_ERR(TAG, "The LC byte is greater than the remaining command data"); + return FALSE; + } + } + + /* read LE */ + if (le) + { + if (Stream_GetRemainingLength(s) < 1) + { + WLog_ERR(TAG, "Missing LE byte"); + return FALSE; + } + Stream_Read_UINT8(s, *le); + } + + return TRUE; +} + +static BOOL vgids_create_response(UINT16 status, const BYTE* answer, DWORD answerSize, + BYTE** outData, DWORD* outDataSize) +{ + BYTE* out = malloc(answerSize + 2); + if (!out) + { + WLog_ERR(TAG, "Failed to allocate memory for response data"); + return FALSE; + } + + *outData = out; + if (answer) + { + memcpy(out, answer, answerSize); + out += answerSize; + } + + *out = (BYTE)((status >> 8) & 0xFF); + *(out + 1) = (BYTE)(status & 0xFF); + *outDataSize = answerSize + 2; + return TRUE; +} + +static BOOL vgids_read_do_fkt(void* data, size_t index, va_list ap) +{ + BYTE* response; + DWORD responseSize; + vgidsEF* file = (vgidsEF*)data; + vgidsContext* context = va_arg(ap, vgidsContext*); + UINT16 efID = (UINT16)va_arg(ap, unsigned); + UINT16 doID = (UINT16)va_arg(ap, unsigned); + WINPR_UNUSED(index); + + if (efID == 0x3FFF || efID == file->id) + { + /* If the DO was successfully read - abort file enum */ + if (vgids_ef_read_do(file, doID, &response, &responseSize)) + { + context->responseData = Stream_New(response, (size_t)responseSize); + return FALSE; + } + } + + return TRUE; +} + +static void vgids_read_do(vgidsContext* context, UINT16 efID, UINT16 doID) +{ + ArrayList_ForEach(context->files, vgids_read_do_fkt, context, efID, doID); +} + +static void vgids_reset_context_response(vgidsContext* context) +{ + Stream_Free(context->responseData, TRUE); + context->responseData = NULL; +} + +static void vgids_reset_context_command_data(vgidsContext* context) +{ + Stream_Free(context->commandData, TRUE); + context->commandData = NULL; +} + +static BOOL vgids_ins_select(vgidsContext* context, wStream* s, BYTE** response, + DWORD* responseSize) +{ + BYTE p1, p2, lc; + DWORD resultDataSize = 0; + const BYTE* resultData = NULL; + UINT16 status = ISO_STATUS_SUCCESS; + + /* The only select operations performed are either select by AID or select 3FFF (return + * information about the currently selected DF) */ + if (!vgids_parse_apdu_header(s, NULL, NULL, &p1, &p2, &lc, NULL)) + return FALSE; + + /* Check P1 for selection mode */ + switch (p1) + { + /* Select by AID */ + case 0x04: + { + /* read AID from APDU */ + BYTE aid[ISO_AID_MAX_SIZE] = { 0 }; + if (lc > ISO_AID_MAX_SIZE) + { + WLog_ERR(TAG, "The LC byte is greater than the maximum AID length"); + status = ISO_STATUS_INVALIDLC; + break; + } + + /* Check if we select MS GIDS App (only one we know) */ + Stream_Read(s, aid, lc); + if (memcmp(aid, g_MsGidsAID, lc) != 0) + { + status = ISO_STATUS_FILENOTFOUND; + break; + } + + /* Return FCI or FCP for MsGids App */ + switch (p2) + { + /* Return FCI information */ + case 0x00: + { + resultData = g_GidsAppFCI; + resultDataSize = sizeof(g_GidsAppFCI); + break; + } + /* Return FCP information */ + case 0x04: + { + resultData = g_GidsAppFCP; + resultDataSize = sizeof(g_GidsAppFCP); + break; + } + default: + status = ISO_STATUS_INVALIDP1P2; + break; + } + + if (resultData) + context->currentDF = ISO_FID_MF; + break; + } + /* Select by FID */ + case 0x00: + { + /* read FID from APDU */ + UINT16 fid; + if (lc > 2) + { + WLog_ERR(TAG, "The LC byte for the file ID is greater than 2"); + status = ISO_STATUS_INVALIDLC; + break; + } + + Stream_Read_UINT16_BE(s, fid); + if (fid != VGIDS_EFID_CURRENTDF || context->currentDF == 0) + { + status = ISO_STATUS_FILENOTFOUND; + break; + } + break; + } + default: + { + /* P1 P2 combination not supported */ + status = ISO_STATUS_INVALIDP1P2; + break; + } + } + + return vgids_create_response(status, resultData, resultDataSize, response, responseSize); +} + +static UINT16 vgids_handle_chained_response(vgidsContext* context, const BYTE** response, + DWORD* responseSize) +{ + /* Cap to a maximum of 256 bytes and set status to more data */ + UINT16 status = ISO_STATUS_SUCCESS; + DWORD remainingBytes = (DWORD)Stream_Length(context->responseData); + if (remainingBytes > 256) + { + status = ISO_STATUS_MORE_DATA; + remainingBytes = 256; + } + + *response = Stream_Buffer(context->responseData); + *responseSize = remainingBytes; + Stream_Seek(context->responseData, remainingBytes); + + /* Check if there are more than 256 bytes left or if we can already provide the remaining length + * in the status word */ + remainingBytes = (DWORD)(Stream_Length(context->responseData) - remainingBytes); + if (remainingBytes < 256 && remainingBytes != 0) + status |= (remainingBytes & 0xFF); + return status; +} + +static BOOL vgids_get_public_key(vgidsContext* context, UINT16 doTag) +{ + BYTE* buf = NULL; + wStream* pubKey = NULL; + wStream* response = NULL; + const BIGNUM *n, *e; + int nSize, eSize; + + /* Get key components */ + RSA_get0_key(context->publicKey, &n, &e, NULL); + nSize = BN_num_bytes(n); + eSize = BN_num_bytes(e); + + buf = malloc(nSize > eSize ? nSize : eSize); + if (!buf) + { + WLog_ERR(TAG, "Failed to allocate buffer for public key"); + goto handle_error; + } + + pubKey = Stream_New(NULL, nSize + eSize + 0x10); + if (!pubKey) + { + WLog_ERR(TAG, "Failed to allocate public key stream"); + goto handle_error; + } + + response = Stream_New(NULL, Stream_Capacity(pubKey) + 0x10); + if (!response) + { + WLog_ERR(TAG, "Failed to allocate response stream"); + goto handle_error; + } + + /* write modulus and exponent DOs */ + BN_bn2bin(n, buf); + if (!vgids_write_tlv(pubKey, 0x81, buf, nSize)) + goto handle_error; + + BN_bn2bin(e, buf); + if (!vgids_write_tlv(pubKey, 0x82, buf, eSize)) + goto handle_error; + + /* write ISO public key template */ + if (!vgids_write_tlv(response, doTag, Stream_Buffer(pubKey), (DWORD)Stream_Length(pubKey))) + goto handle_error; + + /* set response data */ + Stream_SetPosition(response, 0); + context->responseData = response; + + free(buf); + Stream_Free(pubKey, TRUE); + return TRUE; + +handle_error: + free(buf); + Stream_Free(pubKey, TRUE); + Stream_Free(response, TRUE); + return FALSE; +} + +static BOOL vgids_ins_getdata(vgidsContext* context, wStream* s, BYTE** response, + DWORD* responseSize) +{ + UINT16 doId; + UINT16 fileId; + BYTE p1, p2, lc; + DWORD resultDataSize = 0; + const BYTE* resultData = NULL; + UINT16 status = ISO_STATUS_SUCCESS; + + /* GetData is called a lot! + - To retrieve DOs from files + - To retrieve public key information + */ + if (!vgids_parse_apdu_header(s, NULL, NULL, &p1, &p2, &lc, NULL)) + return FALSE; + + /* free any previous queried data */ + vgids_reset_context_response(context); + + /* build up file identifier */ + fileId = ((UINT16)p1 << 8) | p2; + + /* Do we have a DO reference? */ + switch (lc) + { + case 4: + { + BYTE tag, length; + Stream_Read_UINT8(s, tag); + Stream_Read_UINT8(s, length); + if (tag != 0x5C && length != 0x02) + { + status = ISO_STATUS_INVALIDCOMMANDDATA; + break; + } + + Stream_Read_UINT16_BE(s, doId); + vgids_read_do(context, fileId, doId); + break; + } + case 0xA: + { + UINT16 pubKeyDO; + BYTE tag, length, keyRef; + + /* We want to retrieve the public key? */ + if (p1 != 0x3F && p2 != 0xFF) + { + status = ISO_STATUS_INVALIDP1P2; + break; + } + + /* read parent tag/length */ + Stream_Read_UINT8(s, tag); + Stream_Read_UINT8(s, length); + if (tag != 0x70 || length != 0x08) + { + status = ISO_STATUS_INVALIDCOMMANDDATA; + break; + } + + /* read key reference TLV */ + Stream_Read_UINT8(s, tag); + Stream_Read_UINT8(s, length); + Stream_Read_UINT8(s, keyRef); + if (tag != 0x84 || length != 0x01 || keyRef != VGIDS_DEFAULT_KEY_REF) + { + status = ISO_STATUS_INVALIDCOMMANDDATA; + break; + } + + /* read key value template TLV */ + Stream_Read_UINT8(s, tag); + Stream_Read_UINT8(s, length); + if (tag != 0xA5 || length != 0x03) + { + status = ISO_STATUS_INVALIDCOMMANDDATA; + break; + } + + Stream_Read_UINT16_BE(s, pubKeyDO); + Stream_Read_UINT8(s, length); + if (pubKeyDO != 0x7F49 || length != 0x80) + { + status = ISO_STATUS_INVALIDCOMMANDDATA; + break; + } + + if (Stream_GetRemainingLength(s) < 1) + { + status = ISO_STATUS_INVALIDLC; + break; + } + + /* Return public key value */ + vgids_get_public_key(context, pubKeyDO); + break; + } + default: + status = ISO_STATUS_INVALIDCOMMANDDATA; + break; + } + + /* If we have response data, make it ready for return */ + if (context->responseData) + status = vgids_handle_chained_response(context, &resultData, &resultDataSize); + else if (status == ISO_STATUS_SUCCESS) + status = ISO_STATUS_REFERENCEDATANOTFOUND; + + return vgids_create_response(status, resultData, resultDataSize, response, responseSize); +} + +static BOOL vgids_ins_manage_security_environment(vgidsContext* context, wStream* s, + BYTE** response, DWORD* responseSize) +{ + BYTE tag, length; + BYTE p1, p2, lc; + DWORD resultDataSize = 0; + const BYTE* resultData = NULL; + UINT16 status = ISO_STATUS_SUCCESS; + + vgids_reset_context_command_data(context); + vgids_reset_context_response(context); + + /* Manage security environment prepares the card for performing crypto operations. */ + if (!vgids_parse_apdu_header(s, NULL, NULL, &p1, &p2, &lc, NULL)) + return FALSE; + + /* Check APDU params */ + /* P1: Set Computation, decipherment, Internal Auth */ + /* P2: Digital Signature (B6), Confidentiality (B8) */ + if (p1 != 0x41 && p2 != 0xB6 && p2 != 0xB8) + { + status = ISO_STATUS_INVALIDP1P2; + goto create_response; + } + + if (lc != 6) + { + status = ISO_STATUS_WRONGLC; + goto create_response; + } + + context->currentSE.crt = p2; + + /* parse command buffer */ + /* Read algo ID */ + Stream_Read_UINT8(s, tag); + Stream_Read_UINT8(s, length); + if (tag != 0x80 || length != 0x01) + { + status = ISO_STATUS_INVALIDCOMMANDDATA; + goto create_response; + } + Stream_Read_UINT8(s, context->currentSE.algoId); + + /* Read private key reference */ + Stream_Read_UINT8(s, tag); + Stream_Read_UINT8(s, length); + if (tag != 0x84 || length != 0x01) + { + status = ISO_STATUS_INVALIDCOMMANDDATA; + goto create_response; + } + Stream_Read_UINT8(s, context->currentSE.keyRef); + +create_response: + /* If an error occured reset SE */ + if (status != ISO_STATUS_SUCCESS) + memset(&context->currentSE, 0, sizeof(context->currentSE)); + return vgids_create_response(status, resultData, resultDataSize, response, responseSize); +} + +static BOOL vgids_perform_digital_signature(vgidsContext* context) +{ + size_t sigSize, msgSize; + EVP_PKEY_CTX* ctx = NULL; + EVP_PKEY* pk = EVP_PKEY_new(); + vgidsDigestInfoMap gidsDigestInfo[VGIDS_MAX_DIGEST_INFO] = { + { g_PKCS1_SHA1, sizeof(g_PKCS1_SHA1), EVP_sha1() }, + { g_PKCS1_SHA224, sizeof(g_PKCS1_SHA224), EVP_sha224() }, + { g_PKCS1_SHA256, sizeof(g_PKCS1_SHA256), EVP_sha256() }, + { g_PKCS1_SHA384, sizeof(g_PKCS1_SHA384), EVP_sha384() }, + { g_PKCS1_SHA512, sizeof(g_PKCS1_SHA512), EVP_sha512() }, + { g_PKCS1_SHA512_224, sizeof(g_PKCS1_SHA512_224), EVP_sha512_224() }, + { g_PKCS1_SHA512_256, sizeof(g_PKCS1_SHA512_256), EVP_sha512_256() } + }; + + if (!pk) + { + WLog_ERR(TAG, "Failed to create PKEY"); + return FALSE; + } + + EVP_PKEY_set1_RSA(pk, context->privateKey); + vgids_reset_context_response(context); + + /* for each digest info */ + Stream_SetPosition(context->commandData, 0); + for (int i = 0; i < VGIDS_MAX_DIGEST_INFO; ++i) + { + /* have we found our digest? */ + const vgidsDigestInfoMap* digest = &gidsDigestInfo[i]; + if (Stream_Length(context->commandData) >= digest->infoSize && + memcmp(Stream_Buffer(context->commandData), digest->info, digest->infoSize) == 0) + { + /* skip digest info and calculate message size */ + Stream_Seek(context->commandData, digest->infoSize); + if (Stream_GetRemainingLength(context->commandData) <= 1) + goto sign_failed; + msgSize = Stream_GetRemainingLength(context->commandData); + + /* setup signing context */ + ctx = EVP_PKEY_CTX_new(pk, NULL); + if (!ctx) + { + WLog_ERR(TAG, "Failed to create signing context"); + goto sign_failed; + } + + if (EVP_PKEY_sign_init(ctx) <= 0) + { + WLog_ERR(TAG, "Failed to init signing context"); + goto sign_failed; + } + + /* set padding and signature algo */ + if (context->currentSE.algoId & VGIDS_SE_ALGOID_DST_PAD_PKCS1) + { + if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0) + { + WLog_ERR(TAG, "Failed to set padding mode"); + goto sign_failed; + } + } + + if (EVP_PKEY_CTX_set_signature_md(ctx, digest->digest) <= 0) + { + WLog_ERR(TAG, "Failed to set signing mode"); + goto sign_failed; + } + + /* Determine buffer length */ + if (EVP_PKEY_sign(ctx, NULL, &sigSize, Stream_Pointer(context->commandData), msgSize) <= + 0) + { + WLog_ERR(TAG, "Failed to determine signature size"); + goto sign_failed; + } + + context->responseData = Stream_New(NULL, sigSize); + if (!context->responseData) + { + WLog_ERR(TAG, "Failed to allocate signing buffer"); + goto sign_failed; + } + + /* sign */ + if (EVP_PKEY_sign(ctx, Stream_Buffer(context->responseData), &sigSize, + Stream_Pointer(context->commandData), msgSize) <= 0) + { + WLog_ERR(TAG, "Failed to create signature"); + goto sign_failed; + } + + Stream_SetLength(context->responseData, sigSize); + EVP_PKEY_CTX_free(ctx); + break; + } + } + + EVP_PKEY_free(pk); + vgids_reset_context_command_data(context); + return TRUE; + +sign_failed: + vgids_reset_context_command_data(context); + vgids_reset_context_response(context); + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(pk); + return FALSE; +} + +static BOOL vgids_perform_decrypt(vgidsContext* context) +{ + int res; + int padding = RSA_NO_PADDING; + + vgids_reset_context_response(context); + + /* determine padding */ + if (context->currentSE.algoId & VGIDS_SE_ALGOID_CT_PAD_PKCS1) + padding = RSA_PKCS1_PADDING; + else if (context->currentSE.algoId & VGIDS_SE_ALGOID_CT_PAD_OAEP) + padding = RSA_PKCS1_OAEP_PADDING; + + /* init response buffer */ + context->responseData = Stream_New(NULL, RSA_size(context->privateKey)); + if (!context->responseData) + { + WLog_ERR(TAG, "Failed to create decryption buffer"); + goto decrypt_failed; + } + + /* Determine buffer length */ + res = RSA_private_decrypt((int)Stream_Length(context->commandData), + Stream_Buffer(context->commandData), + Stream_Buffer(context->responseData), context->privateKey, padding); + if (res < 0) + { + WLog_ERR(TAG, "Failed to decrypt data"); + goto decrypt_failed; + } + + Stream_SetLength(context->responseData, res); + vgids_reset_context_command_data(context); + return TRUE; + +decrypt_failed: + vgids_reset_context_command_data(context); + vgids_reset_context_response(context); + return FALSE; +} + +static BOOL vgids_ins_perform_security_operation(vgidsContext* context, wStream* s, BYTE** response, + DWORD* responseSize) +{ + BYTE cla, p1, p2, lc; + DWORD resultDataSize = 0; + const BYTE* resultData = NULL; + UINT16 status = ISO_STATUS_SUCCESS; + + /* Perform security operation */ + if (!vgids_parse_apdu_header(s, &cla, NULL, &p1, &p2, &lc, NULL)) + return FALSE; + + if (lc == 0) + { + status = ISO_STATUS_WRONGLC; + goto create_response; + } + + /* Is our default key referenced? */ + if (context->currentSE.keyRef != VGIDS_DEFAULT_KEY_REF) + { + status = ISO_STATUS_SECURITYSTATUSNOTSATISFIED; + goto create_response; + } + + /* is the pin protecting the key verified? */ + if (!context->pinVerified) + { + status = ISO_STATUS_SECURITYSTATUSNOTSATISFIED; + goto create_response; + } + + /* Append the data to the context command buffer (PSO might chain command data) */ + if (!context->commandData) + { + context->commandData = Stream_New(NULL, lc); + if (!context->commandData) + return FALSE; + } + else + Stream_EnsureRemainingCapacity(context->commandData, lc); + + Stream_Write(context->commandData, Stream_Pointer(s), lc); + Stream_SealLength(context->commandData); + + /* Check if the correct operation is requested for our current SE */ + switch (context->currentSE.crt) + { + case VGIDS_SE_CRT_SIGN: + { + if (p1 != 0x9E || p2 != 0x9A) + { + status = ISO_STATUS_INVALIDP1P2; + break; + } + + /* If chaining is over perform op */ + if (!(cla & 0x10)) + vgids_perform_digital_signature(context); + break; + } + case VGIDS_SE_CRT_CONF: + { + if ((p1 != 0x86 || p2 != 0x80) && (p1 != 0x80 || p2 != 0x86)) + { + status = ISO_STATUS_INVALIDP1P2; + break; + } + + /* If chaining is over perform op */ + if (!(cla & 0x10)) + vgids_perform_decrypt(context); + break; + } + default: + status = ISO_STATUS_INVALIDP1P2; + break; + } + + /* Do chaining of response data if necessary */ + if (status == ISO_STATUS_SUCCESS && context->responseData) + status = vgids_handle_chained_response(context, &resultData, &resultDataSize); + + /* Check APDU params */ +create_response: + return vgids_create_response(status, resultData, resultDataSize, response, responseSize); +} + +static BOOL vgids_ins_getresponse(vgidsContext* context, wStream* s, BYTE** response, + DWORD* responseSize) +{ + BYTE p1, p2, le; + DWORD resultDataSize = 0; + const BYTE* resultData = NULL; + DWORD expectedLen, remainingSize; + UINT16 status = ISO_STATUS_SUCCESS; + + /* Get response continues data transfer after a previous get data command */ + /* Check if there is any data to transfer left */ + if (!context->responseData || Stream_GetRemainingLength(context->responseData) < 1) + { + status = ISO_STATUS_COMMANDNOTALLOWED; + goto create_response; + } + + if (!vgids_parse_apdu_header(s, NULL, NULL, &p1, &p2, NULL, &le)) + return FALSE; + + /* Check APDU params */ + if (p1 != 00 || p2 != 0x00) + { + status = ISO_STATUS_INVALIDP1P2; + goto create_response; + } + + /* LE = 0 means 256 bytes expected */ + expectedLen = le; + if (expectedLen == 0) + expectedLen = 256; + + /* prepare response size and update offset */ + remainingSize = (DWORD)Stream_GetRemainingLength(context->responseData); + if (remainingSize < expectedLen) + expectedLen = remainingSize; + + resultData = Stream_Pointer(context->responseData); + resultDataSize = expectedLen; + Stream_Seek(context->responseData, expectedLen); + + /* If more data is left return 61XX - otherwise 9000 */ + remainingSize = (DWORD)Stream_GetRemainingLength(context->responseData); + if (remainingSize > 0) + { + status = ISO_STATUS_MORE_DATA; + if (remainingSize < 256) + status |= (remainingSize & 0xFF); + } + +create_response: + return vgids_create_response(status, resultData, resultDataSize, response, responseSize); +} + +static BOOL vgids_ins_verify(vgidsContext* context, wStream* s, BYTE** response, + DWORD* responseSize) +{ + BYTE ins, p1, p2, lc; + UINT16 status = ISO_STATUS_SUCCESS; + char pin[VGIDS_MAX_PIN_SIZE + 1] = { 0 }; + + /* Verify is always called for the application password (PIN) P2=0x80 */ + if (!vgids_parse_apdu_header(s, NULL, &ins, &p1, &p2, NULL, NULL)) + return FALSE; + + /* Check APDU params */ + if (p1 != 00 && p2 != 0x80 && p2 != 0x82) + { + status = ISO_STATUS_INVALIDP1P2; + goto create_response; + } + + /* shall we reset the security state? */ + if (p2 == 0x82) + { + context->pinVerified = FALSE; + goto create_response; + } + + /* Check if pin is not already blocked */ + if (context->curRetryCounter == 0) + { + status = ISO_STATUS_AUTHMETHODBLOCKED; + goto create_response; + } + + /* Read and verify LC */ + if (Stream_GetRemainingLength(s) < 1) + { + status = ISO_STATUS_INVALIDLC; + goto create_response; + } + + Stream_Read_UINT8(s, lc); + if (Stream_GetRemainingLength(s) < lc || lc > VGIDS_MAX_PIN_SIZE) + { + status = ISO_STATUS_INVALIDLC; + goto create_response; + } + + /* read and verify pin */ + Stream_Read(s, pin, lc); + if (strcmp(context->pin, pin) != 0) + { + /* retries are encoded in the lowest 4-bit of the status code */ + --context->curRetryCounter; + context->pinVerified = FALSE; + status = (ISO_STATUS_VERIFYFAILED | (context->curRetryCounter & 0xFF)); + } + else + { + /* reset retry counter and mark pin as verified */ + context->curRetryCounter = context->retryCounter; + context->pinVerified = TRUE; + } + +create_response: + return vgids_create_response(status, NULL, 0, response, responseSize); +} + +vgidsContext* vgids_new() +{ + wObject* obj; + vgidsContext* ctx = calloc(1, sizeof(vgidsContext)); + + ctx->files = ArrayList_New(FALSE); + if (!ctx->files) + { + WLog_ERR(TAG, "Failed to create files array list"); + goto create_failed; + } + + obj = ArrayList_Object(ctx->files); + obj->fnObjectFree = vgids_ef_free; + + return ctx; + +create_failed: + vgids_free(ctx); + return NULL; +} + +BOOL vgids_init(vgidsContext* ctx, const char* cert, const char* privateKey, const char* pin) +{ + DWORD kxcSize; + DWORD keymapSize; + DWORD fsTableSize; + BOOL rc = FALSE; + BIO* certBio = NULL; + BIO* privateKeyBio = NULL; + BYTE* kxc = NULL; + BYTE* keymap = NULL; + BYTE* fsTable = NULL; + EVP_PKEY* pubKey = NULL; + vgidsEF* masterEF = NULL; + vgidsEF* cardidEF = NULL; + vgidsEF* commonEF = NULL; + BYTE cardid[VGIDS_CARDID_SIZE] = { 0 }; + vgidsContainerMapEntry cmrec = { { 'P', 'r', 'i', 'v', 'a', 't', 'e', ' ', 'K', 'e', 'y', ' ', + '0', '0' }, + CONTAINER_MAP_VALID_CONTAINER | + CONTAINER_MAP_DEFAULT_CONTAINER, + 0, + 0, + 0x00 /* key-size in bits - filled out later */ }; + vgidsFilesysTableEntry filesys[] = { + { "mscp", "", 0, 0, 0, 0xA000, 0 }, + { "", "cardid", 0, 0xDF20, 0, 0xA012, 0 }, + { "", "cardapps", 0, 0xDF21, 0, 0xA010, 0 }, + { "", "cardcf", 0, 0xDF22, 0, 0xA010, 0 }, + { "mscp", "cmapfile", 0, 0xDF23, 0, 0xA010, 0 }, + { "mscp", "kxc00", 0, 0xDF24, 0, 0xA010, 0 }, + }; + + /* Check params */ + if (!cert || !privateKey || !pin) + { + WLog_ERR(TAG, "Passed invalid NULL pointer argument"); + goto init_failed; + } + + /* Convert PEM input to DER certificate/public key/private key */ + certBio = BIO_new_mem_buf((const void*)cert, (int)strlen(cert)); + if (!certBio) + goto init_failed; + ctx->certificate = PEM_read_bio_X509(certBio, NULL, NULL, NULL); + if (!ctx->certificate) + goto init_failed; + pubKey = X509_get_pubkey(ctx->certificate); + if (!pubKey) + goto init_failed; + ctx->publicKey = EVP_PKEY_get1_RSA(pubKey); + if (!ctx->publicKey) + goto init_failed; + + privateKeyBio = BIO_new_mem_buf((const void*)privateKey, (int)strlen(privateKey)); + if (!privateKeyBio) + goto init_failed; + ctx->privateKey = PEM_read_bio_RSAPrivateKey(privateKeyBio, NULL, NULL, NULL); + if (!ctx->privateKey) + goto init_failed; + + /* create masterfile */ + masterEF = vgids_ef_new(ctx, VGIDS_EFID_MASTER); + if (!masterEF) + goto init_failed; + + /* create cardid file with cardid DO */ + cardidEF = vgids_ef_new(ctx, VGIDS_EFID_CARDID); + if (!cardidEF) + goto init_failed; + RAND_bytes(cardid, sizeof(cardid)); + if (!vgids_ef_write_do(cardidEF, VGIDS_DO_CARDID, cardid, sizeof(cardid))) + goto init_failed; + + /* create user common file */ + commonEF = vgids_ef_new(ctx, VGIDS_EFID_COMMON); + if (!commonEF) + goto init_failed; + + /* write card cache DO */ + if (!vgids_ef_write_do(commonEF, VGIDS_DO_CARDCF, g_CardCFContents, sizeof(g_CardCFContents))) + goto init_failed; + + /* write container map DO */ + cmrec.wKeyExchangeKeySizeBits = (WORD)(RSA_size(ctx->privateKey) * 8); + if (!vgids_ef_write_do(commonEF, VGIDS_DO_CMAPFILE, (BYTE*)&cmrec, sizeof(cmrec))) + goto init_failed; + + /* write cardapps DO */ + if (!vgids_ef_write_do(commonEF, VGIDS_DO_CARDAPPS, g_CardAppsContents, + sizeof(g_CardAppsContents))) + goto init_failed; + + /* convert and write certificate to key exchange container */ + if (!vgids_prepare_certificate(ctx->certificate, &kxc, &kxcSize)) + goto init_failed; + if (!vgids_ef_write_do(commonEF, VGIDS_DO_KXC00, kxc, kxcSize)) + goto init_failed; + + /* prepare and write file system table */ + if (!vgids_prepare_fstable(filesys, ARRAYSIZE(filesys), &fsTable, &fsTableSize)) + goto init_failed; + if (!vgids_ef_write_do(masterEF, VGIDS_DO_FILESYSTEMTABLE, fsTable, fsTableSize)) + goto init_failed; + + /* vgids_prepare_keymap and write to masterEF */ + if (!vgids_prepare_keymap(ctx, &keymap, &keymapSize)) + goto init_failed; + if (!vgids_ef_write_do(masterEF, VGIDS_DO_KEYMAP, keymap, keymapSize)) + goto init_failed; + + /* store user pin */ + ctx->curRetryCounter = ctx->retryCounter = VGIDS_DEFAULT_RETRY_COUNTER; + ctx->pin = _strdup(pin); + if (!ctx->pin) + goto init_failed; + + rc = TRUE; + +init_failed: + EVP_PKEY_free(pubKey); + BIO_free_all(certBio); + BIO_free_all(privateKeyBio); + free(kxc); + free(keymap); + free(fsTable); + return rc; +} + +BOOL vgids_process_apdu(vgidsContext* context, const BYTE* data, DWORD dataSize, BYTE** response, + DWORD* responseSize) +{ + wStream s; + static int x = 1; + + /* Check params */ + if (!context || !data || !response || !responseSize) + { + WLog_ERR(TAG, "Invalid NULL pointer passed"); + return FALSE; + } + + if (dataSize < 4) + { + WLog_ERR(TAG, "APDU buffer is less than 4 bytes: %" PRIu32, dataSize); + return FALSE; + } + + /* Examine INS byte */ + Stream_StaticConstInit(&s, data, dataSize); + if (x++ == 0xe) + x = 0xe + 1; + switch (data[1]) + { + case ISO_INS_SELECT: + return vgids_ins_select(context, &s, response, responseSize); + case ISO_INS_GETDATA: + return vgids_ins_getdata(context, &s, response, responseSize); + case ISO_INS_GETRESPONSE: + return vgids_ins_getresponse(context, &s, response, responseSize); + case ISO_INS_MSE: + return vgids_ins_manage_security_environment(context, &s, response, responseSize); + case ISO_INS_PSO: + return vgids_ins_perform_security_operation(context, &s, response, responseSize); + case ISO_INS_VERIFY: + return vgids_ins_verify(context, &s, response, responseSize); + default: + break; + } + + /* return command not allowed */ + return vgids_create_response(ISO_STATUS_COMMANDNOTALLOWED, NULL, 0, response, responseSize); +} + +void vgids_free(vgidsContext* context) +{ + if (context) + { + RSA_free(context->privateKey); + RSA_free(context->publicKey); + X509_free(context->certificate); + Stream_Free(context->commandData, TRUE); + Stream_Free(context->responseData, TRUE); + free(context->pin); + ArrayList_Free(context->files); + free(context); + } +} diff --git a/libfreerdp/emu/scard/smartcard_virtual_gids.h b/libfreerdp/emu/scard/smartcard_virtual_gids.h new file mode 100644 index 000000000..c59b00ec1 --- /dev/null +++ b/libfreerdp/emu/scard/smartcard_virtual_gids.h @@ -0,0 +1,57 @@ +/** + * WinPR: Windows Portable Runtime + * Virtual GIDS implementation + * + * Copyright 2021 Martin Fleisz + * Copyright 2021 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SMARTCARD_VIRTUAL_GIDS_H +#define WINPR_SMARTCARD_VIRTUAL_GIDS_H + +#include +#include +#include + +/* Virtual GIDS context */ +typedef struct vgids_context vgidsContext; + +/* Creates a new virtual gids context */ +vgidsContext* vgids_new(void); + +/* + Initializes the virtual gids context. + cert: PEM encoded smartcard certificate + privateKey: PEM encoded private key for the smartcard certificate + pin: Pin protecting the usage of the private key + Returns: TRUE on success, FALSE in case of an error +*/ +BOOL vgids_init(vgidsContext* ctx, const char* cert, const char* privateKey, const char* pin); + +/* + Processes the provided APDU returning a response for each processed command. + data: APDU byte stream + dataSize: size of the APDU provided in data + response: Pointer where the response buffer is stored to. Must be freed by caller! + responseSize: Size of the returned data buffer + Returns: TRUE on success, FALSE in case of an error +*/ +BOOL vgids_process_apdu(vgidsContext* context, const BYTE* data, DWORD dataSize, BYTE** response, + DWORD* responseSize); + +/* frees a previously created virtual gids context */ +void vgids_free(vgidsContext* context); + +#endif /* WINPR_SMARTCARD_VIRTUAL_GIDS_H */ diff --git a/resources/FreeRDP.ico b/resources/FreeRDP.ico index 0864d1cda..0f35c685e 100644 Binary files a/resources/FreeRDP.ico and b/resources/FreeRDP.ico differ diff --git a/winpr/libwinpr/smartcard/CMakeLists.txt b/winpr/libwinpr/smartcard/CMakeLists.txt index 5d67ec4e0..0e2701ed1 100644 --- a/winpr/libwinpr/smartcard/CMakeLists.txt +++ b/winpr/libwinpr/smartcard/CMakeLists.txt @@ -21,17 +21,26 @@ if(PCSC_WINPR_FOUND) winpr_definition_add(-DWITH_WINPR_PCSC) endif() -if(WITH_SMARTCARD_INSPECT) - winpr_definition_add(-DWITH_SMARTCARD_INSPECT) -endif() +option(WITH_SMARTCARD_PCSC "Enable smartcard PCSC backend" ON) + set(${MODULE_PREFIX}_SRCS smartcard.c - smartcard.h + smartcard.h) + +if(WITH_SMARTCARD_PCSC) + winpr_definition_add(-DWITH_SMARTCARD_PCSC) + list(APPEND ${MODULE_PREFIX}_SRCS smartcard_pcsc.c - smartcard_pcsc.h - smartcard_inspect.c - smartcard_inspect.h) + smartcard_pcsc.h) +endif() + +if(WITH_SMARTCARD_INSPECT) + winpr_definition_add(-DWITH_SMARTCARD_INSPECT) + list(APPEND ${MODULE_PREFIX}_SRCS + smartcard_inspect.c + smartcard_inspect.h) +endif() if(WIN32) list(APPEND ${MODULE_PREFIX}_SRCS diff --git a/winpr/libwinpr/smartcard/smartcard.c b/winpr/libwinpr/smartcard/smartcard.c index 22d6b03ed..946e7cb49 100644 --- a/winpr/libwinpr/smartcard/smartcard.c +++ b/winpr/libwinpr/smartcard/smartcard.c @@ -33,7 +33,9 @@ #include "smartcard.h" +#if defined(WITH_SMARTCARD_INSPECT) #include "smartcard_inspect.h" +#endif static INIT_ONCE g_Initialized = INIT_ONCE_STATIC_INIT; static const SCardApiFunctionTable* g_SCardApi = NULL; @@ -53,16 +55,24 @@ static const SCardApiFunctionTable* g_SCardApi = NULL; } \ return g_SCardApi->pfn##_name(__VA_ARGS__) -#define SCARDAPI_STUB_CALL_HANDLE(_name, ...) \ - InitOnceExecuteOnce(&g_Initialized, InitializeSCardApiStubs, NULL, NULL); \ - if (!g_SCardApi || !g_SCardApi->pfn##_name) \ - return NULL; \ +#define SCARDAPI_STUB_CALL_HANDLE(_name, ...) \ + InitOnceExecuteOnce(&g_Initialized, InitializeSCardApiStubs, NULL, NULL); \ + if (!g_SCardApi || !g_SCardApi->pfn##_name) \ + { \ + WLog_DBG(TAG, "Missing function pointer g_SCardApi=%p->" xstr(pfn##_name) "=%p", \ + g_SCardApi, g_SCardApi ? g_SCardApi->pfn##_name : NULL); \ + return NULL; \ + } \ return g_SCardApi->pfn##_name(__VA_ARGS__) -#define SCARDAPI_STUB_CALL_VOID(_name, ...) \ - InitOnceExecuteOnce(&g_Initialized, InitializeSCardApiStubs, NULL, NULL); \ - if (!g_SCardApi || !g_SCardApi->pfn##_name) \ - return; \ +#define SCARDAPI_STUB_CALL_VOID(_name, ...) \ + InitOnceExecuteOnce(&g_Initialized, InitializeSCardApiStubs, NULL, NULL); \ + if (!g_SCardApi || !g_SCardApi->pfn##_name) \ + { \ + WLog_DBG(TAG, "Missing function pointer g_SCardApi=%p->" xstr(pfn##_name) "=%p", \ + g_SCardApi, g_SCardApi ? g_SCardApi->pfn##_name : NULL); \ + return; \ + } \ g_SCardApi->pfn##_name(__VA_ARGS__) /** @@ -75,6 +85,7 @@ const SCARD_IO_REQUEST g_rgSCardRawPci = { SCARD_PROTOCOL_RAW, 8 }; static BOOL CALLBACK InitializeSCardApiStubs(PINIT_ONCE once, PVOID param, PVOID* context) { +#if defined(WITH_SMARTCARD_PCSC) #ifndef _WIN32 if (PCSC_InitializeSCardApi() >= 0) @@ -86,7 +97,8 @@ static BOOL CALLBACK InitializeSCardApiStubs(PINIT_ONCE once, PVOID param, PVOID g_SCardApi = WinSCard_GetSCardApiFunctionTable(); #endif -#ifdef WITH_SMARTCARD_INSPECT +#endif +#if defined(WITH_SMARTCARD_INSPECT) g_SCardApi = Inspect_RegisterSCardApi(g_SCardApi); #endif return TRUE; diff --git a/winpr/libwinpr/smartcard/smartcard_inspect.c b/winpr/libwinpr/smartcard/smartcard_inspect.c index b80bb859b..fc6681765 100644 --- a/winpr/libwinpr/smartcard/smartcard_inspect.c +++ b/winpr/libwinpr/smartcard/smartcard_inspect.c @@ -32,6 +32,41 @@ #include "smartcard_inspect.h" +#include "../log.h" +#define TAG WINPR_TAG("smartcard.inspect") + +#define xstr(s) str(s) +#define str(s) #s + +#define SCARDAPI_STUB_CALL_LONG(status, _name, ...) \ + if (!g_SCardApi || !g_SCardApi->pfn##_name) \ + { \ + WLog_DBG(TAG, "Missing function pointer g_SCardApi=%p->" xstr(pfn##_name) "=%p", \ + g_SCardApi, g_SCardApi ? g_SCardApi->pfn##_name : NULL); \ + status = SCARD_E_NO_SERVICE; \ + } \ + else \ + status = g_SCardApi->pfn##_name(__VA_ARGS__) + +#define SCARDAPI_STUB_CALL_HANDLE(status, _name, ...) \ + if (!g_SCardApi || !g_SCardApi->pfn##_name) \ + { \ + WLog_DBG(TAG, "Missing function pointer g_SCardApi=%p->" xstr(pfn##_name) "=%p", \ + g_SCardApi, g_SCardApi ? g_SCardApi->pfn##_name : NULL); \ + status = NULL; \ + } \ + else \ + status = g_SCardApi->pfn##_name(__VA_ARGS__) + +#define SCARDAPI_STUB_CALL_VOID(_name, ...) \ + if (!g_SCardApi || !g_SCardApi->pfn##_name) \ + { \ + WLog_DBG(TAG, "Missing function pointer g_SCardApi=%p->" xstr(pfn##_name) "=%p", \ + g_SCardApi, g_SCardApi ? g_SCardApi->pfn##_name : NULL); \ + } \ + else \ + g_SCardApi->pfn##_name(__VA_ARGS__) + static const DWORD g_LogLevel = WLOG_DEBUG; static wLog* g_Log = NULL; @@ -49,7 +84,8 @@ static LONG WINAPI Inspect_SCardEstablishContext(DWORD dwScope, LPCVOID pvReserv WLog_Print(g_Log, g_LogLevel, "SCardEstablishContext { dwScope: %s (0x%08" PRIX32 ")", SCardGetScopeString(dwScope), dwScope); - status = g_SCardApi->pfnSCardEstablishContext(dwScope, pvReserved1, pvReserved2, phContext); + SCARDAPI_STUB_CALL_LONG(status, SCardEstablishContext, dwScope, pvReserved1, pvReserved2, + phContext); WLog_Print(g_Log, g_LogLevel, "SCardEstablishContext } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -63,7 +99,7 @@ static LONG WINAPI Inspect_SCardReleaseContext(SCARDCONTEXT hContext) WLog_Print(g_Log, g_LogLevel, "SCardReleaseContext { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardReleaseContext(hContext); + SCARDAPI_STUB_CALL_LONG(status, SCardReleaseContext, hContext); WLog_Print(g_Log, g_LogLevel, "SCardReleaseContext } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -77,7 +113,7 @@ static LONG WINAPI Inspect_SCardIsValidContext(SCARDCONTEXT hContext) WLog_Print(g_Log, g_LogLevel, "SCardIsValidContext { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardIsValidContext(hContext); + SCARDAPI_STUB_CALL_LONG(status, SCardIsValidContext, hContext); WLog_Print(g_Log, g_LogLevel, "SCardIsValidContext } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -92,7 +128,7 @@ static LONG WINAPI Inspect_SCardListReaderGroupsA(SCARDCONTEXT hContext, LPSTR m WLog_Print(g_Log, g_LogLevel, "SCardListReaderGroupsA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardListReaderGroupsA(hContext, mszGroups, pcchGroups); + SCARDAPI_STUB_CALL_LONG(status, SCardListReaderGroupsA, hContext, mszGroups, pcchGroups); WLog_Print(g_Log, g_LogLevel, "SCardListReaderGroupsA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -107,7 +143,7 @@ static LONG WINAPI Inspect_SCardListReaderGroupsW(SCARDCONTEXT hContext, LPWSTR WLog_Print(g_Log, g_LogLevel, "SCardListReaderGroupsW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardListReaderGroupsW(hContext, mszGroups, pcchGroups); + SCARDAPI_STUB_CALL_LONG(status, SCardListReaderGroupsW, hContext, mszGroups, pcchGroups); WLog_Print(g_Log, g_LogLevel, "SCardListReaderGroupsW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -122,7 +158,8 @@ static LONG WINAPI Inspect_SCardListReadersA(SCARDCONTEXT hContext, LPCSTR mszGr WLog_Print(g_Log, g_LogLevel, "SCardListReadersA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardListReadersA(hContext, mszGroups, mszReaders, pcchReaders); + SCARDAPI_STUB_CALL_LONG(status, SCardListReadersA, hContext, mszGroups, mszReaders, + pcchReaders); WLog_Print(g_Log, g_LogLevel, "SCardListReadersA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -136,7 +173,8 @@ static LONG WINAPI Inspect_SCardListReadersW(SCARDCONTEXT hContext, LPCWSTR mszG LONG status; WLog_Print(g_Log, g_LogLevel, "SCardListReadersW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardListReadersW(hContext, mszGroups, mszReaders, pcchReaders); + SCARDAPI_STUB_CALL_LONG(status, SCardListReadersW, hContext, mszGroups, mszReaders, + pcchReaders); WLog_Print(g_Log, g_LogLevel, "SCardListReadersW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -151,8 +189,8 @@ static LONG WINAPI Inspect_SCardListCardsA(SCARDCONTEXT hContext, LPCBYTE pbAtr, WLog_Print(g_Log, g_LogLevel, "SCardListCardsA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardListCardsA(hContext, pbAtr, rgquidInterfaces, cguidInterfaceCount, - mszCards, pcchCards); + SCARDAPI_STUB_CALL_LONG(status, SCardListCardsA, hContext, pbAtr, rgquidInterfaces, + cguidInterfaceCount, mszCards, pcchCards); WLog_Print(g_Log, g_LogLevel, "SCardListCardsA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -168,8 +206,8 @@ static LONG WINAPI Inspect_SCardListCardsW(SCARDCONTEXT hContext, LPCBYTE pbAtr, WLog_Print(g_Log, g_LogLevel, "SCardListCardsW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardListCardsW(hContext, pbAtr, rgquidInterfaces, cguidInterfaceCount, - mszCards, pcchCards); + SCARDAPI_STUB_CALL_LONG(status, SCardListCardsW, hContext, pbAtr, rgquidInterfaces, + cguidInterfaceCount, mszCards, pcchCards); WLog_Print(g_Log, g_LogLevel, "SCardListCardsW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -184,8 +222,8 @@ static LONG WINAPI Inspect_SCardListInterfacesA(SCARDCONTEXT hContext, LPCSTR sz WLog_Print(g_Log, g_LogLevel, "SCardListInterfacesA { hContext: %p", (void*)hContext); - status = - g_SCardApi->pfnSCardListInterfacesA(hContext, szCard, pguidInterfaces, pcguidInterfaces); + SCARDAPI_STUB_CALL_LONG(status, SCardListInterfacesA, hContext, szCard, pguidInterfaces, + pcguidInterfaces); WLog_Print(g_Log, g_LogLevel, "SCardListInterfacesA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -200,8 +238,8 @@ static LONG WINAPI Inspect_SCardListInterfacesW(SCARDCONTEXT hContext, LPCWSTR s WLog_Print(g_Log, g_LogLevel, "SCardListInterfacesW { hContext: %p", (void*)hContext); - status = - g_SCardApi->pfnSCardListInterfacesW(hContext, szCard, pguidInterfaces, pcguidInterfaces); + SCARDAPI_STUB_CALL_LONG(status, SCardListInterfacesW, hContext, szCard, pguidInterfaces, + pcguidInterfaces); WLog_Print(g_Log, g_LogLevel, "SCardListInterfacesW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -216,7 +254,7 @@ static LONG WINAPI Inspect_SCardGetProviderIdA(SCARDCONTEXT hContext, LPCSTR szC WLog_Print(g_Log, g_LogLevel, "SCardGetProviderIdA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardGetProviderIdA(hContext, szCard, pguidProviderId); + SCARDAPI_STUB_CALL_LONG(status, SCardGetProviderIdA, hContext, szCard, pguidProviderId); WLog_Print(g_Log, g_LogLevel, "SCardGetProviderIdA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -231,7 +269,7 @@ static LONG WINAPI Inspect_SCardGetProviderIdW(SCARDCONTEXT hContext, LPCWSTR sz WLog_Print(g_Log, g_LogLevel, "SCardGetProviderIdW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardGetProviderIdW(hContext, szCard, pguidProviderId); + SCARDAPI_STUB_CALL_LONG(status, SCardGetProviderIdW, hContext, szCard, pguidProviderId); WLog_Print(g_Log, g_LogLevel, "SCardGetProviderIdW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -247,8 +285,8 @@ static LONG WINAPI Inspect_SCardGetCardTypeProviderNameA(SCARDCONTEXT hContext, WLog_Print(g_Log, g_LogLevel, "SCardGetCardTypeProviderNameA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardGetCardTypeProviderNameA(hContext, szCardName, dwProviderId, - szProvider, pcchProvider); + SCARDAPI_STUB_CALL_LONG(status, SCardGetCardTypeProviderNameA, hContext, szCardName, + dwProviderId, szProvider, pcchProvider); WLog_Print(g_Log, g_LogLevel, "SCardGetCardTypeProviderNameA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -264,8 +302,8 @@ static LONG WINAPI Inspect_SCardGetCardTypeProviderNameW(SCARDCONTEXT hContext, WLog_Print(g_Log, g_LogLevel, "SCardGetCardTypeProviderNameW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardGetCardTypeProviderNameW(hContext, szCardName, dwProviderId, - szProvider, pcchProvider); + SCARDAPI_STUB_CALL_LONG(status, SCardGetCardTypeProviderNameW, hContext, szCardName, + dwProviderId, szProvider, pcchProvider); WLog_Print(g_Log, g_LogLevel, "SCardGetCardTypeProviderNameW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -279,7 +317,7 @@ static LONG WINAPI Inspect_SCardIntroduceReaderGroupA(SCARDCONTEXT hContext, LPC WLog_Print(g_Log, g_LogLevel, "SCardIntroduceReaderGroupA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardIntroduceReaderGroupA(hContext, szGroupName); + SCARDAPI_STUB_CALL_LONG(status, SCardIntroduceReaderGroupA, hContext, szGroupName); WLog_Print(g_Log, g_LogLevel, "SCardIntroduceReaderGroupA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -293,7 +331,7 @@ static LONG WINAPI Inspect_SCardIntroduceReaderGroupW(SCARDCONTEXT hContext, LPC WLog_Print(g_Log, g_LogLevel, "SCardIntroduceReaderGroupW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardIntroduceReaderGroupW(hContext, szGroupName); + SCARDAPI_STUB_CALL_LONG(status, SCardIntroduceReaderGroupW, hContext, szGroupName); WLog_Print(g_Log, g_LogLevel, "SCardIntroduceReaderGroupW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -307,7 +345,7 @@ static LONG WINAPI Inspect_SCardForgetReaderGroupA(SCARDCONTEXT hContext, LPCSTR WLog_Print(g_Log, g_LogLevel, "SCardForgetReaderGroupA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardForgetReaderGroupA(hContext, szGroupName); + SCARDAPI_STUB_CALL_LONG(status, SCardForgetReaderGroupA, hContext, szGroupName); WLog_Print(g_Log, g_LogLevel, "SCardForgetReaderGroupA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -321,7 +359,7 @@ static LONG WINAPI Inspect_SCardForgetReaderGroupW(SCARDCONTEXT hContext, LPCWST WLog_Print(g_Log, g_LogLevel, "SCardForgetReaderGroupW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardForgetReaderGroupW(hContext, szGroupName); + SCARDAPI_STUB_CALL_LONG(status, SCardForgetReaderGroupW, hContext, szGroupName); WLog_Print(g_Log, g_LogLevel, "SCardForgetReaderGroupW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -336,7 +374,7 @@ static LONG WINAPI Inspect_SCardIntroduceReaderA(SCARDCONTEXT hContext, LPCSTR s WLog_Print(g_Log, g_LogLevel, "SCardIntroduceReaderA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardIntroduceReaderA(hContext, szReaderName, szDeviceName); + SCARDAPI_STUB_CALL_LONG(status, SCardIntroduceReaderA, hContext, szReaderName, szDeviceName); WLog_Print(g_Log, g_LogLevel, "SCardIntroduceReaderA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -351,7 +389,7 @@ static LONG WINAPI Inspect_SCardIntroduceReaderW(SCARDCONTEXT hContext, LPCWSTR WLog_Print(g_Log, g_LogLevel, "SCardIntroduceReaderW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardIntroduceReaderW(hContext, szReaderName, szDeviceName); + SCARDAPI_STUB_CALL_LONG(status, SCardIntroduceReaderW, hContext, szReaderName, szDeviceName); WLog_Print(g_Log, g_LogLevel, "SCardIntroduceReaderW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -365,7 +403,7 @@ static LONG WINAPI Inspect_SCardForgetReaderA(SCARDCONTEXT hContext, LPCSTR szRe WLog_Print(g_Log, g_LogLevel, "SCardForgetReaderA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardForgetReaderA(hContext, szReaderName); + SCARDAPI_STUB_CALL_LONG(status, SCardForgetReaderA, hContext, szReaderName); WLog_Print(g_Log, g_LogLevel, "SCardForgetReaderA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -379,7 +417,7 @@ static LONG WINAPI Inspect_SCardForgetReaderW(SCARDCONTEXT hContext, LPCWSTR szR WLog_Print(g_Log, g_LogLevel, "SCardForgetReaderW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardForgetReaderW(hContext, szReaderName); + SCARDAPI_STUB_CALL_LONG(status, SCardForgetReaderW, hContext, szReaderName); WLog_Print(g_Log, g_LogLevel, "SCardForgetReaderW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -394,7 +432,7 @@ static LONG WINAPI Inspect_SCardAddReaderToGroupA(SCARDCONTEXT hContext, LPCSTR WLog_Print(g_Log, g_LogLevel, "SCardAddReaderToGroupA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardAddReaderToGroupA(hContext, szReaderName, szGroupName); + SCARDAPI_STUB_CALL_LONG(status, SCardAddReaderToGroupA, hContext, szReaderName, szGroupName); WLog_Print(g_Log, g_LogLevel, "SCardAddReaderToGroupA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -409,7 +447,7 @@ static LONG WINAPI Inspect_SCardAddReaderToGroupW(SCARDCONTEXT hContext, LPCWSTR WLog_Print(g_Log, g_LogLevel, "SCardAddReaderToGroupW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardAddReaderToGroupW(hContext, szReaderName, szGroupName); + SCARDAPI_STUB_CALL_LONG(status, SCardAddReaderToGroupW, hContext, szReaderName, szGroupName); WLog_Print(g_Log, g_LogLevel, "SCardAddReaderToGroupW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -424,7 +462,8 @@ static LONG WINAPI Inspect_SCardRemoveReaderFromGroupA(SCARDCONTEXT hContext, LP WLog_Print(g_Log, g_LogLevel, "SCardRemoveReaderFromGroupA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardRemoveReaderFromGroupA(hContext, szReaderName, szGroupName); + SCARDAPI_STUB_CALL_LONG(status, SCardRemoveReaderFromGroupA, hContext, szReaderName, + szGroupName); WLog_Print(g_Log, g_LogLevel, "SCardRemoveReaderFromGroupA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -439,7 +478,8 @@ static LONG WINAPI Inspect_SCardRemoveReaderFromGroupW(SCARDCONTEXT hContext, LP WLog_Print(g_Log, g_LogLevel, "SCardRemoveReaderFromGroupW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardRemoveReaderFromGroupW(hContext, szReaderName, szGroupName); + SCARDAPI_STUB_CALL_LONG(status, SCardRemoveReaderFromGroupW, hContext, szReaderName, + szGroupName); WLog_Print(g_Log, g_LogLevel, "SCardRemoveReaderFromGroupW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -456,9 +496,9 @@ static LONG WINAPI Inspect_SCardIntroduceCardTypeA(SCARDCONTEXT hContext, LPCSTR WLog_Print(g_Log, g_LogLevel, "SCardIntroduceCardTypeA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardIntroduceCardTypeA(hContext, szCardName, pguidPrimaryProvider, - rgguidInterfaces, dwInterfaceCount, pbAtr, - pbAtrMask, cbAtrLen); + SCARDAPI_STUB_CALL_LONG(status, SCardIntroduceCardTypeA, hContext, szCardName, + pguidPrimaryProvider, rgguidInterfaces, dwInterfaceCount, pbAtr, + pbAtrMask, cbAtrLen); WLog_Print(g_Log, g_LogLevel, "SCardIntroduceCardTypeA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -475,9 +515,9 @@ static LONG WINAPI Inspect_SCardIntroduceCardTypeW(SCARDCONTEXT hContext, LPCWST WLog_Print(g_Log, g_LogLevel, "SCardIntroduceCardTypeW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardIntroduceCardTypeW(hContext, szCardName, pguidPrimaryProvider, - rgguidInterfaces, dwInterfaceCount, pbAtr, - pbAtrMask, cbAtrLen); + SCARDAPI_STUB_CALL_LONG(status, SCardIntroduceCardTypeW, hContext, szCardName, + pguidPrimaryProvider, rgguidInterfaces, dwInterfaceCount, pbAtr, + pbAtrMask, cbAtrLen); WLog_Print(g_Log, g_LogLevel, "SCardIntroduceCardTypeW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -492,8 +532,8 @@ static LONG WINAPI Inspect_SCardSetCardTypeProviderNameA(SCARDCONTEXT hContext, WLog_Print(g_Log, g_LogLevel, "SCardSetCardTypeProviderNameA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardSetCardTypeProviderNameA(hContext, szCardName, dwProviderId, - szProvider); + SCARDAPI_STUB_CALL_LONG(status, SCardSetCardTypeProviderNameA, hContext, szCardName, + dwProviderId, szProvider); WLog_Print(g_Log, g_LogLevel, "SCardSetCardTypeProviderNameA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -508,8 +548,8 @@ static LONG WINAPI Inspect_SCardSetCardTypeProviderNameW(SCARDCONTEXT hContext, WLog_Print(g_Log, g_LogLevel, "SCardSetCardTypeProviderNameA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardSetCardTypeProviderNameW(hContext, szCardName, dwProviderId, - szProvider); + SCARDAPI_STUB_CALL_LONG(status, SCardSetCardTypeProviderNameW, hContext, szCardName, + dwProviderId, szProvider); WLog_Print(g_Log, g_LogLevel, "SCardSetCardTypeProviderNameW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -523,7 +563,7 @@ static LONG WINAPI Inspect_SCardForgetCardTypeA(SCARDCONTEXT hContext, LPCSTR sz WLog_Print(g_Log, g_LogLevel, "SCardForgetCardTypeA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardForgetCardTypeA(hContext, szCardName); + SCARDAPI_STUB_CALL_LONG(status, SCardForgetCardTypeA, hContext, szCardName); WLog_Print(g_Log, g_LogLevel, "SCardForgetCardTypeA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -537,7 +577,7 @@ static LONG WINAPI Inspect_SCardForgetCardTypeW(SCARDCONTEXT hContext, LPCWSTR s WLog_Print(g_Log, g_LogLevel, "SCardForgetCardTypeW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardForgetCardTypeW(hContext, szCardName); + SCARDAPI_STUB_CALL_LONG(status, SCardForgetCardTypeW, hContext, szCardName); WLog_Print(g_Log, g_LogLevel, "SCardForgetCardTypeW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -551,7 +591,7 @@ static LONG WINAPI Inspect_SCardFreeMemory(SCARDCONTEXT hContext, LPVOID pvMem) WLog_Print(g_Log, g_LogLevel, "SCardFreeMemory { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardFreeMemory(hContext, pvMem); + SCARDAPI_STUB_CALL_LONG(status, SCardFreeMemory, hContext, pvMem); WLog_Print(g_Log, g_LogLevel, "SCardFreeMemory } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -565,7 +605,7 @@ static HANDLE WINAPI Inspect_SCardAccessStartedEvent(void) WLog_Print(g_Log, g_LogLevel, "SCardAccessStartedEvent {"); - hEvent = g_SCardApi->pfnSCardAccessStartedEvent(); + SCARDAPI_STUB_CALL_HANDLE(hEvent, SCardAccessStartedEvent); WLog_Print(g_Log, g_LogLevel, "SCardAccessStartedEvent } hEvent: %p", hEvent); @@ -576,7 +616,7 @@ static void WINAPI Inspect_SCardReleaseStartedEvent(void) { WLog_Print(g_Log, g_LogLevel, "SCardReleaseStartedEvent {"); - g_SCardApi->pfnSCardReleaseStartedEvent(); + SCARDAPI_STUB_CALL_VOID(SCardReleaseStartedEvent); WLog_Print(g_Log, g_LogLevel, "SCardReleaseStartedEvent }"); } @@ -588,7 +628,8 @@ static LONG WINAPI Inspect_SCardLocateCardsA(SCARDCONTEXT hContext, LPCSTR mszCa WLog_Print(g_Log, g_LogLevel, "SCardLocateCardsA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardLocateCardsA(hContext, mszCards, rgReaderStates, cReaders); + SCARDAPI_STUB_CALL_LONG(status, SCardLocateCardsA, hContext, mszCards, rgReaderStates, + cReaders); WLog_Print(g_Log, g_LogLevel, "SCardLocateCardsA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -603,7 +644,8 @@ static LONG WINAPI Inspect_SCardLocateCardsW(SCARDCONTEXT hContext, LPCWSTR mszC WLog_Print(g_Log, g_LogLevel, "SCardLocateCardsW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardLocateCardsW(hContext, mszCards, rgReaderStates, cReaders); + SCARDAPI_STUB_CALL_LONG(status, SCardLocateCardsW, hContext, mszCards, rgReaderStates, + cReaders); WLog_Print(g_Log, g_LogLevel, "SCardLocateCardsW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -619,8 +661,8 @@ static LONG WINAPI Inspect_SCardLocateCardsByATRA(SCARDCONTEXT hContext, LPSCARD WLog_Print(g_Log, g_LogLevel, "SCardLocateCardsByATRA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardLocateCardsByATRA(hContext, rgAtrMasks, cAtrs, rgReaderStates, - cReaders); + SCARDAPI_STUB_CALL_LONG(status, SCardLocateCardsByATRA, hContext, rgAtrMasks, cAtrs, + rgReaderStates, cReaders); WLog_Print(g_Log, g_LogLevel, "SCardLocateCardsByATRA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -636,8 +678,8 @@ static LONG WINAPI Inspect_SCardLocateCardsByATRW(SCARDCONTEXT hContext, LPSCARD WLog_Print(g_Log, g_LogLevel, "SCardLocateCardsByATRW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardLocateCardsByATRW(hContext, rgAtrMasks, cAtrs, rgReaderStates, - cReaders); + SCARDAPI_STUB_CALL_LONG(status, SCardLocateCardsByATRW, hContext, rgAtrMasks, cAtrs, + rgReaderStates, cReaders); WLog_Print(g_Log, g_LogLevel, "SCardLocateCardsByATRW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -653,7 +695,8 @@ static LONG WINAPI Inspect_SCardGetStatusChangeA(SCARDCONTEXT hContext, DWORD dw WLog_Print(g_Log, g_LogLevel, "SCardGetStatusChangeA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardGetStatusChangeA(hContext, dwTimeout, rgReaderStates, cReaders); + SCARDAPI_STUB_CALL_LONG(status, SCardGetStatusChangeA, hContext, dwTimeout, rgReaderStates, + cReaders); WLog_Print(g_Log, g_LogLevel, "SCardGetStatusChangeA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -669,7 +712,8 @@ static LONG WINAPI Inspect_SCardGetStatusChangeW(SCARDCONTEXT hContext, DWORD dw WLog_Print(g_Log, g_LogLevel, "SCardGetStatusChangeW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardGetStatusChangeW(hContext, dwTimeout, rgReaderStates, cReaders); + SCARDAPI_STUB_CALL_LONG(status, SCardGetStatusChangeW, hContext, dwTimeout, rgReaderStates, + cReaders); WLog_Print(g_Log, g_LogLevel, "SCardGetStatusChangeW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -683,7 +727,7 @@ static LONG WINAPI Inspect_SCardCancel(SCARDCONTEXT hContext) WLog_Print(g_Log, g_LogLevel, "SCardCancel { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardCancel(hContext); + SCARDAPI_STUB_CALL_LONG(status, SCardCancel, hContext); WLog_Print(g_Log, g_LogLevel, "SCardCancel } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -699,8 +743,8 @@ static LONG WINAPI Inspect_SCardConnectA(SCARDCONTEXT hContext, LPCSTR szReader, WLog_Print(g_Log, g_LogLevel, "SCardConnectA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardConnectA(hContext, szReader, dwShareMode, dwPreferredProtocols, - phCard, pdwActiveProtocol); + SCARDAPI_STUB_CALL_LONG(status, SCardConnectA, hContext, szReader, dwShareMode, + dwPreferredProtocols, phCard, pdwActiveProtocol); WLog_Print(g_Log, g_LogLevel, "SCardConnectA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -716,8 +760,8 @@ static LONG WINAPI Inspect_SCardConnectW(SCARDCONTEXT hContext, LPCWSTR szReader WLog_Print(g_Log, g_LogLevel, "SCardConnectW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardConnectW(hContext, szReader, dwShareMode, dwPreferredProtocols, - phCard, pdwActiveProtocol); + SCARDAPI_STUB_CALL_LONG(status, SCardConnectW, hContext, szReader, dwShareMode, + dwPreferredProtocols, phCard, pdwActiveProtocol); WLog_Print(g_Log, g_LogLevel, "SCardConnectW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -733,8 +777,8 @@ static LONG WINAPI Inspect_SCardReconnect(SCARDHANDLE hCard, DWORD dwShareMode, WLog_Print(g_Log, g_LogLevel, "SCardReconnect { hCard: %p", (void*)hCard); - status = g_SCardApi->pfnSCardReconnect(hCard, dwShareMode, dwPreferredProtocols, - dwInitialization, pdwActiveProtocol); + SCARDAPI_STUB_CALL_LONG(status, SCardReconnect, hCard, dwShareMode, dwPreferredProtocols, + dwInitialization, pdwActiveProtocol); WLog_Print(g_Log, g_LogLevel, "SCardReconnect } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -748,7 +792,7 @@ static LONG WINAPI Inspect_SCardDisconnect(SCARDHANDLE hCard, DWORD dwDispositio WLog_Print(g_Log, g_LogLevel, "SCardDisconnect { hCard: %p", (void*)hCard); - status = g_SCardApi->pfnSCardDisconnect(hCard, dwDisposition); + SCARDAPI_STUB_CALL_LONG(status, SCardDisconnect, hCard, dwDisposition); WLog_Print(g_Log, g_LogLevel, "SCardDisconnect } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -762,7 +806,7 @@ static LONG WINAPI Inspect_SCardBeginTransaction(SCARDHANDLE hCard) WLog_Print(g_Log, g_LogLevel, "SCardBeginTransaction { hCard: %p", (void*)hCard); - status = g_SCardApi->pfnSCardBeginTransaction(hCard); + SCARDAPI_STUB_CALL_LONG(status, SCardBeginTransaction, hCard); WLog_Print(g_Log, g_LogLevel, "SCardBeginTransaction } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -776,7 +820,7 @@ static LONG WINAPI Inspect_SCardEndTransaction(SCARDHANDLE hCard, DWORD dwDispos WLog_Print(g_Log, g_LogLevel, "SCardEndTransaction { hCard: %p", (void*)hCard); - status = g_SCardApi->pfnSCardEndTransaction(hCard, dwDisposition); + SCARDAPI_STUB_CALL_LONG(status, SCardEndTransaction, hCard, dwDisposition); WLog_Print(g_Log, g_LogLevel, "SCardEndTransaction } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -790,7 +834,7 @@ static LONG WINAPI Inspect_SCardCancelTransaction(SCARDHANDLE hCard) WLog_Print(g_Log, g_LogLevel, "SCardCancelTransaction { hCard: %p", (void*)hCard); - status = g_SCardApi->pfnSCardCancelTransaction(hCard); + SCARDAPI_STUB_CALL_LONG(status, SCardCancelTransaction, hCard); WLog_Print(g_Log, g_LogLevel, "SCardCancelTransaction } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -805,7 +849,7 @@ static LONG WINAPI Inspect_SCardState(SCARDHANDLE hCard, LPDWORD pdwState, LPDWO WLog_Print(g_Log, g_LogLevel, "SCardState { hCard: %p", (void*)hCard); - status = g_SCardApi->pfnSCardState(hCard, pdwState, pdwProtocol, pbAtr, pcbAtrLen); + SCARDAPI_STUB_CALL_LONG(status, SCardState, hCard, pdwState, pdwProtocol, pbAtr, pcbAtrLen); WLog_Print(g_Log, g_LogLevel, "SCardState } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -821,8 +865,8 @@ static LONG WINAPI Inspect_SCardStatusA(SCARDHANDLE hCard, LPSTR mszReaderNames, WLog_Print(g_Log, g_LogLevel, "SCardStatusA { hCard: %p", (void*)hCard); - status = g_SCardApi->pfnSCardStatusA(hCard, mszReaderNames, pcchReaderLen, pdwState, - pdwProtocol, pbAtr, pcbAtrLen); + SCARDAPI_STUB_CALL_LONG(status, SCardStatusA, hCard, mszReaderNames, pcchReaderLen, pdwState, + pdwProtocol, pbAtr, pcbAtrLen); WLog_Print(g_Log, g_LogLevel, "SCardStatusA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -838,8 +882,8 @@ static LONG WINAPI Inspect_SCardStatusW(SCARDHANDLE hCard, LPWSTR mszReaderNames WLog_Print(g_Log, g_LogLevel, "SCardStatusW { hCard: %p", (void*)hCard); - status = g_SCardApi->pfnSCardStatusW(hCard, mszReaderNames, pcchReaderLen, pdwState, - pdwProtocol, pbAtr, pcbAtrLen); + SCARDAPI_STUB_CALL_LONG(status, SCardStatusW, hCard, mszReaderNames, pcchReaderLen, pdwState, + pdwProtocol, pbAtr, pcbAtrLen); WLog_Print(g_Log, g_LogLevel, "SCardStatusW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -856,8 +900,8 @@ static LONG WINAPI Inspect_SCardTransmit(SCARDHANDLE hCard, LPCSCARD_IO_REQUEST WLog_Print(g_Log, g_LogLevel, "SCardTransmit { hCard: %p", (void*)hCard); - status = g_SCardApi->pfnSCardTransmit(hCard, pioSendPci, pbSendBuffer, cbSendLength, pioRecvPci, - pbRecvBuffer, pcbRecvLength); + SCARDAPI_STUB_CALL_LONG(status, SCardTransmit, hCard, pioSendPci, pbSendBuffer, cbSendLength, + pioRecvPci, pbRecvBuffer, pcbRecvLength); WLog_Print(g_Log, g_LogLevel, "SCardTransmit } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -871,7 +915,7 @@ static LONG WINAPI Inspect_SCardGetTransmitCount(SCARDHANDLE hCard, LPDWORD pcTr WLog_Print(g_Log, g_LogLevel, "SCardGetTransmitCount { hCard: %p", (void*)hCard); - status = g_SCardApi->pfnSCardGetTransmitCount(hCard, pcTransmitCount); + SCARDAPI_STUB_CALL_LONG(status, SCardGetTransmitCount, hCard, pcTransmitCount); WLog_Print(g_Log, g_LogLevel, "SCardGetTransmitCount } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -887,8 +931,8 @@ static LONG WINAPI Inspect_SCardControl(SCARDHANDLE hCard, DWORD dwControlCode, WLog_Print(g_Log, g_LogLevel, "SCardControl { hCard: %p", (void*)hCard); - status = g_SCardApi->pfnSCardControl(hCard, dwControlCode, lpInBuffer, cbInBufferSize, - lpOutBuffer, cbOutBufferSize, lpBytesReturned); + SCARDAPI_STUB_CALL_LONG(status, SCardControl, hCard, dwControlCode, lpInBuffer, cbInBufferSize, + lpOutBuffer, cbOutBufferSize, lpBytesReturned); WLog_Print(g_Log, g_LogLevel, "SCardControl } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -903,7 +947,7 @@ static LONG WINAPI Inspect_SCardGetAttrib(SCARDHANDLE hCard, DWORD dwAttrId, LPB WLog_Print(g_Log, g_LogLevel, "SCardGetAttrib { hCard: %p", (void*)hCard); - status = g_SCardApi->pfnSCardGetAttrib(hCard, dwAttrId, pbAttr, pcbAttrLen); + SCARDAPI_STUB_CALL_LONG(status, SCardGetAttrib, hCard, dwAttrId, pbAttr, pcbAttrLen); WLog_Print(g_Log, g_LogLevel, "SCardGetAttrib } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -918,7 +962,7 @@ static LONG WINAPI Inspect_SCardSetAttrib(SCARDHANDLE hCard, DWORD dwAttrId, LPC WLog_Print(g_Log, g_LogLevel, "SCardSetAttrib { hCard: %p", (void*)hCard); - status = g_SCardApi->pfnSCardSetAttrib(hCard, dwAttrId, pbAttr, cbAttrLen); + SCARDAPI_STUB_CALL_LONG(status, SCardSetAttrib, hCard, dwAttrId, pbAttr, cbAttrLen); WLog_Print(g_Log, g_LogLevel, "SCardSetAttrib } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -932,7 +976,7 @@ static LONG WINAPI Inspect_SCardUIDlgSelectCardA(LPOPENCARDNAMEA_EX pDlgStruc) WLog_Print(g_Log, g_LogLevel, "SCardUIDlgSelectCardA {"); - status = g_SCardApi->pfnSCardUIDlgSelectCardA(pDlgStruc); + SCARDAPI_STUB_CALL_LONG(status, SCardUIDlgSelectCardA, pDlgStruc); WLog_Print(g_Log, g_LogLevel, "SCardUIDlgSelectCardA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -946,7 +990,7 @@ static LONG WINAPI Inspect_SCardUIDlgSelectCardW(LPOPENCARDNAMEW_EX pDlgStruc) WLog_Print(g_Log, g_LogLevel, "SCardUIDlgSelectCardW {"); - status = g_SCardApi->pfnSCardUIDlgSelectCardW(pDlgStruc); + SCARDAPI_STUB_CALL_LONG(status, SCardUIDlgSelectCardW, pDlgStruc); WLog_Print(g_Log, g_LogLevel, "SCardUIDlgSelectCardW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -960,7 +1004,7 @@ static LONG WINAPI Inspect_GetOpenCardNameA(LPOPENCARDNAMEA pDlgStruc) WLog_Print(g_Log, g_LogLevel, "GetOpenCardNameA {"); - status = g_SCardApi->pfnGetOpenCardNameA(pDlgStruc); + SCARDAPI_STUB_CALL_LONG(status, GetOpenCardNameA, pDlgStruc); WLog_Print(g_Log, g_LogLevel, "GetOpenCardNameA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -974,7 +1018,7 @@ static LONG WINAPI Inspect_GetOpenCardNameW(LPOPENCARDNAMEW pDlgStruc) WLog_Print(g_Log, g_LogLevel, "GetOpenCardNameW {"); - status = g_SCardApi->pfnGetOpenCardNameW(pDlgStruc); + SCARDAPI_STUB_CALL_LONG(status, GetOpenCardNameW, pDlgStruc); WLog_Print(g_Log, g_LogLevel, "GetOpenCardNameW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -988,7 +1032,7 @@ static LONG WINAPI Inspect_SCardDlgExtendedError(void) WLog_Print(g_Log, g_LogLevel, "SCardDlgExtendedError {"); - status = g_SCardApi->pfnSCardDlgExtendedError(); + SCARDAPI_STUB_CALL_LONG(status, SCardDlgExtendedError); WLog_Print(g_Log, g_LogLevel, "SCardDlgExtendedError } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -1004,8 +1048,8 @@ static LONG WINAPI Inspect_SCardReadCacheA(SCARDCONTEXT hContext, UUID* CardIden WLog_Print(g_Log, g_LogLevel, "SCardReadCacheA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardReadCacheA(hContext, CardIdentifier, FreshnessCounter, LookupName, - Data, DataLen); + SCARDAPI_STUB_CALL_LONG(status, SCardReadCacheA, hContext, CardIdentifier, FreshnessCounter, + LookupName, Data, DataLen); WLog_Print(g_Log, g_LogLevel, "SCardReadCacheA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -1021,8 +1065,8 @@ static LONG WINAPI Inspect_SCardReadCacheW(SCARDCONTEXT hContext, UUID* CardIden WLog_Print(g_Log, g_LogLevel, "SCardReadCacheW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardReadCacheW(hContext, CardIdentifier, FreshnessCounter, LookupName, - Data, DataLen); + SCARDAPI_STUB_CALL_LONG(status, SCardReadCacheW, hContext, CardIdentifier, FreshnessCounter, + LookupName, Data, DataLen); WLog_Print(g_Log, g_LogLevel, "SCardReadCacheW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -1038,8 +1082,8 @@ static LONG WINAPI Inspect_SCardWriteCacheA(SCARDCONTEXT hContext, UUID* CardIde WLog_Print(g_Log, g_LogLevel, "SCardWriteCacheA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardWriteCacheA(hContext, CardIdentifier, FreshnessCounter, LookupName, - Data, DataLen); + SCARDAPI_STUB_CALL_LONG(status, SCardWriteCacheA, hContext, CardIdentifier, FreshnessCounter, + LookupName, Data, DataLen); WLog_Print(g_Log, g_LogLevel, "SCardWriteCacheA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -1055,8 +1099,8 @@ static LONG WINAPI Inspect_SCardWriteCacheW(SCARDCONTEXT hContext, UUID* CardIde WLog_Print(g_Log, g_LogLevel, "SCardWriteCacheW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardWriteCacheW(hContext, CardIdentifier, FreshnessCounter, LookupName, - Data, DataLen); + SCARDAPI_STUB_CALL_LONG(status, SCardWriteCacheW, hContext, CardIdentifier, FreshnessCounter, + LookupName, Data, DataLen); WLog_Print(g_Log, g_LogLevel, "SCardWriteCacheW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -1071,7 +1115,7 @@ static LONG WINAPI Inspect_SCardGetReaderIconA(SCARDCONTEXT hContext, LPCSTR szR WLog_Print(g_Log, g_LogLevel, "SCardGetReaderIconA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardGetReaderIconA(hContext, szReaderName, pbIcon, pcbIcon); + SCARDAPI_STUB_CALL_LONG(status, SCardGetReaderIconA, hContext, szReaderName, pbIcon, pcbIcon); WLog_Print(g_Log, g_LogLevel, "SCardGetReaderIconA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -1086,7 +1130,7 @@ static LONG WINAPI Inspect_SCardGetReaderIconW(SCARDCONTEXT hContext, LPCWSTR sz WLog_Print(g_Log, g_LogLevel, "SCardGetReaderIconW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardGetReaderIconW(hContext, szReaderName, pbIcon, pcbIcon); + SCARDAPI_STUB_CALL_LONG(status, SCardGetReaderIconW, hContext, szReaderName, pbIcon, pcbIcon); WLog_Print(g_Log, g_LogLevel, "SCardGetReaderIconW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -1101,7 +1145,7 @@ static LONG WINAPI Inspect_SCardGetDeviceTypeIdA(SCARDCONTEXT hContext, LPCSTR s WLog_Print(g_Log, g_LogLevel, "SCardGetDeviceTypeIdA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardGetDeviceTypeIdA(hContext, szReaderName, pdwDeviceTypeId); + SCARDAPI_STUB_CALL_LONG(status, SCardGetDeviceTypeIdA, hContext, szReaderName, pdwDeviceTypeId); WLog_Print(g_Log, g_LogLevel, "SCardGetDeviceTypeIdA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -1116,7 +1160,7 @@ static LONG WINAPI Inspect_SCardGetDeviceTypeIdW(SCARDCONTEXT hContext, LPCWSTR WLog_Print(g_Log, g_LogLevel, "SCardGetDeviceTypeIdW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardGetDeviceTypeIdW(hContext, szReaderName, pdwDeviceTypeId); + SCARDAPI_STUB_CALL_LONG(status, SCardGetDeviceTypeIdW, hContext, szReaderName, pdwDeviceTypeId); WLog_Print(g_Log, g_LogLevel, "SCardGetDeviceTypeIdW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -1134,8 +1178,8 @@ static LONG WINAPI Inspect_SCardGetReaderDeviceInstanceIdA(SCARDCONTEXT hContext WLog_Print(g_Log, g_LogLevel, "SCardGetReaderDeviceInstanceIdA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardGetReaderDeviceInstanceIdA( - hContext, szReaderName, szDeviceInstanceId, pcchDeviceInstanceId); + SCARDAPI_STUB_CALL_LONG(status, SCardGetReaderDeviceInstanceIdA, hContext, szReaderName, + szDeviceInstanceId, pcchDeviceInstanceId); WLog_Print(g_Log, g_LogLevel, "SCardGetReaderDeviceInstanceIdA } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -1153,8 +1197,8 @@ static LONG WINAPI Inspect_SCardGetReaderDeviceInstanceIdW(SCARDCONTEXT hContext WLog_Print(g_Log, g_LogLevel, "SCardGetReaderDeviceInstanceIdW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardGetReaderDeviceInstanceIdW( - hContext, szReaderName, szDeviceInstanceId, pcchDeviceInstanceId); + SCARDAPI_STUB_CALL_LONG(status, SCardGetReaderDeviceInstanceIdW, hContext, szReaderName, + szDeviceInstanceId, pcchDeviceInstanceId); WLog_Print(g_Log, g_LogLevel, "SCardGetReaderDeviceInstanceIdW } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status); @@ -1172,8 +1216,8 @@ static LONG WINAPI Inspect_SCardListReadersWithDeviceInstanceIdA(SCARDCONTEXT hC WLog_Print(g_Log, g_LogLevel, "SCardListReadersWithDeviceInstanceIdA { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardListReadersWithDeviceInstanceIdA(hContext, szDeviceInstanceId, - mszReaders, pcchReaders); + SCARDAPI_STUB_CALL_LONG(status, SCardListReadersWithDeviceInstanceIdA, hContext, + szDeviceInstanceId, mszReaders, pcchReaders); WLog_Print(g_Log, g_LogLevel, "SCardListReadersWithDeviceInstanceIdA } status: %s (0x%08" PRIX32 ")", @@ -1192,8 +1236,8 @@ static LONG WINAPI Inspect_SCardListReadersWithDeviceInstanceIdW(SCARDCONTEXT hC WLog_Print(g_Log, g_LogLevel, "SCardListReadersWithDeviceInstanceIdW { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardListReadersWithDeviceInstanceIdW(hContext, szDeviceInstanceId, - mszReaders, pcchReaders); + SCARDAPI_STUB_CALL_LONG(status, SCardListReadersWithDeviceInstanceIdW, hContext, + szDeviceInstanceId, mszReaders, pcchReaders); WLog_Print(g_Log, g_LogLevel, "SCardListReadersWithDeviceInstanceIdW } status: %s (0x%08" PRIX32 ")", @@ -1208,7 +1252,7 @@ static LONG WINAPI Inspect_SCardAudit(SCARDCONTEXT hContext, DWORD dwEvent) WLog_Print(g_Log, g_LogLevel, "SCardAudit { hContext: %p", (void*)hContext); - status = g_SCardApi->pfnSCardAudit(hContext, dwEvent); + SCARDAPI_STUB_CALL_LONG(status, SCardAudit, hContext, dwEvent); WLog_Print(g_Log, g_LogLevel, "SCardAudit } status: %s (0x%08" PRIX32 ")", SCardGetErrorString(status), status);