windows-nt/Source/XPSP1/NT/base/remoteboot/rcc/lib/lib.c

917 lines
24 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/****************************************************************************
Copyright (c) Microsoft Corporation 1999
All rights reserved
***************************************************************************/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <winsock2.h>
#include <ntexapi.h>
#include <devioctl.h>
#include <stdlib.h>
#include <rccxport.h>
#include "rcclib.h"
#include "error.h"
//
// Defines for our internal memory, starting size and then buffers are grown by the increment
//
#define START_MEMORY_SIZE 0x100000
#define MEMORY_INCREMENT 0x1000
BOOL
EnableDebugPriv(
IN PVOID GlobalBuffer
);
DWORD
RCCLibReportEventA(
DWORD EventID,
DWORD EventType,
DWORD NumStrings,
DWORD DataLength,
LPSTR *Strings,
LPVOID Data
);
HANDLE RCCLibSemHandle = NULL;
DWORD
RCCLibInit(
OUT PVOID *GlobalBuffer,
OUT PULONG GlobalBufferCurrentSize
)
{
NTSTATUS Status;
DWORD Error;
ULONG Priority;
PPROCESS_PRIORITY_CLASS PriorityClass;
//
// Check if another copy of this exe is running already
//
RCCLibSemHandle = CreateSemaphore(NULL, 1, 1, "RCCLibSem");
Error = GetLastError();
if (RCCLibSemHandle == NULL) {
RCCLibReportEventA(ERROR_RCCLIB_CREATE_SEM_FAILED,
EVENTLOG_ERROR_TYPE,
0,
sizeof(DWORD),
NULL,
&Error
);
return ERROR_RCCLIB_CREATE_SEM_FAILED;
}
if (Error == ERROR_ALREADY_EXISTS) {
CloseHandle(RCCLibSemHandle);
RCCLibReportEventA(ERROR_RCCLIB_ALREADY_RUNNING,
EVENTLOG_ERROR_TYPE,
0,
sizeof(DWORD),
NULL,
&Error
);
return ERROR_RCCLIB_ALREADY_RUNNING;
}
//
// Allocate memory for our buffers
//
*GlobalBuffer = LocalAlloc(LPTR, START_MEMORY_SIZE * sizeof(char));
if (*GlobalBuffer == NULL) {
CloseHandle(RCCLibSemHandle);
RCCLibSemHandle = NULL;
RCCLibReportEventA(ERROR_RCCLIB_INITIAL_ALLOC_FAILED,
EVENTLOG_ERROR_TYPE,
0,
0,
NULL,
NULL
);
return ERROR_RCCLIB_INITIAL_ALLOC_FAILED;
}
*GlobalBufferCurrentSize = START_MEMORY_SIZE * sizeof(char);
//
// Give ourselve God access if possible
//
if (!EnableDebugPriv(*GlobalBuffer)) {
LocalFree(*GlobalBuffer);
CloseHandle(RCCLibSemHandle);
RCCLibSemHandle = NULL;
Error = GetLastError();
RCCLibReportEventA(ERROR_RCCLIB_PRIVILEDGES_FAILED,
EVENTLOG_WARNING_TYPE,
0,
sizeof(DWORD),
NULL,
&Error
);
return Error;
}
//
// Now try and bump up our priority so that we can guarantee service
//
PriorityClass = (PPROCESS_PRIORITY_CLASS)(*GlobalBuffer);
PriorityClass = (PPROCESS_PRIORITY_CLASS)(ALIGN_UP_POINTER(PriorityClass, PROCESS_PRIORITY_CLASS));
Status = NtQueryInformationProcess(NtCurrentProcess(),
ProcessPriorityClass,
PriorityClass,
sizeof(PROCESS_PRIORITY_CLASS),
NULL
);
if (!NT_SUCCESS(Status)) {
LocalFree(*GlobalBuffer);
CloseHandle(RCCLibSemHandle);
RCCLibSemHandle = NULL;
Error = RtlNtStatusToDosError(Status);
RCCLibReportEventA(ERROR_RCCLIB_QUERY_PRIORITY_FAILED,
EVENTLOG_ERROR_TYPE,
0,
sizeof(DWORD),
NULL,
&Error
);
return Error;
}
PriorityClass->PriorityClass = PROCESS_PRIORITY_CLASS_REALTIME;
Status = NtSetInformationProcess(NtCurrentProcess(),
ProcessPriorityClass,
PriorityClass,
sizeof(PROCESS_PRIORITY_CLASS)
);
if (!NT_SUCCESS(Status)) {
LocalFree(*GlobalBuffer);
CloseHandle(RCCLibSemHandle);
RCCLibSemHandle = NULL;
Error = RtlNtStatusToDosError(Status);
RCCLibReportEventA(ERROR_RCCLIB_SET_PRIORITY_FAILED,
EVENTLOG_ERROR_TYPE,
0,
sizeof(DWORD),
NULL,
&Error
);
return Error;
}
//
// A *total* DaveC hack is that if you use ThreadBasePriority, and send
// down (HIGH_PRIORITY + 1) / 2, this actually puts you at HIGH_PRIORITY
// such that you cannot be bumped back down in priority... NT trivia for you.
//
Priority = (HIGH_PRIORITY + 1) / 2;
Status = NtSetInformationThread(NtCurrentThread(),
ThreadBasePriority,
&Priority,
sizeof(Priority)
);
if (!NT_SUCCESS(Status)) {
//
// Log an error!
//
Error = RtlNtStatusToDosError(Status);
RCCLibReportEventA(ERROR_RCCLIB_SET_PRIORITY_FAILED,
EVENTLOG_ERROR_TYPE,
0,
sizeof(DWORD),
NULL,
&Error
);
return Error;
}
}
VOID
RCCLibExit(
IN PVOID GlobalBuffer,
IN ULONG GlobalBufferSize
)
{
if (RCCLibSemHandle != NULL) {
CloseHandle(RCCLibSemHandle);
RCCLibSemHandle = NULL;
}
if (GlobalBuffer != NULL) {
LocalFree(GlobalBuffer);
}
}
DWORD
RCCLibReportEventA(
DWORD EventID,
DWORD EventType,
DWORD NumStrings,
DWORD DataLength,
LPSTR *Strings,
LPVOID Data
)
/*++
Routine Description:
This function writes the specified (EventID) log at the end of the
eventlog.
Arguments:
EventID - The specific event identifier. This identifies the
message that goes with this event.
EventType - Specifies the type of event being logged. This
parameter can have one of the following
values:
Value Meaning
EVENTLOG_ERROR_TYPE Error event
EVENTLOG_WARNING_TYPE Warning event
EVENTLOG_INFORMATION_TYPE Information event
NumStrings - Specifies the number of strings that are in the array
at 'Strings'. A value of zero indicates no strings
are present.
DataLength - Specifies the number of bytes of event-specific raw
(binary) data to write to the log. If cbData is
zero, no event-specific data is present.
Strings - Points to a buffer containing an array of null-terminated
strings that are merged into the message before
displaying to the user. This parameter must be a valid
pointer (or NULL), even if cStrings is zero.
Data - Buffer containing the raw data. This parameter must be a
valid pointer (or NULL), even if cbData is zero.
Return Value:
Returns the WIN32 extended error obtained by GetLastError().
NOTE : This function works slow since it calls the open and close
eventlog source everytime.
--*/
{
HANDLE EventlogHandle;
DWORD ReturnCode;
//
// open eventlog section.
//
EventlogHandle = RegisterEventSourceW(NULL, L"RCCLib");
if (EventlogHandle == NULL) {
ReturnCode = GetLastError();
goto Cleanup;
}
//
// Log the error code specified
//
if(!ReportEventA(EventlogHandle,
(WORD)EventType,
0, // event category
EventID,
NULL,
(WORD)NumStrings,
DataLength,
Strings,
Data
)) {
ReturnCode = GetLastError();
goto Cleanup;
}
ReturnCode = ERROR_SUCCESS;
Cleanup:
if (EventlogHandle != NULL) {
DeregisterEventSource(EventlogHandle);
}
return ReturnCode;
}
BOOL
EnableDebugPriv(
IN PVOID GlobalBuffer
)
/*++
Routine Description:
Changes the process's privilige so that kill works properly.
Arguments:
Return Value:
TRUE - success
FALSE - failure
--*/
{
HANDLE hToken;
LUID DebugValue;
PTOKEN_PRIVILEGES ptkp;
//
// Retrieve a handle of the access token
//
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
return FALSE;
}
//
// Enable the SE_DEBUG_NAME privilege.
//
if (!LookupPrivilegeValue((LPSTR) NULL, SE_DEBUG_NAME, &DebugValue)) {
return FALSE;
}
ptkp = (PTOKEN_PRIVILEGES)GlobalBuffer;
ptkp->PrivilegeCount = 4;
ptkp->Privileges[0].Luid = DebugValue;
ptkp->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
//
// Enable the INCREASE_BASE_PRIORITY privilege.
//
if (!LookupPrivilegeValue((LPSTR) NULL, SE_INC_BASE_PRIORITY_NAME, &DebugValue)) {
return FALSE;
}
ptkp->Privileges[1].Luid = DebugValue;
ptkp->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
//
// Enable the SHUTDOWN privilege.
//
if (!LookupPrivilegeValue((LPSTR) NULL, SE_SHUTDOWN_NAME, &DebugValue)) {
return FALSE;
}
ptkp->Privileges[2].Luid = DebugValue;
ptkp->Privileges[2].Attributes = SE_PRIVILEGE_ENABLED;
//
// Enable the QUOTA privilege.
//
if (!LookupPrivilegeValue((LPSTR) NULL, SE_INCREASE_QUOTA_NAME, &DebugValue)) {
return FALSE;
}
ptkp->Privileges[3].Luid = DebugValue;
ptkp->Privileges[3].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken,
FALSE,
ptkp,
sizeof(TOKEN_PRIVILEGES) + (3 * sizeof(LUID_AND_ATTRIBUTES)),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL)) {
//
// The return value of AdjustTokenPrivileges be texted
//
return FALSE;
}
return TRUE;
}
DWORD
RCCLibGetTListInfo(
OUT PRCC_RSP_TLIST ResponseBuffer,
IN LONG ResponseBufferSize,
OUT PULONG ResponseDataSize
)
{
DWORD Error;
NTSTATUS Status;
TIME_FIELDS UpTime;
LARGE_INTEGER Time;
PSYSTEM_PAGEFILE_INFORMATION PageFileInfo;
PUCHAR DataBuffer;
PUCHAR StartProcessInfo;
LONG CurrentBufferSize;
ULONG ReturnLength;
ULONG TotalOffset;
PSYSTEM_PROCESS_INFORMATION ProcessInfo;
PSYSTEM_THREAD_INFORMATION ThreadInfo;
*ResponseDataSize = 0;
if (ResponseBufferSize < sizeof(ResponseBuffer)) {
return(RtlNtStatusToDosError(STATUS_INFO_LENGTH_MISMATCH));
}
DataBuffer = (PUCHAR)(ResponseBuffer + 1);
CurrentBufferSize = ResponseBufferSize - sizeof(RCC_RSP_TLIST);
if (CurrentBufferSize < 0) {
return ERROR_OUTOFMEMORY;
}
//
// Get system-wide information
//
Status = NtQuerySystemInformation(SystemTimeOfDayInformation,
&(ResponseBuffer->TimeOfDayInfo),
sizeof(SYSTEM_TIMEOFDAY_INFORMATION),
NULL
);
if (!NT_SUCCESS(Status)) {
return(RtlNtStatusToDosError(Status));
}
Status = NtQuerySystemInformation(SystemBasicInformation,
&(ResponseBuffer->BasicInfo),
sizeof(SYSTEM_BASIC_INFORMATION),
NULL
);
if (!NT_SUCCESS(Status)) {
return(RtlNtStatusToDosError(Status));
}
//
// Get pagefile information
//
PageFileInfo = (PSYSTEM_PAGEFILE_INFORMATION)DataBuffer;
Status = NtQuerySystemInformation(SystemPageFileInformation,
PageFileInfo,
CurrentBufferSize,
&ReturnLength
);
if (NT_SUCCESS(Status)) {
ResponseBuffer->PagefileInfoOffset = ResponseBufferSize - CurrentBufferSize;
CurrentBufferSize -= ReturnLength;
DataBuffer += ReturnLength;
if (CurrentBufferSize < 0) {
return ERROR_OUTOFMEMORY;
}
//
// Go thru each pagefile and fixup the names...
//
for (; ; ) {
if (PageFileInfo->PageFileName.Length > CurrentBufferSize) {
return(RtlNtStatusToDosError(STATUS_INFO_LENGTH_MISMATCH));
}
RtlCopyMemory(DataBuffer,
(PUCHAR)(PageFileInfo->PageFileName.Buffer),
PageFileInfo->PageFileName.Length
);
PageFileInfo->PageFileName.Buffer = (PWSTR)(ResponseBufferSize - CurrentBufferSize);
DataBuffer += PageFileInfo->PageFileName.Length;
CurrentBufferSize -= PageFileInfo->PageFileName.Length;
if (CurrentBufferSize < 0) {
return ERROR_OUTOFMEMORY;
}
if (PageFileInfo->NextEntryOffset == 0) {
break;
}
PageFileInfo = (PSYSTEM_PAGEFILE_INFORMATION)((PCHAR)PageFileInfo + PageFileInfo->NextEntryOffset);
}
} else if (((ULONG)CurrentBufferSize) < ReturnLength) {
return(RtlNtStatusToDosError(STATUS_INFO_LENGTH_MISMATCH));
} else {
ResponseBuffer->PagefileInfoOffset = 0;
}
//
// Get process information
//
Status = NtQuerySystemInformation(SystemFileCacheInformation,
&(ResponseBuffer->FileCache),
sizeof(ResponseBuffer->FileCache),
NULL
);
if (!NT_SUCCESS(Status)) {
return(RtlNtStatusToDosError(Status));
}
Status = NtQuerySystemInformation(SystemPerformanceInformation,
&(ResponseBuffer->PerfInfo),
sizeof(ResponseBuffer->PerfInfo),
NULL
);
if (!NT_SUCCESS(Status)) {
return(RtlNtStatusToDosError(Status));
}
//
// Realign DataBuffer for the next query
//
DataBuffer = ALIGN_UP_POINTER(DataBuffer, SYSTEM_PROCESS_INFORMATION);
CurrentBufferSize = ResponseBufferSize - (((ULONG_PTR)DataBuffer) - ((ULONG_PTR)ResponseBuffer));
if (CurrentBufferSize < 0) {
return ERROR_OUTOFMEMORY;
}
Status = NtQuerySystemInformation(SystemProcessInformation,
DataBuffer,
CurrentBufferSize,
&ReturnLength
);
if (!NT_SUCCESS(Status)) {
return(RtlNtStatusToDosError(Status));
}
StartProcessInfo = DataBuffer;
ResponseBuffer->ProcessInfoOffset = ResponseBufferSize - CurrentBufferSize;
DataBuffer += ReturnLength;
CurrentBufferSize -= ReturnLength;
if (CurrentBufferSize < 0) {
return ERROR_OUTOFMEMORY;
}
TotalOffset = 0;
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)StartProcessInfo;
while (TRUE) {
//
// We have to take the name of each process and change the UNICODE_STRING to an offset, copying
// the name to later in the buffer.
//
if (ProcessInfo->ImageName.Buffer) {
if (CurrentBufferSize < ProcessInfo->ImageName.Length) {
return(STATUS_INFO_LENGTH_MISMATCH);
}
RtlCopyMemory(DataBuffer, (PUCHAR)(ProcessInfo->ImageName.Buffer), ProcessInfo->ImageName.Length);
ProcessInfo->ImageName.Buffer = (PWSTR)(ResponseBufferSize - CurrentBufferSize);
DataBuffer += ProcessInfo->ImageName.Length;
CurrentBufferSize -= ProcessInfo->ImageName.Length;
if (CurrentBufferSize < 0) {
return ERROR_OUTOFMEMORY;
}
}
if (ProcessInfo->NextEntryOffset == 0) {
break;
}
TotalOffset += ProcessInfo->NextEntryOffset;
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)&(StartProcessInfo[TotalOffset]);
}
*ResponseDataSize = (ULONG)(ResponseBufferSize - CurrentBufferSize);
return ERROR_SUCCESS;
}
DWORD
RCCLibKillProcess(
DWORD ProcessId
)
{
DWORD Error;
HANDLE Handle;
//
// Try to open the process
//
Handle = OpenProcess(PROCESS_TERMINATE, FALSE, ProcessId);
if (Handle == NULL) {
return GetLastError();
}
//
// Kill it
//
if (!TerminateProcess(Handle, 1)) {
CloseHandle(Handle);
return GetLastError();
}
//
// All done
//
CloseHandle(Handle);
return ERROR_SUCCESS;
}
DWORD
RCCLibLowerProcessPriority(
DWORD ProcessId
)
{
DWORD Error;
HANDLE JobHandle;
HANDLE ProcessHandle;
JOBOBJECT_EXTENDED_LIMIT_INFORMATION ProposedLimits;
TCHAR NameBuffer[50];
BOOLEAN CreatedJobObject;
DWORD ReturnedLength;
//
// Create the name for the job object
//
sprintf(NameBuffer, "RCCSrv%d", ProcessId);
//
// Try and open the existing job object
//
JobHandle = OpenJobObject(MAXIMUM_ALLOWED, FALSE, NameBuffer);
if (JobHandle == NULL) {
//
// Try to open the process
//
ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
if (ProcessHandle == NULL) {
return GetLastError();
}
//
// Now try and create a job object to wrap around this process.
//
JobHandle = CreateJobObject(NULL, NameBuffer);
if (JobHandle == NULL) {
CloseHandle(ProcessHandle);
return GetLastError();
}
CreatedJobObject = TRUE;
//
// Assign the process to this new job object.
//
if (!AssignProcessToJobObject(JobHandle, ProcessHandle)) {
CloseHandle(ProcessHandle);
goto ErrorExit;
}
CloseHandle(ProcessHandle);
} else {
CreatedJobObject = FALSE;
}
//
// Get the current set of limits
//
if (!QueryInformationJobObject(JobHandle,
JobObjectExtendedLimitInformation,
&ProposedLimits,
sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION),
&ReturnedLength
)) {
goto ErrorExit;
}
//
// Change the scheduling class and priority fields
//
ProposedLimits.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PRIORITY_CLASS;
ProposedLimits.BasicLimitInformation.PriorityClass = IDLE_PRIORITY_CLASS;
ProposedLimits.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_SCHEDULING_CLASS;
ProposedLimits.BasicLimitInformation.SchedulingClass = 0;
if (!SetInformationJobObject(JobHandle,
JobObjectExtendedLimitInformation,
&ProposedLimits,
sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)
)) {
goto ErrorExit;
}
//
// All done - leave the job handle out there, so we can get to it later.
//
return ERROR_SUCCESS;
ErrorExit:
if (CreatedJobObject) {
CloseHandle(JobHandle);
}
return GetLastError();
}
DWORD
RCCLibLimitProcessMemory(
DWORD ProcessId,
DWORD MemoryLimit // in number of KB allowed
)
{
DWORD Error;
HANDLE JobHandle;
HANDLE ProcessHandle;
JOBOBJECT_EXTENDED_LIMIT_INFORMATION ProposedLimits;
TCHAR NameBuffer[50];
BOOLEAN CreatedJobObject;
DWORD ReturnedLength;
//
// Create the name for the job object
//
sprintf(NameBuffer, "RCCSrv%d", ProcessId);
//
// Try and open the existing job object
//
JobHandle = OpenJobObject(MAXIMUM_ALLOWED, FALSE, NameBuffer);
if (JobHandle == NULL) {
//
// Try to open the process
//
ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
if (ProcessHandle == NULL) {
return GetLastError();
}
//
// Now try and create a job object to wrap around this process.
//
JobHandle = CreateJobObject(NULL, NameBuffer);
if (JobHandle == NULL) {
CloseHandle(ProcessHandle);
return GetLastError();
}
CreatedJobObject = TRUE;
//
// Assign the process to this new job object.
//
if (!AssignProcessToJobObject(JobHandle, ProcessHandle)) {
CloseHandle(ProcessHandle);
goto ErrorExit;
}
CloseHandle(ProcessHandle);
} else {
CreatedJobObject = FALSE;
}
//
// Get the current set of limits
//
if (!QueryInformationJobObject(JobHandle,
JobObjectExtendedLimitInformation,
&ProposedLimits,
sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION),
&ReturnedLength
)) {
goto ErrorExit;
}
//
// Change the memory limits
//
ProposedLimits.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PROCESS_MEMORY;
ProposedLimits.ProcessMemoryLimit = MemoryLimit * 1024 * 1024;
ProposedLimits.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_JOB_MEMORY;
ProposedLimits.JobMemoryLimit = MemoryLimit * 1024 * 1024;
if (!SetInformationJobObject(JobHandle,
JobObjectExtendedLimitInformation,
&ProposedLimits,
sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)
)) {
goto ErrorExit;
}
//
// All done - leave the job handle out there, so we can get to it later.
//
return ERROR_SUCCESS;
ErrorExit:
if (CreatedJobObject) {
CloseHandle(JobHandle);
}
return GetLastError();
}
DWORD
RCCLibIncreaseMemory(
OUT PVOID *GlobalBuffer,
OUT PULONG GlobalBufferCurrentSize
)
{
NTSTATUS Status;
PVOID NewBuffer;
NewBuffer = VirtualAlloc(NULL,
*GlobalBufferCurrentSize + MEMORY_INCREMENT,
MEM_COMMIT,
PAGE_READWRITE | PAGE_NOCACHE
);
if (NewBuffer == NULL) {
return ERROR_OUTOFMEMORY;
}
VirtualFree(*GlobalBuffer, *GlobalBufferCurrentSize, MEM_DECOMMIT);
*GlobalBufferCurrentSize += MEMORY_INCREMENT;
*GlobalBuffer = NewBuffer;
return ERROR_SUCCESS;
}