// Session.cpp : This file contains the // Created: Feb '98 // Author : a-rakeba // History: // Copyright (C) 1998 Microsoft Corporation // All rights reserved. // Microsoft Confidential #include #include #include #include #include #include #include #include #include #include #include #pragma warning( disable: 4127 ) #pragma warning( disable: 4706 ) using namespace _Utils; using CDebugLevel::TRACE_DEBUGGING; using CDebugLevel::TRACE_HANDLE; using CDebugLevel::TRACE_SOCKET; extern HANDLE g_hSyncCloseHandle; CSession::CSession() : CIoHandler(), CRFCProtocol(), CShell(), CScraper() { m_bNtVersionGTE5 = false; m_dwTickCountAtLogon = 0; m_wNumFailedLogins = 0; m_bContinueSession = true; m_dwHandleCount = 0; m_wIsAnAdmin = DONT_KNOW; m_bIsStreamMode = false; m_bIsTelnetClientsGroupMember = false; m_hLogHandle = NULL; m_hToken = NULL; m_bIsTelnetVersion2 = false; m_AuthenticationId.HighPart = m_AuthenticationId.LowPart = 0; m_pszTermType[0] = 0; m_bNegotiatedTermType = false; m_wCols = DEFAULT_COLS; m_wRows = DEFAULT_ROWS; m_dwAllowTrustedDomain = DEFAULT_ALLOW_TRUSTED_DOMAIN; m_dwIdleSessionTimeOut = DEFAULT_IDLE_SESSION_TIME_OUT; m_pszDefaultDomain = NULL; m_pszDefaultShell = NULL; m_pszDifferentShell = NULL; m_pszSwitchToKeepShellRunning = NULL; m_pszSwitchForOneTimeUseOfShell = NULL; m_pszLoginScript = NULL; m_dwNTLMSetting = DEFAULT_SECURITY_MECHANISM; m_dwMaxFailedLogins = DEFAULT_MAX_FAILED_LOGINS; m_dwSysAuditing = DEFAULT_SYSAUDITING; m_dwLogEvents = DEFAULT_LOGEVENTS; m_dwLogAdmin = DEFAULT_LOGADMIN; m_dwLogFailures = DEFAULT_LOGFAILURES; m_dwLogToFile = DEFAULT_LOGTOFILE; m_pszUserName[0] = 0; m_szMachineName[0] = 0; m_szPeerHostName[0] = 0; m_szUser[0] = L'\0'; m_szDomain[0] = L'\0'; administrators = pTelnetClientsSid = NULL; } void CSession::FreeInitialVariables() { CShell::FreeInitialVariables(); delete [] m_pszDifferentShell; delete [] m_pszDefaultDomain; delete [] m_pszDefaultShell; delete [] m_pszSwitchForOneTimeUseOfShell; delete [] m_pszSwitchToKeepShellRunning; delete [] m_pszLoginScript; m_pszDefaultDomain = NULL; m_pszSwitchForOneTimeUseOfShell = NULL; m_pszDefaultShell = NULL; m_pszSwitchToKeepShellRunning = NULL; m_pszLoginScript = NULL; m_pszDifferentShell = NULL; } CSession::~CSession() { FreeInitialVariables(); FreeSid(administrators); delete pTelnetClientsSid; TELNET_CLOSE_HANDLE( m_hToken ); } bool CSession::Init() { // get a handle to log events with _chVERIFY2( m_hLogHandle = RegisterEventSource(NULL, L"TlntSvr" ) ); //Get the registry values from HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\TelnetServer if( !GetRegistryValues( ) ) { return( FALSE ); } if( !CIoHandler::Init( this ) ) { return( FALSE ); } CShell::Init( this ); CRFCProtocol::Init( this ); CScraper::Init( this ); SetLastError( 0 ); AddHandleToWaitOn( CIoHandler::m_oReadFromPipe.hEvent ); if( !CIoHandler::IssueFirstReadOnPipe() ) { return( FALSE ); } CScraper::m_dwPollInterval = PRE_SESSION_STATE_TIMEOUT; return ( TRUE ); } void CSession::AddHandleToWaitOn( HANDLE hNew ) { _chASSERT( m_dwHandleCount < MAX_HANDLES ); _chASSERT( hNew ); m_rghHandlestoWaitOn[ m_dwHandleCount ] = hNew; m_dwHandleCount++; } #define HALF_K 512 void CSession::WaitForIo() { DWORD dwRetVal = WAIT_FAILED; char szMessageBuffer[HALF_K + 1]; while( m_bContinueSession ) { dwRetVal = WaitForMultipleObjects(m_dwHandleCount, m_rghHandlestoWaitOn, FALSE, CScraper::m_dwPollInterval ); switch( dwRetVal ) { case PIPE_READ : m_bContinueSession = CIoHandler::OnReadFromPipeCompletion(); break; case SOCKET_READ: m_bContinueSession = CIoHandler::OnReadFromSocketCompletion(); break; case CMD_KILLED: m_bContinueSession = false; break; case READ_FROM_CMD : m_dwPollInterval = MIN_POLL_INTERVAL; m_bContinueSession = CShell::OnDataFromCmdPipe(); break; case WAIT_TIMEOUT: if( CIoHandler::m_SocketControlState == STATE_AUTH_NAME || CIoHandler::m_SocketControlState == STATE_AUTH_PASSWORD ) { DWORD dwNumBytesWritten = 0; _snprintf( szMessageBuffer, HALF_K, "\r\n%s\r\n%s", TIMEOUT_STR, TERMINATE ); CIoHandler::SendTerminateString(szMessageBuffer); CIoHandler::WriteToClient( ); FinishIncompleteIo( ( HANDLE ) CIoHandler::m_sSocket, &( CIoHandler::m_oWriteToSocket) , &dwNumBytesWritten ); m_bContinueSession = false; } else { m_bContinueSession = CScraper::OnWaitTimeOut(); if( m_bContinueSession ) { m_bContinueSession = ( CScraper::IsSessionTimedOut() == false ); } } break; default: //incase WAIT_FAILED, call GetLastError() _chVERIFY2( dwRetVal != WAIT_FAILED ); m_bContinueSession = false; } } return; } void CSession::CollectPeerInfo () { m_dwTickCountAtLogon = GetTickCount(); INT len = sizeof( m_ssPeerAddress ); TCHAR szDebugString[500]; if( getpeername(CIoHandler::m_sSocket, (struct sockaddr *) &m_ssPeerAddress, &len) == SOCKET_ERROR ) { _TRACE( TRACE_DEBUGGING, "Error: getpeername() : %lu", GetLastError() ); } if( getnameinfo((SOCKADDR*)&m_ssPeerAddress, len, CSession::m_szPeerHostName, sizeof(CSession::m_szPeerHostName)-1, NULL, 0, NI_NUMERICHOST) ) { _TRACE( TRACE_DEBUGGING, "Error: getnameinfo() : %d", (DWORD)GetLastError() ); } else { _TRACE(TRACE_DEBUGGING, "getnameinfo() : %s",CSession::m_szPeerHostName); } if(getnameinfo((SOCKADDR*)&m_ssPeerAddress, len, m_szMachineName, sizeof(m_szMachineName)-1, NULL, 0, 0) ) { _TRACE( TRACE_DEBUGGING, "Error: getnameinfo() - machinename : %d", (DWORD)GetLastError() ); strcpy( m_szMachineName, "" ); // No Attack :-) Baskar } else { _TRACE(TRACE_DEBUGGING, "getnameinfo() - machinename : %s",m_szMachineName); } return; } bool CSession::GetRegistryValues() { DWORD dwRetVal; HKEY hk = NULL; DWORD dwDisp = 0; if( dwRetVal = TnSecureRegCreateKeyEx( HKEY_LOCAL_MACHINE, REG_PARAMS_KEY, NULL, NULL, REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED , NULL, &hk, &dwDisp, 0) ) { _TRACE( TRACE_DEBUGGING, "Error: RegCreateKey() -- 0x%1x", dwRetVal); LogEvent( EVENTLOG_ERROR_TYPE, MSG_REGISTRYKEY, REG_PARAMS_KEY ); return ( FALSE ); } if( !GetRegistryDW( hk, NULL, L"MaxFailedLogins", &m_dwMaxFailedLogins, DEFAULT_MAX_FAILED_LOGINS,FALSE ) ) { return false; } if( !GetRegistryDW( hk, NULL, L"AllowTrustedDomain", &m_dwAllowTrustedDomain, DEFAULT_ALLOW_TRUSTED_DOMAIN,FALSE ) ) { return false; } if( !GetRegistryDW( hk, NULL, L"SecurityMechanism", &m_dwNTLMSetting, DEFAULT_SECURITY_MECHANISM,FALSE ) ) { return false; } if( !GetRegistryDW( hk, NULL, L"LogToFile", &m_dwLogToFile, DEFAULT_LOGTOFILE,FALSE ) ) { return false; } if( !GetRegistryDW( hk, NULL, L"EventLoggingEnabled", &m_dwSysAuditing, DEFAULT_SYSAUDITING,FALSE ) ) { return false; } if( !GetRegistryDW( hk, NULL, L"LogNonAdminAttempts", &m_dwLogEvents, DEFAULT_LOGEVENTS,FALSE ) ) { return false; } if( !GetRegistryDW( hk, NULL, L"LogAdminAttempts", &m_dwLogAdmin, DEFAULT_LOGADMIN,FALSE ) ) { return false; } if( !GetRegistryDW( hk, NULL, L"LogFailures", &m_dwLogFailures, DEFAULT_LOGFAILURES,FALSE ) ) { return false; } DWORD dwModeOfOperation = DEFAULT_MODE_OF_OPERATION; DWORD dwKillAllApps; if( !GetRegistryDW( hk, NULL, L"AltKeyMapping", &m_dwAltKeyMapping, DEFAULT_ALT_KEY_MAPPING,FALSE ) ) { return false; } if( !GetRegistryDW( hk, NULL, L"IdleSessionTimeOut", &m_dwIdleSessionTimeOut, DEFAULT_IDLE_SESSION_TIME_OUT,FALSE ) ) { return false; } if( !GetRegistryDW( hk, NULL, L"DisconnectKillAllApps", &dwKillAllApps, DEFAULT_DISCONNECT_KILLALL_APPS,FALSE ) ) { return false; } if( !GetRegistryDW( hk, NULL, L"ModeOfOperation", &dwModeOfOperation, DEFAULT_MODE_OF_OPERATION,FALSE ) ) { return false; } m_bIsStreamMode = false; if( dwModeOfOperation == STREAM_MODE ) { m_bIsStreamMode = true; } if( !GetRegistryString( hk, NULL, L"DefaultShell", &m_pszDefaultShell, DEFAULT_SHELL,FALSE ) ) { return false; } if( !GetRegistryString( hk, NULL, SWITCH_TO_KEEP_SHELL_RUNNING, &m_pszSwitchToKeepShellRunning, DEFAULT_SWITCH_TO_KEEP_SHELL_RUNNING ,FALSE) ) { return false; } if( !GetRegistryString( hk, NULL, SWITCH_FOR_ONE_TIME_USE_OF_SHELL, &m_pszSwitchForOneTimeUseOfShell, DEFAULT_SWITCH_FOR_ONE_TIME_USE_OF_SHELL,FALSE ) ) { return false; } if( !GetRegistryString( hk, NULL, L"Shell", &m_pszDifferentShell, L"",FALSE ) ) { return false; } //When server comes up it creates all keys. So, assume they are available. //Default values need not be correct as in the case of following. if( !GetRegistryString( hk, NULL, L"LoginScript", &m_pszLoginScript, L"",FALSE ) ) { return false; } if( !GetRegistryString( hk, NULL, L"DefaultDomain", &m_pszDefaultDomain, DEFAULT_DOMAIN,FALSE ) ) { return false; } RegCloseKey( hk ); if( !GetWindowsVersion( &m_bNtVersionGTE5 ) ) { return( FALSE ); } return( TRUE ); } #ifdef LOGGING_ENABLED //This function logs the logon/logoff details as opted by the user. //This function can be furthur optimized by using the values calculated at logon //at the time of logoff. void CSession::LogIfOpted( BOOL result, LOGEVENT logon, BOOL bNTLMAuth ) { LPWSTR wideLogStr; UCHAR *logStr; BOOL isAdmin = false; if( result == LOGOFF && !m_dwLogFailures ) { return; } if ( result == LOGON && !m_dwLogAdmin && !m_dwLogEvents) { return; } if ( result == LOGON && ( m_dwLogAdmin || m_dwLogEvents ) ) { if( m_wIsAnAdmin == DONT_KNOW ) { IsAnAdminstratorOrMember(); } ( m_wIsAnAdmin == ADMIN) ? isAdmin = true : isAdmin = false; if ( !isAdmin && !m_dwLogEvents ) { return; } if( isAdmin && !m_dwLogAdmin ) { return; } } if (!result && bNTLMAuth) { DWORD dwSize = strlen(NTLM_LOGON_FAILED) + strlen(CSession::m_szPeerHostName) + strlen(CSession::m_szMachineName) + 1; logStr = new UCHAR[IPC_HEADER_SIZE + dwSize]; if (!logStr) { return; } SfuZeroMemory(logStr, IPC_HEADER_SIZE + dwSize); logStr[0] = AUDIT_CLIENT; memcpy(logStr + 1, &dwSize, sizeof(DWORD)); // No Attack :-), Baskar _snprintf((LPSTR)(logStr + IPC_HEADER_SIZE), (dwSize - 1), NTLM_LOGON_FAILED, CSession::m_szPeerHostName, CSession::m_szMachineName); if (m_dwSysAuditing) { ConvertSChartoWChar( ( LPSTR )( logStr+IPC_HEADER_SIZE ), &wideLogStr ); LogEvent(EVENTLOG_AUDIT_FAILURE, 0, wideLogStr); } if (m_dwLogToFile) { WriteToPipe(CIoHandler::m_hWritePipe, (LPVOID)logStr, IPC_HEADER_SIZE + dwSize, &(CIoHandler::m_oWriteToPipe)); } delete[] logStr; return; } DWORD dwRestOfMsgLen = strlen( ADMINISTRATOR ) + strlen( CSession::m_pszDomain) + strlen( BLANK ) + strlen( CSession::m_pszUserName ) + strlen( BLANK ) + strlen( FAILED_TO_LOG ) + strlen( BLANK ) + strlen( OFF ) + strlen( BLANK ) + strlen( FROM ) + strlen( BLANK ) + strlen( CSession::m_szPeerHostName ) + strlen( BLANK ) + strlen( CSession::m_szMachineName ) + 1 ; logStr = new UCHAR[ IPC_HEADER_SIZE + dwRestOfMsgLen ]; if( !logStr ) { return; } logStr[0] = AUDIT_CLIENT; memcpy( logStr+1, &dwRestOfMsgLen, sizeof( DWORD ) ); // No Attack, Baskar :-) _snprintf( ( LPSTR )( logStr+IPC_HEADER_SIZE ), (dwRestOfMsgLen - 1), "%s%s\\%s %s %s %s %s %s", isAdmin ? ADMINISTRATOR : "", CSession::m_pszDomain, CSession::m_pszUserName, result ? LOGGED : FAILED_TO_LOG, logon ? ON : OFF, FROM, CSession::m_szPeerHostName, CSession::m_szMachineName ); if( m_dwSysAuditing ) { ConvertSChartoWChar( ( LPSTR )( logStr+IPC_HEADER_SIZE ), &wideLogStr ); LogEvent( result ? ( WORD )EVENTLOG_AUDIT_SUCCESS: ( WORD )EVENTLOG_AUDIT_FAILURE, 0, wideLogStr ); delete[] wideLogStr; } if( m_dwLogToFile ) { WriteToPipe( CIoHandler::m_hWritePipe, ( LPVOID )logStr, IPC_HEADER_SIZE + dwRestOfMsgLen, &( CIoHandler::m_oWriteToPipe ) ); } delete[] logStr; } #endif bool CSession::IsAnAdminstratorOrMember() { TOKEN_GROUPS *buffer = NULL; DWORD needed_length = 0; BOOL success = FALSE; ULONG x; TCHAR szDomain[ MAX_PATH + 1 ]; DWORD dwDomainLen = MAX_PATH + 1; SID_NAME_USE sidNameUse; m_wIsAnAdmin = NON_ADMIN; m_bIsTelnetClientsGroupMember = false; if (NULL == administrators) { SID_IDENTIFIER_AUTHORITY local_system_authority = SECURITY_NT_AUTHORITY; if (! AllocateAndInitializeSid( &local_system_authority, 2, /* there are only two sub-authorities */ SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0,0,0,0,0,0, /* Don't care about the rest */ &administrators )) { administrators = NULL; return false; } } if (NULL == pTelnetClientsSid) { needed_length = 0; DWORD dwErr = 0; LookupAccountName( NULL, TELNETCLIENTS_GROUP_NAME, pTelnetClientsSid, &needed_length, szDomain, &dwDomainLen, &sidNameUse ); pTelnetClientsSid = ( PSID ) new UCHAR[ needed_length ]; //Even if if allocation fails just go ahead. success = LookupAccountName( NULL, TELNETCLIENTS_GROUP_NAME, pTelnetClientsSid, &needed_length, szDomain, &dwDomainLen, &sidNameUse ); if( !success ) { if (pTelnetClientsSid) { delete pTelnetClientsSid; pTelnetClientsSid = NULL; } //return false; //We should not return back if TelnetClients group does not exist } } /* We are making the first call to GetTokenInformation to get the size of the memory required for the group data */ _chVERIFY2( success = GetTokenInformation( m_hToken, TokenGroups, buffer, 0, &needed_length) ); buffer = (TOKEN_GROUPS *)GlobalAlloc(GPTR, needed_length); if (buffer == NULL) { return false; } _chVERIFY2( success = GetTokenInformation( m_hToken, TokenGroups, buffer, needed_length, & needed_length) ); if (! success) { _TRACE(TRACE_DEBUGGING,L"GetTokenInformation failed"); GlobalFree(buffer); return false; } _TRACE(TRACE_DEBUGGING,L"GetTokenInformation succeeded Token = 0x%lx", m_hToken); success = FALSE; for (x = 0; x < buffer->GroupCount; x ++) { if (EqualSid(buffer->Groups[x].Sid, administrators) && (buffer->Groups[x].Attributes & SE_GROUP_ENABLED) && (!(buffer->Groups[x].Attributes & SE_GROUP_USE_FOR_DENY_ONLY)) ) { m_bIsTelnetClientsGroupMember = true; m_wIsAnAdmin = ADMIN; break; } if( pTelnetClientsSid && !m_bIsTelnetClientsGroupMember ) { if ( EqualSid(buffer->Groups[x].Sid, pTelnetClientsSid )) { m_bIsTelnetClientsGroupMember = true; break; } } } GlobalFree(buffer); return ( success ? true : false ); } void CSession::Shutdown() { #ifdef LOGGING_ENABLED if( m_fLogonUserResult ) { LogIfOpted( SUCCESS, LOGOFF ); } #endif CScraper::Shutdown(); CIoHandler::Shutdown(); CShell::Shutdown(); _chVERIFY2( DeregisterEventSource( m_hLogHandle ) ); } bool CSession::CheckGroupMembership ( bool *fIsAdmin, bool *pfIsTelnetClientsMember) { *pfIsTelnetClientsMember = false; *fIsAdmin = false; if( m_wIsAnAdmin == DONT_KNOW ) { IsAnAdminstratorOrMember(); } if( m_wIsAnAdmin == ADMIN) { *fIsAdmin = true; *pfIsTelnetClientsMember = true; //All admins are default members. // As defined by US :-))) } *pfIsTelnetClientsMember = m_bIsTelnetClientsGroupMember; return( true ); }