summaryrefslogtreecommitdiffstats
path: root/libfreerdp/utils/smartcard_call.c
diff options
context:
space:
mode:
Diffstat (limited to 'libfreerdp/utils/smartcard_call.c')
-rw-r--r--libfreerdp/utils/smartcard_call.c2019
1 files changed, 2019 insertions, 0 deletions
diff --git a/libfreerdp/utils/smartcard_call.c b/libfreerdp/utils/smartcard_call.c
new file mode 100644
index 0000000..a957e08
--- /dev/null
+++ b/libfreerdp/utils/smartcard_call.c
@@ -0,0 +1,2019 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Smartcard Device Service Virtual Channel
+ *
+ * Copyright (C) Alexi Volkov <alexi@myrealbox.com> 2006
+ * Copyright 2011 O.S. Systems Software Ltda.
+ * Copyright 2011 Anthony Tong <atong@trustedcs.com>
+ * Copyright 2015 Thincast Technologies GmbH
+ * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
+ * Copyright 2017 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2017 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/assert.h>
+
+#include <winpr/crt.h>
+#include <winpr/print.h>
+#include <winpr/stream.h>
+#include <winpr/library.h>
+#include <winpr/smartcard.h>
+
+#include <freerdp/freerdp.h>
+#include <freerdp/channels/rdpdr.h>
+#include <freerdp/channels/scard.h>
+
+#include <freerdp/utils/rdpdr_utils.h>
+#include <freerdp/utils/smartcard_pack.h>
+#include <freerdp/utils/smartcard_call.h>
+
+#include <freerdp/log.h>
+#define TAG FREERDP_TAG("utils.smartcard.call")
+
+#if defined(WITH_SMARTCARD_EMULATE)
+#include <freerdp/emulate/scard/smartcard_emulate.h>
+
+#define str(x) #x
+#define wrap(ctx, fkt, ...) \
+ ctx->useEmulatedCard ? Emulate_##fkt(ctx->emulation, ##__VA_ARGS__) \
+ : ctx->pWinSCardApi->pfn##fkt(__VA_ARGS__)
+#define wrap_ptr(ctx, fkt, ...) wrap(ctx, fkt, ##__VA_ARGS__)
+#else
+#define wrap(ctx, fkt, ...) \
+ ctx->useEmulatedCard ? SCARD_F_INTERNAL_ERROR : ctx->pWinSCardApi->pfn##fkt(__VA_ARGS__)
+#define wrap_ptr(ctx, fkt, ...) \
+ ctx->useEmulatedCard ? NULL : ctx->pWinSCardApi->pfn##fkt(__VA_ARGS__)
+#endif
+
+struct s_scard_call_context
+{
+ BOOL useEmulatedCard;
+ HANDLE StartedEvent;
+ wLinkedList* names;
+ wHashTable* rgSCardContextList;
+#if defined(WITH_SMARTCARD_EMULATE)
+ SmartcardEmulationContext* emulation;
+#endif
+ HANDLE hWinSCardLibrary;
+ SCardApiFunctionTable WinSCardApi;
+ const SCardApiFunctionTable* pWinSCardApi;
+ HANDLE stopEvent;
+ void* userdata;
+
+ void* (*fn_new)(void*, SCARDCONTEXT);
+ void (*fn_free)(void*);
+};
+
+struct s_scard_context_element
+{
+ void* context;
+ void (*fn_free)(void*);
+};
+
+static void context_free(void* arg);
+
+static LONG smartcard_EstablishContext_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ SCARDCONTEXT hContext = { 0 };
+ EstablishContext_Return ret = { 0 };
+ EstablishContext_Call* call = &operation->call.establishContext;
+ status = ret.ReturnCode =
+ wrap(smartcard, SCardEstablishContext, call->dwScope, NULL, NULL, &hContext);
+
+ if (ret.ReturnCode == SCARD_S_SUCCESS)
+ {
+ const void* key = (void*)(size_t)hContext;
+ struct s_scard_context_element* pContext =
+ calloc(1, sizeof(struct s_scard_context_element));
+ if (!pContext)
+ return STATUS_NO_MEMORY;
+
+ pContext->fn_free = smartcard->fn_free;
+
+ if (smartcard->fn_new)
+ {
+ pContext->context = smartcard->fn_new(smartcard->userdata, hContext);
+ if (!pContext->context)
+ {
+ free(pContext);
+ return STATUS_NO_MEMORY;
+ }
+ }
+
+ if (!HashTable_Insert(smartcard->rgSCardContextList, key, (void*)pContext))
+ {
+ WLog_ERR(TAG, "ListDictionary_Add failed!");
+ context_free(pContext);
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+ else
+ {
+ return scard_log_status_error(TAG, "SCardEstablishContext", status);
+ }
+
+ // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert takes ownership of pContext
+ smartcard_scard_context_native_to_redir(&(ret.hContext), hContext);
+
+ status = smartcard_pack_establish_context_return(out, &ret);
+ if (status != SCARD_S_SUCCESS)
+ {
+ return scard_log_status_error(TAG, "smartcard_pack_establish_context_return", status);
+ }
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_ReleaseContext_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ ret.ReturnCode = wrap(smartcard, SCardReleaseContext, operation->hContext);
+
+ if (ret.ReturnCode == SCARD_S_SUCCESS)
+ HashTable_Remove(smartcard->rgSCardContextList, (void*)operation->hContext);
+ else
+ {
+ return scard_log_status_error(TAG, "SCardReleaseContext", ret.ReturnCode);
+ }
+
+ smartcard_trace_long_return(&ret, "ReleaseContext");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_IsValidContext_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ ret.ReturnCode = wrap(smartcard, SCardIsValidContext, operation->hContext);
+ smartcard_trace_long_return(&ret, "IsValidContext");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_ListReaderGroupsA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ ListReaderGroups_Return ret = { 0 };
+ LPSTR mszGroups = NULL;
+ DWORD cchGroups = 0;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ cchGroups = SCARD_AUTOALLOCATE;
+ ret.ReturnCode =
+ wrap(smartcard, SCardListReaderGroupsA, operation->hContext, (LPSTR)&mszGroups, &cchGroups);
+ ret.msz = (BYTE*)mszGroups;
+ ret.cBytes = cchGroups;
+
+ status = smartcard_pack_list_reader_groups_return(out, &ret, FALSE);
+
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (mszGroups)
+ wrap(smartcard, SCardFreeMemory, operation->hContext, mszGroups);
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_ListReaderGroupsW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ ListReaderGroups_Return ret = { 0 };
+ LPWSTR mszGroups = NULL;
+ DWORD cchGroups = 0;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ cchGroups = SCARD_AUTOALLOCATE;
+ status = ret.ReturnCode = wrap(smartcard, SCardListReaderGroupsW, operation->hContext,
+ (LPWSTR)&mszGroups, &cchGroups);
+ ret.msz = (BYTE*)mszGroups;
+ ret.cBytes = cchGroups * sizeof(WCHAR);
+
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ status = smartcard_pack_list_reader_groups_return(out, &ret, TRUE);
+
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (mszGroups)
+ wrap(smartcard, SCardFreeMemory, operation->hContext, mszGroups);
+
+ return ret.ReturnCode;
+}
+
+static BOOL filter_match(wLinkedList* list, LPCSTR reader, size_t readerLen)
+{
+ if (readerLen < 1)
+ return FALSE;
+
+ LinkedList_Enumerator_Reset(list);
+
+ while (LinkedList_Enumerator_MoveNext(list))
+ {
+ const char* filter = LinkedList_Enumerator_Current(list);
+
+ if (filter)
+ {
+ if (strstr(reader, filter) != NULL)
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static DWORD filter_device_by_name_a(wLinkedList* list, LPSTR* mszReaders, DWORD cchReaders)
+{
+ size_t rpos = 0;
+ size_t wpos = 0;
+
+ if (*mszReaders == NULL || LinkedList_Count(list) < 1)
+ return cchReaders;
+
+ do
+ {
+ LPCSTR rreader = &(*mszReaders)[rpos];
+ LPSTR wreader = &(*mszReaders)[wpos];
+ size_t readerLen = strnlen(rreader, cchReaders - rpos);
+
+ rpos += readerLen + 1;
+
+ if (filter_match(list, rreader, readerLen))
+ {
+ if (rreader != wreader)
+ memmove(wreader, rreader, readerLen + 1);
+
+ wpos += readerLen + 1;
+ }
+ } while (rpos < cchReaders);
+
+ /* this string must be double 0 terminated */
+ if (rpos != wpos)
+ {
+ if (wpos >= cchReaders)
+ return 0;
+
+ (*mszReaders)[wpos++] = '\0';
+ }
+
+ return (DWORD)wpos;
+}
+
+static DWORD filter_device_by_name_w(wLinkedList* list, LPWSTR* mszReaders, DWORD cchReaders)
+{
+ DWORD rc = 0;
+ LPSTR readers = NULL;
+
+ if (LinkedList_Count(list) < 1)
+ return cchReaders;
+
+ readers = ConvertMszWCharNToUtf8Alloc(*mszReaders, cchReaders, NULL);
+
+ if (!readers)
+ {
+ free(readers);
+ return 0;
+ }
+
+ free(*mszReaders);
+ *mszReaders = NULL;
+ rc = filter_device_by_name_a(list, &readers, cchReaders);
+
+ *mszReaders = ConvertMszUtf8NToWCharAlloc(readers, rc, NULL);
+ if (!*mszReaders)
+ rc = 0;
+
+ free(readers);
+ return rc;
+}
+
+static LONG smartcard_ListReadersA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ ListReaders_Return ret = { 0 };
+ LPSTR mszReaders = NULL;
+ DWORD cchReaders = 0;
+ ListReaders_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.listReaders;
+ cchReaders = SCARD_AUTOALLOCATE;
+ status = ret.ReturnCode = wrap(smartcard, SCardListReadersA, operation->hContext,
+ (LPCSTR)call->mszGroups, (LPSTR)&mszReaders, &cchReaders);
+
+ if (status != SCARD_S_SUCCESS)
+ {
+ return scard_log_status_error(TAG, "SCardListReadersA", status);
+ }
+
+ cchReaders = filter_device_by_name_a(smartcard->names, &mszReaders, cchReaders);
+ ret.msz = (BYTE*)mszReaders;
+ ret.cBytes = cchReaders;
+
+ status = smartcard_pack_list_readers_return(out, &ret, FALSE);
+ if (status != SCARD_S_SUCCESS)
+ {
+ return scard_log_status_error(TAG, "smartcard_pack_list_readers_return", status);
+ }
+
+ if (mszReaders)
+ wrap(smartcard, SCardFreeMemory, operation->hContext, mszReaders);
+
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_ListReadersW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ ListReaders_Return ret = { 0 };
+ DWORD cchReaders = 0;
+ ListReaders_Call* call = NULL;
+ union
+ {
+ const BYTE* bp;
+ const char* sz;
+ const WCHAR* wz;
+ } string;
+ union
+ {
+ WCHAR** ppw;
+ WCHAR* pw;
+ CHAR* pc;
+ BYTE* pb;
+ } mszReaders;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.listReaders;
+
+ string.bp = call->mszGroups;
+ cchReaders = SCARD_AUTOALLOCATE;
+ status = ret.ReturnCode = wrap(smartcard, SCardListReadersW, operation->hContext, string.wz,
+ (LPWSTR)&mszReaders.pw, &cchReaders);
+
+ if (status != SCARD_S_SUCCESS)
+ return scard_log_status_error(TAG, "SCardListReadersW", status);
+
+ cchReaders = filter_device_by_name_w(smartcard->names, &mszReaders.pw, cchReaders);
+ ret.msz = mszReaders.pb;
+ ret.cBytes = cchReaders * sizeof(WCHAR);
+ status = smartcard_pack_list_readers_return(out, &ret, TRUE);
+
+ if (mszReaders.pb)
+ wrap(smartcard, SCardFreeMemory, operation->hContext, mszReaders.pb);
+
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_IntroduceReaderGroupA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ ContextAndStringA_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.contextAndStringA;
+ ret.ReturnCode = wrap(smartcard, SCardIntroduceReaderGroupA, operation->hContext, call->sz);
+ scard_log_status_error(TAG, "SCardIntroduceReaderGroupA", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "IntroduceReaderGroupA");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_IntroduceReaderGroupW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ ContextAndStringW_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.contextAndStringW;
+ ret.ReturnCode = wrap(smartcard, SCardIntroduceReaderGroupW, operation->hContext, call->sz);
+ scard_log_status_error(TAG, "SCardIntroduceReaderGroupW", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "IntroduceReaderGroupW");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_IntroduceReaderA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ ContextAndTwoStringA_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.contextAndTwoStringA;
+ ret.ReturnCode =
+ wrap(smartcard, SCardIntroduceReaderA, operation->hContext, call->sz1, call->sz2);
+ scard_log_status_error(TAG, "SCardIntroduceReaderA", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "IntroduceReaderA");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_IntroduceReaderW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ ContextAndTwoStringW_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.contextAndTwoStringW;
+ ret.ReturnCode =
+ wrap(smartcard, SCardIntroduceReaderW, operation->hContext, call->sz1, call->sz2);
+ scard_log_status_error(TAG, "SCardIntroduceReaderW", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "IntroduceReaderW");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_ForgetReaderA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ ContextAndStringA_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.contextAndStringA;
+ ret.ReturnCode = wrap(smartcard, SCardForgetReaderA, operation->hContext, call->sz);
+ scard_log_status_error(TAG, "SCardForgetReaderA", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "SCardForgetReaderA");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_ForgetReaderW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ ContextAndStringW_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.contextAndStringW;
+ ret.ReturnCode = wrap(smartcard, SCardForgetReaderW, operation->hContext, call->sz);
+ scard_log_status_error(TAG, "SCardForgetReaderW", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "SCardForgetReaderW");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_AddReaderToGroupA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ ContextAndTwoStringA_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.contextAndTwoStringA;
+ ret.ReturnCode =
+ wrap(smartcard, SCardAddReaderToGroupA, operation->hContext, call->sz1, call->sz2);
+ scard_log_status_error(TAG, "SCardAddReaderToGroupA", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "SCardAddReaderToGroupA");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_AddReaderToGroupW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ ContextAndTwoStringW_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.contextAndTwoStringW;
+ ret.ReturnCode =
+ wrap(smartcard, SCardAddReaderToGroupW, operation->hContext, call->sz1, call->sz2);
+ scard_log_status_error(TAG, "SCardAddReaderToGroupW", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "SCardAddReaderToGroupA");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_RemoveReaderFromGroupA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ ContextAndTwoStringA_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.contextAndTwoStringA;
+ ret.ReturnCode =
+ wrap(smartcard, SCardRemoveReaderFromGroupA, operation->hContext, call->sz1, call->sz2);
+ scard_log_status_error(TAG, "SCardRemoveReaderFromGroupA", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "SCardRemoveReaderFromGroupA");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_RemoveReaderFromGroupW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ ContextAndTwoStringW_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.contextAndTwoStringW;
+ ret.ReturnCode =
+ wrap(smartcard, SCardRemoveReaderFromGroupW, operation->hContext, call->sz1, call->sz2);
+ scard_log_status_error(TAG, "SCardRemoveReaderFromGroupW", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "SCardRemoveReaderFromGroupW");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_LocateCardsA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ LocateCards_Return ret = { 0 };
+ LocateCardsA_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.locateCardsA;
+
+ ret.ReturnCode = wrap(smartcard, SCardLocateCardsA, operation->hContext, call->mszCards,
+ call->rgReaderStates, call->cReaders);
+ scard_log_status_error(TAG, "SCardLocateCardsA", ret.ReturnCode);
+ ret.cReaders = call->cReaders;
+ ret.rgReaderStates = NULL;
+
+ if (ret.cReaders > 0)
+ {
+ ret.rgReaderStates = (ReaderState_Return*)calloc(ret.cReaders, sizeof(ReaderState_Return));
+
+ if (!ret.rgReaderStates)
+ return STATUS_NO_MEMORY;
+ }
+
+ for (UINT32 x = 0; x < ret.cReaders; x++)
+ {
+ ret.rgReaderStates[x].dwCurrentState = call->rgReaderStates[x].dwCurrentState;
+ ret.rgReaderStates[x].dwEventState = call->rgReaderStates[x].dwEventState;
+ ret.rgReaderStates[x].cbAtr = call->rgReaderStates[x].cbAtr;
+ CopyMemory(&(ret.rgReaderStates[x].rgbAtr), &(call->rgReaderStates[x].rgbAtr),
+ sizeof(ret.rgReaderStates[x].rgbAtr));
+ }
+
+ status = smartcard_pack_locate_cards_return(out, &ret);
+
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_LocateCardsW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ LocateCards_Return ret = { 0 };
+ LocateCardsW_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.locateCardsW;
+
+ ret.ReturnCode = wrap(smartcard, SCardLocateCardsW, operation->hContext, call->mszCards,
+ call->rgReaderStates, call->cReaders);
+ scard_log_status_error(TAG, "SCardLocateCardsW", ret.ReturnCode);
+ ret.cReaders = call->cReaders;
+ ret.rgReaderStates = NULL;
+
+ if (ret.cReaders > 0)
+ {
+ ret.rgReaderStates = (ReaderState_Return*)calloc(ret.cReaders, sizeof(ReaderState_Return));
+
+ if (!ret.rgReaderStates)
+ return STATUS_NO_MEMORY;
+ }
+
+ for (UINT32 x = 0; x < ret.cReaders; x++)
+ {
+ ret.rgReaderStates[x].dwCurrentState = call->rgReaderStates[x].dwCurrentState;
+ ret.rgReaderStates[x].dwEventState = call->rgReaderStates[x].dwEventState;
+ ret.rgReaderStates[x].cbAtr = call->rgReaderStates[x].cbAtr;
+ CopyMemory(&(ret.rgReaderStates[x].rgbAtr), &(call->rgReaderStates[x].rgbAtr),
+ sizeof(ret.rgReaderStates[x].rgbAtr));
+ }
+
+ status = smartcard_pack_locate_cards_return(out, &ret);
+
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_ReadCacheA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ BOOL autoalloc = 0;
+ ReadCache_Return ret = { 0 };
+ ReadCacheA_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.readCacheA;
+ 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 = wrap(smartcard, SCardReadCacheA, operation->hContext,
+ call->Common.CardIdentifier, call->Common.FreshnessCounter,
+ call->szLookupName, (BYTE*)&ret.pbData, &ret.cbDataLen);
+ else
+ 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))
+ {
+ scard_log_status_error(TAG, "SCardReadCacheA", ret.ReturnCode);
+ }
+
+ status = smartcard_pack_read_cache_return(out, &ret);
+ if (autoalloc)
+ wrap(smartcard, SCardFreeMemory, operation->hContext, ret.pbData);
+ else
+ free(ret.pbData);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_ReadCacheW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ ReadCache_Return ret = { 0 };
+ ReadCacheW_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.readCacheW;
+
+ 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))
+ {
+ scard_log_status_error(TAG, "SCardReadCacheA", ret.ReturnCode);
+ }
+
+ status = smartcard_pack_read_cache_return(out, &ret);
+
+ wrap(smartcard, SCardFreeMemory, operation->hContext, ret.pbData);
+
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_WriteCacheA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ WriteCacheA_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.writeCacheA;
+
+ ret.ReturnCode = wrap(smartcard, SCardWriteCacheA, operation->hContext,
+ call->Common.CardIdentifier, call->Common.FreshnessCounter,
+ call->szLookupName, call->Common.pbData, call->Common.cbDataLen);
+ scard_log_status_error(TAG, "SCardWriteCacheA", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "SCardWriteCacheA");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_WriteCacheW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ WriteCacheW_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.writeCacheW;
+
+ ret.ReturnCode = wrap(smartcard, SCardWriteCacheW, operation->hContext,
+ call->Common.CardIdentifier, call->Common.FreshnessCounter,
+ call->szLookupName, call->Common.pbData, call->Common.cbDataLen);
+ scard_log_status_error(TAG, "SCardWriteCacheW", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "SCardWriteCacheW");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_GetTransmitCount_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ GetTransmitCount_Return ret = { 0 };
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ ret.ReturnCode = wrap(smartcard, SCardGetTransmitCount, operation->hCard, &ret.cTransmitCount);
+ scard_log_status_error(TAG, "SCardGetTransmitCount", ret.ReturnCode);
+ status = smartcard_pack_get_transmit_count_return(out, &ret);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_ReleaseStartedEvent_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ WINPR_UNUSED(smartcard);
+ WINPR_UNUSED(out);
+ WINPR_UNUSED(operation);
+
+ WLog_WARN(TAG, "According to [MS-RDPESC] 3.1.4 Message Processing Events and Sequencing Rules "
+ "this is not supported?!?");
+ return SCARD_E_UNSUPPORTED_FEATURE;
+}
+
+static LONG smartcard_GetReaderIcon_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ GetReaderIcon_Return ret = { 0 };
+ GetReaderIcon_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.getReaderIcon;
+
+ ret.cbDataLen = SCARD_AUTOALLOCATE;
+ ret.ReturnCode = wrap(smartcard, SCardGetReaderIconW, operation->hContext, call->szReaderName,
+ (LPBYTE)&ret.pbData, &ret.cbDataLen);
+ scard_log_status_error(TAG, "SCardGetReaderIconW", ret.ReturnCode);
+
+ status = smartcard_pack_get_reader_icon_return(out, &ret);
+ wrap(smartcard, SCardFreeMemory, operation->hContext, ret.pbData);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_GetDeviceTypeId_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ GetDeviceTypeId_Return ret = { 0 };
+ GetDeviceTypeId_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.getDeviceTypeId;
+
+ ret.ReturnCode = wrap(smartcard, SCardGetDeviceTypeIdW, operation->hContext, call->szReaderName,
+ &ret.dwDeviceId);
+ scard_log_status_error(TAG, "SCardGetDeviceTypeIdW", ret.ReturnCode);
+
+ status = smartcard_pack_device_type_id_return(out, &ret);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_GetStatusChangeA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = STATUS_NO_MEMORY;
+ DWORD dwTimeOut = 0;
+ const DWORD dwTimeStep = 100;
+ GetStatusChange_Return ret = { 0 };
+ GetStatusChangeA_Call* call = NULL;
+ LPSCARD_READERSTATEA rgReaderStates = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.getStatusChangeA;
+ dwTimeOut = call->dwTimeOut;
+
+ if (call->cReaders > 0)
+ {
+ ret.cReaders = call->cReaders;
+ rgReaderStates = calloc(ret.cReaders, sizeof(SCARD_READERSTATEA));
+ ret.rgReaderStates = (ReaderState_Return*)calloc(ret.cReaders, sizeof(ReaderState_Return));
+ if (!rgReaderStates || !ret.rgReaderStates)
+ goto fail;
+ }
+
+ for (UINT32 x = 0; x < MAX(1, dwTimeOut);)
+ {
+ if (call->cReaders > 0)
+ memcpy(rgReaderStates, call->rgReaderStates,
+ call->cReaders * sizeof(SCARD_READERSTATEA));
+ ret.ReturnCode = wrap(smartcard, SCardGetStatusChangeA, operation->hContext,
+ MIN(dwTimeOut, dwTimeStep), rgReaderStates, call->cReaders);
+ if (ret.ReturnCode != SCARD_E_TIMEOUT)
+ break;
+ if (WaitForSingleObject(smartcard->stopEvent, 0) == WAIT_OBJECT_0)
+ break;
+ if (dwTimeOut != INFINITE)
+ x += dwTimeStep;
+ }
+ scard_log_status_error(TAG, "SCardGetStatusChangeA", ret.ReturnCode);
+
+ for (UINT32 index = 0; index < ret.cReaders; index++)
+ {
+ const SCARD_READERSTATEA* cur = &rgReaderStates[index];
+ ReaderState_Return* rout = &ret.rgReaderStates[index];
+
+ rout->dwCurrentState = cur->dwCurrentState;
+ rout->dwEventState = cur->dwEventState;
+ rout->cbAtr = cur->cbAtr;
+ CopyMemory(&(rout->rgbAtr), cur->rgbAtr, sizeof(rout->rgbAtr));
+ }
+
+ status = smartcard_pack_get_status_change_return(out, &ret, TRUE);
+fail:
+ free(ret.rgReaderStates);
+ free(rgReaderStates);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_GetStatusChangeW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = STATUS_NO_MEMORY;
+ DWORD dwTimeOut = 0;
+ const DWORD dwTimeStep = 100;
+ GetStatusChange_Return ret = { 0 };
+ GetStatusChangeW_Call* call = NULL;
+ LPSCARD_READERSTATEW rgReaderStates = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.getStatusChangeW;
+ dwTimeOut = call->dwTimeOut;
+
+ if (call->cReaders > 0)
+ {
+ ret.cReaders = call->cReaders;
+ rgReaderStates = calloc(ret.cReaders, sizeof(SCARD_READERSTATEW));
+ ret.rgReaderStates = (ReaderState_Return*)calloc(ret.cReaders, sizeof(ReaderState_Return));
+ if (!rgReaderStates || !ret.rgReaderStates)
+ goto fail;
+ }
+
+ for (UINT32 x = 0; x < MAX(1, dwTimeOut);)
+ {
+ if (call->cReaders > 0)
+ memcpy(rgReaderStates, call->rgReaderStates,
+ call->cReaders * sizeof(SCARD_READERSTATEW));
+ {
+ ret.ReturnCode = wrap(smartcard, SCardGetStatusChangeW, operation->hContext,
+ MIN(dwTimeOut, dwTimeStep), rgReaderStates, call->cReaders);
+ }
+ if (ret.ReturnCode != SCARD_E_TIMEOUT)
+ break;
+ if (WaitForSingleObject(smartcard->stopEvent, 0) == WAIT_OBJECT_0)
+ break;
+ if (dwTimeOut != INFINITE)
+ x += dwTimeStep;
+ }
+ scard_log_status_error(TAG, "SCardGetStatusChangeW", ret.ReturnCode);
+
+ for (UINT32 index = 0; index < ret.cReaders; index++)
+ {
+ const SCARD_READERSTATEW* cur = &rgReaderStates[index];
+ ReaderState_Return* rout = &ret.rgReaderStates[index];
+
+ rout->dwCurrentState = cur->dwCurrentState;
+ rout->dwEventState = cur->dwEventState;
+ rout->cbAtr = cur->cbAtr;
+ CopyMemory(&(rout->rgbAtr), cur->rgbAtr, sizeof(rout->rgbAtr));
+ }
+
+ status = smartcard_pack_get_status_change_return(out, &ret, TRUE);
+fail:
+ free(ret.rgReaderStates);
+ free(rgReaderStates);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_Cancel_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ ret.ReturnCode = wrap(smartcard, SCardCancel, operation->hContext);
+ scard_log_status_error(TAG, "SCardCancel", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "Cancel");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_ConnectA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ SCARDHANDLE hCard = 0;
+ Connect_Return ret = { 0 };
+ ConnectA_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.connectA;
+
+ if ((call->Common.dwPreferredProtocols == SCARD_PROTOCOL_UNDEFINED) &&
+ (call->Common.dwShareMode != SCARD_SHARE_DIRECT))
+ {
+ call->Common.dwPreferredProtocols = SCARD_PROTOCOL_Tx;
+ }
+
+ 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(&(ret.hContext), operation->hContext);
+ smartcard_scard_handle_native_to_redir(&(ret.hCard), hCard);
+
+ status = smartcard_pack_connect_return(out, &ret);
+ if (status != SCARD_S_SUCCESS)
+ goto out_fail;
+
+ status = ret.ReturnCode;
+out_fail:
+
+ return status;
+}
+
+static LONG smartcard_ConnectW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ SCARDHANDLE hCard = 0;
+ Connect_Return ret = { 0 };
+ ConnectW_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.connectW;
+
+ if ((call->Common.dwPreferredProtocols == SCARD_PROTOCOL_UNDEFINED) &&
+ (call->Common.dwShareMode != SCARD_SHARE_DIRECT))
+ {
+ call->Common.dwPreferredProtocols = SCARD_PROTOCOL_Tx;
+ }
+
+ 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(&(ret.hContext), operation->hContext);
+ smartcard_scard_handle_native_to_redir(&(ret.hCard), hCard);
+
+ status = smartcard_pack_connect_return(out, &ret);
+ if (status != SCARD_S_SUCCESS)
+ goto out_fail;
+
+ status = ret.ReturnCode;
+out_fail:
+
+ return status;
+}
+
+static LONG smartcard_Reconnect_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ Reconnect_Return ret = { 0 };
+ Reconnect_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.reconnect;
+ ret.ReturnCode =
+ wrap(smartcard, SCardReconnect, operation->hCard, call->dwShareMode,
+ call->dwPreferredProtocols, call->dwInitialization, &ret.dwActiveProtocol);
+ scard_log_status_error(TAG, "SCardReconnect", ret.ReturnCode);
+ status = smartcard_pack_reconnect_return(out, &ret);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_Disconnect_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ HCardAndDisposition_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.hCardAndDisposition;
+
+ ret.ReturnCode = wrap(smartcard, SCardDisconnect, operation->hCard, call->dwDisposition);
+ scard_log_status_error(TAG, "SCardDisconnect", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "Disconnect");
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_BeginTransaction_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ ret.ReturnCode = wrap(smartcard, SCardBeginTransaction, operation->hCard);
+ scard_log_status_error(TAG, "SCardBeginTransaction", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "BeginTransaction");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_EndTransaction_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ HCardAndDisposition_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.hCardAndDisposition;
+
+ ret.ReturnCode = wrap(smartcard, SCardEndTransaction, operation->hCard, call->dwDisposition);
+ scard_log_status_error(TAG, "SCardEndTransaction", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "EndTransaction");
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_State_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ State_Return ret = { 0 };
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ ret.cbAtrLen = SCARD_ATR_LENGTH;
+ ret.ReturnCode = wrap(smartcard, SCardState, operation->hCard, &ret.dwState, &ret.dwProtocol,
+ (BYTE*)&ret.rgAtr, &ret.cbAtrLen);
+
+ scard_log_status_error(TAG, "SCardState", ret.ReturnCode);
+ status = smartcard_pack_state_return(out, &ret);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_StatusA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ Status_Return ret = { 0 };
+ DWORD cchReaderLen = 0;
+ DWORD cbAtrLen = 0;
+ LPSTR mszReaderNames = NULL;
+ Status_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.status;
+
+ call->cbAtrLen = 32;
+ cbAtrLen = call->cbAtrLen;
+
+ if (call->fmszReaderNamesIsNULL)
+ cchReaderLen = 0;
+ else
+ cchReaderLen = SCARD_AUTOALLOCATE;
+
+ status = ret.ReturnCode =
+ wrap(smartcard, SCardStatusA, operation->hCard,
+ call->fmszReaderNamesIsNULL ? NULL : (LPSTR)&mszReaderNames, &cchReaderLen,
+ &ret.dwState, &ret.dwProtocol, cbAtrLen ? (BYTE*)&ret.pbAtr : NULL, &cbAtrLen);
+
+ scard_log_status_error(TAG, "SCardStatusA", status);
+ if (status == SCARD_S_SUCCESS)
+ {
+ if (!call->fmszReaderNamesIsNULL)
+ ret.mszReaderNames = (BYTE*)mszReaderNames;
+
+ ret.cBytes = cchReaderLen;
+
+ if (call->cbAtrLen)
+ ret.cbAtrLen = cbAtrLen;
+ }
+
+ status = smartcard_pack_status_return(out, &ret, FALSE);
+
+ if (mszReaderNames)
+ wrap(smartcard, SCardFreeMemory, operation->hContext, mszReaderNames);
+
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_StatusW_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ Status_Return ret = { 0 };
+ LPWSTR mszReaderNames = NULL;
+ Status_Call* call = NULL;
+ DWORD cbAtrLen = 0;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.status;
+
+ /**
+ * [MS-RDPESC]
+ * According to 2.2.2.18 Status_Call cbAtrLen is unused an must be ignored upon receipt.
+ */
+ cbAtrLen = call->cbAtrLen = 32;
+
+ if (call->fmszReaderNamesIsNULL)
+ ret.cBytes = 0;
+ else
+ ret.cBytes = SCARD_AUTOALLOCATE;
+
+ status = ret.ReturnCode =
+ wrap(smartcard, SCardStatusW, operation->hCard,
+ call->fmszReaderNamesIsNULL ? NULL : (LPWSTR)&mszReaderNames, &ret.cBytes,
+ &ret.dwState, &ret.dwProtocol, (BYTE*)&ret.pbAtr, &cbAtrLen);
+ scard_log_status_error(TAG, "SCardStatusW", status);
+ if (status == SCARD_S_SUCCESS)
+ {
+ if (!call->fmszReaderNamesIsNULL)
+ ret.mszReaderNames = (BYTE*)mszReaderNames;
+
+ ret.cbAtrLen = cbAtrLen;
+ }
+
+ /* SCardStatusW returns number of characters, we need number of bytes */
+ ret.cBytes *= sizeof(WCHAR);
+
+ status = smartcard_pack_status_return(out, &ret, TRUE);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+
+ if (mszReaderNames)
+ wrap(smartcard, SCardFreeMemory, operation->hContext, mszReaderNames);
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_Transmit_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ Transmit_Return ret = { 0 };
+ Transmit_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.transmit;
+ ret.cbRecvLength = 0;
+ ret.pbRecvBuffer = NULL;
+
+ if (call->cbRecvLength && !call->fpbRecvBufferIsNULL)
+ {
+ if (call->cbRecvLength >= 66560)
+ call->cbRecvLength = 66560;
+
+ ret.cbRecvLength = call->cbRecvLength;
+ ret.pbRecvBuffer = (BYTE*)malloc(ret.cbRecvLength);
+
+ if (!ret.pbRecvBuffer)
+ return STATUS_NO_MEMORY;
+ }
+
+ ret.pioRecvPci = call->pioRecvPci;
+ ret.ReturnCode =
+ wrap(smartcard, SCardTransmit, operation->hCard, call->pioSendPci, call->pbSendBuffer,
+ call->cbSendLength, ret.pioRecvPci, ret.pbRecvBuffer, &(ret.cbRecvLength));
+
+ scard_log_status_error(TAG, "SCardTransmit", ret.ReturnCode);
+
+ status = smartcard_pack_transmit_return(out, &ret);
+ free(ret.pbRecvBuffer);
+
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_Control_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ Control_Return ret = { 0 };
+ Control_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.control;
+ ret.cbOutBufferSize = call->cbOutBufferSize;
+ ret.pvOutBuffer = (BYTE*)malloc(call->cbOutBufferSize);
+
+ if (!ret.pvOutBuffer)
+ return SCARD_E_NO_MEMORY;
+
+ ret.ReturnCode =
+ wrap(smartcard, SCardControl, operation->hCard, call->dwControlCode, call->pvInBuffer,
+ call->cbInBufferSize, ret.pvOutBuffer, call->cbOutBufferSize, &ret.cbOutBufferSize);
+ scard_log_status_error(TAG, "SCardControl", ret.ReturnCode);
+ status = smartcard_pack_control_return(out, &ret);
+
+ free(ret.pvOutBuffer);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_GetAttrib_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ BOOL autoAllocate = FALSE;
+ LONG status = 0;
+ DWORD cbAttrLen = 0;
+ LPBYTE pbAttr = NULL;
+ GetAttrib_Return ret = { 0 };
+ const GetAttrib_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.getAttrib;
+
+ if (!call->fpbAttrIsNULL)
+ {
+ autoAllocate = (call->cbAttrLen == SCARD_AUTOALLOCATE) ? TRUE : FALSE;
+ cbAttrLen = call->cbAttrLen;
+ if (cbAttrLen && !autoAllocate)
+ {
+ ret.pbAttr = (BYTE*)malloc(cbAttrLen);
+
+ if (!ret.pbAttr)
+ return SCARD_E_NO_MEMORY;
+ }
+
+ pbAttr = autoAllocate ? (LPBYTE) & (ret.pbAttr) : ret.pbAttr;
+ }
+
+ ret.ReturnCode =
+ wrap(smartcard, SCardGetAttrib, operation->hCard, call->dwAttrId, pbAttr, &cbAttrLen);
+ scard_log_status_error(TAG, "SCardGetAttrib", ret.ReturnCode);
+ ret.cbAttrLen = cbAttrLen;
+
+ status = smartcard_pack_get_attrib_return(out, &ret, call->dwAttrId, call->cbAttrLen);
+
+ if (autoAllocate)
+ wrap(smartcard, SCardFreeMemory, operation->hContext, ret.pbAttr);
+ else
+ free(ret.pbAttr);
+ return status;
+}
+
+static LONG smartcard_SetAttrib_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ Long_Return ret = { 0 };
+ SetAttrib_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.setAttrib;
+
+ ret.ReturnCode = wrap(smartcard, SCardSetAttrib, operation->hCard, call->dwAttrId, call->pbAttr,
+ call->cbAttrLen);
+ scard_log_status_error(TAG, "SCardSetAttrib", ret.ReturnCode);
+ smartcard_trace_long_return(&ret, "SetAttrib");
+
+ return ret.ReturnCode;
+}
+
+static LONG smartcard_AccessStartedEvent_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = SCARD_S_SUCCESS;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_UNUSED(operation);
+
+ if (!smartcard->StartedEvent)
+ smartcard->StartedEvent = wrap_ptr(smartcard, SCardAccessStartedEvent);
+
+ if (!smartcard->StartedEvent)
+ status = SCARD_E_NO_SERVICE;
+
+ return status;
+}
+
+static LONG smartcard_LocateCardsByATRA_Call(scard_call_context* smartcard, wStream* out,
+ SMARTCARD_OPERATION* operation)
+{
+ LONG status = 0;
+ GetStatusChange_Return ret = { 0 };
+ LPSCARD_READERSTATEA state = NULL;
+ LPSCARD_READERSTATEA states = NULL;
+ LocateCardsByATRA_Call* call = NULL;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(operation);
+
+ call = &operation->call.locateCardsByATRA;
+ states = (LPSCARD_READERSTATEA)calloc(call->cReaders, sizeof(SCARD_READERSTATEA));
+
+ if (!states)
+ return STATUS_NO_MEMORY;
+
+ for (UINT32 i = 0; i < call->cReaders; i++)
+ {
+ states[i].szReader = (LPSTR)call->rgReaderStates[i].szReader;
+ states[i].dwCurrentState = call->rgReaderStates[i].dwCurrentState;
+ states[i].dwEventState = call->rgReaderStates[i].dwEventState;
+ states[i].cbAtr = call->rgReaderStates[i].cbAtr;
+ CopyMemory(&(states[i].rgbAtr), &(call->rgReaderStates[i].rgbAtr), 36);
+ }
+
+ status = ret.ReturnCode = wrap(smartcard, SCardGetStatusChangeA, operation->hContext,
+ 0x000001F4, states, call->cReaders);
+
+ scard_log_status_error(TAG, "SCardGetStatusChangeA", status);
+ for (UINT32 i = 0; i < call->cAtrs; i++)
+ {
+ for (UINT32 j = 0; j < call->cReaders; j++)
+ {
+ for (UINT32 k = 0; k < call->rgAtrMasks[i].cbAtr; k++)
+ {
+ if ((call->rgAtrMasks[i].rgbAtr[k] & call->rgAtrMasks[i].rgbMask[k]) !=
+ (states[j].rgbAtr[k] & call->rgAtrMasks[i].rgbMask[k]))
+ {
+ break;
+ }
+
+ states[j].dwEventState |= SCARD_STATE_ATRMATCH;
+ }
+ }
+ }
+
+ ret.cReaders = call->cReaders;
+ ret.rgReaderStates = NULL;
+
+ if (ret.cReaders > 0)
+ ret.rgReaderStates = (ReaderState_Return*)calloc(ret.cReaders, sizeof(ReaderState_Return));
+
+ if (!ret.rgReaderStates)
+ {
+ free(states);
+ return STATUS_NO_MEMORY;
+ }
+
+ for (UINT32 i = 0; i < ret.cReaders; i++)
+ {
+ state = &states[i];
+ ret.rgReaderStates[i].dwCurrentState = state->dwCurrentState;
+ ret.rgReaderStates[i].dwEventState = state->dwEventState;
+ ret.rgReaderStates[i].cbAtr = state->cbAtr;
+ CopyMemory(&(ret.rgReaderStates[i].rgbAtr), &(state->rgbAtr),
+ sizeof(ret.rgReaderStates[i].rgbAtr));
+ }
+
+ free(states);
+
+ status = smartcard_pack_get_status_change_return(out, &ret, FALSE);
+
+ free(ret.rgReaderStates);
+ if (status != SCARD_S_SUCCESS)
+ return status;
+ return ret.ReturnCode;
+}
+
+LONG smartcard_irp_device_control_call(scard_call_context* smartcard, wStream* out,
+ UINT32* pIoStatus, SMARTCARD_OPERATION* operation)
+{
+ LONG result = 0;
+ UINT32 offset = 0;
+ UINT32 ioControlCode = 0;
+ size_t outputBufferLength = 0;
+ size_t objectBufferLength = 0;
+
+ WINPR_ASSERT(smartcard);
+ WINPR_ASSERT(out);
+ WINPR_ASSERT(pIoStatus);
+ WINPR_ASSERT(operation);
+
+ ioControlCode = operation->ioControlCode;
+ /**
+ * [MS-RDPESC] 3.2.5.1: Sending Outgoing Messages:
+ * the output buffer length SHOULD be set to 2048
+ *
+ * Since it's a SHOULD and not a MUST, we don't care
+ * about it, but we still reserve at least 2048 bytes.
+ */
+ if (!Stream_EnsureRemainingCapacity(out, 2048))
+ return SCARD_E_NO_MEMORY;
+
+ /* Device Control Response */
+ Stream_Write_UINT32(out, 0); /* OutputBufferLength (4 bytes) */
+ Stream_Zero(out, SMARTCARD_COMMON_TYPE_HEADER_LENGTH); /* CommonTypeHeader (8 bytes) */
+ Stream_Zero(out, SMARTCARD_PRIVATE_TYPE_HEADER_LENGTH); /* PrivateTypeHeader (8 bytes) */
+ Stream_Write_UINT32(out, 0); /* Result (4 bytes) */
+
+ /* Call */
+ switch (ioControlCode)
+ {
+ case SCARD_IOCTL_ESTABLISHCONTEXT:
+ result = smartcard_EstablishContext_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_RELEASECONTEXT:
+ result = smartcard_ReleaseContext_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_ISVALIDCONTEXT:
+ result = smartcard_IsValidContext_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_LISTREADERGROUPSA:
+ result = smartcard_ListReaderGroupsA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_LISTREADERGROUPSW:
+ result = smartcard_ListReaderGroupsW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_LISTREADERSA:
+ result = smartcard_ListReadersA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_LISTREADERSW:
+ result = smartcard_ListReadersW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_INTRODUCEREADERGROUPA:
+ result = smartcard_IntroduceReaderGroupA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_INTRODUCEREADERGROUPW:
+ result = smartcard_IntroduceReaderGroupW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_FORGETREADERGROUPA:
+ result = smartcard_ForgetReaderA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_FORGETREADERGROUPW:
+ result = smartcard_ForgetReaderW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_INTRODUCEREADERA:
+ result = smartcard_IntroduceReaderA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_INTRODUCEREADERW:
+ result = smartcard_IntroduceReaderW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_FORGETREADERA:
+ result = smartcard_ForgetReaderA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_FORGETREADERW:
+ result = smartcard_ForgetReaderW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_ADDREADERTOGROUPA:
+ result = smartcard_AddReaderToGroupA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_ADDREADERTOGROUPW:
+ result = smartcard_AddReaderToGroupW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_REMOVEREADERFROMGROUPA:
+ result = smartcard_RemoveReaderFromGroupA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_REMOVEREADERFROMGROUPW:
+ result = smartcard_RemoveReaderFromGroupW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_LOCATECARDSA:
+ result = smartcard_LocateCardsA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_LOCATECARDSW:
+ result = smartcard_LocateCardsW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_GETSTATUSCHANGEA:
+ result = smartcard_GetStatusChangeA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_GETSTATUSCHANGEW:
+ result = smartcard_GetStatusChangeW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_CANCEL:
+ result = smartcard_Cancel_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_CONNECTA:
+ result = smartcard_ConnectA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_CONNECTW:
+ result = smartcard_ConnectW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_RECONNECT:
+ result = smartcard_Reconnect_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_DISCONNECT:
+ result = smartcard_Disconnect_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_BEGINTRANSACTION:
+ result = smartcard_BeginTransaction_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_ENDTRANSACTION:
+ result = smartcard_EndTransaction_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_STATE:
+ result = smartcard_State_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_STATUSA:
+ result = smartcard_StatusA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_STATUSW:
+ result = smartcard_StatusW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_TRANSMIT:
+ result = smartcard_Transmit_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_CONTROL:
+ result = smartcard_Control_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_GETATTRIB:
+ result = smartcard_GetAttrib_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_SETATTRIB:
+ result = smartcard_SetAttrib_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_ACCESSSTARTEDEVENT:
+ result = smartcard_AccessStartedEvent_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_LOCATECARDSBYATRA:
+ result = smartcard_LocateCardsByATRA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_LOCATECARDSBYATRW:
+ result = smartcard_LocateCardsW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_READCACHEA:
+ result = smartcard_ReadCacheA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_READCACHEW:
+ result = smartcard_ReadCacheW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_WRITECACHEA:
+ result = smartcard_WriteCacheA_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_WRITECACHEW:
+ result = smartcard_WriteCacheW_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_GETTRANSMITCOUNT:
+ result = smartcard_GetTransmitCount_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_RELEASETARTEDEVENT:
+ result = smartcard_ReleaseStartedEvent_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_GETREADERICON:
+ result = smartcard_GetReaderIcon_Call(smartcard, out, operation);
+ break;
+
+ case SCARD_IOCTL_GETDEVICETYPEID:
+ result = smartcard_GetDeviceTypeId_Call(smartcard, out, operation);
+ break;
+
+ default:
+ result = STATUS_UNSUCCESSFUL;
+ break;
+ }
+
+ /**
+ * [MS-RPCE] 2.2.6.3 Primitive Type Serialization
+ * The type MUST be aligned on an 8-byte boundary. If the size of the
+ * primitive type is not a multiple of 8 bytes, the data MUST be padded.
+ */
+
+ if ((ioControlCode != SCARD_IOCTL_ACCESSSTARTEDEVENT) &&
+ (ioControlCode != SCARD_IOCTL_RELEASETARTEDEVENT))
+ {
+ offset = (RDPDR_DEVICE_IO_RESPONSE_LENGTH + RDPDR_DEVICE_IO_CONTROL_RSP_HDR_LENGTH);
+ smartcard_pack_write_size_align(out, Stream_GetPosition(out) - offset, 8);
+ }
+
+ if ((result != SCARD_S_SUCCESS) && (result != SCARD_E_TIMEOUT) &&
+ (result != SCARD_E_NO_READERS_AVAILABLE) && (result != SCARD_E_NO_SERVICE) &&
+ (result != SCARD_W_CACHE_ITEM_NOT_FOUND) && (result != SCARD_W_CACHE_ITEM_STALE))
+ {
+ WLog_WARN(TAG, "IRP failure: %s (0x%08" PRIX32 "), status: %s (0x%08" PRIX32 ")",
+ scard_get_ioctl_string(ioControlCode, TRUE), ioControlCode,
+ SCardGetErrorString(result), result);
+ }
+
+ *pIoStatus = STATUS_SUCCESS;
+
+ if ((result & 0xC0000000L) == 0xC0000000L)
+ {
+ /* NTSTATUS error */
+ *pIoStatus = (UINT32)result;
+ WLog_WARN(TAG, "IRP failure: %s (0x%08" PRIX32 "), ntstatus: 0x%08" PRIX32 "",
+ scard_get_ioctl_string(ioControlCode, TRUE), ioControlCode, result);
+ }
+
+ Stream_SealLength(out);
+ outputBufferLength = Stream_Length(out);
+ WINPR_ASSERT(outputBufferLength >= RDPDR_DEVICE_IO_RESPONSE_LENGTH - 4U);
+ outputBufferLength -= (RDPDR_DEVICE_IO_RESPONSE_LENGTH + 4U);
+ WINPR_ASSERT(outputBufferLength >= RDPDR_DEVICE_IO_RESPONSE_LENGTH);
+ objectBufferLength = outputBufferLength - RDPDR_DEVICE_IO_RESPONSE_LENGTH;
+ WINPR_ASSERT(outputBufferLength <= UINT32_MAX);
+ WINPR_ASSERT(objectBufferLength <= UINT32_MAX);
+ Stream_SetPosition(out, RDPDR_DEVICE_IO_RESPONSE_LENGTH);
+ /* Device Control Response */
+ Stream_Write_UINT32(out, (UINT32)outputBufferLength); /* OutputBufferLength (4 bytes) */
+ smartcard_pack_common_type_header(out); /* CommonTypeHeader (8 bytes) */
+ smartcard_pack_private_type_header(
+ out, (UINT32)objectBufferLength); /* PrivateTypeHeader (8 bytes) */
+ Stream_Write_INT32(out, result); /* Result (4 bytes) */
+ Stream_SetPosition(out, Stream_Length(out));
+ return SCARD_S_SUCCESS;
+}
+
+void context_free(void* arg)
+{
+ struct s_scard_context_element* element = arg;
+ if (!arg)
+ return;
+
+ if (element->fn_free)
+ element->fn_free(element->context);
+ free(element);
+}
+
+scard_call_context* smartcard_call_context_new(const rdpSettings* settings)
+{
+ wObject* obj = NULL;
+ scard_call_context* ctx = NULL;
+
+ WINPR_ASSERT(settings);
+ ctx = calloc(1, sizeof(scard_call_context));
+ if (!ctx)
+ goto fail;
+
+ ctx->stopEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
+ if (!ctx->stopEvent)
+ goto fail;
+
+ ctx->names = LinkedList_New();
+ if (!ctx->names)
+ goto fail;
+
+#if defined(WITH_SMARTCARD_EMULATE)
+ ctx->useEmulatedCard = freerdp_settings_get_bool(settings, FreeRDP_SmartcardEmulation);
+#endif
+
+ if (ctx->useEmulatedCard)
+ {
+#if defined(WITH_SMARTCARD_EMULATE)
+ ctx->emulation = Emulate_New(settings);
+ if (!ctx->emulation)
+ goto fail;
+#else
+ WLog_ERR(TAG, "Smartcard emulation requested, but not supported!");
+ goto fail;
+#endif
+ }
+ else
+ {
+ const char* WinSCardModule = freerdp_settings_get_string(settings, FreeRDP_WinSCardModule);
+ if (WinSCardModule)
+ {
+ ctx->hWinSCardLibrary = LoadLibraryX(WinSCardModule);
+
+ if (!ctx->hWinSCardLibrary)
+ {
+ WLog_ERR(TAG, "Failed to load WinSCard library: '%s'", WinSCardModule);
+ goto fail;
+ }
+
+ if (!WinSCard_LoadApiTableFunctions(&ctx->WinSCardApi, ctx->hWinSCardLibrary))
+ goto fail;
+ ctx->pWinSCardApi = &ctx->WinSCardApi;
+ }
+ else
+ {
+ ctx->pWinSCardApi = WinPR_GetSCardApiFunctionTable();
+ }
+
+ if (!ctx->pWinSCardApi)
+ {
+ WLog_ERR(TAG, "Failed to load WinSCard API!");
+ goto fail;
+ }
+ }
+
+ ctx->rgSCardContextList = HashTable_New(FALSE);
+ if (!ctx->rgSCardContextList)
+ goto fail;
+
+ obj = HashTable_ValueObject(ctx->rgSCardContextList);
+ WINPR_ASSERT(obj);
+ obj->fnObjectFree = context_free;
+
+ return ctx;
+fail:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ smartcard_call_context_free(ctx);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+void smartcard_call_context_free(scard_call_context* ctx)
+{
+ if (!ctx)
+ return;
+
+ smartcard_call_context_signal_stop(ctx, FALSE);
+
+ LinkedList_Free(ctx->names);
+ if (ctx->StartedEvent)
+ {
+ WINPR_ASSERT(ctx->useEmulatedCard || ctx->pWinSCardApi);
+ wrap(ctx, SCardReleaseStartedEvent);
+ }
+
+ if (ctx->useEmulatedCard)
+ {
+#ifdef WITH_SMARTCARD_EMULATE
+ if (ctx->emulation)
+ {
+ Emulate_Free(ctx->emulation);
+ ctx->emulation = NULL;
+ }
+#endif
+ }
+
+ if (ctx->hWinSCardLibrary)
+ {
+ ZeroMemory(&ctx->WinSCardApi, sizeof(SCardApiFunctionTable));
+ FreeLibrary(ctx->hWinSCardLibrary);
+ ctx->hWinSCardLibrary = NULL;
+ }
+
+ ctx->pWinSCardApi = NULL;
+
+ HashTable_Free(ctx->rgSCardContextList);
+ CloseHandle(ctx->stopEvent);
+ free(ctx);
+}
+
+BOOL smartcard_call_context_add(scard_call_context* ctx, const char* name)
+{
+ WINPR_ASSERT(ctx);
+ WINPR_ASSERT(name);
+ return LinkedList_AddLast(ctx->names, name);
+}
+
+BOOL smartcard_call_cancel_context(scard_call_context* ctx, SCARDCONTEXT hContext)
+{
+ WINPR_ASSERT(ctx);
+ if (wrap(ctx, SCardIsValidContext, hContext) == SCARD_S_SUCCESS)
+ {
+ wrap(ctx, SCardCancel, hContext);
+ }
+ return TRUE;
+}
+
+BOOL smartcard_call_release_context(scard_call_context* ctx, SCARDCONTEXT hContext)
+{
+ WINPR_ASSERT(ctx);
+ wrap(ctx, SCardReleaseContext, hContext);
+ return TRUE;
+}
+
+BOOL smartcard_call_cancel_all_context(scard_call_context* ctx)
+{
+ WINPR_ASSERT(ctx);
+
+ HashTable_Clear(ctx->rgSCardContextList);
+ return TRUE;
+}
+
+BOOL smarcard_call_set_callbacks(scard_call_context* ctx, void* userdata,
+ void* (*fn_new)(void*, SCARDCONTEXT), void (*fn_free)(void*))
+{
+ WINPR_ASSERT(ctx);
+ ctx->userdata = userdata;
+ ctx->fn_new = fn_new;
+ ctx->fn_free = fn_free;
+ return TRUE;
+}
+
+void* smartcard_call_get_context(scard_call_context* ctx, SCARDCONTEXT hContext)
+{
+ struct s_scard_context_element* element = NULL;
+
+ WINPR_ASSERT(ctx);
+ element = HashTable_GetItemValue(ctx->rgSCardContextList, (void*)hContext);
+ if (!element)
+ return NULL;
+ return element->context;
+}
+
+BOOL smartcard_call_is_configured(scard_call_context* ctx)
+{
+ WINPR_ASSERT(ctx);
+
+#if defined(WITH_SMARTCARD_EMULATE)
+ if (ctx->useEmulatedCard)
+ return Emulate_IsConfigured(ctx->emulation);
+#endif
+
+ return FALSE;
+}
+
+BOOL smartcard_call_context_signal_stop(scard_call_context* ctx, BOOL reset)
+{
+ WINPR_ASSERT(ctx);
+ if (!ctx->stopEvent)
+ return TRUE;
+
+ if (reset)
+ return ResetEvent(ctx->stopEvent);
+ else
+ return SetEvent(ctx->stopEvent);
+}