/*++ Copyright (C) Microsoft Corporation, 1996 - 1999 Module Name: WinSCard Abstract: This module supplies the API for the Calais Smartcard Service Manager. The Calais Service Manager does the work of coordinating the protocols, readers, drivers, and smartcards on behalf of the application. The following services are provided as part of a library to simplify access to the Service Manager. These routines are the documented, exposed APIs. These routines merely package the requests and forward them to the Calais Service Manager, allowing the actual implementation of Calais to vary over time. At no time does the API library make security decisions. All security-related functions must be performed by the Service Manager, running in its own address space, or in the operating system kernel. However, some utility routines may be implemented in the API library for speed, as long as they do not involve security decisions. Author: Doug Barlow (dbarlow) 10/23/1996 Environment: Win32, C++ w/ Exceptions Notes: ?Notes? --*/ #define __SUBROUTINE__ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #include #include #include #include #include "client.h" #include "redirect.h" #include "winsta.h" const SCARD_IO_REQUEST g_rgSCardT0Pci = { SCARD_PROTOCOL_T0, sizeof(SCARD_IO_REQUEST) }, g_rgSCardT1Pci = { SCARD_PROTOCOL_T1, sizeof(SCARD_IO_REQUEST) }, g_rgSCardRawPci = { SCARD_PROTOCOL_RAW, sizeof(SCARD_IO_REQUEST) }; CHandleList * g_phlContexts = NULL, * g_phlReaders = NULL; const WCHAR g_wszBlank[] = L"\000"; HINSTANCE g_hInst; HANDLE g_hSessionChangeEvent = NULL; HANDLE g_hSessionChangeCallbackHandle = NULL; ULONG_PTR g_SessionChangeID = 0; CRITICAL_SECTION g_RegisterForSessionChangeNoticationsCS; BOOL g_fRegisteredForSessionChangeNotications = FALSE; DWORD g_dwTimerCallbacksMade = 0; DWORD g_dwClientCount = 0; HANDLE g_hTimerEvent = NULL; HANDLE g_hWaitTimerEventCallbackHandle = NULL; CRITICAL_SECTION g_SafeCreateHandleCS; CRITICAL_SECTION g_SetStartedEventCS; CRITICAL_SECTION g_RegisterForStoppedEventCS; CRITICAL_SECTION g_DllMainCS; HANDLE g_hWaitForStartedCallbackHandle = NULL; HANDLE g_hUnifiedStartedEvent = NULL; HANDLE g_hWaitForStoppedCallbackHandle = NULL; CRITICAL_SECTION g_TermSrvEnabledCS; BOOL g_fTermSrvEnableChecked = FALSE; BOOL g_bTermSrvEnabled = FALSE; BOOL g_fInClientRundown = FALSE; BOOL g_fInDllMain = FALSE; BOOL SetStartedEventWhenSCardSubsytemIsStarted(BOOL fUseLocal); void PrintDebugString(LPSTR szString, DWORD dwValue) { char szOutString[256]; sprintf(szOutString, "%d.%d> WINSCARD: %s - %lx\n", GetCurrentProcessId(), GetCurrentThreadId(), szString, dwValue); //OutputDebugStringA(szOutString); } // //////////////////////////////////////////////////////////////////////////////// // // Mark all current held contexts bad // WINSCARDAPI void WINAPI MarkContextsAsBad(BOOL fCancel) { try { g_phlContexts->MarkContentAsBad(fCancel); g_phlReaders->MarkContentAsBad(FALSE); } catch (...) { } } // //////////////////////////////////////////////////////////////////////////////// // // DllMain // BOOL WINAPI DllMain( HMODULE hInstDLL, DWORD fdwReason, LPVOID lpvReserved) { DWORD dw; BOOL f; g_fInDllMain = TRUE; switch (fdwReason) { case DLL_PROCESS_ATTACH: g_hInst = hInstDLL; try { dw = 0;; InitializeCriticalSection(&g_RegisterForSessionChangeNoticationsCS); dw++; InitializeCriticalSection(&g_SafeCreateHandleCS); dw++; InitializeCriticalSection(&g_SetStartedEventCS); dw++; InitializeCriticalSection(&g_TermSrvEnabledCS); dw++; InitializeCriticalSection(&g_RegisterForStoppedEventCS); dw++; InitializeCriticalSection(&g_DllMainCS); } catch(...) { if (dw >= 1) { DeleteCriticalSection(&g_RegisterForSessionChangeNoticationsCS); } if (dw >= 2) { DeleteCriticalSection(&g_SafeCreateHandleCS); } if (dw >= 3) { DeleteCriticalSection(&g_SetStartedEventCS); } if (dw >= 4) { DeleteCriticalSection(&g_TermSrvEnabledCS); } if (dw >= 5) { DeleteCriticalSection(&g_RegisterForStoppedEventCS); } g_fInDllMain = FALSE; return FALSE; } g_phlContexts = new CHandleList(CONTEXT_HANDLE_ID); g_phlReaders = new CHandleList(READER_HANDLE_ID); if ((NULL == g_phlContexts) || (NULL == g_phlReaders) || g_phlContexts->InitFailed() || g_phlReaders->InitFailed()) { if (g_phlContexts) { delete g_phlContexts; g_phlContexts = NULL; } if (g_phlReaders) { delete g_phlReaders; g_phlReaders = NULL; } DeleteCriticalSection(&g_RegisterForSessionChangeNoticationsCS); DeleteCriticalSection(&g_SafeCreateHandleCS); DeleteCriticalSection(&g_SetStartedEventCS); DeleteCriticalSection(&g_TermSrvEnabledCS); DeleteCriticalSection(&g_RegisterForStoppedEventCS); DeleteCriticalSection(&g_DllMainCS); g_fInDllMain = FALSE; return FALSE; } break; case DLL_PROCESS_DETACH: // // Clean up the registered waits if they are still outstanding // HANDLE hCallbackToUnregister; EnterCriticalSection(&g_DllMainCS); g_fInDllMain = TRUE; LeaveCriticalSection(&g_DllMainCS); hCallbackToUnregister = InterlockedExchangePointer( &g_hWaitTimerEventCallbackHandle, NULL); if (hCallbackToUnregister != NULL) { UnregisterWaitEx(hCallbackToUnregister, INVALID_HANDLE_VALUE); } hCallbackToUnregister = InterlockedExchangePointer( &g_hSessionChangeCallbackHandle, NULL); if (hCallbackToUnregister != NULL) { UnregisterWaitEx(hCallbackToUnregister, INVALID_HANDLE_VALUE); } hCallbackToUnregister = InterlockedExchangePointer( &g_hWaitForStoppedCallbackHandle, NULL); if (hCallbackToUnregister != NULL) { UnregisterWaitEx(hCallbackToUnregister, INVALID_HANDLE_VALUE); } hCallbackToUnregister = InterlockedExchangePointer( &g_hWaitForStartedCallbackHandle, NULL); if (hCallbackToUnregister != NULL) { UnregisterWaitEx(hCallbackToUnregister, INVALID_HANDLE_VALUE); } // // The third parameter, lpvReserved, passed to DllMain // is NULL for FreeLibrary and non-NULL for ProcessExit. // Only clean up for FreeLibrary // if (lpvReserved == NULL) { if (g_hSessionChangeEvent != NULL) { CloseHandle(g_hSessionChangeEvent); } if (g_hTimerEvent != NULL) { CloseHandle(g_hTimerEvent); } // // Cleanup CritSecs. // DeleteCriticalSection(&g_RegisterForSessionChangeNoticationsCS); DeleteCriticalSection(&g_SafeCreateHandleCS); DeleteCriticalSection(&g_SetStartedEventCS); DeleteCriticalSection(&g_TermSrvEnabledCS); DeleteCriticalSection(&g_RegisterForStoppedEventCS); DeleteCriticalSection(&g_DllMainCS); try { if (g_phlReaders) { g_fInClientRundown = TRUE; CHandle * pReader = g_phlReaders->GetFirst(); while (pReader != NULL) { try { ((CReaderContext *) pReader)->EndTransaction(SCARD_LEAVE_CARD_FORCE); } catch (...){} pReader = g_phlReaders->GetNext(pReader); } delete g_phlReaders; g_phlReaders = NULL; } if (g_phlContexts) { delete g_phlContexts; g_phlContexts = NULL; } } catch (...) { } ReleaseStartedEvent(); ReleaseStoppedEvent(); if (g_hUnifiedStartedEvent != NULL) { CloseHandle(g_hUnifiedStartedEvent); } } break; } f = RedirDllMain(hInstDLL, fdwReason, lpvReserved); g_fInDllMain = FALSE; return(f); } // //////////////////////////////////////////////////////////////////////////////// // // SessionChangeCallback // VOID CALLBACK SessionChangeCallback( PVOID lpParameter, BOOLEAN TimerOrWaitFired) { WTS_CONNECTSTATE_CLASS *pConnectState = NULL; BOOL fConnected = FALSE; DWORD dw; HANDLE hCallbackToUnregister; EnterCriticalSection(&g_DllMainCS); if (g_fInDllMain) { LeaveCriticalSection(&g_DllMainCS); return; } // // If we are registered for local smart card stopped callbacks then // unregister // hCallbackToUnregister = InterlockedExchangePointer( &g_hWaitForStoppedCallbackHandle, NULL); if (hCallbackToUnregister != NULL) { UnregisterWait(hCallbackToUnregister); } // // Detect whether we are in a connected state or not // if (!WTSQuerySessionInformation( WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSConnectState, (LPTSTR *) &pConnectState, &dw)) { //OutputDebugString("WINSCARD: SessionChangeCallback: WTSQuerySessionInformation failed!\n"); LeaveCriticalSection(&g_DllMainCS); return; } fConnected = ( (*pConnectState == WTSActive) || (*pConnectState == WTSConnected)); WTSFreeMemory(pConnectState); // // If there is an outstanding wait, it may be waiting on the wrong event, // so cancel the wait here. When there is a connect event the // SetStartedEventWhenSCardSubsytemIsStarted() API will be called again // to wait on the correct event // hCallbackToUnregister = InterlockedExchangePointer( &g_hWaitForStartedCallbackHandle, NULL); if (hCallbackToUnregister != NULL) { UnregisterWait(hCallbackToUnregister); } if (!fConnected) { // // Make sure the unified started event isn't set since we // are now in a disconnected state // //OutputDebugString("WINSCARD: SessionChangeCallback: Disconnect\n"); ResetEvent(g_hUnifiedStartedEvent); // // Only mark the contexts bad if we're not in a service, because we // know we're not redirecting if we're in a service. // if (FALSE == InAService()) MarkContextsAsBad(TRUE); } else { //OutputDebugString("WINSCARD: SessionChangeCallback: Reconnect\n"); SetRedirectDisabledValue(); SetStartedEventWhenSCardSubsytemIsStarted(FALSE); } LeaveCriticalSection(&g_DllMainCS); } // //////////////////////////////////////////////////////////////////////////////// // // TimerCallback // VOID CALLBACK TimerCallback( PVOID lpParameter, BOOLEAN TimerOrWaitFired ) { HANDLE h = NULL; BOOL fUnregister = TRUE; __try { EnterCriticalSection(&g_RegisterForSessionChangeNoticationsCS); } __except(EXCEPTION_EXECUTE_HANDLER) { return; } if (g_dwClientCount == 0) { LeaveCriticalSection(&g_RegisterForSessionChangeNoticationsCS); return; } // // The check for !g_fInDllMain is only to reduce the window in which a deadlock // can occur. The deadlock condition is as follows: // // 1) A thread enters winscard's DllMain (the thread in DllMain is of course holding the loader lock) // 2) TimerCallback gets called // 3) winscard's DllMain calls UnregisterWaitEx(INVALID_HANDLE_VALUE) for TimerCallback, which will // block until the TimerCallback callback completes. // 4) TimerCallback makes the WinStationRegisterNotificationEvent call which may try to load or unload // a DLL, which of course requires the loader lock. // 5) WHAMO!!! // if (!g_fInDllMain) { if (!g_fRegisteredForSessionChangeNotications) { if (WinStationRegisterNotificationEvent( g_hSessionChangeEvent, &g_SessionChangeID, NOTIFY_FOR_THIS_SESSION, WTS_CONSOLE_CONNECT_MASK | WTS_CONSOLE_DISCONNECT_MASK | WTS_REMOTE_CONNECT_MASK | WTS_REMOTE_DISCONNECT_MASK)) { g_fRegisteredForSessionChangeNotications = TRUE; } else if (++g_dwTimerCallbacksMade < 60) // 60 attemps at 10 seconds each will be ten minutes { // // We still haven't succusfully registered for session change notifications and we haven't // exausted our max retries, so don't unregister the callback yet. // fUnregister = FALSE; } } } else { fUnregister = FALSE; } if (fUnregister) { h = InterlockedExchangePointer(&g_hWaitTimerEventCallbackHandle, NULL); if (h != NULL) { UnregisterWait(h); } } LeaveCriticalSection(&g_RegisterForSessionChangeNoticationsCS); } // //////////////////////////////////////////////////////////////////////////////// // // RegisterForSessionChangeNotifications // BOOL RegisterForSessionChangeNotifications() { BOOL fRet = TRUE; // // Make sure we only register for session change notifications once // __try { EnterCriticalSection(&g_RegisterForSessionChangeNoticationsCS); } __except(EXCEPTION_EXECUTE_HANDLER) { return FALSE; } g_dwClientCount++; // // If the global session change event and callback aren't setup then do that // if (g_hSessionChangeEvent == NULL) { g_hSessionChangeEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (g_hSessionChangeEvent == NULL) { goto ErrorReturn; } if (!RegisterWaitForSingleObject( &g_hSessionChangeCallbackHandle, g_hSessionChangeEvent, SessionChangeCallback, 0, INFINITE, WT_EXECUTEDEFAULT)) { CloseHandle(g_hSessionChangeEvent); g_hSessionChangeEvent = NULL; goto ErrorReturn; } } // // Register with the WTS subsystem for change notifications // if (!g_fRegisteredForSessionChangeNotications) { if (WinStationRegisterNotificationEvent( g_hSessionChangeEvent, &g_SessionChangeID, NOTIFY_FOR_THIS_SESSION, WTS_CONSOLE_CONNECT_MASK | WTS_CONSOLE_DISCONNECT_MASK | WTS_REMOTE_CONNECT_MASK | WTS_REMOTE_DISCONNECT_MASK)) { g_fRegisteredForSessionChangeNotications = TRUE; } else if (g_hWaitTimerEventCallbackHandle == NULL) { //OutputDebugString("WINSCARD: RegisterForSessionChangeNotifications - WinStationRegisterNotificationEvent failed!!\n"); // // Since the WinStationRegisterNotificationEvent call failed, TermSrv probably // isn't ready, so just register for a callback and try to register again later // g_dwTimerCallbacksMade = 0; if (g_hTimerEvent == NULL) { g_hTimerEvent = CreateEvent(NULL, TRUE, FALSE, NULL); } if (!RegisterWaitForSingleObject( &g_hWaitTimerEventCallbackHandle, g_hTimerEvent, TimerCallback, 0, 10000, WT_EXECUTEDEFAULT)) { goto ErrorReturn; } } } Return: LeaveCriticalSection(&g_RegisterForSessionChangeNoticationsCS); return fRet; ErrorReturn: g_dwClientCount--; fRet = FALSE; goto Return; } // //////////////////////////////////////////////////////////////////////////////// // // UnRegisterForSessionChangeNotifications // BOOL UnRegisterForSessionChangeNotifications() { BOOL fRet = TRUE; HANDLE h; // // Make sure we only unregister if there are no more clients // __try { EnterCriticalSection(&g_RegisterForSessionChangeNoticationsCS); } __except(EXCEPTION_EXECUTE_HANDLER) { return FALSE; } if (g_dwClientCount == 0) { fRet = FALSE; goto Return; } else if (g_dwClientCount == 1) { g_dwClientCount = 0; // // If the timer callback is going then kill it // h = InterlockedExchangePointer(&g_hWaitTimerEventCallbackHandle, NULL); if (h != NULL) { UnregisterWait(h); } // // If we are registered then unregister // if (g_fRegisteredForSessionChangeNotications) { WinStationUnRegisterNotificationEvent(g_SessionChangeID); g_SessionChangeID = 0; } g_fRegisteredForSessionChangeNotications = FALSE; } else { g_dwClientCount--; } Return: LeaveCriticalSection(&g_RegisterForSessionChangeNoticationsCS); return fRet; } // //////////////////////////////////////////////////////////////////////////////// // // SetStartedEventAfterTestingConnectedState // BOOL SetStartedEventAfterTestingConnectedState() { BOOL fRet = TRUE; WTS_CONNECTSTATE_CLASS *pConnectState = NULL; BOOL fConnected = FALSE; DWORD dw; BOOL fUnregister = FALSE; // // Register for connect/disconnect notifications from the WTS subsystem // if (!RegisterForSessionChangeNotifications()) { //OutputDebugString("WINSCARD: SetStartedEventAfterTestingConnectedState - RegisterForSessionChangeNotifications failed!!\n"); goto ErrorReturn; } fUnregister = TRUE; // // Detect whether we are in a connected state or not // if (!WTSQuerySessionInformation( WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSConnectState, (LPTSTR *) &pConnectState, &dw)) { //OutputDebugString("WINSCARD: SetStartedEventAfterTestingConnectedState - WTSQuerySessionInformation failed!!\n"); // // Since that failed, TermSrv is probably not started, so just go local // if (!SetStartedEventWhenSCardSubsytemIsStarted(TRUE)) { goto ErrorReturn; } goto Return; } fConnected = ( (*pConnectState == WTSActive) || (*pConnectState == WTSConnected)); WTSFreeMemory(pConnectState); // // If we are connected, then call SetStartedEventWhenSCardSubsytemIsStarted // which will detect whether we are in local or redirect mode and subsequently // wait on the appropriate smart card subsystem (the local or the remote). // Otherwise, we are not connected, so do nothing since // SetStartedEventWhenSCardSubsytemIsStarted will be called once when we get // a connnected notification from the WTS subsystem // if (fConnected) { //OutputDebugString("WINSCARD: SetStartedEventAfterTestingConnectedState - Connected!!\n"); if (!SetStartedEventWhenSCardSubsytemIsStarted(FALSE)) { goto ErrorReturn; } } else { //OutputDebugString("WINSCARD: SetStartedEventAfterTestingConnectedState - NOT Connected!!\n"); } Return: return fRet; ErrorReturn: if (fUnregister) { UnRegisterForSessionChangeNotifications(); } fRet = FALSE; goto Return; } // //////////////////////////////////////////////////////////////////////////////// // // TermSrvEnabled // BOOL TermSrvEnabled() { BOOL fRet = TRUE; SC_HANDLE schSCM = NULL; SC_HANDLE schService = NULL; LPQUERY_SERVICE_CONFIG pServiceConfig = NULL; DWORD cbServiceConfig = 0; // // Make sure we only do this once // __try { EnterCriticalSection(&g_TermSrvEnabledCS); } __except(EXCEPTION_EXECUTE_HANDLER) { return FALSE; } if (g_fTermSrvEnableChecked) { goto Return; } // // Open the service control manager // schSCM = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT ); if(schSCM == NULL) { //OutputDebugString("WINSCARD: TermSrvEnabled - OpenSCManagerW failed!!\n"); goto Return; } // // open the "Terminal Services" service so we can query it's configuration // schService = OpenServiceW(schSCM, L"TermService", SERVICE_QUERY_CONFIG); if (schService == NULL) { //OutputDebugString("WINSCARD: TermSrvEnabled - OpenServiceW failed!!\n"); goto Return; } // // Get and check the services configuration // QueryServiceConfig(schService, NULL, 0, &cbServiceConfig); if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { pServiceConfig = (LPQUERY_SERVICE_CONFIG) HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, cbServiceConfig); if (pServiceConfig == NULL) { goto Return; } if (QueryServiceConfig(schService, pServiceConfig, cbServiceConfig, &cbServiceConfig)) { if(pServiceConfig->dwStartType == SERVICE_DISABLED) { g_fTermSrvEnableChecked = TRUE; goto Return; } } else { //OutputDebugString("WINSCARD: TermSrvEnabled - QueryServiceConfig failed - 2!!\n"); goto Return; } } else { //OutputDebugString("WINSCARD: TermSrvEnabled - QueryServiceConfig failed!!\n"); goto Return; } g_fTermSrvEnableChecked = TRUE; g_bTermSrvEnabled = TRUE; Return: LeaveCriticalSection(&g_TermSrvEnabledCS); if (pServiceConfig != NULL) { HeapFree(GetProcessHeap(), 0, pServiceConfig); } if (schService != NULL) { CloseServiceHandle(schService); } if (schSCM != NULL) { CloseServiceHandle(schSCM); } return g_bTermSrvEnabled; } // //////////////////////////////////////////////////////////////////////////////// // // SafeCreateEvent // BOOL SafeCreateEvent( HANDLE *phEvent) { BOOL fRet = TRUE; __try { EnterCriticalSection(&g_SafeCreateHandleCS); } __except(EXCEPTION_EXECUTE_HANDLER) { return (FALSE); } if (*phEvent != NULL) { goto Return; } *phEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (*phEvent == NULL) { fRet = FALSE; } Return: LeaveCriticalSection(&g_SafeCreateHandleCS); return (fRet); } // //////////////////////////////////////////////////////////////////////////////// // // SmartCardSubsystemStoppedCallback and RegisterForStoppedCallback // VOID CALLBACK SmartCardSubsystemStoppedCallback( PVOID lpParameter, BOOLEAN TimerOrWaitFired ) { HANDLE hCallbackToUnregister; EnterCriticalSection(&g_DllMainCS); if (g_fInDllMain) { LeaveCriticalSection(&g_DllMainCS); return; } //OutputDebugString("WINSCARD: SmartCardSubsystemStoppedCallback - resetting event \n"); ResetEvent(g_hUnifiedStartedEvent); SetStartedEventWhenSCardSubsytemIsStarted(TRUE); hCallbackToUnregister = InterlockedExchangePointer( &g_hWaitForStoppedCallbackHandle, NULL); if (hCallbackToUnregister != NULL) { UnregisterWait(hCallbackToUnregister); } LeaveCriticalSection(&g_DllMainCS); } BOOL RegisterForStoppedCallback() { BOOL fRet = TRUE; BOOL fEnteredCS = FALSE; HANDLE h = NULL; h = AccessStoppedEvent(); if (h == NULL) { goto ErrorReturn; } __try { EnterCriticalSection(&g_RegisterForStoppedEventCS); } __except(EXCEPTION_EXECUTE_HANDLER) { goto ErrorReturn; } fEnteredCS = TRUE; if (g_hWaitForStoppedCallbackHandle != NULL) { goto Return; } if (!RegisterWaitForSingleObject( &g_hWaitForStoppedCallbackHandle, h, SmartCardSubsystemStoppedCallback, 0, INFINITE, WT_EXECUTEONLYONCE)) { goto ErrorReturn; } Return: if (fEnteredCS) { LeaveCriticalSection(&g_RegisterForStoppedEventCS); } return fRet; ErrorReturn: fRet = FALSE; goto Return; } // //////////////////////////////////////////////////////////////////////////////// // // SmartCardSubsystemStartedCallback // // This callback is fired when the smart card subsystem sets its started event. // NOTE: Both local and remote scard subsystems being started will fire this // same callback // VOID CALLBACK SmartCardSubsystemStartedCallback( PVOID lpParameter, BOOLEAN TimerOrWaitFired ) { BOOL fLocal = (lpParameter == (PVOID) 1); HANDLE hCallbackToUnregister; EnterCriticalSection(&g_DllMainCS); if (g_fInDllMain) { LeaveCriticalSection(&g_DllMainCS); return; } //OutputDebugString("WINSCARD: SmartCardSubsystemStartedCallback - setting event \n"); SetEvent(g_hUnifiedStartedEvent); if (fLocal) { RegisterForStoppedCallback(); } hCallbackToUnregister = InterlockedExchangePointer( &g_hWaitForStartedCallbackHandle, NULL); if (hCallbackToUnregister != NULL) { UnregisterWait(hCallbackToUnregister); } LeaveCriticalSection(&g_DllMainCS); } // //////////////////////////////////////////////////////////////////////////////// // // SetStartedEventWhenSCardSubsytemIsStarted // BOOL SetStartedEventWhenSCardSubsytemIsStarted( BOOL fUseLocal) { HANDLE h = NULL; BOOL fRet = TRUE; BOOL fEnteredCritSec = FALSE; BOOL fLocal = FALSE; // // If termsrv is enabled and we are in redirect mode then get the // started event that corresponds to the remoted scard subsystem being // available, otherwise, get the started event of the local scard // resource manager // if (!fUseLocal && TermSrvEnabled() && InTSRedirectMode()) { //OutputDebugString("WINSCARD: SetStartedEventWhenSCardSubsytemIsStarted REDIRECT\n"); // // if redirect is disabled, then just get out // if (TS_REDIRECT_DISABLED) { goto Return; } if (TS_REDIRECT_READY) { h = pfnSCardAccessStartedEvent(); } else { goto ErrorReturn; } } else { //OutputDebugString("WINSCARD: SetStartedEventWhenSCardSubsytemIsStarted LOCAL\n"); h = AccessStartedEvent(); fLocal = TRUE; } if (h == NULL) { goto ErrorReturn; } // // If the event is already set, then just set the event returned // to the caller and return // if (WAIT_OBJECT_0 == WaitForSingleObject(h, 0)) { //OutputDebugString("WINSCARD: SetStartedEventWhenSCardSubsytemIsStarted SETTING EVENT\n"); SetEvent(g_hUnifiedStartedEvent); if (fLocal) { RegisterForStoppedCallback(); } goto Return; } // // The event wasn't set so we need to register a callback which // fires when the scard subsystem is started. // // Make sure only one callback is registered. // __try { EnterCriticalSection(&g_SetStartedEventCS); } __except(EXCEPTION_EXECUTE_HANDLER) { goto ErrorReturn; } fEnteredCritSec = TRUE; // // There is already a callback registered, so just get out // if (g_hWaitForStartedCallbackHandle != NULL) { goto Return; } // // Register for the callback. The callback is fired when the smart // card resource manager event is set (either the remote or the local // subsystem event, based on whether this is a redirected session or not). // if (!RegisterWaitForSingleObject( &g_hWaitForStartedCallbackHandle, h, SmartCardSubsystemStartedCallback, (fLocal ? ((PVOID) 1) : ((PVOID) 0)), // tell the callback whether this is local or not INFINITE, WT_EXECUTEONLYONCE)) { goto ErrorReturn; } Return: if (fEnteredCritSec) { LeaveCriticalSection(&g_SetStartedEventCS); } return (fRet); ErrorReturn: fRet = FALSE; goto Return; } // //////////////////////////////////////////////////////////////////////////////// // // Service Manager Access Services // // The following services are used to manage user and terminal contexts for // smartcards. // /*++ SCardEstablishContext: This service establishes a context within which communication to the Service Manager is performed. Arguments: dwScope supplies the scope under which this context acts. Possible values are: SCARD_SCOPE_USER - The context is a user context, and any database operations are performed within the domain of the user. SCARD_SCOPE_TERMINAL - The context is that of the current terminal, and any database operations are performed within the domain of that terminal. (The calling application must have appropriate access permissions for any database actions.) SCARD_SCOPE_SYSTEM - The context is the system context, and any database operations are performed within the domain of the system. (The calling application must have appropriate access permissions for any database actions.) pvReserved1 is reserved for future use, and must be NULL. [Reserved to allow a suitably privileged management application to act on behalf of another user.] PvReserved2 is reserved for future use, and must be NULL. [Reserved to allow a suitably privileged management application to act on behalf of another terminal.] phContext receives a handle to the established context, to be supplied to other routines attempting to do work within the context. Return Value: A 32-bit value indicating whether or not the service completed successfully. SCARD_S_SUCCESS is returned on successful completion. Otherwise, the value represents an error condition. Author: Doug Barlow (dbarlow) 10/23/1996 --*/ #undef __SUBROUTINE__ #define __SUBROUTINE__ DBGT("SCardEstablishContext") WINSCARDAPI LONG WINAPI SCardEstablishContext( IN DWORD dwScope, IN LPCVOID pvReserved1, IN LPCVOID pvReserved2, OUT LPSCARDCONTEXT phContext) { LONG nReturn = SCARD_S_SUCCESS; CSCardUserContext *pCtx = NULL; try { if (NULL != pvReserved1) throw (DWORD)SCARD_E_INVALID_VALUE; if (NULL != pvReserved2) throw (DWORD)SCARD_E_INVALID_VALUE; if ((SCARD_SCOPE_USER != dwScope) // && (SCARD_SCOPE_TERMINAL != dwScope) // Maybe NT V5+? && (SCARD_SCOPE_SYSTEM != dwScope)) throw (DWORD)SCARD_E_INVALID_VALUE; *phContext = 0; // Make sure it's valid. pCtx = new CSCardUserContext(dwScope); if (NULL == pCtx) { CalaisWarning( __SUBROUTINE__, DBGT("Client can't allocate a new context")); throw (DWORD)SCARD_E_NO_MEMORY; } if (pCtx->InitFailed()) { delete pCtx; pCtx = NULL; return SCARD_E_NO_MEMORY; } if (!SafeCreateEvent(&g_hUnifiedStartedEvent)) { throw GetLastError(); } // // If TermSrv is enabled then register for session change notifications. // Don't fail if we can't do this, since it may not be fatal // if (TermSrvEnabled() && RegisterForSessionChangeNotifications()) { pCtx->fCallUnregister = TRUE; } else { pCtx->fCallUnregister = FALSE; } if (InTSRedirectMode()) { HANDLE hHeap; SCARDCONTEXT hContext = NULL; HANDLE hEvent = NULL; // // if redirect is disabled, then just get out // if (TS_REDIRECT_DISABLED) { throw (DWORD)SCARD_E_NO_SERVICE; } hEvent = g_hUnifiedStartedEvent; pCtx->AllocateMemory(0); hHeap = pCtx->HeapHandle(); if (NULL == hHeap) throw GetLastError(); if (!TS_REDIRECT_READY) { throw GetLastError(); } nReturn = pfnSCardEstablishContext(dwScope, (LPCVOID)hHeap, (LPCVOID) hEvent, &hContext); // // See if there is an indication that the client's scardsvr service was shutdown // if (SCARD_E_NO_SERVICE == nReturn) { SetStartedEventAfterTestingConnectedState(); //OutputDebugString("WINSCARD: SCardEstablishContext: got E_NO_SERVICE!\n"); } if (SCARD_S_SUCCESS != nReturn) throw (DWORD)nReturn; pCtx->SetRedirContext(hContext); } else { pCtx->EstablishContext(); } *phContext = g_phlContexts->Add(pCtx); } catch (DWORD dwStatus) { if (NULL != pCtx) delete pCtx; nReturn = (LONG)dwStatus; } catch (...) { if (NULL != pCtx) delete pCtx; nReturn = SCARD_E_INVALID_PARAMETER; } return nReturn; } /*++ SCardIsValidContext: This routine verifies that the context to the Service Manager is intact. It is possible that if someone stops the Resource Manager Service, that existing handles can be rendered useless, resulting in an SCARD_E_SERVICE_STOPPED error. This routine simply tests to see if the context is valid by pinging the server. It's used internally to validate handles, and appears useful for external tools. Arguments: hContext supplies the handle to the context previously established via the SCardEstablishContext service. Return Value: A 32-bit value indicating whether or not the service completed successfully. SCARD_S_SUCCESS is returned on successful completion. Otherwise, the value represents an error condition. Specific interesting error codes are: SCARD_E_SERVICE_STOPPED - The Resource Manager Service has been ended. SCARD_E_INVALID_HANDLE - The supplied handle isn't valid. Author: Doug Barlow (dbarlow) 11/2/1998 --*/ #undef __SUBROUTINE__ #define __SUBROUTINE__ DBGT("SCardIsValidContext") WINSCARDAPI LONG WINAPI SCardIsValidContext( IN SCARDCONTEXT hContext) { LONG nReturn = SCARD_S_SUCCESS; try { CSCardUserContext *pCtx = (CSCardUserContext *)((*g_phlContexts)[hContext]); SCARDCONTEXT hRedirContext = pCtx->GetRedirContext(); if (pCtx->IsBad()) { throw (DWORD)SCARD_E_SERVICE_STOPPED; } if (NULL != hRedirContext) { nReturn = pfnSCardIsValidContext(hRedirContext); } else { try { if (!pCtx->IsValidContext()) throw (DWORD)SCARD_E_SERVICE_STOPPED; } catch (...) { SCardReleaseContext(hContext); throw; } } } catch (DWORD dwStatus) { nReturn = (LONG)dwStatus; } catch (...) { nReturn = SCARD_E_INVALID_PARAMETER; } return nReturn; } //////////////////////////////////////////////////////////////////////////////// // // IsSafeToUnregisterForSessionChangeNotifications // BOOL IsSafeToUnregisterForSessionChangeNotifications( IN SCARDCONTEXT hContext) { // // Check if the loader lock is held by the caller (in which case // we may be in the caller's DllMain right now). If the lock is held, // it's not safe to make the Unregister RPC call, since that could cause // deadlock. // // IsSafe is TRUE if the current Thread is Not Equal to the lock's owning // thread. // // Note, we tried using RtlIsThreadWithinLoaderCallout here, but that only // tells us if we're in PROCESS_ATTACH. It doesn't tell us if we're in // any other loader callbacks so it's not sufficient. return ( NtCurrentTeb()->ClientId.UniqueThread ) != ( ((PRTL_CRITICAL_SECTION)(NtCurrentPeb()->LoaderLock))->OwningThread ); } /*++ SCardReleaseContext: This routine closes an established context to the Service Manager, and frees any resources allocated under that context. Arguments: hContext supplies the handle to the context previously established via the SCardEstablishContext service. Return Value: A 32-bit value indicating whether or not the service completed successfully. SCARD_S_SUCCESS is returned on successful completion. Otherwise, the value represents an error condition. Author: Doug Barlow (dbarlow) 10/23/1996 --*/ #undef __SUBROUTINE__ #define __SUBROUTINE__ DBGT("SCardReleaseContext") WINSCARDAPI LONG WINAPI SCardReleaseContext( IN SCARDCONTEXT hContext) { LONG nReturn = SCARD_S_SUCCESS; CSCardUserContext *pCtx = NULL; try { pCtx = (CSCardUserContext *)g_phlContexts->Close(hContext); SCARDCONTEXT hRedirContext = pCtx->GetRedirContext(); if (pCtx->fCallUnregister) { if (IsSafeToUnregisterForSessionChangeNotifications(hContext)) { UnRegisterForSessionChangeNotifications(); } } if (NULL != hRedirContext) { if (pCtx->IsBad()) { nReturn = pfnSCardReleaseBadContext(hRedirContext); } else { nReturn = pfnSCardReleaseContext(hRedirContext); } } else { try { pCtx->Cancel(); pCtx->ReleaseContext(); } catch (...) {} } delete pCtx; pCtx = NULL; } catch (DWORD dwStatus) { nReturn = (LONG)dwStatus; } catch (...) { nReturn = SCARD_E_INVALID_PARAMETER; } return nReturn; } // //////////////////////////////////////////////////////////////////////////////// // // Service Manager Support Routines // // The following services are supplied to simplify the use of the Service // Manager API. // /*++ SCardFreeMemory: This routine releases memory that has been returned from the Service Manager API via the use of the SCARD_AUTOALLOCATE length designator. Arguments: hContext - This is the reference value returned from the SCardEstablishContext service. pvMem - This supplies the memory block to be released. Return Value: A 32-bit value indicating whether or not the service completed successfully. SCARD_S_SUCCESS is returned on successful completion. Otherwise, the value represents an error condition. Author: Doug Barlow (dbarlow) 10/23/1996 --*/ #undef __SUBROUTINE__ #define __SUBROUTINE__ DBGT("SCardFreeMemory") WINSCARDAPI LONG WINAPI SCardFreeMemory( IN SCARDCONTEXT hContext, IN LPCVOID pvMem) { LONG nReturn = SCARD_S_SUCCESS; try { if ((NULL != pvMem) && ((LPVOID)g_wszBlank != pvMem)) { if (NULL == hContext) HeapFree(GetProcessHeap(), 0, (LPVOID)pvMem); else { CSCardUserContext *pCtx = (CSCardUserContext *)((*g_phlContexts)[hContext]); nReturn = (LONG)pCtx->FreeMemory(pvMem); } } } catch (DWORD dwStatus) { nReturn = (LONG)dwStatus; } catch (...) { nReturn = SCARD_E_INVALID_PARAMETER; } return nReturn; } // //////////////////////////////////////////////////////////////////////////////// // // Reader Services // // The following services supply means for tracking cards within readers. // /*++ SCardCancel: This service is used to terminate any and all outstanding actions within the context. The caller supplies the context handle under which outstanding requests will be canceled. Not all requests are cancelable; only those which require waiting for external action by the smartcard or user. Any such outstanding action requests will terminate with a status indication that the action was canceled. This is especially useful to force outstanding SCardGetStatusChange calls to terminate. Arguments: hContext supplies the handle identifying the Service Manager Context established previously via the SCardEstablishContext() service. Return Value: A 32-bit value indicating whether or not the service completed successfully. SCARD_S_SUCCESS is returned on successful completion. Otherwise, the value represents an error condition. Author: Doug Barlow (dbarlow) 10/23/1996 --*/ #undef __SUBROUTINE__ #define __SUBROUTINE__ DBGT("SCardCancel") WINSCARDAPI LONG WINAPI SCardCancel( IN SCARDCONTEXT hContext) { LONG nReturn = SCARD_S_SUCCESS; try { CSCardUserContext *pCtx = (CSCardUserContext *)((*g_phlContexts)[hContext]); SCARDCONTEXT hRedirContext = pCtx->GetRedirContext(); if (pCtx->IsBad()) { throw (DWORD)SCARD_E_SERVICE_STOPPED; } if (NULL != hRedirContext) { return pfnSCardCancel(hRedirContext); } else pCtx->Cancel(); } catch (DWORD dwStatus) { nReturn = (LONG)dwStatus; } catch (...) { nReturn = SCARD_E_INVALID_PARAMETER; } return nReturn; } // //////////////////////////////////////////////////////////////////////////////// // // Card/Reader Access Services // // The following services provide means for establishing communication with // the card. // /*++ SCardReconnect: This service re-establishes an existing connection from the calling application to the smartcard. This service is used to move a card handle from direct access to general access (see Section 4), or to acknowledge and clear an error condition that is preventing further access to the card. Arguments: hCard - This supplies the reference value obtained from a previous call to the SCardConnect or SCardOpenReader service. DwShareMode supplies a flag indicating whether or not other applications may form connections to this card. Possible values are: SCARD_SHARE_SHARED - This application is willing to share this card with other applications. SCARD_SHARE_EXCLUSIVE - This application is not willing to share this card with other applications. DwPreferredProtocols supplies a bit mask of acceptable protocols for this connection. Possible values, which may be combined via the OR operation, are: SCARD_PROTOCOL_T0 - T=0 is an acceptable protocol. SCARD_PROTOCOL_T1 - T=1 is an acceptable protocol. DwInitialization supplies an indication as to the form of initialization that should be performed on the card. Possible values are: SCARD_LEAVE_CARD - Don't do anything special on reconnect SCARD_RESET_CARD - Reset the card (Warm Reset) SCARD_UNPOWER_CARD - Power down the card and reset it (Cold Reset) pdwActiveProtocol receives a flag indicating the established active protocol. Possible values are: SCARD_PROTOCOL_T0 - T=0 is the active protocol. SCARD_PROTOCOL_T1 - T=1 is the active protocol. Return Value: A 32-bit value indicating whether or not the service completed successfully. SCARD_S_SUCCESS is returned on successful completion. Otherwise, the value represents an error condition. Author: Doug Barlow (dbarlow) 10/23/1996 --*/ #undef __SUBROUTINE__ #define __SUBROUTINE__ DBGT("SCardReconnect") WINSCARDAPI LONG WINAPI SCardReconnect( IN SCARDHANDLE hCard, IN DWORD dwShareMode, IN DWORD dwPreferredProtocols, IN DWORD dwInitialization, OUT LPDWORD pdwActiveProtocol) { LONG nReturn = SCARD_S_SUCCESS; try { CReaderContext *pRdr = (CReaderContext *)((*g_phlReaders)[hCard]); if (pRdr->IsBad()) { throw (DWORD)SCARD_E_SERVICE_STOPPED; } if (NULL != pRdr->GetRedirCard()) { nReturn = pfnSCardReconnect(pRdr->GetRedirCard(), dwShareMode, dwPreferredProtocols, dwInitialization, pdwActiveProtocol); } else { pRdr->Reconnect( dwShareMode, dwPreferredProtocols, dwInitialization); if (NULL != pdwActiveProtocol) *pdwActiveProtocol = pRdr->Protocol(); } } catch (DWORD dwStatus) { nReturn = (LONG)dwStatus; } catch (...) { nReturn = SCARD_E_INVALID_PARAMETER; } return nReturn; } /*++ SCardDisconnect: This service terminates a previously opened connection between the calling application and the smartcard in the target reader. Arguments: hCard - This supplies the reference value obtained from a previous call to the SCardConnect or SCardOpenReader service. dwDisposition - Supplies an indication of what should be done with the card in the connected reader. Possible values are: SCARD_LEAVE_CARD - Don't do anything special on close SCARD_RESET_CARD - Reset the card on close SCARD_UNPOWER_CARD - Power down the card on close SCARD_EJECT_CARD - Eject the card on close Return Value: A 32-bit value indicating whether or not the service completed successfully. SCARD_S_SUCCESS is returned on successful completion. Otherwise, the value represents a warning condition. The connection is terminated regardless of the return code. Author: Doug Barlow (dbarlow) 10/23/1996 --*/ #undef __SUBROUTINE__ #define __SUBROUTINE__ DBGT("SCardDisconnect") WINSCARDAPI LONG WINAPI SCardDisconnect( IN SCARDHANDLE hCard, IN DWORD dwDisposition) { CReaderContext *pRdr = NULL; LONG nReturn = SCARD_S_SUCCESS; try { pRdr = (CReaderContext *)g_phlReaders->Close(hCard); if (NULL != pRdr->GetRedirCard()) { nReturn = pfnSCardDisconnect(pRdr->GetRedirCard(), dwDisposition); } else { ASSERT(pRdr->Context()->m_hReaderHandle == hCard); pRdr->Context()->m_hReaderHandle = NULL; nReturn = pRdr->Disconnect(dwDisposition); } delete pRdr; } catch (DWORD dwStatus) { if (NULL != pRdr) delete pRdr; nReturn = (LONG)dwStatus; } catch (...) { if (NULL != pRdr) delete pRdr; nReturn = SCARD_E_INVALID_PARAMETER; } return nReturn; } /*++ SCardBeginTransaction: This service temporarily blocks other applications from accessing the smartcard, in order for this application to perform an operation that requires multiple interactions. Arguments: hCard - This supplies the reference value obtained from a previous call to the SCardConnect or SCardOpenReader service. Return Value: A 32-bit value indicating whether or not the service completed successfully. SCARD_S_SUCCESS is returned on successful completion. Otherwise, the value represents an error condition. Author: Doug Barlow (dbarlow) 10/23/1996 --*/ #undef __SUBROUTINE__ #define __SUBROUTINE__ DBGT("SCardBeginTransaction") WINSCARDAPI LONG WINAPI SCardBeginTransaction( IN SCARDHANDLE hCard) { LONG nReturn = SCARD_S_SUCCESS; try { CReaderContext *pRdr = (CReaderContext *)((*g_phlReaders)[hCard]); if (pRdr->IsBad()) { throw (DWORD)SCARD_E_SERVICE_STOPPED; } if (NULL != pRdr->GetRedirCard()) { nReturn = pfnSCardBeginTransaction(pRdr->GetRedirCard()); } else pRdr->BeginTransaction(); } catch (DWORD dwStatus) { nReturn = (LONG)dwStatus; } catch (...) { nReturn = SCARD_E_INVALID_PARAMETER; } return nReturn; } /*++ SCardEndTransaction: This service completes a previously declared transaction, allowing other applications to resume interactions with the card. Arguments: hCard - This supplies the reference value obtained from a previous call to the SCardConnect or SCardOpenReader service. dwDisposition - Supplies an indication of what should be done with the card in the connected reader. Possible values are: SCARD_LEAVE_CARD - Don't do anything special on close SCARD_RESET_CARD - Reset the card on close SCARD_UNPOWER_CARD - Power down the card on close SCARD_EJECT_CARD - Eject the card on close Return Value: A 32-bit value indicating whether or not the service completed successfully. SCARD_S_SUCCESS is returned on successful completion. Otherwise, the value represents an error condition. Author: Doug Barlow (dbarlow) 10/23/1996 --*/ #undef __SUBROUTINE__ #define __SUBROUTINE__ DBGT("SCardEndTransaction") WINSCARDAPI LONG WINAPI SCardEndTransaction( IN SCARDHANDLE hCard, IN DWORD dwDisposition) { LONG nReturn = SCARD_S_SUCCESS; try { CReaderContext *pRdr = (CReaderContext *)((*g_phlReaders)[hCard]); if (pRdr->IsBad()) { throw (DWORD)SCARD_E_SERVICE_STOPPED; } if (NULL != pRdr->GetRedirCard()) { nReturn = pfnSCardEndTransaction(pRdr->GetRedirCard(), dwDisposition); } else pRdr->EndTransaction(dwDisposition); } catch (DWORD dwStatus) { nReturn = (LONG)dwStatus; } catch (...) { nReturn = SCARD_E_INVALID_PARAMETER; } return nReturn; } /*++ SCardState: This routine provides the current state of the reader. It may be used at any time following a successful call to SCardConnect or SCardOpenReader, and prior to a successful call to SCardDisconnect. It does not effect the state of the reader or driver. Arguments: hCard - This is the reference value returned from the SCardConnect or SCardOpenReader service. pdwState - This receives the current state of the reader. Upon success, it receives one of the following state indicators: SCARD_ABSENT - This value implies there is no card in the reader. SCARD_PRESENT - This value implies there is a card is present in the reader, but that it has not been moved into position for use. SCARD_SWALLOWED - This value implies there is a card in the reader in position for use. The card is not powered. SCARD_POWERED - This value implies there is power is being provided to the card, but the Reader Driver is unaware of the mode of the card. SCARD_NEGOTIABLEMODE - This value implies the card has been reset and is awaiting PTS negotiation. SCARD_SPECIFICMODE - This value implies the card has been reset and specific communication protocols have been established. pdwProtocol - This receives the current protocol, if any. Possible returned values are listed below. Other values may be added in the future. The returned value is only meaningful if the returned state is SCARD_SPECIFICMODE. SCARD_PROTOCOL_RAW - The Raw Transfer Protocol is in use. SCARD_PROTOCOL_T0 - The ISO 7816/3 T=0 Protocol is in use. SCARD_PROTOCOL_T1 - The ISO 7816/3 T=1 Protocol is in use. pbAtr - This parameter points to a 32-byte buffer which receives the ATR string from the currently inserted card, if available. pbcAtrLen - This points to a DWORD which supplies the length of the pbAtr buffer, and receives the actual number of bytes in the ATR string. Return Value: A 32-bit value indicating whether or not the service completed successfully. SCARD_S_SUCCESS is returned on successful completion. Otherwise, the value represents an error condition. Author: Doug Barlow (dbarlow) 10/23/1996 --*/ #undef __SUBROUTINE__ #define __SUBROUTINE__ DBGT("SCardState") WINSCARDAPI LONG WINAPI SCardState( IN SCARDHANDLE hCard, OUT LPDWORD pdwState, OUT LPDWORD pdwProtocol, OUT LPBYTE pbAtr, IN OUT LPDWORD pcbAtrLen) { LONG nReturn = SCARD_S_SUCCESS; try { CReaderContext *pRdr = (CReaderContext *)((*g_phlReaders)[hCard]); if (pRdr->IsBad()) { throw (DWORD)SCARD_E_SERVICE_STOPPED; } if (NULL != pRdr->GetRedirCard()) { nReturn = pfnSCardState(pRdr->GetRedirCard(), pdwState, pdwProtocol, pbAtr, pcbAtrLen); } else { CBuffer bfAtr, bfRdr; DWORD dwLocalState, dwLocalProtocol; pRdr->Status(&dwLocalState, &dwLocalProtocol, bfAtr, bfRdr); if (NULL != pdwState) *pdwState = dwLocalState; if (NULL != pdwProtocol) *pdwProtocol = dwLocalProtocol; if (NULL != pcbAtrLen) PlaceResult(pRdr->Context()->Parent(), bfAtr, pbAtr, pcbAtrLen); } } catch (DWORD dwStatus) { nReturn = (LONG)dwStatus; } catch (...) { nReturn = SCARD_E_INVALID_PARAMETER; } return nReturn; } /*++ SCardTransmit: This routine sends a service request to the smartcard, and expects to receive data back from the card. Arguments: hCard - This is the reference value returned from the SCardConnect service. pioSendPci - This supplies the protocol header structure for the instruction. This buffer is in the format of a SCARD_IO_REQUEST structure, followed by the specific protocol control information. pbSendBuffer - This supplies the actual data to be written to the card in conjunction with the command. cbSendLength - This supplies the length of the pbSendBuffer parameter, in bytes. pioRecvPci - This supplies the protocol header structure for the instruction, followed by a buffer in which to receive any returned protocol control information specific to the protocol in use. This parameter may be NULL if no returned PCI is desired. pbRecvBuffer - This receives any data returned from the card in conjunction with the command. pcbRecvLength - This supplies the length of the pbRecvBuffer parameter, in bytes, and receives the actual number of bytes received from the smartcard. If the buffer length is specified as SCARD_AUTOALLOCATE, then pbAttrBuffer is converted to a pointer to a byte pointer, and receives the address of a block of memory containing the returned data. This block of memory must be deallocated via the SCardFreeMemory() service. Return Value: A 32-bit value indicating whether or not the service completed successfully. SCARD_S_SUCCESS is returned on successful completion. Otherwise, the value represents an error condition. Author: Doug Barlow (dbarlow) 2/6/1997 --*/ #undef __SUBROUTINE__ #define __SUBROUTINE__ DBGT("SCardTransmit") WINSCARDAPI LONG WINAPI SCardTransmit( IN SCARDHANDLE hCard, IN LPCSCARD_IO_REQUEST pioSendPci, IN LPCBYTE pbSendBuffer, IN DWORD cbSendLength, IN OUT LPSCARD_IO_REQUEST pioRecvPci, OUT LPBYTE pbRecvBuffer, IN OUT LPDWORD pcbRecvLength) { LONG nReturn = SCARD_S_SUCCESS; try { CReaderContext *pRdr = (CReaderContext *)((*g_phlReaders)[hCard]); if (pRdr->IsBad()) { throw (DWORD)SCARD_E_SERVICE_STOPPED; } if (NULL != pRdr->GetRedirCard()) { nReturn = pfnSCardTransmit(pRdr->GetRedirCard(), pioSendPci, pbSendBuffer, cbSendLength, pioRecvPci, pbRecvBuffer, pcbRecvLength); } else { CBuffer bfData(*pcbRecvLength); DWORD dwLen = 0; if (NULL != pcbRecvLength) { if (SCARD_AUTOALLOCATE != *pcbRecvLength) dwLen = *pcbRecvLength; } pRdr->Transmit( pioSendPci, pbSendBuffer, cbSendLength, pioRecvPci, bfData, dwLen); PlaceResult( pRdr->Context()->Parent(), bfData, pbRecvBuffer, pcbRecvLength); } } catch (DWORD dwStatus) { nReturn = (LONG)dwStatus; } catch (...) { nReturn = SCARD_E_INVALID_PARAMETER; } return nReturn; } // //////////////////////////////////////////////////////////////////////////////// // // Reader Control Routines // // The following services provide for direct, low-level manipulation of the // reader by the calling application allowing it control over the // attributes of the communications with the card. // /*++ SCardControl: This routine provides for direct application control of the reader, should it be necessary. It may be used at any time following a successful call to SCardConnect or SCardOpenReader, and prior to a successful call to SCardDisconnect. The effect on the state of the reader is dependent on the control code. Arguments: hCard - This is the reference value returned from the SCardConnect or SCardOpenReader service. dwControlCode - This supplies the control code for the operation. This value identifies the specific operation to be performed. pvInBuffer - This supplies a pointer to a buffer that contains the data required to perform the operation. This parameter can be NULL if the dwControlCode parameter specifies an operation that does not require input data. cbInBufferSize - This supplies the size, in bytes, of the buffer pointed to by pvInBuffer. pvOutBuffer - This buffer receives the operation's output data. This parameter can be NULL if the dwControlCode parameter specifies an operation that does not produce output data. cbOutBufferSize - This supplies the size, in bytes, of the buffer pointed to by pvOutBuffer. pcbBytesReturned - This receives the size, in bytes, of the data stored into the buffer pointed to by pvOutBuffer. Return Value: A 32-bit value indicating whether or not the service completed successfully. SCARD_S_SUCCESS is returned on successful completion. Otherwise, the value represents an error condition. Author: Doug Barlow (dbarlow) 10/23/1996 --*/ #undef __SUBROUTINE__ #define __SUBROUTINE__ DBGT("SCardControl") WINSCARDAPI LONG WINAPI SCardControl( IN SCARDHANDLE hCard, IN DWORD dwControlCode, IN LPCVOID pvInBuffer, IN DWORD cbInBufferSize, OUT LPVOID pvOutBuffer, IN DWORD cbOutBufferSize, OUT LPDWORD pcbBytesReturned) { LONG nReturn = SCARD_S_SUCCESS; try { CReaderContext *pRdr = (CReaderContext *)((*g_phlReaders)[hCard]); if (pRdr->IsBad()) { throw (DWORD)SCARD_E_SERVICE_STOPPED; } if (NULL != pRdr->GetRedirCard()) { nReturn = pfnSCardControl(pRdr->GetRedirCard(), dwControlCode, pvInBuffer, cbInBufferSize, pvOutBuffer, cbOutBufferSize, pcbBytesReturned); } else { CBuffer bfResponse(cbOutBufferSize); *pcbBytesReturned = cbOutBufferSize; pRdr->Control(dwControlCode, pvInBuffer, cbInBufferSize, bfResponse); PlaceResult( pRdr->Context()->Parent(), bfResponse, (LPBYTE)pvOutBuffer, pcbBytesReturned); } } catch (DWORD dwStatus) { nReturn = (LONG)dwStatus; } catch (...) { nReturn = SCARD_E_INVALID_PARAMETER; } return nReturn; } /*++ SCardGetAttrib: This routine gets the current communications attributes for the given handle. It does not effect the state of the reader, driver, or card. Arguments: hCard - This is the reference value returned from the SCardConnect or SCardOpenReader service. dwAttrId - This supplies the identifier for the attribute to get. pbAttr - This buffer receives the attribute corresponding to the attribute id supplied in the dwAttrId parameter. If this value is NULL, the supplied buffer length in pcbAttrLength is ignored, the length of the buffer that would have been returned had this parameter not been null is written to pcbAttrLength, and a success code is returned. pcbAttrLength - This supplies the length of the pbAttr buffer in bytes, and receives the actual length of the received attribute. If the buffer length is specified as SCARD_AUTOALLOCATE, then pbAttrBuffer is converted to a pointer to a byte pointer, and receives the address of a block of memory containing the attribute. This block of memory must be deallocated via the SCardFreeMemory() service. Return Value: A 32-bit value indicating whether or not the service completed successfully. SCARD_S_SUCCESS is returned on successful completion. Otherwise, the value represents an error condition. Note that strings are always returned as ANSI characters, per PC/SC standards. Author: Doug Barlow (dbarlow) 10/23/1996 --*/ #undef __SUBROUTINE__ #define __SUBROUTINE__ DBGT("SCardGetAttrib") WINSCARDAPI LONG WINAPI SCardGetAttrib( IN SCARDHANDLE hCard, IN DWORD dwAttrId, OUT LPBYTE pbAttr, IN OUT LPDWORD pcbAttrLen) { LONG nReturn = SCARD_S_SUCCESS; try { CReaderContext *pRdr = (CReaderContext *)((*g_phlReaders)[hCard]); if (pRdr->IsBad()) { throw (DWORD)SCARD_E_SERVICE_STOPPED; } if (NULL != pRdr->GetRedirCard()) { nReturn = pfnSCardGetAttrib(pRdr->GetRedirCard(), dwAttrId, pbAttr, pcbAttrLen); } else { CBuffer bfAttrib; DWORD dwLen = 0; if (NULL != pcbAttrLen) { if (SCARD_AUTOALLOCATE != *pcbAttrLen) dwLen = *pcbAttrLen; } switch (dwAttrId) { case SCARD_ATTR_DEVICE_FRIENDLY_NAME_A: { CBuffer bfSysName, bfNames; CTextMultistring mtzNames; pRdr->GetAttrib(SCARD_ATTR_DEVICE_SYSTEM_NAME, bfSysName, MAX_PATH); ListReaderNames( pRdr->Context()->Scope(), bfSysName, bfNames); mtzNames = (LPCTSTR)bfNames.Access(); bfAttrib.Set( (LPCBYTE)((LPCSTR)mtzNames), (mtzNames.Length()) * sizeof(CHAR)); break; } case SCARD_ATTR_DEVICE_FRIENDLY_NAME_W: { CBuffer bfSysName, bfNames; CTextMultistring mtzNames; pRdr->GetAttrib(SCARD_ATTR_DEVICE_SYSTEM_NAME, bfSysName, MAX_PATH); ListReaderNames( pRdr->Context()->Scope(), bfSysName, bfNames); mtzNames = (LPCTSTR)bfNames.Access(); bfAttrib.Set( (LPCBYTE)((LPCWSTR)mtzNames), (mtzNames.Length()) * sizeof(WCHAR)); break; } case SCARD_ATTR_DEVICE_SYSTEM_NAME_A: { CBuffer bfSysName; CTextString szSysName; pRdr->GetAttrib(SCARD_ATTR_DEVICE_SYSTEM_NAME, bfSysName, dwLen); szSysName = (LPCTSTR)bfSysName.Access(); bfAttrib.Set( (LPCBYTE)((LPCSTR)szSysName), (szSysName.Length() + 1) * sizeof(CHAR)); break; } case SCARD_ATTR_DEVICE_SYSTEM_NAME_W: { CBuffer bfSysName; CTextString szSysName; pRdr->GetAttrib(SCARD_ATTR_DEVICE_SYSTEM_NAME, bfSysName, dwLen); szSysName = (LPCTSTR)bfSysName.Access(); bfAttrib.Set( (LPCBYTE)((LPCWSTR)szSysName), (szSysName.Length() + 1) * sizeof(WCHAR)); break; } default: pRdr->GetAttrib(dwAttrId, bfAttrib, dwLen); } PlaceResult(pRdr->Context()->Parent(), bfAttrib, pbAttr, pcbAttrLen); } } catch (DWORD dwStatus) { nReturn = (LONG)dwStatus; } catch (...) { nReturn = SCARD_E_INVALID_PARAMETER; } return nReturn; } /*++ SCardSetAttrib: This routine sets the current communications attributes for the given handle. It does not effect the state of the reader, driver, or card. Not all attributes are settable at all times, as many of the attributes are directly under control of the transport protocol. These attributes are offered only as a suggestion to the reader -- the reader may ignore any attributes it feels are inappropriate. Arguments: hCard - This is the reference value returned from the SCardOpenReader service. dwAttrId - This supplies the identifier for the attribute to get. pbAttr - This buffer supplies the attribute corresponding to the attribute id supplied in the dwAttrId parameter. cbAttrLength - This supplies the length of the attribute value in pbAttr buffer in bytes. Return Value: A 32-bit value indicating whether or not the service completed successfully. SCARD_S_SUCCESS is returned on successful completion. Otherwise, the value represents an error condition. Author: Doug Barlow (dbarlow) 10/23/1996 --*/ #undef __SUBROUTINE__ #define __SUBROUTINE__ DBGT("SCardSetAttrib") WINSCARDAPI LONG WINAPI SCardSetAttrib( IN SCARDHANDLE hCard, IN DWORD dwAttrId, IN LPCBYTE pbAttr, IN DWORD cbAttrLen) { LONG nReturn = SCARD_S_SUCCESS; try { CReaderContext *pRdr = (CReaderContext *)((*g_phlReaders)[hCard]); if (pRdr->IsBad()) { throw (DWORD)SCARD_E_SERVICE_STOPPED; } if (NULL != pRdr->GetRedirCard()) { nReturn = pfnSCardSetAttrib(pRdr->GetRedirCard(), dwAttrId, pbAttr, cbAttrLen); } else pRdr->SetAttrib(dwAttrId, pbAttr, cbAttrLen); } catch (DWORD dwStatus) { nReturn = (LONG)dwStatus; } catch (...) { nReturn = SCARD_E_INVALID_PARAMETER; } return nReturn; } // //////////////////////////////////////////////////////////////////////////////// // // SCard Service Information // // The following services are used to manage the Calais Service itself. // These routines are not documented to users, and are not guaranteed // to exist in future releases. // /*++ SCardAccessStartedEvent: This function obtains a local handle to the Calais Resource Manager Start event. The handle must be released via the SCardReleaseStartedEvent service. Arguments: None Return Value: The Handle, or NULL if an error occurs. Throws: None Remarks: Programs other than the resource manager should only wait on these flags. Author: Doug Barlow (dbarlow) 7/1/1998 --*/ #undef __SUBROUTINE__ #define __SUBROUTINE__ DBGT("SCardAccessStartedEvent") WINSCARDAPI HANDLE WINAPI SCardAccessStartedEvent( void) { HANDLE hRet = NULL; // // Create the event that is passed back to the caller... // if it hasn't already been created // if (SafeCreateEvent(&g_hUnifiedStartedEvent)) { hRet = g_hUnifiedStartedEvent; } else { goto ErrorReturn; } if (TermSrvEnabled()) { // // // if (!SetStartedEventAfterTestingConnectedState()) { goto ErrorReturn; } } else { // // TermSrv is disabled, so go ahead and call the // SetStartedEventWhenSCardSubsytemIsStarted function which will make sure // the event which is returned to the caller will be set when the LOCAL // smart card subsystem becomes available. // if (!SetStartedEventWhenSCardSubsytemIsStarted(TRUE)) { goto ErrorReturn; } } Return: return (hRet); ErrorReturn: hRet = NULL; goto Return; } /*++ SCardAccessNewReaderEvent: This function obtains a local handle to the Calais Resource Manager's New Reader event. The handle must be released via the SCardReleaseNewReaderEvent service. Arguments: None Return Value: The Handle, or NULL if an error occurs. Throws: None Remarks: Programs other than the resource manager should only wait on these flags. Author: Doug Barlow (dbarlow) 7/1/1998 --*/ #undef __SUBROUTINE__ #define __SUBROUTINE__ DBGT("SCardAccessNewReaderEvent") WINSCARDAPI HANDLE WINAPI SCardAccessNewReaderEvent( void) { return AccessNewReaderEvent(); } /*++ SCardReleaseStartedEvent: This function releases a previously accessed handle to the Calais Resource Manager Start event. The handle must be obtained via the SCardAccessStartedEvent service. Arguments: None Return Value: None. Throws: None Remarks: Programs other than the resource manager should only wait on these flags. Author: Doug Barlow (dbarlow) 7/1/1998 --*/ #undef __SUBROUTINE__ #define __SUBROUTINE__ DBGT("SCardReleaseStartedEvent") WINSCARDAPI void WINAPI SCardReleaseStartedEvent( void) { if (TermSrvEnabled()) { UnRegisterForSessionChangeNotifications(); } } /*++ SCardReleaseNewReaderEvent: This function releases a previously accessed handle to the Calais Resource Manager New Reader event. The handle must be obtained via the SCardAccessNewReaderEvent service. Arguments: None Return Value: None. Throws: None Remarks: Programs other than the resource manager should only wait on these flags. Author: Doug Barlow (dbarlow) 7/1/1998 --*/ #undef __SUBROUTINE__ #define __SUBROUTINE__ DBGT("SCardReleaseNewReaderEvent") WINSCARDAPI void WINAPI SCardReleaseNewReaderEvent( void) { ReleaseNewReaderEvent(); } /*++ SCardReleaseAllEvents: This is a catch-all routine that releases all known special event handles. Arguments: None Return Value: None Throws: None Remarks: Author: Doug Barlow (dbarlow) 7/6/1998 --*/ #undef __SUBROUTINE__ #define __SUBROUTINE__ DBGT("SCardReleaseAllEvents") WINSCARDAPI void WINAPI SCardReleaseAllEvents( void) { ReleaseAllEvents(); } // /////////////////////////////////////////////////////////////////////////////// // // Utility Routines // /*++ PlaceResult: This set of routines places the result of an operation into the user's output buffer, supporting SCARD_AUTO_ALLOCATE, invalid buffer sizes, etc. Arguments: pCtx supplies the context under which this operation is being performed. bfResult supplies the result to be returned to the user. pbOutput receives the result for the user, as a byte stream. szOutput receives the result as an ANSI or UNICODE string. pcbLength supplies the length of the user's output buffer in bytes, and receives how much of it was used. pcchLength supplies the length of the user's output buffer in characters, and receives how much of it was used. Return Value: None Throws: Error conditions are thrown as DWORD status codes. Author: Doug Barlow (dbarlow) 12/7/1996 --*/ #undef __SUBROUTINE__ #define __SUBROUTINE__ DBGT("PlaceResult") void PlaceResult( CSCardUserContext *pCtx, CBuffer &bfResult, LPBYTE pbOutput, LPDWORD pcbLength) { LPBYTE pbForUser = NULL; LPBYTE pbOutBuff = pbOutput; try { if (NULL == pbOutput) *pcbLength = 0; switch (*pcbLength) { case 0: // They just want the length. *pcbLength = bfResult.Length(); break; case SCARD_AUTOALLOCATE: if (0 < bfResult.Length()) { if (NULL == pCtx) { pbForUser = (LPBYTE)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, bfResult.Length()); } else pbForUser = (LPBYTE)pCtx->AllocateMemory(bfResult.Length()); if (NULL == pbForUser) { CalaisWarning( __SUBROUTINE__, DBGT("Client can't get return memory")); throw (DWORD)SCARD_E_NO_MEMORY; } *(LPBYTE *)pbOutput = pbForUser; pbOutBuff = pbForUser; // Fall through intentionally } else { *pcbLength = 0; *(LPBYTE *)pbOutput = (LPBYTE)g_wszBlank; break; // Do terminate the case now. } default: if (*pcbLength < bfResult.Length()) { *pcbLength = bfResult.Length(); throw (DWORD)SCARD_E_INSUFFICIENT_BUFFER; } CopyMemory(pbOutBuff, bfResult.Access(), bfResult.Length()); *pcbLength = bfResult.Length(); break; } } catch (...) { if (NULL != pbForUser) { if (NULL == pCtx) HeapFree(GetProcessHeap(), 0, pbForUser); else pCtx->FreeMemory(pbForUser); } throw; } } #include // // On a system that installs a smart card reader for the very first time // the smart card subsystem must be started manually, but only this first time. // After that, it is started automatically whenever the system boots // DWORD APIENTRY ClassInstall32( IN DI_FUNCTION dif, IN HDEVINFO hdi, IN PSP_DEVINFO_DATA pdevData) OPTIONAL { if (dif == DIF_INSTALLDEVICE) { StartCalaisService(); } return ERROR_DI_DO_DEFAULT; }