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.
 
 
 
 
 
 

2294 lines
66 KiB

// TlntSvr.cpp : This file contains the
// Created: Feb '98
// History:
// Copyright (C) 1998 Microsoft Corporation
// All rights reserved.
// Microsoft Confidential
// TlntSvr.cpp : Implementation of WinMain
// Note: Proxy/Stub Information
// To build a separate proxy/stub DLL,
// run nmake -f TlntSvrps.mk in the project directory.
#include <Stdafx.h>
#include <Stdio.h>
#include <stdlib.h>
#include <time.h>
#include <WinError.h>
#include <TChar.h>
#include <New.h>
#include <OleAuto.h>
#include <Resource.h>
#include <Debug.h>
#include <MsgFile.h>
#include <TlntUtils.h>
#include <regutil.h>
#include <TlntSvr.h>
#ifdef WHISTLER_BUILD
#include "tlntsvr_i.c"
#else
#ifndef NO_PCHECK
#include <PiracyCheck.h>
#endif
#endif
#include <TelnetD.h>
#include <TelntSrv.h>
#include <EnumData.h>
#include <EnCliSvr.h>
#include "locresman.h"
#include <lm.h>
#include <lmaccess.h>
#pragma warning(disable:4100)
#define heapalloc(x) (HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, x))
#define heapfree(x) (HeapFree(GetProcessHeap(), 0, x))
CTelnetService* g_pTelnetService = 0;
HANDLE *g_phLogFile = NULL;
LPWSTR g_pszTelnetInstallPath = NULL;
LPWSTR g_pszLogFile = NULL;
LONG g_lMaxFileSize = 0;
bool g_fIsLogFull = false;
bool g_fLogToFile = false;
#ifdef WHISTLER_BUILD
DWORD g_dwStartType = SERVICE_DISABLED;
#else
DWORD g_dwStartType = SERVICE_AUTO_START;
#endif
HINSTANCE g_hInstRes = NULL;
void LogEvent ( WORD wType, DWORD dwEventID, LPCTSTR pFormat, ... );
using namespace _Utils;
using CDebugLevel::TRACE_DEBUGGING;
using CDebugLevel::TRACE_HANDLE;
using CDebugLevel::TRACE_SOCKET;
TCHAR g_szErrRegDelete[ MAX_STRING_LENGTH ];
TCHAR g_szErrOpenSCM [ MAX_STRING_LENGTH ];
TCHAR g_szErrOpenSvc [ MAX_STRING_LENGTH ];
TCHAR g_szErrCreateSvc[ MAX_STRING_LENGTH ];
TCHAR g_szErrDeleteSvc[ MAX_STRING_LENGTH ];
TCHAR g_szMaxConnectionsReached[ MAX_STRING_LENGTH ];
TCHAR g_szLicenseLimitReached [ MAX_STRING_LENGTH ];
extern PSID localLocalSid;
/////////////////////////////////////////////////////////////////////////////
//
LPWSTR
GetDefaultLoginScriptFullPath( )
{
HKEY hk;
DWORD dwDisp = 0;
if( TnSecureRegCreateKeyEx( HKEY_LOCAL_MACHINE, REG_SERVICE_KEY, NULL, NULL,
REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED , NULL, &hk, &dwDisp, 0 ) )
{
return NULL;
}
if( !GetRegistryString( hk, NULL, L"ImagePath", &g_pszTelnetInstallPath, L"",FALSE ) )
{
return NULL;
}
RegCloseKey( hk );
if( !g_pszTelnetInstallPath )
{
//ImagePath key was not created.
return NULL;
}
LPWSTR pLastBackSlash = wcsrchr( g_pszTelnetInstallPath, L'\\' );
if( pLastBackSlash != NULL )
{
//Nuke the trailing "tlntsvr.exe"
*pLastBackSlash = 0;
}
DWORD length_required = wcslen( g_pszTelnetInstallPath ) + wcslen( DEFAULT_LOGIN_SCRIPT ) + 2; // one for \ and one more for NULL in the end
LPWSTR lpszDefaultLoginScriptFullPathName = new WCHAR[ length_required ];
if( !lpszDefaultLoginScriptFullPathName )
{
return NULL;
}
_snwprintf(lpszDefaultLoginScriptFullPathName, length_required - 1, L"%s\\%s", g_pszTelnetInstallPath, DEFAULT_LOGIN_SCRIPT);
lpszDefaultLoginScriptFullPathName[length_required-1] = 0; // When the buffer is full snwprintf could return non-null terminated string
return lpszDefaultLoginScriptFullPathName;
}
/*Mem allocation by this function.
To be deleted by the caller.
It just forms the reg key as required by console.
See comments for HandleJapSpecificRegKeys */
bool
FormTlntSessKeyForCmd( LPWSTR *lpszKey )
{
WCHAR szPathName[MAX_PATH + 1 ] = { 0 };
WCHAR session_path[MAX_PATH*2];
if( !GetModuleFileName( NULL, szPathName, MAX_PATH ) )
{
return ( FALSE );
}
//
// Nuke the trailing "tlntsvr.exe"
//
LPTSTR pSlash = wcsrchr( szPathName, L'\\' );
if( pSlash == NULL )
{
return ( FALSE );
}
else
{
*pSlash = L'\0';
}
wint_t ch = L'\\';
LPTSTR pBackSlash = NULL;
//
// Replace all '\\' with '_' This format is required for the console to
// interpret the key.
//
while ( 1 )
{
pBackSlash = wcschr( szPathName, ch );
if( pBackSlash == NULL )
{
break;
}
else
{
*pBackSlash = L'_';
}
}
_snwprintf(session_path, MAX_PATH*2 - 1, L"%s_tlntsess.exe", szPathName);
session_path[MAX_PATH*2 - 1] = L'\0'; // snwprintf could return non-null terminated string, if the buffer size is an exact fit
DWORD length_required = wcslen( REG_LOCALSERVICE_CONSOLE_KEY ) + wcslen( session_path ) + 2;
*lpszKey = new WCHAR[ length_required ];
if( *lpszKey == NULL )
{
return( FALSE );
}
_snwprintf(*lpszKey, length_required - 1, L"%s\\%s", REG_LOCALSERVICE_CONSOLE_KEY, session_path );
(*lpszKey)[length_required - 1] = L'\0'; // snwprintf could return non-null terminated string, if the buffer size is an exact fit
return ( TRUE );
}
//
// If Japanese codepage, then we need to verify 3 registry settings for
// console fonts:
// HKEY_USERS\.DEFAULT\Console\FaceName :REG_SZ:‚l‚r ƒSƒVƒbƒN
// where the FaceName is "MS gothic" written in Japanese full widthKana
// HKEY_USERS\.DEFAULT\Console\FontFamily:REG_DWORD:0x36
// HKEY_USERS\.DEFAULT\Console\C:_SFU_Telnet_tlntsess.exe\FontFamily:REG_DWORD: 0x36
// where the "C:" part is the actual path to SFU installation
//
//
bool
HandleFarEastSpecificRegKeys( void )
{
HKEY hk;
DWORD dwFontSize = 0;
const TCHAR szJAPFaceName[] = { 0xFF2D ,0xFF33 ,L' ' ,0x30B4 ,0x30B7 ,0x30C3 ,0x30AF ,L'\0' };
const TCHAR szCHTFaceName[] = { 0x7D30 ,0x660E ,0x9AD4 ,L'\0'};
const TCHAR szKORFaceName[] = { 0xAD74 ,0xB9BC ,0xCCB4 ,L'\0'};
const TCHAR szCHSFaceName[] = { 0x65B0 ,0x5B8B ,0x4F53 ,L'\0' };
TCHAR szFaceNameDef[MAX_STRING_LENGTH];
DWORD dwCodePage = GetACP();
DWORD dwFaceNameSize = 0;
DWORD dwFontFamily = 54;
DWORD dwFontWeight = 400;
DWORD dwHistoryNoDup = 0;
DWORD dwSize = 0;
switch (dwCodePage)
{
case JAP_CODEPAGE:
_tcscpy(szFaceNameDef, szJAPFaceName); //On JAP, set the FaceName to "MS Gothic"
dwFontSize = JAP_FONTSIZE;
break;
case CHT_CODEPAGE:
_tcscpy(szFaceNameDef, szCHTFaceName); //On CHT, set the FaceName to "MingLiU"
dwFontSize = CHT_FONTSIZE;
break;
case KOR_CODEPAGE:
_tcscpy(szFaceNameDef, szKORFaceName);//On KOR, set the FaceName to "GulimChe"
dwFontSize = KOR_FONTSIZE;
break;
case CHS_CODEPAGE:
_tcscpy(szFaceNameDef, szCHSFaceName);//On CHS, set the FaceName to "NSimSun"
dwFontSize = CHS_FONTSIZE;
break;
default:
_tcscpy(szFaceNameDef,L"\0");
break;
}
dwFaceNameSize = ( _tcslen( szFaceNameDef ) + 1 ) * sizeof( TCHAR );
if( !RegOpenKeyEx( HKEY_CURRENT_USER, REG_LOCALSERVICE_CONSOLE_KEY, 0, KEY_SET_VALUE, &hk ) )
{
RegSetValueEx( hk, L"FaceName", 0, REG_SZ, (LPBYTE) szFaceNameDef,
dwFaceNameSize );
DWORD dwVal;
dwSize = sizeof( DWORD );
SETREGISTRYDW( dwFontFamily, NULL, hk, L"FontFamily", dwVal,dwSize );
RegCloseKey( hk );
return ( TRUE );
}
return ( FALSE );
}
bool
CreateRegistryEntriesIfNotPresent( void )
{
HKEY hk = NULL;
HKEY hkDef = NULL;
HKEY hkReadConf = NULL;
DWORD dwCreateInitially;
LPWSTR pszCreateInitially = NULL;
LPWSTR lpszDefaultLoginScriptFullPathName = NULL;
DWORD dwType;
DWORD dwSize = sizeof( DWORD );
DWORD dwValue = NULL;
DWORD dwDisp = 0;
DWORD dwSecurityMechanism = DEFAULT_SECURITY_MECHANISM;
BOOL fFound = FALSE;
if( TnSecureRegCreateKeyEx( HKEY_LOCAL_MACHINE, REG_PARAMS_KEY, NULL, NULL,
REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED , NULL, &hk, &dwDisp, 0 ) )
{
return ( FALSE );
}
/*++
When the telnet server is getting modified from w2k telnet, there'll be NTLM value present
under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\TelnetServer\1.0
This is treated as a gateway for updating the registry. Since this value is present,
we are updating from win2k telnet to any upper version ( Garuda or whistler ). In that case,
we need to map the data contained by this value into the corresponding data for
"SecurityMechanism".
If we are upgrading from win2k, the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\TelnetServer\Defaults
key is deleted and created again. According to the telnet spec, all the values under this key
should be overwritten by the default values. So instead of checking all values, deleting the key -
which will create all the values with the defaults in them.
After updating all the values, we create a new value called UpdateTo and put data '3' in that.
This is defined by TELNET_LATEST_VERSION. Next version should be modifying this to the proper
value.
After the values are once modified, we need not go through the whole procedure again.
Now the open issue here is :
What if admin changes the registry value after once they are updated ?
Well, no one is supposed to edit the registry. So the service would not start properly
if the registry values are changed with invalid data in them.
Once the UpdatedTo value is created, the registry will never be changed. Few values need to be
read because some global variables are initialized with them.
This is done at the end of the function.
--*/
if( ERROR_SUCCESS == (RegQueryValueEx( hk, L"UpdatedTo", NULL, &dwType, ( LPBYTE )(&dwValue),
&dwSize )))
{
if(dwValue == LATEST_TELNET_VERSION )
{
goto Done;
}
}
dwDisp=0;
//ignore the failure... since we are immediately doing RegCreateKey(). If RegDeleteKey() fails due to
//some permission problems, RegCreateKey() will also fail. In other cases, we should continue.
RegDeleteKey(HKEY_LOCAL_MACHINE, REG_DEFAULTS_KEY);
if( TnSecureRegCreateKeyEx( HKEY_LOCAL_MACHINE, REG_DEFAULTS_KEY, NULL, NULL,
REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED , NULL, &hkDef, &dwDisp , 0 ))
{
return(FALSE);
}
if( ERROR_SUCCESS == (RegQueryValueEx( hk, L"NTLM", NULL, &dwType, ( LPBYTE )(&dwValue),
&dwSize )))
{
fFound = TRUE;
switch (dwValue)
{
case 0:
dwValue = NO_NTLM;
break;
case 1:
dwValue = NTLM_ELSE_OR_LOGIN;
break;
case 2:
dwValue = NTLM_ONLY;
break;
default:
//should never happen
_TRACE(TRACE_DEBUGGING,"ERROR: NTLM contains unexpected data");
return(FALSE);
}
dwSecurityMechanism = dwValue;
if( !GetRegistryDW( hk, NULL, L"SecurityMechanism", &dwCreateInitially,
dwSecurityMechanism ,fFound) )
{
return ( FALSE );
}
if(ERROR_SUCCESS != RegDeleteValue(hk,L"NTLM"))
{
_TRACE(TRACE_DEBUGGING,"CreateRegistryEntries : RegDelete failed");
return(FALSE);
}
if(ERROR_SUCCESS!=RegDeleteKey(hk,L"Performance"))
{
_TRACE(TRACE_DEBUGGING,"CreateRegistryEntries : RegDeleteKey failed");
return(FALSE);
}
}
dwDisp = 0;
if( TnSecureRegCreateKeyEx( HKEY_LOCAL_MACHINE, READ_CONFIG_KEY, NULL, NULL,
REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED , NULL, &hkReadConf, &dwDisp, 0 ) )
{
return( FALSE );
}
if(!GetRegistryDWORD(hkReadConf,L"Defaults",&dwCreateInitially,1,TRUE))
{
return(FALSE);
}
RegCloseKey( hkReadConf );
if( !GetRegistryDW( hk, hkDef, L"MaxConnections", &dwCreateInitially,
DEFAULT_MAX_CONNECTIONS,FALSE) )
{
return ( FALSE );
}
if( !GetRegistryDW( hk, hkDef, L"TelnetPort", &dwCreateInitially,
DEFAULT_TELNET_PORT,FALSE) )
{
return ( FALSE );
}
if( !GetRegistryString( hk, hkDef, L"DefaultShell", &pszCreateInitially,
DEFAULT_SHELL,fFound ) )
{
return ( FALSE );
}
delete[] pszCreateInitially;
pszCreateInitially = NULL;
if( !GetRegistryString( hk, hkDef, L"ListenToSpecificIpAddr", &pszCreateInitially,
DEFAULT_IP_ADDR,fFound ) )
{
return ( FALSE );
}
delete[] pszCreateInitially;
pszCreateInitially = NULL;
if( !GetRegistryString( hk, hkDef, SWITCH_TO_KEEP_SHELL_RUNNING, &pszCreateInitially,
DEFAULT_SWITCH_TO_KEEP_SHELL_RUNNING,fFound ) )
{
return ( FALSE );
}
delete[] pszCreateInitially;
pszCreateInitially = NULL;
if( !GetRegistryString( hk, hkDef, SWITCH_FOR_ONE_TIME_USE_OF_SHELL,
&pszCreateInitially,
DEFAULT_SWITCH_FOR_ONE_TIME_USE_OF_SHELL,fFound ) )
{
return ( FALSE );
}
delete[] pszCreateInitially;
pszCreateInitially = NULL;
if( !GetRegistryString( hk, hkDef, L"DefaultDomain", &pszCreateInitially,
DEFAULT_DOMAIN,FALSE) )
{
return ( FALSE );
}
delete[] pszCreateInitially;
pszCreateInitially = NULL;
if( !GetRegistryString( hk, NULL, L"Shell", &pszCreateInitially, L"",fFound ) )
{
return false;
}
delete[] pszCreateInitially;
pszCreateInitially = NULL;
if( !GetRegistryDW( hk, hkDef, L"AllowTrustedDomain", &dwCreateInitially,
DEFAULT_ALLOW_TRUSTED_DOMAIN,FALSE) )
{
return ( FALSE );
}
if( !GetRegistryDW( hk, hkDef, L"MaxFailedLogins", &dwCreateInitially,
DEFAULT_MAX_FAILED_LOGINS,FALSE) )
{
return ( FALSE );
}
if( !GetRegistryDW( hk, hkDef, L"SecurityMechanism", &dwCreateInitially,
DEFAULT_SECURITY_MECHANISM ,FALSE) )
{
return ( FALSE );
}
if( !GetRegistryDW( hk, hkDef, L"EventLoggingEnabled", &dwCreateInitially,
DEFAULT_SYSAUDITING,fFound ) )
{
return ( FALSE );
}
if( !GetRegistryDW( hk, hkDef, L"LogNonAdminAttempts", &dwCreateInitially,
DEFAULT_LOGEVENTS,fFound ) )
{
return ( FALSE );
}
if( !GetRegistryDW( hk, hkDef, L"LogAdminAttempts", &dwCreateInitially,
DEFAULT_LOGADMIN,fFound ) )
{
return ( FALSE );
}
if( !GetRegistryDW( hk, hkDef, L"LogFailures", &dwCreateInitially,
DEFAULT_LOGFAILURES,fFound ) )
{
return ( FALSE );
}
//In SFU2.0, AltKeyMapping has 1 for TRUE and 2 for FALSE where as now we have
// 1 for TRUE and 0 for FALSE. So if 'AltKeyMapping' value is found, we need to map it
// to the correct data.
if( ERROR_SUCCESS == (RegQueryValueEx( hk, L"AltKeyMapping", NULL, &dwType, ( LPBYTE )(&dwValue),
&dwSize )))
{
switch (dwValue)
{
case 1://In sfu2.0 and Garuda, AltKeyMapping ON = 1
dwValue = ALT_KEY_MAPPING_ON;
break;
case 2: //In SFU 2.0, AltKeyMapping off = 2. In garuda, it's 0.
dwValue = ALT_KEY_MAPPING_OFF;
break;
default:
dwValue = ALT_KEY_MAPPING_ON;
_TRACE(TRACE_DEBUGGING,"ERROR: AltKeyMapping contains unexpected data");
}
}
else
{
dwValue = DEFAULT_ALT_KEY_MAPPING;
}
if( !GetRegistryDW( hk, hkDef, L"AltKeyMapping", &dwCreateInitially,
dwValue,TRUE) )
{
return ( FALSE );
}
if( !GetRegistryDW( hk, hkDef, L"IdleSessionTimeOut", &dwCreateInitially,
DEFAULT_IDLE_SESSION_TIME_OUT,fFound ) )
{
return ( FALSE );
}
if( !GetRegistryDW( hk, hkDef, L"IdleSessionTimeOutBkup", &dwCreateInitially,
DEFAULT_IDLE_SESSION_TIME_OUT,fFound ) )
{
return ( FALSE );
}
if( !GetRegistryDW( hk, hkDef, L"DisconnectKillAllApps", &dwCreateInitially,
DEFAULT_DISCONNECT_KILLALL_APPS,fFound ) )
{
return ( FALSE );
}
if( !GetRegistryDW( hk, hkDef, L"ModeOfOperation", &dwCreateInitially,
DEFAULT_MODE_OF_OPERATION,fFound ) )
{
return ( FALSE );
}
//Delete the Termcap entry. It will exist only when you are upgrading.
//It will get created when the first session connects to the server.
RegDeleteValue(hk,L"Termcap");
if( !GetRegistryDW( hk, hkDef, L"UpdatedTo", &dwCreateInitially,
LATEST_TELNET_VERSION,TRUE) )
{
return ( FALSE );
}
Done:
/*++
These things are needed to be done everytime at the net start because
they initialize some global variables.
--*/
dwCreateInitially = 0;
if( !GetRegistryDW( hk, hkDef, L"LogToFile", &dwCreateInitially,
DEFAULT_LOGTOFILE,fFound ) )
{
return ( FALSE );
}
if( dwCreateInitially )
{
g_fLogToFile = true;
}
if( !( lpszDefaultLoginScriptFullPathName
= GetDefaultLoginScriptFullPath() ) )
{
return ( FALSE );
}
if( !GetRegistryString( hk, hkDef, L"LoginScript", &pszCreateInitially,
lpszDefaultLoginScriptFullPathName,FALSE) )
{
delete[] lpszDefaultLoginScriptFullPathName;
return ( FALSE );
}
delete[] pszCreateInitially;
pszCreateInitially = NULL;
delete[] lpszDefaultLoginScriptFullPathName;
if( !GetRegistryString( hk, hkDef, L"LogFile", &g_pszLogFile,
DEFAULT_LOGFILE,fFound ) )
{
return ( FALSE );
}
if( !GetRegistryDW( hk, hkDef, LOGFILESIZE, (DWORD *)&g_lMaxFileSize,
DEFAULT_LOGFILESIZE,fFound ) )
{
return ( FALSE );
}
RegCloseKey( hkDef );
RegCloseKey( hk );
return ( TRUE );
}
void CloseLogFile( LPWSTR *pszLogFile, HANDLE *hLogFile )
{
delete[] ( *pszLogFile );
*pszLogFile = NULL;
if( *hLogFile ) // global variable
TELNET_CLOSE_HANDLE( *hLogFile );
delete hLogFile;
}
void CloseAnyGlobalObjects()
{
delete[] g_pszTelnetInstallPath;
CloseLogFile( &g_pszLogFile, g_phLogFile );
}
bool InitializeLogFile( LPWSTR szLogFile, HANDLE *hLogFile )
{
//open logfile handle
//
_chASSERT( hLogFile != NULL );
_chASSERT( szLogFile != NULL );
*hLogFile = NULL;
LPWSTR szExpandedLogFile = NULL;
if( !g_fLogToFile )
{
return ( TRUE );
}
if( wcscmp( szLogFile, L"" ) != 0 )
{
LONG lDistanceToMove = 0 ;
if( !AllocateNExpandEnvStrings( szLogFile, &szExpandedLogFile) )
{
return( FALSE );
}
*hLogFile = CreateFile( szExpandedLogFile, GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL );
delete [] szExpandedLogFile;
if( *hLogFile == INVALID_HANDLE_VALUE )
{
DWORD dwErr = GetLastError();
*hLogFile = NULL;
LogEvent( EVENTLOG_ERROR_TYPE, MSG_ERRLOGFILE, szLogFile );
LogFormattedGetLastError( EVENTLOG_ERROR_TYPE, 0, dwErr );
return( FALSE );
}
_chVERIFY2( SetFilePointer( *hLogFile, lDistanceToMove, &lDistanceToMove, FILE_END ) );
}
return ( TRUE );
}
bool InitializeGlobalObjects()
{
_chASSERT ( g_hInstRes );
if( !CreateRegistryEntriesIfNotPresent( ) )
{
return ( FALSE );
}
DWORD dwCodePage = GetACP();
if ( dwCodePage == JAP_CODEPAGE || dwCodePage == CHS_CODEPAGE ||dwCodePage == CHT_CODEPAGE || dwCodePage == KOR_CODEPAGE )
{
//Fareast code page
if( !HandleFarEastSpecificRegKeys() )
{
return( FALSE );
}
}
g_phLogFile = new HANDLE;
if( !g_phLogFile )
{
return ( FALSE );
}
InitializeLogFile( g_pszLogFile, g_phLogFile );
return ( TRUE );
}
void WriteAuditedMsgsToFile( LPSTR szString )
{
DWORD dwNumBytesWritten, dwMsgLength;
LPSTR logStr = NULL;
LPSTR logTime = NULL;
UDATE uSysDate; //local time
DATE dtCurrent;
DWORD dwFlags = VAR_VALIDDATE;
BSTR szDate = NULL;
DWORD dwSize = 0;
DWORD dwFileSizeLowWord = 0, dwFileSizeHighWord = 0;
LARGE_INTEGER liActualSize = { 0 };
GetLocalTime( &uSysDate.st );
if( VarDateFromUdate( &uSysDate, dwFlags, &dtCurrent ) != S_OK )
{
goto AuditAbort;
}
if( VarBstrFromDate( dtCurrent,
MAKELCID( MAKELANGID( LANG_NEUTRAL, SUBLANG_SYS_DEFAULT ), SORT_DEFAULT ),
LOCALE_NOUSEROVERRIDE, &szDate ) != S_OK )
{
goto AuditAbort;
}
dwSize = WideCharToMultiByte( GetOEMCP(), 0, szDate, SysStringByteLen( szDate )/sizeof(TCHAR),
NULL, 0, NULL, NULL );
logTime = new CHAR[ dwSize+1 ];
if( !logTime )
{
goto AuditAbort;
}
dwSize = WideCharToMultiByte( GetOEMCP(), 0, szDate, SysStringByteLen( szDate )/sizeof(TCHAR),
logTime, dwSize, NULL, NULL );
logTime[dwSize] = 0;
dwMsgLength = strlen( szString ) + strlen( logTime ) + strlen( NEW_LINE ) + 1;
logStr = new CHAR[ dwMsgLength + 1 ];
if( !logStr )
{
goto AuditAbort;
}
_snprintf( logStr, dwMsgLength, "%s %s%s", szString, logTime, NEW_LINE );
dwFileSizeLowWord = GetFileSize( *g_phLogFile, &dwFileSizeHighWord );
if( dwFileSizeLowWord == INVALID_FILE_SIZE )
{
goto AuditAbort;
}
liActualSize.QuadPart = dwFileSizeHighWord;
liActualSize.QuadPart = liActualSize.QuadPart << (sizeof( dwFileSizeHighWord ) * 8);
liActualSize.QuadPart += dwFileSizeLowWord + dwMsgLength;
if( liActualSize.QuadPart <= (g_lMaxFileSize * ONE_MB) )
{
g_fIsLogFull = false;
_chVERIFY2( WriteFile( *g_phLogFile, (LPCVOID) logStr,
dwMsgLength, &dwNumBytesWritten, NULL ) );
}
else
{
if( !g_fIsLogFull )
{
//Log event
g_fIsLogFull = true;
LogEvent(EVENTLOG_INFORMATION_TYPE, LOGFILE_FULL, g_pszLogFile );
}
}
AuditAbort:
SysFreeString( szDate );
delete[] logTime;
delete[] logStr;
}
BOOL GetInstalledTlntsvrPath( LPTSTR szPath, DWORD *dwSize )
{
HKEY hKey = NULL;
BOOL bResult = FALSE;
DWORD dwDisp = 0;
DWORD dwType = 0;
if( !szPath || !dwSize )
{
goto GetInstalledTlntsvrPathAbort;
}
if( TnSecureRegCreateKeyEx( HKEY_LOCAL_MACHINE, REG_SERVICE_KEY, NULL, NULL,
REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE, NULL, &hKey, &dwDisp, 0 ) )
{
goto GetInstalledTlntsvrPathAbort;
}
if( RegQueryValueEx( hKey, L"ImagePath", NULL, &dwType, ( LPBYTE )szPath, dwSize) )
{
goto GetInstalledTlntsvrPathAbort;
}
bResult = TRUE;
GetInstalledTlntsvrPathAbort:
if( hKey )
{
RegCloseKey( hKey );
}
return bResult;
}
void Regsvr32IfNotAlreadyDone()
{
TCHAR szPath[MAX_PATH+1];
TCHAR szInstalledTlntsvr[MAX_PATH+1];
DWORD dwSize = 2* ( MAX_PATH + 1 ); //in bytes;
CRegKey keyAppID;
LONG lRes = 0;
CRegKey key;
TCHAR szValue[_MAX_PATH];
DWORD dwLen = _MAX_PATH;
LPTSTR szTelnetPath = NULL;
LPTSTR szInstalledDll = NULL;
STARTUPINFO sinfo;
PROCESS_INFORMATION pinfo;
WCHAR szApp[MAX_PATH+14];
lRes = keyAppID.Open(HKEY_CLASSES_ROOT, _T("CLSID"));
if (lRes != ERROR_SUCCESS)
return;
lRes = key.Open( keyAppID, L"{FE9E48A2-A014-11D1-855C-00A0C944138C}" );
if (lRes != ERROR_SUCCESS)
return;
lRes = key.QueryValue(szValue, _T("Default"), &dwLen);
keyAppID.Close();
key.Close();
//Get the Installed service path from HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TlntSvr\ImagePath
if( !GetInstalledTlntsvrPath( szInstalledTlntsvr, &dwSize ) )
{
return;
}
//path contains tlntsvr.exe at the tail
//truncate at the last '\' - so *szTelnetPath will have z:\some\folder
szTelnetPath = wcsrchr( szInstalledTlntsvr, L'\\' );
if(szTelnetPath)
*szTelnetPath = L'\0';
//HKEY_CLASSES_ROOT\CLSID\{FE9E48A2-A014-11D1-855C-00A0C944138C}\InProcServer32
//truncate at last '\'
szInstalledDll = wcsrchr( szValue, L'\\' );
if(szInstalledDll)
*szInstalledDll = L'\0';
if( lstrcmpi( szInstalledTlntsvr, szValue ) == 0 )
{
//Since both the paths match, our dll is already registered
return;
}
_tcscpy(szPath, L"regsvr32.exe /s " );
_tcscat(szPath, szInstalledTlntsvr );
_tcscat(szPath, L"\\tlntsvrp.dll");
SfuZeroMemory(&sinfo, sizeof(STARTUPINFO));
sinfo.cb = sizeof(STARTUPINFO);
if(!GetSystemDirectory(szApp,MAX_PATH))
{
return;
}
wcsncat(szApp,L"\\regsvr32.exe",13);
szApp[MAX_PATH+13] = L'\0';
//initiate Regsvr32 /s path\tlntsvrp.dll
_TRACE(TRACE_DEBUGGING,L"Calling regsvr32 with szApp = %s and szPath = %s",szApp,szPath);
if ( CreateProcess( szApp, szPath, NULL, NULL,
FALSE, 0, NULL, NULL, &sinfo, &pinfo) )
{
// wait for the process to finish.
TlntSynchronizeOn(pinfo.hProcess);
TELNET_CLOSE_HANDLE(pinfo.hProcess);
TELNET_CLOSE_HANDLE(pinfo.hThread);
}
}
DWORD
WINAPI
TelnetServiceThread ( )
{
g_pTelnetService = CTelnetService::Instance();
_chASSERT( g_pTelnetService != NULL );
if( !g_pTelnetService )
{
return( FALSE );
}
if( !InitializeGlobalObjects() )
{
return ( FALSE );
}
_Module.SetServiceStatus(SERVICE_START_PENDING);
//This is needed because W2k telnet server is registering tlntsvrp.dll of %systemdir%. Even if this fails the service can continue
Regsvr32IfNotAlreadyDone();
LogEvent(EVENTLOG_INFORMATION_TYPE, MSG_STARTUP, _T("Service started"));
g_pTelnetService->ListenerThread();
_Module.SetServiceStatus( SERVICE_STOP_PENDING );
CloseAnyGlobalObjects();
delete g_pTelnetService;
g_pTelnetService = NULL;
return ( TRUE );
}
/////////////////////////////////////////////////////////////////////////////
//
CServiceModule _Module;
BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_EnumTelnetClientsSvr, CEnumTelnetClientsSvr)
END_OBJECT_MAP()
LPCTSTR
FindOneOf
(
LPCTSTR p1,
LPCTSTR p2
)
{
while (*p1 != NULL)
{
LPCTSTR p = p2;
while (*p != NULL)
{
if (*p1 == *p++)
return p1+1;
}
p1++;
}
return NULL;
}
BOOL
IsThatMe()
{
HKEY hk = NULL;
DWORD dwType;
DWORD dwSize = sizeof( DWORD );
DWORD dwValue = 0;
BOOL bIsThatMe = FALSE;
DWORD dwDisp = 0;
if( TnSecureRegCreateKeyEx( HKEY_LOCAL_MACHINE, REG_PARAMS_KEY, NULL, NULL,
REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED , NULL, &hk, &dwDisp, 0 ) )
{
goto Done;
}
if( ERROR_SUCCESS == (RegQueryValueEx( hk, L"UpdatedTo", NULL, &dwType, ( LPBYTE )(&dwValue),
&dwSize )))
{
if(dwValue == LATEST_TELNET_VERSION )
{
bIsThatMe = TRUE;
goto Done;
}
}
Done:
if(hk)
{
RegCloseKey(hk);
}
return bIsThatMe;
}
BOOL
SetServiceConfigToSelf( LPTSTR szServiceName )
{
BOOL bResult = FALSE;
HKEY hKey = NULL;
DWORD dwDisp = 0;
WCHAR szMyName[ MAX_PATH + 1 ];
DWORD dwSize = 0;
LONG lRes = 0;
DWORD dwCreateInitially = 0;
if( !szServiceName )
{
goto SetServiceConfigToSelfAbort;
}
// Get our Path.
if ( !GetModuleFileName(NULL, szMyName, MAX_PATH) )
{
goto SetServiceConfigToSelfAbort;
}
if( TnSecureRegCreateKeyEx( HKEY_LOCAL_MACHINE, REG_SERVICE_KEY, NULL, NULL,
REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED , NULL, &hKey, &dwDisp, 0 ) )
{
goto SetServiceConfigToSelfAbort;
}
dwSize = ( wcslen( szMyName ) + 1 ) * 2 ;
if( lRes = RegSetValueEx( hKey, L"ImagePath", NULL, REG_EXPAND_SZ, ( BYTE * ) szMyName, dwSize) )
{
goto SetServiceConfigToSelfAbort;
}
if( !GetRegistryDW( hKey, NULL, L"Start", &g_dwStartType,
g_dwStartType,FALSE) )
{
goto SetServiceConfigToSelfAbort;
}
bResult = TRUE;
SetServiceConfigToSelfAbort:
if( hKey )
{
RegCloseKey( hKey );
}
return bResult;
}
/**************************
* Appends the SID to current DACL of a file.
* Parameters:
* [in] SID to be appended
* [in] Name of the file ( full path )
* [in] Access Mask
* Return values:
* boolean to return success or failure.
*/
BOOL AddAccessRights(PSID pSid, LPCWSTR lpszFileName, DWORD dwAccessMask) {
// File SD variables.
PSECURITY_DESCRIPTOR pFileSD = NULL;
DWORD cbFileSD = 0;
// New SD variables.
PSECURITY_DESCRIPTOR pNewSD = NULL;
// ACL variables.
PACL pACL = NULL;
BOOL fDaclPresent;
BOOL fDaclDefaulted;
ACL_SIZE_INFORMATION AclInfo;
// New ACL variables.
PACL pNewACL = NULL;
DWORD cbNewACL = 0;
// Temporary ACE.
LPVOID pTempAce = NULL;
UINT CurrentAceIndex;
// Assume function will fail.
BOOL fResult = FALSE;
BOOL fAPISuccess;
__try {
//
// Get security descriptor (SD) for file.
//
fAPISuccess = GetFileSecurity(lpszFileName,
DACL_SECURITY_INFORMATION, pFileSD, 0, &cbFileSD);
// API should have failed with insufficient buffer.
if (fAPISuccess)
__leave;
else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
_TRACE(TRACE_DEBUGGING,L"GetFileSecurity() failed. Error %d\n",
GetLastError());
__leave;
}
pFileSD = heapalloc(cbFileSD);
if (!pFileSD) {
_TRACE(TRACE_DEBUGGING,L"HeapAlloc() failed. Error %d\n", GetLastError());
__leave;
}
fAPISuccess = GetFileSecurity(lpszFileName,
DACL_SECURITY_INFORMATION, pFileSD, cbFileSD, &cbFileSD);
if (!fAPISuccess) {
_TRACE(TRACE_DEBUGGING,L"GetFileSecurity() failed. Error %d\n",
GetLastError());
__leave;
}
//
// Initialize new SD.
//
pNewSD = heapalloc(cbFileSD); // Should be same size as FileSD.
if (!pNewSD) {
_TRACE(TRACE_DEBUGGING,L"HeapAlloc() failed. Error %d\n", GetLastError());
__leave;
}
if (!InitializeSecurityDescriptor(pNewSD,
SECURITY_DESCRIPTOR_REVISION)) {
_TRACE(TRACE_DEBUGGING,L"InitializeSecurityDescriptor() failed. Error %d\n",
GetLastError());
__leave;
}
//
// Get DACL from SD.
//
if (!GetSecurityDescriptorDacl(pFileSD, &fDaclPresent, &pACL,
&fDaclDefaulted)) {
_TRACE(TRACE_DEBUGGING,L"GetSecurityDescriptorDacl() failed. Error %d\n",
GetLastError());
__leave;
}
//
// Get size information for DACL.
//
AclInfo.AceCount = 0; // Assume NULL DACL.
AclInfo.AclBytesFree = 0;
AclInfo.AclBytesInUse = sizeof(ACL);
// If not NULL DACL, gather size information from DACL.
if (fDaclPresent && pACL) {
if(!GetAclInformation(pACL, &AclInfo,
sizeof(ACL_SIZE_INFORMATION), AclSizeInformation)) {
_TRACE(TRACE_DEBUGGING,L"GetAclInformation() failed. Error %d\n",
GetLastError());
__leave;
}
}
//
// Compute size needed for the new ACL.
//
cbNewACL = AclInfo.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE)
+ GetLengthSid(pSid) - sizeof(DWORD);
//
// Allocate memory for new ACL.
//
pNewACL = (PACL) heapalloc(cbNewACL);
if (!pNewACL) {
_TRACE(TRACE_DEBUGGING,L"HeapAlloc() failed. Error %d\n", GetLastError());
__leave;
}
//
// Initialize the new ACL.
//
if(!InitializeAcl(pNewACL, cbNewACL, ACL_REVISION2)) {
_TRACE(TRACE_DEBUGGING,L"InitializeAcl() failed. Error %d\n", GetLastError());
__leave;
}
//
// If DACL is present, copy it to a new DACL.
//
if (fDaclPresent) {
//
// Copy the file's ACEs to the new ACL.
//
if (AclInfo.AceCount) {
//See if the ACE is present in the end.
if(!GetAce(pACL,AclInfo.AceCount -1, &pTempAce))
{
__leave;
}
if(((ACE_HEADER *)pTempAce)->AceType == ACCESS_ALLOWED_ACE_TYPE &&
EqualSid((PSID)&(((ACCESS_ALLOWED_ACE*)pTempAce)->SidStart),pSid))
{
//ACE is already present.
goto Done;
}
for (CurrentAceIndex = 0;
CurrentAceIndex < AclInfo.AceCount;
CurrentAceIndex++) {
//
// Get an ACE.
//
if(!GetAce(pACL, CurrentAceIndex, &pTempAce)) {
_TRACE(TRACE_DEBUGGING,L"GetAce() failed. Error %d\n",
GetLastError());
__leave;
}
//Keep checking if the ACE is already present.
if(((ACE_HEADER *)pTempAce)->AceType == ACCESS_ALLOWED_ACE_TYPE &&
EqualSid((PSID)&(((ACCESS_ALLOWED_ACE*)pTempAce)->SidStart),pSid))
{
//ACE is already present.
goto Done;
}
//
// Add the ACE to the new ACL.
//
if(!AddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,
((PACE_HEADER) pTempAce)->AceSize)) {
_TRACE(TRACE_DEBUGGING,L"AddAce() failed. Error %d\n",
GetLastError());
__leave;
}
}
}
}
//
// Add the access-allowed ACE to the new DACL.
//
if (!AddAccessAllowedAce(pNewACL, ACL_REVISION2, dwAccessMask,
pSid)) {
_TRACE(TRACE_DEBUGGING,L"AddAccessAllowedAce() failed. Error %d\n",
GetLastError());
__leave;
}
//
// Set the new DACL to the file SD.
//
if (!SetSecurityDescriptorDacl(pNewSD, TRUE, pNewACL,
FALSE)) {
_TRACE(TRACE_DEBUGGING,L"() failed. Error %d\n", GetLastError());
__leave;
}
//
// Set the SD to the File.
//
if (!SetFileSecurity(lpszFileName, DACL_SECURITY_INFORMATION,
pNewSD)) {
_TRACE(TRACE_DEBUGGING,L"SetFileSecurity() failed. Error %d\n",
GetLastError());
__leave;
}
Done:
fResult = TRUE;
} __finally {
//
// Free allocated memory
//
if (pFileSD)
heapfree(pFileSD);
if (pNewSD)
heapfree(pNewSD);
if (pNewACL)
heapfree(pNewACL);
}
return fResult;
}
/******************************************************************
* CreateTelnetClientsGroupAndSetPermissions()
* Creates a group called TelnetClients and sets permissions on cmd.exe
* to give Read+Execute rights to TelnetClients group.
* Parameters:
* None
* Return values:
* Boolean to return success or failure.
*/
BOOL CreateTelnetClientsGroupAndSetPermissions()
{
LOCALGROUP_INFO_1 giTelnetGroup;
TCHAR wzGrpi1Name[GNLEN] = { 0 };
TCHAR wzGrpi1Comment[MAXCOMMENTSZ] = { 0 };
DWORD dwCount = MAX_PATH;
BOOL bRetVal = FALSE;
NET_API_STATUS success = NERR_Success;
PSID pSidTelnetClients = NULL;
WCHAR szApp[MAX_PATH+9] = { 0 }; //for "System32_path\\cmd.exe" + NULL
wcsncpy(wzGrpi1Name,TELNETCLIENTS_GROUP_NAME, GNLEN -1);
if (! LoadString(g_hInstRes, IDS_TELNETCLIENTS_GROUP_COMMENT, wzGrpi1Comment,
sizeof(wzGrpi1Comment) / sizeof(TCHAR)))
{
_tcsncpy(wzGrpi1Comment, TEXT("Members of this group have access to Telnet Server on this system."),MAXCOMMENTSZ-1);
}
giTelnetGroup.lgrpi1_name = wzGrpi1Name;
giTelnetGroup.lgrpi1_comment = wzGrpi1Comment;
success = NetLocalGroupAdd( NULL, 1, (LPBYTE)&giTelnetGroup, NULL);
if(success != NERR_Success && success != NERR_GroupExists && success != ERROR_ALIAS_EXISTS )
{
goto ExitOnError;
}
//Get TelnetClients Sid.
{
DWORD needed_length = 0;
DWORD dwErr = 0, dwDomainLen = 0;
SID_NAME_USE sidNameUse;
TCHAR szDomain[ MAX_PATH + 1 ];
BOOL success = FALSE;
TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH + 1 + 14] = { 0 }; //+14 for '\TelnetClients'
DWORD dwNameLen = MAX_COMPUTERNAME_LENGTH + 1;
success = GetComputerName(szComputerName, &dwNameLen);
if(success)
{
_sntprintf(szComputerName+dwNameLen,(MAX_COMPUTERNAME_LENGTH + 14) - dwNameLen,_T("\\%s"),TELNETCLIENTS_GROUP_NAME);
}
LookupAccountName( NULL, szComputerName, pSidTelnetClients, &needed_length,
szDomain, &dwDomainLen, &sidNameUse );
pSidTelnetClients = ( PSID ) new UCHAR[ needed_length ];
if(pSidTelnetClients == NULL)
{
_TRACE(TRACE_DEBUGGING,L"Allocation for pSidTelnetClients failed in CreateTelnetClientsGroupAndSetPermissions");
goto ExitOnError;
}
//Even if if allocation fails just go ahead.
success = LookupAccountName( NULL, szComputerName, pSidTelnetClients, &needed_length,
szDomain, &dwDomainLen, &sidNameUse );
if( !success )
{
_TRACE(TRACE_DEBUGGING,L"LookupAccountName failed in CreateTelnetClientsGroupAnd... with %d",GetLastError());
goto ExitOnError;
}
}
if(!GetSystemDirectory(szApp,MAX_PATH+1))
{
goto ExitOnError;
}
wcsncat(szApp,L"\\cmd.exe",8);
bRetVal = AddAccessRights(pSidTelnetClients, (LPCWSTR)szApp, FILE_EXECUTE | FILE_READ_DATA |
FILE_READ_ATTRIBUTES |
FILE_READ_EA |
STANDARD_RIGHTS_READ |
SYNCHRONIZE);
ExitOnError:
if (pSidTelnetClients)
{
delete pSidTelnetClients;
pSidTelnetClients = NULL;
}
return bRetVal;
}
HRESULT
CServiceModule::RegisterServer
(
BOOL bRegTypeLib,
BOOL bService
)
{
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr))
return hr;
if( IsInstalled() )
{
if( IsThatMe() )
{
//This is needed because it is possible to call tlntsvr /Service
//multiple times from commandline
return S_OK;
}
//Set the service keys point to self
if( !SetServiceConfigToSelf( m_szServiceName ) )
{
//Let the latest version run
return S_OK;
}
else
{
//Set the service keys point to self
if( !SetServiceConfigToSelf( m_szServiceName ) )
{
if (! LoadString(g_hInstRes, IDS_ERR_CONFIG_SVC, g_szErrRegDelete,
sizeof(g_szErrRegDelete) / sizeof(g_szErrRegDelete[0])))
{
lstrcpy(g_szErrRegDelete, TEXT(""));
}
}
}
}
if(!CreateTelnetClientsGroupAndSetPermissions())
return E_FAIL;
// Add service entries
UpdateRegistryFromResource(IDR_TlntSvr, TRUE);
// Adjust the AppID for Local Server or Service
CRegKey keyAppID;
LONG lRes = keyAppID.Open(HKEY_CLASSES_ROOT, _T("AppID"));
if (lRes != ERROR_SUCCESS)
return lRes;
CRegKey key;
lRes = key.Open( keyAppID, APPID );
if (lRes != ERROR_SUCCESS)
return lRes;
key.DeleteValue(_T("LocalService"));
HKEY hk = NULL;
WCHAR szAccnt[_MAX_PATH+1];
LPWSTR pszAccount = NULL, pszPassword = NULL;
WCHAR szDomainName[_MAX_PATH+1];
DWORD dwAccntSize = _MAX_PATH, dwDomainNameSize = _MAX_PATH;
WCHAR szFullAccountName[_MAX_PATH*2 + 2] = { 0 }; // 3 for '\' and NULL.
SID_NAME_USE sid_name_use;
LPWSTR szPasswd = L"";
if(!TnInitializeStandardSids())
return FALSE;
if(!LookupAccountSid(NULL,localLocalSid,szAccnt,&dwAccntSize,szDomainName,&dwDomainNameSize,&sid_name_use))
{
// If error is ERROR_NONE_MAPPED, the account is not present.
// Probably we are running W2k or NT4.
// the service will run as local system.
if(GetLastError() != ERROR_NONE_MAPPED)
{
TnFreeStandardSids();
return E_FAIL;
}
}
else
{
_snwprintf(szFullAccountName,MAX_PATH*2+1,L"NT AUTHORITY\\LocalService");
pszAccount = szFullAccountName; // Name of the Local Service account
pszPassword = szPasswd; //Empty String
}
TnFreeStandardSids();
if (bService)
{
key.SetValue(_T("TlntSvr"), _T("LocalService"));
key.SetValue(_T("-Service -From_DCOM"), _T("ServiceParameters"));
// Create service
Install(pszAccount,pszPassword);
}
keyAppID.Close();
key.Close();
// Add object entries
hr = CComModule::RegisterServer(bRegTypeLib);
CoUninitialize();
//register the message resource
if(bService)
{
// Add event log info
CRegKey eventLog;
TCHAR local_key[_MAX_PATH];
lstrcpy( local_key, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog"
_T("\\Application\\"))); // NO, BO, Baskar.
TCHAR szModule[_MAX_PATH];
GetModuleFileName(_Module.GetModuleInstance(), szModule, _MAX_PATH);
TCHAR szResModule[_MAX_PATH*2];
DWORD len = GetModuleFileName(g_hInstRes, szResModule, _MAX_PATH);
OSVERSIONINFOEX osvi = { 0 };
WCHAR szSysDir[MAX_PATH+1] = {0};
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if ( !GetVersionEx((OSVERSIONINFO *) &osvi ) )
{
//OSVERSIONINFOEX is supported from NT4 SP6 on. So GetVerEx() should succeed.
return E_FAIL;
}
//Check if the OS is XPSP, in that case, we need to append the xpspresdll name to
//eventmessagefile value in HKLM\system\...\eventlog\tlntsvr.
//The event message will first be searched in szResModule and then in xpsp1res.dll
if(osvi.dwPlatformId == VER_PLATFORM_WIN32_NT && osvi.wProductType == VER_NT_WORKSTATION && osvi.wServicePackMajor > 0)
{
//OS is Windows XP.
if(!GetSystemDirectory(szSysDir,MAX_PATH+1))
{
_tcsncpy(szSysDir,L"%SYSTEMROOT%\\system32",MAX_PATH);
}
_snwprintf(szResModule+len,(_MAX_PATH*2)-len-1,L";%s\\xpsp1res.dll",szSysDir);
}
TCHAR szName[_MAX_FNAME];
_tsplitpath( szModule, NULL, NULL, szName, NULL);
lstrcat(local_key, szName); // NO overflow, Baskar
LONG result = eventLog.Create(HKEY_LOCAL_MACHINE, local_key);
if( ERROR_SUCCESS != result)
return result;
result = eventLog.SetValue(szResModule, _T("EventMessageFile"));
if(ERROR_SUCCESS != result)
return result;
DWORD dwTypes = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
EVENTLOG_INFORMATION_TYPE | EVENTLOG_AUDIT_SUCCESS |
EVENTLOG_AUDIT_FAILURE;
result = eventLog.SetValue(dwTypes, _T("TypesSupported"));
if(ERROR_SUCCESS != result)
return result;
eventLog.Close();
SC_HANDLE hService = NULL, hSCM = NULL;
TCHAR szServiceDesc[ _MAX_PATH * 2 ];
DWORD dwData = 0;
DWORD dwTag = 0;
DWORD dwDisp = 0;
if( (result = TnSecureRegCreateKeyEx( HKEY_LOCAL_MACHINE, REG_SERVICE_KEY, NULL, NULL,
REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED , NULL, &hk, &dwDisp, 0 )) == ERROR_SUCCESS )
{
LPWSTR pszCreateInitially = NULL;
if(LoadString(_Module.GetResourceInstance(),
IDS_SERVICE_DESCRIPTION,
szServiceDesc,
sizeof(szServiceDesc) / sizeof(TCHAR)))
{
if( !GetRegistryString( hk, NULL, L"Description", &pszCreateInitially,
szServiceDesc,TRUE ) )
{
RegCloseKey(hk);
return E_FAIL;
}
RegCloseKey(hk);
}
else
{
RegCloseKey(hk);
return E_FAIL;
}
}
else
{
return E_FAIL;
}
hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE |
SC_MANAGER_ENUMERATE_SERVICE | MAXIMUM_ALLOWED );
if (hSCM != NULL)
{
hService = ::OpenService(hSCM, m_szServiceName,
SERVICE_CHANGE_CONFIG );
if (hService == NULL)
{
result = E_FAIL;
::CloseServiceHandle(hSCM);
return result;
}
if(!ChangeServiceConfig(hService,SERVICE_NO_CHANGE,SERVICE_NO_CHANGE,
SERVICE_ERROR_NORMAL,NULL,NULL,
NULL,DEFAULT_SERVICE_DEPENDENCY,
pszAccount,pszPassword,SERVICE_DISPLAY_NAME))
{
result = E_FAIL;
}
::CloseServiceHandle(hService);
::CloseServiceHandle(hSCM);
}
else
{
return result;
}
}
//Create HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\TelnetServer key and it's values.
if( !CreateRegistryEntriesIfNotPresent( ) )
{
return E_FAIL;
}
return hr;
}
HRESULT
CServiceModule::UnregisterServer()
{
DWORD dwError = 0;
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr))
return hr;
// Remove service entries
UpdateRegistryFromResource(IDR_TlntSvr, FALSE);
// Remove service
Uninstall();
// Remove object entries
CComModule::UnregisterServer();
//This is so that reg key related to typelib gets deleted
(void)UnRegisterTypeLib(LIBID_TLNTSVRLib, 1, 0, LOCALE_NEUTRAL, SYS_WIN32); // Stop prefast from reporting an error.
//remove telnet specific registry entries
//RegDeleteKey is returning ERROR_INVALID_FUNCTION in the absence of the key
RegDeleteKey( HKEY_LOCAL_MACHINE, ( READ_CONFIG_KEY ) );
RegDeleteKey( HKEY_LOCAL_MACHINE, ( REG_PARAMS_KEY ) );
RegDeleteKey( HKEY_LOCAL_MACHINE, ( REG_DEFAULTS_KEY ) );
RegDeleteKey( HKEY_LOCAL_MACHINE, ( REG_SERVER_KEY ) );
RegDeleteKey( HKEY_CLASSES_ROOT, ( APPID ) );
RegDeleteKey( HKEY_LOCAL_MACHINE, _T( "System\\CurrentControlSet\\Services\\EventLog\\Application\\TlntSvr" ) );
LPWSTR lpszKey = NULL;
if( FormTlntSessKeyForCmd( &lpszKey ) )
{
if( ( dwError = RegDeleteKey( HKEY_USERS, lpszKey)) != ERROR_SUCCESS
&& ( dwError != ERROR_INVALID_FUNCTION ) )
{
//do nothing
}
delete [] lpszKey;
}
//The following key is not created by this program. But by tlntsvr.rgs
//I couldn't delete this in any other way. So, manually deleting it
RegDeleteKey( HKEY_CLASSES_ROOT, _T( "AppID\\TlntSvr.Exe" ) );
CoUninitialize();
return S_OK;
}
void
CServiceModule::Init
(
_ATL_OBJMAP_ENTRY* p,
HINSTANCE h,
UINT nServiceNameID
)
{
CComModule::Init(p, h);
m_bService = TRUE;
if (! LoadString(h, nServiceNameID, m_szServiceName,
sizeof(m_szServiceName) / sizeof(TCHAR)))
{
lstrcpy(m_szServiceName, TEXT(""));
}
if (! LoadString(h, IDS_ERR_REG_DELETE, g_szErrRegDelete,
sizeof(g_szErrRegDelete) / sizeof(TCHAR)))
{
lstrcpy(g_szErrRegDelete, TEXT(""));
}
if (! LoadString(h, IDS_ERR_OPEN_SCM, g_szErrOpenSCM,
sizeof(g_szErrOpenSCM) / sizeof(TCHAR)))
{
lstrcpy(g_szErrOpenSCM, TEXT(""));
}
if (! LoadString(h, IDS_ERR_CREATE_SVC, g_szErrCreateSvc,
sizeof(g_szErrCreateSvc) / sizeof(TCHAR)))
{
lstrcpy(g_szErrCreateSvc, TEXT(""));
}
if (! LoadString(h, IDS_ERR_OPEN_SVC, g_szErrOpenSvc,
sizeof(g_szErrOpenSvc) / sizeof(TCHAR)))
{
lstrcpy(g_szErrOpenSvc, TEXT(""));
}
if (! LoadString(h, IDS_ERR_DELETE_SVC, g_szErrDeleteSvc,
sizeof(g_szErrDeleteSvc) / sizeof(TCHAR)))
{
lstrcpy(g_szErrDeleteSvc, TEXT(""));
}
if (! LoadString(h, IDS_MAX_CONNECTIONS_REACHED, g_szMaxConnectionsReached,
sizeof(g_szMaxConnectionsReached) / sizeof(TCHAR)))
{
lstrcpy(g_szMaxConnectionsReached, TEXT(""));
}
if (! LoadString(h, IDS_LICENSE_LIMIT_REACHED, g_szLicenseLimitReached,
sizeof(g_szLicenseLimitReached) / sizeof(TCHAR)))
{
lstrcpy(g_szLicenseLimitReached, TEXT(""));
}
// set up the initial service status
m_hServiceStatus = NULL;
m_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
m_status.dwCurrentState = SERVICE_STOPPED;
m_status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;
m_status.dwWin32ExitCode = 0;
m_status.dwServiceSpecificExitCode = 0;
m_status.dwCheckPoint = 0;
m_status.dwWaitHint = 0;
}
LONG
CServiceModule::Unlock()
{
LONG x = CComModule::Unlock();
// May be Telnet server has to shutdown
// if (l == 0 && !m_bService)
// PostThreadMessage(dwThreadID, WM_QUIT, 0, 0);
return x;
}
BOOL
CServiceModule::IsInstalled()
{
BOOL bResult = FALSE;
SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE |
SC_MANAGER_ENUMERATE_SERVICE | MAXIMUM_ALLOWED );
if (hSCM != NULL)
{
SC_HANDLE hService = ::OpenService(hSCM, m_szServiceName,
SERVICE_QUERY_CONFIG);
if (hService != NULL)
{
bResult = TRUE;
::CloseServiceHandle(hService);
}
::CloseServiceHandle(hSCM);
}
return bResult;
}
BOOL
CServiceModule::Install(LPWSTR pszAccount, LPWSTR pszPassword)
{
if (IsInstalled())
return TRUE;
SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE |
SC_MANAGER_ENUMERATE_SERVICE | MAXIMUM_ALLOWED );
if (hSCM == NULL)
{
MessageBox(NULL, g_szErrOpenSCM, m_szServiceName, MB_OK);
return FALSE;
}
// Get the executable file path
TCHAR szFilePath[_MAX_PATH + 1] = { 0 };
::GetModuleFileName(NULL, szFilePath, _MAX_PATH);
SC_HANDLE hService = ::CreateService(hSCM, m_szServiceName,
SERVICE_DISPLAY_NAME, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
g_dwStartType, SERVICE_ERROR_NORMAL, szFilePath, NULL, NULL,
DEFAULT_SERVICE_DEPENDENCY,pszAccount,pszPassword);
if (hService == NULL)
{
::CloseServiceHandle(hSCM);
MessageBox(NULL, g_szErrCreateSvc, m_szServiceName, MB_OK);
return FALSE;
}
::CloseServiceHandle(hService);
::CloseServiceHandle(hSCM);
return TRUE;
}
BOOL
CServiceModule::Uninstall()
{
if (!IsInstalled())
return TRUE;
SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE |
SC_MANAGER_ENUMERATE_SERVICE | MAXIMUM_ALLOWED );
if (hSCM == NULL)
{
MessageBox(NULL, g_szErrOpenSCM, m_szServiceName, MB_OK);
return FALSE;
}
SC_HANDLE hService = ::OpenService(
hSCM, m_szServiceName, SERVICE_STOP | DELETE);
if (hService == NULL)
{
::CloseServiceHandle(hSCM);
MessageBox(NULL, g_szErrOpenSvc, m_szServiceName, MB_OK);
return FALSE;
}
SERVICE_STATUS status;
::ControlService(hService, SERVICE_CONTROL_STOP, &status);
BOOL bDelete = ::DeleteService(hService);
::CloseServiceHandle(hService);
::CloseServiceHandle(hSCM);
if (bDelete)
return TRUE;
MessageBox(NULL, g_szErrDeleteSvc, m_szServiceName, MB_OK);
return FALSE;
}
////////////////////////////////////////////////////////////////////////////////
// Logging functions
void
LogEvent
(
WORD wType,
DWORD dwEventID,
LPCTSTR pFormat,
...
)
{
#define CHARS_TO_LOG 1024
TCHAR chMsg[CHARS_TO_LOG] = { 0 };
LPTSTR lpszStrings[1] = { chMsg };
INT format_length = lstrlen(pFormat);
if (format_length < CHARS_TO_LOG)
{
va_list pArg;
va_start(pArg, pFormat);
_vsntprintf(chMsg, CHARS_TO_LOG - 1, pFormat, pArg);
va_end(pArg);
}
else
{
_sntprintf(chMsg, CHARS_TO_LOG - 1, TEXT("TLNTSVR: Too long a format string to log, Length == %d"), format_length);
}
if (_Module.m_bService)
{
LogToTlntsvrLog( _Module.m_hEventSource, wType, dwEventID,
(LPCTSTR*) &lpszStrings[0] );
}
else
{
// As we are not running as a service, just write the error to the
//console.
_putts(chMsg);
}
#undef CHARS_TO_LOG
}
///////////////////////////////////////////////////////////////////////////////
// Service startup and registration
void
CServiceModule::Start()
{
SERVICE_TABLE_ENTRY st[] =
{
{ m_szServiceName, _ServiceMain },
{ NULL, NULL }
};
if (m_bService)
{
if (! ::StartServiceCtrlDispatcher(st))
{
m_bService = FALSE;
}
}
if (m_bService == FALSE)
Run();
}
void
CServiceModule::ServiceMain
(
DWORD dwArgc,
LPTSTR* lpszArgv
)
{
// DebugBreak();
// Register the control request handler
m_status.dwCurrentState = SERVICE_START_PENDING;
m_hServiceStatus = RegisterServiceCtrlHandler(m_szServiceName, _Handler);
if (m_hServiceStatus == NULL)
{
LogEvent(EVENTLOG_ERROR_TYPE, 0, _T("Handler not installed"));
return;
}
// DebugBreak();
SetServiceStatus(SERVICE_START_PENDING);
m_status.dwWin32ExitCode = S_OK;
m_status.dwCheckPoint = 0;
m_status.dwWaitHint = 0;
//
// Security bug fix: When the CLSID of telnet object is
// embedded into a web page then DCOM is starting up
// the service. To disable this from happening the fix
// we did was to put in a new command line switch in
// ServiceParameters under the HKCR\AppID\{FE9E4896-A014-11D1-855C-00A0C944138C}\ServiceParameters
// key. This switch is "-From_DCOM". This will tell us when
// IE is starting the service versus when the service
// is started via net.exe or tlntadmn.exe. If IE is starting
// the service then we immediately exit.
//
for (DWORD dwIndex =1; dwIndex < dwArgc; ++dwIndex)
{
if (!_tcsicmp(lpszArgv[dwIndex], TEXT("-From_DCOM")) ||
!_tcsicmp(lpszArgv[dwIndex], TEXT("/From_DCOM")))
{
goto ExitOnIEInstantiation;
}
}
// DebugBreak();
// When the Run function returns, the service has stopped.
Run();
LogEvent(EVENTLOG_INFORMATION_TYPE, MSG_SHUTDOWN, _T("Service stopped"));
ExitOnIEInstantiation:
DeregisterEventSource(_Module.m_hEventSource);
SetServiceStatus( SERVICE_STOPPED );
}
void
CServiceModule::Handler
(
DWORD dwOpcode
)
{
switch (dwOpcode)
{
case SERVICE_CONTROL_STOP:
g_pTelnetService->Shutdown();
SetServiceStatus(SERVICE_STOP_PENDING);
break;
case SERVICE_CONTROL_PAUSE:
SetServiceStatus(SERVICE_PAUSE_PENDING);
g_pTelnetService->Pause();
SetServiceStatus(SERVICE_PAUSED);
break;
case SERVICE_CONTROL_CONTINUE:
SetServiceStatus(SERVICE_CONTINUE_PENDING);
g_pTelnetService->Resume();
SetServiceStatus(SERVICE_RUNNING);
break;
case SERVICE_CONTROL_INTERROGATE:
break;
case SERVICE_CONTROL_SHUTDOWN:
g_pTelnetService->SystemShutdown();
break;
default:
LogEvent(EVENTLOG_WARNING_TYPE, 0, _T("Bad service request"));
break;
}
}
void
WINAPI
CServiceModule::_ServiceMain
(
DWORD dwArgc,
LPTSTR* lpszArgv
)
{
_Module.ServiceMain(dwArgc, lpszArgv);
}
void
WINAPI
CServiceModule::_Handler
(
DWORD dwOpcode
)
{
_Module.Handler(dwOpcode);
}
void
CServiceModule::SetServiceStatus
(
DWORD dwState
)
{
m_status.dwCurrentState = dwState;
::SetServiceStatus(m_hServiceStatus, &m_status);
}
HRESULT SetSecurityForTheComObject()
{
HRESULT hr = S_FALSE;
PSID pSidAdministrators = NULL;
int aclSize = 0;
PACL newACL = NULL;
SECURITY_DESCRIPTOR sd;
{
SID_IDENTIFIER_AUTHORITY local_system_authority = SECURITY_NT_AUTHORITY;
//Build administrators alias sid
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 */
&pSidAdministrators
))
{
goto Done;
}
}
aclSize = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(pSidAdministrators) - sizeof(DWORD);
newACL = (PACL) new BYTE[aclSize];
if (newACL == NULL)
{
goto Done;
}
if (!InitializeAcl(newACL, aclSize, ACL_REVISION))
{
goto Done;
}
if (!AddAccessAllowedAce(newACL, ACL_REVISION, (CLSCTX_INPROC_SERVER|CLSCTX_LOCAL_SERVER), pSidAdministrators))
{
goto Done;
}
if( !InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION ) )
{
goto Done;
}
if( !SetSecurityDescriptorDacl(&sd, TRUE, newACL, FALSE) )
{
goto Done;
}
if( !SetSecurityDescriptorOwner(&sd, pSidAdministrators, FALSE ) )
{
goto Done;
}
if( !SetSecurityDescriptorGroup(&sd, pSidAdministrators, FALSE) )
{
goto Done;
}
// DebugBreak();
hr = CoInitializeSecurity(
&sd,
-1, // Let COM choose it
NULL, // -do-
NULL,
RPC_C_AUTHN_LEVEL_PKT,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE,
NULL
);
Done:
if( pSidAdministrators != NULL )
{
FreeSid (pSidAdministrators );
}
if( newACL )
{
delete[] newACL;
}
return hr;
}
void
CServiceModule::Run()
{
HRESULT hr;
HRESULT hRes = CoInitializeEx(NULL, COINIT_MULTITHREADED );
_ASSERTE(SUCCEEDED(hRes));
if( SetSecurityForTheComObject( ) != S_OK )
{
m_status.dwWin32ExitCode = ERROR_ACCESS_DENIED;
SetServiceStatus( SERVICE_STOPPED );
//log the failure and return;
goto Done;
}
hr = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER|CLSCTX_REMOTE_SERVER,
REGCLS_MULTIPLEUSE);
_ASSERTE(SUCCEEDED(hr));
if( !TelnetServiceThread( ) )
{
m_status.dwWin32ExitCode = ERROR_INVALID_DATA;
SetServiceStatus( SERVICE_STOPPED );
}
_Module.RevokeClassObjects();
Done:
CoUninitialize();
}
int __cdecl NoMoreMemory( size_t size )
{
int NO_MORE_MEMORY = 1;
size=size;
_chASSERT(NO_MORE_MEMORY != 1);
LogEvent( EVENTLOG_ERROR_TYPE, MSG_NOMOREMEMORY, _T(" ") );
ExitProcess( 1 );
return( 0 );
}
/////////////////////////////////////////////////////////////////////////////
//
extern "C" int WINAPI _tWinMain(HINSTANCE hInstance,
HINSTANCE /*hPrevInstance*/, LPTSTR lpCmdLine, int /*nShowCmd*/)
{
// DebugBreak();
// _set_new_handler( NoMoreMemory );
// We do not really care about the return value.
// because g_hInstRes will get the value hInstance in case of any failure
HrLoadLocalizedLibrarySFU(hInstance, L"TLNTSVRR.DLL", &g_hInstRes, NULL);
lpCmdLine = GetCommandLine(); //this line necessary for _ATL_MIN_CRT
_Module.Init(ObjectMap, g_hInstRes, IDS_SERVICENAME);
_Module.m_bService = TRUE;
// Get a handle to use with ReportEvent().
_Module.m_hEventSource = RegisterEventSource(NULL, _Module.m_szServiceName);
TCHAR szTokens[] = _T("-/");
LPCTSTR lpszToken = FindOneOf(lpCmdLine, szTokens);
while (lpszToken != NULL)
{
if (_tcsicmp(lpszToken, _T("UnregServer"))==0)
return _Module.UnregisterServer();
// Register as Local Server
if (_tcsicmp(lpszToken, _T("RegServer"))==0)
return _Module.RegisterServer(TRUE, FALSE);
// Register as Service
if (_tcsicmp(lpszToken, _T("Service"))==0)
return _Module.RegisterServer(TRUE, TRUE);
lpszToken = FindOneOf(lpszToken, szTokens);
}
OSVERSIONINFOEX osvi = { 0 };
WCHAR szSysDir[MAX_PATH+1] = {0};
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if ( !GetVersionEx((OSVERSIONINFO *) &osvi ) )
{
//OSVERSIONINFOEX is supported from NT4 SP6 on. So GetVerEx() should succeed.
return E_FAIL;
}
if(osvi.dwPlatformId == VER_PLATFORM_WIN32_NT && osvi.wProductType == VER_NT_WORKSTATION && osvi.wServicePackMajor > 0)
{
//OS is Windows XP.
// Add event log info
CRegKey eventLog;
TCHAR local_key[_MAX_PATH];
lstrcpy( local_key, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog"
_T("\\Application\\"))); // NO, BO, Baskar.
TCHAR szModule[_MAX_PATH + 1] = { 0 };
GetModuleFileName(_Module.GetModuleInstance(), szModule, _MAX_PATH);
TCHAR szResModule[(_MAX_PATH*2) +2];
DWORD len = GetModuleFileName(g_hInstRes, szResModule, _MAX_PATH);
//Check if the OS is XPSP, in that case, we need to append the xpspresdll name to
//eventmessagefile value in HKLM\system\...\eventlog\tlntsvr.
//The event message will first be searched in szResModule and then in xpsp1res.dll
if(!GetSystemDirectory(szSysDir,MAX_PATH+1))
{
_tcsncpy(szSysDir,L"%SYSTEMROOT%\\system32",MAX_PATH);
}
_snwprintf(szResModule+len,(_MAX_PATH*2)-len-1,L";%s\\xpsp1res.dll",szSysDir);
TCHAR szName[_MAX_FNAME];
_tsplitpath( szModule, NULL, NULL, szName, NULL);
lstrcat(local_key, szName); // NO overflow, Baskar
LONG result = eventLog.Create(HKEY_LOCAL_MACHINE, local_key);
if( ERROR_SUCCESS != result)
return result;
result = eventLog.SetValue(szResModule, _T("EventMessageFile"));
if(ERROR_SUCCESS != result)
return result;
DWORD dwTypes = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
EVENTLOG_INFORMATION_TYPE | EVENTLOG_AUDIT_SUCCESS |
EVENTLOG_AUDIT_FAILURE;
result = eventLog.SetValue(dwTypes, _T("TypesSupported"));
if(ERROR_SUCCESS != result)
return result;
eventLog.Close();
}
#ifndef NO_PCHECK
#ifndef WHISTLER_BUILD
if( ! IsLicensedCopy() )
{
LogEvent(EVENTLOG_ERROR_TYPE, MSG_LICENSEEXPIRED, _T(" "));
return 1;
}
#endif
#endif
// Are we Service or Local Server
CRegKey keyAppID;
LONG lRes = keyAppID.Open(HKEY_CLASSES_ROOT, _T("AppID"),MAXIMUM_ALLOWED);
if (lRes != ERROR_SUCCESS)
return lRes;
CRegKey key;
lRes = key.Open( keyAppID, APPID,MAXIMUM_ALLOWED );
if (lRes != ERROR_SUCCESS)
return lRes;
TCHAR szValue[_MAX_PATH];
DWORD dwLen = _MAX_PATH;
lRes = key.QueryValue(szValue, _T("LocalService"), &dwLen);
keyAppID.Close();
key.Close();
_Module.m_bService = FALSE;
if (lRes == ERROR_SUCCESS)
_Module.m_bService = TRUE;
#if _DEBUG || DBG
CDebugLogger::Init( TRACE_DEBUGGING, "C:\\temp\\TlntSvr.log" );
#endif
// DebugBreak();
_Module.Start();
#if _DEBUG || DBG
CDebugLogger::ShutDown();
#endif
// When we get here, the service has been stopped
return _Module.m_status.dwWin32ExitCode;
}