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.
690 lines
16 KiB
690 lines
16 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1996 - 1999
|
|
|
|
Module Name:
|
|
|
|
ResMgrSt
|
|
|
|
Abstract:
|
|
|
|
This file contains the implementation of threads that monitor
|
|
the status of the smart card resource manager, and notify the
|
|
application when that state has changed via callbacks.
|
|
|
|
Author:
|
|
|
|
Amanda Matlosz 03/18/1998
|
|
|
|
Environment:
|
|
|
|
Win32, C++ w/Exceptions, MFC
|
|
|
|
Revision History:
|
|
|
|
5/28/98 AMatlosz Previously, this thread just watched for the RM to move
|
|
from a 'down' state to an 'up' state. Now it keeps on
|
|
eye on the state to make up for the fact that the other
|
|
two threads who previously monitored status must shut
|
|
themselves down if the RM is up but there are no readers
|
|
available.
|
|
|
|
10/28/98 AMatlosz Added thread to watch for new readers.
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Includes
|
|
//
|
|
#include "stdafx.h"
|
|
#include <winsvc.h>
|
|
#include <winscard.h>
|
|
#include <calaislb.h>
|
|
#include <scEvents.h>
|
|
|
|
#include "SCAlert.h"
|
|
#include "ResMgrSt.h"
|
|
#include "miscdef.h"
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Globals
|
|
//
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CResMgrStatusThrd
|
|
//
|
|
|
|
IMPLEMENT_DYNCREATE(CResMgrStatusThrd, CWinThread)
|
|
|
|
/*++
|
|
|
|
InitInstance
|
|
|
|
Must override init instance to do the loop
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE on build start message loop. FALSE otherwise
|
|
|
|
|
|
Notes:
|
|
|
|
This thread uses two callbacks to inform the caller of its status:
|
|
|
|
WM_SCARD_RESMGR_STATUS -- WPARAM is bool indicating RM status: true == up
|
|
WM_SCARD_RESMGR_EXIT -- indicates thread has been shut down or is shutting
|
|
down.
|
|
--*/
|
|
BOOL CResMgrStatusThrd::InitInstance(void)
|
|
{
|
|
SC_HANDLE schService = NULL;
|
|
SC_HANDLE schSCManager = NULL;
|
|
SERVICE_STATUS ssStatus; // current status of the service
|
|
DWORD dwSts;
|
|
DWORD dwReturn = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Take a short break, then ping the service manager to see if the resource
|
|
// manager is running. If not, wait a long time for it to start. Repeat
|
|
// until the thread has been asked to die.
|
|
//
|
|
|
|
BOOL fContinue = TRUE;
|
|
|
|
while (fContinue)
|
|
{
|
|
try
|
|
{
|
|
if (NULL == schSCManager)
|
|
{
|
|
schSCManager = OpenSCManager(
|
|
NULL, // machine (NULL == local)
|
|
NULL, // database (NULL == default)
|
|
SC_MANAGER_CONNECT); // access required
|
|
if (NULL == schSCManager)
|
|
throw (DWORD)GetLastError();
|
|
}
|
|
if (NULL == schService)
|
|
{
|
|
schService = OpenService(
|
|
schSCManager,
|
|
TEXT("SCardSvr"),
|
|
SERVICE_QUERY_STATUS);
|
|
if (NULL == schService)
|
|
throw (DWORD)GetLastError();
|
|
}
|
|
if (!QueryServiceStatus(schService, &ssStatus))
|
|
throw (DWORD)GetLastError();
|
|
|
|
switch (ssStatus.dwCurrentState)
|
|
{
|
|
case SERVICE_CONTINUE_PENDING:
|
|
case SERVICE_PAUSE_PENDING:
|
|
case SERVICE_PAUSED:
|
|
continue;
|
|
break;
|
|
case SERVICE_START_PENDING:
|
|
case SERVICE_STOP_PENDING:
|
|
case SERVICE_STOPPED:
|
|
dwReturn = SCARD_E_NO_SERVICE;
|
|
break;
|
|
case SERVICE_RUNNING:
|
|
dwReturn = SCARD_S_SUCCESS;
|
|
break;
|
|
default:
|
|
throw (DWORD)SCARD_F_INTERNAL_ERROR;
|
|
}
|
|
}
|
|
|
|
catch (DWORD dwErr)
|
|
{
|
|
_ASSERTE(FALSE); // For debugging.
|
|
if (NULL != schService)
|
|
{
|
|
CloseServiceHandle(schService);
|
|
schService = NULL;
|
|
}
|
|
if (NULL != schSCManager)
|
|
{
|
|
CloseServiceHandle(schSCManager);
|
|
schSCManager = NULL;
|
|
}
|
|
dwReturn = dwErr;
|
|
}
|
|
|
|
catch (...)
|
|
{
|
|
_ASSERTE(FALSE); // For debugging.
|
|
if (NULL != schService)
|
|
{
|
|
CloseServiceHandle(schService);
|
|
schService = NULL;
|
|
}
|
|
if (NULL != schSCManager)
|
|
{
|
|
CloseServiceHandle(schSCManager);
|
|
schSCManager = NULL;
|
|
}
|
|
dwReturn = ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (SCARD_S_SUCCESS == dwReturn)
|
|
{
|
|
// say it's UP!
|
|
::PostMessage(m_hCallbackWnd,
|
|
WM_SCARD_RESMGR_STATUS,
|
|
TRUE,
|
|
0);
|
|
}
|
|
else
|
|
{
|
|
// say it's DOWN!
|
|
::PostMessage(m_hCallbackWnd,
|
|
WM_SCARD_RESMGR_STATUS,
|
|
FALSE,
|
|
0);
|
|
}
|
|
|
|
//
|
|
// Wait for ~30 seconds, continuing on Start or timeout
|
|
// and stopping immediately if the stop event is signaled
|
|
//
|
|
|
|
HANDLE rgHandle[2];
|
|
int nHandle = 2;
|
|
rgHandle[0] = CalaisAccessStartedEvent();
|
|
rgHandle[1] = m_hKillThrd;
|
|
|
|
dwSts = WaitForMultipleObjects(
|
|
nHandle,
|
|
rgHandle,
|
|
FALSE,
|
|
300000);
|
|
if (WAIT_OBJECT_0 != dwSts && WAIT_TIMEOUT != dwSts)
|
|
{
|
|
fContinue = FALSE;
|
|
}
|
|
|
|
CalaisReleaseStartedEvent();
|
|
|
|
}
|
|
|
|
//
|
|
// Clean up & let our caller know that we're shutting down.
|
|
//
|
|
|
|
if (NULL != schService)
|
|
{
|
|
CloseServiceHandle(schService);
|
|
schService = NULL;
|
|
}
|
|
if (NULL != schSCManager)
|
|
{
|
|
CloseServiceHandle(schSCManager);
|
|
schSCManager = NULL;
|
|
}
|
|
CalaisReleaseStartedEvent();
|
|
if (NULL != m_hCallbackWnd)
|
|
{
|
|
::PostMessage(m_hCallbackWnd,
|
|
WM_SCARD_RESMGR_EXIT,
|
|
0, 0);
|
|
}
|
|
|
|
AfxEndThread(0);
|
|
return TRUE; // to make compiler happy
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CRemovalOptionsThrd
|
|
//
|
|
|
|
IMPLEMENT_DYNCREATE(CRemovalOptionsThrd, CWinThread)
|
|
|
|
/*++
|
|
|
|
InitInstance
|
|
|
|
Must override init instance to do the loop
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE on build start message loop. FALSE otherwise
|
|
|
|
|
|
Notes:
|
|
|
|
This thread uses one message to inform the caller of a change in
|
|
the user's removal options:
|
|
|
|
WM_SCARD_REMOPT_CHNG -- re-query smart card removal options
|
|
|
|
--*/
|
|
BOOL CRemovalOptionsThrd::InitInstance(void)
|
|
{
|
|
DWORD dwSts = WAIT_FAILED;
|
|
LONG lResult = ERROR_SUCCESS;
|
|
BOOL fContinue = TRUE;
|
|
int nHandle = 2;
|
|
HANDLE rgHandle[2] = {NULL, NULL};
|
|
rgHandle[1] = m_hKillThrd;
|
|
HKEY hKey = NULL;
|
|
|
|
while (fContinue)
|
|
{
|
|
// open regkey
|
|
lResult = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
szScRemoveOptionKey,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hKey);
|
|
if (ERROR_SUCCESS != lResult)
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// reset/create event
|
|
if (NULL != rgHandle[0])
|
|
{
|
|
if (!ResetEvent(rgHandle[0]))
|
|
{
|
|
CloseHandle(rgHandle[0]);
|
|
rgHandle[0] = NULL;
|
|
}
|
|
}
|
|
if (NULL == rgHandle[0])
|
|
{
|
|
rgHandle[0] = CreateEvent(
|
|
NULL,
|
|
TRUE, // must call ResetEvent() to set non-signaled
|
|
FALSE, // not signaled when it starts
|
|
NULL);
|
|
if (NULL == rgHandle[0])
|
|
{
|
|
// give up!
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
lResult = RegNotifyChangeKeyValue(
|
|
hKey,
|
|
TRUE,
|
|
REG_NOTIFY_CHANGE_LAST_SET,
|
|
rgHandle[0],
|
|
TRUE);
|
|
if (ERROR_SUCCESS != lResult)
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
|
|
dwSts = WaitForMultipleObjects(
|
|
nHandle,
|
|
rgHandle,
|
|
FALSE,
|
|
INFINITE);
|
|
|
|
if (WAIT_OBJECT_0 == dwSts)
|
|
{
|
|
// announce the change
|
|
::PostMessage(m_hCallbackWnd,
|
|
WM_SCARD_REMOPT_CHNG,
|
|
0, 0);
|
|
}
|
|
else if (WAIT_OBJECT_0+1 == dwSts || WAIT_FAILED == dwSts)
|
|
{
|
|
// Time for thread to quit
|
|
fContinue = FALSE;
|
|
}
|
|
else
|
|
{
|
|
_ASSERTE(WAIT_TIMEOUT == dwSts);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// Clean up & let our caller know that we're shutting down.
|
|
//
|
|
ErrorExit:
|
|
|
|
if (NULL != hKey)
|
|
{
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
if (NULL != rgHandle[0])
|
|
{
|
|
CloseHandle(rgHandle[0]);
|
|
}
|
|
|
|
if (NULL != m_hCallbackWnd)
|
|
{
|
|
// announce thread's exit
|
|
::PostMessage(m_hCallbackWnd,
|
|
WM_SCARD_REMOPT_EXIT,
|
|
0, 0);
|
|
}
|
|
|
|
AfxEndThread(0);
|
|
return TRUE; // to make compiler happy
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CNewReaderThrd
|
|
//
|
|
|
|
IMPLEMENT_DYNCREATE(CNewReaderThrd, CWinThread)
|
|
|
|
/*++
|
|
|
|
InitInstance
|
|
|
|
Must override init instance to do the loop
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE on build start message loop. FALSE otherwise
|
|
|
|
|
|
Notes:
|
|
|
|
This thread uses one callback to inform the caller of an addition to
|
|
the active reader list.
|
|
|
|
WM_SCARD_NEWREADER -- indicates that Calais reports a reader just added
|
|
|
|
--*/
|
|
BOOL CNewReaderThrd::InitInstance(void)
|
|
{
|
|
if (NULL == m_hCallbackWnd)
|
|
{
|
|
_ASSERTE(FALSE);
|
|
return TRUE; // for compiler
|
|
}
|
|
|
|
DWORD dwSts = 0;
|
|
BOOL fContinue = TRUE;
|
|
|
|
while (fContinue)
|
|
{
|
|
HANDLE rgHandle[2];
|
|
int nHandle = 2;
|
|
rgHandle[0] = CalaisAccessNewReaderEvent();
|
|
rgHandle[1] = m_hKillThrd;
|
|
|
|
dwSts = WaitForMultipleObjects(
|
|
nHandle,
|
|
rgHandle,
|
|
FALSE,
|
|
300000);
|
|
|
|
if (WAIT_OBJECT_0 == dwSts)
|
|
{
|
|
// a new reader event happened! Fire off the notice
|
|
::PostMessage(m_hCallbackWnd,
|
|
WM_SCARD_NEWREADER,
|
|
TRUE,
|
|
0);
|
|
}
|
|
else if (WAIT_OBJECT_0+1 == dwSts || WAIT_FAILED == dwSts)
|
|
{
|
|
// Time for thread to quit
|
|
fContinue = FALSE;
|
|
}
|
|
else
|
|
{
|
|
_ASSERTE(WAIT_TIMEOUT == dwSts);
|
|
}
|
|
|
|
CalaisReleaseNewReaderEvent();
|
|
}
|
|
|
|
if (NULL != m_hCallbackWnd)
|
|
{
|
|
::PostMessage(m_hCallbackWnd,
|
|
WM_SCARD_NEWREADER_EXIT,
|
|
0, 0);
|
|
}
|
|
|
|
AfxEndThread(0);
|
|
return TRUE; // to make compiler happy
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CCardStatusThrd
|
|
//
|
|
|
|
IMPLEMENT_DYNCREATE(CCardStatusThrd, CWinThread)
|
|
|
|
/*++
|
|
|
|
InitInstance
|
|
|
|
Must override init instance to do the loop
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE on build start message loop. FALSE otherwise
|
|
|
|
|
|
Notes:
|
|
|
|
This thread uses one callback to inform the caller of a change in
|
|
smart card status -- a card is available, no cards are available,
|
|
or a card has been idle for >30 seconds.
|
|
|
|
WM_SCARD_CARDSTATUS -- indicates a new card status
|
|
WM_SCARD_CARDSTATUS_EXIT -- indicates imminent thread death.
|
|
|
|
--*/
|
|
BOOL CCardStatusThrd::InitInstance(void)
|
|
{
|
|
LONG lResult = SCardEstablishContext(SCARD_SCOPE_USER,NULL,NULL,&m_hCtx);
|
|
|
|
if (SCARD_S_SUCCESS != lResult)
|
|
{
|
|
CString str;
|
|
str.Format(_T("CCardStatusThrd:: SCardEstablishContext returned 0x%x."), lResult);
|
|
}
|
|
|
|
BOOL fContinue = TRUE;
|
|
LPTSTR szReaders = NULL;
|
|
LPCTSTR pchReader = NULL;
|
|
DWORD dwReadersLen = SCARD_AUTOALLOCATE;
|
|
SCARD_READERSTATE rgReaderStates[MAXIMUM_SMARTCARD_READERS];
|
|
int nIndex = 0, nCnReaders = 0;
|
|
BOOL fLogonLock = (NULL != m_pstrLogonReader && !m_pstrLogonReader->IsEmpty());
|
|
|
|
lResult = SCardListReaders(
|
|
m_hCtx,
|
|
SCARD_ALL_READERS,
|
|
(LPTSTR)&szReaders,
|
|
&dwReadersLen
|
|
);
|
|
|
|
if(SCARD_S_SUCCESS != lResult ||
|
|
(0 == dwReadersLen || NULL == szReaders || 0 == *szReaders) )
|
|
{
|
|
fContinue = FALSE;
|
|
}
|
|
|
|
if (fContinue)
|
|
{
|
|
// use the list of readers to build a readerstate array
|
|
for (nIndex = 0, pchReader = szReaders;
|
|
nIndex < MAXIMUM_SMARTCARD_READERS && 0 != *pchReader;
|
|
nIndex++)
|
|
{
|
|
rgReaderStates[nIndex].szReader = pchReader;
|
|
rgReaderStates[nIndex].dwCurrentState = SCARD_STATE_UNAWARE;
|
|
pchReader += lstrlen(pchReader)+1;
|
|
}
|
|
nCnReaders = nIndex;
|
|
}
|
|
|
|
while (fContinue)
|
|
{
|
|
UINT uState = (UINT)k_State_NoCard;
|
|
|
|
lResult = SCardGetStatusChange(
|
|
m_hCtx,
|
|
10000, // IN DWORD dwTimeout (10 seconds)
|
|
rgReaderStates, // IN OUT LPSCARD_READERSTATE
|
|
nCnReaders // IN DWORD cReaders
|
|
);
|
|
|
|
// IF return is success, determine if there are any cards inserted
|
|
// if YES, send a messge to notfywnd saying "card in"
|
|
// if NO, send a message to notfywnd saying "no card"
|
|
if (SCARD_S_SUCCESS == lResult)
|
|
{
|
|
// Determine if
|
|
// (a) any card is present in the system and
|
|
// (b) if each idle card has ceased to be idle or present
|
|
BOOL fIdle = FALSE;
|
|
|
|
for(nIndex=0; nIndex < nCnReaders; nIndex++)
|
|
{
|
|
if (rgReaderStates[nIndex].dwEventState & SCARD_STATE_PRESENT)
|
|
{
|
|
uState = (UINT)k_State_CardAvailable;
|
|
}
|
|
|
|
if (k_State_CardIdle == (UINT_PTR)(rgReaderStates[nIndex].pvUserData))
|
|
{
|
|
if ( !(rgReaderStates[nIndex].dwEventState & SCARD_STATE_PRESENT) ||
|
|
(rgReaderStates[nIndex].dwEventState & SCARD_STATE_INUSE) )
|
|
{
|
|
rgReaderStates[nIndex].pvUserData = NULL;
|
|
}
|
|
else
|
|
{
|
|
fIdle = TRUE;
|
|
}
|
|
}
|
|
|
|
rgReaderStates[nIndex].dwCurrentState = rgReaderStates[nIndex].dwEventState;
|
|
}
|
|
|
|
if (fIdle) uState = k_State_CardIdle;
|
|
|
|
}
|
|
// IF return indicates timeout, determine if any cards are idle.
|
|
else if (SCARD_E_TIMEOUT == lResult)
|
|
{
|
|
BOOL fIdle = FALSE;
|
|
|
|
// is there an idle card?
|
|
for(nIndex=0; nIndex < nCnReaders; nIndex++)
|
|
{
|
|
if (rgReaderStates[nIndex].dwEventState & SCARD_STATE_PRESENT)
|
|
{
|
|
uState = k_State_CardAvailable;
|
|
|
|
if (!(rgReaderStates[nIndex].dwEventState & SCARD_STATE_INUSE))
|
|
{
|
|
// card used for logon & logoff or lock is not considered idle
|
|
if (!fLogonLock ||
|
|
0 != m_pstrLogonReader->Compare(rgReaderStates[nIndex].szReader))
|
|
{
|
|
rgReaderStates[nIndex].pvUserData = ULongToPtr(k_State_CardIdle);
|
|
fIdle = TRUE;
|
|
}
|
|
}
|
|
rgReaderStates[nIndex].dwCurrentState = rgReaderStates[nIndex].dwEventState;
|
|
}
|
|
}
|
|
|
|
// there's an overdue idle card! Fire off the notification.
|
|
if (fIdle) uState = k_State_CardIdle;
|
|
}
|
|
else
|
|
{
|
|
fContinue = FALSE;
|
|
}
|
|
|
|
// update list of readers w/idle cards
|
|
m_csLock.Lock();
|
|
{
|
|
m_paIdleList->RemoveAll();
|
|
|
|
for(nIndex=0; nIndex < nCnReaders; nIndex++)
|
|
{
|
|
if (k_State_CardIdle == (UINT_PTR)rgReaderStates[nIndex].pvUserData)
|
|
{
|
|
m_paIdleList->Add(rgReaderStates[nIndex].szReader);
|
|
}
|
|
}
|
|
}
|
|
m_csLock.Unlock();
|
|
|
|
// inform caller
|
|
if (NULL != m_hCallbackWnd)
|
|
{
|
|
::PostMessage(m_hCallbackWnd,
|
|
WM_SCARD_CARDSTATUS,
|
|
uState,
|
|
0);
|
|
}
|
|
|
|
}
|
|
|
|
if (NULL != m_hCallbackWnd)
|
|
{
|
|
::PostMessage(m_hCallbackWnd,
|
|
WM_SCARD_CARDSTATUS_EXIT,
|
|
0, 0);
|
|
}
|
|
|
|
AfxEndThread(0);
|
|
return TRUE; // to make compiler happy
|
|
}
|
|
|
|
void CCardStatusThrd::CopyIdleList(CStringArray* paStr)
|
|
{
|
|
if (NULL == paStr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_csLock.Lock();
|
|
{
|
|
paStr->Copy(*m_paIdleList);
|
|
}
|
|
m_csLock.Unlock();
|
|
}
|