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
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;
|
|
|
|
}
|
|
|