Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1187 lines
34 KiB

#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;
}