Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1944 lines
42 KiB

/*++
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 <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <process.h>
#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;
}