|
|
#include <windows.h>
#include <wincrypt.h>
#pragma warning(push)
#pragma warning(disable:4201)
// Disable error C4201 in public header
// nonstandard extension used : nameless struct/union
#include <winscard.h>
#pragma warning(pop)
#include "basecsp.h"
#include "datacach.h"
#include "cardmod.h"
#include "debug.h"
//
// Debugging Macros
//
#define LOG_BEGIN_FUNCTION(x) \
{ DebugLog((DEB_TRACE_FINDCARD, "%s: Entering\n", #x)); } #define LOG_END_FUNCTION(x, y) \
{ DebugLog((DEB_TRACE_FINDCARD, "%s: Leaving, status: 0x%x\n", #x, y)); }
//
// Function: FindCardMakeCardHandles
//
// Purpose: Based on reader name information in the CARD_MATCH_DATA
// structure, build and return an SCARD_CONTEXT and SCARD_HANDLE
// for the target card.
//
// Note, the wszMatchedReader, dwShareMode, and dwPreferredProtocols fields
// of the CARD_MATCH_DATA structure must be initialized by the caller.
//
DWORD FindCardMakeCardHandles( IN PCARD_MATCH_DATA pCardMatchData, OUT SCARDCONTEXT *pSCardContext, OUT SCARDHANDLE *pSCardHandle) { DWORD dwSts = ERROR_SUCCESS; DWORD dwActiveProtocol = 0;
*pSCardContext = 0; *pSCardHandle = 0;
dwSts = SCardEstablishContext( SCARD_SCOPE_USER, NULL, NULL, pSCardContext);
if (ERROR_SUCCESS != dwSts) goto Ret;
dwSts = SCardConnectW( *pSCardContext, pCardMatchData->wszMatchedReader, pCardMatchData->dwShareMode, pCardMatchData->dwPreferredProtocols, pSCardHandle, &dwActiveProtocol);
Ret:
if (ERROR_SUCCESS != dwSts) { if (*pSCardHandle) { SCardDisconnect(*pSCardHandle, SCARD_LEAVE_CARD); *pSCardHandle = 0; }
if (*pSCardContext) { SCardReleaseContext(*pSCardContext); *pSCardContext = 0; } }
return dwSts; }
//
// Function: CardStateCacheFindAddItem
//
// Purpose: Lookup the card specified in the CARD_MATCH_DATA structure
// in the CSP's cache of CARD_STATE items. If the card is found
// in the cache, set the CARD_MATCH_DATA to point to the cached
// item.
//
// If the matching card is not found cached, add it to the cache.
//
// If this function Succeeds, the returned CARD_STATE structure
// has its own valid card context and card handle.
//
DWORD CardStateCacheFindAddItem( IN PCARD_MATCH_DATA pCardMatchData) { DWORD dwSts = ERROR_SUCCESS; BOOL fInCspCS = FALSE; DATA_BLOB dbKeys; DATA_BLOB dbCardState; PCARD_STATE pCardState = pCardMatchData->pCardState; BOOL fMakeNewCardHandle = FALSE; BOOL fInCardStateCS = FALSE;
memset(&dbKeys, 0, sizeof(dbKeys)); memset(&dbCardState, 0, sizeof(dbCardState));
DsysAssert(0 != pCardState->pCardData->hScard); DsysAssert(0 != pCardState->pCardData->hSCardCtx); DsysAssert(0 != pCardMatchData->hSCard); DsysAssert(0 != pCardMatchData->hSCardCtx);
// Now going to search the CSP_STATE for a cached entry for the current
// card. Grab the critical section protecting the cache.
dwSts = CspEnterCriticalSection( &pCardMatchData->pCspState->cs);
if (ERROR_SUCCESS != dwSts) goto Ret;
fInCspCS = TRUE;
// Lookup a cache entry via card serial number
dbKeys.pbData = (PBYTE) pCardMatchData->pCardState->wszSerialNumber; dbKeys.cbData = wcslen(pCardMatchData->pCardState->wszSerialNumber) * sizeof( pCardMatchData->pCardState->wszSerialNumber[0]);
dwSts = CacheGetItem( pCardMatchData->pCspState->hCache, &dbKeys, 1, &dbCardState);
if (ERROR_NOT_FOUND != dwSts && ERROR_SUCCESS != dwSts) // some unexpected error has occurred
goto Ret;
if (ERROR_NOT_FOUND == dwSts) { // This is a new card that has not been cached yet. Add it
// to the cache.
//
// Since we're not using an already-cached card, and we expect
// that this card object was just passed to us new, we know
// that we need to create a new card handle for it.
//
dbCardState.cbData = sizeof(CARD_STATE); dbCardState.pbData = (PBYTE) pCardState; dwSts = CacheAddItem( pCardMatchData->pCspState->hCache, &dbKeys, 1, &dbCardState);
dbCardState.pbData = NULL;
if (ERROR_SUCCESS != dwSts) goto Ret;
DsysAssert(TRUE == fInCspCS);
// We're done mucking with the cache list, so let it go.
CspLeaveCriticalSection(&pCardMatchData->pCspState->cs);
fInCspCS = FALSE; } else { DsysAssert(TRUE == fInCspCS);
// We're now done with the cache list in this case.
CspLeaveCriticalSection(&pCardMatchData->pCspState->cs);
fInCspCS = FALSE;
//
// The current card has already been cached. Free the local copy of
// the CSP_STATE struct and use the cached one instead.
//
// We can't hold the critsec on the current CardState, and keep its
// associated card's transaction, while waiting for the critsec of
// another CardState. That could deadlock.
//
if (pCardMatchData->fTransacted) { dwSts = CspEndTransaction(pCardState); if (ERROR_SUCCESS != dwSts) goto Ret;
pCardMatchData->fTransacted = FALSE; }
// Can't let the SCARDCONTEXT be released, because the scarddlg
// routines will still be expecting to use it.
pCardState->pCardData->hSCardCtx = 0;
DeleteCardState(pCardState); CspFreeH(pCardState); pCardMatchData->pCardState = (PCARD_STATE) dbCardState.pbData;
// Update the local pointer for convenience.
pCardState = (PCARD_STATE) dbCardState.pbData;
// Don't want this pointer freed out from under us since we're going
// to use this struct.
dbCardState.pbData = NULL;
//
// Now we need to verify that the handles cached with this card
// structure are still valid. Check now. If the handles
// aren't valid anymore, we'll reconnect to this card below.
//
// We want to ensure that each card object has it's own handles.
//
// Since the card state objects are shared, we need to take the
// critical section of the target object before we can examine its
// handles.
//
dwSts = CspEnterCriticalSection(&pCardState->cs);
if (ERROR_SUCCESS != dwSts) goto Ret; else fInCardStateCS = TRUE;
if (pCardMatchData->hSCardCtx == pCardState->pCardData->hSCardCtx) dwSts = ValidateCardHandle(pCardState, FALSE, NULL); else dwSts = ValidateCardHandle(pCardState, TRUE, NULL);
if (ERROR_SUCCESS != dwSts) fMakeNewCardHandle = TRUE; }
if (fMakeNewCardHandle) { if (FALSE == fInCardStateCS) { dwSts = CspEnterCriticalSection(&pCardState->cs);
if (ERROR_SUCCESS != dwSts) goto Ret; else fInCardStateCS = TRUE; }
dwSts = FindCardMakeCardHandles( pCardMatchData, &pCardState->pCardData->hSCardCtx, &pCardState->pCardData->hScard); }
Ret: if (fInCardStateCS) CspLeaveCriticalSection(&pCardState->cs); if (fInCspCS) CspLeaveCriticalSection( &pCardMatchData->pCspState->cs); if (dbCardState.pbData) CspFreeH(dbCardState.pbData);
return dwSts; }
//
// Function: GetCardSerialNumber
//
// Purpose: Attempt to read the serial number of the card
// specified in pCardMatchData.
//
// Assumes that a transaction is not currently held on the target
// card by the caller.
//
DWORD GetCardSerialNumber( IN OUT PCARD_MATCH_DATA pCardMatchData) { PFN_CARD_ACQUIRE_CONTEXT pfnCardAcquireContext = NULL; DWORD cch = 0; WCHAR rgwsz[MAX_PATH]; DWORD dwSts = ERROR_SUCCESS; PCARD_STATE pCardState = NULL; PCARD_DATA pCardData = NULL; DATA_BLOB DataBlob; DWORD dwState = 0; DWORD dwProtocol = 0; LPWSTR mszReaders = NULL;
LOG_BEGIN_FUNCTION(GetCardSerialNumber);
memset(&DataBlob, 0, sizeof(DataBlob));
//
// Determine how to talk to the card by looking
// for the appropriate Card Specific Module in the Calais
// database.
//
cch = sizeof(rgwsz) / sizeof(rgwsz[0]); dwSts = SCardGetCardTypeProviderNameW( pCardMatchData->hSCardCtx, pCardMatchData->wszMatchedCard, SCARD_PROVIDER_CARD_MODULE, rgwsz, &cch);
if (ERROR_SUCCESS != dwSts) goto Ret;
pCardState = (PCARD_STATE) CspAllocH(sizeof(CARD_STATE));
LOG_CHECK_ALLOC(pCardState);
pCardState->dwVersion = CARD_STATE_CURRENT_VERSION;
dwSts = InitializeCardState(pCardState);
if (ERROR_SUCCESS != dwSts) goto Ret;
pCardState->hCardModule = LoadLibraryW(rgwsz);
if (0 == pCardState->hCardModule) { dwSts = GetLastError(); goto Ret; }
pfnCardAcquireContext = (PFN_CARD_ACQUIRE_CONTEXT) GetProcAddress( pCardState->hCardModule, "CardAcquireContext");
if (NULL == pfnCardAcquireContext) { dwSts = GetLastError(); goto Ret; }
pCardData = (PCARD_DATA) CspAllocH(sizeof(CARD_DATA));
LOG_CHECK_ALLOC(pCardData);
dwSts = InitializeCardData(pCardData);
if (ERROR_SUCCESS != dwSts) goto Ret;
//
// We need to temporarily copy the current card handle into the
// CARD_DATA structure, so we can make some calls into the card
// module. We may keep these handles in some cases.
//
pCardData->hScard = pCardMatchData->hSCard; pCardData->hSCardCtx = pCardMatchData->hSCardCtx; pCardData->pwszCardName = (LPWSTR) CspAllocH( (1 + wcslen(pCardMatchData->wszMatchedCard)) * sizeof(WCHAR));
LOG_CHECK_ALLOC(pCardData->pwszCardName);
wcscpy( pCardData->pwszCardName, pCardMatchData->wszMatchedCard);
pCardData->cbAtr = cbATR_BUFFER; pCardData->pbAtr = (PBYTE) CspAllocH(cbATR_BUFFER);
LOG_CHECK_ALLOC(pCardData->pbAtr);
//
// Use SCardStatus to get the ATR of the card we're trying
// to talk to.
//
cch = SCARD_AUTOALLOCATE; dwSts = SCardStatusW( pCardData->hScard, (LPWSTR) &mszReaders, &cch, &dwState, &dwProtocol, pCardData->pbAtr, &pCardData->cbAtr);
switch (dwSts) { case SCARD_W_RESET_CARD: // The card managed to get reset already. Try to reconnect.
dwSts = SCardReconnect( pCardData->hScard, pCardMatchData->dwShareMode, pCardMatchData->dwPreferredProtocols, SCARD_LEAVE_CARD, &dwProtocol);
if (ERROR_SUCCESS != dwSts) goto Ret;
break;
case ERROR_SUCCESS: break;
default: goto Ret; }
//
// Now acquire a card module context for this card.
//
dwSts = pfnCardAcquireContext(pCardData, 0);
if (ERROR_SUCCESS != dwSts) goto Ret;
pCardState->pCardData = pCardData; pCardData = NULL;
//
// Now that we have both the CARD_STATE and CARD_DATA structures,
// we can setup the caching contexts to be used by the CSP and to
// be exposed to the card module.
//
dwSts = InitializeCspCaching(pCardState);
if (ERROR_SUCCESS != dwSts) goto Ret;
dwSts = CspBeginTransaction(pCardState);
if (ERROR_SUCCESS != dwSts) goto Ret;
pCardMatchData->fTransacted = TRUE;
//
// Get the serial number for this card
//
dwSts = CspReadFile( pCardState, wszCARD_IDENTIFIER_FILE_FULL_PATH, 0, &DataBlob.pbData, &DataBlob.cbData);
if (ERROR_SUCCESS != dwSts) { CspEndTransaction(pCardState); pCardMatchData->fTransacted = FALSE; goto Ret; }
memcpy( pCardState->wszSerialNumber, DataBlob.pbData, DataBlob.cbData);
pCardState->pfnCardAcquireContext = pfnCardAcquireContext; pCardMatchData->pCardState = pCardState; pCardState = NULL;
Ret:
// If we're not in a transaction, assume the current handles will be
// cleaned up by the caller
/*
if (NULL != pCardMatchData->pCardState && NULL != pCardMatchData->pCardState->pCardData && FALSE == pCardMatchData->fTransacted) { pCardMatchData->pCardState->pCardData->hScard = 0; pCardMatchData->pCardState->pCardData->hSCardCtx = 0; } */
if (DataBlob.pbData) CspFreeH(DataBlob.pbData); if (pCardState) { DeleteCardState(pCardState); CspFreeH(pCardState); } if (pCardData) { CleanupCardData(pCardData); CspFreeH(pCardData); } if (mszReaders) SCardFreeMemory(pCardMatchData->hSCardCtx, mszReaders);
LOG_END_FUNCTION(GetCardSerialNumber, dwSts);
return dwSts; }
//
// Function: FindCardConnectProc
//
// Purpose: This function is called by SCardUIDlgSelectCard to
// connect to candidate cards. This is a wrapper for
// SCardConnectW, useful because the reader name and card
// name are copied into the PCARD_MATCH_DATA structure for
// reference by FindCardCheckProc, below.
//
SCARDHANDLE WINAPI FindCardConnectProc( IN SCARDCONTEXT hSCardCtx, IN LPWSTR wszReader, IN LPWSTR wszCard, IN OUT PVOID pvCardMatchData) { SCARDHANDLE hSCard = 0; DWORD dwSts = ERROR_SUCCESS; PCARD_MATCH_DATA pCardMatchData = (PCARD_MATCH_DATA) pvCardMatchData;
DsysAssert(FALSE == pCardMatchData->fTransacted);
dwSts = SCardConnectW( hSCardCtx, wszReader, pCardMatchData->dwShareMode, pCardMatchData->dwPreferredProtocols, &hSCard, &pCardMatchData->dwActiveProtocol);
if (ERROR_SUCCESS != dwSts) { SetLastError(dwSts); return 0; }
wcsncpy( pCardMatchData->wszMatchedCard, wszCard, pCardMatchData->cchMatchedCard);
wcsncpy( pCardMatchData->wszMatchedReader, wszReader, pCardMatchData->cchMatchedReader);
return hSCard; }
//
// Function: FindCardDisconnectProc
//
// Purpose: Called by SCardUIDlgSelectCard, this is a wrapper
// for SCardDisconnect. Some cleanup is also done in the
// provided CARD_MATCH_DATA structure to indicate that no
// card handle is currently active.
//
void WINAPI FindCardDisconnectProc( IN SCARDCONTEXT hSCardCtx, IN SCARDHANDLE hSCard, IN PVOID pvCardMatchData) { DWORD dwSts = ERROR_SUCCESS; PCARD_MATCH_DATA pCardMatchData = (PCARD_MATCH_DATA) pvCardMatchData;
UNREFERENCED_PARAMETER(hSCardCtx);
DsysAssert(FALSE == pCardMatchData->fTransacted);
//
// Get rid of the matching card and reader information.
//
memset( pCardMatchData->wszMatchedCard, 0, pCardMatchData->cchMatchedCard * sizeof(pCardMatchData->wszMatchedCard[0]));
memset( pCardMatchData->wszMatchedReader, 0, pCardMatchData->cchMatchedReader * sizeof(pCardMatchData->wszMatchedReader[0]));
pCardMatchData->hSCard = 0;
// If there is a matched card currently, we may be about to disconnect its
// card handle. If so, set that handle value to zero.
if (NULL != pCardMatchData->pCardState) { dwSts = CspEnterCriticalSection( &pCardMatchData->pCardState->cs);
if (ERROR_SUCCESS != dwSts) { SetLastError(dwSts); return; }
if (hSCard == pCardMatchData->pCardState->pCardData->hScard) pCardMatchData->pCardState->pCardData->hScard = 0;
CspLeaveCriticalSection( &pCardMatchData->pCardState->cs); }
dwSts = SCardDisconnect(hSCard, SCARD_LEAVE_CARD);
if (ERROR_SUCCESS != dwSts) { SetLastError(dwSts); return; } }
//
// Function: FindCardMatchUserParamaters
//
// Purpose: Check the card specified in the CARD_MATCH_DATA structure against
// the user parameters specified in CryptAcquireContext.
//
// Assumes that the caller does not hold a transaction on the
// target card.
//
// If the card match is successful, the transaction will still be held
// when the function returns. The caller must release it.
//
DWORD WINAPI FindCardMatchUserParameters( IN PCARD_MATCH_DATA pCardMatchData) { DWORD dwSts = ERROR_SUCCESS; CARD_FREE_SPACE_INFO CardFreeSpaceInfo; DATA_BLOB DataBlob; PCARD_STATE pCardState = pCardMatchData->pCardState; CONTAINER_MAP_RECORD ContainerRecord; BYTE cContainers = 0;
LOG_BEGIN_FUNCTION(FindCardMatchUserParameters);
memset(&CardFreeSpaceInfo, 0, sizeof(CardFreeSpaceInfo)); memset(&DataBlob, 0, sizeof(DataBlob)); memset(&ContainerRecord, 0, sizeof(ContainerRecord));
//
// Now start checking this card for matching
// information.
//
if (CRYPT_NEWKEYSET & pCardMatchData->dwCtxFlags) { if (FALSE == pCardMatchData->fTransacted) { dwSts = CspBeginTransaction(pCardState);
if (ERROR_SUCCESS != dwSts) goto Ret;
pCardMatchData->fTransacted = TRUE; }
//
// The user wants to create a new keyset. Will that
// be possible on this card?
//
dwSts = CspQueryFreeSpace( pCardState, 0, &CardFreeSpaceInfo);
if (ERROR_SUCCESS != dwSts) goto Ret;
// Determine how many valid containers are already present on this card
dwSts = ContainerMapEnumContainers( pCardState, &cContainers, NULL);
if (ERROR_SUCCESS != dwSts) goto Ret;
if (cContainers >= CardFreeSpaceInfo.dwMaxKeyContainers) { // No space for an additional container exists on
// this card.
dwSts = (DWORD) NTE_TOKEN_KEYSET_STORAGE_FULL; goto Ret; } // If a container name was specified, make sure that container name
// doesn't already exist on this card.
if (NULL != pCardMatchData->pwszContainerName) { wcscpy(ContainerRecord.wszGuid, pCardMatchData->pwszContainerName);
dwSts = ContainerMapFindContainer( pCardState, &ContainerRecord, NULL);
switch (dwSts) { case ERROR_SUCCESS: // If that call succeeded, then the specified container
// already exists, so this card can't be used.
dwSts = (DWORD) NTE_EXISTS; break; case NTE_BAD_KEYSET: // In this case, we're successful because the keyset
// wasn't found.
dwSts = ERROR_SUCCESS; break; default: goto Ret; } } else { // Otherwise, the caller is attempting to create a new default
// container, using a random Guid. Nothing else to do at this time.
} } else if (CRYPT_VERIFYCONTEXT & pCardMatchData->dwCtxFlags) { //
// Caller is requesting VERIFYCONTEXT only. We don't need to check
// for a specific container, we we're done.
//
} else { if (FALSE == pCardMatchData->fTransacted) { dwSts = CspBeginTransaction(pCardState);
if (ERROR_SUCCESS != dwSts) goto Ret;
pCardMatchData->fTransacted = TRUE; }
//
// The user wants to open an existing keyset.
//
if (pCardMatchData->pwszContainerName) { wcscpy(ContainerRecord.wszGuid, pCardMatchData->pwszContainerName);
dwSts = ContainerMapFindContainer( pCardState, &ContainerRecord, &pCardMatchData->bContainerIndex);
if (ERROR_SUCCESS != dwSts) { dwSts = (DWORD) SCARD_E_NO_KEY_CONTAINER; goto Ret; } } else { // User wants to open an existing default container.
dwSts = ContainerMapGetDefaultContainer( pCardState, &ContainerRecord, &pCardMatchData->bContainerIndex);
if (ERROR_SUCCESS != dwSts) { dwSts = (DWORD) SCARD_E_NO_KEY_CONTAINER; goto Ret; }
// Hang onto the default container name - it will be needed in
// the User Context.
pCardMatchData->pwszContainerName = (LPWSTR) CspAllocH( (wcslen(ContainerRecord.wszGuid) + 1) * sizeof(WCHAR));
LOG_CHECK_ALLOC(pCardMatchData->pwszContainerName);
wcscpy( pCardMatchData->pwszContainerName, ContainerRecord.wszGuid);
pCardMatchData->fFreeContainerName = TRUE; } }
Ret:
if (ERROR_SUCCESS != dwSts && TRUE == pCardMatchData->fTransacted) { CspEndTransaction(pCardMatchData->pCardState); pCardMatchData->fTransacted = FALSE; }
if (DataBlob.pbData) CspFreeH(DataBlob.pbData);
LOG_END_FUNCTION(FindCardMatchUserParameters, dwSts);
return dwSts; }
//
// Function: FindCardCheckProc
//
// Purpose: Called by SCardUIDlgSelectCard to check candidate cards.
//
BOOL WINAPI FindCardCheckProc( IN SCARDCONTEXT hSCardCtx, IN SCARDHANDLE hSCard, IN PVOID pvCardMatchData) { DWORD dwSts = ERROR_SUCCESS; PCARD_MATCH_DATA pCardMatchData = (PCARD_MATCH_DATA) pvCardMatchData; BOOL fCardMatches = FALSE;
UNREFERENCED_PARAMETER(hSCardCtx);
LOG_BEGIN_FUNCTION(FindCardCheckProc); pCardMatchData->hSCard = hSCard; pCardMatchData->dwError = ERROR_SUCCESS;
//
// Read the serial number from the card
//
dwSts = GetCardSerialNumber( pCardMatchData);
if (ERROR_SUCCESS != dwSts) goto Ret;
//
// Lookup the serial number in our cached list
// of cards
//
dwSts = CardStateCacheFindAddItem( pCardMatchData);
if (ERROR_SUCCESS != dwSts) goto Ret;
if (CARD_MATCH_TYPE_SERIAL_NUMBER == pCardMatchData->dwMatchType) { // We're working on a "by Serial Number" match, so compare
// the requested serial number to that of the card being
// examined. If they match, the search ends successfully.
// If they don't, we know this is the wrong card.
if (0 == wcscmp( pCardMatchData->pwszSerialNumber, pCardMatchData->pCardState->wszSerialNumber)) { fCardMatches = TRUE; } } else { // We're not searching by serial number, so we'll make some sort
// of container-based decision for matching a card.
dwSts = FindCardMatchUserParameters( pCardMatchData);
if (ERROR_SUCCESS == dwSts || (NTE_TOKEN_KEYSET_STORAGE_FULL == dwSts && SC_DLG_NO_UI != pCardMatchData->dwUIFlags)) { //
// If the user picked this card explicitly from the UI, but
// the card is full, report a successful match to scarddlg.
// This allows the CSP to return an appropriate error code
// to allow enrollment to restart by re-using an existing key,
// rather than trying to create a new one.
//
fCardMatches = TRUE; } } Ret:
pCardMatchData->hSCard = 0;
if (pCardMatchData->fTransacted) { CspEndTransaction(pCardMatchData->pCardState); pCardMatchData->fTransacted = FALSE; }
if (TRUE == fCardMatches) { pCardMatchData->pUIMatchedCardState = pCardMatchData->pCardState; } else { pCardMatchData->pCardState = NULL; pCardMatchData->dwError = dwSts; }
LOG_END_FUNCTION(FindCardCheckProc, dwSts);
return fCardMatches; }
//
// Function: FindCardCached
//
// Purpose: Attempt to find a matching card using only cached
// data.
//
DWORD FindCardCached( IN OUT PCARD_MATCH_DATA pCardMatchData) { PCARD_STATE pCardState = NULL; DWORD dwSts = ERROR_SUCCESS; PDATA_BLOB pdb = NULL; DWORD cItems = 0; BOOL fCardStatusChanged = FALSE;
LOG_BEGIN_FUNCTION(FindCardCached);
dwSts = CspEnterCriticalSection( &pCardMatchData->pCspState->cs);
if (ERROR_SUCCESS != dwSts) goto Ret;
dwSts = CacheEnumItems( pCardMatchData->pCspState->hCache, &pdb, &cItems);
CspLeaveCriticalSection( &pCardMatchData->pCspState->cs);
if (ERROR_SUCCESS != dwSts) goto Ret;
while (cItems--) { pCardState = (PCARD_STATE) pdb[cItems].pbData; pCardMatchData->pCardState = pCardState;
// Make sure the handle for this cached card is still valid. If
// the handle isn't valid, skip this card for the cached search.
dwSts = CspEnterCriticalSection(&pCardState->cs);
if (ERROR_SUCCESS != dwSts) goto Ret;
dwSts = ValidateCardHandle(pCardState, TRUE, &fCardStatusChanged);
if ((ERROR_SUCCESS != dwSts || TRUE == fCardStatusChanged) && NULL != pCardState->hPinCache) { // Flush the pin cache for this card. Not checking error code
// since we'll keep processing, anyway.
CspRemoveCachedPin(pCardState, wszCARD_USER_USER); }
CspLeaveCriticalSection(&pCardState->cs);
if (ERROR_SUCCESS == dwSts) { dwSts = FindCardMatchUserParameters( pCardMatchData); if (ERROR_SUCCESS == dwSts) break; else pCardMatchData->pCardState = NULL; } }
CacheFreeEnumItems(pdb);
Ret:
if (TRUE == pCardMatchData->fTransacted) { CspEndTransaction(pCardMatchData->pCardState); pCardMatchData->fTransacted = FALSE; }
LOG_END_FUNCTION(FindCardCached, dwSts);
return dwSts; }
//
// Function: FindCard
//
// Purpose: Primary internal routine for matching a suitable card
// based on these factors:
//
// a) Cards that are supportable by this CSP.
// b) Cards that meet the user supplied criteria from
// CryptAcquireContext.
//
DWORD FindCard( IN OUT PCARD_MATCH_DATA pCardMatchData) { DWORD dwSts = ERROR_SUCCESS; OPENCARDNAME_EXW ocnx; OPENCARD_SEARCH_CRITERIAW ocsc;
LOG_BEGIN_FUNCTION(FindCard);
if (CARD_MATCH_TYPE_READER_AND_CONTAINER == pCardMatchData->dwMatchType) { //
// Only look for a cached card if the search type is
// by Reader and Container.
//
// The reason for this is: if we already know the serial number
// of the card we're looking for, then we must have already had a
// valid card previously. Assume that we'll have to go through
// the Resource Manager to locate such a card, because the card
// handle became invalid and reconnect failed (the card was withdrawn
// and possibly inserted in a different reader).
//
dwSts = FindCardCached(pCardMatchData); if (ERROR_SUCCESS == dwSts && NULL != pCardMatchData->pCardState) { //
// Found a cached card, so we're done.
//
goto Ret; } }
//
// No cached card was found, or this is a "by Serial Number" search,
// so continue the search via
// the smart card subsystem.
//
dwSts = SCardEstablishContext( SCARD_SCOPE_USER, NULL, NULL, &pCardMatchData->hSCardCtx);
if (ERROR_SUCCESS != dwSts) goto Ret;
memset(&ocnx, 0, sizeof(ocnx)); memset(&ocsc, 0, sizeof(ocsc));
ocsc.dwStructSize = sizeof(ocsc); ocsc.lpfnCheck = FindCardCheckProc; ocsc.lpfnConnect = FindCardConnectProc; ocsc.lpfnDisconnect = FindCardDisconnectProc; ocsc.dwShareMode = pCardMatchData->dwShareMode; ocsc.dwPreferredProtocols = pCardMatchData->dwPreferredProtocols; ocsc.pvUserData = (PVOID) pCardMatchData;
ocnx.dwStructSize = sizeof(ocnx); ocnx.pvUserData = (PVOID) pCardMatchData; ocnx.hSCardContext = pCardMatchData->hSCardCtx; ocnx.pOpenCardSearchCriteria = &ocsc; ocnx.lpstrCard = pCardMatchData->wszMatchedCard; ocnx.nMaxCard = pCardMatchData->cchMatchedCard; ocnx.lpstrRdr = pCardMatchData->wszMatchedReader; ocnx.nMaxRdr = pCardMatchData->cchMatchedReader; ocnx.lpfnConnect = FindCardConnectProc; ocnx.dwShareMode = pCardMatchData->dwShareMode; ocnx.dwPreferredProtocols = pCardMatchData->dwPreferredProtocols; //
// The first attempt at finding a matching card should be done
// "silently." We want to control whether card selection UI is
// displayed, depending on whether a card is currently in the
// reader, and depending on the context flags.
//
ocnx.dwFlags = SC_DLG_NO_UI; pCardMatchData->dwUIFlags = ocnx.dwFlags;
dwSts = SCardUIDlgSelectCardW(&ocnx);
switch (dwSts) { case ERROR_SUCCESS: break; // Success, we're done.
case SCARD_E_CANCELLED:
if (NTE_TOKEN_KEYSET_STORAGE_FULL == pCardMatchData->dwError) { dwSts = (DWORD) NTE_TOKEN_KEYSET_STORAGE_FULL; break; }
if ( (CRYPT_SILENT & pCardMatchData->dwCtxFlags) || (CRYPT_VERIFYCONTEXT & pCardMatchData->dwCtxFlags)) { //
// We couldn't show UI, and the caller specified a key container
// (or simply asked for the default) that we couldn't find. Apps
// such as enrollment station expect that we return NTE_BAD_KEYSET
// in this scenario.
//
if (SCARD_E_NO_KEY_CONTAINER == pCardMatchData->dwError) { dwSts = (DWORD) NTE_BAD_KEYSET; break; }
dwSts = (DWORD) SCARD_E_NO_SMARTCARD; break; }
// Allow UI and try again.
ocnx.dwFlags = 0; pCardMatchData->dwUIFlags = ocnx.dwFlags;
dwSts = SCardUIDlgSelectCardW(&ocnx);
//
// If scarddlg thinks the match was successful, but the matched card
// is actually full, then report that error. This is done so that the
// user can manually select a "full" card in the UI, and
// certificate enrollment can proceed by re-using the existing key.
//
if (ERROR_SUCCESS == dwSts && NTE_TOKEN_KEYSET_STORAGE_FULL == pCardMatchData->dwError) { dwSts = (DWORD) NTE_TOKEN_KEYSET_STORAGE_FULL; break; }
break;
default: break; // Return error to caller.
}
if (ERROR_SUCCESS == dwSts) { // Make sure scarddlg didn't fail unexpectedly
if (0 == ocnx.hCardHandle) { dwSts = SCARD_E_NO_SMARTCARD; goto Ret; }
DsysAssert(NULL != pCardMatchData->pUIMatchedCardState);
pCardMatchData->pCardState = pCardMatchData->pUIMatchedCardState;
dwSts = CspEnterCriticalSection( &pCardMatchData->pCardState->cs);
if (ERROR_SUCCESS != dwSts) goto Ret;
if (0 == pCardMatchData->pCardState->pCardData->hScard) { pCardMatchData->pCardState->pCardData->hScard = ocnx.hCardHandle; ocnx.hCardHandle = 0; pCardMatchData->pCardState->pCardData->hSCardCtx = pCardMatchData->hSCardCtx; pCardMatchData->hSCardCtx = 0; }
CspLeaveCriticalSection( &pCardMatchData->pCardState->cs);
ocnx.hCardHandle = 0; }
Ret:
DsysAssert(FALSE == pCardMatchData->fTransacted); DsysAssert(0 == pCardMatchData->hSCard);
if (0 != ocnx.hCardHandle) SCardDisconnect(ocnx.hCardHandle, SCARD_LEAVE_CARD);
if (0 != pCardMatchData->hSCardCtx && ERROR_SUCCESS != dwSts) SCardReleaseContext(pCardMatchData->hSCardCtx);
LOG_END_FUNCTION(FindCard, dwSts);
return dwSts; }
|