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