Merge pull request #2793 from rkondratenko/pcsclite-transactions

Fix for transactions support for Smart Cards
This commit is contained in:
Marc-André Moreau 2015-07-28 08:44:58 -04:00
commit 06c3f2fca4
2 changed files with 73 additions and 122 deletions

View File

@ -110,7 +110,6 @@ void smartcard_context_free(SMARTCARD_CONTEXT* pContext)
/* cancel blocking calls like SCardGetStatusChange */
SCardCancel(pContext->hContext);
if (MessageQueue_PostQuit(pContext->IrpQueue, 0))
WaitForSingleObject(pContext->thread, INFINITE);
CloseHandle(pContext->thread);
@ -120,64 +119,21 @@ void smartcard_context_free(SMARTCARD_CONTEXT* pContext)
free(pContext);
}
static void smartcard_free(DEVICE* device)
{
SMARTCARD_DEVICE* smartcard = (SMARTCARD_DEVICE*) device;
if (smartcard->IrpQueue)
{
if (MessageQueue_PostQuit(smartcard->IrpQueue, 0))
WaitForSingleObject(smartcard->thread, INFINITE);
MessageQueue_Free(smartcard->IrpQueue);
smartcard->IrpQueue = NULL;
CloseHandle(smartcard->thread);
smartcard->thread = NULL;
}
if (smartcard->device.data)
{
Stream_Free(smartcard->device.data, TRUE);
smartcard->device.data = NULL;
}
ListDictionary_Free(smartcard->rgSCardContextList);
ListDictionary_Free(smartcard->rgOutstandingMessages);
Queue_Free(smartcard->CompletedIrpQueue);
if (smartcard->StartedEvent)
{
SCardReleaseStartedEvent();
smartcard->StartedEvent = NULL;
}
free(device);
}
/**
* Initialization occurs when the protocol server sends a device announce message.
* At that time, we need to cancel all outstanding IRPs.
*/
static void smartcard_init(DEVICE* device)
{
static void smartcard_release_all_contexts(SMARTCARD_DEVICE* smartcard) {
int index;
int keyCount;
ULONG_PTR* pKeys;
SCARDCONTEXT hContext;
SMARTCARD_CONTEXT* pContext;
SMARTCARD_DEVICE* smartcard = (SMARTCARD_DEVICE*) device;
/**
* On protocol termination, the following actions are performed:
* For each context in rgSCardContextList, SCardCancel is called causing all outstanding messages to be processed.
* After there are no more outstanding messages, SCardReleaseContext is called on each context and the context MUST
* be removed from rgSCardContextList.
* For each context in rgSCardContextList, SCardCancel is called causing all SCardGetStatusChange calls to be processed.
* After that, SCardReleaseContext is called on each context and the context MUST be removed from rgSCardContextList.
*/
/**
* Call SCardCancel on existing contexts, unblocking all outstanding IRPs.
* Call SCardCancel on existing contexts, unblocking all outstanding SCardGetStatusChange calls.
*/
if (ListDictionary_Count(smartcard->rgSCardContextList) > 0)
@ -194,7 +150,7 @@ static void smartcard_init(DEVICE* device)
hContext = pContext->hContext;
if (SCardIsValidContext(hContext))
if (SCardIsValidContext(hContext) == SCARD_S_SUCCESS)
{
SCardCancel(hContext);
}
@ -221,7 +177,7 @@ static void smartcard_init(DEVICE* device)
hContext = pContext->hContext;
if (SCardIsValidContext(hContext))
if (SCardIsValidContext(hContext) == SCARD_S_SUCCESS)
{
SCardReleaseContext(hContext);
}
@ -229,6 +185,64 @@ static void smartcard_init(DEVICE* device)
free(pKeys);
}
}
static void smartcard_free(DEVICE* device)
{
SMARTCARD_DEVICE* smartcard = (SMARTCARD_DEVICE*) device;
/**
* Calling smartcard_release_all_contexts to unblock all operations waiting for transactions
* to unlock.
*/
smartcard_release_all_contexts(smartcard);
/* Stopping all threads and cancelling all IRPs */
if (smartcard->IrpQueue)
{
if (MessageQueue_PostQuit(smartcard->IrpQueue, 0))
WaitForSingleObject(smartcard->thread, INFINITE);
MessageQueue_Free(smartcard->IrpQueue);
smartcard->IrpQueue = NULL;
CloseHandle(smartcard->thread);
smartcard->thread = NULL;
}
if (smartcard->device.data)
{
Stream_Free(smartcard->device.data, TRUE);
smartcard->device.data = NULL;
}
ListDictionary_Free(smartcard->rgSCardContextList);
ListDictionary_Free(smartcard->rgOutstandingMessages);
Queue_Free(smartcard->CompletedIrpQueue);
if (smartcard->StartedEvent)
{
SCardReleaseStartedEvent();
smartcard->StartedEvent = NULL;
}
free(device);
}
/**
* Initialization occurs when the protocol server sends a device announce message.
* At that time, we need to cancel all outstanding IRPs.
*/
static void smartcard_init(DEVICE* device)
{
SMARTCARD_DEVICE* smartcard = (SMARTCARD_DEVICE*) device;
smartcard_release_all_contexts(smartcard);
}
void smartcard_complete_irp(SMARTCARD_DEVICE* smartcard, IRP* irp)

View File

@ -127,7 +127,6 @@ struct _PCSC_SCARDHANDLE
{
BOOL shared;
SCARDCONTEXT hSharedContext;
SCARDCONTEXT hPrivateContext;
};
struct _PCSC_READER
@ -150,31 +149,7 @@ static BOOL g_PnP_Notification = TRUE;
static unsigned int OSXVersion = 0;
#endif
/**
* g_LockTransactions: enable pcsc-lite SCardBeginTransaction/SCardEndTransaction.
*
* After wasting months trying to fix and work around an appalling number of serious issues
* in both the pcsc-lite client library and the pcscd daemon, I decided to just give up on
* the transaction system. Using them inevitably leads to multiple SCardConnect calls deadlocking.
*
* It is not very clear how WinSCard transactions should lock: some logs on Windows show that is
* possible to call SCardBeginTransaction twice on the same SCARDHANDLE without the second call
* being blocked. Worse, in this specific case one corresponding SCardEndTransaction is missing.
*
* pcsc-lite apparently implements these "nested" transactions as well, because it allows the same
* SCARDHANDLE to be locked more than once with a counter. However, there must be a bug even in the
* latest pcsc-lite daemon as we still get deadlocked on SCardConnect calls when using those.
*
* Trying to disable nested transactions by letting pcsc-lite know about only one transaction level
* gives the same deadlocks on SCardConnect. In other words, there are serious deadlock issues in
* pcsc-lite even when disabling nested transactions.
*
* Transactions are simply too much of a pain to support properly without deadlocking the entire
* smartcard subsystem. In practice, there is not much of a difference if locking occurs or not.
* We could revisit transactions later on based on the demand, but for now we just want things to work.
*/
static BOOL g_LockTransactions = FALSE;
static wArrayList* g_Readers = NULL;
static wListDictionary* g_CardHandles = NULL;
@ -439,7 +414,7 @@ SCARDCONTEXT PCSC_GetCardContextFromHandle(SCARDHANDLE hCard)
if (!pCard)
return 0;
return pCard->hPrivateContext;
return pCard->hSharedContext;
}
BOOL PCSC_WaitForCardAccess(SCARDCONTEXT hContext, SCARDHANDLE hCard, BOOL shared)
@ -553,7 +528,7 @@ BOOL PCSC_ReleaseCardAccess(SCARDCONTEXT hContext, SCARDHANDLE hCard)
return TRUE;
}
PCSC_SCARDHANDLE* PCSC_ConnectCardHandle(SCARDCONTEXT hSharedContext, SCARDCONTEXT hPrivateContext, SCARDHANDLE hCard)
PCSC_SCARDHANDLE* PCSC_ConnectCardHandle(SCARDCONTEXT hSharedContext, SCARDHANDLE hCard)
{
PCSC_SCARDHANDLE* pCard;
PCSC_SCARDCONTEXT* pContext;
@ -570,7 +545,6 @@ PCSC_SCARDHANDLE* PCSC_ConnectCardHandle(SCARDCONTEXT hSharedContext, SCARDCONTE
return NULL;
pCard->hSharedContext = hSharedContext;
pCard->hPrivateContext = hPrivateContext;
if (!g_CardHandles)
{
@ -601,7 +575,6 @@ void PCSC_DisconnectCardHandle(SCARDHANDLE hCard)
return;
pContext = PCSC_GetCardContextData(pCard->hSharedContext);
PCSC_SCardReleaseContext_Internal(pCard->hPrivateContext);
free(pCard);
if (!g_CardHandles)
@ -743,7 +716,7 @@ char* PCSC_ConvertReaderNameToWinSCard(const char* name)
if (!name)
return NULL;
memset(tokens, 0, sizeof(tokens));
length = strlen(name);
if (length < 10)
@ -1712,7 +1685,6 @@ WINSCARDAPI LONG WINAPI PCSC_SCardConnect_Internal(SCARDCONTEXT hContext,
char* szReaderPCSC;
LONG status = SCARD_S_SUCCESS;
PCSC_SCARDHANDLE* pCard = NULL;
SCARDCONTEXT hPrivateContext = 0;
PCSC_DWORD pcsc_dwShareMode = (PCSC_DWORD) dwShareMode;
PCSC_DWORD pcsc_dwPreferredProtocols = 0;
PCSC_DWORD pcsc_dwActiveProtocol = 0;
@ -1724,11 +1696,6 @@ WINSCARDAPI LONG WINAPI PCSC_SCardConnect_Internal(SCARDCONTEXT hContext,
access = PCSC_WaitForCardAccess(hContext, 0, shared);
status = PCSC_SCardEstablishContext_Internal(SCARD_SCOPE_SYSTEM, NULL, NULL, &hPrivateContext);
if (status != SCARD_S_SUCCESS)
return status;
szReaderPCSC = PCSC_GetReaderNameFromAlias((char*) szReader);
if (!szReaderPCSC)
@ -1744,23 +1711,19 @@ WINSCARDAPI LONG WINAPI PCSC_SCardConnect_Internal(SCARDCONTEXT hContext,
else
pcsc_dwPreferredProtocols = (PCSC_DWORD) PCSC_ConvertProtocolsFromWinSCard(dwPreferredProtocols);
status = (LONG) g_PCSC.pfnSCardConnect(hPrivateContext, szReaderPCSC,
status = (LONG) g_PCSC.pfnSCardConnect(hContext, szReaderPCSC,
pcsc_dwShareMode, pcsc_dwPreferredProtocols, phCard, &pcsc_dwActiveProtocol);
status = PCSC_MapErrorCodeToWinSCard(status);
if (status == SCARD_S_SUCCESS)
{
pCard = PCSC_ConnectCardHandle(hContext, hPrivateContext, *phCard);
pCard = PCSC_ConnectCardHandle(hContext, *phCard);
*pdwActiveProtocol = PCSC_ConvertProtocolsToWinSCard((DWORD) pcsc_dwActiveProtocol);
pCard->shared = shared;
PCSC_WaitForCardAccess(hContext, pCard->hSharedContext, shared);
}
else
{
PCSC_SCardReleaseContext(hPrivateContext);
}
return status;
}
@ -1876,11 +1839,8 @@ WINSCARDAPI LONG WINAPI PCSC_SCardBeginTransaction(SCARDHANDLE hCard)
if (pContext->isTransactionLocked)
return SCARD_S_SUCCESS; /* disable nested transactions */
if (g_LockTransactions)
{
status = (LONG) g_PCSC.pfnSCardBeginTransaction(hCard);
status = PCSC_MapErrorCodeToWinSCard(status);
}
status = (LONG) g_PCSC.pfnSCardBeginTransaction(hCard);
status = PCSC_MapErrorCodeToWinSCard(status);
pContext->isTransactionLocked = TRUE;
return status;
@ -1911,11 +1871,8 @@ WINSCARDAPI LONG WINAPI PCSC_SCardEndTransaction(SCARDHANDLE hCard, DWORD dwDisp
if (!pContext->isTransactionLocked)
return SCARD_S_SUCCESS; /* disable nested transactions */
if (g_LockTransactions)
{
status = (LONG) g_PCSC.pfnSCardEndTransaction(hCard, pcsc_dwDisposition);
status = PCSC_MapErrorCodeToWinSCard(status);
}
status = (LONG) g_PCSC.pfnSCardEndTransaction(hCard, pcsc_dwDisposition);
status = PCSC_MapErrorCodeToWinSCard(status);
pContext->isTransactionLocked = FALSE;
@ -3005,26 +2962,6 @@ PSCardApiFunctionTable PCSC_GetSCardApiFunctionTable(void)
int PCSC_InitializeSCardApi(void)
{
DWORD nSize;
char* env = NULL;
nSize = GetEnvironmentVariableA("WINPR_WINSCARD_LOCK_TRANSACTIONS", NULL, 0);
if (nSize)
{
env = (LPSTR) malloc(nSize);
if (!env)
return -1;
nSize = GetEnvironmentVariableA("WINPR_WINSCARD_LOCK_TRANSACTIONS", env, nSize);
if (strcmp(env, "1") == 0)
g_LockTransactions = TRUE;
else if (strcmp(env, "0") == 0)
g_LockTransactions = FALSE;
free(env);
}
/* Disable pcsc-lite's (poor) blocking so we can handle it ourselves */
SetEnvironmentVariableA("PCSCLITE_NO_BLOCKING", "1");