/*++ 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 #include #include #include #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(); }