/*++ Copyright (c) 1999-2001 Microsoft Corporation Module Name: secio.cpp Abstract: This file contains the implementation of the CSecurityIoHandler class which enapsulates the primary security elements used by the CSession class. TODO: this class needs to use a TermCap class to abstract the screen controls - currently, the class implicitly uses VT-UTF8 Author: Brian Guarraci (briangu) 2001. Revision History: --*/ #include #include #include #include #include #include "cmnhdr.h" #include "secio.h" #include "nullio.h" #include "sacmsg.h" #include "utils.h" #define VTUTF8_CLEAR_SCREEN L"\033[2J\033[0;0H" #define READ_BUFFER_LENGTH 512 #define READ_BUFFER_SIZE (READ_BUFFER_LENGTH * sizeof(WCHAR)) CSecurityIoHandler::CSecurityIoHandler( IN CIoHandler *LockedIoHandler, IN CIoHandler *UnlockedIoHandler ) : CLockableIoHandler( LockedIoHandler, UnlockedIoHandler ) /*++ Routine Description: Constructor Arguments: LockedIoHandler - the IoHandler to use when the channel is locked UnlockedIoHandler - the IoHandler to use when the channel is unlocked Return Value: N/A --*/ { // // Lock the IoHandler so that the read/write routines // are disabled. // Lock(); // // Validate that our Io Handler pointers are valid // // This way, we don't have to check everytime we want // to use them. // ASSERT(myLockedIoHandler != NULL); ASSERT(myUnlockedIoHandler != NULL); ASSERT(myIoHandler != NULL); // // initialize our internal lock event // m_InternalLockEvent = 0; // // init // m_StartedAuthentication = FALSE; } CSecurityIoHandler::~CSecurityIoHandler() /*++ Routine Description: Desctructor Arguments: N/A Return Value: N/A --*/ { // // Notify the remote user that we are shutting down the // command console session // WriteResourceMessage(SHUTDOWN_NOTICE); // // release the redraw handler // if (m_RedrawHandler) { delete m_RedrawHandler; } // // The CLockableIoHandler destructor deletes the IoHandlers for us. // NOTHING; // // If the TimeOut thread is running, then stop it // if ((m_TimeOutThreadHandle != INVALID_HANDLE_VALUE) && (m_ThreadExitEvent != NULL)) { // // Tell the TimeOut Thread to exit // SetEvent(m_ThreadExitEvent); // // Wait for the thread to exit // WaitForSingleObject( m_TimeOutThreadHandle, INFINITE ); } // // Close the internal lock event // if (m_InternalLockEvent) { CloseHandle(m_InternalLockEvent); } // // Close the thread exit handle // if (m_ThreadExitEvent != NULL) { CloseHandle(m_ThreadExitEvent); } // // Close the thread handle // if (m_TimeOutThreadHandle != INVALID_HANDLE_VALUE) { CloseHandle(m_TimeOutThreadHandle); } } CSecurityIoHandler* CSecurityIoHandler::Construct( IN SAC_CHANNEL_OPEN_ATTRIBUTES Attributes ) /*++ Routine Description: This routine constructs a security IoHandler connected to a channel with the specified attributes. Arguments: Attributes - the attributes of the new channel Return Value: Success - A ptr to a CSecurityIoHandler object. Failure - NULL --*/ { BOOL bSuccess; CSecurityIoHandler *IoHandler; CIoHandler *SacIoHandler; CIoHandler *NullIoHandler; // // default: failed to construct // bSuccess = FALSE; IoHandler = NULL; SacIoHandler = NULL; NullIoHandler = NULL; do { BOOL bStatus; // // Validate the LockEvent for the timeout thread // ASSERT(Attributes.LockEvent != NULL); if (Attributes.LockEvent == NULL) { break; } ASSERT(Attributes.LockEvent != INVALID_HANDLE_VALUE); if (Attributes.LockEvent == INVALID_HANDLE_VALUE) { break; } // // Validate the CloseEvent for the WaitForInput thread // ASSERT(Attributes.CloseEvent != NULL); if (Attributes.CloseEvent == NULL) { break; } ASSERT(Attributes.CloseEvent != INVALID_HANDLE_VALUE); if (Attributes.CloseEvent == INVALID_HANDLE_VALUE) { break; } // // Validate the RedrawEvent for the Redraw IoHandler // ASSERT(Attributes.RedrawEvent != NULL); if (Attributes.RedrawEvent == NULL) { break; } ASSERT(Attributes.RedrawEvent != INVALID_HANDLE_VALUE); if (Attributes.RedrawEvent == INVALID_HANDLE_VALUE) { break; } // // Attempt to open a SAC channel // SacIoHandler = CSacIoHandler::Construct(Attributes); // // If we failed to open the SAC channel, // then notify the caller that we failed by returning null // if (SacIoHandler == NULL) { break; } // // Get the Null Io Handler to be the locked IoHandler // NullIoHandler = new CNullIoHandler(); // // Create a new SAC IoHandler // IoHandler = new CSecurityIoHandler( NullIoHandler, SacIoHandler ); // // Create the event we will use to signal that a timeout // occured while we were trying to authenticate a user // IoHandler->m_InternalLockEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); ASSERT(IoHandler->m_InternalLockEvent); if (IoHandler->m_InternalLockEvent == 0) { break; } // // Keep the LockEvent for the timeout thread // Keep the CloseEvent for the WaitForInput thread // IoHandler->m_CloseEvent = Attributes.CloseEvent; IoHandler->m_LockEvent = Attributes.LockEvent; IoHandler->m_RedrawEvent = Attributes.RedrawEvent; // // Create the event used to signal the threads to exit // IoHandler->m_ThreadExitEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); if (IoHandler->m_ThreadExitEvent == NULL) { break; } // // Start the timeout thread if we need to // bStatus = IoHandler->InitializeTimeOutThread(); if (! bStatus) { break; } // // Create handler for redraw events // IoHandler->m_RedrawHandler = CRedrawHandler::Construct( IoHandler, Attributes.RedrawEvent ); ASSERT(IoHandler->m_RedrawHandler); if (IoHandler->m_RedrawHandler == NULL) { break; } // // we were successful // bSuccess = TRUE; } while ( FALSE ); // // if we were not successful // then clean up // if (! bSuccess) { // // Note: we do not need to clean up the Lock, Close, and Redraw events // because we do not own them. Also, we do not need // to clean up the NullIo and SacIo IoHandlers because // they are cleaned up by the LockIo parent class. // if (IoHandler) { delete IoHandler; IoHandler = NULL; } } return IoHandler; } BOOL CSecurityIoHandler::Write( PBYTE Buffer, ULONG BufferSize ) /*++ Routine Description: This routine implements the IoHandler Write behavior. Arguments: (see iohandler) Return Value: (see iohandler) --*/ { // // Pass through to the secured Io Handler // return myIoHandler->Write( Buffer, BufferSize ); } BOOL CSecurityIoHandler::Flush( VOID ) /*++ Routine Description: This routine implements the IoHandler Flush behavior. Arguments: (see iohandler) Return Value: (see iohandler) --*/ { // // Pass through to the secured Io Handler // return myIoHandler->Flush(); } BOOL CSecurityIoHandler::Read( PBYTE Buffer, ULONG BufferSize, PULONG ByteCount ) /*++ Routine Description: This routine implements the IoHandler Read behavior and resets the timeout counter when ever there is a successful read. Arguments: (see iohandler) Return Value: (see iohandler) --*/ { BOOL bSuccess; // // Pass through to the secured Io Handler // // if this iohandler is locked, // then the caller will be reading from the NullIoHandler // else they will read from the UnlockedIoHandler // bSuccess = myIoHandler->Read( Buffer, BufferSize, ByteCount ); // // If the sesssion received new user input, // then reset the timeout counter // if (*ByteCount > 0) { ResetTimeOut(); } return bSuccess; } BOOL CSecurityIoHandler::ReadUnlockedIoHandler( PBYTE Buffer, ULONG BufferSize, PULONG ByteCount ) /*++ Routine Description: This routine reads a character from the unlocked io hander so that we can authenticate the user while the scraper still uses the LockedIohandler. resets the timeout counter when ever there is a successful read. Arguments: (see iohandler) Return Value: (see iohandler) --*/ { BOOL bSuccess; // // Pass through to the secured Io Handler // bSuccess = myUnlockedIoHandler->Read( Buffer, BufferSize, ByteCount ); // // If the sesssion received new user input, // then reset the timeout counter // if (*ByteCount > 0) { ResetTimeOut(); } return bSuccess; } BOOL CSecurityIoHandler::HasNewData( PBOOL InputWaiting ) /*++ Routine Description: This routine implements the IoHandler HasNewData behavior. Arguments: (see iohandler) Return Value: (see iohandler) --*/ { // // Pass through to the secured Io Handler // return myIoHandler->HasNewData(InputWaiting); } BOOL CSecurityIoHandler::WriteResourceMessage( IN INT MsgId ) /*++ Routine Description: This routine writes a resource string message to the Unlocked ioHandler. Arguments: MsgId - the id of the message to write Return Value: TRUE - the message was loaded and written FALSE - failed --*/ { UNICODE_STRING UnicodeString = {0}; BOOL bSuccess; // // Default: failed // bSuccess = FALSE; // // Attempt to load the string and write it // do { if ( LoadStringResource(&UnicodeString, MsgId) ) { // // Terminate the string at the %0 marker, if it is present // if( wcsstr( UnicodeString.Buffer, L"%0" ) ) { *((PWCHAR)wcsstr( UnicodeString.Buffer, L"%0" )) = L'\0'; } // // Write the message // bSuccess = m_RedrawHandler->Write( (PUCHAR)UnicodeString.Buffer, (ULONG)(wcslen( UnicodeString.Buffer) * sizeof(WCHAR)) ); if (!bSuccess) { break; } bSuccess = m_RedrawHandler->Flush(); } } while ( FALSE ); return bSuccess; } BOOL CSecurityIoHandler::LoadStringResource( IN PUNICODE_STRING pUnicodeString, IN INT MsgId ) /*++ Routine Description: This is a simple implementation of LoadString(). Arguments: usString - Returns the resource string. MsgId - Supplies the message id of the resource string. Return Value: FALSE - Failure. TRUE - Success. --*/ { NTSTATUS Status; PMESSAGE_RESOURCE_ENTRY MessageEntry; ANSI_STRING AnsiString; Status = RtlFindMessage( NtCurrentPeb()->ImageBaseAddress, (ULONG_PTR) RT_MESSAGETABLE, 0, (ULONG)MsgId, &MessageEntry ); if (!NT_SUCCESS( Status )) { return FALSE; } if (!(MessageEntry->Flags & MESSAGE_RESOURCE_UNICODE)) { RtlInitAnsiString( &AnsiString, (PCSZ)&MessageEntry->Text[ 0 ] ); Status = RtlAnsiStringToUnicodeString( pUnicodeString, &AnsiString, TRUE ); if (!NT_SUCCESS( Status )) { return FALSE; } } else { RtlCreateUnicodeString(pUnicodeString, (PWSTR)MessageEntry->Text); } return TRUE; } BOOL CSecurityIoHandler::AuthenticateCredentials( IN PWSTR UserName, IN PWSTR DomainName, IN PWSTR Password, OUT PHANDLE pUserToken ) /*++ Routine Description: This routine will attempt to authenticate the supplied credentials. Arguments: UserName - Supplied UserName DomainName - Supplied DomainName Password - Supplied Password pUserToken - Holds the valid token for the authenticated user credentials. Return Value: TRUE - Credentials successfully authenticated. FALSE - Credentials failed to authenticate. Security: interface: exposes user input to LogonUser() --*/ { BOOL b; // // Notify the user that we are attempting to authenticate // WriteResourceMessage(LOGIN_IN_PROGRESS); // // Try the authentication. // b = LogonUser( UserName, DomainName, Password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, pUserToken ); if (b) { // // Reset the timeout counter before we start the thread // ResetTimeOut(); } else { // // Wait 3 seconds before returning control to user in // order to slow iterative attacks // Sleep(3000); // // Notify the user that the attempt failed // WriteResourceMessage(LOGIN_FAILURE); // // Wait for a key press // WaitForUserInput(TRUE); } return b; } BOOL CSecurityIoHandler::RetrieveCredential( OUT PWSTR String, IN ULONG StringLength, IN BOOL EchoClearText ) /*++ Routine Description: This routine will request credentials from the user. Those credentials are then returned to the caller. Arguments: String - on success, contains the credential StringLength - the # of WCHARS (length) in the string (includes NULL termination) EchoClearText - TRUE: echo user input in clear text FALSE: echo user input as '*' Return Value: TRUE - Credential recieved. FALSE - Something failed when we were trying to get credentials from the user. Security: interface: external input echos user input --*/ { PWCHAR buffer; ULONG bufferSize; ULONG i, j; BOOLEAN Done = FALSE; BOOL bSuccess; // // validate parameters // if (! String) { return FALSE; } if (StringLength < 1) { return FALSE; } // // default: failed // bSuccess = FALSE; // // allocate the buffer we'll use to read the channel // buffer = new WCHAR[READ_BUFFER_LENGTH]; // // default: start at the first character // i = 0; // // default: we need to read user input // Done = FALSE; // // Attempt to retrieve the credential // while ( !Done ) { // // Wait until the user inputs something // bSuccess = WaitForUserInput(FALSE); if (!bSuccess) { Done = TRUE; continue; } // // Read what the user input // // // we should be in a locked state now // which implies we are using the unlocked // io handler - the scraper is using the locked one // ASSERT(myLockedIoHandler == myIoHandler); bSuccess = ReadUnlockedIoHandler( (PUCHAR)buffer, READ_BUFFER_SIZE, &bufferSize ); if (bSuccess) { // // We have received at least one character // hence by setting this to true, we enable // the internal lock event to succeed in // resetting the authentication attempt // that is, if the user starts to authenticate // and stops before finishing, the timer will // fire and reset the authentication attempt // m_StartedAuthentication = TRUE; // // Process the characters he gave us. // // Note: the buffer contains WCHARs, hence we need to // divide the returned buffersize by sizeof(WCHAR) in // order to get the # of wchars to process. // for ( j = 0; j < bufferSize/sizeof(WCHAR); j++ ) { // // stop if: // // we reached the end of the Credentials buffer (not including the NULL) // we received a CR || LF // if ( (i >= (StringLength-1)) || (buffer[j] == 0x0D) || (buffer[j] == 0x0A) ) { Done = TRUE; break; } // // handle user input // if( buffer[j] == '\b' ) { // // The user gave us a backspace. We should cover up the // character on the screen, then backup our index so we // essentially forget the last thing he gave us. // // If the very first thing the user did was type in a backspace, // no need to back anything up. // if( i > 0 ) { i--; String[i] = L'\0'; bSuccess = m_RedrawHandler->Write( (PUCHAR)L"\b \b", (ULONG)(wcslen(L"\b \b") * sizeof(WCHAR)) ); if (!bSuccess) { // // write failed: exit // Done = TRUE; } } } else if (buffer[j] < ' ') { // // If the character is less than ' ' (a control char), // then ignore it // NOTHING; } else { // // It was a valid character: remember the input and echo it back // to the user. // String[i] = buffer[j]; i++; // // Echo according to caller specifications // bSuccess = m_RedrawHandler->Write( EchoClearText ? (PUCHAR)&buffer[j] : (PUCHAR)L"*", sizeof(WCHAR) ); if (!bSuccess) { // // write failed: exit // Done = TRUE; } } } if (bSuccess) { // // Flush any text echoing we've done. // bSuccess = m_RedrawHandler->Flush(); if (!bSuccess) { // // write failed: exit // Done = TRUE; } } } else { // // read failed: exit // Done = TRUE; } } // // Terminate the credential // String[i] = UNICODE_NULL; // // release the read buffer // delete [] buffer; return bSuccess; } BOOL CSecurityIoHandler::RetrieveCredentials( IN OUT PWSTR UserName, IN ULONG UserNameLength, IN OUT PWSTR DomainName, IN ULONG DomainNameLength, IN OUT PWSTR Password, IN ULONG PasswordLength ) /*++ Routine Description: This routine will request credentials from the user. Those credentials are then returned to the caller. Arguments: UserName - Buffer to hold the UserName UserNameLength - Length of the UserName buffer DomainName - Buffer to hold the DomainName DomainNameLength - Length of the DomainName buffer Password - Buffer to hold the Password PasswordLength - Length of the Password buffer Return Value: TRUE - Credentials recieved. FALSE - Something failed when we were trying to get credentials from the user. --*/ { BOOL HaveDomainName; BOOL bSuccess; // // Initialize the flag that we use to keep track // of when the user first starts to authenticate. // that is, after they enter at least one character // they have started to authenticate. // m_StartedAuthentication = FALSE; // // Initialize our redraw screen // m_RedrawHandler->Reset(); // // Clear the screen // m_RedrawHandler->Write( (PUCHAR)VTUTF8_CLEAR_SCREEN, (ULONG)(wcslen( VTUTF8_CLEAR_SCREEN ) * sizeof(WCHAR)) ); m_RedrawHandler->Flush(); // // Put up the login banner. // if (! WriteResourceMessage(LOGIN_BANNER) ) { return FALSE; } // // Prompt for user name. // if (! WriteResourceMessage(USERNAME_PROMPT) ) { return FALSE; } if ( UserName[0] != UNICODE_NULL ) { // // We were supplied a UserName. Put that up and proceed. // m_RedrawHandler->Write( (PUCHAR)UserName, (ULONG)(wcslen( UserName ) * sizeof(WCHAR)) ); m_RedrawHandler->Flush(); // // If we were given the username, we conclude that // they gave us the domain name as well. We need // to do this since the domain name may be empty // and we automatlically conclude that because the // domain name is empty we need to retrieve it. // HaveDomainName = TRUE; } else { // // Retrieve the UserName. // bSuccess = RetrieveCredential( UserName, UserNameLength, TRUE ); if (!bSuccess) { return FALSE; } // // We need to retrieve the domain name too. // HaveDomainName = FALSE; } // // // m_RedrawHandler->Write( (PUCHAR)L"\r\n", (ULONG)(wcslen(L"\r\n") * sizeof(WCHAR)) ); m_RedrawHandler->Flush(); // // Prompt for domain name // if (! WriteResourceMessage(DOMAINNAME_PROMPT) ) { return FALSE; } // // Prompt for domain name. // if ( HaveDomainName ) { // // We were supplied a username. Put that up and proceed. // m_RedrawHandler->Write( (PUCHAR)DomainName, (ULONG)(wcslen( DomainName ) * sizeof(WCHAR)) ); m_RedrawHandler->Flush(); } else { // // Retrieve the DomainName. // bSuccess = RetrieveCredential( DomainName, DomainNameLength, TRUE ); if (!bSuccess) { return FALSE; } // // If user entered a blank domain, force it to '.' // which implies local machine domain // if (wcslen(DomainName) == 0) { wsprintf( DomainName, L"." ); } } // // // m_RedrawHandler->Write( (PUCHAR)L"\r\n", (ULONG)(wcslen(L"\r\n") * sizeof(WCHAR)) ); // // Prompt for password. // if (! WriteResourceMessage(PASSWORD_PROMPT) ) { return FALSE; } // // Retrieve the Password. // bSuccess = RetrieveCredential( Password, PasswordLength, FALSE ); if (!bSuccess) { return FALSE; } // // // m_RedrawHandler->Write( (PUCHAR)L"\r\n", (ULONG)(wcslen(L"\r\n") * sizeof(WCHAR)) ); m_RedrawHandler->Flush(); return TRUE; } VOID CSecurityIoHandler::ResetTimeOut( VOID ) /*++ Routine Description: This routine resets the StartTickCount to 0 Arguments: None Return Value: None --*/ { ULONG TimerTick; // // Get the current timer tick // TimerTick = GetTickCount(); // // Reset the timeout counter by making the current timer tick // the starting tick count // InterlockedExchange(&m_StartTickCount, TimerTick); #if ENABLE_EVENT_DEBUG { WCHAR blob[256]; wsprintf(blob,L"ResetTimeOut\n"); OutputDebugString(blob); } #endif } BOOL CSecurityIoHandler::TimeOutOccured( VOID ) /*++ Routine Description: This routine determines if the specified timeout interval has been reached. It takes the specified TickCount and compares it to the m_StartTickCount. If the interval equals or exceeds the timeout interval, then a timeout has occured. Arguments: None Return Value: TRUE - The timeout interval has been reached FALSE - otherwise --*/ { BOOL bTimedOut; DWORD DeltaT; // // default: we did not time out // bTimedOut = FALSE; // // See if we timed out // DeltaT = GetAndComputeTickCountDeltaT(m_StartTickCount); if (DeltaT >= m_TimeOutInterval) { // // Reset the timeout counter // ResetTimeOut(); // // We timed out // bTimedOut = TRUE; #if ENABLE_EVENT_DEBUG { WCHAR blob[256]; wsprintf(blob,L"TimeOutOccured\n"); OutputDebugString(blob); } #endif } return bTimedOut; } BOOL CSecurityIoHandler::InitializeTimeOutThread( VOID ) /*++ Routine Description: This routine initializes the timeout thread if timeout behavior is enabled. Arguments: None Return Value: TRUE Success FALSE Otherwise --*/ { BOOL bSuccess; // // default: failed to init // bSuccess = FALSE; // // default: we do not have a timeout thread // m_TimeOutThreadHandle = INVALID_HANDLE_VALUE; // // determine if we need a timeout thread // if we do, then set one up // do { // // If the timeout behavior is disabled, // then we are done. // if (IsTimeOutEnabled() == FALSE) { // // No Initialization required // bSuccess = TRUE; break; } // // determine the time out interval // if (GetTimeOutInterval(&m_TimeOutInterval) == TRUE) { // // Reset the timeout counter before we start the thread // ResetTimeOut(); // // Create thread to handle Input // m_TimeOutThreadHandle = (HANDLE)_beginthreadex( NULL, 0, CSecurityIoHandler::TimeOutThread, this, 0, (unsigned int*)&m_TimeOutThreadTID ); if (m_TimeOutThreadHandle == INVALID_HANDLE_VALUE) { break; } // // we successfully started the thread // bSuccess = TRUE; } } while ( FALSE ); return bSuccess; } BOOL CSecurityIoHandler::GetTimeOutInterval( OUT PULONG TimeOutDuration ) /*++ Routine Description: This routine determines the timeout interval. It attempts to read the registry and use a specified value from there, or defaults. Arguments: TimeOutDuration - the determined timeout interval Return Value: TRUE - TimeOutDuration is valid FALSE - otherwise Security: interface: registry --*/ { DWORD rc; HKEY hKey; DWORD DWord; DWORD dwsize; DWORD DataType; // // See if the user gave us a registry key to define the timeout duration // rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE, SACSVR_PARAMETERS_KEY, 0, KEY_READ, &hKey ); if( rc == NO_ERROR ) { dwsize = sizeof(DWORD); rc = RegQueryValueEx( hKey, SACSVR_TIMEOUT_INTERVAL_VALUE, NULL, &DataType, (LPBYTE)&DWord, &dwsize ); RegCloseKey( hKey ); if ((rc == NO_ERROR) && (DataType == REG_DWORD) && (dwsize == sizeof(DWORD)) ) { // // Convert the specified timeout from minutes --> ms // *TimeOutDuration = DWord * (60 * 1000); // // A timeout interval of 0 is not allowed, default. // if (*TimeOutDuration == 0) { *TimeOutDuration = DEFAULT_TIME_OUT_INTERVAL; } // // clamp the timeout interval to a reasonable value // if (*TimeOutDuration > MAX_TIME_OUT_INTERVAL) { *TimeOutDuration = MAX_TIME_OUT_INTERVAL; } return TRUE; } } // // Default: timeout duration // *TimeOutDuration = DEFAULT_TIME_OUT_INTERVAL; return TRUE; } BOOL CSecurityIoHandler::IsTimeOutEnabled( VOID ) /*++ Routine Description: This routine determines if the timeout behavior is enabled by the system. Arguments: None. Return Value: TRUE - timeout behavior is enabled FALSE - otherwise Security: interface: registry --*/ { DWORD rc; HKEY hKey; DWORD DWord; DWORD dwsize; DWORD DataType; // // See if the user gave us a registry key to disable the timeout behavior // rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE, SACSVR_PARAMETERS_KEY, 0, KEY_READ, &hKey ); if( rc == NO_ERROR ) { dwsize = sizeof(DWORD); rc = RegQueryValueEx( hKey, SACSVR_TIMEOUT_DISABLED_VALUE, NULL, &DataType, (LPBYTE)&DWord, &dwsize ); RegCloseKey( hKey ); if ((rc == NO_ERROR) && (DataType == REG_DWORD) && (dwsize == sizeof(DWORD)) ) { return DWord == 1 ? FALSE : TRUE; } } // // default: timeout is enabled // return TRUE; } unsigned int CSecurityIoHandler::TimeOutThread( PVOID pParam ) /*++ Routine Description: This routine is a the timeout management thread. When the Timeout interval is reached, this routine fires the lock event and causes the session to perform its locking behavior. Arguments: pParam - thread context Return Value: thread return value --*/ { CSecurityIoHandler *IoHandler; BOOL bContinueSession; DWORD dwRetVal; DWORD dwPollInterval; HANDLE handles[2]; ULONG HandleCount; BOOL bRedrawEventSignaled; enum { CHANNEL_THREAD_EXIT_EVENT = WAIT_OBJECT_0, CHANNEL_REDRAW_EVENT }; // // Get the session object // IoHandler = (CSecurityIoHandler*)pParam; // // Assign the events to listen for // handles[0] = IoHandler->m_ThreadExitEvent; handles[1] = IoHandler->m_RedrawEvent; // // default: listen // bContinueSession = TRUE; // // wait on the redraw event // bRedrawEventSignaled = FALSE; // // Poll Interval = 1 second // dwPollInterval = 1000; // // While we should listen... // while ( bContinueSession ) { HandleCount = bRedrawEventSignaled ? 1 : 2; // // Wait for our exit event // dwRetVal = WaitForMultipleObjects( HandleCount, handles, FALSE, dwPollInterval ); switch ( dwRetVal ) { case CHANNEL_REDRAW_EVENT: // // reset the timeout if someone switches back to a channel // IoHandler->ResetTimeOut(); // // We don't need to waint on this event again until it clears // bRedrawEventSignaled = TRUE; break; case WAIT_TIMEOUT: { // // Check for timeout // if (IoHandler->TimeOutOccured()) { // // set the lock event causing // the command console session to lock. // SetEvent(IoHandler->m_LockEvent); // // Set the internal lock event // SetEvent(IoHandler->m_InternalLockEvent); } // // Wait until the event clears by looking // for a WAIT_TIMEOUT // dwRetVal = WaitForSingleObject( IoHandler->m_RedrawEvent, 0 ); // // Check the wait result // switch (dwRetVal) { case WAIT_TIMEOUT: // // It's ok to wait for this event again // bRedrawEventSignaled = FALSE; break; default: ASSERT (dwRetVal != WAIT_FAILED); if (dwRetVal == WAIT_FAILED) { bContinueSession = false; } break; } break; } case CHANNEL_THREAD_EXIT_EVENT: default: // // incase WAIT_FAILED, call GetLastError() // ASSERT(dwRetVal != WAIT_FAILED); // // An error has occured, stop listening // bContinueSession = FALSE; break; } } return 0; } BOOL CSecurityIoHandler::WaitForUserInput( IN BOOL Consume ) /*++ Routine Description: This routine blocks waiting for user input. Arguments: Consume - if TRUE, this routine eats the character that caused the the channel to have new data. Return Value: TRUE - no errors FALSE - otherwise --*/ { BOOL bSuccess; DWORD dwRetVal; BOOL bHasNewData; BOOL done; HANDLE handles[2]; enum { CHANNEL_CLOSE_EVENT = WAIT_OBJECT_0, CHANNEL_LOCK_EVENT }; // // Assign the events to listen for // handles[0] = m_CloseEvent; handles[1] = m_InternalLockEvent; // // Default: we succeeded // bSuccess = TRUE; // // Default: we loop // done = FALSE; // // Wait for a key press // while (!done) { dwRetVal = WaitForMultipleObjects( sizeof(handles) / sizeof(handles[0]), handles, FALSE, 20 // 20ms ); switch(dwRetVal) { case CHANNEL_CLOSE_EVENT: // // The channel closed, we need to exit // // // The channel has locked, // or the timeout has gone fired and we need to // clear the current logon attempt // in either case, we need to exit // done = TRUE; // // Our attempt to get new data failed // bSuccess = FALSE; break; case CHANNEL_LOCK_EVENT: // // clear the internal lock event // ResetEvent(m_InternalLockEvent); if (m_StartedAuthentication) { #if ENABLE_EVENT_DEBUG { WCHAR blob[256]; wsprintf(blob,L"ResettingAuthentication\n"); OutputDebugString(blob); } #endif // // the timeout has gone fired and we need to // clear the current logon attempt // done = TRUE; // // Our attempt to get new data failed // bSuccess = FALSE; } break; case WAIT_TIMEOUT: // // we should be in a locked state now // which implies we are using the unlocked // io handler - the scraper is using the locked one // ASSERT(myLockedIoHandler == myIoHandler); // // determine the input buffer status // bSuccess = myUnlockedIoHandler->HasNewData(&bHasNewData); if (! bSuccess) { done = TRUE; break; } if (bHasNewData) { // // We have new data, so we need to exit // done = TRUE; // // Consume character the character which caused // the waitforuserinput to return // if (Consume) { WCHAR buffer; ULONG bufferSize; bSuccess = ReadUnlockedIoHandler( (PUCHAR)&buffer, sizeof(WCHAR), &bufferSize ); } } break; default: // // We should not get here unless something broke // ASSERT(0); bSuccess = FALSE; done = TRUE; break; } } return bSuccess; }