/*++ Copyright (c) 1999-2001 Microsoft Corporation Module Name: session.cpp Abstract: Class for creating a command console shell Author: Brian Guarraci (briangu) 2001. Revision History: --*/ #include #include #include #include "secio.h" #include "utils.h" #include "scraper.h" #include "vtutf8scraper.h" extern "C" { #include #include } CSession::CSession() /*++ Routine Description: Constructor Arguments: None Return Value: N/A --*/ { // // // m_dwPollInterval = MIN_POLL_INTERVAL; // // Initialize the # of rows and cols the session // screen will have // m_wCols = DEFAULT_COLS; m_wRows = DEFAULT_ROWS; // // Initialize the username and password for // the authentication of a user // RtlZeroMemory(m_UserName, sizeof(m_UserName)); RtlZeroMemory(m_DomainName, sizeof(m_DomainName)); // // Initialize the WaitForIo attributes // m_bContinueSession = true; m_dwHandleCount = 0; // // NULL all event the handles we use // m_ThreadExitEvent = NULL; m_SacChannelCloseEvent = NULL; m_SacChannelHasNewDataEvent = NULL; m_SacChannelLockEvent = NULL; m_SacChannelRedrawEvent = NULL; // // thread handle is invalid // m_InputThreadHandle = INVALID_HANDLE_VALUE; // // // m_ioHandler = NULL; m_Scraper = NULL; m_Shell = NULL; } CSession::~CSession() /*++ Routine Description: Destructor Arguments: N/A Return Value: N/A --*/ { if (m_Shell) { delete m_Shell; } if (m_Scraper) { delete m_Scraper; } if (m_ioHandler) { delete m_ioHandler; } if (m_ThreadExitEvent) { CloseHandle( m_ThreadExitEvent ); } if (m_SacChannelCloseEvent) { CloseHandle( m_SacChannelCloseEvent ); } if (m_SacChannelHasNewDataEvent) { CloseHandle( m_SacChannelHasNewDataEvent ); } if (m_SacChannelLockEvent) { CloseHandle( m_SacChannelLockEvent ); } if (m_SacChannelRedrawEvent) { CloseHandle( m_SacChannelRedrawEvent ); } } BOOL CSession::Authenticate( OUT PHANDLE phToken ) /*++ Routine Description: This routine attempts to authenticate a user (if necessary) and acquire credentials. Arguments: hToken - on success, contains the authenticated credentials Return Value: TRUE - success FALSE - otherwise Security: interface: registry external input LogonUser() --*/ { BOOL bSuccess; PWCHAR Password; // // Allocate the password on the heap // Password = new WCHAR[MAX_PASSWORD_LENGTH+1]; // // Purge the password buffer // RtlZeroMemory(Password, (MAX_PASSWORD_LENGTH+1) * sizeof(Password[0])); // // Default: the credentials are invalid // *phToken = INVALID_HANDLE_VALUE; // // Default: we were successful // bSuccess = TRUE; // // Attempt to authenticate a user if authentication // as actually needed. // do { if( NeedCredentials() ) { // // Get the credentials to authenticate // bSuccess = m_ioHandler->RetrieveCredentials( m_UserName, sizeof(m_UserName) / sizeof(m_UserName[0]), m_DomainName, sizeof(m_DomainName) / sizeof(m_DomainName[0]), Password, MAX_PASSWORD_LENGTH+1 ); if (!bSuccess) { break; } // // Attempt the actual authentication // bSuccess = m_ioHandler->AuthenticateCredentials( m_UserName, m_DomainName, Password, phToken ); if (!bSuccess) { break; } } } while ( FALSE ); // // Purge and release the password buffer // RtlSecureZeroMemory(Password, (MAX_PASSWORD_LENGTH+1) * sizeof(Password[0])); delete [] Password; return bSuccess; } BOOL CSession::Lock( VOID ) /*++ Routine Description: This routine manages the locking of the session. Arguments: None Return Value: status --*/ { // // Lock the IOHandler // // this causes the Security Iohandler to switch from using // the SAC IOHandler to using the NULLIO handler. // m_ioHandler->Lock(); return TRUE; } BOOL CSession::Unlock( VOID ) /*++ Routine Description: This routine manages the unlocking of the session after a user has authenticated. Arguments: None Return Value: status --*/ { BOOL bStatus; // // Unlock the IOHandler // // this causes the Security Iohandler to switch from using // the NULLIO handler to using the SAC IOhandler // m_ioHandler->Unlock(); // // It is possible that the Lock Event was signalled while we were // waiting for authentication // // e.g. Imagine a user starting a cmd session, // not logging in, and walking away for longer than the timeout // period // // -or- // // a SAC "lock" command was issued while the session was already // locked. // // After the user logs on successfully, we should not lock again. // Hence, we need to reset the lock event. // // Note: It is not correct to do this in the security IO handler, as it // should not have global knowledge of all the circumstances that // can signal the lock event. // bStatus = ResetEvent(m_SacChannelLockEvent); return bStatus; } BOOL CSession::Init( VOID ) /*++ Routine Description: This routine does the core initialization for the session. If this routine is successful, the WaitForIo routine may be called. Arguments: None Return Value: TRUE - the session was successfully initialized FALSE - otherwise --*/ { SAC_CHANNEL_OPEN_ATTRIBUTES Attributes; HANDLE hToken; BOOL bSuccess; // // Initialize the last error status // SetLastError( 0 ); // // Construct the manually resetting events that we'll use // // Note: we do not need to CloseHandle() on the events if // fail because they are cleaned up in the destructor // m_SacChannelLockEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); ASSERT_STATUS(m_SacChannelLockEvent, FALSE); m_SacChannelCloseEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); ASSERT_STATUS(m_SacChannelCloseEvent, FALSE); m_ThreadExitEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); ASSERT_STATUS(m_ThreadExitEvent, FALSE); m_SacChannelHasNewDataEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); ASSERT_STATUS(m_SacChannelHasNewDataEvent, FALSE); m_SacChannelRedrawEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); ASSERT_STATUS(m_SacChannelRedrawEvent, FALSE); // // Configure the attributes of the command console channel // // Note: we don't use all of the attributes since most are defaulted // in the SAC driver // RtlZeroMemory(&Attributes, sizeof(SAC_CHANNEL_OPEN_ATTRIBUTES)); Attributes.Flags = SAC_CHANNEL_FLAG_CLOSE_EVENT | SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT | SAC_CHANNEL_FLAG_LOCK_EVENT | SAC_CHANNEL_FLAG_REDRAW_EVENT; Attributes.CloseEvent = m_SacChannelCloseEvent; Attributes.HasNewDataEvent = m_SacChannelHasNewDataEvent; Attributes.LockEvent = m_SacChannelLockEvent; Attributes.RedrawEvent = m_SacChannelRedrawEvent; // // Attempt to open the Secure Io Handler to the SAC command console channel // m_ioHandler = CSecurityIoHandler::Construct(Attributes); if (! m_ioHandler) { return FALSE; } // // Attempt to authenticate the session user // bSuccess = Authenticate(&hToken); if (!bSuccess) { return (FALSE); } // // The user successfully authenticated, so unlock the session // Unlock(); // // Create the scraper we'll use for this session // m_Scraper = new CVTUTF8Scraper( m_ioHandler, m_wCols, m_wRows ); // // Create the shell we'll use for this session // m_Shell = new CShell(); // // Attempt to launch the command console session // bSuccess = m_Shell->StartUserSession ( this, hToken ); ASSERT_STATUS(bSuccess, FALSE); // // We are done with the token // CloseHandle(hToken); // // Start the scraper // bSuccess = m_Scraper->Start(); ASSERT_STATUS(bSuccess, FALSE); // // The scraping thread needs to also listen if the SAC channel closes // AddHandleToWaitOn(m_SacChannelCloseEvent); AddHandleToWaitOn(m_SacChannelLockEvent); AddHandleToWaitOn(m_SacChannelRedrawEvent); // // Create threads to handle Input // m_InputThreadHandle = (HANDLE)_beginthreadex( NULL, 0, CSession::InputThread, this, 0, (unsigned int*)&m_InputThreadTID ); ASSERT_STATUS(m_InputThreadHandle != INVALID_HANDLE_VALUE, FALSE); // // We successfully initialized the session // return( TRUE ); } void CSession::AddHandleToWaitOn( HANDLE hNew ) /*++ Routine Description: This routine adds a handle to an array of handles that will be waiting on in "WaitForIo" Note: the handle array is fixed length, so if there are additional handles which need to be waited on, MAX_HANDLES must be modified Arguments: hNew - the new handle to wait on Return Value: None --*/ { ASSERT( m_dwHandleCount < MAX_HANDLES ); ASSERT( hNew ); // // Add the new handle to our array of multiple handles // m_rghHandlestoWaitOn[ m_dwHandleCount ] = hNew; // // Account for the new handle // m_dwHandleCount++; } void CSession::WaitForIo( VOID ) /*++ Routine Description: This routine is the main work loop for the session and is responsible for: running the scraper handling the session close event handling the session redraw event handling the session lock event Arguments: None Return Value: None --*/ { DWORD dwRetVal = WAIT_FAILED; BOOL ScrapeEnabled; enum { CMD_KILLED = WAIT_OBJECT_0, CHANNEL_CLOSE_EVENT, CHANNEL_LOCK_EVENT, CHANNEL_REDRAW_EVENT }; // // Default: it is not an appropriate time to scrape // ScrapeEnabled = FALSE; // // Service the session's events // while ( m_bContinueSession ) { ULONG HandleCount; // // If scraping is enabled, // then don't wait on the scrape event. // // Note: the redraw event must be the last event // in the m_rghHandlestoWaitOn array // HandleCount = ScrapeEnabled ? m_dwHandleCount - 1 : m_dwHandleCount; dwRetVal = WaitForMultipleObjects( HandleCount, m_rghHandlestoWaitOn, FALSE, m_dwPollInterval ); switch ( dwRetVal ) { case CHANNEL_REDRAW_EVENT: ASSERT(!ScrapeEnabled); // // Tell the screen scraper we need a full dump of it's screen // m_Scraper->DisplayFullScreen(); // // We are now an appropriate time to scrape // ScrapeEnabled = TRUE; break; case WAIT_TIMEOUT: if (ScrapeEnabled) { // // Scrape // m_bContinueSession = m_Scraper->Write(); // // Wait until the event clears by looking // for a WAIT_TIMEOUT // dwRetVal = WaitForSingleObject( m_SacChannelRedrawEvent, 0 ); // // Check the wait result // switch (dwRetVal) { case WAIT_TIMEOUT: // // We need to stop scraping now // ScrapeEnabled = FALSE; break; default: ASSERT (dwRetVal != WAIT_FAILED); if (dwRetVal == WAIT_FAILED) { m_bContinueSession = false; } break; } } break; case CHANNEL_LOCK_EVENT: BOOL bSuccess; HANDLE hToken; // // Lock the session // Lock(); // // Attempt to authenticate the session user // bSuccess = Authenticate(&hToken); if (bSuccess) { // // The user successfully authenticated, so unlock the session // Unlock(); // // Tell the screen scraper we need a full dump of it's screen // m_Scraper->DisplayFullScreen(); } else { // // Loop back around to the WaitForMultipleObjects so that // we can catch the close event if it occurred // NOTHING; } break; case CMD_KILLED: case CHANNEL_CLOSE_EVENT: // // Tell the Input Thread to exit // SetEvent(m_ThreadExitEvent); default: // // incase WAIT_FAILED, call GetLastError() // ASSERT( dwRetVal != WAIT_FAILED); m_bContinueSession = false; break; } } return; } void CSession::Shutdown( VOID ) /*++ Routine Description: This routine does the cleanup work for shutting down the session. Note: this routine does not free memory Arguments: None Return Value: None --*/ { // // Shutdown the shell // if (m_Shell) { m_Shell->Shutdown(); } // // If we started the input thread, // then shut it down // if (m_InputThreadHandle != INVALID_HANDLE_VALUE) { // // Wait for the thread to exit // WaitForSingleObject( m_InputThreadHandle, INFINITE ); CloseHandle(m_InputThreadHandle); } } unsigned int CSession::InputThread( PVOID pParam ) /*++ Routine Description: This routine provides asynchronous support for handling user-input from the IoHandler to the session. Arguments: pParam - thread context Return Value: status --*/ { BOOL bContinueSession; DWORD dwRetVal; CSession *session; HANDLE handles[2]; // // default: listen // bContinueSession = TRUE; // // Get the session object // session = (CSession*)pParam; // // Assign the events to listen for // handles[0] = session->m_SacChannelHasNewDataEvent; handles[1] = session->m_ThreadExitEvent; // // While we should listen: // // 1. wait for a HasNewDataEvent from the SAC driver // 2. wait for a CloseEvent from the SAC driver // while ( bContinueSession ) { // // Wait for our events // dwRetVal = WaitForMultipleObjects( sizeof(handles)/sizeof(HANDLE), handles, FALSE, INFINITE ); switch ( dwRetVal ) { case WAIT_OBJECT_0: { // // Read user-input from the channel // bContinueSession = session->m_Scraper->Read(); break; } default: // // incase WAIT_FAILED, call GetLastError() // ASSERT(dwRetVal != WAIT_FAILED); // // An error has occured, stop listening // bContinueSession = FALSE; break; } } return 0; }