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.
 
 
 
 
 
 

1573 lines
44 KiB

// RFCProto.cpp : This file contains the
// Created: Feb '98
// Author : a-rakeba
// History:
// Copyright (C) 1998 Microsoft Corporation
// All rights reserved.
// Microsoft Confidential
#include <CmnHdr.h>
#ifdef WHISTLER_BUILD
#include "ntverp.h"
#else
#include <SolarVer.h>
#endif //WHISTLER_BUILD
#include <Common.ver>
#include <RFCProto.h>
#include <Debug.h>
#include <FSM.h>
#include <TelnetD.h>
#include <Session.h>
#include <Scraper.h>
#include <vtnt.h>
#pragma warning( disable: 4242 )
#pragma warning( disable: 4127 )
#pragma warning(disable: 4100)
#pragma warning(disable: 4244)
extern FSM_TRANSITION telnetTransTable[];
extern FSM_TRANSITION subNegTransTable[];
using namespace _Utils;
using CDebugLevel::TRACE_DEBUGGING;
using CDebugLevel::TRACE_HANDLE;
using CDebugLevel::TRACE_SOCKET;
COORD g_coCurPosOnClient = { 0, 3 };
CRFCProtocol::CRFCProtocol()
{
fSubTermType = false;
m_dwSubNawsByteNumber = 0;
fSubAuth = false;
m_wNTLMDataBufferIndex = 0;
fSubNaws = false;
m_fSubNawsFirstTime = true;
m_dwExcludeTerm = 0;
m_pSession = 0;
SfuZeroMemory( m_remoteOptions, sizeof( m_remoteOptions ) );
SfuZeroMemory( m_localOptions, sizeof( m_localOptions ) );
m_fPasswordConcealMode = false;
//optionCmd = ?
//m_telnetState = ?
//m_subNegState = ?
m_fWaitingForResponseToA_DO_ForTO_ECHO = false;
m_fWaitingForAResponseToA_WILL_ForTO_ECHO = false;;
m_fWaitingForResponseToA_DO_ForTO_SGA = false;
m_fWaitingForAResponseToA_WILL_ForTO_SGA = false;
m_fWaitingForResponseToA_DO_ForTO_TXBINARY = false;
m_fWaitingForAResponseToA_WILL_ForTO_TXBINARY = false;
m_fWaitingForResponseToA_DO_ForTO_TERMTYPE = false;
m_fWaitingForAResponseToA_DO_ForTO_AUTH = false;
m_fWaitingForResponseToA_DO_ForTO_NAWS = false;
m_bIsUserNameProvided = false;
m_fSubNewEnv = false;
m_dwWhatVal = E_UNDEFINED;
m_dwWhichVar = E_UNKNOWN;
m_szCurrentEnvVariable[0] = 0;
m_fWaitingForResponseToA_DO_ForTO_NEWENVIRON = false;
BuildFSMs();
}
CRFCProtocol::~CRFCProtocol()
{
}
void
CRFCProtocol::Init ( CSession* pSession )
{
_chASSERT( pSession != 0 );
m_pSession = pSession;
}
bool
CRFCProtocol::InitialNegotiation
(
)
{
UCHAR puchBuffer[1024];
PUCHAR pCursor;
INT bytes_to_write;
pCursor = puchBuffer;
m_pSession->CIoHandler::m_SocketControlState = CIoHandler::STATE_INIT;
if( m_pSession->m_dwNTLMSetting != NO_NTLM )
{
// this is actually the place where we need to figure out if we can do
// authentication and what kind. If there atleast one authentication type
// available then we send the DO AUTH option to the client else we don't.
// For now this checks for only NTLM auth. has to be made more generic in V2.
if ( m_pSession->StartNTLMAuth() )
{
m_fWaitingForAResponseToA_DO_ForTO_AUTH = true;
DO_OPTION( pCursor, TO_AUTH );
pCursor += 3;
}
else
{
// since we don't have any security package the registry setting is
//meaningless, we should just fall back to the username/password.
m_pSession->m_dwNTLMSetting = NO_NTLM;
}
}
m_fWaitingForAResponseToA_WILL_ForTO_ECHO = true;
WILL_OPTION( pCursor, TO_ECHO );
pCursor += 3;
m_fWaitingForAResponseToA_WILL_ForTO_SGA = true;
WILL_OPTION( pCursor, TO_SGA );
pCursor += 3;
m_fWaitingForResponseToA_DO_ForTO_NEWENVIRON = true;
DO_OPTION( pCursor, TO_NEW_ENVIRON );
pCursor += 3;
m_fWaitingForResponseToA_DO_ForTO_NAWS = true;
DO_OPTION( pCursor, TO_NAWS );
pCursor += 3;
m_fWaitingForResponseToA_DO_ForTO_TXBINARY = true;
DO_OPTION( pCursor, TO_TXBINARY );
pCursor += 3;
m_fWaitingForAResponseToA_WILL_ForTO_TXBINARY = true;
WILL_OPTION( pCursor, TO_TXBINARY );
pCursor += 3;
if( NO_NTLM == m_pSession->m_dwNTLMSetting )
{
m_pSession->CIoHandler::m_SocketControlState = CIoHandler::STATE_BANNER_FOR_AUTH;
}
//This is before we start writing anything on to the socket asyncronously.
//So, writing to m_WriteToSocketBuff does not cause problem
bytes_to_write = (INT) (pCursor - puchBuffer);
if (bytes_to_write &&
((m_pSession->CIoHandler::m_dwWriteToSocketIoLength + bytes_to_write) < MAX_WRITE_SOCKET_BUFFER))
{
memcpy( m_pSession->CIoHandler::m_WriteToSocketBuff, puchBuffer, bytes_to_write);
m_pSession->CIoHandler::m_dwWriteToSocketIoLength += bytes_to_write;
return ( true );
}
return ( false );
}
// have to keep updating m_WriteToSocketBuffer while in Action() functions
// have to set the IO response to WRITE_TO_SOCKET and somehow convey this
// have to keep updating pButBack
// have to finally update the lpdwIoSize
CIoHandler::IO_OPERATIONS
CRFCProtocol::ProcessDataReceivedOnSocket
(
LPDWORD lpdwIoSize
)
{
#define TWO_K 2048
CIoHandler::IO_OPERATIONS ioOpsToPerform = 0;
LPBYTE pByte;
LPBYTE pPutBack = m_pSession->CIoHandler::m_pReadFromSocketBufferCursor;
DWORD dwLength = *lpdwIoSize;
UCHAR szMsgBuf[TWO_K];
UCHAR* p = szMsgBuf;
szMsgBuf[0] = 0;
INT tableIndex;
for( pByte = m_pSession->CIoHandler::m_pReadFromSocketBufferCursor;
pByte<(m_pSession->CIoHandler::m_pReadFromSocketBufferCursor + dwLength);
++pByte )
{
tableIndex = m_telnetFSM[ m_telnetState ][ *pByte ];
if( (p - szMsgBuf) > TWO_K )
{
_TRACE( TRACE_DEBUGGING, "too much data; possible suspicious activity" );
_chASSERT( 0 );
}
else
{
(this->*(telnetTransTable[ tableIndex ].pmfnAction))(&pPutBack,&p,*pByte);
}
m_telnetState = telnetTransTable[ tableIndex ].uchNextState;
}
DWORD dwMsgLen = p - szMsgBuf;
if( dwMsgLen > 0 )
{
if (dwMsgLen > TWO_K)
{
dwMsgLen = TWO_K;
}
m_pSession->CIoHandler::WriteToSocket( szMsgBuf, dwMsgLen);
ioOpsToPerform |= CIoHandler::WRITE_TO_SOCKET;
}
*lpdwIoSize = pPutBack - m_pSession->CIoHandler::m_pReadFromSocketBufferCursor;
return ( ioOpsToPerform );
#undef TWO_K
}
void CRFCProtocol::BuildFSMs( void )
{
FSMInit( m_telnetFSM, telnetTransTable, NUM_TS_STATES );
m_telnetState = TS_DATA;
FSMInit( m_subNegFSM, subNegTransTable, NUM_SS_STATES );
m_subNegState = SS_START;
}
void
CRFCProtocol::FSMInit
(
UCHAR fSM[][ NUM_CHARS ],
void* transTable1,
INT numStates
)
{
FSM_TRANSITION* transTable = (FSM_TRANSITION*)transTable1;
INT s, tableIndex, c;
for( c = 0; c < NUM_CHARS; ++c)
{
for( tableIndex = 0; tableIndex < numStates; ++tableIndex )
{
fSM[ tableIndex ][ c ] = T_INVALID;
}
}
for( tableIndex = 0; transTable[ tableIndex ].uchCurrState != FS_INVALID;
++tableIndex )
{
s = transTable[ tableIndex ].uchCurrState;
if( transTable[ tableIndex ].wInputChar == TC_ANY )
{
for( c = 0; c < NUM_CHARS; ++c )
{
if( fSM[ s ][ c ] == T_INVALID )
{
fSM[ s ][ c ] = tableIndex;
}
}
}
else
{
fSM[ s ][ transTable[ tableIndex ].wInputChar ] = tableIndex;
}
}
for( c = 0; c < NUM_CHARS; ++c)
{
for( tableIndex = 0; tableIndex < numStates; ++tableIndex )
{
if( fSM[ tableIndex ][ c ] == T_INVALID )
{
fSM[ tableIndex ][ c ] = 32;//tableIndex;
}
}
}
}
void CRFCProtocol::NoOp( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
_TRACE( TRACE_DEBUGGING, "NoOp()" );
}
void CRFCProtocol::GoAhead( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
_TRACE( TRACE_DEBUGGING, "GoAhead()" );
}
void CRFCProtocol::EraseLine( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
_TRACE( TRACE_DEBUGGING, "EraseLine()" );
}
void CRFCProtocol::EraseChar( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
_TRACE( TRACE_DEBUGGING, "EraseChar()" );
}
#define INCREMENT_ROWS( rows, inc ) \
{ \
rows += inc; \
if( rows >= m_pSession->CSession::m_wRows ) \
{ \
rows = m_pSession->CSession::m_wRows - 1;\
wTypeOfCoords = RELATIVE_COORDS;\
}\
}
#define INCREMENT_COLS( cols, inc ) \
{ \
cols += inc;\
if( cols >= m_pSession->CSession::m_wCols ) \
{\
cols = 0;\
}\
}
#define IGNORE_0x0A_FOLLOWING_0x0D( dwIndex, dwDataLen ) \
if( dwIndex < dwDataLen && rgchSessionData[ dwIndex -1 ] == L'\r' && rgchSessionData[ dwIndex] == L'\n' ) \
{ \
dwIndex++; \
}
void CRFCProtocol::FillVtntHeader( UCHAR *pucBlob, WORD wTypeOfCoords,
WORD wNoOfRows, WORD wNoOfCols,
WORD wCurrenRowOnClient, WORD wCurrenColOnClient,
SHORT *psCurrentCol,
LPTSTR rgchSessionData, DWORD dwDataLen )
{
if( !pucBlob )
{
return;
}
//Fill the header
VTNT_CHAR_INFO* pVTNTCharInfo = ( VTNT_CHAR_INFO* ) pucBlob;
//csbi.wAttributes is filled by v2 server with following meaning
//When a scrolling case is detected, this is set to 1.
pVTNTCharInfo->csbi.wAttributes = wTypeOfCoords;
pVTNTCharInfo->coDest.X = 0;
pVTNTCharInfo->coDest.Y = 0;
pVTNTCharInfo->coSizeOfData.Y = wNoOfRows;
pVTNTCharInfo->coSizeOfData.X = wNoOfCols;
pVTNTCharInfo->srDestRegion.Left = wCurrenColOnClient;
pVTNTCharInfo->srDestRegion.Top = wCurrenRowOnClient;
pVTNTCharInfo->srDestRegion.Right = pVTNTCharInfo->srDestRegion.Left + pVTNTCharInfo->coSizeOfData.X - 1;
pVTNTCharInfo->srDestRegion.Bottom = wCurrenRowOnClient + pVTNTCharInfo->coSizeOfData.Y - 1;
pVTNTCharInfo->coCursorPos.Y = pVTNTCharInfo->srDestRegion.Bottom;
//Fill char info structs
//iterate thru each character in the string
//for each character store the corr. values in CHAR_INFO struct
PCHAR_INFO pCharInfo = ( PCHAR_INFO )(pucBlob + sizeof( VTNT_CHAR_INFO ));
DWORD dwIndex = 0;
DWORD dwCtr = 0;
DWORD dwSize = wNoOfRows * wNoOfCols;
WORD wLastNonSpaceCol = 0;
while( dwCtr < dwSize )
{
if( dwIndex >= dwDataLen )
{
if( !wLastNonSpaceCol )
{
wLastNonSpaceCol = dwCtr;
}
pCharInfo[dwCtr].Char.UnicodeChar = L' ';
}
else
{
if( rgchSessionData[ dwIndex ] == L'\t' )
{
rgchSessionData[ dwIndex ] = L' ';
}
else if( rgchSessionData[ dwIndex ] == L'\r' )
{
rgchSessionData[ dwIndex ] = L' ';
while ( dwCtr < dwSize - 1 && ( ( dwCtr + 1 ) % wNoOfCols != 0 || dwCtr == 0 ) )
{
pCharInfo[dwCtr].Char.UnicodeChar = L' ';
pCharInfo[dwCtr].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
dwCtr++;
}
}
else if( rgchSessionData[ dwIndex ] == L'\n' )
{
dwIndex++;
continue;
}
pCharInfo[dwCtr].Char.UnicodeChar = rgchSessionData[ dwIndex ];
}
pCharInfo[dwCtr].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
dwIndex++;
dwCtr++;
}
if( !wLastNonSpaceCol )
{
wLastNonSpaceCol = dwCtr;
}
pVTNTCharInfo->coCursorPos.X = ( wCurrenColOnClient + wLastNonSpaceCol ) % m_pSession->CSession::m_wCols;
if( psCurrentCol )
{
*psCurrentCol = pVTNTCharInfo->coCursorPos.X;
}
}
//caller gets data blob and its size
//this needs to sent to the client
//caller needs to free memory
/*
The following routine is primarily used for stream mode and vtnt.
cmd outputs a stream of ascii chars. When in vtnt, the client expects VTNT_CHAR_INFO structs.
The chars should be in the form of rectangles of console screen.
So, This routine does this conversion. For this,
1)We need to keep track of cursor position on client
2)Know For any given bloc of data whether to start on a new row on client or on the current row
We break the data from cmd into two rectangle.
1) rectagle on the current row of breadth 1 ( one row rectangle )
2) rectangle from next row onwards ( second rectangle )
*/
bool
CRFCProtocol::StrToVTNTResponse
(
LPSTR rgchData,
DWORD dwDataSize,
VOID** ppResponse,
DWORD* pdwSize
)
{
_TRACE( TRACE_DEBUGGING, "StrToVTNTResponse()" );
DWORD dwIndex = 0;
COORD coRectSize = { m_pSession->CSession::m_wCols, 0 }; //size of the rectagular data
WORD wNoOfColsOnCurrentRow = 0;
WORD wSpacesInserted = 0;
LPTSTR rgchSessionData = 0;
DWORD dwDataLen = 0;
static WORD wTypeOfCoords = ABSOLUTE_COORDS;
if( rgchData == NULL || pdwSize == NULL || ppResponse == NULL )
{
return ( false );
}
dwDataLen = MultiByteToWideChar( GetConsoleCP(), 0, rgchData, dwDataSize, NULL, 0 );
rgchSessionData = new WCHAR[ dwDataLen ];
if( !rgchSessionData )
{
return false;
}
MultiByteToWideChar( GetConsoleCP(), 0, rgchData, dwDataSize, rgchSessionData, dwDataLen );
//make one pass over the stream from cmd to find the amt of space needed to hold converted VTNT data
dwIndex = 0;
//Find number of chars on to the current row. ie; on the one row rectangle
while( dwIndex < dwDataLen && //Size of data
g_coCurPosOnClient.X + ( WORD )dwIndex < coRectSize.X && //In a single row on client
rgchSessionData[ dwIndex ] != L'\r' && //Not new line
g_coCurPosOnClient.X !=0
)
{
dwIndex ++;
IGNORE_0x0A_FOLLOWING_0x0D( dwIndex, dwDataLen );
}
wNoOfColsOnCurrentRow = dwIndex;
if( g_coCurPosOnClient.X !=0 && rgchSessionData[ dwIndex ] == L'\r' )
{
dwIndex++;
IGNORE_0x0A_FOLLOWING_0x0D( dwIndex, dwDataLen );
wNoOfColsOnCurrentRow = coRectSize.X - g_coCurPosOnClient.X;
wSpacesInserted = wNoOfColsOnCurrentRow - ( dwIndex - 1 );
}
//Find the number of rows
while( dwIndex < dwDataLen )
{
WORD wCol = 0;
while( dwIndex + wCol < dwDataLen &&
rgchSessionData[ dwIndex + wCol ] != L'\r' &&
wCol < coRectSize.X )
{
wCol++;
}
dwIndex += wCol;
dwIndex++;
coRectSize.Y++;
IGNORE_0x0A_FOLLOWING_0x0D( dwIndex, dwDataLen );
}
int size = 0;
if( wNoOfColsOnCurrentRow > 0 )
{
//size for one row rectangle
size += sizeof( VTNT_CHAR_INFO ) + sizeof( CHAR_INFO ) * wNoOfColsOnCurrentRow;
}
if( coRectSize.Y > 0 )
{
//size for rest of rectangle
size += sizeof( VTNT_CHAR_INFO ) + sizeof( CHAR_INFO ) * coRectSize.Y * coRectSize.X;
}
UCHAR* pucBlob = new UCHAR[ size ];
UCHAR* pucBlobHead = pucBlob;
if( !pucBlob )
{
_chASSERT( 0 );
goto ExitOnError;
}
SfuZeroMemory( pucBlob, size );
if( wNoOfColsOnCurrentRow > 0 )
{
//Fill one row rectangle
FillVtntHeader( pucBlob, wTypeOfCoords,
1, wNoOfColsOnCurrentRow,
g_coCurPosOnClient.Y, g_coCurPosOnClient.X,
NULL,
rgchSessionData, wNoOfColsOnCurrentRow );
INCREMENT_COLS( g_coCurPosOnClient.X, wNoOfColsOnCurrentRow );
pucBlob += sizeof( VTNT_CHAR_INFO ) + sizeof( CHAR_INFO ) * wNoOfColsOnCurrentRow;
}
if( coRectSize.Y > 0 )
{
//Fill second rectangle
if( g_coCurPosOnClient.Y != 0 )
{
g_coCurPosOnClient.Y++;
}
FillVtntHeader( pucBlob, wTypeOfCoords,
coRectSize.Y, coRectSize.X,
g_coCurPosOnClient.Y, 0,
&g_coCurPosOnClient.X,
rgchSessionData+wNoOfColsOnCurrentRow-wSpacesInserted,
dwDataLen - ( wNoOfColsOnCurrentRow-wSpacesInserted ) );
INCREMENT_ROWS( g_coCurPosOnClient.Y, coRectSize.Y - 1 );
}
*ppResponse = (VOID*) pucBlobHead;
*pdwSize = size;
delete[] rgchSessionData;
return ( true );
ExitOnError:
delete[] rgchSessionData;
return ( false );
}
void CRFCProtocol::AreYouThere( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
_TRACE( TRACE_DEBUGGING, "AreYouThere()" );
if( !m_pSession->CSession::m_bIsStreamMode &&
m_pSession->CIoHandler::m_SocketControlState == m_pSession->CIoHandler::STATE_SESSION )
{
m_pSession->CScraper::WriteMessageToCmd( L"\r\nYES\r\n" );
}
else
{
if( _strcmpi( VTNT, m_pSession->CSession::m_pszTermType ) == 0 )
{
DWORD dwSize = 0;
PCHAR pResponse = NULL;
if( !StrToVTNTResponse( " YES ", strlen( " YES " ), (VOID**) &pResponse, &dwSize ) )
{
return;
}
if( !pResponse || (dwSize == 0) )
{
return;
}
memcpy( *pBuffer, pResponse, dwSize ); // Don't know size of pBuffer, Baskar. Attack ?
*pBuffer += dwSize;
delete [] pResponse;
}
else
{
(*pBuffer)[0] = '\r';
(*pBuffer)[1] = '\n';
(*pBuffer)[2] = '[';
(*pBuffer)[3] = 'Y';
(*pBuffer)[4] = 'e';
(*pBuffer)[5] = 's';
(*pBuffer)[6] = ']';
(*pBuffer)[7] = '\r';
(*pBuffer)[8] = '\n';
(*pBuffer)[9] = 0;
*pBuffer += 9;
}
}
}
void CRFCProtocol::AbortOutput( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
_TRACE( TRACE_DEBUGGING, "AbortOutput()" );
}
void CRFCProtocol::InterruptProcess( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b)
{
_TRACE( TRACE_DEBUGGING, "InterruptProcess()" );
_chVERIFY2( GenerateConsoleCtrlEvent( CTRL_C_EVENT, 0 ) );
}
void CRFCProtocol::Break( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
_TRACE( TRACE_DEBUGGING, "Break()" );
_chVERIFY2( GenerateConsoleCtrlEvent( CTRL_C_EVENT, 0 ) );
}
void CRFCProtocol::DataMark( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
// basically a no op for now
_TRACE( TRACE_DEBUGGING, "DataMark()" );
}
void CRFCProtocol::PutBack( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
_TRACE( TRACE_DEBUGGING, "PutBack()" );
*( *ppPutBack ) = b;
(*ppPutBack)++;
}
void CRFCProtocol::RecordOption( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
_TRACE( TRACE_DEBUGGING, "RecordOption()" );
m_optionCmd = b;
}
void CRFCProtocol::Abort( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
// basically a no op for now
_TRACE( TRACE_DEBUGGING, "Abort()" );
}
void CRFCProtocol::WillNotSup( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
//DO, DONT logic
_TRACE( TRACE_DEBUGGING, "WillNotSup() - %d ", b );
if( m_optionCmd == TC_DO )
{
if( m_localOptions[ b ] == ENABLED )
{
}
else
{
PUCHAR p = *pBuffer;
WONT_OPTION( p, b );
*pBuffer += 3;
}
}
else if( m_optionCmd == TC_DONT )
{
if( m_localOptions[ b ] == ENABLED )
{
m_localOptions[ b ] = DISABLED;
}
else
{
}
}
}
void CRFCProtocol::DoNotSup( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
//WILL, WONT logic
_TRACE( TRACE_DEBUGGING, "DoNotSup() - %d ", b );
if( m_optionCmd == TC_WILL )
{
if( m_remoteOptions[ b ] == ENABLED )
{
}
else
{
PUCHAR p = *pBuffer;
DONT_OPTION( p, b );
*pBuffer += 3;
}
}
else if( m_optionCmd == TC_WONT )
{
if( m_remoteOptions[ b ] == ENABLED )
{
m_remoteOptions[ b ] = DISABLED;
}
else
{
}
}
return;
}
void CRFCProtocol::DoEcho( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
//WILL, WONT logic
_TRACE( TRACE_DEBUGGING, "DoEcho()" );
if( m_optionCmd == TC_WILL )
{
if( m_remoteOptions[ b ] == ENABLED )
{
}
else if( m_fWaitingForResponseToA_DO_ForTO_ECHO )
{
m_remoteOptions[ b ] = ENABLED;
m_fWaitingForResponseToA_DO_ForTO_ECHO = false;
}
else
{
m_remoteOptions[ b ] = ENABLED;
PUCHAR p = *pBuffer;
DO_OPTION( p, b );
*pBuffer += 3;
m_fWaitingForResponseToA_DO_ForTO_ECHO = true;
}
}
else if( m_optionCmd == TC_WONT )
{
if( m_fWaitingForResponseToA_DO_ForTO_ECHO )
{
m_fWaitingForResponseToA_DO_ForTO_ECHO = false;
}
else if( m_remoteOptions[ b ] == ENABLED )
{
m_remoteOptions[ b ] = DISABLED;
}
else
{
}
}
return;
}
void CRFCProtocol::DoNaws( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
//WILL, WONT logic
_TRACE( TRACE_DEBUGGING, "DoNaws()" );
if( m_optionCmd == TC_WILL )
{
if( m_remoteOptions[ b ] == ENABLED )
{
}
else if( m_fWaitingForResponseToA_DO_ForTO_NAWS )
{
m_remoteOptions[ b ] = ENABLED;
m_fWaitingForResponseToA_DO_ForTO_NAWS = false;
}
else
{
PUCHAR p = *pBuffer;
DO_OPTION( p, b );
*pBuffer += 3;
m_fWaitingForResponseToA_DO_ForTO_NAWS = true;
m_remoteOptions[ b ] = ENABLED;
}
}
else if( m_optionCmd == TC_WONT )
{
if( m_fWaitingForResponseToA_DO_ForTO_NAWS )
{
m_fWaitingForResponseToA_DO_ForTO_NAWS = false;
}
else if( m_remoteOptions[ b ] == ENABLED )
{
m_remoteOptions[ b ] = DISABLED;
}
else
{
}
}
return;
}
void CRFCProtocol::DoSuppressGA( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
//WILL, WONT logic
_TRACE( TRACE_DEBUGGING, "DoSuppressGA()" );
if( m_optionCmd == TC_WILL )
{
if( m_remoteOptions[ b ] == ENABLED )
{
}
else if( m_fWaitingForResponseToA_DO_ForTO_SGA )
{
m_remoteOptions[ b ] = ENABLED;
m_fWaitingForResponseToA_DO_ForTO_SGA = false;
}
else
{
m_remoteOptions[ b ] = ENABLED;
PUCHAR p = *pBuffer;
DO_OPTION( p, b );
*pBuffer += 3;
m_fWaitingForResponseToA_DO_ForTO_SGA = true;
}
}
else if( m_optionCmd == TC_WONT )
{
if( m_fWaitingForResponseToA_DO_ForTO_SGA )
{
m_fWaitingForResponseToA_DO_ForTO_SGA = false;
}
else if( m_remoteOptions[ b ] == ENABLED )
{
m_remoteOptions[ b ] = DISABLED;
}
else
{
}
}
}
void CRFCProtocol::DoTxBinary( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
//WILL, WONT logic
_TRACE( TRACE_DEBUGGING, "DoTxBinary()" );
if( m_optionCmd == TC_WILL )
{
if( m_remoteOptions[ b ] == ENABLED )
{
}
else if( m_fWaitingForResponseToA_DO_ForTO_TXBINARY )
{
m_remoteOptions[ b ] = ENABLED;
m_fWaitingForResponseToA_DO_ForTO_TXBINARY = false;
}
else
{
m_remoteOptions[ b ] = ENABLED;
PUCHAR p = *pBuffer;
DO_OPTION( p, b );
*pBuffer += 3;
m_fWaitingForResponseToA_DO_ForTO_TXBINARY = true;
}
}
else if( m_optionCmd == TC_WONT )
{
if( m_fWaitingForResponseToA_DO_ForTO_TXBINARY )
{
m_fWaitingForResponseToA_DO_ForTO_TXBINARY = false;
}
else if( m_remoteOptions[ b ] == ENABLED )
{
m_remoteOptions[ b ] = DISABLED;
}
else
{
}
DisAllowVtnt( pBuffer );
}
}
void CRFCProtocol::AskForSendingNewEnviron( PUCHAR* pBuffer )
{
if( m_remoteOptions[ TO_NEW_ENVIRON ] == ENABLED )
{
DWORD dwLen = 0;
//dwLen will be incremented by the macro and will leave
//it with exact number of bytes used
DO_NEW_ENVIRON_SUB_NE( (*pBuffer ), TO_NEW_ENVIRON, dwLen );
*pBuffer += dwLen;
//This is broken into 2 sub negos for supporting linux.
//When we ask, user, sfutlntvar, sfutlntmode variables in single shot, it
// is not giving data about even user. So, ask in 2 phases.
dwLen = 0;
DO_NEW_ENVIRON_SUB_NE_MY_VARS( (*pBuffer ), TO_NEW_ENVIRON, dwLen );
*pBuffer += dwLen;
}
}
void CRFCProtocol::AskForSendingTermType( PUCHAR* pBuffer )
{
if( m_remoteOptions[ TO_TERMTYPE ] == ENABLED )
{
DO_TERMTYPE_SUB_NE( (*pBuffer ) );
*pBuffer += 6;
}
}
void CRFCProtocol::DoNewEnviron( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
//WILL, WONT logic
_TRACE( TRACE_DEBUGGING, "DoNewEnviron()" );
if( m_optionCmd == TC_WILL )
{
if( m_remoteOptions[ b ] == ENABLED )
{
}
else if( m_fWaitingForResponseToA_DO_ForTO_NEWENVIRON )
{
m_remoteOptions[ b ] = ENABLED;
m_fWaitingForResponseToA_DO_ForTO_NEWENVIRON = false;
AskForSendingNewEnviron( pBuffer );
}
else
{
// Some clients are pro active, they tell us that they WILL Terminal type.
PUCHAR p = *pBuffer;
DO_OPTION( p, b );
*pBuffer += 3;
m_fWaitingForResponseToA_DO_ForTO_NEWENVIRON = true;
m_remoteOptions[ b ] = ENABLED;
}
}
else if( m_optionCmd == TC_WONT )
{
//Give login prompt. No way of getting user name
SubNewEnvShowLoginPrompt( ppPutBack, pBuffer, b );
if( m_fWaitingForResponseToA_DO_ForTO_NEWENVIRON )
{
m_fWaitingForResponseToA_DO_ForTO_NEWENVIRON = false;
}
else if( m_remoteOptions[ b ] == ENABLED )
{
m_remoteOptions[ b ] = DISABLED;
}
else
{
}
}
return;
}
void CRFCProtocol::SubNewEnvShowLoginPrompt( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
m_pSession->CIoHandler::m_bWaitForEnvOptionOver = true;
}
void CRFCProtocol::DoTermType( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
//WILL, WONT logic
_TRACE( TRACE_DEBUGGING, "DoTermType()" );
if( m_optionCmd == TC_WILL )
{
if( m_remoteOptions[ b ] == ENABLED )
{
}
else if( m_fWaitingForResponseToA_DO_ForTO_TERMTYPE )
{
m_remoteOptions[ b ] = ENABLED;
m_fWaitingForResponseToA_DO_ForTO_TERMTYPE = false;
AskForSendingTermType( pBuffer );
}
else
{
// Some clients are pro active, they tell us that they WILL Terminal type.
PUCHAR p = *pBuffer;
DO_OPTION( p, b );
*pBuffer += 3;
m_remoteOptions[ b ] = ENABLED;
m_fWaitingForResponseToA_DO_ForTO_TERMTYPE = false;
}
}
else if( m_optionCmd == TC_WONT )
{
if( m_fWaitingForResponseToA_DO_ForTO_TERMTYPE )
{
m_fWaitingForResponseToA_DO_ForTO_TERMTYPE = false;
// we default to vt100.
strncpy( m_pSession->CSession::m_pszTermType, VT100, (sizeof(m_pSession->CSession::m_pszTermType) - 1));
m_pSession->CSession::m_bIsStreamMode = true;//Set it to stream mode
// set a flag to continue the telnet session
m_pSession->CSession::m_bNegotiatedTermType = true;
}
else if( m_remoteOptions[ b ] == ENABLED )
{
m_remoteOptions[ b ] = DISABLED;
// theoretically should never happen. because once this option
// is enabled it should never be disabled.
_chASSERT(0);
}
else
{
}
}
}
void CRFCProtocol::DoAuthentication( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
//WILL, WONT logic
_TRACE( TRACE_DEBUGGING, "DoAuthentication()" );
if( m_optionCmd == TC_WILL )
{
if( m_remoteOptions[ b ] == ENABLED )
{
}
else if( m_fWaitingForAResponseToA_DO_ForTO_AUTH )
{
m_remoteOptions[ b ] = ENABLED;
if ( m_pSession->CIoHandler::m_SocketControlState == CIoHandler::STATE_INIT )
{
m_pSession->CIoHandler::m_SocketControlState = CIoHandler::STATE_NTLMAUTH;
}
PUCHAR p = *pBuffer;
DO_AUTH_SUB_NE_NTLM(p);
*pBuffer += 8;
m_fWaitingForAResponseToA_DO_ForTO_AUTH = false;
}
else
{
//presently, this should not happen
}
}
else if( m_optionCmd == TC_WONT )
{
if( m_fWaitingForAResponseToA_DO_ForTO_AUTH )
{
m_fWaitingForAResponseToA_DO_ForTO_AUTH = false;
if( m_pSession->m_dwNTLMSetting == NTLM_ONLY )
{
char *p = (char *)*pBuffer;
sprintf(p, "%s%s", NTLM_ONLY_STR, TERMINATE); // Don't know the size of pbuffer here -- Baskar, Attack ?
*pBuffer += strlen(p);
m_pSession->CIoHandler::m_SocketControlState = CIoHandler::STATE_TERMINATE;
m_pSession->CIoHandler::m_fShutDownAfterIO = true;
}
if ( m_pSession->CIoHandler::m_SocketControlState == CIoHandler::STATE_INIT )
{
m_pSession->CIoHandler::m_SocketControlState = CIoHandler::STATE_BANNER_FOR_AUTH;
}
}
else if( m_remoteOptions[ b ] == ENABLED )
{
m_remoteOptions[ b ] = DISABLED;
// theoretically should never happen. because once this option
// is enabled it should never be disabled. Since server initiates
// the negotiation and currently our server never re-negotiates.
_chASSERT(0);
}
else
{
}
}
}
void CRFCProtocol::WillTxBinary( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
// DO, DONT logic
_TRACE( TRACE_DEBUGGING, "WillTxBinary()" );
if( m_optionCmd == TC_DO )
{
if( m_localOptions[ b ] == ENABLED )
{
}
else if( m_fWaitingForAResponseToA_WILL_ForTO_TXBINARY )
{
m_fWaitingForAResponseToA_WILL_ForTO_TXBINARY = false;
m_localOptions[ b ] = ENABLED;
}
else
{
//I want to enable this option
PUCHAR p = *pBuffer;
WILL_OPTION( p, b );
*pBuffer += 3;
m_localOptions[ b ] = ENABLED;
}
}
else if( m_optionCmd == TC_DONT )
{
if( m_fWaitingForAResponseToA_WILL_ForTO_TXBINARY )
{
m_fWaitingForAResponseToA_WILL_ForTO_TXBINARY = false;
}
else if( m_localOptions[ b ] == ENABLED )
{
m_localOptions[ b ] = DISABLED;
}
else
{
}
DisAllowVtnt( pBuffer );
}
}
void CRFCProtocol::DisAllowVtnt( PUCHAR *pBuffer )
{
//Bug:1003 - VTNT no BINARY mode
//Check if Term type is VTNT. If so, renegotiate termtype.
//Now that binary is nomore, VTNT is not an option. VTNT needs binary.
if( !( m_dwExcludeTerm & TERMVTNT ) )
{
m_dwExcludeTerm = TERMVTNT;
if( _strcmpi( m_pSession->CSession::m_pszTermType, VTNT ) == 0 )
{
//re negotiation of term type
AskForSendingTermType( pBuffer );
}
}
}
void CRFCProtocol::WillSuppressGA( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
// DO, DONT logic
_TRACE( TRACE_DEBUGGING, "WillSuppressGA()" );
if( m_optionCmd == TC_DO )
{
if( m_localOptions[ b ] == ENABLED )
{
}
else if( m_fWaitingForAResponseToA_WILL_ForTO_SGA )
{
m_fWaitingForAResponseToA_WILL_ForTO_SGA = false;
m_localOptions[ b ] = ENABLED;
}
else
{
//I want to enable this option
PUCHAR p = *pBuffer;
WILL_OPTION( p, b );
*pBuffer += 3;
m_localOptions[ b ] = ENABLED;
}
}
else if( m_optionCmd == TC_DONT )
{
if( m_fWaitingForAResponseToA_WILL_ForTO_SGA )
{
m_fWaitingForAResponseToA_WILL_ForTO_SGA = false;
}
else if( m_localOptions[ b ] == ENABLED )
{
m_localOptions[ b ] = DISABLED;
}
else
{
}
}
}
void CRFCProtocol::WillEcho( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
// DO, DONT logic
_TRACE( TRACE_DEBUGGING, "WillEcho()" );
if( m_optionCmd == TC_DO )
{
if( m_localOptions[ b ] == ENABLED )
{
}
else if( m_fWaitingForAResponseToA_WILL_ForTO_ECHO )
{
m_fWaitingForAResponseToA_WILL_ForTO_ECHO = false;
m_localOptions[ b ] = ENABLED;
}
else
{
//I want to enable this option
PUCHAR p = *pBuffer;
WILL_OPTION( p, b );
*pBuffer += 3;
m_localOptions[ b ] = ENABLED;
}
}
else if( m_optionCmd == TC_DONT )
{
if( m_fWaitingForAResponseToA_WILL_ForTO_ECHO )
{
m_fWaitingForAResponseToA_WILL_ForTO_ECHO = false;
}
else if( m_localOptions[ b ] == ENABLED )
{
m_localOptions[ b ] = DISABLED;
}
else
{
}
}
}
void CRFCProtocol::SubOption( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
INT tableIndex = m_subNegFSM[ m_subNegState ][ b ];
if( subNegTransTable[ tableIndex ].pmfnAction )
{
(this->*(subNegTransTable[ tableIndex ].pmfnAction))(ppPutBack, pBuffer, b);
m_subNegState = subNegTransTable[ tableIndex ].uchNextState;
}
else
{
/*Should not happen */
_chASSERT( 0 );
}
}
void CRFCProtocol::FindVariable()
{
m_dwWhichVar = E_UNKNOWN;
if( _strcmpi( m_szCurrentEnvVariable, USER ) == 0 )
{
m_dwWhichVar = E_USER;
}
else if( _strcmpi( m_szCurrentEnvVariable, SFUTLNTVER ) == 0 )
{
m_dwWhichVar = E_SFUTLNTVER;
}
else if( _strcmpi( m_szCurrentEnvVariable, SFUTLNTMODE ) == 0 )
{
m_dwWhichVar = E_SFUTLNTMODE;
}
else
{
}
}
void CRFCProtocol::SubNewEnvGetValue( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
if( m_dwWhatVal == E_UNDEFINED )
{
FindVariable();
m_szCurrentEnvVariable[0] = 0;
}
/* Here,
if m_szCurrentEnvVariable[0] != 0, variable has value as in m_szCurrentEnvVariable[0]
else it has value as in m_dwWhatVal */
switch( m_dwWhichVar )
{
case E_USER:
{
m_bIsUserNameProvided = true;
strncpy(m_pSession->CSession::m_pszUserName, m_szCurrentEnvVariable, (sizeof(m_pSession->CSession::m_pszUserName) - 1));
}
break;
case E_SFUTLNTVER:
//set by default to current version
if( _strcmpi( m_szCurrentEnvVariable, VERSION1 ) == 0 )
{
;// version 1
}
else if( _strcmpi( m_szCurrentEnvVariable, VERSION2 ) == 0 )
{
m_pSession->CSession::m_bIsTelnetVersion2 = true; //version 2
}
break;
case E_SFUTLNTMODE:
if( _strcmpi( m_szCurrentEnvVariable, STREAM ) == 0 )
{
m_pSession->CSession::m_bIsStreamMode = true;//Set it to stream mode
}
break;
}
m_dwWhichVar = E_UNKNOWN;
m_dwWhatVal = E_UNDEFINED;
m_szCurrentEnvVariable[0] = 0;
}
void CRFCProtocol::SubNewEnvGetVariable( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
/*For us, it doesn't matter whether a variable is VAR or USERVAR
Not bothering about any difference between IS and INFO */
/* VALUE is present */
m_dwWhatVal = E_DEFINED_BUT_NONE;
m_dwWhichVar = E_UNKNOWN;
if( m_szCurrentEnvVariable[0] != 0 )
{
FindVariable();
}
else
{
//Should not happen
_chASSERT( 0 );
}
m_szCurrentEnvVariable[0] = 0;
}
void CRFCProtocol::SubNewEnvGetString( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
static char str[] = " ";
str[0] = b;
if( strlen( m_szCurrentEnvVariable ) < MAX_PATH )
{
strcat( m_szCurrentEnvVariable, str );
}
m_fSubNewEnv = true;
}
void CRFCProtocol::SubTermType( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
char str[2];
str[0] = ( char ) b;
str[1] = 0;
if( strlen( m_pSession->CSession::m_pszTermType ) < MAX_PATH )
strcat( m_pSession->CSession::m_pszTermType, str );
fSubTermType = true;
}
void CRFCProtocol::SubAuth( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
if( m_wNTLMDataBufferIndex >= 2047 )
{
//if this happens, it is likely that somebody is screwing around.
_TRACE( TRACE_DEBUGGING, "Error: NTLMDataBuffer overflow" );
_chASSERT( 0 );
}
else
{
m_NTLMDataBuffer[ m_wNTLMDataBufferIndex++ ] = b;
}
fSubAuth = true;
}
#define MAX_ROWS 300
#define MAX_COLS 300
void CRFCProtocol::SubNaws( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
m_dwSubNawsByteNumber++;
//we ignore 1st and 3rd bytes because that is too many rows and
//cols for NT to support
if( 2 == m_dwSubNawsByteNumber )
{
if( b > 0 && b <= MAX_COLS )
m_pSession->CSession::m_wCols = b;
}
if( 4 == m_dwSubNawsByteNumber )
{
if( b > 0 && b <= MAX_ROWS )
m_pSession->CSession::m_wRows = b;
}
fSubNaws = true;
}
void CRFCProtocol::ChangeCurrentTerm()
{
if( !( m_pSession->CScraper::m_dwTerm & TERMVTNT ) )
{
m_pSession->CScraper::DeleteCMStrings();
}
m_pSession->CSession::InitTerm();
}
void CRFCProtocol::SubEnd( LPBYTE* ppPutBack, PUCHAR* pBuffer, BYTE b )
{
//other clients might first send DEC-vt100
//best to follow Assigned Numbers RFC
//and also change our ( not so compliant ;-) ) GUI telnet client
if( fSubTermType )
{
if( _strcmpi( VT52, m_pSession->CSession::m_pszTermType ) == 0 ||
_strcmpi( VT100, m_pSession->CSession::m_pszTermType ) == 0 ||
_strcmpi( ANSI, m_pSession->CSession::m_pszTermType ) == 0 ||
( !( m_dwExcludeTerm & TERMVTNT ) &&
_strcmpi( VTNT, m_pSession->CSession::m_pszTermType ) == 0 ) )
{
// we got a good term type.
// set a flag to continue the telnet session
m_pSession->CSession::m_bNegotiatedTermType = true;
if( m_pSession->CSession::m_dwTerm != 0 )
{
ChangeCurrentTerm();
}
}
else
{
if( _strcmpi( m_szPrevTermType,
m_pSession->CSession::m_pszTermType ) != 0 )
{
(*pBuffer)[0] = TC_IAC;
(*pBuffer)[1] = TC_SB;
(*pBuffer)[2] = TO_TERMTYPE;
(*pBuffer)[3] = TT_SEND;
(*pBuffer)[4] = TC_IAC;
(*pBuffer)[5] = TC_SE;
(*pBuffer)[6] = 0;
*pBuffer += 6;
strncpy(m_szPrevTermType, m_pSession->CSession::m_pszTermType, (sizeof(m_szPrevTermType) - 1));
m_pSession->CSession::m_pszTermType[0] = 0;
}
else
{
//the client sent the same term type twice
//this means that the client has sent the last term type
//in its list
// this means that the client supports terminal types
// but it does not support anything we support;
// too bad ; either we should default to vt100
// or we should demand that the client don't do
// terminal types and we should go into NVT ASCII (tty) mode
// The client doesn't support anything that we like, we
// default to vt100 instead of doing the right thing as described
// above.
strncpy( m_pSession->CSession::m_pszTermType, VT100, (sizeof(m_pSession->CSession::m_pszTermType)-1));
m_pSession->CSession::m_bIsStreamMode = true;//Set it to stream mode
// set a flag to continue the telnet session
m_pSession->CSession::m_bNegotiatedTermType = true;
if( m_pSession->CSession::m_dwTerm != 0 )
{
ChangeCurrentTerm();
}
}
}
fSubTermType = false;
}
if( fSubAuth )
{
m_pSession->CIoHandler::DoNTLMAuth( (unsigned char*) m_NTLMDataBuffer,
m_wNTLMDataBufferIndex, pBuffer );
m_wNTLMDataBufferIndex = 0;
fSubAuth = false;
}
if( fSubNaws )
{
fSubNaws = false;
if( !m_fSubNawsFirstTime )
{
//For the first time we need to wait till IOHandles are created for
//Making following initialization
if( !m_pSession->CScraper::SetCmdInfo() )
{
_chASSERT( 0 );
}
}
else
{
m_fSubNawsFirstTime = false;
}
m_dwSubNawsByteNumber = 0;
}
if( m_fSubNewEnv )
{
SubNewEnvGetValue( ppPutBack, pBuffer, b );
m_fSubNewEnv = false;
}
m_subNegState = SS_START;
}
/*From RFC:
Specifically careful analysis should be done to determine which variables are "safe"
to set prior to having the client login. An example of a bad choice would be permitting
a variable to be changed that allows an intruder to circumvent or compromise the
login/authentication program itself */