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.
 
 
 
 
 
 

861 lines
18 KiB

/*++
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 <Session.h>
#include <assert.h>
#include <process.h>
#include "secio.h"
#include "utils.h"
#include "scraper.h"
#include "vtutf8scraper.h"
extern "C" {
#include <ntddsac.h>
#include <sacapi.h>
}
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;
}