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.
1227 lines
25 KiB
1227 lines
25 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 2000
|
|
|
|
Module Name:
|
|
|
|
scuisupp
|
|
|
|
Abstract:
|
|
|
|
Support for smart card certificate selection UI
|
|
|
|
Author:
|
|
|
|
Klaus Schutz
|
|
|
|
--*/
|
|
|
|
#include "stdafx.h"
|
|
#include <wincrypt.h>
|
|
#include <winscard.h>
|
|
#include <winwlx.h>
|
|
#include <string.h>
|
|
|
|
#include "calaislb.h"
|
|
#include "scuisupp.h"
|
|
#include "StatMon.h" // smart card reader status monitor
|
|
#include "scevents.h"
|
|
|
|
#include <mmsystem.h>
|
|
|
|
typedef struct _READER_DATA
|
|
{
|
|
CERT_ENUM CertEnum;
|
|
LPTSTR pszReaderName;
|
|
LPTSTR pszCardName;
|
|
LPTSTR pszCSPName;
|
|
|
|
} READER_DATA, *PREADER_DATA;
|
|
|
|
typedef struct _THREAD_DATA
|
|
{
|
|
// handle to heap of this thread
|
|
HANDLE hHeap;
|
|
|
|
// the smart card context we use
|
|
SCARDCONTEXT hSCardContext;
|
|
|
|
// the window we send messages to
|
|
HWND hWindow;
|
|
|
|
// event to signal that the monitor thread can terminate
|
|
HANDLE hClose;
|
|
|
|
// thread handle for the monitor thread
|
|
HANDLE hThread;
|
|
|
|
// number of readers detected
|
|
DWORD dwNumReaders;
|
|
|
|
// messages to be sent to parent
|
|
UINT msgReaderArrival;
|
|
UINT msgReaderRemoval;
|
|
UINT msgSmartCardInsertion;
|
|
UINT msgSmartCardRemoval;
|
|
UINT msgSmartCardStatus;
|
|
UINT msgSmartCardCertAvail;
|
|
|
|
// reader state array
|
|
PSCARD_READERSTATE rgReaders;
|
|
|
|
// number of removed readers
|
|
DWORD dwRemovedReaders;
|
|
|
|
// pointer array of removed reader data
|
|
PREADER_DATA *ppRemovedReaderData;
|
|
|
|
} THREAD_DATA, *PTHREAD_DATA;
|
|
|
|
#ifndef TEST
|
|
|
|
#define SC_DEBUG(a)
|
|
|
|
#else
|
|
|
|
#define SC_DEBUG(a) _DebugPrint a
|
|
|
|
#undef PostMessage
|
|
#define PostMessage(a,b,c,d) _PostMessage(a, b, c, d)
|
|
|
|
#undef RegisterWindowMessage
|
|
#define RegisterWindowMessage(a) _RegisterWindowMessage(a)
|
|
|
|
void
|
|
__cdecl
|
|
_DebugPrint(
|
|
LPCTSTR szFormat,
|
|
...
|
|
)
|
|
{
|
|
TCHAR szBuffer[1024];
|
|
va_list ap;
|
|
|
|
va_start(ap, szFormat);
|
|
_vstprintf(szBuffer, szFormat, ap);
|
|
OutputDebugString(szBuffer);
|
|
_tprintf(szBuffer);
|
|
}
|
|
|
|
#define MAX_MESSAGES 10
|
|
|
|
static struct {
|
|
|
|
UINT dwMessage;
|
|
LPCTSTR lpMessage;
|
|
|
|
} Messages[MAX_MESSAGES];
|
|
|
|
UINT
|
|
_RegisterWindowMessage(
|
|
LPCTSTR lpString
|
|
)
|
|
{
|
|
for (DWORD dwIndex = 0; dwIndex < MAX_MESSAGES; dwIndex += 1) {
|
|
|
|
if (Messages[dwIndex].lpMessage == NULL) {
|
|
|
|
break;
|
|
}
|
|
|
|
if (_tcscmp(lpString, Messages[dwIndex].lpMessage) == 0) {
|
|
|
|
return Messages[dwIndex].dwMessage;
|
|
}
|
|
}
|
|
|
|
Messages[dwIndex].lpMessage = lpString;
|
|
Messages[dwIndex].dwMessage = dwIndex;
|
|
|
|
return dwIndex;
|
|
}
|
|
|
|
LPCTSTR
|
|
_GetWindowMessageString(
|
|
UINT dwMessage
|
|
)
|
|
{
|
|
for (DWORD dwIndex = 0; dwIndex < MAX_MESSAGES; dwIndex += 1) {
|
|
|
|
if (Messages[dwIndex].lpMessage == NULL) {
|
|
|
|
return TEXT("(NOT DEFINED)");
|
|
}
|
|
|
|
if (dwMessage == Messages[dwIndex].dwMessage) {
|
|
|
|
return Messages[dwIndex].lpMessage;
|
|
}
|
|
}
|
|
|
|
return TEXT("(INTERNAL ERROR)");
|
|
}
|
|
|
|
LRESULT
|
|
_PostMessage(
|
|
HWND hWindow,
|
|
UINT Msg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
SC_DEBUG((TEXT("Received message %s\n"), _GetWindowMessageString(Msg)));
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
static
|
|
LPCTSTR
|
|
FirstString(
|
|
IN LPCTSTR szMultiString
|
|
)
|
|
/*++
|
|
|
|
FirstString:
|
|
|
|
This routine returns a pointer to the first string in a multistring, or NULL
|
|
if there aren't any.
|
|
|
|
Arguments:
|
|
|
|
szMultiString - This supplies the address of the current position within a
|
|
Multi-string structure.
|
|
|
|
Return Value:
|
|
|
|
The address of the first null-terminated string in the structure, or NULL if
|
|
there are no strings.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 11/25/1996
|
|
|
|
--*/
|
|
{
|
|
LPCTSTR szFirst = NULL;
|
|
|
|
try
|
|
{
|
|
if (0 != *szMultiString)
|
|
szFirst = szMultiString;
|
|
}
|
|
catch (...) {}
|
|
|
|
return szFirst;
|
|
}
|
|
|
|
static
|
|
LPCTSTR
|
|
NextString(
|
|
IN LPCTSTR szMultiString)
|
|
/*++
|
|
|
|
NextString:
|
|
|
|
In some cases, the Smartcard API returns multiple strings, separated by Null
|
|
characters, and terminated by two null characters in a row. This routine
|
|
simplifies access to such structures. Given the current string in a
|
|
multi-string structure, it returns the next string, or NULL if no other
|
|
strings follow the current string.
|
|
|
|
Arguments:
|
|
|
|
szMultiString - This supplies the address of the current position within a
|
|
Multi-string structure.
|
|
|
|
Return Value:
|
|
|
|
The address of the next Null-terminated string in the structure, or NULL if
|
|
no more strings follow.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 8/12/1996
|
|
|
|
--*/
|
|
{
|
|
LPCTSTR szNext;
|
|
|
|
try
|
|
{
|
|
DWORD cchLen = lstrlen(szMultiString);
|
|
if (0 == cchLen)
|
|
szNext = NULL;
|
|
else
|
|
{
|
|
szNext = szMultiString + cchLen + 1;
|
|
if (0 == *szNext)
|
|
szNext = NULL;
|
|
}
|
|
}
|
|
|
|
catch (...)
|
|
{
|
|
szNext = NULL;
|
|
}
|
|
|
|
return szNext;
|
|
}
|
|
|
|
static
|
|
void
|
|
FreeReaderData(
|
|
PTHREAD_DATA pThreadData,
|
|
PREADER_DATA pReaderData
|
|
)
|
|
{
|
|
if (pThreadData->hSCardContext && pReaderData->pszCardName) {
|
|
|
|
SCardFreeMemory(pThreadData->hSCardContext, pReaderData->pszCardName);
|
|
pReaderData->pszCardName = NULL;
|
|
pReaderData->CertEnum.pszCardName = NULL;
|
|
}
|
|
|
|
if (pThreadData->hSCardContext && pReaderData->pszCSPName) {
|
|
|
|
SCardFreeMemory(pThreadData->hSCardContext, pReaderData->pszCSPName);
|
|
pReaderData->pszCSPName = NULL;
|
|
}
|
|
|
|
if (pReaderData->CertEnum.pCertContext) {
|
|
|
|
CertFreeCertificateContext(pReaderData->CertEnum.pCertContext);
|
|
pReaderData->CertEnum.pCertContext = NULL;
|
|
}
|
|
|
|
pReaderData->CertEnum.dwStatus = 0;
|
|
}
|
|
|
|
static
|
|
void
|
|
UpdateCertificates(
|
|
PTHREAD_DATA pThreadData,
|
|
PSCARD_READERSTATE pReaderState
|
|
)
|
|
{
|
|
PREADER_DATA pReaderData = (PREADER_DATA) pReaderState->pvUserData;
|
|
|
|
FreeReaderData(pThreadData, pReaderData);
|
|
|
|
SC_DEBUG((TEXT("Enumerating certificates on %s...\n"), pReaderState->szReader));
|
|
|
|
LPTSTR pszContainerName = NULL;
|
|
LPTSTR pszDefaultContainerName = NULL;
|
|
PBYTE pbCert = NULL;
|
|
HCRYPTKEY hKey = NULL;
|
|
HCRYPTPROV hCryptProv = NULL;
|
|
|
|
__try {
|
|
|
|
pReaderData->CertEnum.dwStatus = SCARD_F_UNKNOWN_ERROR;
|
|
|
|
// Get the name of the card
|
|
DWORD dwAutoAllocate = SCARD_AUTOALLOCATE;
|
|
LONG lReturn = SCardListCards(
|
|
pThreadData->hSCardContext,
|
|
pReaderState->rgbAtr,
|
|
NULL,
|
|
0,
|
|
(LPTSTR)&pReaderData->pszCardName,
|
|
&dwAutoAllocate
|
|
);
|
|
|
|
if (lReturn != SCARD_S_SUCCESS) {
|
|
|
|
SC_DEBUG((TEXT("Failed to get card name for card in %s\n"), pReaderState->szReader));
|
|
pReaderData->CertEnum.dwStatus = lReturn;
|
|
__leave;
|
|
}
|
|
|
|
pReaderData->CertEnum.pszCardName = pReaderData->pszCardName;
|
|
|
|
dwAutoAllocate = SCARD_AUTOALLOCATE;
|
|
lReturn = SCardGetCardTypeProviderName(
|
|
pThreadData->hSCardContext,
|
|
pReaderData->pszCardName,
|
|
SCARD_PROVIDER_CSP,
|
|
(LPTSTR)&pReaderData->pszCSPName,
|
|
&dwAutoAllocate
|
|
);
|
|
|
|
if (lReturn != SCARD_S_SUCCESS) {
|
|
|
|
SC_DEBUG((TEXT("Failed to get CSP name from %s\n"), pReaderState->szReader));
|
|
pReaderData->CertEnum.dwStatus = SCARD_E_UNKNOWN_CARD;
|
|
__leave;
|
|
}
|
|
|
|
pszContainerName = (LPTSTR) HeapAlloc(
|
|
pThreadData->hHeap,
|
|
HEAP_ZERO_MEMORY,
|
|
(_tcslen(pReaderState->szReader) + 10) * sizeof(TCHAR)
|
|
);
|
|
|
|
if (pszContainerName == NULL) {
|
|
|
|
pReaderData->CertEnum.dwStatus = SCARD_E_NO_MEMORY;
|
|
__leave;
|
|
}
|
|
|
|
_stprintf(
|
|
pszContainerName,
|
|
TEXT("\\\\.\\%s\\"),
|
|
pReaderState->szReader);
|
|
|
|
BOOL fSuccess = CryptAcquireContext(
|
|
&hCryptProv,
|
|
pszContainerName,
|
|
pReaderData->pszCSPName,
|
|
PROV_RSA_FULL,
|
|
CRYPT_SILENT
|
|
);
|
|
|
|
if (fSuccess == FALSE) {
|
|
|
|
SC_DEBUG((
|
|
TEXT("Failed to acquire context on %s (%lx)\n"),
|
|
pReaderState->szReader, GetLastError()
|
|
));
|
|
|
|
pReaderData->CertEnum.dwStatus = GetLastError();
|
|
__leave;
|
|
}
|
|
|
|
// Get the default container name, so we can use it
|
|
DWORD cbDefaultContainerName;
|
|
fSuccess = CryptGetProvParam(
|
|
hCryptProv,
|
|
PP_CONTAINER,
|
|
NULL,
|
|
&cbDefaultContainerName,
|
|
0
|
|
);
|
|
|
|
if (!fSuccess) {
|
|
|
|
SC_DEBUG((TEXT("Failed to get default container name from %s\n"), pReaderState->szReader));
|
|
pReaderData->CertEnum.dwStatus = GetLastError();
|
|
__leave;
|
|
}
|
|
|
|
pszDefaultContainerName =
|
|
(LPTSTR) HeapAlloc(pThreadData->hHeap, HEAP_ZERO_MEMORY, cbDefaultContainerName * sizeof(TCHAR));
|
|
|
|
if (NULL == pszContainerName) {
|
|
|
|
pReaderData->CertEnum.dwStatus = SCARD_E_NO_MEMORY;
|
|
__leave;
|
|
}
|
|
|
|
fSuccess = CryptGetProvParam(
|
|
hCryptProv,
|
|
PP_CONTAINER,
|
|
(PBYTE)pszDefaultContainerName,
|
|
&cbDefaultContainerName,
|
|
0
|
|
);
|
|
|
|
if (!fSuccess) {
|
|
|
|
pReaderData->CertEnum.dwStatus = GetLastError();
|
|
__leave;
|
|
}
|
|
|
|
fSuccess = CryptGetUserKey(
|
|
hCryptProv,
|
|
AT_KEYEXCHANGE,
|
|
&hKey
|
|
);
|
|
|
|
if (fSuccess == FALSE) {
|
|
|
|
SC_DEBUG((TEXT("Failed to get key from %s\n"), pReaderState->szReader));
|
|
pReaderData->CertEnum.dwStatus = GetLastError();
|
|
__leave;
|
|
}
|
|
|
|
// get length of certificate
|
|
DWORD cbCertLen = 0;
|
|
fSuccess = CryptGetKeyParam(
|
|
hKey,
|
|
KP_CERTIFICATE,
|
|
NULL,
|
|
&cbCertLen,
|
|
0
|
|
);
|
|
|
|
DWORD dwError = GetLastError();
|
|
|
|
if (fSuccess == FALSE && dwError != ERROR_MORE_DATA) {
|
|
|
|
SC_DEBUG((TEXT("Failed to get certificate from %s\n"), pReaderState->szReader));
|
|
pReaderData->CertEnum.dwStatus = GetLastError();
|
|
__leave;
|
|
}
|
|
|
|
pbCert = (LPBYTE) HeapAlloc(pThreadData->hHeap, HEAP_ZERO_MEMORY, cbCertLen);
|
|
|
|
if (pbCert == NULL)
|
|
{
|
|
pReaderData->CertEnum.dwStatus = SCARD_E_NO_MEMORY;
|
|
__leave;
|
|
}
|
|
|
|
// read the certificate off the card
|
|
fSuccess = CryptGetKeyParam(
|
|
hKey,
|
|
KP_CERTIFICATE,
|
|
pbCert,
|
|
&cbCertLen,
|
|
0
|
|
);
|
|
|
|
if (fSuccess == FALSE) {
|
|
|
|
SC_DEBUG((TEXT("Failed to get certificate from %s\n"), pReaderState->szReader));
|
|
pReaderData->CertEnum.dwStatus = GetLastError();
|
|
__leave;
|
|
}
|
|
|
|
PCERT_CONTEXT pCertContext = (PCERT_CONTEXT) CertCreateCertificateContext(
|
|
X509_ASN_ENCODING,
|
|
pbCert,
|
|
cbCertLen
|
|
);
|
|
|
|
if (pCertContext == NULL) {
|
|
|
|
__leave;
|
|
}
|
|
|
|
pReaderData->CertEnum.dwStatus = SCARD_S_SUCCESS;
|
|
pReaderData->CertEnum.pCertContext = pCertContext;
|
|
SC_DEBUG((TEXT("Found new certificate on %s\n"), pReaderState->szReader));
|
|
|
|
PostMessage(
|
|
pThreadData->hWindow,
|
|
pThreadData->msgSmartCardCertAvail,
|
|
(WPARAM) &pReaderData->CertEnum,
|
|
0
|
|
);
|
|
}
|
|
|
|
__finally {
|
|
|
|
//
|
|
// free all allocated memory
|
|
//
|
|
if (NULL != pszContainerName)
|
|
{
|
|
HeapFree(pThreadData->hHeap, 0, pszContainerName);
|
|
}
|
|
|
|
if (NULL != pszDefaultContainerName)
|
|
{
|
|
HeapFree(pThreadData->hHeap, 0, pszDefaultContainerName);
|
|
}
|
|
|
|
if (NULL != pbCert)
|
|
{
|
|
HeapFree(pThreadData->hHeap, 0, pbCert);
|
|
}
|
|
|
|
if (NULL != hKey)
|
|
{
|
|
CryptDestroyKey(hKey);
|
|
}
|
|
|
|
if (NULL != hCryptProv)
|
|
{
|
|
CryptReleaseContext(hCryptProv, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
void
|
|
StopMonitorReaders(
|
|
PTHREAD_DATA pThreadData
|
|
)
|
|
{
|
|
_ASSERT(pThreadData != NULL);
|
|
|
|
SetEvent(pThreadData->hClose);
|
|
|
|
if (pThreadData->hSCardContext) {
|
|
|
|
SCardCancel(pThreadData->hSCardContext);
|
|
}
|
|
}
|
|
|
|
static
|
|
void
|
|
RemoveCard(
|
|
PTHREAD_DATA pThreadData,
|
|
PREADER_DATA pReaderData
|
|
)
|
|
{
|
|
SC_DEBUG((TEXT("Smart Card removed from %s\n"), pReaderData->CertEnum.pszReaderName));
|
|
|
|
PostMessage(
|
|
pThreadData->hWindow,
|
|
pThreadData->msgSmartCardRemoval,
|
|
(WPARAM) &pReaderData->CertEnum,
|
|
0
|
|
);
|
|
}
|
|
|
|
static
|
|
BOOL
|
|
AddReader(
|
|
PTHREAD_DATA pThreadData,
|
|
LPCTSTR pszNewReader
|
|
)
|
|
{
|
|
PSCARD_READERSTATE pScardReaderState =
|
|
(PSCARD_READERSTATE) HeapReAlloc(
|
|
pThreadData->hHeap,
|
|
HEAP_ZERO_MEMORY,
|
|
pThreadData->rgReaders,
|
|
(pThreadData->dwNumReaders + 1) * (sizeof(SCARD_READERSTATE))
|
|
);
|
|
|
|
if (pScardReaderState == NULL) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
pThreadData->rgReaders = pScardReaderState;
|
|
|
|
PREADER_DATA pReaderData =
|
|
(PREADER_DATA) HeapAlloc(
|
|
pThreadData->hHeap,
|
|
HEAP_ZERO_MEMORY,
|
|
sizeof(READER_DATA)
|
|
);
|
|
|
|
if (pReaderData == NULL) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
pThreadData->rgReaders[pThreadData->dwNumReaders].pvUserData =
|
|
pReaderData;
|
|
|
|
LPTSTR pszReaderName =
|
|
(LPTSTR) HeapAlloc(
|
|
pThreadData->hHeap,
|
|
HEAP_ZERO_MEMORY,
|
|
(_tcslen(pszNewReader) + 1) * sizeof(TCHAR)
|
|
);
|
|
|
|
if (pszReaderName == NULL) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
_tcscpy(pszReaderName, pszNewReader);
|
|
|
|
pReaderData->pszReaderName = pszReaderName;
|
|
|
|
pThreadData->rgReaders[pThreadData->dwNumReaders].szReader =
|
|
pszReaderName;
|
|
|
|
pThreadData->rgReaders[pThreadData->dwNumReaders].dwCurrentState =
|
|
SCARD_STATE_EMPTY;
|
|
|
|
pReaderData->CertEnum.pszReaderName = (LPTSTR) pszReaderName;
|
|
pThreadData->dwNumReaders++;
|
|
|
|
PostMessage(
|
|
pThreadData->hWindow,
|
|
pThreadData->msgReaderArrival,
|
|
(WPARAM) &pReaderData->CertEnum,
|
|
0
|
|
);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
BOOL
|
|
RemoveReader(
|
|
PTHREAD_DATA pThreadData,
|
|
PSCARD_READERSTATE pReaderState
|
|
)
|
|
{
|
|
PREADER_DATA pReaderData =
|
|
(PREADER_DATA) pReaderState->pvUserData;
|
|
|
|
if (pReaderState->dwCurrentState & SCARD_STATE_PRESENT) {
|
|
|
|
RemoveCard(
|
|
pThreadData,
|
|
pReaderData
|
|
);
|
|
}
|
|
|
|
SC_DEBUG((TEXT("Reader %s removed\n"), pReaderData->CertEnum.pszReaderName));
|
|
|
|
PostMessage(
|
|
pThreadData->hWindow,
|
|
pThreadData->msgReaderRemoval,
|
|
(WPARAM) &pReaderData->CertEnum,
|
|
0
|
|
);
|
|
|
|
// build an array of reader data that needs to be deleted on exit
|
|
PREADER_DATA *ppRemovedReaderData = NULL;
|
|
|
|
if (pThreadData->dwRemovedReaders == 0) {
|
|
|
|
ppRemovedReaderData = (PREADER_DATA *) HeapAlloc(
|
|
pThreadData->hHeap,
|
|
HEAP_ZERO_MEMORY,
|
|
sizeof(PREADER_DATA)
|
|
);
|
|
|
|
} else {
|
|
|
|
ppRemovedReaderData = (PREADER_DATA *) HeapReAlloc(
|
|
pThreadData->hHeap,
|
|
HEAP_ZERO_MEMORY,
|
|
pThreadData->ppRemovedReaderData,
|
|
(pThreadData->dwRemovedReaders + 1) * sizeof(PREADER_DATA)
|
|
);
|
|
}
|
|
|
|
if (ppRemovedReaderData == NULL) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// add the reader data to the list of stuff that needs to be freed on exit
|
|
pThreadData->ppRemovedReaderData = ppRemovedReaderData;
|
|
pThreadData->ppRemovedReaderData[pThreadData->dwRemovedReaders] = pReaderData;
|
|
|
|
for (DWORD dwIndex = 1; dwIndex < pThreadData->dwNumReaders; dwIndex += 1) {
|
|
|
|
if (pReaderState == &pThreadData->rgReaders[dwIndex]) {
|
|
|
|
// check if the reader we remove is not the last or the only one.
|
|
if (pThreadData->dwNumReaders > 1 && dwIndex != pThreadData->dwNumReaders - 1) {
|
|
|
|
// put the reader from the end of the list into this available slot
|
|
pThreadData->rgReaders[dwIndex] =
|
|
pThreadData->rgReaders[pThreadData->dwNumReaders - 1];
|
|
}
|
|
|
|
// shrink the reader state array
|
|
PSCARD_READERSTATE pReaders = (PSCARD_READERSTATE) HeapReAlloc(
|
|
pThreadData->hHeap,
|
|
0,
|
|
pThreadData->rgReaders,
|
|
(pThreadData->dwNumReaders - 1) * sizeof(SCARD_READERSTATE)
|
|
);
|
|
|
|
if (pReaders == NULL) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
pThreadData->rgReaders = pReaders;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
pThreadData->dwNumReaders -= 1;
|
|
pThreadData->dwRemovedReaders += 1;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
BOOL
|
|
RemoveAllReaders(
|
|
PTHREAD_DATA pThreadData
|
|
)
|
|
{
|
|
if (pThreadData->rgReaders == NULL) {
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// This loop will destroy all the readers starting with the first one
|
|
// dwIndex doesn't have to be incremented. pThreadData->dwNumReaders is
|
|
// decremented in RemoveReader
|
|
for (DWORD dwIndex = 1; dwIndex < pThreadData->dwNumReaders; ) {
|
|
|
|
if (RemoveReader(
|
|
pThreadData,
|
|
&pThreadData->rgReaders[dwIndex]
|
|
) == FALSE) {
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Remove the PnP pseudo reader
|
|
HeapFree(
|
|
pThreadData->hHeap,
|
|
0,
|
|
pThreadData->rgReaders
|
|
);
|
|
pThreadData->rgReaders = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
DWORD
|
|
StartMonitorReaders(
|
|
LPVOID pData
|
|
)
|
|
{
|
|
PTHREAD_DATA pThreadData = (PTHREAD_DATA) pData;
|
|
LPCTSTR szReaderNameList = NULL;
|
|
|
|
//
|
|
// We use this outer loop to restart in case the
|
|
// resource manager was stopped
|
|
//
|
|
__try {
|
|
|
|
pThreadData->rgReaders =
|
|
(PSCARD_READERSTATE) HeapAlloc(
|
|
pThreadData->hHeap,
|
|
HEAP_ZERO_MEMORY,
|
|
sizeof(SCARD_READERSTATE)
|
|
);
|
|
|
|
if (pThreadData->rgReaders == NULL) {
|
|
|
|
__leave;
|
|
}
|
|
|
|
pThreadData->rgReaders[0].szReader = SCPNP_NOTIFICATION;
|
|
pThreadData->rgReaders[0].dwCurrentState = 0;
|
|
pThreadData->dwNumReaders = 1;
|
|
|
|
while (WaitForSingleObject(pThreadData->hClose, 0) == WAIT_TIMEOUT) {
|
|
|
|
// Acquire context with resource manager
|
|
LONG lReturn = SCardEstablishContext(
|
|
SCARD_SCOPE_USER,
|
|
NULL,
|
|
NULL,
|
|
&pThreadData->hSCardContext
|
|
);
|
|
|
|
if (SCARD_S_SUCCESS != lReturn) {
|
|
|
|
// The prev. call should never fail
|
|
// It's better to terminate this thread.
|
|
__leave;
|
|
}
|
|
|
|
szReaderNameList = NULL;
|
|
DWORD dwAutoAllocate = SCARD_AUTOALLOCATE;
|
|
// now list the available readers
|
|
lReturn = SCardListReaders(
|
|
pThreadData->hSCardContext,
|
|
SCARD_DEFAULT_READERS,
|
|
(LPTSTR)&szReaderNameList,
|
|
&dwAutoAllocate
|
|
);
|
|
|
|
if (SCARD_S_SUCCESS == lReturn)
|
|
{
|
|
// bugbug - this pointer should not be modified
|
|
for (LPCTSTR szReader = FirstString( szReaderNameList );
|
|
szReader != NULL;
|
|
szReader = NextString(szReader)) {
|
|
|
|
BOOL fFound = FALSE;
|
|
|
|
// now check if this reader is already in the reader array
|
|
for (DWORD dwIndex = 1; dwIndex < pThreadData->dwNumReaders; dwIndex++) {
|
|
|
|
if (lstrcmp(
|
|
szReader,
|
|
pThreadData->rgReaders[dwIndex].szReader
|
|
) == 0) {
|
|
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fFound == FALSE) {
|
|
|
|
if (AddReader(pThreadData, szReader) == FALSE) {
|
|
|
|
__leave;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL fNewReader = FALSE;
|
|
|
|
// analyze newly inserted cards
|
|
while (WaitForSingleObject(pThreadData->hClose, 0) == WAIT_TIMEOUT &&
|
|
fNewReader == FALSE) {
|
|
|
|
lReturn = SCardGetStatusChange(
|
|
pThreadData->hSCardContext,
|
|
INFINITE,
|
|
pThreadData->rgReaders,
|
|
pThreadData->dwNumReaders
|
|
);
|
|
|
|
if (SCARD_E_SYSTEM_CANCELLED == lReturn) {
|
|
|
|
// the smart card system has been stopped
|
|
// send notification that all readers are gone
|
|
if (RemoveAllReaders(pThreadData) == FALSE) {
|
|
|
|
__leave;
|
|
}
|
|
|
|
// Wait until it restarted
|
|
HANDLE hCalaisStarted = CalaisAccessStartedEvent();
|
|
|
|
if (hCalaisStarted == NULL) {
|
|
|
|
// no way to recover. stop cert prop
|
|
StopMonitorReaders(pThreadData);
|
|
break;
|
|
}
|
|
|
|
HANDLE lHandles[2] = { hCalaisStarted, pThreadData->hClose };
|
|
|
|
lReturn = WaitForMultipleObjectsEx(
|
|
2,
|
|
lHandles,
|
|
FALSE,
|
|
INFINITE,
|
|
FALSE
|
|
);
|
|
|
|
if (lReturn != WAIT_OBJECT_0) {
|
|
|
|
// We stop if an error occured
|
|
StopMonitorReaders(pThreadData);
|
|
break;
|
|
}
|
|
|
|
// Otherwise the resource manager has been restarted
|
|
break;
|
|
}
|
|
|
|
if (SCARD_S_SUCCESS != lReturn)
|
|
{
|
|
StopMonitorReaders(pThreadData);
|
|
break;
|
|
}
|
|
|
|
// Enumerate the readers and for every card change send a message
|
|
for (DWORD dwIndex = 1; dwIndex < pThreadData->dwNumReaders; dwIndex++)
|
|
{
|
|
// Check if the reader has been removed
|
|
if ((pThreadData->rgReaders[dwIndex].dwEventState & SCARD_STATE_UNAVAILABLE)) {
|
|
|
|
if (RemoveReader(
|
|
pThreadData,
|
|
&pThreadData->rgReaders[dwIndex]
|
|
) == FALSE) {
|
|
|
|
__leave;
|
|
}
|
|
|
|
// Continue the loop with the same index
|
|
dwIndex--;
|
|
continue;
|
|
}
|
|
|
|
// check if this is a card insertion
|
|
if ((pThreadData->rgReaders[dwIndex].dwCurrentState & SCARD_STATE_EMPTY) &&
|
|
(pThreadData->rgReaders[dwIndex].dwEventState & SCARD_STATE_PRESENT)) {
|
|
|
|
PREADER_DATA pReaderData =
|
|
(PREADER_DATA) pThreadData->rgReaders[dwIndex].pvUserData;
|
|
|
|
SC_DEBUG((TEXT("Smart Card inserted into %s\n"), pThreadData->rgReaders[dwIndex].szReader));
|
|
|
|
PostMessage(
|
|
pThreadData->hWindow,
|
|
pThreadData->msgSmartCardInsertion,
|
|
(WPARAM) &pReaderData->CertEnum,
|
|
0
|
|
);
|
|
|
|
// read in all certificates
|
|
UpdateCertificates(
|
|
pThreadData,
|
|
&pThreadData->rgReaders[dwIndex]
|
|
);
|
|
|
|
PostMessage(
|
|
pThreadData->hWindow,
|
|
pThreadData->msgSmartCardStatus,
|
|
(WPARAM) &pReaderData->CertEnum,
|
|
0
|
|
);
|
|
}
|
|
|
|
// check if this is a card removal
|
|
if ((pThreadData->rgReaders[dwIndex].dwCurrentState & SCARD_STATE_PRESENT) &&
|
|
(pThreadData->rgReaders[dwIndex].dwEventState & SCARD_STATE_EMPTY)) {
|
|
|
|
PREADER_DATA pReaderData = (PREADER_DATA) pThreadData->rgReaders[dwIndex].pvUserData;
|
|
|
|
RemoveCard(pThreadData, pReaderData);
|
|
|
|
// we can't update the certificates because it would delete
|
|
// some memory data that the caller could reference.
|
|
}
|
|
|
|
// Update the "current state" of this reader
|
|
pThreadData->rgReaders[dwIndex].dwCurrentState =
|
|
pThreadData->rgReaders[dwIndex].dwEventState;
|
|
}
|
|
|
|
// check if a new reader showed up
|
|
if ((pThreadData->dwNumReaders == 1 ||
|
|
pThreadData->rgReaders[0].dwCurrentState != 0) &&
|
|
pThreadData->rgReaders[0].dwEventState & SCARD_STATE_CHANGED) {
|
|
|
|
fNewReader = TRUE;
|
|
}
|
|
|
|
pThreadData->rgReaders[0].dwCurrentState =
|
|
pThreadData->rgReaders[0].dwEventState;
|
|
|
|
}
|
|
|
|
// Clean up
|
|
if (NULL != szReaderNameList)
|
|
{
|
|
SCardFreeMemory(pThreadData->hSCardContext, (PVOID) szReaderNameList);
|
|
szReaderNameList = NULL;
|
|
}
|
|
|
|
if (NULL != pThreadData->hSCardContext)
|
|
{
|
|
SCardReleaseContext(pThreadData->hSCardContext);
|
|
pThreadData->hSCardContext = NULL;
|
|
}
|
|
}
|
|
}
|
|
__finally {
|
|
|
|
if (NULL != szReaderNameList)
|
|
{
|
|
SCardFreeMemory(pThreadData->hSCardContext, (PVOID) szReaderNameList);
|
|
}
|
|
|
|
if (NULL != pThreadData->hSCardContext)
|
|
{
|
|
SCardReleaseContext(pThreadData->hSCardContext);
|
|
pThreadData->hSCardContext = NULL;
|
|
}
|
|
|
|
RemoveAllReaders(pThreadData);
|
|
|
|
SC_DEBUG((TEXT("Terminating monitor thread\n")));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HSCARDUI
|
|
WINAPI
|
|
SCardUIInit(
|
|
HWND hWindow
|
|
)
|
|
{
|
|
PTHREAD_DATA pThreadData =
|
|
(PTHREAD_DATA) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(THREAD_DATA));
|
|
|
|
BOOL fSuccess = FALSE;
|
|
|
|
__try {
|
|
|
|
if (pThreadData == NULL) {
|
|
|
|
__leave;
|
|
}
|
|
|
|
pThreadData->hHeap = GetProcessHeap();
|
|
|
|
pThreadData->hWindow = hWindow;
|
|
|
|
pThreadData->msgReaderArrival =
|
|
RegisterWindowMessage(TEXT(SCARDUI_READER_ARRIVAL));
|
|
pThreadData->msgReaderRemoval =
|
|
RegisterWindowMessage(TEXT(SCARDUI_READER_REMOVAL));
|
|
pThreadData->msgSmartCardInsertion =
|
|
RegisterWindowMessage(TEXT(SCARDUI_SMART_CARD_INSERTION));
|
|
pThreadData->msgSmartCardRemoval =
|
|
RegisterWindowMessage(TEXT(SCARDUI_SMART_CARD_REMOVAL));
|
|
pThreadData->msgSmartCardStatus =
|
|
RegisterWindowMessage(TEXT(SCARDUI_SMART_CARD_STATUS));
|
|
pThreadData->msgSmartCardCertAvail =
|
|
RegisterWindowMessage(TEXT(SCARDUI_SMART_CARD_CERT_AVAIL));
|
|
|
|
pThreadData->hClose = CreateEvent(
|
|
NULL,
|
|
TRUE,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
if (pThreadData->hClose == NULL) {
|
|
|
|
__leave;
|
|
}
|
|
|
|
pThreadData->hThread = CreateThread(
|
|
NULL,
|
|
0,
|
|
StartMonitorReaders,
|
|
(LPVOID) pThreadData,
|
|
CREATE_SUSPENDED,
|
|
NULL
|
|
);
|
|
|
|
if (pThreadData->hThread == NULL) {
|
|
|
|
__leave;
|
|
}
|
|
|
|
ResumeThread(pThreadData->hThread);
|
|
|
|
fSuccess = TRUE;
|
|
}
|
|
|
|
__finally {
|
|
|
|
if (fSuccess == FALSE) {
|
|
|
|
if (pThreadData && pThreadData->hClose) {
|
|
|
|
CloseHandle(pThreadData->hClose);
|
|
}
|
|
|
|
if (pThreadData) {
|
|
|
|
HeapFree(pThreadData->hHeap, 0, pThreadData);
|
|
pThreadData = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (HSCARDUI) pThreadData;
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
SCardUIExit(
|
|
HSCARDUI hSCardUI
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Stops cert. propagation when the user logs out.
|
|
|
|
Arguments:
|
|
lpvParam - Winlogon notification info.
|
|
|
|
--*/
|
|
{
|
|
PTHREAD_DATA pThreadData = (PTHREAD_DATA) hSCardUI;
|
|
|
|
if(NULL != pThreadData->hThread)
|
|
{
|
|
DWORD dwStatus;
|
|
|
|
StopMonitorReaders(pThreadData);
|
|
|
|
dwStatus = WaitForSingleObject(
|
|
pThreadData->hThread,
|
|
INFINITE
|
|
);
|
|
_ASSERT(dwStatus == WAIT_OBJECT_0);
|
|
|
|
CloseHandle(pThreadData->hClose);
|
|
|
|
// now free all data
|
|
for (DWORD dwIndex = 0; dwIndex < pThreadData->dwRemovedReaders; dwIndex++) {
|
|
|
|
FreeReaderData(
|
|
pThreadData,
|
|
pThreadData->ppRemovedReaderData[dwIndex]
|
|
);
|
|
|
|
HeapFree(
|
|
pThreadData->hHeap,
|
|
0,
|
|
pThreadData->ppRemovedReaderData[dwIndex]->pszReaderName
|
|
);
|
|
|
|
HeapFree(
|
|
pThreadData->hHeap,
|
|
0,
|
|
pThreadData->ppRemovedReaderData[dwIndex]
|
|
);
|
|
}
|
|
|
|
HeapFree(pThreadData->hHeap, 0, pThreadData);
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
#ifdef TEST
|
|
#include <conio.h>
|
|
__cdecl
|
|
main(
|
|
int argc,
|
|
char ** argv
|
|
)
|
|
{
|
|
HSCARDUI hScardUi;
|
|
|
|
hScardUi = SCardUIInit(NULL);
|
|
|
|
while (TRUE) {
|
|
|
|
_sleep(1000);
|
|
|
|
if (_kbhit()) {
|
|
|
|
_getch();
|
|
SCardUIExit(hScardUi);
|
|
break;
|
|
}
|
|
}
|
|
|
|
hScardUi = SCardUIInit(NULL);
|
|
|
|
while (TRUE) {
|
|
|
|
_sleep(1000);
|
|
|
|
if (_kbhit()) {
|
|
|
|
SCardUIExit(hScardUi);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|