Skip to content

Commit

Permalink
Merge pull request #2793 from rkondratenko/pcsclite-transactions
Browse files Browse the repository at this point in the history
Fix for transactions support for Smart Cards
  • Loading branch information
awakecoding committed Jul 28, 2015
2 parents a637cdd + 3f81ff8 commit 06c3f2f
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 122 deletions.
114 changes: 64 additions & 50 deletions channels/smartcard/client/smartcard_main.c
Expand Up @@ -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);
Expand All @@ -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)
Expand All @@ -194,7 +150,7 @@ static void smartcard_init(DEVICE* device)

hContext = pContext->hContext;

if (SCardIsValidContext(hContext))
if (SCardIsValidContext(hContext) == SCARD_S_SUCCESS)
{
SCardCancel(hContext);
}
Expand All @@ -221,14 +177,72 @@ static void smartcard_init(DEVICE* device)

hContext = pContext->hContext;

if (SCardIsValidContext(hContext))
if (SCardIsValidContext(hContext) == SCARD_S_SUCCESS)
{
SCardReleaseContext(hContext);
}
}

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)
Expand Down
81 changes: 9 additions & 72 deletions winpr/libwinpr/smartcard/smartcard_pcsc.c
Expand Up @@ -127,7 +127,6 @@ struct _PCSC_SCARDHANDLE
{
BOOL shared;
SCARDCONTEXT hSharedContext;
SCARDCONTEXT hPrivateContext;
};

struct _PCSC_READER
Expand All @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -570,7 +545,6 @@ PCSC_SCARDHANDLE* PCSC_ConnectCardHandle(SCARDCONTEXT hSharedContext, SCARDCONTE
return NULL;

pCard->hSharedContext = hSharedContext;
pCard->hPrivateContext = hPrivateContext;

if (!g_CardHandles)
{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -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)
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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");

Expand Down

0 comments on commit 06c3f2f

Please sign in to comment.