/****************************************************************************/ // icasrv.c // // TermSrv service process entry points. // // Copyright (C) 1997-2000 Microsoft Corporation /****************************************************************************/ #include "precomp.h" #pragma hdrstop #include #include "icaevent.h" #include "sessdir.h" #include extern BOOL UpdateOemAndProductInfo(HKEY); extern BOOL IsServiceLoggedAsSystem( VOID ); extern VOID WriteErrorLogEntry( IN NTSTATUS NtStatusCode, IN PVOID pRawData, IN ULONG RawDataLength ); extern NTSTATUS WinStationInitRPC(); extern NTSTATUS InitializeWinStationSecurityLock(VOID); extern VOID AuditEnd(); /* * Definitions */ #define STACKSIZE_LPCTHREAD (4 * 0x1000) /* * Internal Procedures defined */ VOID ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv); VOID Handler(DWORD fdwControl); BOOL UpdateServiceStatus(DWORD, DWORD, DWORD, DWORD); void ShutdownService(); /* * Global variables */ WCHAR gpszServiceName[] = L"TermService"; SERVICE_TABLE_ENTRY gpServiceTable[] = { gpszServiceName, (LPSERVICE_MAIN_FUNCTION)ServiceMain, NULL, NULL, }; SERVICE_STATUS_HANDLE gStatusHandle; SERVICE_STATUS gStatus; DWORD gExitStatus = STATUS_SUCCESS; WCHAR g_wszProductVersion[22]; TCHAR g_tszServiceAccount[UNLEN + 1]; BOOL g_fAppCompat = TRUE; BOOL g_bPersonalTS = FALSE; BOOL g_bPersonalWks = FALSE; BOOL g_SafeBootWithNetwork = FALSE; BOOL gbServer = FALSE; BOOL gBreakOnProcessExit = FALSE; OSVERSIONINFOEX gOsVersion; HANDLE gReadyEventHandle = NULL; // // The following is used to inform Session 0 winlogon of the credentials needed to notify 3rd party n/w logon providers // This happens only during force logoff console reconnect scenario in PTS and /console in Server // ExtendedClientCredentials g_MprNotifyInfo; extern PSID gAdminSid; extern PSID gSystemSid; // Local prototypes. void LicenseModeInit(HKEY); NTSTATUS WsxInit(VOID); NTSTATUS VfyInit(VOID); BOOL WINAPI IsSafeBootWithNetwork(); void CreateTermsrvHeap () { IcaHeap = GetProcessHeap(); return; } #ifdef TERMSRV_PROC /****************************************************************************/ // main // // Standard console-app-style entry point. Returns an NTSTATUS code. /****************************************************************************/ int _cdecl main(int argc, char *argv[]) { NTSTATUS Status = STATUS_SUCCESS; KPRIORITY BasePriority; HRESULT hr; TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: Loading...\n")); /* * Run TermSrv at just above foreground priority. */ BasePriority = FOREGROUND_BASE_PRIORITY + 1; Status = NtSetInformationProcess(NtCurrentProcess(), ProcessBasePriority, &BasePriority, sizeof(BasePriority) ); ASSERT((Status == STATUS_PRIVILEGE_NOT_HELD) || NT_SUCCESS(Status)); // Initialize COM once with multithreaded capability. This must be done // on the main service thread to allow other threads in the service to // inherit this initialization, if not specifically initialized for // apartment threading. hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if (!SUCCEEDED(hr)) { HANDLE h; WCHAR hrString[16]; PWSTR String; h = RegisterEventSource(NULL, gpszServiceName); if (h != NULL) { wsprintfW(hrString, L"0x%X", hr); String = hrString; ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, EVENT_TERMSRV_FAIL_COM_INIT, NULL, 1, 0, &String, NULL); DeregisterEventSource(h); } DbgPrint("TERMSRV: Failed init COM, hr=0x%X\n", hr); goto done; } /* * Call service dispatcher */ if (!StartServiceCtrlDispatcher(gpServiceTable)) { Status = GetLastError(); DbgPrint("TERMSRV: Error %d in StartServiceCtrlDispatcher\n", Status); goto done; } done: if (SUCCEEDED(hr)) CoUninitialize(); TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: Unloading...\n")); return Status; } #else // TERMSRV_PROC BOOL WINAPI DllMain( HINSTANCE hinstDLL, // handle to the DLL module DWORD fdwReason, // reason for calling function LPVOID lpvReserved // reserved ) { BOOL fResult = TRUE; switch(fdwReason) { case DLL_PROCESS_ATTACH: hModuleWin = hinstDLL; DisableThreadLibraryCalls(hinstDLL); break; default:; } return fResult; } #endif // TERMSRV_PROC /***************************************************************************** * * InitializeLoadMetrics * * Grabs baseline system resource values for use in load balancing. These * values are used to factor out the system resources required for basic OS * operation so they don't get into the calculations for how much resource on * average a user is consuming. * * * ENTRY: * no arguments. * * EXIT: * void * ****************************************************************************/ VOID InitializeLoadMetrics() { SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION ProcessorInfo[MAX_PROCESSORS]; SYSTEM_PERFORMANCE_INFORMATION SysPerfInfo; SYSTEM_BASIC_INFORMATION SysBasicInfo; ULONG i; NTSTATUS Status; memset(&gLB, 0, sizeof(LOAD_BALANCING_METRICS)); // Get basic system information Status = NtQuerySystemInformation(SystemBasicInformation, &SysBasicInfo, sizeof(SysBasicInfo), NULL); if (!NT_SUCCESS(Status)) { TRACE((hTrace, TC_LOAD, TT_ERROR, "InitializeLoadMetrics failed! SystemBasicInformation: %lx\n", Status)); return; } gLB.NumProcessors = SysBasicInfo.NumberOfProcessors; gLB.PageSize = SysBasicInfo.PageSize; gLB.PhysicalPages = SysBasicInfo.NumberOfPhysicalPages; // Establish minimum usage levels to prevent absurd estimation gLB.MinPtesPerUser = SimAvgPtesPerUser; gLB.MinPagedPoolPerUser = (SimAvgPagedPoolPerUser * 1024) / gLB.PageSize; gLB.MinCommitPerUser = (SimCommitPerUser * 1024) / gLB.PageSize; // Grab base boot values. This isn't perfect, but it allows us to factor // out base OS resource requirements from the per user averages. The runtime // algorithm will reset the baselines if we go below these. Status = NtQuerySystemInformation(SystemPerformanceInformation, &SysPerfInfo, sizeof(SysPerfInfo), NULL); if (!NT_SUCCESS(Status)) { TRACE((hTrace, TC_LOAD, TT_ERROR, "InitializeLoadMetrics failed! SystemPerformanceInformation: %lx\n", Status)); return; } // Note: we have an unsolvable problem in that there is no way to get // perfect values for how much memory the baseline system consumes. We // default baseline commit to 64M since that is the minimum recommended // system requirement. gLB.BaselineCommit = (64 * 1024*1024) / gLB.PageSize; // gLB.BaselineCommit = SysPerfInfo.CommittedPages; gLB.BaselineFreePtes = SysPerfInfo.FreeSystemPtes; gLB.BaselinePagedPool = SysPerfInfo.PagedPoolPages; // Initialize CPU Loading Status = NtQuerySystemInformation(SystemProcessorPerformanceInformation, ProcessorInfo, sizeof(ProcessorInfo), NULL); if (!NT_SUCCESS(Status)) { TRACE((hTrace, TC_LOAD, TT_ERROR, "InitializeLoadMetrics failed! SystemProcessorPerformanceInformation: %lx\n", Status)); return; } for (i = 0; i < gLB.NumProcessors; i++) { gLB.IdleCPU.QuadPart += ProcessorInfo[i].IdleTime.QuadPart; gLB.TotalCPU.QuadPart += ProcessorInfo[i].KernelTime.QuadPart + ProcessorInfo[i].UserTime.QuadPart; } // Start out saying we're 80 percent idle (0-255 based) gLB.AvgIdleCPU = 204 ; // Indicate we got all the intial values! gLB.fInitialized = TRUE; TRACE((hTrace, TC_LOAD, TT_API1, "InitializeLoadMetrics():\n")); TRACE((hTrace, TC_LOAD, TT_API1, " Processors [%6ld], PageSize [%6ld], Physical [%6ld]\n", gLB.NumProcessors, gLB.PageSize, gLB.PhysicalPages)); TRACE((hTrace, TC_LOAD, TT_API1, " PtesAvail [%6ld], PagedUsed [%6ld], Commit [%6ld]\n", gLB.BaselineFreePtes, gLB.BaselinePagedPool, gLB.BaselineCommit)); } BOOL IsKernelDebuggerAttached () { SYSTEM_KERNEL_DEBUGGER_INFORMATION KernelDebuggerInfo; NTSTATUS Status; Status = NtQuerySystemInformation( SystemKernelDebuggerInformation, &KernelDebuggerInfo, sizeof(KernelDebuggerInfo), NULL ); return ( NT_SUCCESS(Status) && KernelDebuggerInfo.KernelDebuggerEnabled ); } void DebugBreakIfAsked() { TCHAR REG_TERMSRV_DEBUGBREAK[] = TEXT("DebugTS"); TCHAR REG_TERMSRV_DEBUGGER[] = TEXT("Debugger"); TCHAR szDebugger[256]; TCHAR szCommand[256]; HKEY hTermSrv = NULL; DWORD dwBreakIn; DWORD dwValueType; DWORD dwSize; DWORD dwError; enum { TermSrvDoNotBreak = 0, TermSrvBreakIfBeingDebugged = 1, TermSrvAttachDebugger = 2, TermSrvBreakAlways = 3 }; dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REG_CONTROL_TSERVER, 0, KEY_READ, &hTermSrv ); if (ERROR_SUCCESS == dwError) { dwSize = sizeof(dwBreakIn); dwError = RegQueryValueEx( hTermSrv, REG_TERMSRV_DEBUGBREAK, NULL, &dwValueType, (LPBYTE)&dwBreakIn, &dwSize ); if (ERROR_SUCCESS == dwError && dwValueType == REG_DWORD) { switch (dwBreakIn) { case TermSrvAttachDebugger: // // if its already being debugged Break into it. // if (IsDebuggerPresent()) { DebugBreak(); break; } // // Get the debugger to be launched. // must contain %d which will be replaced by processid // dwSize = sizeof(szDebugger) / sizeof(TCHAR); dwError = RegQueryValueEx( hTermSrv, REG_TERMSRV_DEBUGGER, NULL, &dwValueType, (LPBYTE)szDebugger, &dwSize ); if (ERROR_SUCCESS == dwError && dwValueType == REG_SZ) { PROCESS_INFORMATION ProcessInfo; STARTUPINFO StartupInfo; wsprintf(szCommand, szDebugger, GetCurrentProcessId()); DbgPrint("TERMSRV:*-----------------* Executing:<%ws> *-----------------*\n", szCommand); ZeroMemory(&StartupInfo, sizeof(StartupInfo)); StartupInfo.cb = sizeof(StartupInfo); if (!CreateProcess(NULL, szCommand, NULL, NULL, FALSE, 0, NULL, NULL, &StartupInfo, &ProcessInfo)) { DbgPrint("TERMSRV:*-----------------* TERMSRV:CreateProcess failed *-----------------*\n"); } else { CloseHandle(ProcessInfo.hProcess); CloseHandle(ProcessInfo.hThread); while (!IsDebuggerPresent()) { Sleep(500); } } } else { DbgPrint("TERMSRV:*-----------------* Did not find the debugger entry. *-----------------*\n"); } break; case TermSrvBreakIfBeingDebugged: // check if any debugger is attached, if not dont breakin. if (!IsDebuggerPresent() && !IsKernelDebuggerAttached ()) break; case TermSrvBreakAlways: DebugBreak(); break; case TermSrvDoNotBreak: default: break; } } RegCloseKey(hTermSrv); } else { DbgPrint("TERMSRV:*-----------------* Could not open termsrv registry *-----------------*\n"); } } /****************************************************************************/ // ServiceMain // // TermSrv service entry point. /****************************************************************************/ VOID ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) { HANDLE hIcaLPCThread; HANDLE hIcaLPCPort = NULL; DWORD dwValueType; LONG lReturn; DWORD cbValue; BOOL bAdvertiseTS; DWORD dwTSAdvertise; NTSTATUS Status; HKEY hKeyTermSrv = NULL; DWORDLONG dwlConditionMask; DebugBreakIfAsked(); TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: ServiceMain entered...\n")); gStatus.dwServiceType = SERVICE_WIN32; gStatus.dwWaitHint = 30000; gStatus.dwCurrentState = SERVICE_STOPPED; /* * Register the control handler */ if (!(gStatusHandle = RegisterServiceCtrlHandler(gpszServiceName, Handler))) { DbgPrint("TERMSRV: Error %d in RegisterServiceCtrlHandler\n", GetLastError()); goto done; } // If Terminal Services are not enabled then don't allow starting termsrv // service. if (!IsTerminalServicesEnabled()) { HANDLE h; TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: Not a TSBox.")); h = RegisterEventSource(NULL, gpszServiceName); if (h != NULL) { if (!ReportEvent( h, // event log handle EVENTLOG_ERROR_TYPE, // event type 0, // category zero EVENT_NOT_A_TSBOX, // event identifier NULL, // no user security identifier 0, // one substitution string 0, // no data NULL, // pointer to string array NULL // pointer to data )) { DBGPRINT(("ReportEvent Failed %ld. Event ID=%lx\n",GetLastError(), EVENT_NOT_A_TSBOX)); } } goto done; } CreateTermsrvHeap (); /* * Create and set an event which indicates that TermSrv is ready. * WinLogon checks this event. Do not signal now. * */ gReadyEventHandle = CreateEvent(NULL, TRUE, FALSE, TEXT("Global\\TermSrvReadyEvent")); // Initialize Global System and Admin SID Status = NtCreateAdminSid(&gAdminSid); if (!NT_SUCCESS(Status)) { goto done; } Status = InitializeWinStationSecurityLock(); if (!NT_SUCCESS(Status)) { goto done; } Status = NtCreateSystemSid(&gSystemSid); if (!NT_SUCCESS(Status)) { goto done; } if (!IsServiceLoggedAsSystem()) { WriteErrorLogEntry(EVENT_NOT_SYSTEM_ACCOUNT, NULL, 0); gExitStatus = ERROR_PRIVILEGE_NOT_HELD; goto done; } // Set global flag for Personal WorkStation g_bPersonalWks = IsPersonalWorkstation(); #if DBG if( TRUE == g_bPersonalWks ) { DbgPrint("TERMSRV : TS running on Personal Workstation\n"); } else { DbgPrint("TERMSRV : Not Personal Workstation\n"); } #endif // // Initialize HelpAssistant password encryption. // lReturn = TSHelpAssistantInitializeEncryptionLib(); // // Not a critical error, No help will be available // #if DBG if( lReturn != ERROR_SUCCESS ) { DbgPrint( "TERMSRV : EncryptionLib failed with %d, no help is available\n", lReturn ); } #endif // // We are booting in safeboot with network support // g_SafeBootWithNetwork = IsSafeBootWithNetwork(); // Set the global flag for Personal TS support. We use this to reduce // the feature set based on product (e.g. no load balancing session // directory if not on Server). g_bPersonalTS = IsPersonalTerminalServicesEnabled(); ZeroMemory(&gOsVersion, sizeof(OSVERSIONINFOEX)); gOsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); gOsVersion.wProductType = VER_NT_WORKSTATION; dwlConditionMask = 0; VER_SET_CONDITION(dwlConditionMask, VER_PRODUCT_TYPE, VER_EQUAL); gbServer = !VerifyVersionInfo(&gOsVersion, VER_PRODUCT_TYPE, dwlConditionMask); // Open a single, global HKLM\System\CCS\Control\TS reg handle, from which // other init code can query. lReturn = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CONTROL_TSERVER, 0, KEY_READ, &hKeyTermSrv); if (lReturn != ERROR_SUCCESS) { DbgPrint("TERMSRV: Unable to open TS key in HKLM, lasterr=0x%X", GetLastError()); goto done; } /* * Indicate service is starting. */ Status = UpdateServiceStatus(SERVICE_START_PENDING, 0, 1, 0); if (!NT_SUCCESS(Status)) { DbgPrint("TERMSRV: Unable update service status %X\n", Status ); } Status = RtlCreateEnvironment(TRUE, &DefaultEnvironment); if (!NT_SUCCESS(Status)) { DbgPrint("TERMSRV: Unable to alloc default environment, Status=0x%X\n", Status); goto done; } #ifdef TERMSRV_PROC /* * Get the module handle for messages. */ hModuleWin = GetModuleHandleW(NULL); #endif // TERMSRV_PROC /* * Indicate service has started successfully. * Maybe this should be moved below? No way!!! */ Status = UpdateServiceStatus(SERVICE_RUNNING, 0, 2, 0); if (!Status) DbgPrint("TERMSRV: Unable to update service status %X\n", Status); /* * Connect to the session manager */ Status = SmConnectToSm((PUNICODE_STRING)NULL, (HANDLE)NULL, 0, &IcaSmApiPort); if (!NT_SUCCESS(Status)) goto done; // Initialize the licensing mode - this only gets information, it doesn't // initialize the licensing core. LicenseModeInit(hKeyTermSrv); // Perform the bulk of the TermSrv init. Status = InitTermSrv(hKeyTermSrv); if (!NT_SUCCESS(Status)) goto ShutdownService; /* * Indicate that we are a Terminal Server unless were asked not to * advertise ourselves as a Terminal Server. */ bAdvertiseTS = TRUE; cbValue = sizeof(dwTSAdvertise); lReturn = RegQueryValueEx(hKeyTermSrv, REG_TERMSRV_ADVERTISE, NULL, &dwValueType, (LPBYTE)&dwTSAdvertise, &cbValue); if (ERROR_SUCCESS == lReturn && dwValueType == REG_DWORD) bAdvertiseTS = dwTSAdvertise; if (bAdvertiseTS) SetServiceBits(gStatusHandle, SV_TYPE_TERMINALSERVER, TRUE, TRUE); /* * Need to do this at least once */ UpdateOemAndProductInfo(hKeyTermSrv); // Initialize TermSrv and TermDD trace. InitializeSystemTrace(hKeyTermSrv); /* * Set TermDD parameters. */ GetSetSystemParameters(hKeyTermSrv); /* * Initialize WinStation extension DLL support */ Status = WsxInit(); if (!NT_SUCCESS(Status)) goto ShutdownService; /* * Initialize DLL Verification mechanism. */ Status = VfyInit(); if (!NT_SUCCESS(Status)) goto ShutdownService; /* * Start WinStations */ StartAllWinStations(hKeyTermSrv); // Initialize the TS Session Directory for load balancing. // Not available on Personal TS or remote admin. if (!g_bPersonalTS && g_fAppCompat) InitSessionDirectory(); InitializeLoadMetrics(); // Done with init, close the TermSrv regkey. RegCloseKey(hKeyTermSrv); hKeyTermSrv = NULL; /* * Initialize WinStationAPI's */ Status = WinStationInitRPC(); ASSERT( NT_SUCCESS( Status ) ); if (!NT_SUCCESS(Status)) { goto done; } /* * Set break on termination flag for our process. */ { NTSTATUS stp; if ( IsKernelDebuggerAttached ()) { stp = RtlSetProcessIsCritical(TRUE, NULL, FALSE); if (stp == STATUS_SUCCESS) { gBreakOnProcessExit = TRUE; } else { DbgPrint("TERMSRV: RtlSetProcessIsCritical returned: %x ", stp); } } } /* * Set the event which indicates that TermSrv is ready. * WinLogon checks this event. */ if (gReadyEventHandle != NULL) SetEvent(gReadyEventHandle); TSStartupSalem(); return; ShutdownService: ShutdownService(); done: // Kill the session directory. if (!g_bPersonalTS && g_fAppCompat) DestroySessionDirectory(); // In case of error, check the TermSrv regkey again. if (hKeyTermSrv != NULL) RegCloseKey(hKeyTermSrv); UpdateServiceStatus(SERVICE_STOPPED, gExitStatus, 5, 0); } /****************************************************************************/ // Handler // // TermSrv service control event handler. /****************************************************************************/ VOID Handler(DWORD fdwControl) { TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: Handler %d\n", fdwControl)); switch (fdwControl) { case SERVICE_CONTROL_STOP: // We absolutely do not want to be stopping TermSrv -- it is // the only location for a lot of system-wide TS related state. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: control code %d, stopping service...\n", fdwControl)); if (gStatus.dwCurrentState == SERVICE_RUNNING) { UpdateServiceStatus(SERVICE_STOP_PENDING, 0, 3, 0); #ifdef notdef // For now don't stop TermSRV // The CDM service does a KeAttachProcess() to this process if (gReadyEventHandle != NULL) { ResetEvent(gReadyEventHandle); CloseHandle(gReadyEventHandle); gReadyEventHandle = NULL; } ShutdownService(); UpdateServiceStatus(SERVICE_STOPPED, gExitStatus, 5, 0); #endif } break; case SERVICE_CONTROL_SHUTDOWN: DBGPRINT(("TERMSRV: control code %d, shutdown service...\n", fdwControl)); if (gStatus.dwCurrentState == SERVICE_RUNNING) { // 2 seconds at most to shut down. UpdateServiceStatus(SERVICE_STOP_PENDING, 0, 4, 2000); #ifdef notdef // We don't trigger this event that invokes destructors for // all of TermSrv, since on shutdown we don't want to be // destroying machine state. We want to invoke only those // destructors that are required for proper functioning of // the system. #endif // Invoke required destruction code. if (gReadyEventHandle != NULL) { ResetEvent(gReadyEventHandle); CloseHandle(gReadyEventHandle); gReadyEventHandle = NULL; } ShutdownService(); UpdateServiceStatus(SERVICE_STOPPED, 0, 4, 0); } break; case SERVICE_CONTROL_INTERROGATE : TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: Interrogating service...\n")); SetServiceStatus(gStatusHandle, &gStatus); break; default: DBGPRINT(("TERMSRV: Unhandled control code %d\n", fdwControl)); break; } } /****************************************************************************/ // ShutdownService // // Called by service manager to shut down the service at system shutdown // time. This function should invoke only the most important and required // destruction code, since we're on a strict time limit on system shutdown. /****************************************************************************/ void ShutdownService() { //free authz resource manager AuditEnd(); // Destroy the session directory so the directory can be informed to // remove server- and session-specific information. if (!g_bPersonalTS && g_fAppCompat) DestroySessionDirectory(); } /****************************************************************************/ // UpdateServiceStatus // // Updates the service's status to the Service Control Manager. Returns // FALSE on error. /****************************************************************************/ BOOL UpdateServiceStatus( DWORD CurrentState, DWORD ExitCode, DWORD CheckPoint, DWORD WaitHint) { // If service is starting, then disable all control requests, otherwise // accept shutdown notifications if we are an app server, to properly // clean up the session directory. We do not accept stop requests // during the lifetime of the server up state, the CDM service does a // KeAttachProcess() to this process so it must always be around. if (gStatusHandle == NULL) { return FALSE; } gStatus.dwControlsAccepted = 0; gStatus.dwCurrentState = CurrentState; gStatus.dwWin32ExitCode = ExitCode; gStatus.dwCheckPoint = CheckPoint; gStatus.dwServiceSpecificExitCode = 0; gStatus.dwWaitHint = WaitHint; return SetServiceStatus(gStatusHandle, &gStatus); } /***************************************************************************** * LicenseModeInit * * Initialize the licensing mode ****************************************************************************/ void LicenseModeInit(HKEY hKeyTermSrv) { DWORD dwValueType; LONG lReturn; DWORD cbValue = sizeof( DWORD ), dwAccount = UNLEN + 1; DWORD dwRegValue; OSVERSIONINFO VersionInfo; ASSERT(hKeyTermSrv != NULL); // // Get the user name for which the service is started under // GetUserName(g_tszServiceAccount, &dwAccount); // // Check whether Remote Admin is enabled // lReturn = RegQueryValueEx(hKeyTermSrv, REG_TERMSRV_APPCOMPAT, NULL, &dwValueType, (LPBYTE) &dwRegValue, &cbValue); if (lReturn == ERROR_SUCCESS) { g_fAppCompat = (BOOL)dwRegValue; } // // Get the product version // memset( &VersionInfo, 0, sizeof( OSVERSIONINFO ) ); VersionInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); if (GetVersionEx(&VersionInfo)) { wsprintf( g_wszProductVersion, L"%d.%d", VersionInfo.dwMajorVersion, VersionInfo.dwMinorVersion ); } else { TRACE((hTrace, TC_ICASRV, TT_ERROR, "LicenseModeInit: GetVersionEx " "failed: 0x%x\n", GetLastError())); } } // // Get Safeboot option, code modified from ds\security\gina\winlogon\aenrlhlp.c // BOOL WINAPI IsSafeBootWithNetwork() { DWORD dwSafeBoot = 0; DWORD cbSafeBoot = sizeof(dwSafeBoot); DWORD dwType = 0; HKEY hKeySafeBoot = NULL; if(ERROR_SUCCESS == RegOpenKeyW( HKEY_LOCAL_MACHINE, L"system\\currentcontrolset\\control\\safeboot\\option", &hKeySafeBoot)) { // we did in fact boot under safeboot control if(ERROR_SUCCESS != RegQueryValueExW( hKeySafeBoot, L"OptionValue", NULL, &dwType, (LPBYTE)&dwSafeBoot, &cbSafeBoot)) { dwSafeBoot = 0; } if(hKeySafeBoot) RegCloseKey(hKeySafeBoot); } return ( SAFEBOOT_NETWORK == dwSafeBoot ); }