diff options
Diffstat (limited to 'libfreerdp/emu/scard/smartcard_emulate.c')
-rw-r--r-- | libfreerdp/emu/scard/smartcard_emulate.c | 2712 |
1 files changed, 2712 insertions, 0 deletions
diff --git a/libfreerdp/emu/scard/smartcard_emulate.c b/libfreerdp/emu/scard/smartcard_emulate.c new file mode 100644 index 0000000..b2809c3 --- /dev/null +++ b/libfreerdp/emu/scard/smartcard_emulate.c @@ -0,0 +1,2712 @@ +/** + * WinPR: Windows Portable Runtime + * Smart Card API emulation + * + * Copyright 2021 Armin Novak <armin.novak@thincast.com> + * 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. + */ + +#include <freerdp/config.h> + +#include <winpr/crt.h> +#include <winpr/wlog.h> +#include <winpr/file.h> +#include <winpr/path.h> +#include <winpr/library.h> +#include <winpr/smartcard.h> +#include <winpr/collections.h> +#include <winpr/crypto.h> + +#include <freerdp/emulate/scard/smartcard_emulate.h> +#include "FreeRDP.ico.h" + +#include "smartcard_virtual_gids.h" + +#define MAX_CACHE_ITEM_SIZE 4096 +#define MAX_CACHE_ITEM_VALUES 4096 + +static const CHAR g_ReaderNameA[] = { 'F', 'r', 'e', 'e', 'R', 'D', 'P', ' ', 'E', + 'm', 'u', 'l', 'a', 't', 'o', 'r', '\0', '\0' }; +static INIT_ONCE g_ReaderNameWGuard = INIT_ONCE_STATIC_INIT; +static WCHAR g_ReaderNameW[32] = { 0 }; +static size_t g_ReaderNameWLen = 0; + +static BOOL CALLBACK g_ReaderNameWInit(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context) +{ + WINPR_UNUSED(InitOnce); + WINPR_UNUSED(Parameter); + WINPR_UNUSED(Context); + InitializeConstWCharFromUtf8(g_ReaderNameA, g_ReaderNameW, ARRAYSIZE(g_ReaderNameW)); + g_ReaderNameWLen = _wcsnlen(g_ReaderNameW, ARRAYSIZE(g_ReaderNameW) - 2) + 2; + return TRUE; +} + +struct smartcard_emulation_context +{ + const rdpSettings* settings; + DWORD log_default_level; + wLog* log; + wHashTable* contexts; + wHashTable* handles; + BOOL configured; + const char* pem; + const char* key; + const char* pin; +}; + +#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 const BYTE ATR[] = { 0x3b, 0xf7, 0x18, 0x00, 0x00, 0x80, 0x31, 0xfe, 0x45, + 0x73, 0x66, 0x74, 0x65, 0x2d, 0x6e, 0x66, 0xc4 }; + +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 = sizeof(ATR); + memcpy(reader->rgbAtr, ATR, sizeof(ATR)); + } + { + InitOnceExecuteOnce(&g_ReaderNameWGuard, g_ReaderNameWInit, NULL, NULL); + SCARD_READERSTATEW* reader = &context->readerStateW[0]; + reader->szReader = g_ReaderNameW; + reader->dwEventState = SCARD_STATE_PRESENT; + reader->cbAtr = sizeof(ATR); + memcpy(reader->rgbAtr, ATR, sizeof(ATR)); + } + 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 = winpr_ObjectStringClone; + key->fnObjectFree = winpr_ObjectStringFree; + + 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 = winpr_ObjectWStringClone; + key->fnObjectFree = winpr_ObjectStringFree; + + 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 = NULL; + + 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.pc = calloc(s + 2, sizeof(CHAR)); + if (!hdl->szReader.pc) + 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 = 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 = NULL; + + 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) +{ + SCardContext* ctx = NULL; + + WINPR_ASSERT(smartcard); + ctx = HashTable_GetItemValue(smartcard->contexts, (const void*)context); + + WINPR_ASSERT(name); + WINPR_ASSERT(ctx); + + for (size_t 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) +{ + SCardContext* ctx = NULL; + + WINPR_ASSERT(smartcard); + ctx = HashTable_GetItemValue(smartcard->contexts, (const void*)context); + + WINPR_ASSERT(name); + WINPR_ASSERT(ctx); + + for (size_t 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 = NULL; + + 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(&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); + // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert takes ownership of ctx + return status; +} + +LONG WINAPI Emulate_SCardReleaseContext(SmartcardEmulationContext* smartcard, SCARDCONTEXT hContext) +{ + LONG status = 0; + SCardContext* value = NULL; + + 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 = 0; + + 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 */ + } + + 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 */ + + InitOnceExecuteOnce(&g_ReaderNameWGuard, g_ReaderNameWInit, NULL, NULL); + 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 = g_ReaderNameWLen; + else + { + *pcchReaders = scard_copy_strings(value, mszReaders, *pcchReaders, g_ReaderNameW, + g_ReaderNameWLen * sizeof(WCHAR)) / + 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 = NULL; + + WINPR_ASSERT(smartcard); + + WLog_Print(smartcard->log, smartcard->log_default_level, "SCardAccessStartedEvent {"); + + /* Not required, return random */ + winpr_RAND(&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 (status == SCARD_S_SUCCESS) + { + const DWORD diff = 100; + 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 (size_t x = 0; x < cReaders; x++) + { + LPSCARD_READERSTATEA out = &rgReaderStates[x]; + + for (size_t 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); + if (dwTimeout != INFINITE) + 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 (status == SCARD_S_SUCCESS) + { + const DWORD diff = 100; + 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 (size_t x = 0; x < cReaders; x++) + { + LPSCARD_READERSTATEW out = &rgReaderStates[x]; + + for (size_t 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); + if (dwTimeout != INFINITE) + 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 count = 0; + + WINPR_ASSERT(smartcard); + count = HashTable_GetKeys(smartcard->handles, &keys); + for (size_t 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 = NULL; + + 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(&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) + { + SCardContext* ctx = + HashTable_GetItemValue(smartcard->contexts, (const void*)hdl->hContext); + WINPR_ASSERT(ctx); + + for (size_t 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 = NULL; + 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) + { + for (size_t 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 = NULL; + 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) + { + for (size_t 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 = 0; + + 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 = 0; + + 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 = 0; + + 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 = 0; + + 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 = 0; + + 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 = NULL; + 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 = NULL; + 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 LONG insert_data(wHashTable* table, DWORD FreshnessCounter, const void* key, + const PBYTE Data, DWORD DataLen) +{ + BOOL rc = 0; + SCardCacheItem* item = NULL; + + 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); + + // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert takes ownership of item + 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 = NULL; + + 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: + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC + Emulate_Free(smartcard); + WINPR_PRAGMA_DIAG_POP + 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 = NULL; + const char* pem = NULL; + const char* key = NULL; + const char* pin = NULL; + + WINPR_ASSERT(context); + + pem = freerdp_settings_get_string(context->settings, FreeRDP_SmartcardCertificate); + key = freerdp_settings_get_string(context->settings, FreeRDP_SmartcardPrivateKey); + pin = freerdp_settings_get_string(context->settings, FreeRDP_Password); + + /* Cache result only, if no initialization arguments changed. */ + if ((context->pem == pem) && (context->key == key) && (context->pin == pin)) + return context->configured; + + context->pem = pem; + context->key = key; + context->pin = pin; + + vgids = vgids_new(); + if (vgids) + rc = vgids_init(vgids, context->pem, context->key, context->pin); + vgids_free(vgids); + + context->configured = rc; + return rc; +} |