/** * WinPR: Windows Portable Runtime * Smart Card API * * Copyright 2014 Marc-Andre Moreau * Copyright 2020 Armin Novak * Copyright 2020 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 #ifndef _WIN32 #ifdef __APPLE__ #include #include #include #include #include #include #endif #include #include #include #include #include #include #include #include #include #include "smartcard_pcsc.h" #include "../log.h" #define TAG WINPR_TAG("smartcard") #define WINSCARD_LOAD_PROC_EX(module, pcsc, _fname, _name, ...) \ do \ { \ WINPR_PRAGMA_DIAG_PUSH \ WINPR_PRAGMA_DIAG_IGNORED_PEDANTIC \ pcsc.pfn##_fname = (fnPCSC##_fname)GetProcAddress(module, #_name); \ WINPR_PRAGMA_DIAG_POP \ } while (0) #define WINSCARD_LOAD_PROC(module, pcsc, _name, ...) \ WINSCARD_LOAD_PROC_EX(module, pcsc, _name, _name, ##__VA_ARGS__) /** * PC/SC transactions: * http://developersblog.wwpass.com/?p=180 */ /** * Smart Card Logon on Windows Vista: * http://blogs.msdn.com/b/shivaram/archive/2007/02/26/smart-card-logon-on-windows-vista.aspx */ /** * The Smart Card Cryptographic Service Provider Cookbook: * http://msdn.microsoft.com/en-us/library/ms953432.aspx * * SCARDCONTEXT * * The context is a communication channel with the smart card resource manager and * all calls to the resource manager must go through this link. * * All functions that take a context as a parameter or a card handle as parameter, * which is indirectly associated with a particular context, may be blocking calls. * Examples of these are SCardGetStatusChange and SCardBeginTransaction, which takes * a card handle as a parameter. If such a function blocks then all operations wanting * to use the context are blocked as well. So, it is recommended that a CSP using * monitoring establishes at least two contexts with the resource manager; one for * monitoring (with SCardGetStatusChange) and one for other operations. * * If multiple cards are present, it is recommended that a separate context or pair * of contexts be established for each card to prevent operations on one card from * blocking operations on another. * * Example one * * The example below shows what can happen if a CSP using SCardGetStatusChange for * monitoring does not establish two contexts with the resource manager. * The context becomes unusable until SCardGetStatusChange unblocks. * * In this example, there is one process running called P1. * P1 calls SCardEstablishContext, which returns the context hCtx. * P1 calls SCardConnect (with the hCtx context) which returns a handle to the card, hCard. * P1 calls SCardGetStatusChange (with the hCtx context) which blocks because * there are no status changes to report. * Until the thread running SCardGetStatusChange unblocks, another thread in P1 trying to * perform an operation using the context hCtx (or the card hCard) will also be blocked. * * Example two * * The example below shows how transaction control ensures that operations meant to be * performed without interruption can do so safely within a transaction. * * In this example, there are two different processes running; P1 and P2. * P1 calls SCardEstablishContext, which returns the context hCtx1. * P2 calls SCardEstablishContext, which returns the context hCtx2. * P1 calls SCardConnect (with the hCtx1 context) which returns a handle to the card, hCard1. * P2 calls SCardConnect (with the hCtx2 context) which returns a handle to the same card, hCard2. * P1 calls SCardBeginTransaction (with the hCard 1 context). * Until P1 calls SCardEndTransaction (with the hCard1 context), * any operation using hCard2 will be blocked. * Once an operation using hCard2 is blocked and until it's returning, * any operation using hCtx2 (and hCard2) will also be blocked. */ //#define DISABLE_PCSC_SCARD_AUTOALLOCATE #include "smartcard_pcsc.h" #define PCSC_SCARD_PCI_T0 (&g_PCSC_rgSCardT0Pci) #define PCSC_SCARD_PCI_T1 (&g_PCSC_rgSCardT1Pci) #define PCSC_SCARD_PCI_RAW (&g_PCSC_rgSCardRawPci) typedef PCSC_LONG (*fnPCSCSCardEstablishContext)(PCSC_DWORD dwScope, LPCVOID pvReserved1, LPCVOID pvReserved2, LPSCARDCONTEXT phContext); typedef PCSC_LONG (*fnPCSCSCardReleaseContext)(SCARDCONTEXT hContext); typedef PCSC_LONG (*fnPCSCSCardIsValidContext)(SCARDCONTEXT hContext); typedef PCSC_LONG (*fnPCSCSCardConnect)(SCARDCONTEXT hContext, LPCSTR szReader, PCSC_DWORD dwShareMode, PCSC_DWORD dwPreferredProtocols, LPSCARDHANDLE phCard, PCSC_LPDWORD pdwActiveProtocol); typedef PCSC_LONG (*fnPCSCSCardReconnect)(SCARDHANDLE hCard, PCSC_DWORD dwShareMode, PCSC_DWORD dwPreferredProtocols, PCSC_DWORD dwInitialization, PCSC_LPDWORD pdwActiveProtocol); typedef PCSC_LONG (*fnPCSCSCardDisconnect)(SCARDHANDLE hCard, PCSC_DWORD dwDisposition); typedef PCSC_LONG (*fnPCSCSCardBeginTransaction)(SCARDHANDLE hCard); typedef PCSC_LONG (*fnPCSCSCardEndTransaction)(SCARDHANDLE hCard, PCSC_DWORD dwDisposition); typedef PCSC_LONG (*fnPCSCSCardStatus)(SCARDHANDLE hCard, LPSTR mszReaderName, PCSC_LPDWORD pcchReaderLen, PCSC_LPDWORD pdwState, PCSC_LPDWORD pdwProtocol, LPBYTE pbAtr, PCSC_LPDWORD pcbAtrLen); typedef PCSC_LONG (*fnPCSCSCardGetStatusChange)(SCARDCONTEXT hContext, PCSC_DWORD dwTimeout, PCSC_SCARD_READERSTATE* rgReaderStates, PCSC_DWORD cReaders); typedef PCSC_LONG (*fnPCSCSCardControl)(SCARDHANDLE hCard, PCSC_DWORD dwControlCode, LPCVOID pbSendBuffer, PCSC_DWORD cbSendLength, LPVOID pbRecvBuffer, PCSC_DWORD cbRecvLength, PCSC_LPDWORD lpBytesReturned); typedef PCSC_LONG (*fnPCSCSCardTransmit)(SCARDHANDLE hCard, const PCSC_SCARD_IO_REQUEST* pioSendPci, LPCBYTE pbSendBuffer, PCSC_DWORD cbSendLength, PCSC_SCARD_IO_REQUEST* pioRecvPci, LPBYTE pbRecvBuffer, PCSC_LPDWORD pcbRecvLength); typedef PCSC_LONG (*fnPCSCSCardListReaderGroups)(SCARDCONTEXT hContext, LPSTR mszGroups, PCSC_LPDWORD pcchGroups); typedef PCSC_LONG (*fnPCSCSCardListReaders)(SCARDCONTEXT hContext, LPCSTR mszGroups, LPSTR mszReaders, PCSC_LPDWORD pcchReaders); typedef PCSC_LONG (*fnPCSCSCardFreeMemory)(SCARDCONTEXT hContext, LPCVOID pvMem); typedef PCSC_LONG (*fnPCSCSCardCancel)(SCARDCONTEXT hContext); typedef PCSC_LONG (*fnPCSCSCardGetAttrib)(SCARDHANDLE hCard, PCSC_DWORD dwAttrId, LPBYTE pbAttr, PCSC_LPDWORD pcbAttrLen); typedef PCSC_LONG (*fnPCSCSCardSetAttrib)(SCARDHANDLE hCard, PCSC_DWORD dwAttrId, LPCBYTE pbAttr, PCSC_DWORD cbAttrLen); typedef struct { fnPCSCSCardEstablishContext pfnSCardEstablishContext; fnPCSCSCardReleaseContext pfnSCardReleaseContext; fnPCSCSCardIsValidContext pfnSCardIsValidContext; fnPCSCSCardConnect pfnSCardConnect; fnPCSCSCardReconnect pfnSCardReconnect; fnPCSCSCardDisconnect pfnSCardDisconnect; fnPCSCSCardBeginTransaction pfnSCardBeginTransaction; fnPCSCSCardEndTransaction pfnSCardEndTransaction; fnPCSCSCardStatus pfnSCardStatus; fnPCSCSCardGetStatusChange pfnSCardGetStatusChange; fnPCSCSCardControl pfnSCardControl; fnPCSCSCardTransmit pfnSCardTransmit; fnPCSCSCardListReaderGroups pfnSCardListReaderGroups; fnPCSCSCardListReaders pfnSCardListReaders; fnPCSCSCardFreeMemory pfnSCardFreeMemory; fnPCSCSCardCancel pfnSCardCancel; fnPCSCSCardGetAttrib pfnSCardGetAttrib; fnPCSCSCardSetAttrib pfnSCardSetAttrib; } PCSCFunctionTable; typedef struct { DWORD len; DWORD freshness; BYTE* data; } PCSC_CACHE_ITEM; typedef struct { SCARDHANDLE owner; CRITICAL_SECTION lock; SCARDCONTEXT hContext; DWORD dwCardHandleCount; BOOL isTransactionLocked; wHashTable* cache; } PCSC_SCARDCONTEXT; typedef struct { BOOL shared; SCARDCONTEXT hSharedContext; } PCSC_SCARDHANDLE; static HMODULE g_PCSCModule = NULL; static PCSCFunctionTable g_PCSC = { 0 }; static HANDLE g_StartedEvent = NULL; static int g_StartedEventRefCount = 0; static BOOL g_SCardAutoAllocate = FALSE; static BOOL g_PnP_Notification = TRUE; #ifdef __MACOSX__ static unsigned int OSXVersion = 0; #endif static wListDictionary* g_CardHandles = NULL; static wListDictionary* g_CardContexts = NULL; static wListDictionary* g_MemoryBlocks = NULL; static const char SMARTCARD_PNP_NOTIFICATION_A[] = "\\\\?PnP?\\Notification"; static const PCSC_SCARD_IO_REQUEST g_PCSC_rgSCardT0Pci = { SCARD_PROTOCOL_T0, sizeof(PCSC_SCARD_IO_REQUEST) }; static const PCSC_SCARD_IO_REQUEST g_PCSC_rgSCardT1Pci = { SCARD_PROTOCOL_T1, sizeof(PCSC_SCARD_IO_REQUEST) }; static const PCSC_SCARD_IO_REQUEST g_PCSC_rgSCardRawPci = { PCSC_SCARD_PROTOCOL_RAW, sizeof(PCSC_SCARD_IO_REQUEST) }; static LONG WINAPI PCSC_SCardFreeMemory_Internal(SCARDCONTEXT hContext, LPVOID pvMem); static LONG WINAPI PCSC_SCardEstablishContext_Internal(DWORD dwScope, LPCVOID pvReserved1, LPCVOID pvReserved2, LPSCARDCONTEXT phContext); static LONG WINAPI PCSC_SCardReleaseContext_Internal(SCARDCONTEXT hContext); static LONG PCSC_SCard_LogError(const char* what) { WLog_WARN(TAG, "Missing function pointer %s=NULL", what); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG PCSC_MapErrorCodeToWinSCard(PCSC_LONG errorCode) { /** * pcsc-lite returns SCARD_E_UNEXPECTED when it * should return SCARD_E_UNSUPPORTED_FEATURE. * * Additionally, the pcsc-lite headers incorrectly * define SCARD_E_UNSUPPORTED_FEATURE to 0x8010001F, * when the real value should be 0x80100022. */ if (errorCode != SCARD_S_SUCCESS) { if (errorCode == SCARD_E_UNEXPECTED) errorCode = SCARD_E_UNSUPPORTED_FEATURE; } return (LONG)errorCode; } static DWORD PCSC_ConvertCardStateToWinSCard(DWORD dwCardState, PCSC_LONG status) { /** * pcsc-lite's SCardStatus returns a bit-field, not an enumerated value. * * State WinSCard pcsc-lite * * SCARD_UNKNOWN 0 0x0001 * SCARD_ABSENT 1 0x0002 * SCARD_PRESENT 2 0x0004 * SCARD_SWALLOWED 3 0x0008 * SCARD_POWERED 4 0x0010 * SCARD_NEGOTIABLE 5 0x0020 * SCARD_SPECIFIC 6 0x0040 * * pcsc-lite also never sets SCARD_SPECIFIC, * which is expected by some windows applications. */ if (status == SCARD_S_SUCCESS) { if ((dwCardState & PCSC_SCARD_NEGOTIABLE) || (dwCardState & PCSC_SCARD_SPECIFIC)) return SCARD_SPECIFIC; } if (dwCardState & PCSC_SCARD_POWERED) return SCARD_POWERED; if (dwCardState & PCSC_SCARD_NEGOTIABLE) return SCARD_NEGOTIABLE; if (dwCardState & PCSC_SCARD_SPECIFIC) return SCARD_SPECIFIC; if (dwCardState & PCSC_SCARD_ABSENT) return SCARD_ABSENT; if (dwCardState & PCSC_SCARD_PRESENT) return SCARD_PRESENT; if (dwCardState & PCSC_SCARD_SWALLOWED) return SCARD_SWALLOWED; if (dwCardState & PCSC_SCARD_UNKNOWN) return SCARD_UNKNOWN; return SCARD_UNKNOWN; } static DWORD PCSC_ConvertProtocolsToWinSCard(PCSC_DWORD dwProtocols) { /** * pcsc-lite uses a different value for SCARD_PROTOCOL_RAW, * and also has SCARD_PROTOCOL_T15 which is not in WinSCard. */ if (dwProtocols & PCSC_SCARD_PROTOCOL_RAW) { dwProtocols &= ~PCSC_SCARD_PROTOCOL_RAW; dwProtocols |= SCARD_PROTOCOL_RAW; } if (dwProtocols & PCSC_SCARD_PROTOCOL_T15) { dwProtocols &= ~PCSC_SCARD_PROTOCOL_T15; } return (DWORD)dwProtocols; } static DWORD PCSC_ConvertProtocolsFromWinSCard(DWORD dwProtocols) { /** * pcsc-lite uses a different value for SCARD_PROTOCOL_RAW, * and it does not define WinSCard's SCARD_PROTOCOL_DEFAULT. */ if (dwProtocols & SCARD_PROTOCOL_RAW) { dwProtocols &= ~SCARD_PROTOCOL_RAW; dwProtocols |= PCSC_SCARD_PROTOCOL_RAW; } if (dwProtocols & SCARD_PROTOCOL_DEFAULT) { dwProtocols &= ~SCARD_PROTOCOL_DEFAULT; } if (dwProtocols == SCARD_PROTOCOL_UNDEFINED) { dwProtocols = SCARD_PROTOCOL_Tx; } return dwProtocols; } static PCSC_SCARDCONTEXT* PCSC_GetCardContextData(SCARDCONTEXT hContext) { PCSC_SCARDCONTEXT* pContext = NULL; if (!g_CardContexts) return NULL; pContext = (PCSC_SCARDCONTEXT*)ListDictionary_GetItemValue(g_CardContexts, (void*)hContext); if (!pContext) return NULL; return pContext; } static void pcsc_cache_item_free(void* ptr) { PCSC_CACHE_ITEM* data = ptr; if (data) free(data->data); free(data); } static PCSC_SCARDCONTEXT* PCSC_EstablishCardContext(SCARDCONTEXT hContext) { PCSC_SCARDCONTEXT* pContext = NULL; pContext = (PCSC_SCARDCONTEXT*)calloc(1, sizeof(PCSC_SCARDCONTEXT)); if (!pContext) return NULL; pContext->hContext = hContext; if (!InitializeCriticalSectionAndSpinCount(&(pContext->lock), 4000)) goto error_spinlock; pContext->cache = HashTable_New(FALSE); if (!pContext->cache) goto errors; if (!HashTable_SetupForStringData(pContext->cache, FALSE)) goto errors; { wObject* obj = HashTable_ValueObject(pContext->cache); obj->fnObjectFree = pcsc_cache_item_free; } if (!g_CardContexts) { g_CardContexts = ListDictionary_New(TRUE); if (!g_CardContexts) goto errors; } if (!ListDictionary_Add(g_CardContexts, (void*)hContext, (void*)pContext)) goto errors; return pContext; errors: HashTable_Free(pContext->cache); DeleteCriticalSection(&(pContext->lock)); error_spinlock: free(pContext); return NULL; } static void PCSC_ReleaseCardContext(SCARDCONTEXT hContext) { PCSC_SCARDCONTEXT* pContext = NULL; pContext = PCSC_GetCardContextData(hContext); if (!pContext) { WLog_ERR(TAG, "PCSC_ReleaseCardContext: null pContext!"); return; } DeleteCriticalSection(&(pContext->lock)); HashTable_Free(pContext->cache); free(pContext); if (!g_CardContexts) return; ListDictionary_Remove(g_CardContexts, (void*)hContext); } static BOOL PCSC_LockCardContext(SCARDCONTEXT hContext) { PCSC_SCARDCONTEXT* pContext = NULL; pContext = PCSC_GetCardContextData(hContext); if (!pContext) { WLog_ERR(TAG, "PCSC_LockCardContext: invalid context (%p)", (void*)hContext); return FALSE; } EnterCriticalSection(&(pContext->lock)); return TRUE; } static BOOL PCSC_UnlockCardContext(SCARDCONTEXT hContext) { PCSC_SCARDCONTEXT* pContext = NULL; pContext = PCSC_GetCardContextData(hContext); if (!pContext) { WLog_ERR(TAG, "PCSC_UnlockCardContext: invalid context (%p)", (void*)hContext); return FALSE; } LeaveCriticalSection(&(pContext->lock)); return TRUE; } static PCSC_SCARDHANDLE* PCSC_GetCardHandleData(SCARDHANDLE hCard) { PCSC_SCARDHANDLE* pCard = NULL; if (!g_CardHandles) return NULL; pCard = (PCSC_SCARDHANDLE*)ListDictionary_GetItemValue(g_CardHandles, (void*)hCard); if (!pCard) return NULL; return pCard; } static SCARDCONTEXT PCSC_GetCardContextFromHandle(SCARDHANDLE hCard) { PCSC_SCARDHANDLE* pCard = NULL; pCard = PCSC_GetCardHandleData(hCard); if (!pCard) return 0; return pCard->hSharedContext; } static BOOL PCSC_WaitForCardAccess(SCARDCONTEXT hContext, SCARDHANDLE hCard, BOOL shared) { BOOL status = TRUE; PCSC_SCARDHANDLE* pCard = NULL; PCSC_SCARDCONTEXT* pContext = NULL; if (!hCard) { /* SCardConnect */ pContext = PCSC_GetCardContextData(hContext); if (!pContext) return FALSE; if (!pContext->owner) return TRUE; /* wait for card ownership */ return TRUE; } pCard = PCSC_GetCardHandleData(hCard); if (!pCard) return FALSE; shared = pCard->shared; hContext = pCard->hSharedContext; pContext = PCSC_GetCardContextData(hContext); if (!pContext) return FALSE; if (!pContext->owner) { /* card is not owned */ if (!shared) pContext->owner = hCard; return TRUE; } if (pContext->owner == hCard) { /* already card owner */ } else { /* wait for card ownership */ } return status; } static BOOL PCSC_ReleaseCardAccess(SCARDCONTEXT hContext, SCARDHANDLE hCard) { PCSC_SCARDHANDLE* pCard = NULL; PCSC_SCARDCONTEXT* pContext = NULL; if (!hCard) { /* release current owner */ pContext = PCSC_GetCardContextData(hContext); if (!pContext) return FALSE; hCard = pContext->owner; if (!hCard) return TRUE; pCard = PCSC_GetCardHandleData(hCard); if (!pCard) return FALSE; /* release card ownership */ pContext->owner = 0; return TRUE; } pCard = PCSC_GetCardHandleData(hCard); if (!pCard) return FALSE; hContext = pCard->hSharedContext; pContext = PCSC_GetCardContextData(hContext); if (!pContext) return FALSE; if (pContext->owner == hCard) { /* release card ownership */ pContext->owner = 0; } return TRUE; } static PCSC_SCARDHANDLE* PCSC_ConnectCardHandle(SCARDCONTEXT hSharedContext, SCARDHANDLE hCard) { PCSC_SCARDHANDLE* pCard = NULL; PCSC_SCARDCONTEXT* pContext = NULL; pContext = PCSC_GetCardContextData(hSharedContext); if (!pContext) { WLog_ERR(TAG, "PCSC_ConnectCardHandle: null pContext!"); return NULL; } pCard = (PCSC_SCARDHANDLE*)calloc(1, sizeof(PCSC_SCARDHANDLE)); if (!pCard) return NULL; pCard->hSharedContext = hSharedContext; if (!g_CardHandles) { g_CardHandles = ListDictionary_New(TRUE); if (!g_CardHandles) goto error; } if (!ListDictionary_Add(g_CardHandles, (void*)hCard, (void*)pCard)) goto error; pContext->dwCardHandleCount++; return pCard; error: free(pCard); return NULL; } static void PCSC_DisconnectCardHandle(SCARDHANDLE hCard) { PCSC_SCARDHANDLE* pCard = NULL; PCSC_SCARDCONTEXT* pContext = NULL; pCard = PCSC_GetCardHandleData(hCard); if (!pCard) return; pContext = PCSC_GetCardContextData(pCard->hSharedContext); free(pCard); if (!g_CardHandles) return; ListDictionary_Remove(g_CardHandles, (void*)hCard); if (!pContext) { WLog_ERR(TAG, "PCSC_DisconnectCardHandle: null pContext!"); return; } pContext->dwCardHandleCount--; } static BOOL PCSC_AddMemoryBlock(SCARDCONTEXT hContext, void* pvMem) { if (!g_MemoryBlocks) { g_MemoryBlocks = ListDictionary_New(TRUE); if (!g_MemoryBlocks) return FALSE; } return ListDictionary_Add(g_MemoryBlocks, pvMem, (void*)hContext); } static void* PCSC_RemoveMemoryBlock(SCARDCONTEXT hContext, void* pvMem) { WINPR_UNUSED(hContext); if (!g_MemoryBlocks) return NULL; return ListDictionary_Take(g_MemoryBlocks, pvMem); } /** * Standard Windows Smart Card API (PCSC) */ static LONG WINAPI PCSC_SCardEstablishContext_Internal(DWORD dwScope, LPCVOID pvReserved1, LPCVOID pvReserved2, LPSCARDCONTEXT phContext) { WINPR_UNUSED(dwScope); /* SCARD_SCOPE_SYSTEM is the only scope supported by pcsc-lite */ PCSC_LONG status = SCARD_S_SUCCESS; if (!g_PCSC.pfnSCardEstablishContext) return PCSC_SCard_LogError("g_PCSC.pfnSCardEstablishContext"); status = g_PCSC.pfnSCardEstablishContext(SCARD_SCOPE_SYSTEM, pvReserved1, pvReserved2, phContext); return PCSC_MapErrorCodeToWinSCard(status); } static LONG WINAPI PCSC_SCardEstablishContext(DWORD dwScope, LPCVOID pvReserved1, LPCVOID pvReserved2, LPSCARDCONTEXT phContext) { LONG status = 0; status = PCSC_SCardEstablishContext_Internal(dwScope, pvReserved1, pvReserved2, phContext); if (status == SCARD_S_SUCCESS) PCSC_EstablishCardContext(*phContext); return status; } static LONG WINAPI PCSC_SCardReleaseContext_Internal(SCARDCONTEXT hContext) { PCSC_LONG status = SCARD_S_SUCCESS; if (!g_PCSC.pfnSCardReleaseContext) return PCSC_SCard_LogError("g_PCSC.pfnSCardReleaseContext"); if (!hContext) { WLog_ERR(TAG, "SCardReleaseContext: null hContext"); return PCSC_MapErrorCodeToWinSCard(status); } status = g_PCSC.pfnSCardReleaseContext(hContext); return PCSC_MapErrorCodeToWinSCard(status); } static LONG WINAPI PCSC_SCardReleaseContext(SCARDCONTEXT hContext) { LONG status = SCARD_S_SUCCESS; status = PCSC_SCardReleaseContext_Internal(hContext); if (status != SCARD_S_SUCCESS) PCSC_ReleaseCardContext(hContext); return status; } static LONG WINAPI PCSC_SCardIsValidContext(SCARDCONTEXT hContext) { PCSC_LONG status = SCARD_S_SUCCESS; if (!g_PCSC.pfnSCardIsValidContext) return PCSC_SCard_LogError("g_PCSC.pfnSCardIsValidContext"); status = g_PCSC.pfnSCardIsValidContext(hContext); return PCSC_MapErrorCodeToWinSCard(status); } static LONG WINAPI PCSC_SCardListReaderGroups_Internal(SCARDCONTEXT hContext, LPSTR mszGroups, LPDWORD pcchGroups) { PCSC_LONG status = SCARD_S_SUCCESS; BOOL pcchGroupsAlloc = FALSE; PCSC_DWORD pcsc_cchGroups = 0; if (!pcchGroups) return SCARD_E_INVALID_PARAMETER; if (!g_PCSC.pfnSCardListReaderGroups) return PCSC_SCard_LogError("g_PCSC.pfnSCardListReaderGroups"); if (*pcchGroups == SCARD_AUTOALLOCATE) pcchGroupsAlloc = TRUE; pcsc_cchGroups = pcchGroupsAlloc ? PCSC_SCARD_AUTOALLOCATE : (PCSC_DWORD)*pcchGroups; if (pcchGroupsAlloc && !g_SCardAutoAllocate) { pcsc_cchGroups = 0; status = g_PCSC.pfnSCardListReaderGroups(hContext, NULL, &pcsc_cchGroups); if (status == SCARD_S_SUCCESS) { LPSTR tmp = calloc(1, pcsc_cchGroups); if (!tmp) return SCARD_E_NO_MEMORY; status = g_PCSC.pfnSCardListReaderGroups(hContext, tmp, &pcsc_cchGroups); if (status != SCARD_S_SUCCESS) { free(tmp); tmp = NULL; } else PCSC_AddMemoryBlock(hContext, tmp); *(LPSTR*)mszGroups = tmp; } } else { status = g_PCSC.pfnSCardListReaderGroups(hContext, mszGroups, &pcsc_cchGroups); } *pcchGroups = (DWORD)pcsc_cchGroups; return PCSC_MapErrorCodeToWinSCard(status); } static LONG WINAPI PCSC_SCardListReaderGroupsA(SCARDCONTEXT hContext, LPSTR mszGroups, LPDWORD pcchGroups) { LONG status = SCARD_S_SUCCESS; if (!g_PCSC.pfnSCardListReaderGroups) return PCSC_SCard_LogError("g_PCSC.pfnSCardListReaderGroups"); if (!PCSC_LockCardContext(hContext)) return SCARD_E_INVALID_HANDLE; status = PCSC_SCardListReaderGroups_Internal(hContext, mszGroups, pcchGroups); if (!PCSC_UnlockCardContext(hContext)) return SCARD_E_INVALID_HANDLE; return status; } static LONG WINAPI PCSC_SCardListReaderGroupsW(SCARDCONTEXT hContext, LPWSTR mszGroups, LPDWORD pcchGroups) { LPSTR mszGroupsA = NULL; LPSTR* pMszGroupsA = &mszGroupsA; LONG status = SCARD_S_SUCCESS; if (!g_PCSC.pfnSCardListReaderGroups) return PCSC_SCard_LogError("g_PCSC.pfnSCardListReaderGroups"); if (!PCSC_LockCardContext(hContext)) return SCARD_E_INVALID_HANDLE; status = PCSC_SCardListReaderGroups_Internal(hContext, (LPSTR)&mszGroupsA, pcchGroups); if (status == SCARD_S_SUCCESS) { size_t size = 0; WCHAR* str = ConvertMszUtf8NToWCharAlloc(*pMszGroupsA, *pcchGroups, &size); if (!str) return SCARD_E_NO_MEMORY; *(WCHAR**)mszGroups = str; *pcchGroups = (DWORD)size; PCSC_AddMemoryBlock(hContext, str); PCSC_SCardFreeMemory_Internal(hContext, *pMszGroupsA); } if (!PCSC_UnlockCardContext(hContext)) return SCARD_E_INVALID_HANDLE; return status; } static LONG WINAPI PCSC_SCardListReaders_Internal(SCARDCONTEXT hContext, LPCSTR mszGroups, LPSTR mszReaders, LPDWORD pcchReaders) { PCSC_LONG status = SCARD_S_SUCCESS; BOOL pcchReadersAlloc = FALSE; PCSC_DWORD pcsc_cchReaders = 0; if (!pcchReaders) return SCARD_E_INVALID_PARAMETER; if (!g_PCSC.pfnSCardListReaders) return PCSC_SCard_LogError("g_PCSC.pfnSCardListReaders"); mszGroups = NULL; /* mszGroups is not supported by pcsc-lite */ if (*pcchReaders == SCARD_AUTOALLOCATE) pcchReadersAlloc = TRUE; pcsc_cchReaders = pcchReadersAlloc ? PCSC_SCARD_AUTOALLOCATE : (PCSC_DWORD)*pcchReaders; if (pcchReadersAlloc && !g_SCardAutoAllocate) { pcsc_cchReaders = 0; status = g_PCSC.pfnSCardListReaders(hContext, mszGroups, NULL, &pcsc_cchReaders); if (status == SCARD_S_SUCCESS) { char* tmp = calloc(1, pcsc_cchReaders); if (!tmp) return SCARD_E_NO_MEMORY; status = g_PCSC.pfnSCardListReaders(hContext, mszGroups, tmp, &pcsc_cchReaders); if (status != SCARD_S_SUCCESS) { free(tmp); tmp = NULL; } else PCSC_AddMemoryBlock(hContext, tmp); *(char**)mszReaders = tmp; } } else { status = g_PCSC.pfnSCardListReaders(hContext, mszGroups, mszReaders, &pcsc_cchReaders); } *pcchReaders = (DWORD)pcsc_cchReaders; return PCSC_MapErrorCodeToWinSCard(status); } static LONG WINAPI PCSC_SCardListReadersA(SCARDCONTEXT hContext, LPCSTR mszGroups, LPSTR mszReaders, LPDWORD pcchReaders) { BOOL nullCardContext = FALSE; LONG status = SCARD_S_SUCCESS; if (!g_PCSC.pfnSCardListReaders) return PCSC_SCard_LogError("g_PCSC.pfnSCardListReaders"); if (!hContext) { status = PCSC_SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext); if (status != SCARD_S_SUCCESS) return status; nullCardContext = TRUE; } if (!PCSC_LockCardContext(hContext)) return SCARD_E_INVALID_HANDLE; status = PCSC_SCardListReaders_Internal(hContext, mszGroups, mszReaders, pcchReaders); if (!PCSC_UnlockCardContext(hContext)) return SCARD_E_INVALID_HANDLE; if (nullCardContext) { status = PCSC_SCardReleaseContext(hContext); } return status; } static LONG WINAPI PCSC_SCardListReadersW(SCARDCONTEXT hContext, LPCWSTR mszGroups, LPWSTR mszReaders, LPDWORD pcchReaders) { LPSTR mszGroupsA = NULL; LPSTR mszReadersA = NULL; LONG status = SCARD_S_SUCCESS; BOOL nullCardContext = FALSE; if (!g_PCSC.pfnSCardListReaders) return PCSC_SCard_LogError("g_PCSC.pfnSCardListReaders"); if (!hContext) { status = PCSC_SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext); if (status != SCARD_S_SUCCESS) return status; nullCardContext = TRUE; } if (!PCSC_LockCardContext(hContext)) return SCARD_E_INVALID_HANDLE; mszGroups = NULL; /* mszGroups is not supported by pcsc-lite */ if (mszGroups) { mszGroupsA = ConvertWCharToUtf8Alloc(mszGroups, NULL); if (!mszGroups) return SCARD_E_NO_MEMORY; } union { LPSTR* ppc; LPSTR pc; } cnv; cnv.ppc = &mszReadersA; status = PCSC_SCardListReaders_Internal(hContext, mszGroupsA, cnv.pc, pcchReaders); if (status == SCARD_S_SUCCESS) { size_t size = 0; WCHAR* str = ConvertMszUtf8NToWCharAlloc(mszReadersA, *pcchReaders, &size); PCSC_SCardFreeMemory_Internal(hContext, mszReadersA); if (!str || (size > UINT32_MAX)) { free(mszGroupsA); return SCARD_E_NO_MEMORY; } *(LPWSTR*)mszReaders = str; *pcchReaders = (DWORD)size; PCSC_AddMemoryBlock(hContext, str); } free(mszGroupsA); if (!PCSC_UnlockCardContext(hContext)) return SCARD_E_INVALID_HANDLE; if (nullCardContext) { status = PCSC_SCardReleaseContext(hContext); } return status; } typedef struct { BYTE atr[64]; size_t atrLen; const char* cardName; } PcscKnownAtr; static PcscKnownAtr knownAtrs[] = { /* Yubico YubiKey 5 NFC (PKI) */ { { 0x3B, 0xFD, 0x13, 0x00, 0x00, 0x81, 0x31, 0xFE, 0x15, 0x80, 0x73, 0xC0, 0x21, 0xC0, 0x57, 0x59, 0x75, 0x62, 0x69, 0x4B, 0x65, 0x79, 0x40 }, 23, "NIST SP 800-73 [PIV]" }, /* PIVKey C910 PKI Smart Card (eID) */ { { 0x3B, 0xFC, 0x18, 0x00, 0x00, 0x81, 0x31, 0x80, 0x45, 0x90, 0x67, 0x46, 0x4A, 0x00, 0x64, 0x16, 0x06, 0xF2, 0x72, 0x7E, 0x00, 0xE0 }, 22, "PIVKey Feitian (E0)" } }; #ifndef ARRAY_LENGTH #define ARRAY_LENGTH(a) (sizeof(a) / sizeof(a)[0]) #endif static const char* findCardByAtr(LPCBYTE pbAtr) { for (size_t i = 0; i < ARRAY_LENGTH(knownAtrs); i++) { if (memcmp(knownAtrs[i].atr, pbAtr, knownAtrs[i].atrLen) == 0) return knownAtrs[i].cardName; } return NULL; } static LONG WINAPI PCSC_SCardListCardsA(SCARDCONTEXT hContext, LPCBYTE pbAtr, LPCGUID rgquidInterfaces, DWORD cguidInterfaceCount, CHAR* mszCards, LPDWORD pcchCards) { const char* cardName = NULL; DWORD outputLen = 1; CHAR* output = NULL; BOOL autoAllocate = 0; if (!pbAtr || rgquidInterfaces || cguidInterfaceCount) return SCARD_E_UNSUPPORTED_FEATURE; if (!pcchCards) return SCARD_E_INVALID_PARAMETER; autoAllocate = (*pcchCards == SCARD_AUTOALLOCATE); cardName = findCardByAtr(pbAtr); if (cardName) outputLen += strlen(cardName) + 1; *pcchCards = outputLen; if (autoAllocate) { output = malloc(outputLen); if (!output) return SCARD_E_NO_MEMORY; *((LPSTR*)mszCards) = output; } else { if (!mszCards) return SCARD_S_SUCCESS; if (*pcchCards < outputLen) return SCARD_E_INSUFFICIENT_BUFFER; output = mszCards; } if (cardName) { size_t toCopy = strlen(cardName) + 1; memcpy(output, cardName, toCopy); output += toCopy; } *output = '\0'; return SCARD_S_SUCCESS; } static LONG WINAPI PCSC_SCardListCardsW(SCARDCONTEXT hContext, LPCBYTE pbAtr, LPCGUID rgquidInterfaces, DWORD cguidInterfaceCount, WCHAR* mszCards, LPDWORD pcchCards) { const char* cardName = NULL; DWORD outputLen = 1; WCHAR* output = NULL; BOOL autoAllocate = 0; if (!pbAtr || rgquidInterfaces || cguidInterfaceCount) return SCARD_E_UNSUPPORTED_FEATURE; if (!pcchCards) return SCARD_E_INVALID_PARAMETER; autoAllocate = (*pcchCards == SCARD_AUTOALLOCATE); cardName = findCardByAtr(pbAtr); if (cardName) outputLen += strlen(cardName) + 1; *pcchCards = outputLen; if (autoAllocate) { output = malloc(outputLen * 2); if (!output) return SCARD_E_NO_MEMORY; *((LPWSTR*)mszCards) = output; } else { if (!mszCards) return SCARD_S_SUCCESS; if (*pcchCards < outputLen) return SCARD_E_INSUFFICIENT_BUFFER; output = mszCards; } if (cardName) { size_t toCopy = strlen(cardName) + 1; if (ConvertUtf8ToWChar(cardName, output, toCopy) < 0) return SCARD_F_INTERNAL_ERROR; output += toCopy; } *output = 0; return SCARD_S_SUCCESS; } static LONG WINAPI PCSC_SCardListInterfacesA(SCARDCONTEXT hContext, LPCSTR szCard, LPGUID pguidInterfaces, LPDWORD pcguidInterfaces) { WINPR_UNUSED(hContext); WINPR_UNUSED(szCard); WINPR_UNUSED(pguidInterfaces); WINPR_UNUSED(pcguidInterfaces); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardListInterfacesW(SCARDCONTEXT hContext, LPCWSTR szCard, LPGUID pguidInterfaces, LPDWORD pcguidInterfaces) { WINPR_UNUSED(hContext); WINPR_UNUSED(szCard); WINPR_UNUSED(pguidInterfaces); WINPR_UNUSED(pcguidInterfaces); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardGetProviderIdA(SCARDCONTEXT hContext, LPCSTR szCard, LPGUID pguidProviderId) { WINPR_UNUSED(hContext); WINPR_UNUSED(szCard); WINPR_UNUSED(pguidProviderId); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardGetProviderIdW(SCARDCONTEXT hContext, LPCWSTR szCard, LPGUID pguidProviderId) { WINPR_UNUSED(hContext); WINPR_UNUSED(szCard); WINPR_UNUSED(pguidProviderId); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardGetCardTypeProviderNameA(SCARDCONTEXT hContext, LPCSTR szCardName, DWORD dwProviderId, CHAR* szProvider, LPDWORD pcchProvider) { WINPR_UNUSED(hContext); WINPR_UNUSED(szCardName); WINPR_UNUSED(dwProviderId); WINPR_UNUSED(szProvider); WINPR_UNUSED(pcchProvider); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardGetCardTypeProviderNameW(SCARDCONTEXT hContext, LPCWSTR szCardName, DWORD dwProviderId, WCHAR* szProvider, LPDWORD pcchProvider) { WINPR_UNUSED(hContext); WINPR_UNUSED(szCardName); WINPR_UNUSED(dwProviderId); WINPR_UNUSED(szProvider); WINPR_UNUSED(pcchProvider); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardIntroduceReaderGroupA(SCARDCONTEXT hContext, LPCSTR szGroupName) { WINPR_UNUSED(hContext); WINPR_UNUSED(szGroupName); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardIntroduceReaderGroupW(SCARDCONTEXT hContext, LPCWSTR szGroupName) { WINPR_UNUSED(hContext); WINPR_UNUSED(szGroupName); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardForgetReaderGroupA(SCARDCONTEXT hContext, LPCSTR szGroupName) { WINPR_UNUSED(hContext); WINPR_UNUSED(szGroupName); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardForgetReaderGroupW(SCARDCONTEXT hContext, LPCWSTR szGroupName) { WINPR_UNUSED(hContext); WINPR_UNUSED(szGroupName); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardIntroduceReaderA(SCARDCONTEXT hContext, LPCSTR szReaderName, LPCSTR szDeviceName) { WINPR_UNUSED(hContext); WINPR_UNUSED(szReaderName); WINPR_UNUSED(szDeviceName); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardIntroduceReaderW(SCARDCONTEXT hContext, LPCWSTR szReaderName, LPCWSTR szDeviceName) { WINPR_UNUSED(hContext); WINPR_UNUSED(szReaderName); WINPR_UNUSED(szDeviceName); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardForgetReaderA(SCARDCONTEXT hContext, LPCSTR szReaderName) { WINPR_UNUSED(hContext); WINPR_UNUSED(szReaderName); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardForgetReaderW(SCARDCONTEXT hContext, LPCWSTR szReaderName) { WINPR_UNUSED(hContext); WINPR_UNUSED(szReaderName); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardAddReaderToGroupA(SCARDCONTEXT hContext, LPCSTR szReaderName, LPCSTR szGroupName) { WINPR_UNUSED(hContext); WINPR_UNUSED(szReaderName); WINPR_UNUSED(szGroupName); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardAddReaderToGroupW(SCARDCONTEXT hContext, LPCWSTR szReaderName, LPCWSTR szGroupName) { WINPR_UNUSED(hContext); WINPR_UNUSED(szReaderName); WINPR_UNUSED(szGroupName); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardRemoveReaderFromGroupA(SCARDCONTEXT hContext, LPCSTR szReaderName, LPCSTR szGroupName) { WINPR_UNUSED(hContext); WINPR_UNUSED(szReaderName); WINPR_UNUSED(szGroupName); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardRemoveReaderFromGroupW(SCARDCONTEXT hContext, LPCWSTR szReaderName, LPCWSTR szGroupName) { WINPR_UNUSED(hContext); WINPR_UNUSED(szReaderName); WINPR_UNUSED(szGroupName); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardIntroduceCardTypeA(SCARDCONTEXT hContext, LPCSTR szCardName, LPCGUID pguidPrimaryProvider, LPCGUID rgguidInterfaces, DWORD dwInterfaceCount, LPCBYTE pbAtr, LPCBYTE pbAtrMask, DWORD cbAtrLen) { WINPR_UNUSED(hContext); WINPR_UNUSED(szCardName); WINPR_UNUSED(pguidPrimaryProvider); WINPR_UNUSED(rgguidInterfaces); WINPR_UNUSED(dwInterfaceCount); WINPR_UNUSED(pbAtr); WINPR_UNUSED(pbAtrMask); WINPR_UNUSED(cbAtrLen); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardIntroduceCardTypeW(SCARDCONTEXT hContext, LPCWSTR szCardName, LPCGUID pguidPrimaryProvider, LPCGUID rgguidInterfaces, DWORD dwInterfaceCount, LPCBYTE pbAtr, LPCBYTE pbAtrMask, DWORD cbAtrLen) { WINPR_UNUSED(hContext); WINPR_UNUSED(szCardName); WINPR_UNUSED(pguidPrimaryProvider); WINPR_UNUSED(rgguidInterfaces); WINPR_UNUSED(dwInterfaceCount); WINPR_UNUSED(pbAtr); WINPR_UNUSED(pbAtrMask); WINPR_UNUSED(cbAtrLen); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardSetCardTypeProviderNameA(SCARDCONTEXT hContext, LPCSTR szCardName, DWORD dwProviderId, LPCSTR szProvider) { WINPR_UNUSED(hContext); WINPR_UNUSED(szCardName); WINPR_UNUSED(dwProviderId); WINPR_UNUSED(szProvider); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardSetCardTypeProviderNameW(SCARDCONTEXT hContext, LPCWSTR szCardName, DWORD dwProviderId, LPCWSTR szProvider) { WINPR_UNUSED(hContext); WINPR_UNUSED(szCardName); WINPR_UNUSED(dwProviderId); WINPR_UNUSED(szProvider); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardForgetCardTypeA(SCARDCONTEXT hContext, LPCSTR szCardName) { WINPR_UNUSED(hContext); WINPR_UNUSED(szCardName); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardForgetCardTypeW(SCARDCONTEXT hContext, LPCWSTR szCardName) { WINPR_UNUSED(hContext); WINPR_UNUSED(szCardName); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardFreeMemory_Internal(SCARDCONTEXT hContext, LPVOID pvMem) { PCSC_LONG status = SCARD_S_SUCCESS; if (PCSC_RemoveMemoryBlock(hContext, pvMem)) { free((void*)pvMem); status = SCARD_S_SUCCESS; } else { if (g_PCSC.pfnSCardFreeMemory) { status = g_PCSC.pfnSCardFreeMemory(hContext, pvMem); } } return PCSC_MapErrorCodeToWinSCard(status); } static LONG WINAPI PCSC_SCardFreeMemory(SCARDCONTEXT hContext, LPVOID pvMem) { LONG status = SCARD_S_SUCCESS; if (hContext) { if (!PCSC_LockCardContext(hContext)) return SCARD_E_INVALID_HANDLE; } status = PCSC_SCardFreeMemory_Internal(hContext, pvMem); if (hContext) { if (!PCSC_UnlockCardContext(hContext)) return SCARD_E_INVALID_HANDLE; } return status; } static HANDLE WINAPI PCSC_SCardAccessStartedEvent(void) { LONG status = 0; SCARDCONTEXT hContext = 0; status = PCSC_SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext); if (status != SCARD_S_SUCCESS) return NULL; status = PCSC_SCardReleaseContext(hContext); if (status != SCARD_S_SUCCESS) return NULL; if (!g_StartedEvent) { if (!(g_StartedEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) return NULL; if (!SetEvent(g_StartedEvent)) { CloseHandle(g_StartedEvent); return NULL; } } g_StartedEventRefCount++; return g_StartedEvent; } static void WINAPI PCSC_SCardReleaseStartedEvent(void) { g_StartedEventRefCount--; if (g_StartedEventRefCount == 0) { if (g_StartedEvent) { CloseHandle(g_StartedEvent); g_StartedEvent = NULL; } } } static LONG WINAPI PCSC_SCardLocateCardsA(SCARDCONTEXT hContext, LPCSTR mszCards, LPSCARD_READERSTATEA rgReaderStates, DWORD cReaders) { WINPR_UNUSED(hContext); WINPR_UNUSED(mszCards); WINPR_UNUSED(rgReaderStates); WINPR_UNUSED(cReaders); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardLocateCardsW(SCARDCONTEXT hContext, LPCWSTR mszCards, LPSCARD_READERSTATEW rgReaderStates, DWORD cReaders) { WINPR_UNUSED(hContext); WINPR_UNUSED(mszCards); WINPR_UNUSED(rgReaderStates); WINPR_UNUSED(cReaders); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardLocateCardsByATRA(SCARDCONTEXT hContext, LPSCARD_ATRMASK rgAtrMasks, DWORD cAtrs, LPSCARD_READERSTATEA rgReaderStates, DWORD cReaders) { WINPR_UNUSED(hContext); WINPR_UNUSED(rgAtrMasks); WINPR_UNUSED(cAtrs); WINPR_UNUSED(rgReaderStates); WINPR_UNUSED(cReaders); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardLocateCardsByATRW(SCARDCONTEXT hContext, LPSCARD_ATRMASK rgAtrMasks, DWORD cAtrs, LPSCARD_READERSTATEW rgReaderStates, DWORD cReaders) { WINPR_UNUSED(hContext); WINPR_UNUSED(rgAtrMasks); WINPR_UNUSED(cAtrs); WINPR_UNUSED(rgReaderStates); WINPR_UNUSED(cReaders); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardGetStatusChange_Internal(SCARDCONTEXT hContext, DWORD dwTimeout, LPSCARD_READERSTATEA rgReaderStates, DWORD cReaders) { INT64* map = NULL; PCSC_DWORD cMappedReaders = 0; PCSC_SCARD_READERSTATE* states = NULL; PCSC_LONG status = SCARD_S_SUCCESS; PCSC_DWORD pcsc_dwTimeout = (PCSC_DWORD)dwTimeout; PCSC_DWORD pcsc_cReaders = (PCSC_DWORD)cReaders; if (!g_PCSC.pfnSCardGetStatusChange) return PCSC_SCard_LogError("g_PCSC.pfnSCardGetStatusChange"); if (!cReaders) return SCARD_S_SUCCESS; /* pcsc-lite interprets value 0 as INFINITE, work around the problem by using value 1 */ pcsc_dwTimeout = pcsc_dwTimeout ? pcsc_dwTimeout : 1; /** * Apple's SmartCard Services (not vanilla pcsc-lite) appears to have trouble with the * "\\\\?PnP?\\Notification" reader name. I am always getting EXC_BAD_ACCESS with it. * * The SmartCard Services tarballs can be found here: * http://opensource.apple.com/tarballs/SmartCardServices/ * * The "\\\\?PnP?\\Notification" string cannot be found anywhere in the sources, * while this string is present in the vanilla pcsc-lite sources. * * To work around this apparent lack of "\\\\?PnP?\\Notification" support, * we have to filter rgReaderStates to exclude the special PnP reader name. */ map = (INT64*)calloc(pcsc_cReaders, sizeof(INT64)); if (!map) return SCARD_E_NO_MEMORY; states = (PCSC_SCARD_READERSTATE*)calloc(pcsc_cReaders, sizeof(PCSC_SCARD_READERSTATE)); if (!states) { free(map); return SCARD_E_NO_MEMORY; } PCSC_DWORD j = 0; for (PCSC_DWORD i = 0; i < pcsc_cReaders; i++) { if (!g_PnP_Notification) { LPSCARD_READERSTATEA reader = &rgReaderStates[i]; if (!reader->szReader) continue; if (0 == _stricmp(reader->szReader, SMARTCARD_PNP_NOTIFICATION_A)) { map[i] = -1; /* unmapped */ continue; } } map[i] = (INT64)j; states[j].szReader = rgReaderStates[i].szReader; states[j].dwCurrentState = rgReaderStates[i].dwCurrentState; states[j].pvUserData = rgReaderStates[i].pvUserData; states[j].dwEventState = rgReaderStates[i].dwEventState; states[j].cbAtr = rgReaderStates[i].cbAtr; CopyMemory(&(states[j].rgbAtr), &(rgReaderStates[i].rgbAtr), PCSC_MAX_ATR_SIZE); j++; } cMappedReaders = j; if (cMappedReaders > 0) { status = g_PCSC.pfnSCardGetStatusChange(hContext, pcsc_dwTimeout, states, cMappedReaders); } else { status = SCARD_S_SUCCESS; } for (PCSC_DWORD i = 0; i < pcsc_cReaders; i++) { if (map[i] < 0) continue; /* unmapped */ PCSC_DWORD j = (PCSC_DWORD)map[i]; rgReaderStates[i].dwCurrentState = (DWORD)states[j].dwCurrentState; rgReaderStates[i].cbAtr = (DWORD)states[j].cbAtr; CopyMemory(&(rgReaderStates[i].rgbAtr), &(states[j].rgbAtr), PCSC_MAX_ATR_SIZE); rgReaderStates[i].dwEventState = (DWORD)states[j].dwEventState; } free(map); free(states); return PCSC_MapErrorCodeToWinSCard(status); } static LONG WINAPI PCSC_SCardGetStatusChangeA(SCARDCONTEXT hContext, DWORD dwTimeout, LPSCARD_READERSTATEA rgReaderStates, DWORD cReaders) { LONG status = SCARD_S_SUCCESS; if (!PCSC_LockCardContext(hContext)) return SCARD_E_INVALID_HANDLE; status = PCSC_SCardGetStatusChange_Internal(hContext, dwTimeout, rgReaderStates, cReaders); if (!PCSC_UnlockCardContext(hContext)) return SCARD_E_INVALID_HANDLE; return status; } static LONG WINAPI PCSC_SCardGetStatusChangeW(SCARDCONTEXT hContext, DWORD dwTimeout, LPSCARD_READERSTATEW rgReaderStates, DWORD cReaders) { LPSCARD_READERSTATEA states = NULL; LONG status = SCARD_S_SUCCESS; if (!g_PCSC.pfnSCardGetStatusChange) return PCSC_SCard_LogError("g_PCSC.pfnSCardGetStatusChange"); if (!PCSC_LockCardContext(hContext)) return SCARD_E_INVALID_HANDLE; states = (LPSCARD_READERSTATEA)calloc(cReaders, sizeof(SCARD_READERSTATEA)); if (!states) { PCSC_UnlockCardContext(hContext); return SCARD_E_NO_MEMORY; } for (DWORD index = 0; index < cReaders; index++) { const LPSCARD_READERSTATEW curReader = &rgReaderStates[index]; LPSCARD_READERSTATEA cur = &states[index]; cur->szReader = ConvertWCharToUtf8Alloc(curReader->szReader, NULL); cur->pvUserData = curReader->pvUserData; cur->dwCurrentState = curReader->dwCurrentState; cur->dwEventState = curReader->dwEventState; cur->cbAtr = curReader->cbAtr; CopyMemory(&(cur->rgbAtr), &(curReader->rgbAtr), ARRAYSIZE(cur->rgbAtr)); } status = PCSC_SCardGetStatusChange_Internal(hContext, dwTimeout, states, cReaders); for (DWORD index = 0; index < cReaders; index++) { free((void*)states[index].szReader); rgReaderStates[index].pvUserData = states[index].pvUserData; rgReaderStates[index].dwCurrentState = states[index].dwCurrentState; rgReaderStates[index].dwEventState = states[index].dwEventState; rgReaderStates[index].cbAtr = states[index].cbAtr; CopyMemory(&(rgReaderStates[index].rgbAtr), &(states[index].rgbAtr), 36); } free(states); if (!PCSC_UnlockCardContext(hContext)) return SCARD_E_INVALID_HANDLE; return status; } static LONG WINAPI PCSC_SCardCancel(SCARDCONTEXT hContext) { PCSC_LONG status = SCARD_S_SUCCESS; if (!g_PCSC.pfnSCardCancel) return PCSC_SCard_LogError("g_PCSC.pfnSCardCancel"); status = g_PCSC.pfnSCardCancel(hContext); return PCSC_MapErrorCodeToWinSCard(status); } static LONG WINAPI PCSC_SCardConnect_Internal(SCARDCONTEXT hContext, LPCSTR szReader, DWORD dwShareMode, DWORD dwPreferredProtocols, LPSCARDHANDLE phCard, LPDWORD pdwActiveProtocol) { BOOL shared = 0; const char* szReaderPCSC = NULL; PCSC_LONG status = SCARD_S_SUCCESS; PCSC_SCARDHANDLE* pCard = NULL; PCSC_DWORD pcsc_dwShareMode = (PCSC_DWORD)dwShareMode; PCSC_DWORD pcsc_dwPreferredProtocols = 0; PCSC_DWORD pcsc_dwActiveProtocol = 0; if (!g_PCSC.pfnSCardConnect) return PCSC_SCard_LogError("g_PCSC.pfnSCardConnect"); shared = (dwShareMode == SCARD_SHARE_DIRECT) ? TRUE : FALSE; PCSC_WaitForCardAccess(hContext, 0, shared); szReaderPCSC = szReader; /** * As stated here : * https://pcsclite.alioth.debian.org/api/group__API.html#ga4e515829752e0a8dbc4d630696a8d6a5 * SCARD_PROTOCOL_UNDEFINED is valid for dwPreferredProtocols (only) if dwShareMode == * SCARD_SHARE_DIRECT and allows to send control commands to the reader (with SCardControl()) * even if a card is not present in the reader */ if (pcsc_dwShareMode == SCARD_SHARE_DIRECT && dwPreferredProtocols == SCARD_PROTOCOL_UNDEFINED) pcsc_dwPreferredProtocols = SCARD_PROTOCOL_UNDEFINED; else pcsc_dwPreferredProtocols = (PCSC_DWORD)PCSC_ConvertProtocolsFromWinSCard(dwPreferredProtocols); status = g_PCSC.pfnSCardConnect(hContext, szReaderPCSC, pcsc_dwShareMode, pcsc_dwPreferredProtocols, phCard, &pcsc_dwActiveProtocol); if (status == SCARD_S_SUCCESS) { pCard = PCSC_ConnectCardHandle(hContext, *phCard); *pdwActiveProtocol = PCSC_ConvertProtocolsToWinSCard((DWORD)pcsc_dwActiveProtocol); pCard->shared = shared; // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): ListDictionary_Add takes ownership of pCard PCSC_WaitForCardAccess(hContext, pCard->hSharedContext, shared); } return PCSC_MapErrorCodeToWinSCard(status); } static LONG WINAPI PCSC_SCardConnectA(SCARDCONTEXT hContext, LPCSTR szReader, DWORD dwShareMode, DWORD dwPreferredProtocols, LPSCARDHANDLE phCard, LPDWORD pdwActiveProtocol) { LONG status = SCARD_S_SUCCESS; if (!PCSC_LockCardContext(hContext)) return SCARD_E_INVALID_HANDLE; status = PCSC_SCardConnect_Internal(hContext, szReader, dwShareMode, dwPreferredProtocols, phCard, pdwActiveProtocol); if (!PCSC_UnlockCardContext(hContext)) return SCARD_E_INVALID_HANDLE; return status; } static LONG WINAPI PCSC_SCardConnectW(SCARDCONTEXT hContext, LPCWSTR szReader, DWORD dwShareMode, DWORD dwPreferredProtocols, LPSCARDHANDLE phCard, LPDWORD pdwActiveProtocol) { LPSTR szReaderA = NULL; LONG status = SCARD_S_SUCCESS; if (!PCSC_LockCardContext(hContext)) return SCARD_E_INVALID_HANDLE; if (szReader) { szReaderA = ConvertWCharToUtf8Alloc(szReader, NULL); if (!szReaderA) return SCARD_E_INSUFFICIENT_BUFFER; } status = PCSC_SCardConnect_Internal(hContext, szReaderA, dwShareMode, dwPreferredProtocols, phCard, pdwActiveProtocol); free(szReaderA); if (!PCSC_UnlockCardContext(hContext)) return SCARD_E_INVALID_HANDLE; return status; } static LONG WINAPI PCSC_SCardReconnect(SCARDHANDLE hCard, DWORD dwShareMode, DWORD dwPreferredProtocols, DWORD dwInitialization, LPDWORD pdwActiveProtocol) { BOOL shared = 0; PCSC_LONG status = SCARD_S_SUCCESS; PCSC_DWORD pcsc_dwShareMode = (PCSC_DWORD)dwShareMode; PCSC_DWORD pcsc_dwPreferredProtocols = 0; PCSC_DWORD pcsc_dwInitialization = (PCSC_DWORD)dwInitialization; PCSC_DWORD pcsc_dwActiveProtocol = 0; if (!g_PCSC.pfnSCardReconnect) return PCSC_SCard_LogError("g_PCSC.pfnSCardReconnect"); shared = (dwShareMode == SCARD_SHARE_DIRECT) ? TRUE : FALSE; PCSC_WaitForCardAccess(0, hCard, shared); pcsc_dwPreferredProtocols = (PCSC_DWORD)PCSC_ConvertProtocolsFromWinSCard(dwPreferredProtocols); status = g_PCSC.pfnSCardReconnect(hCard, pcsc_dwShareMode, pcsc_dwPreferredProtocols, pcsc_dwInitialization, &pcsc_dwActiveProtocol); *pdwActiveProtocol = PCSC_ConvertProtocolsToWinSCard((DWORD)pcsc_dwActiveProtocol); return PCSC_MapErrorCodeToWinSCard(status); } static LONG WINAPI PCSC_SCardDisconnect(SCARDHANDLE hCard, DWORD dwDisposition) { PCSC_LONG status = SCARD_S_SUCCESS; PCSC_DWORD pcsc_dwDisposition = (PCSC_DWORD)dwDisposition; if (!g_PCSC.pfnSCardDisconnect) return PCSC_SCard_LogError("g_PCSC.pfnSCardDisconnect"); status = g_PCSC.pfnSCardDisconnect(hCard, pcsc_dwDisposition); if (status == SCARD_S_SUCCESS) { PCSC_DisconnectCardHandle(hCard); } PCSC_ReleaseCardAccess(0, hCard); return PCSC_MapErrorCodeToWinSCard(status); } static LONG WINAPI PCSC_SCardBeginTransaction(SCARDHANDLE hCard) { PCSC_LONG status = SCARD_S_SUCCESS; PCSC_SCARDHANDLE* pCard = NULL; PCSC_SCARDCONTEXT* pContext = NULL; if (!g_PCSC.pfnSCardBeginTransaction) return PCSC_SCard_LogError("g_PCSC.pfnSCardBeginTransaction"); pCard = PCSC_GetCardHandleData(hCard); if (!pCard) return SCARD_E_INVALID_HANDLE; pContext = PCSC_GetCardContextData(pCard->hSharedContext); if (!pContext) return SCARD_E_INVALID_HANDLE; if (pContext->isTransactionLocked) return SCARD_S_SUCCESS; /* disable nested transactions */ status = g_PCSC.pfnSCardBeginTransaction(hCard); pContext->isTransactionLocked = TRUE; return PCSC_MapErrorCodeToWinSCard(status); } static LONG WINAPI PCSC_SCardEndTransaction(SCARDHANDLE hCard, DWORD dwDisposition) { PCSC_LONG status = SCARD_S_SUCCESS; PCSC_SCARDHANDLE* pCard = NULL; PCSC_SCARDCONTEXT* pContext = NULL; PCSC_DWORD pcsc_dwDisposition = (PCSC_DWORD)dwDisposition; if (!g_PCSC.pfnSCardEndTransaction) return PCSC_SCard_LogError("g_PCSC.pfnSCardEndTransaction"); pCard = PCSC_GetCardHandleData(hCard); if (!pCard) return SCARD_E_INVALID_HANDLE; pContext = PCSC_GetCardContextData(pCard->hSharedContext); if (!pContext) return SCARD_E_INVALID_HANDLE; PCSC_ReleaseCardAccess(0, hCard); if (!pContext->isTransactionLocked) return SCARD_S_SUCCESS; /* disable nested transactions */ status = g_PCSC.pfnSCardEndTransaction(hCard, pcsc_dwDisposition); pContext->isTransactionLocked = FALSE; return PCSC_MapErrorCodeToWinSCard(status); } static LONG WINAPI PCSC_SCardCancelTransaction(SCARDHANDLE hCard) { WINPR_UNUSED(hCard); return SCARD_S_SUCCESS; } /* * PCSC returns a string but Windows SCardStatus requires the return to be a multi string. * Therefore extra length checks and additional buffer allocation is required */ static LONG WINAPI PCSC_SCardStatus_Internal(SCARDHANDLE hCard, LPSTR mszReaderNames, LPDWORD pcchReaderLen, LPDWORD pdwState, LPDWORD pdwProtocol, LPBYTE pbAtr, LPDWORD pcbAtrLen, BOOL unicode) { PCSC_SCARDHANDLE* pCard = NULL; SCARDCONTEXT hContext = 0; PCSC_LONG status = 0; PCSC_DWORD pcsc_cchReaderLen = 0; PCSC_DWORD pcsc_cbAtrLen = 0; PCSC_DWORD pcsc_dwState = 0; PCSC_DWORD pcsc_dwProtocol = 0; BOOL allocateReader = FALSE; BOOL allocateAtr = FALSE; LPSTR readerNames = mszReaderNames; LPBYTE atr = pbAtr; LPSTR tReader = NULL; LPBYTE tATR = NULL; if (!g_PCSC.pfnSCardStatus) return PCSC_SCard_LogError("g_PCSC.pfnSCardStatus"); pCard = PCSC_GetCardHandleData(hCard); if (!pCard) return SCARD_E_INVALID_VALUE; PCSC_WaitForCardAccess(0, hCard, pCard->shared); hContext = PCSC_GetCardContextFromHandle(hCard); if (!hContext) return SCARD_E_INVALID_VALUE; status = g_PCSC.pfnSCardStatus(hCard, NULL, &pcsc_cchReaderLen, NULL, NULL, NULL, &pcsc_cbAtrLen); if (status != STATUS_SUCCESS) return PCSC_MapErrorCodeToWinSCard(status); pcsc_cchReaderLen++; if (unicode) pcsc_cchReaderLen *= 2; if (pcchReaderLen) { if (*pcchReaderLen == SCARD_AUTOALLOCATE) allocateReader = TRUE; else if (mszReaderNames && (*pcchReaderLen < pcsc_cchReaderLen)) return SCARD_E_INSUFFICIENT_BUFFER; else pcsc_cchReaderLen = *pcchReaderLen; } if (pcbAtrLen) { if (*pcbAtrLen == SCARD_AUTOALLOCATE) allocateAtr = TRUE; else if (pbAtr && (*pcbAtrLen < pcsc_cbAtrLen)) return SCARD_E_INSUFFICIENT_BUFFER; else pcsc_cbAtrLen = *pcbAtrLen; } if (allocateReader && pcsc_cchReaderLen > 0 && mszReaderNames) { #ifdef __MACOSX__ /** * Workaround for SCardStatus Bug in MAC OS X Yosemite */ if (OSXVersion == 0x10100000) pcsc_cchReaderLen++; #endif tReader = calloc(sizeof(CHAR), pcsc_cchReaderLen + 1); if (!tReader) { status = ERROR_NOT_ENOUGH_MEMORY; goto out_fail; } readerNames = tReader; } if (allocateAtr && pcsc_cbAtrLen > 0 && pbAtr) { tATR = calloc(1, pcsc_cbAtrLen); if (!tATR) { status = ERROR_NOT_ENOUGH_MEMORY; goto out_fail; } atr = tATR; } status = g_PCSC.pfnSCardStatus(hCard, readerNames, &pcsc_cchReaderLen, &pcsc_dwState, &pcsc_dwProtocol, atr, &pcsc_cbAtrLen); if (status != STATUS_SUCCESS) goto out_fail; if (tATR) { PCSC_AddMemoryBlock(hContext, tATR); *(BYTE**)pbAtr = tATR; } if (tReader) { if (unicode) { size_t size = 0; WCHAR* tmp = ConvertMszUtf8NToWCharAlloc(tReader, pcsc_cchReaderLen + 1, &size); if (tmp == NULL) { status = ERROR_NOT_ENOUGH_MEMORY; goto out_fail; } free(tReader); PCSC_AddMemoryBlock(hContext, tmp); *(WCHAR**)mszReaderNames = tmp; } else { tReader[pcsc_cchReaderLen - 1] = '\0'; PCSC_AddMemoryBlock(hContext, tReader); *(char**)mszReaderNames = tReader; } } pcsc_dwState &= 0xFFFF; if (pdwState) *pdwState = PCSC_ConvertCardStateToWinSCard((DWORD)pcsc_dwState, status); if (pdwProtocol) *pdwProtocol = PCSC_ConvertProtocolsToWinSCard((DWORD)pcsc_dwProtocol); if (pcbAtrLen) *pcbAtrLen = (DWORD)pcsc_cbAtrLen; if (pcchReaderLen) { WINPR_ASSERT(pcsc_cchReaderLen < UINT32_MAX); *pcchReaderLen = (DWORD)pcsc_cchReaderLen + 1u; } return (LONG)status; out_fail: free(tReader); free(tATR); return (LONG)status; } static LONG WINAPI PCSC_SCardState(SCARDHANDLE hCard, LPDWORD pdwState, LPDWORD pdwProtocol, LPBYTE pbAtr, LPDWORD pcbAtrLen) { DWORD cchReaderLen = 0; SCARDCONTEXT hContext = 0; LPSTR mszReaderNames = NULL; PCSC_LONG status = SCARD_S_SUCCESS; PCSC_SCARDHANDLE* pCard = NULL; DWORD pcsc_dwState = 0; DWORD pcsc_dwProtocol = 0; DWORD pcsc_cbAtrLen = 0; if (pcbAtrLen) pcsc_cbAtrLen = (DWORD)*pcbAtrLen; if (!g_PCSC.pfnSCardStatus) return PCSC_SCard_LogError("g_PCSC.pfnSCardStatus"); pCard = PCSC_GetCardHandleData(hCard); if (!pCard) return SCARD_E_INVALID_VALUE; PCSC_WaitForCardAccess(0, hCard, pCard->shared); hContext = PCSC_GetCardContextFromHandle(hCard); if (!hContext) return SCARD_E_INVALID_VALUE; cchReaderLen = SCARD_AUTOALLOCATE; status = PCSC_SCardStatus_Internal(hCard, (LPSTR)&mszReaderNames, &cchReaderLen, &pcsc_dwState, &pcsc_dwProtocol, pbAtr, &pcsc_cbAtrLen, FALSE); if (mszReaderNames) PCSC_SCardFreeMemory_Internal(hContext, mszReaderNames); *pdwState = (DWORD)pcsc_dwState; *pdwProtocol = PCSC_ConvertProtocolsToWinSCard((DWORD)pcsc_dwProtocol); if (pcbAtrLen) *pcbAtrLen = (DWORD)pcsc_cbAtrLen; return PCSC_MapErrorCodeToWinSCard(status); } static LONG WINAPI PCSC_SCardStatusA(SCARDHANDLE hCard, LPSTR mszReaderNames, LPDWORD pcchReaderLen, LPDWORD pdwState, LPDWORD pdwProtocol, LPBYTE pbAtr, LPDWORD pcbAtrLen) { return PCSC_SCardStatus_Internal(hCard, mszReaderNames, pcchReaderLen, pdwState, pdwProtocol, pbAtr, pcbAtrLen, FALSE); } static LONG WINAPI PCSC_SCardStatusW(SCARDHANDLE hCard, LPWSTR mszReaderNames, LPDWORD pcchReaderLen, LPDWORD pdwState, LPDWORD pdwProtocol, LPBYTE pbAtr, LPDWORD pcbAtrLen) { return PCSC_SCardStatus_Internal(hCard, (LPSTR)mszReaderNames, pcchReaderLen, pdwState, pdwProtocol, pbAtr, pcbAtrLen, TRUE); } static LONG WINAPI PCSC_SCardTransmit(SCARDHANDLE hCard, LPCSCARD_IO_REQUEST pioSendPci, LPCBYTE pbSendBuffer, DWORD cbSendLength, LPSCARD_IO_REQUEST pioRecvPci, LPBYTE pbRecvBuffer, LPDWORD pcbRecvLength) { PCSC_LONG status = SCARD_S_SUCCESS; PCSC_SCARDHANDLE* pCard = NULL; PCSC_DWORD cbExtraBytes = 0; BYTE* pbExtraBytes = NULL; BYTE* pcsc_pbExtraBytes = NULL; PCSC_DWORD pcsc_cbSendLength = (PCSC_DWORD)cbSendLength; PCSC_DWORD pcsc_cbRecvLength = 0; union { const PCSC_SCARD_IO_REQUEST* pcs; PCSC_SCARD_IO_REQUEST* ps; LPSCARD_IO_REQUEST lps; LPCSCARD_IO_REQUEST lpcs; BYTE* pb; } sendPci, recvPci, inRecvPci, inSendPci; sendPci.ps = NULL; recvPci.ps = NULL; inRecvPci.lps = pioRecvPci; inSendPci.lpcs = pioSendPci; if (!g_PCSC.pfnSCardTransmit) return PCSC_SCard_LogError("g_PCSC.pfnSCardTransmit"); pCard = PCSC_GetCardHandleData(hCard); if (!pCard) return SCARD_E_INVALID_VALUE; PCSC_WaitForCardAccess(0, hCard, pCard->shared); if (!pcbRecvLength) return SCARD_E_INVALID_PARAMETER; if (*pcbRecvLength == SCARD_AUTOALLOCATE) return SCARD_E_INVALID_PARAMETER; pcsc_cbRecvLength = (PCSC_DWORD)*pcbRecvLength; if (!inSendPci.lpcs) { PCSC_DWORD dwState = 0; PCSC_DWORD cbAtrLen = 0; PCSC_DWORD dwProtocol = 0; PCSC_DWORD cchReaderLen = 0; /** * pcsc-lite cannot have a null pioSendPci parameter, unlike WinSCard. * Query the current protocol and use default SCARD_IO_REQUEST for it. */ status = g_PCSC.pfnSCardStatus(hCard, NULL, &cchReaderLen, &dwState, &dwProtocol, NULL, &cbAtrLen); if (status == SCARD_S_SUCCESS) { if (dwProtocol == SCARD_PROTOCOL_T0) sendPci.pcs = PCSC_SCARD_PCI_T0; else if (dwProtocol == SCARD_PROTOCOL_T1) sendPci.pcs = PCSC_SCARD_PCI_T1; else if (dwProtocol == PCSC_SCARD_PROTOCOL_RAW) sendPci.pcs = PCSC_SCARD_PCI_RAW; } } else { cbExtraBytes = inSendPci.lpcs->cbPciLength - sizeof(SCARD_IO_REQUEST); sendPci.ps = (PCSC_SCARD_IO_REQUEST*)malloc(sizeof(PCSC_SCARD_IO_REQUEST) + cbExtraBytes); if (!sendPci.ps) return SCARD_E_NO_MEMORY; sendPci.ps->dwProtocol = (PCSC_DWORD)inSendPci.lpcs->dwProtocol; sendPci.ps->cbPciLength = sizeof(PCSC_SCARD_IO_REQUEST) + cbExtraBytes; pbExtraBytes = &(inSendPci.pb)[sizeof(SCARD_IO_REQUEST)]; pcsc_pbExtraBytes = &(sendPci.pb)[sizeof(PCSC_SCARD_IO_REQUEST)]; CopyMemory(pcsc_pbExtraBytes, pbExtraBytes, cbExtraBytes); } if (inRecvPci.lps) { cbExtraBytes = inRecvPci.lps->cbPciLength - sizeof(SCARD_IO_REQUEST); recvPci.ps = (PCSC_SCARD_IO_REQUEST*)malloc(sizeof(PCSC_SCARD_IO_REQUEST) + cbExtraBytes); if (!recvPci.ps) { if (inSendPci.lpcs) free(sendPci.ps); return SCARD_E_NO_MEMORY; } recvPci.ps->dwProtocol = (PCSC_DWORD)inRecvPci.lps->dwProtocol; recvPci.ps->cbPciLength = sizeof(PCSC_SCARD_IO_REQUEST) + cbExtraBytes; pbExtraBytes = &(inRecvPci.pb)[sizeof(SCARD_IO_REQUEST)]; pcsc_pbExtraBytes = &(recvPci.pb)[sizeof(PCSC_SCARD_IO_REQUEST)]; CopyMemory(pcsc_pbExtraBytes, pbExtraBytes, cbExtraBytes); } status = g_PCSC.pfnSCardTransmit(hCard, sendPci.ps, pbSendBuffer, pcsc_cbSendLength, recvPci.ps, pbRecvBuffer, &pcsc_cbRecvLength); *pcbRecvLength = (DWORD)pcsc_cbRecvLength; if (inSendPci.lpcs) free(sendPci.ps); /* pcsc_pioSendPci is dynamically allocated only when pioSendPci is non null */ if (inRecvPci.lps) { cbExtraBytes = inRecvPci.lps->cbPciLength - sizeof(SCARD_IO_REQUEST); pbExtraBytes = &(inRecvPci.pb)[sizeof(SCARD_IO_REQUEST)]; pcsc_pbExtraBytes = &(recvPci.pb)[sizeof(PCSC_SCARD_IO_REQUEST)]; CopyMemory(pbExtraBytes, pcsc_pbExtraBytes, cbExtraBytes); /* copy extra bytes */ free(recvPci.ps); /* pcsc_pioRecvPci is dynamically allocated only when pioRecvPci is non null */ } return PCSC_MapErrorCodeToWinSCard(status); } static LONG WINAPI PCSC_SCardGetTransmitCount(SCARDHANDLE hCard, LPDWORD pcTransmitCount) { WINPR_UNUSED(pcTransmitCount); PCSC_SCARDHANDLE* pCard = NULL; pCard = PCSC_GetCardHandleData(hCard); if (!pCard) return SCARD_E_INVALID_VALUE; PCSC_WaitForCardAccess(0, hCard, pCard->shared); return SCARD_S_SUCCESS; } static LONG WINAPI PCSC_SCardControl(SCARDHANDLE hCard, DWORD dwControlCode, LPCVOID lpInBuffer, DWORD cbInBufferSize, LPVOID lpOutBuffer, DWORD cbOutBufferSize, LPDWORD lpBytesReturned) { DWORD IoCtlFunction = 0; DWORD IoCtlDeviceType = 0; BOOL getFeatureRequest = FALSE; PCSC_LONG status = SCARD_S_SUCCESS; PCSC_SCARDHANDLE* pCard = NULL; PCSC_DWORD pcsc_dwControlCode = 0; PCSC_DWORD pcsc_cbInBufferSize = (PCSC_DWORD)cbInBufferSize; PCSC_DWORD pcsc_cbOutBufferSize = (PCSC_DWORD)cbOutBufferSize; PCSC_DWORD pcsc_BytesReturned = 0; if (!g_PCSC.pfnSCardControl) return PCSC_SCard_LogError("g_PCSC.pfnSCardControl"); pCard = PCSC_GetCardHandleData(hCard); if (!pCard) return SCARD_E_INVALID_VALUE; PCSC_WaitForCardAccess(0, hCard, pCard->shared); /** * PCSCv2 Part 10: * http://www.pcscworkgroup.com/specifications/files/pcsc10_v2.02.09.pdf * * Smart Card Driver IOCTLs: * http://msdn.microsoft.com/en-us/library/windows/hardware/ff548988/ * * Converting Windows Feature Request IOCTL code to the pcsc-lite control code: * http://musclecard.996296.n3.nabble.com/Converting-Windows-Feature-Request-IOCTL-code-to-the-pcsc-lite-control-code-td4906.html */ IoCtlFunction = FUNCTION_FROM_CTL_CODE(dwControlCode); IoCtlDeviceType = DEVICE_TYPE_FROM_CTL_CODE(dwControlCode); if (dwControlCode == IOCTL_SMARTCARD_GET_FEATURE_REQUEST) getFeatureRequest = TRUE; if (IoCtlDeviceType == FILE_DEVICE_SMARTCARD) dwControlCode = PCSC_SCARD_CTL_CODE(IoCtlFunction); pcsc_dwControlCode = (PCSC_DWORD)dwControlCode; status = g_PCSC.pfnSCardControl(hCard, pcsc_dwControlCode, lpInBuffer, pcsc_cbInBufferSize, lpOutBuffer, pcsc_cbOutBufferSize, &pcsc_BytesReturned); *lpBytesReturned = (DWORD)pcsc_BytesReturned; if (getFeatureRequest) { UINT32 count = 0; PCSC_TLV_STRUCTURE* tlv = (PCSC_TLV_STRUCTURE*)lpOutBuffer; if ((*lpBytesReturned % sizeof(PCSC_TLV_STRUCTURE)) != 0) return SCARD_E_UNEXPECTED; count = *lpBytesReturned / sizeof(PCSC_TLV_STRUCTURE); for (DWORD index = 0; index < count; index++) { if (tlv[index].length != 4) return SCARD_E_UNEXPECTED; } } return PCSC_MapErrorCodeToWinSCard(status); } static LONG WINAPI PCSC_SCardGetAttrib_Internal(SCARDHANDLE hCard, DWORD dwAttrId, LPBYTE pbAttr, LPDWORD pcbAttrLen) { SCARDCONTEXT hContext = 0; BOOL pcbAttrLenAlloc = FALSE; PCSC_LONG status = SCARD_S_SUCCESS; PCSC_SCARDHANDLE* pCard = NULL; PCSC_DWORD pcsc_dwAttrId = (PCSC_DWORD)dwAttrId; PCSC_DWORD pcsc_cbAttrLen = 0; if (!g_PCSC.pfnSCardGetAttrib) return PCSC_SCard_LogError("g_PCSC.pfnSCardGetAttrib"); pCard = PCSC_GetCardHandleData(hCard); if (!pCard) return SCARD_E_INVALID_VALUE; PCSC_WaitForCardAccess(0, hCard, pCard->shared); hContext = PCSC_GetCardContextFromHandle(hCard); if (!hContext) return SCARD_E_INVALID_HANDLE; if (!pcbAttrLen) return SCARD_E_INVALID_PARAMETER; if (*pcbAttrLen == SCARD_AUTOALLOCATE) { if (!pbAttr) return SCARD_E_INVALID_PARAMETER; pcbAttrLenAlloc = TRUE; } pcsc_cbAttrLen = pcbAttrLenAlloc ? PCSC_SCARD_AUTOALLOCATE : (PCSC_DWORD)*pcbAttrLen; if (pcbAttrLenAlloc && !g_SCardAutoAllocate) { pcsc_cbAttrLen = 0; status = g_PCSC.pfnSCardGetAttrib(hCard, pcsc_dwAttrId, NULL, &pcsc_cbAttrLen); if (status == SCARD_S_SUCCESS) { BYTE* tmp = (BYTE*)calloc(1, pcsc_cbAttrLen); if (!tmp) return SCARD_E_NO_MEMORY; status = g_PCSC.pfnSCardGetAttrib(hCard, pcsc_dwAttrId, tmp, &pcsc_cbAttrLen); if (status != SCARD_S_SUCCESS) { free(tmp); tmp = NULL; } else PCSC_AddMemoryBlock(hContext, tmp); *(BYTE**)pbAttr = tmp; } } else { status = g_PCSC.pfnSCardGetAttrib(hCard, pcsc_dwAttrId, pbAttr, &pcsc_cbAttrLen); } if (status == SCARD_S_SUCCESS) *pcbAttrLen = (DWORD)pcsc_cbAttrLen; return PCSC_MapErrorCodeToWinSCard(status); } static LONG WINAPI PCSC_SCardGetAttrib_FriendlyName(SCARDHANDLE hCard, DWORD dwAttrId, LPBYTE pbAttr, LPDWORD pcbAttrLen) { size_t length = 0; char* namePCSC = NULL; char* pbAttrA = NULL; DWORD cbAttrLen = 0; WCHAR* pbAttrW = NULL; SCARDCONTEXT hContext = 0; LONG status = SCARD_S_SUCCESS; hContext = PCSC_GetCardContextFromHandle(hCard); if (!hContext) return SCARD_E_INVALID_HANDLE; if (!pcbAttrLen) return SCARD_E_INVALID_PARAMETER; cbAttrLen = *pcbAttrLen; *pcbAttrLen = SCARD_AUTOALLOCATE; status = PCSC_SCardGetAttrib_Internal(hCard, SCARD_ATTR_DEVICE_FRIENDLY_NAME_A, (LPBYTE)&pbAttrA, pcbAttrLen); if (status != SCARD_S_SUCCESS) { *pcbAttrLen = SCARD_AUTOALLOCATE; status = PCSC_SCardGetAttrib_Internal(hCard, SCARD_ATTR_DEVICE_FRIENDLY_NAME_W, (LPBYTE)&pbAttrW, pcbAttrLen); if (status != SCARD_S_SUCCESS) return status; namePCSC = ConvertMszWCharNToUtf8Alloc(pbAttrW, *pcbAttrLen, NULL); PCSC_SCardFreeMemory_Internal(hContext, pbAttrW); } else { namePCSC = _strdup(pbAttrA); if (!namePCSC) return SCARD_E_NO_MEMORY; PCSC_SCardFreeMemory_Internal(hContext, pbAttrA); } length = strlen(namePCSC); if (dwAttrId == SCARD_ATTR_DEVICE_FRIENDLY_NAME_W) { size_t size = 0; WCHAR* friendlyNameW = ConvertUtf8ToWCharAlloc(namePCSC, &size); /* length here includes null terminator */ if (!friendlyNameW) status = SCARD_E_NO_MEMORY; else { length = size; if (cbAttrLen == SCARD_AUTOALLOCATE) { WINPR_ASSERT(length <= UINT32_MAX / sizeof(WCHAR)); *(WCHAR**)pbAttr = friendlyNameW; *pcbAttrLen = (UINT32)length * sizeof(WCHAR); PCSC_AddMemoryBlock(hContext, friendlyNameW); } else { if ((length * 2) > cbAttrLen) status = SCARD_E_INSUFFICIENT_BUFFER; else { WINPR_ASSERT(length <= UINT32_MAX / sizeof(WCHAR)); CopyMemory(pbAttr, (BYTE*)friendlyNameW, (length * sizeof(WCHAR))); *pcbAttrLen = (UINT32)length * sizeof(WCHAR); } free(friendlyNameW); } } free(namePCSC); } else { if (cbAttrLen == SCARD_AUTOALLOCATE) { *(CHAR**)pbAttr = namePCSC; WINPR_ASSERT(length <= UINT32_MAX); *pcbAttrLen = (UINT32)length; PCSC_AddMemoryBlock(hContext, namePCSC); } else { if ((length + 1) > cbAttrLen) status = SCARD_E_INSUFFICIENT_BUFFER; else { CopyMemory(pbAttr, namePCSC, length + 1); WINPR_ASSERT(length <= UINT32_MAX); *pcbAttrLen = (UINT32)length; } free(namePCSC); } } return status; } static LONG WINAPI PCSC_SCardGetAttrib(SCARDHANDLE hCard, DWORD dwAttrId, LPBYTE pbAttr, LPDWORD pcbAttrLen) { DWORD cbAttrLen = 0; SCARDCONTEXT hContext = 0; BOOL pcbAttrLenAlloc = FALSE; LONG status = SCARD_S_SUCCESS; if (NULL == pcbAttrLen) return SCARD_E_INVALID_PARAMETER; cbAttrLen = *pcbAttrLen; if (*pcbAttrLen == SCARD_AUTOALLOCATE) { if (NULL == pbAttr) return SCARD_E_INVALID_PARAMETER; pcbAttrLenAlloc = TRUE; *(BYTE**)pbAttr = NULL; } else { /** * pcsc-lite returns SCARD_E_INSUFFICIENT_BUFFER if the given * buffer size is larger than PCSC_MAX_BUFFER_SIZE (264) */ if (*pcbAttrLen > PCSC_MAX_BUFFER_SIZE) *pcbAttrLen = PCSC_MAX_BUFFER_SIZE; } hContext = PCSC_GetCardContextFromHandle(hCard); if (!hContext) return SCARD_E_INVALID_HANDLE; if ((dwAttrId == SCARD_ATTR_DEVICE_FRIENDLY_NAME_A) || (dwAttrId == SCARD_ATTR_DEVICE_FRIENDLY_NAME_W)) { status = PCSC_SCardGetAttrib_FriendlyName(hCard, dwAttrId, pbAttr, pcbAttrLen); return status; } status = PCSC_SCardGetAttrib_Internal(hCard, dwAttrId, pbAttr, pcbAttrLen); if (status == SCARD_S_SUCCESS) { if (dwAttrId == SCARD_ATTR_VENDOR_NAME) { if (pbAttr) { const char* vendorName = NULL; /** * pcsc-lite adds a null terminator to the vendor name, * while WinSCard doesn't. Strip the null terminator. */ if (pcbAttrLenAlloc) vendorName = (char*)*(BYTE**)pbAttr; else vendorName = (char*)pbAttr; if (vendorName) { size_t len = strnlen(vendorName, *pcbAttrLen); WINPR_ASSERT(len <= UINT32_MAX); *pcbAttrLen = (DWORD)len; } else *pcbAttrLen = 0; } } } else { if (dwAttrId == SCARD_ATTR_CURRENT_PROTOCOL_TYPE) { if (!pcbAttrLenAlloc) { PCSC_DWORD dwState = 0; PCSC_DWORD cbAtrLen = 0; PCSC_DWORD dwProtocol = 0; PCSC_DWORD cchReaderLen = 0; status = (LONG)g_PCSC.pfnSCardStatus(hCard, NULL, &cchReaderLen, &dwState, &dwProtocol, NULL, &cbAtrLen); if (status == SCARD_S_SUCCESS) { if (cbAttrLen < sizeof(DWORD)) return SCARD_E_INSUFFICIENT_BUFFER; *(DWORD*)pbAttr = PCSC_ConvertProtocolsToWinSCard(dwProtocol); *pcbAttrLen = sizeof(DWORD); } } } else if (dwAttrId == SCARD_ATTR_CHANNEL_ID) { if (!pcbAttrLenAlloc) { UINT32 channelType = 0x20; /* USB */ UINT32 channelNumber = 0; if (cbAttrLen < sizeof(DWORD)) return SCARD_E_INSUFFICIENT_BUFFER; status = SCARD_S_SUCCESS; *(DWORD*)pbAttr = (channelType << 16u) | channelNumber; *pcbAttrLen = sizeof(DWORD); } } else if (dwAttrId == SCARD_ATTR_VENDOR_IFD_TYPE) { } else if (dwAttrId == SCARD_ATTR_DEFAULT_CLK) { } else if (dwAttrId == SCARD_ATTR_DEFAULT_DATA_RATE) { } else if (dwAttrId == SCARD_ATTR_MAX_CLK) { } else if (dwAttrId == SCARD_ATTR_MAX_DATA_RATE) { } else if (dwAttrId == SCARD_ATTR_MAX_IFSD) { } else if (dwAttrId == SCARD_ATTR_CHARACTERISTICS) { } else if (dwAttrId == SCARD_ATTR_DEVICE_SYSTEM_NAME_A) { } else if (dwAttrId == SCARD_ATTR_DEVICE_UNIT) { } else if (dwAttrId == SCARD_ATTR_POWER_MGMT_SUPPORT) { } else if (dwAttrId == SCARD_ATTR_CURRENT_CLK) { } else if (dwAttrId == SCARD_ATTR_CURRENT_F) { } else if (dwAttrId == SCARD_ATTR_CURRENT_D) { } else if (dwAttrId == SCARD_ATTR_CURRENT_N) { } else if (dwAttrId == SCARD_ATTR_CURRENT_CWT) { } else if (dwAttrId == SCARD_ATTR_CURRENT_BWT) { } else if (dwAttrId == SCARD_ATTR_CURRENT_IFSC) { } else if (dwAttrId == SCARD_ATTR_CURRENT_EBC_ENCODING) { } else if (dwAttrId == SCARD_ATTR_CURRENT_IFSD) { } else if (dwAttrId == SCARD_ATTR_ICC_TYPE_PER_ATR) { } } return status; } static LONG WINAPI PCSC_SCardSetAttrib(SCARDHANDLE hCard, DWORD dwAttrId, LPCBYTE pbAttr, DWORD cbAttrLen) { PCSC_LONG status = SCARD_S_SUCCESS; PCSC_SCARDHANDLE* pCard = NULL; PCSC_DWORD pcsc_dwAttrId = (PCSC_DWORD)dwAttrId; PCSC_DWORD pcsc_cbAttrLen = (PCSC_DWORD)cbAttrLen; if (!g_PCSC.pfnSCardSetAttrib) return PCSC_SCard_LogError("g_PCSC.pfnSCardSetAttrib"); pCard = PCSC_GetCardHandleData(hCard); if (!pCard) return SCARD_E_INVALID_VALUE; PCSC_WaitForCardAccess(0, hCard, pCard->shared); status = g_PCSC.pfnSCardSetAttrib(hCard, pcsc_dwAttrId, pbAttr, pcsc_cbAttrLen); return PCSC_MapErrorCodeToWinSCard(status); } static LONG WINAPI PCSC_SCardUIDlgSelectCardA(LPOPENCARDNAMEA_EX pDlgStruc) { WINPR_UNUSED(pDlgStruc); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardUIDlgSelectCardW(LPOPENCARDNAMEW_EX pDlgStruc) { WINPR_UNUSED(pDlgStruc); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_GetOpenCardNameA(LPOPENCARDNAMEA pDlgStruc) { WINPR_UNUSED(pDlgStruc); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_GetOpenCardNameW(LPOPENCARDNAMEW pDlgStruc) { WINPR_UNUSED(pDlgStruc); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardDlgExtendedError(void) { return SCARD_E_UNSUPPORTED_FEATURE; } static char* card_id_and_name_a(const UUID* CardIdentifier, LPCSTR LookupName) { WINPR_ASSERT(CardIdentifier); WINPR_ASSERT(LookupName); size_t len = strlen(LookupName) + 34; char* id = malloc(len); if (!id) return NULL; snprintf(id, len, "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X\\%s", CardIdentifier->Data1, CardIdentifier->Data2, CardIdentifier->Data3, CardIdentifier->Data4[0], CardIdentifier->Data4[1], CardIdentifier->Data4[2], CardIdentifier->Data4[3], CardIdentifier->Data4[4], CardIdentifier->Data4[5], CardIdentifier->Data4[6], CardIdentifier->Data4[7], LookupName); return id; } static char* card_id_and_name_w(const UUID* CardIdentifier, LPCWSTR LookupName) { char* res = NULL; char* tmp = ConvertWCharToUtf8Alloc(LookupName, NULL); if (!tmp) return NULL; res = card_id_and_name_a(CardIdentifier, tmp); free(tmp); return res; } static LONG WINAPI PCSC_SCardReadCacheA(SCARDCONTEXT hContext, UUID* CardIdentifier, DWORD FreshnessCounter, LPSTR LookupName, PBYTE Data, DWORD* DataLen) { PCSC_CACHE_ITEM* data = NULL; PCSC_SCARDCONTEXT* ctx = PCSC_GetCardContextData(hContext); if (!ctx) return SCARD_E_INVALID_HANDLE; char* id = card_id_and_name_a(CardIdentifier, LookupName); data = HashTable_GetItemValue(ctx->cache, id); free(id); if (!data) { *DataLen = 0; return SCARD_W_CACHE_ITEM_NOT_FOUND; } if (FreshnessCounter != data->freshness) { *DataLen = 0; return SCARD_W_CACHE_ITEM_STALE; } if (*DataLen == SCARD_AUTOALLOCATE) { BYTE* mem = calloc(1, data->len); if (!mem) return SCARD_E_NO_MEMORY; if (!PCSC_AddMemoryBlock(hContext, mem)) { free(mem); return SCARD_E_NO_MEMORY; } memcpy(mem, data->data, data->len); *(BYTE**)Data = mem; } else memcpy(Data, data->data, data->len); *DataLen = data->len; return SCARD_S_SUCCESS; } static LONG WINAPI PCSC_SCardReadCacheW(SCARDCONTEXT hContext, UUID* CardIdentifier, DWORD FreshnessCounter, LPWSTR LookupName, PBYTE Data, DWORD* DataLen) { PCSC_CACHE_ITEM* data = NULL; PCSC_SCARDCONTEXT* ctx = PCSC_GetCardContextData(hContext); if (!ctx) return SCARD_E_INVALID_HANDLE; char* id = card_id_and_name_w(CardIdentifier, LookupName); data = HashTable_GetItemValue(ctx->cache, id); free(id); if (!data) { *DataLen = 0; return SCARD_W_CACHE_ITEM_NOT_FOUND; } if (FreshnessCounter != data->freshness) { *DataLen = 0; return SCARD_W_CACHE_ITEM_STALE; } if (*DataLen == SCARD_AUTOALLOCATE) { BYTE* mem = calloc(1, data->len); if (!mem) return SCARD_E_NO_MEMORY; if (!PCSC_AddMemoryBlock(hContext, mem)) { free(mem); return SCARD_E_NO_MEMORY; } memcpy(mem, data->data, data->len); *(BYTE**)Data = mem; } else memcpy(Data, data->data, data->len); *DataLen = data->len; return SCARD_S_SUCCESS; } static LONG WINAPI PCSC_SCardWriteCacheA(SCARDCONTEXT hContext, UUID* CardIdentifier, DWORD FreshnessCounter, LPSTR LookupName, PBYTE Data, DWORD DataLen) { PCSC_CACHE_ITEM* data = NULL; PCSC_SCARDCONTEXT* ctx = PCSC_GetCardContextData(hContext); char* id = NULL; if (!ctx) return SCARD_E_FILE_NOT_FOUND; id = card_id_and_name_a(CardIdentifier, LookupName); if (!id) return SCARD_E_NO_MEMORY; data = malloc(sizeof(PCSC_CACHE_ITEM)); if (!data) { free(id); return SCARD_E_NO_MEMORY; } data->data = calloc(DataLen, 1); if (!data->data) { free(id); free(data); return SCARD_E_NO_MEMORY; } data->len = DataLen; data->freshness = FreshnessCounter; memcpy(data->data, Data, data->len); HashTable_Remove(ctx->cache, id); const BOOL rc = HashTable_Insert(ctx->cache, id, data); free(id); if (!rc) { pcsc_cache_item_free(data); return SCARD_E_NO_MEMORY; } // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert owns data return SCARD_S_SUCCESS; } static LONG WINAPI PCSC_SCardWriteCacheW(SCARDCONTEXT hContext, UUID* CardIdentifier, DWORD FreshnessCounter, LPWSTR LookupName, PBYTE Data, DWORD DataLen) { PCSC_CACHE_ITEM* data = NULL; PCSC_SCARDCONTEXT* ctx = PCSC_GetCardContextData(hContext); char* id = NULL; if (!ctx) return SCARD_E_FILE_NOT_FOUND; id = card_id_and_name_w(CardIdentifier, LookupName); if (!id) return SCARD_E_NO_MEMORY; data = malloc(sizeof(PCSC_CACHE_ITEM)); if (!data) { free(id); return SCARD_E_NO_MEMORY; } data->data = malloc(DataLen); if (!data->data) { free(id); free(data); return SCARD_E_NO_MEMORY; } data->len = DataLen; data->freshness = FreshnessCounter; memcpy(data->data, Data, data->len); HashTable_Remove(ctx->cache, id); const BOOL rc = HashTable_Insert(ctx->cache, id, data); free(id); if (!rc) { pcsc_cache_item_free(data); return SCARD_E_NO_MEMORY; } // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert owns data return SCARD_S_SUCCESS; } static LONG WINAPI PCSC_SCardGetReaderIconA(SCARDCONTEXT hContext, LPCSTR szReaderName, LPBYTE pbIcon, LPDWORD pcbIcon) { WINPR_UNUSED(hContext); WINPR_UNUSED(szReaderName); WINPR_UNUSED(pbIcon); WINPR_UNUSED(pcbIcon); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardGetReaderIconW(SCARDCONTEXT hContext, LPCWSTR szReaderName, LPBYTE pbIcon, LPDWORD pcbIcon) { WINPR_UNUSED(hContext); WINPR_UNUSED(szReaderName); WINPR_UNUSED(pbIcon); WINPR_UNUSED(pcbIcon); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardGetDeviceTypeIdA(SCARDCONTEXT hContext, LPCSTR szReaderName, LPDWORD pdwDeviceTypeId) { WINPR_UNUSED(hContext); WINPR_UNUSED(szReaderName); WINPR_UNUSED(pdwDeviceTypeId); if (pdwDeviceTypeId) *pdwDeviceTypeId = SCARD_READER_TYPE_USB; return SCARD_S_SUCCESS; } static LONG WINAPI PCSC_SCardGetDeviceTypeIdW(SCARDCONTEXT hContext, LPCWSTR szReaderName, LPDWORD pdwDeviceTypeId) { WINPR_UNUSED(hContext); WINPR_UNUSED(szReaderName); if (pdwDeviceTypeId) *pdwDeviceTypeId = SCARD_READER_TYPE_USB; return SCARD_S_SUCCESS; } static LONG WINAPI PCSC_SCardGetReaderDeviceInstanceIdA(SCARDCONTEXT hContext, LPCSTR szReaderName, LPSTR szDeviceInstanceId, LPDWORD pcchDeviceInstanceId) { WINPR_UNUSED(hContext); WINPR_UNUSED(szReaderName); WINPR_UNUSED(szDeviceInstanceId); WINPR_UNUSED(pcchDeviceInstanceId); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardGetReaderDeviceInstanceIdW(SCARDCONTEXT hContext, LPCWSTR szReaderName, LPWSTR szDeviceInstanceId, LPDWORD pcchDeviceInstanceId) { WINPR_UNUSED(hContext); WINPR_UNUSED(szReaderName); WINPR_UNUSED(szDeviceInstanceId); WINPR_UNUSED(pcchDeviceInstanceId); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardListReadersWithDeviceInstanceIdA(SCARDCONTEXT hContext, LPCSTR szDeviceInstanceId, LPSTR mszReaders, LPDWORD pcchReaders) { WINPR_UNUSED(hContext); WINPR_UNUSED(szDeviceInstanceId); WINPR_UNUSED(mszReaders); WINPR_UNUSED(pcchReaders); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardListReadersWithDeviceInstanceIdW(SCARDCONTEXT hContext, LPCWSTR szDeviceInstanceId, LPWSTR mszReaders, LPDWORD pcchReaders) { WINPR_UNUSED(hContext); WINPR_UNUSED(szDeviceInstanceId); WINPR_UNUSED(mszReaders); WINPR_UNUSED(pcchReaders); return SCARD_E_UNSUPPORTED_FEATURE; } static LONG WINAPI PCSC_SCardAudit(SCARDCONTEXT hContext, DWORD dwEvent) { WINPR_UNUSED(hContext); WINPR_UNUSED(dwEvent); return SCARD_E_UNSUPPORTED_FEATURE; } #ifdef __MACOSX__ unsigned int determineMacOSXVersion(void) { int mib[2]; size_t len = 0; char* kernelVersion = NULL; char* tok = NULL; unsigned int version = 0; long majorVersion = 0; long minorVersion = 0; long patchVersion = 0; int count = 0; char* context = NULL; mib[0] = CTL_KERN; mib[1] = KERN_OSRELEASE; if (sysctl(mib, 2, NULL, &len, NULL, 0) != 0) return 0; kernelVersion = calloc(len, sizeof(char)); if (!kernelVersion) return 0; if (sysctl(mib, 2, kernelVersion, &len, NULL, 0) != 0) { free(kernelVersion); return 0; } tok = strtok_s(kernelVersion, ".", &context); errno = 0; while (tok) { switch (count) { case 0: majorVersion = strtol(tok, NULL, 0); if (errno != 0) goto fail; break; case 1: minorVersion = strtol(tok, NULL, 0); if (errno != 0) goto fail; break; case 2: patchVersion = strtol(tok, NULL, 0); if (errno != 0) goto fail; break; } tok = strtok_s(NULL, ".", &context); count++; } /** * Source : http://en.wikipedia.org/wiki/Darwin_(operating_system) **/ if (majorVersion < 5) { if (minorVersion < 4) version = 0x10000000; else version = 0x10010000; } else { switch (majorVersion) { case 5: version = 0x10010000; break; case 6: version = 0x10020000; break; case 7: version = 0x10030000; break; case 8: version = 0x10040000; break; case 9: version = 0x10050000; break; case 10: version = 0x10060000; break; case 11: version = 0x10070000; break; case 12: version = 0x10080000; break; case 13: version = 0x10090000; break; default: version = 0x10100000; break; } version |= (minorVersion << 8) | (patchVersion); } fail: free(kernelVersion); return version; } #endif static const SCardApiFunctionTable PCSC_SCardApiFunctionTable = { 0, /* dwVersion */ 0, /* dwFlags */ PCSC_SCardEstablishContext, /* SCardEstablishContext */ PCSC_SCardReleaseContext, /* SCardReleaseContext */ PCSC_SCardIsValidContext, /* SCardIsValidContext */ PCSC_SCardListReaderGroupsA, /* SCardListReaderGroupsA */ PCSC_SCardListReaderGroupsW, /* SCardListReaderGroupsW */ PCSC_SCardListReadersA, /* SCardListReadersA */ PCSC_SCardListReadersW, /* SCardListReadersW */ PCSC_SCardListCardsA, /* SCardListCardsA */ PCSC_SCardListCardsW, /* SCardListCardsW */ PCSC_SCardListInterfacesA, /* SCardListInterfacesA */ PCSC_SCardListInterfacesW, /* SCardListInterfacesW */ PCSC_SCardGetProviderIdA, /* SCardGetProviderIdA */ PCSC_SCardGetProviderIdW, /* SCardGetProviderIdW */ PCSC_SCardGetCardTypeProviderNameA, /* SCardGetCardTypeProviderNameA */ PCSC_SCardGetCardTypeProviderNameW, /* SCardGetCardTypeProviderNameW */ PCSC_SCardIntroduceReaderGroupA, /* SCardIntroduceReaderGroupA */ PCSC_SCardIntroduceReaderGroupW, /* SCardIntroduceReaderGroupW */ PCSC_SCardForgetReaderGroupA, /* SCardForgetReaderGroupA */ PCSC_SCardForgetReaderGroupW, /* SCardForgetReaderGroupW */ PCSC_SCardIntroduceReaderA, /* SCardIntroduceReaderA */ PCSC_SCardIntroduceReaderW, /* SCardIntroduceReaderW */ PCSC_SCardForgetReaderA, /* SCardForgetReaderA */ PCSC_SCardForgetReaderW, /* SCardForgetReaderW */ PCSC_SCardAddReaderToGroupA, /* SCardAddReaderToGroupA */ PCSC_SCardAddReaderToGroupW, /* SCardAddReaderToGroupW */ PCSC_SCardRemoveReaderFromGroupA, /* SCardRemoveReaderFromGroupA */ PCSC_SCardRemoveReaderFromGroupW, /* SCardRemoveReaderFromGroupW */ PCSC_SCardIntroduceCardTypeA, /* SCardIntroduceCardTypeA */ PCSC_SCardIntroduceCardTypeW, /* SCardIntroduceCardTypeW */ PCSC_SCardSetCardTypeProviderNameA, /* SCardSetCardTypeProviderNameA */ PCSC_SCardSetCardTypeProviderNameW, /* SCardSetCardTypeProviderNameW */ PCSC_SCardForgetCardTypeA, /* SCardForgetCardTypeA */ PCSC_SCardForgetCardTypeW, /* SCardForgetCardTypeW */ PCSC_SCardFreeMemory, /* SCardFreeMemory */ PCSC_SCardAccessStartedEvent, /* SCardAccessStartedEvent */ PCSC_SCardReleaseStartedEvent, /* SCardReleaseStartedEvent */ PCSC_SCardLocateCardsA, /* SCardLocateCardsA */ PCSC_SCardLocateCardsW, /* SCardLocateCardsW */ PCSC_SCardLocateCardsByATRA, /* SCardLocateCardsByATRA */ PCSC_SCardLocateCardsByATRW, /* SCardLocateCardsByATRW */ PCSC_SCardGetStatusChangeA, /* SCardGetStatusChangeA */ PCSC_SCardGetStatusChangeW, /* SCardGetStatusChangeW */ PCSC_SCardCancel, /* SCardCancel */ PCSC_SCardConnectA, /* SCardConnectA */ PCSC_SCardConnectW, /* SCardConnectW */ PCSC_SCardReconnect, /* SCardReconnect */ PCSC_SCardDisconnect, /* SCardDisconnect */ PCSC_SCardBeginTransaction, /* SCardBeginTransaction */ PCSC_SCardEndTransaction, /* SCardEndTransaction */ PCSC_SCardCancelTransaction, /* SCardCancelTransaction */ PCSC_SCardState, /* SCardState */ PCSC_SCardStatusA, /* SCardStatusA */ PCSC_SCardStatusW, /* SCardStatusW */ PCSC_SCardTransmit, /* SCardTransmit */ PCSC_SCardGetTransmitCount, /* SCardGetTransmitCount */ PCSC_SCardControl, /* SCardControl */ PCSC_SCardGetAttrib, /* SCardGetAttrib */ PCSC_SCardSetAttrib, /* SCardSetAttrib */ PCSC_SCardUIDlgSelectCardA, /* SCardUIDlgSelectCardA */ PCSC_SCardUIDlgSelectCardW, /* SCardUIDlgSelectCardW */ PCSC_GetOpenCardNameA, /* GetOpenCardNameA */ PCSC_GetOpenCardNameW, /* GetOpenCardNameW */ PCSC_SCardDlgExtendedError, /* SCardDlgExtendedError */ PCSC_SCardReadCacheA, /* SCardReadCacheA */ PCSC_SCardReadCacheW, /* SCardReadCacheW */ PCSC_SCardWriteCacheA, /* SCardWriteCacheA */ PCSC_SCardWriteCacheW, /* SCardWriteCacheW */ PCSC_SCardGetReaderIconA, /* SCardGetReaderIconA */ PCSC_SCardGetReaderIconW, /* SCardGetReaderIconW */ PCSC_SCardGetDeviceTypeIdA, /* SCardGetDeviceTypeIdA */ PCSC_SCardGetDeviceTypeIdW, /* SCardGetDeviceTypeIdW */ PCSC_SCardGetReaderDeviceInstanceIdA, /* SCardGetReaderDeviceInstanceIdA */ PCSC_SCardGetReaderDeviceInstanceIdW, /* SCardGetReaderDeviceInstanceIdW */ PCSC_SCardListReadersWithDeviceInstanceIdA, /* SCardListReadersWithDeviceInstanceIdA */ PCSC_SCardListReadersWithDeviceInstanceIdW, /* SCardListReadersWithDeviceInstanceIdW */ PCSC_SCardAudit /* SCardAudit */ }; const SCardApiFunctionTable* PCSC_GetSCardApiFunctionTable(void) { return &PCSC_SCardApiFunctionTable; } int PCSC_InitializeSCardApi(void) { /* Disable pcsc-lite's (poor) blocking so we can handle it ourselves */ SetEnvironmentVariableA("PCSCLITE_NO_BLOCKING", "1"); #ifdef __MACOSX__ g_PCSCModule = LoadLibraryX("/System/Library/Frameworks/PCSC.framework/PCSC"); OSXVersion = determineMacOSXVersion(); if (OSXVersion == 0) return -1; #else g_PCSCModule = LoadLibraryA("libpcsclite.so.1"); if (!g_PCSCModule) g_PCSCModule = LoadLibraryA("libpcsclite.so"); #endif if (!g_PCSCModule) return -1; /* symbols defined in winpr/smartcard.h, might pose an issue with the GetProcAddress macro * below. therefore undefine them here */ #undef SCardListReaderGroups #undef SCardListReaders #undef SCardListCards #undef SCardListInterfaces #undef SCardGetProviderId #undef SCardGetCardTypeProviderName #undef SCardIntroduceReaderGroup #undef SCardForgetReaderGroup #undef SCardIntroduceReader #undef SCardForgetReader #undef SCardAddReaderToGroup #undef SCardRemoveReaderFromGroup #undef SCardIntroduceCardType #undef SCardSetCardTypeProviderName #undef SCardForgetCardType #undef SCardLocateCards #undef SCardLocateCardsByATR #undef SCardGetStatusChange #undef SCardConnect #undef SCardStatus #undef SCardUIDlgSelectCard #undef GetOpenCardName #undef SCardReadCache #undef SCardWriteCache #undef SCardGetReaderIcon #undef SCardGetDeviceTypeId #undef SCardGetReaderDeviceInstanceId #undef SCardListReadersWithDeviceInstanceId WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardEstablishContext); WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardReleaseContext); WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardIsValidContext); WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardConnect); WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardReconnect); WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardDisconnect); WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardBeginTransaction); WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardEndTransaction); WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardStatus); WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardGetStatusChange); #ifdef __MACOSX__ if (OSXVersion >= 0x10050600) { WINSCARD_LOAD_PROC_EX(g_PCSCModule, g_PCSC, SCardControl, SCardControl132); } else { WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardControl); } #else WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardControl); #endif WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardTransmit); WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardListReaderGroups); WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardListReaders); WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardCancel); WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardGetAttrib); WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardSetAttrib); g_PCSC.pfnSCardFreeMemory = NULL; #ifndef __APPLE__ WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardFreeMemory); #endif if (g_PCSC.pfnSCardFreeMemory) g_SCardAutoAllocate = TRUE; #ifdef DISABLE_PCSC_SCARD_AUTOALLOCATE g_PCSC.pfnSCardFreeMemory = NULL; g_SCardAutoAllocate = FALSE; #endif #ifdef __APPLE__ g_PnP_Notification = FALSE; #endif return 1; } #endif