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.
1272 lines
38 KiB
1272 lines
38 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1995 - 1999
|
|
|
|
Module Name:
|
|
|
|
util.c
|
|
|
|
Abstract:
|
|
|
|
Various helper and debug functions shared between platforms.
|
|
|
|
Author:
|
|
|
|
Mario Goertzel [MarioGo]
|
|
|
|
|
|
Revision History:
|
|
|
|
MarioGo 95/10/21 Bits 'n pieces
|
|
|
|
--*/
|
|
|
|
#include <precomp.hxx>
|
|
#include <stdarg.h>
|
|
#include <osfpcket.hxx>
|
|
|
|
#ifdef DEBUGRPC
|
|
BOOL ValidateError(
|
|
IN unsigned int Status,
|
|
IN unsigned int Count,
|
|
IN const int ErrorList[])
|
|
/*++
|
|
Routine Description
|
|
|
|
Tests that 'Status' is one of an expected set of error codes.
|
|
Used on debug builds as part of the VALIDATE() macro.
|
|
|
|
Example:
|
|
|
|
VALIDATE(EventStatus)
|
|
{
|
|
RPC_P_CONNECTION_CLOSED,
|
|
RPC_P_RECEIVE_FAILED,
|
|
RPC_P_CONNECTION_SHUTDOWN
|
|
// more error codes here
|
|
} END_VALIDATE;
|
|
|
|
This function is called with the RpcStatus and expected errors codes
|
|
as parameters. If RpcStatus is not one of the expected error
|
|
codes and it not zero a message will be printed to the debugger
|
|
and the function will return false. The VALIDATE macro ASSERT's the
|
|
return value.
|
|
|
|
Arguments:
|
|
|
|
Status - Status code in question.
|
|
Count - number of variable length arguments
|
|
|
|
... - One or more expected status codes. Terminated with 0 (RPC_S_OK).
|
|
|
|
Return Value:
|
|
|
|
TRUE - Status code is in the list or the status is 0.
|
|
|
|
FALSE - Status code is not in the list.
|
|
|
|
--*/
|
|
{
|
|
unsigned i;
|
|
|
|
for (i = 0; i < Count; i++)
|
|
{
|
|
if (ErrorList[i] == (int) Status)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
PrintToDebugger("RPC Assertion: unexpected failure %lu (0lx%08x)\n",
|
|
(unsigned long)Status, (unsigned long)Status);
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
#endif // DEBUGRPC
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
#ifdef RPC_ENABLE_WMI_TRACE
|
|
|
|
#include <wmistr.h>
|
|
#include <evntrace.h>
|
|
#include "wmlum.h" // private header from clustering
|
|
|
|
extern "C"
|
|
{
|
|
DWORD __stdcall
|
|
I_RpcEnableWmiTrace(
|
|
PWML_TRACE fn,
|
|
WMILIB_REG_STRUCT ** pHandle
|
|
);
|
|
}
|
|
|
|
typedef DWORD (*WMI_TRACE_FN)();
|
|
|
|
PWML_TRACE WmiTraceFn = 0;
|
|
|
|
WMILIB_REG_STRUCT WmiTraceData;
|
|
|
|
GUID WmiMessageGuid = { /* 41de81c0-aa28-460b-a455-c23809e7c170 */
|
|
0x41de81c0,
|
|
0xaa28,
|
|
0x460b,
|
|
{0xa4, 0x55, 0xc2, 0x38, 0x09, 0xe7, 0xc1, 0x70}
|
|
};
|
|
|
|
|
|
DWORD __stdcall
|
|
I_RpcEnableWmiTrace(
|
|
PWML_TRACE fn,
|
|
WMILIB_REG_STRUCT ** pHandle
|
|
)
|
|
{
|
|
WmiTraceFn = fn;
|
|
|
|
*pHandle = &WmiTraceData;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
BOOL fEnableLog = TRUE;
|
|
|
|
C_ASSERT(sizeof(LUID) == sizeof(__int64));
|
|
|
|
struct RPC_EVENT * RpcEvents;
|
|
|
|
long EventArrayLength = MAX_RPC_EVENT;
|
|
long NextEvent = 0;
|
|
|
|
BOOL DisableEvents = 0;
|
|
|
|
/*
|
|
boolean SubjectExceptions[256];
|
|
boolean VerbExceptions[256];
|
|
*/
|
|
|
|
#define LOG_VAR( x ) &(x), sizeof(x)
|
|
|
|
HANDLE hLogFile = 0;
|
|
|
|
struct RPC_EVENT_LOG
|
|
{
|
|
DWORD Thread;
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
unsigned char Subject;
|
|
unsigned char Verb;
|
|
};
|
|
DWORD ZeroSet;
|
|
};
|
|
|
|
void * SubjectPointer;
|
|
void * ObjectPointer;
|
|
|
|
ULONG_PTR Data;
|
|
void * EventStackTrace[STACKTRACE_DEPTH];
|
|
};
|
|
|
|
void
|
|
TrulyLogEvent(
|
|
IN unsigned char Subject,
|
|
IN unsigned char Verb,
|
|
IN void * SubjectPointer,
|
|
IN void * ObjectPointer,
|
|
IN ULONG_PTR Data,
|
|
IN BOOL fCaptureStackTrace,
|
|
IN int AdditionalFramesToSkip
|
|
)
|
|
{
|
|
/*
|
|
if (DisableEvents != SubjectExceptions[Subject] ||
|
|
DisableEvents != VerbExceptions[Verb])
|
|
{
|
|
return;
|
|
}
|
|
*/
|
|
|
|
//
|
|
// Allocate the event table if it isn't already there.
|
|
//
|
|
if (!RpcEvents)
|
|
{
|
|
struct RPC_EVENT * Temp = (struct RPC_EVENT *) HeapAlloc( GetProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
EventArrayLength * sizeof(RPC_EVENT) );
|
|
HANDLE LocalFile;
|
|
if (!Temp)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (InterlockedCompareExchangePointer((void **) &RpcEvents, Temp, 0) != 0)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, Temp);
|
|
}
|
|
|
|
/*
|
|
if (wcsstr(GetCommandLine(), L"fs.exe") != NULL)
|
|
{
|
|
LocalFile = CreateFile(L"d:\\rpcclnt.log", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (LocalFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
hLogFile = LocalFile;
|
|
}
|
|
else
|
|
{
|
|
if (hLogFile == 0)
|
|
{
|
|
DbgPrint("ERROR: Could not create RPC log file: %d\n", GetLastError());
|
|
}
|
|
// else
|
|
// somebody already set it - ignore
|
|
}
|
|
}
|
|
else if (wcsstr(GetCommandLine(), L"fssvr.exe") != NULL)
|
|
{
|
|
LocalFile = CreateFile(L"d:\\rpcsvr.log", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (LocalFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
hLogFile = LocalFile;
|
|
}
|
|
else
|
|
{
|
|
if (hLogFile == 0)
|
|
{
|
|
DbgPrint("ERROR: Could not create RPC log file: %d\n", GetLastError());
|
|
}
|
|
// else
|
|
// somebody already set it - ignore
|
|
}
|
|
}
|
|
*/
|
|
|
|
/*
|
|
DisableEvents = TRUE;
|
|
SubjectExceptions[SU_ADDRESS] = TRUE;
|
|
VerbExceptions[EV_CREATE] = TRUE;
|
|
VerbExceptions[EV_DELETE] = TRUE;
|
|
*/
|
|
/*
|
|
SubjectExceptions[SU_HEAP] = TRUE;
|
|
SubjectExceptions[SU_EVENT] = TRUE;
|
|
SubjectExceptions[SU_BCACHE] = TRUE;
|
|
*/
|
|
/*
|
|
DisableEvents = TRUE;
|
|
SubjectExceptions['a'] = TRUE;
|
|
SubjectExceptions['g'] = TRUE;
|
|
SubjectExceptions['G'] = TRUE;
|
|
SubjectExceptions['W'] = TRUE;
|
|
SubjectExceptions['X'] = TRUE;
|
|
SubjectExceptions['Y'] = TRUE;
|
|
SubjectExceptions['Z'] = TRUE;
|
|
SubjectExceptions['w'] = TRUE;
|
|
SubjectExceptions['x'] = TRUE;
|
|
SubjectExceptions['y'] = TRUE;
|
|
SubjectExceptions['z'] = TRUE;
|
|
VerbExceptions['t'] = TRUE;
|
|
VerbExceptions['G'] = TRUE;
|
|
VerbExceptions['g'] = TRUE;
|
|
VerbExceptions['w'] = TRUE;
|
|
VerbExceptions['x'] = TRUE;
|
|
VerbExceptions['y'] = TRUE;
|
|
VerbExceptions['z'] = TRUE;
|
|
VerbExceptions['W'] = TRUE;
|
|
VerbExceptions['X'] = TRUE;
|
|
VerbExceptions['Y'] = TRUE;
|
|
VerbExceptions['Z'] = TRUE;
|
|
*/
|
|
}
|
|
|
|
unsigned index = InterlockedIncrement(&NextEvent);
|
|
|
|
index %= EventArrayLength;
|
|
|
|
RpcEvents[index].Time = GetTickCount();
|
|
RpcEvents[index].Verb = Verb;
|
|
RpcEvents[index].Subject = Subject;
|
|
RpcEvents[index].Thread = (short) GetCurrentThreadId();
|
|
RpcEvents[index].SubjectPointer = SubjectPointer;
|
|
RpcEvents[index].ObjectPointer = ObjectPointer;
|
|
RpcEvents[index].Data = Data;
|
|
RpcEvents[index].EventStackTrace[0] = NULL;
|
|
RpcEvents[index].EventStackTrace[1] = NULL;
|
|
RpcEvents[index].EventStackTrace[2] = NULL;
|
|
RpcEvents[index].EventStackTrace[3] = NULL;
|
|
|
|
CallTestHook( TH_RPC_LOG_EVENT, &RpcEvents[index], 0 );
|
|
|
|
#ifdef RPC_ENABLE_WMI_TRACE
|
|
if (WmiTraceData.EnableFlags)
|
|
{
|
|
TraceMessage(
|
|
WmiTraceData.LoggerHandle,
|
|
TRACE_MESSAGE_SEQUENCE | TRACE_MESSAGE_GUID | TRACE_MESSAGE_SYSTEMINFO | TRACE_MESSAGE_TIMESTAMP,
|
|
&WmiMessageGuid,
|
|
Verb,
|
|
LOG_VAR(Subject),
|
|
LOG_VAR(SubjectPointer),
|
|
LOG_VAR(ObjectPointer),
|
|
LOG_VAR(Data),
|
|
0
|
|
);
|
|
}
|
|
#endif
|
|
|
|
if (fCaptureStackTrace)
|
|
{
|
|
ULONG ignore;
|
|
|
|
RtlCaptureStackBackTrace(
|
|
1 + AdditionalFramesToSkip,
|
|
STACKTRACE_DEPTH,
|
|
(void **) &RpcEvents[index].EventStackTrace,
|
|
&ignore);
|
|
}
|
|
else
|
|
{
|
|
RpcEvents[index].EventStackTrace[0] = 0;
|
|
}
|
|
|
|
if (hLogFile)
|
|
{
|
|
DWORD BytesWritten;
|
|
|
|
/*
|
|
RPC_EVENT_LOG logEntry;
|
|
RPC_EVENT *CurrentEvent = &RpcEvents[index];
|
|
logEntry.Thread = CurrentEvent->Thread;
|
|
logEntry.ZeroSet = 0;
|
|
logEntry.Subject = CurrentEvent->Subject;
|
|
logEntry.Verb = CurrentEvent->Verb;
|
|
logEntry.Data = CurrentEvent->Data;
|
|
logEntry.ObjectPointer = CurrentEvent->ObjectPointer;
|
|
logEntry.SubjectPointer = CurrentEvent->SubjectPointer;
|
|
memcpy(logEntry.EventStackTrace, CurrentEvent->EventStackTrace, sizeof(logEntry.EventStackTrace));
|
|
WriteFile(hLogFile, &logEntry, sizeof(logEntry), &BytesWritten, NULL);
|
|
*/
|
|
WriteFile(hLogFile, &RpcEvents[index], sizeof(RpcEvents[index]), &BytesWritten, NULL);
|
|
}
|
|
}
|
|
|
|
void RPC_ENTRY
|
|
I_RpcLogEvent (
|
|
IN unsigned char Subject,
|
|
IN unsigned char Verb,
|
|
IN void * SubjectPointer,
|
|
IN void * ObjectPointer,
|
|
IN unsigned Data,
|
|
IN BOOL fCaptureStackTrace,
|
|
IN int AdditionalFramesToSkip
|
|
)
|
|
{
|
|
LogEvent(Subject, Verb, SubjectPointer, ObjectPointer, Data,
|
|
fCaptureStackTrace, AdditionalFramesToSkip);
|
|
}
|
|
|
|
#if 0
|
|
|
|
BOOL
|
|
IsLoggingEnabled()
|
|
{
|
|
RPC_CHAR ModulePath[ MAX_PATH ];
|
|
RPC_CHAR * ModuleName;
|
|
|
|
//
|
|
// Find out the .EXE name.
|
|
//
|
|
if (!GetModuleFileName( NULL, ModulePath, sizeof(ModulePath)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
signed i;
|
|
for (i=RpcpStringLength(ModulePath)-1; i >= 0; --i)
|
|
{
|
|
if (ModulePath[i] == '\\')
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
ModuleName = ModulePath + i + 1;
|
|
|
|
//
|
|
// See whether logging should be enabled.
|
|
//
|
|
HANDLE hImeo;
|
|
HANDLE hMyProcessOptions;
|
|
DWORD Error;
|
|
DWORD Value;
|
|
DWORD Length = sizeof(Value);
|
|
DWORD Type;
|
|
|
|
Error = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
RPC_CONST_STRING("Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options"),
|
|
0,
|
|
KEY_READ,
|
|
&hImeo
|
|
);
|
|
if (Error)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
Error = RegOpenKeyEx( hImeo,
|
|
ModuleName,
|
|
0,
|
|
KEY_READ,
|
|
&hMyProcessOptions
|
|
);
|
|
RegCloseKey( hImeo );
|
|
|
|
if (Error)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
Error = RegQueryValueEx( hMyProcessOptions,
|
|
RPC_CONST_STRING("Enable RPC Logging"),
|
|
0,
|
|
&Type,
|
|
&Value,
|
|
&Length
|
|
);
|
|
|
|
RegCloseKey( hMyProcessOptions );
|
|
|
|
if (Error)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (Type == REG_DWORD && Value)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if (Type == REG_SZ && 0 == RpcpStringCompare((RPC_CHAR *) Value, RPC_CONST_CHAR('Y')))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#endif
|
|
|
|
extern "C" int __cdecl _purecall(void)
|
|
{
|
|
#ifdef DEBUGRPC
|
|
ASSERT(!"PureVirtualCalled");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
PUNICODE_STRING
|
|
FastGetImageBaseNameUnicodeString (
|
|
void
|
|
)
|
|
/*++
|
|
Routine Description
|
|
|
|
Retrieves the image base name with touching minimal amount of
|
|
other memory. Returns a UNICODE_STRING structure.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
A pointer to LDR private UNICODE_STRING string with the image name.
|
|
Don't write or delete it!
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY Module;
|
|
PLDR_DATA_TABLE_ENTRY Entry;
|
|
|
|
Module = NtCurrentPeb()->Ldr->InLoadOrderModuleList.Flink;
|
|
Entry = CONTAINING_RECORD(Module,
|
|
LDR_DATA_TABLE_ENTRY,
|
|
InLoadOrderLinks);
|
|
|
|
return &(Entry->BaseDllName);
|
|
}
|
|
|
|
const RPC_CHAR *
|
|
FastGetImageBaseName (
|
|
void
|
|
)
|
|
/*++
|
|
Routine Description
|
|
|
|
Retrieves the image base name with touching minimal amount of
|
|
other memory.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
A pointer to LDR private string with the image name. Don't write or
|
|
delete it!
|
|
|
|
--*/
|
|
{
|
|
return (FastGetImageBaseNameUnicodeString())->Buffer;
|
|
}
|
|
|
|
//
|
|
// RPC Verifier utility functions
|
|
//
|
|
|
|
unsigned int
|
|
RndInteger (
|
|
void
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
A private fast implementation of a congruential random
|
|
number generator. We do not really care about its being
|
|
"good" and use the results for fault injeciton.
|
|
|
|
Return Value:
|
|
|
|
A "random" integer between 0 and UINT_MAX
|
|
|
|
--*/
|
|
{
|
|
static BOOL fInit;
|
|
static unsigned short nCalls;
|
|
static unsigned long seed;
|
|
RPC_STATUS Status;
|
|
|
|
if (fInit == false || nCalls > 1000)
|
|
{
|
|
fInit = true;
|
|
nCalls = 0;
|
|
|
|
// Get a seed as a "true" random number.
|
|
Status = GenerateRandomNumber((unsigned char *)&seed, sizeof(unsigned long));
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
// On failure, still try to get some entropy.
|
|
ASSERT(Status == RPC_S_OUT_OF_MEMORY);
|
|
seed = GetTickCount();
|
|
}
|
|
}
|
|
|
|
// Use a simple Pi-enspired congruence f-la.
|
|
seed = 3141592653 * seed + 2718281829;
|
|
|
|
VERIFIER_DBG_PRINT_1("RndInteger() returned 0x%x\n", seed);
|
|
|
|
return seed;
|
|
}
|
|
|
|
inline BOOL
|
|
RndBool(
|
|
unsigned int Prob
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generates a boolean with probability of True Prob/10000
|
|
|
|
Return Value:
|
|
|
|
A boolean with the given probability of truth
|
|
|
|
--*/
|
|
{
|
|
BOOL ret = RndInteger() < Prob*(0xffffffff/10000);
|
|
VERIFIER_DBG_PRINT_1("RndBool() returned %d\n", ret);
|
|
return (ret);
|
|
}
|
|
|
|
// Rounding of an unsigned float.
|
|
#define URound(f) (unsigned int)(f+0.5)
|
|
|
|
inline unsigned int RndIntegerInRange(
|
|
unsigned int min,
|
|
unsigned int max
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns a random integer from min to max.
|
|
The distribution of the possible value is uniform.
|
|
|
|
Return Value:
|
|
|
|
A random integer within a given range
|
|
|
|
--*/
|
|
{
|
|
unsigned int ret = min + URound( (float)(max-min) * ((float)RndInteger() / (float)0xffffffff) );
|
|
VERIFIER_DBG_PRINT_3("RndIntegerInRange(0x%x, 0x%x) returned 0x%x\n", min, max, ret);
|
|
return (ret);
|
|
}
|
|
|
|
#define BitFlip(arg,pos) ((arg) ^ (1L << (pos)))
|
|
|
|
inline void
|
|
RndBitFlip(
|
|
unsigned char *addr,
|
|
unsigned int len
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Flips one bit in each of len bytes starting at addr
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
for (unsigned int d = 0; d<len; d++)
|
|
{
|
|
unsigned char bit = (unsigned char)RndIntegerInRange(1,8);
|
|
VERIFIER_DBG_PRINT_2("RndBitFlip() flipping bit %u at address 0x%x\n", bit, &(addr[d]));
|
|
addr[d] = BitFlip(addr[d], bit);
|
|
}
|
|
}
|
|
|
|
inline void
|
|
RndIncDec(
|
|
unsigned char *addr,
|
|
unsigned int len
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Flips one bit in each of len bytes starting at addr
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
for (unsigned int d = 0; d<len; d++)
|
|
{
|
|
if (RndBool(5000))
|
|
{
|
|
addr[d]++;
|
|
VERIFIER_DBG_PRINT_1("RndIncDec() inc byte at address 0x%x\n", &(addr[d]));
|
|
}
|
|
else
|
|
{
|
|
addr[d]--;
|
|
VERIFIER_DBG_PRINT_1("RndIncDec() dec byte at address 0x%x\n", &(addr[d]));
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CorruptBuffer(
|
|
unsigned int BufferLength,
|
|
unsigned char *Buffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Corrupts the buffer in accordance with the RPC verifier settings.
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
unsigned int Size;
|
|
unsigned int Start;
|
|
|
|
VERIFIER_DBG_PRINT_2("CorruptBuffer() length=%d buffer=0x%x\n",
|
|
BufferLength, Buffer);
|
|
|
|
// Determine the size of the corruption.
|
|
if (pRpcVerifierSettings->CorruptionSizeType == FixedSize)
|
|
{
|
|
Size = pRpcVerifierSettings->CorruptionSize;
|
|
}
|
|
else if (pRpcVerifierSettings->CorruptionSizeType == RandomSize)
|
|
{
|
|
Size = RndIntegerInRange(1, min(pRpcVerifierSettings->CorruptionSize,BufferLength));
|
|
}
|
|
else
|
|
{
|
|
ASSERT(0 && "Unexpected CorruptionSizeType\n");
|
|
}
|
|
|
|
// Determine the start of the corruption.
|
|
Start = RndIntegerInRange(0, BufferLength-Size);
|
|
|
|
VERIFIER_DBG_PRINT_2("CorruptBuffer() Size=%d Start=%d\n",
|
|
Size, Start);
|
|
|
|
if (pRpcVerifierSettings->CorruptionPattern == ZeroOut)
|
|
{
|
|
// For localized corruption, zero out random block of size Size.
|
|
if (pRpcVerifierSettings->CorruptionDistributionType == LocalizedDistribution)
|
|
{
|
|
RtlZeroMemory((PVOID)(Buffer+Start), Size);
|
|
}
|
|
// For randomized corruption, zero out Size random bytes from the buffer.
|
|
else
|
|
{
|
|
for (unsigned int i=0; i<Size; i++)
|
|
{
|
|
Buffer[RndIntegerInRange(0, BufferLength-1)] = 0;
|
|
}
|
|
}
|
|
}
|
|
else if (pRpcVerifierSettings->CorruptionPattern == Negate)
|
|
{
|
|
// For localized corruption, negate random block of size Size.
|
|
if (pRpcVerifierSettings->CorruptionDistributionType == LocalizedDistribution)
|
|
{
|
|
for (unsigned int i = 0; i<Size; i++)
|
|
{
|
|
Buffer[Start+i] = 0xff;
|
|
}
|
|
}
|
|
// For randomized corruption, negate Size random bytes from the buffer.
|
|
else
|
|
{
|
|
for (unsigned int i=0; i<Size; i++)
|
|
{
|
|
Buffer[RndIntegerInRange(0, BufferLength-1)] = 0xff;
|
|
}
|
|
}
|
|
}
|
|
else if (pRpcVerifierSettings->CorruptionPattern == BitFlip)
|
|
{
|
|
// For localized corruption, flip bits on Size consecutive bytes.
|
|
if (pRpcVerifierSettings->CorruptionDistributionType == LocalizedDistribution)
|
|
{
|
|
RndBitFlip(Buffer+Start, Size);
|
|
}
|
|
// For randomized corruption, flip random bits in Size bytes all over the buffer.
|
|
else
|
|
{
|
|
for (unsigned int i=0; i<Size; i++)
|
|
{
|
|
RndBitFlip(Buffer + RndIntegerInRange(0, BufferLength-1),1);
|
|
}
|
|
}
|
|
}
|
|
else if (pRpcVerifierSettings->CorruptionPattern == IncDec)
|
|
{
|
|
// For localized corruption, increment or decrement Size consecutive bytes.
|
|
if (pRpcVerifierSettings->CorruptionDistributionType == LocalizedDistribution)
|
|
{
|
|
RndIncDec(Buffer+Start, Size);
|
|
}
|
|
// For randomized corruption, inc/dec Size bytes all over the buffer.
|
|
else
|
|
{
|
|
for (unsigned int i=0; i<Size; i++)
|
|
{
|
|
RndIncDec(Buffer + RndIntegerInRange(0, BufferLength-1),1);
|
|
}
|
|
}
|
|
}
|
|
else if (pRpcVerifierSettings->CorruptionPattern == Randomize)
|
|
{
|
|
// For localized corruption distribution, randomize block of size Size.
|
|
if (pRpcVerifierSettings->CorruptionDistributionType == LocalizedDistribution)
|
|
{
|
|
for (unsigned int i = 0; i<Size; i++)
|
|
{
|
|
Buffer[Start+i] = (unsigned char) RndInteger();
|
|
}
|
|
}
|
|
// For randomized corruption distribution, randomize Size bytes all over the buffer.
|
|
else
|
|
{
|
|
for (unsigned int i=0; i<Size; i++)
|
|
{
|
|
Buffer[RndIntegerInRange(0, BufferLength-1)] = (unsigned char) RndInteger();
|
|
}
|
|
}
|
|
}
|
|
else if (pRpcVerifierSettings->CorruptionPattern == AllPatterns)
|
|
{
|
|
// Choose a random pattern of corrupion only for the duration of this call.
|
|
// This may affect other corruptions taking place, but it's OK since the overal
|
|
// bahavior is correct.
|
|
pRpcVerifierSettings->CorruptionPattern = (tCorruptionPattern)RndIntegerInRange(MIN_CORRUPTION_PATTERN_ID,
|
|
MAX_CORRUPTION_PATTERN_ID);
|
|
CorruptBuffer(BufferLength, Buffer);
|
|
pRpcVerifierSettings->CorruptionPattern = AllPatterns;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(0 && "Unexpected CorruptionPattern\n");
|
|
}
|
|
}
|
|
|
|
void
|
|
CorruptionInject(
|
|
tBufferType BufferType,
|
|
unsigned int *pBufferLength,
|
|
void **pBuffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Injects corruption into a buffer if necesssary.
|
|
Injection is done according to the RPC verifier settings for this buffer type.
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
BOOL fSecure = false;
|
|
void *Buffer = *pBuffer;
|
|
unsigned int BufferLength = *pBufferLength;
|
|
|
|
VERIFIER_DBG_PRINT_3("CorruptionInject() type=%d length=%d buffer=0x%x\n",
|
|
BufferType, *pBufferLength, *pBuffer);
|
|
|
|
// Check if there is a buffer to corrupt and if
|
|
// this type of buffer should have corruption injected into it.
|
|
if (*pBuffer
|
|
&& ((BufferType == ServerReceive && pRpcVerifierSettings->fCorruptionInjectServerReceives) ||
|
|
(BufferType == ClientReceive && pRpcVerifierSettings->fCorruptionInjectClientReceives)))
|
|
{
|
|
// If yes, corrupt the buffer if necessary.
|
|
|
|
// First, we will try to truncate the buffer.
|
|
if (pRpcVerifierSettings->ProbBufferTruncation &&
|
|
RndBool(pRpcVerifierSettings->ProbBufferTruncation))
|
|
{
|
|
VERIFIER_DBG_PRINT_0("CorruptionInject() - truncating the buffer\n");
|
|
|
|
// We truncate OSF buffers only. The scenario is not interesting
|
|
// for DG. The way we can tell the difference between the two types of packets
|
|
// is by the header.
|
|
// For OSF the packet header will look like:
|
|
// rpcconn_common
|
|
// +0x000 rpc_vers : 5
|
|
// +0x001 rpc_vers_minor : 0
|
|
// For DG it will be:
|
|
// NCA_PACKET_HEADER
|
|
// +0x000 RpcVersion : 0x4
|
|
// +0x001 PacketType : 0
|
|
// So we just want the first 2 bytes to be 5,0.
|
|
if (BufferLength >= sizeof(rpcconn_common) &&
|
|
((unsigned short)*((unsigned char*)Buffer + 0x0) == 5 &&
|
|
(unsigned short)*((unsigned char*)Buffer + 0x1) == 0
|
|
)
|
|
)
|
|
{
|
|
unsigned short NewBufferLength = BufferLength - RndIntegerInRange(1, pRpcVerifierSettings->MaxBufferTruncationSize);
|
|
NewBufferLength = max(NewBufferLength, sizeof(rpcconn_common));
|
|
|
|
// We have a connection-oriented buffer - truncate it.
|
|
I_RpcTransConnectionReallocPacket(
|
|
NULL, // The connection argument is ignored by the realloc routine.
|
|
pBuffer,
|
|
*pBufferLength,
|
|
NewBufferLength);
|
|
|
|
VERIFIER_DBG_PRINT_3("CorruptionInject() - truncated buffer 0x%x from 0x%x to 0x%x\n",
|
|
*pBuffer,
|
|
*pBufferLength,
|
|
NewBufferLength);
|
|
|
|
// Update the buffer size so that the right value is seen by the caller.
|
|
Buffer = *pBuffer;
|
|
*pBufferLength = NewBufferLength;
|
|
BufferLength = NewBufferLength;
|
|
|
|
// Adjust the frag_length so that it is equal to the new packetlength.
|
|
// We need to do this since the runtime relies on these being in agreement and the
|
|
// transports guarantee it.
|
|
*((unsigned int*)((unsigned char*)Buffer + 0x8)) = BufferLength;
|
|
}
|
|
}
|
|
|
|
// After the buffer has been truncated, we may corrupt it.
|
|
|
|
// Check if we can corrupt the RPC header section.
|
|
if (RndBool(pRpcVerifierSettings->ProbRpcHeaderCorruption))
|
|
{
|
|
// Corrupt it if we can.
|
|
CorruptBuffer(
|
|
min(sizeof(rpcconn_common),BufferLength), // This will mostly whack the common RPC header.
|
|
(unsigned char *)Buffer);
|
|
}
|
|
|
|
//
|
|
// Determine whether this is a secure buffer.
|
|
// The way we tell is by looking for the signature of rpcconn_common.auth_length != 0.
|
|
// We will query this field directly, using the following offsets:
|
|
//
|
|
// rpcconn_common
|
|
// +0x000 rpc_vers : UChar
|
|
// ...
|
|
// +0x008 frag_length : Uint2B
|
|
// +0x00a auth_length : Uint2B
|
|
// +0x00c call_id : Uint4B
|
|
//
|
|
// This may falsely count some packets as secure occasionally,
|
|
// say in the case of fragmentation, but we can take this downside and the benefits of
|
|
// a compact check outweigh it.
|
|
//
|
|
fSecure = ( (BufferLength > sizeof(rpcconn_common)) &&
|
|
((unsigned short)*((unsigned char*)Buffer + 0xa) != 0) );
|
|
|
|
VERIFIER_DBG_PRINT_1("CorruptionInject() fSecure=%d\n", fSecure);
|
|
|
|
// Check if we can corrupt the Data section.
|
|
if ((!fSecure && RndBool(pRpcVerifierSettings->ProbDataCorruption)) ||
|
|
(fSecure && RndBool(pRpcVerifierSettings->ProbSecureDataCorruption)))
|
|
{
|
|
// Corrupt it if we can.
|
|
CorruptBuffer(
|
|
BufferLength,
|
|
(unsigned char *)Buffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
#define MAX_STACK_TRACE 8
|
|
|
|
PVOID LastEventStackTrace[MAX_STACK_TRACE];
|
|
|
|
void
|
|
PrintCurrentStackTrace(
|
|
unsigned int FramesToSkip,
|
|
unsigned int Size
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Prints a stack trace of Size frames after the first FramesToSkip frames.
|
|
Only the addresses are printed since there is no way to get to the
|
|
symbolic information from here.
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
Size = min (Size, MAX_STACK_TRACE);
|
|
PVOID *EventStackTrace = (PVOID *)alloca(Size*sizeof(PVOID));
|
|
|
|
// Capture and print the stack trace.
|
|
RtlCaptureStackBackTrace(FramesToSkip,
|
|
Size,
|
|
EventStackTrace,
|
|
NULL);
|
|
|
|
for (unsigned int i=0; i<Size; i++)
|
|
{
|
|
DbgPrint("\t0x%x\n", EventStackTrace[i]);
|
|
}
|
|
|
|
// To simplify debugging, save the local stack trace into a global variable.
|
|
memcpy(LastEventStackTrace, EventStackTrace, Size*sizeof(PVOID));
|
|
}
|
|
|
|
void
|
|
PrintUUID(
|
|
GUID *Uuid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Prints a UUID
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
unsigned long *Data = (unsigned long *)Uuid;
|
|
|
|
if ((Data[0] == 0) &&
|
|
(Data[1] == 0) &&
|
|
(Data[2] == 0) &&
|
|
(Data[3] == 0))
|
|
{
|
|
DbgPrint("(Null Uuid)");
|
|
}
|
|
else
|
|
{
|
|
DbgPrint("%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
|
Uuid->Data1, Uuid->Data2, Uuid->Data3, Uuid->Data4[0], Uuid->Data4[1],
|
|
Uuid->Data4[2], Uuid->Data4[3], Uuid->Data4[4], Uuid->Data4[5],
|
|
Uuid->Data4[6], Uuid->Data4[7] );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Per-interface security check exemption settings.
|
|
//
|
|
|
|
// The structure mapping an interface onto a flag.
|
|
// An array of these defines interfaces exempt from some of the rpc verifier checks.
|
|
typedef struct _tRpcVerifierIfExemption
|
|
{
|
|
GUID IfUuid; // An interface UUID.
|
|
DWORD ExemptionFlags; // Which security checks are to be disabled.
|
|
} tRpcVerifierIfExemption;
|
|
|
|
//
|
|
// This is a list of interface UUID's exempt from some or all checks.
|
|
// We match the interfaces by their syntax GUIDs.
|
|
//
|
|
const tRpcVerifierIfExemption RpcVerifierExemptInterfaces[] = {
|
|
// 000001A0-0000-0000-C000-000000000046
|
|
// ok to be remotely accessible, not secured, clear text traffic and no mutual authentication.
|
|
{{0x000001A0, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}, ALLOW_EVERYTHING},
|
|
// 12345678-1234-ABCD-EF00-01234567CFFB
|
|
// Netlogon implements its own authentication protocol via the NetrServerReqChallenge
|
|
// and NetrServerAuthenticate3 rpc calls. These rpc calls happen without packet privacy and result
|
|
// in the servers being mutually authenticated and a key exchange between client and server.
|
|
// After that all calls use privacy via the netlogon package.
|
|
{{0x12345678, 0x1234, 0xABCD, {0xEF, 0x00, 0x01, 0x23, 0x45, 0x67, 0xCF, 0xFB}}, ALLOW_NO_MUTUAL_AUTH_REMOTE_ACCESS|ALLOW_UNENCRYPTED_REMOTE_ACCESS},
|
|
// 338cd001-2244-31f1-aaaa-900038001003
|
|
// ok to be remotely accessible, not secured. It uses an access check in the server routine instead
|
|
// of a callback because it must support downlevel clients which used native named pipes transport security.
|
|
{{0x338cd001, 0x2244, 0x31f1, {0xaa, 0xaa, 0x90, 0x00, 0x38, 0x00, 0x10, 0x03}}, ALLOW_EVERYTHING},
|
|
// 4d9f4ab8-7d1c-11cf-861e-0020af6e7c57
|
|
// ok to be remotely accessible, not secured, clear text traffic and no mutual authentication.
|
|
{{0x4d9f4ab8, 0x7d1c, 0x11cf, {0x86, 0x1e, 0x00, 0x20, 0xaf, 0x6e, 0x7c, 0x57}}, ALLOW_EVERYTHING},
|
|
// 99fcfec4-5260-101b-bbcb-00aa0021347a
|
|
// ok to be remotely accessible, not secured, clear text traffic and no mutual authentication.
|
|
{{0x99fcfec4, 0x5260, 0x101b, {0xbb, 0xcb, 0x00, 0xaa, 0x00, 0x21, 0x34, 0x7a}}, ALLOW_EVERYTHING},
|
|
// e1af8308-5d1f-11c9-91a4-08002b14a0fa
|
|
// ok to be remotely accessible, not secured, clear text traffic and no mutual authentication.
|
|
{{0xe1af8308, 0x5d1f, 0x11c9, {0x91, 0xa4, 0x08, 0x00, 0x2b, 0x14, 0xa0, 0xfa}}, ALLOW_EVERYTHING},
|
|
// e60c73e6-88f9-11cf-9af1-0020af6e72f4
|
|
// ok to be remotely accessible, not secured, clear text traffic and no mutual authentication.
|
|
{{0xe60c73e6, 0x88f9, 0x11cf, {0x9a, 0xf1, 0x00, 0x20, 0xaf, 0x6e, 0x72, 0xf4}}, ALLOW_EVERYTHING},
|
|
|
|
// We make all of the locator interfaces exempt. There will be a message
|
|
// on locator start-up giving a warning for the service as a whole.
|
|
// Also, the locator server manager routines will be protected in the user code.
|
|
// e33c0cc4-0482-101a-bc0c-02608c6ba218 - LocToLoc
|
|
{{0xe33c0cc4, 0x0482, 0x101a, {0xbc, 0x0c, 0x02, 0x60, 0x8c, 0x6b, 0xa2, 0x18}}, ALLOW_EVERYTHING},
|
|
// d3fbb514-0e3b-11cb-8fad-08002b1d29c3 - NsiC
|
|
{{0xd3fbb514, 0x0e3b, 0x11cb, {0x8f, 0xad, 0x08, 0x00, 0x2b, 0x1d, 0x29, 0xc3}}, ALLOW_EVERYTHING},
|
|
// d6d70ef0-0e3b-11cb-acc3-08002b1d29c4 - NsiM
|
|
{{0xd6d70ef0, 0x0e3b, 0x11cb, {0xac, 0xc3, 0x08, 0x00, 0x2b, 0x1d, 0x29, 0xc4}}, ALLOW_EVERYTHING},
|
|
// d6d70ef0-0e3b-11cb-acc3-08002b1d29c3 - NsiS
|
|
{{0xd6d70ef0, 0x0e3b, 0x11cb, {0xac, 0xc3, 0x08, 0x00, 0x2b, 0x1d, 0x29, 0xc3}}, ALLOW_EVERYTHING},
|
|
|
|
// The following lsa and sam interfaces are secure in virtue of using a well-known
|
|
// endpoint and relying on the transport np security.
|
|
// They can't use RPC security because of backward compatibility.
|
|
// 12345778-1234-ABCD-EF00-0123456789AC - SAM
|
|
{{0x12345778, 0x1234, 0xABCD, {0xEF, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAC}}, ALLOW_EVERYTHING},
|
|
// 12345778-1234-ABCD-EF00-0123456789AB - LSA
|
|
{{0x12345778, 0x1234, 0xABCD, {0xEF, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB}}, ALLOW_EVERYTHING},
|
|
// 3919286a-b10c-11d0-9ba8-00c04fd92ef5 - dsrole.
|
|
{{0x3919286a, 0xb10c, 0x11d0, {0x9b, 0xa8, 0x00, 0xc0, 0x4f, 0xd9, 0x2e, 0xf5}}, ALLOW_EVERYTHING},
|
|
|
|
// The DFS interface can't use RPC security because of the compatibility considerations.
|
|
// 4fc742e0-4a10-11cf-8273-00aa004ae673 - DFS
|
|
{{0x4fc742e0, 0x4a10, 0x11cf, {0x82, 0x73, 0x00, 0xaa, 0x00, 0x4a, 0xe6, 0x73}}, ALLOW_EVERYTHING},
|
|
|
|
// The licensing server is listening on the following interface.
|
|
// This is a temp fix to be removed after .NET RC1 after which point the underlying bug will be fixed.
|
|
// 2f59a331-bf7d-48cb-9e5c-7c090d76e8b8
|
|
{{0x2f59a331, 0xbf7d, 0x48cb, {0x9e, 0x5c, 0x7c, 0x09, 0x0d, 0x76, 0xe8, 0xb8}}, ALLOW_EVERYTHING},
|
|
|
|
// srvsvc.dll has backwards compatibility reasons preventing it from using encryption.
|
|
// 4B324FC8-1670-01D3-1278-5A47BF6EE188
|
|
{{0x4B324FC8, 0x1670, 0x01D3, {0x12, 0x78, 0x5A, 0x47, 0xBF, 0x6E, 0xE1, 0x88}}, ALLOW_UNENCRYPTED_REMOTE_ACCESS},
|
|
|
|
// EFS has backwards compatibility considerations and were not able to fix the code on time.
|
|
// c681d488-d850-11d0-8c52-00c04fd90f7e
|
|
{{0xc681d488, 0xd850, 0x11d0, {0x8c, 0x52, 0x00, 0xc0, 0x4f, 0xd9, 0x0f, 0x7e}}, ALLOW_EVERYTHING}
|
|
};
|
|
|
|
BOOL
|
|
IsInterfaceExempt (
|
|
IN GUID *IfUuid,
|
|
IN DWORD CheckFlag
|
|
)
|
|
/*++
|
|
|
|
Function Name: IsInterfaceExempt
|
|
|
|
Parameters:
|
|
If - A UUID for the interface for which we want to look-up the exemption.
|
|
CheckFlag - Flag for the check being tested for exemption.
|
|
|
|
Description:
|
|
Verifies whether an interface is exempt from a particular security check.
|
|
|
|
Returns:
|
|
TRUE - If an interface is exempt from a given check.
|
|
FALSE - Otherwise.
|
|
|
|
--*/
|
|
{
|
|
// Go through all exempt interfaces.
|
|
for (int i=0; i<sizeof(RpcVerifierExemptInterfaces)/sizeof(tRpcVerifierIfExemption); i++)
|
|
{
|
|
// Check if one of them matches the provided UUID.
|
|
if (RpcpMemoryCompare(&(RpcVerifierExemptInterfaces[i].IfUuid), IfUuid, sizeof(UUID)) == 0)
|
|
{
|
|
// If it does, check whether the exemption is enabled for the flag being queried.
|
|
if (RpcVerifierExemptInterfaces[i].ExemptionFlags & CheckFlag)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
// If we could not find the interface then it is not exempt from the check.
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Unsafe protseqs detection.
|
|
//
|
|
|
|
// List of protocol sequences that are not actively used and are unsafe as a result.
|
|
const RPC_CHAR *RpcVerifierUnsafeProtseqs[] = {
|
|
RPC_CONST_STRING("ncadg_ip_udp"),
|
|
RPC_CONST_STRING("ncacn_spx"),
|
|
RPC_CONST_STRING("ncacn_at_dsp")
|
|
};
|
|
|
|
BOOL
|
|
IsProtseqUnsafe (
|
|
IN RPC_CHAR *ProtocolSequence
|
|
)
|
|
/*++
|
|
|
|
Function Name: IsProtseqUnsafe
|
|
|
|
Parameters:
|
|
ProtocolSequence - A string specifying the protseq to be checked for safety.
|
|
|
|
Description:
|
|
Verifies whether a protseq is one of the rarely used and unsafe ones.
|
|
|
|
Returns:
|
|
TRUE - The protseq specified is unsafe.
|
|
FALSE - Otherwise.
|
|
|
|
--*/
|
|
{
|
|
// Go through all the unsafe protseqs.
|
|
for (int i=0; i<sizeof(RpcVerifierUnsafeProtseqs)/sizeof(const RPC_CHAR *); i++)
|
|
{
|
|
// Check if one of them matches the provided protocol.
|
|
if (RpcpStringNCompare(RpcVerifierUnsafeProtseqs[i],
|
|
ProtocolSequence,
|
|
RpcpStringLength(RpcVerifierUnsafeProtseqs[i])) == 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
// If none match, the protseq is safe.
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Security-related utility functions
|
|
//
|
|
|
|
RPC_STATUS
|
|
IsCurrentUserAdmin(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks if the current thread's or process' token is that of an admin.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - if the token is that of an admin.
|
|
RPC_S_ACESS_DENIED - the user is not an admin or a failure occurred.
|
|
|
|
--*/
|
|
{
|
|
SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
|
|
PSID psidAdministrators = NULL;
|
|
BOOL b;
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
BOOL fIsMember = false;
|
|
|
|
b = AllocateAndInitializeSid(
|
|
&siaNtAuthority,
|
|
2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_ADMINS,
|
|
0, 0, 0, 0, 0, 0,
|
|
&psidAdministrators
|
|
);
|
|
|
|
if(b)
|
|
{
|
|
// When TokenHandle is NULL, CheckTokenMembership uses
|
|
// the impersonation token of the calling thread.
|
|
// If the thread is not impersonating, the function duplicates
|
|
// the thread's primary token to create an impersonation token.
|
|
b = CheckTokenMembership(NULL, psidAdministrators, &fIsMember);
|
|
|
|
// Token is not that of an admin or token membership could
|
|
// not be verified.
|
|
if(!b || !fIsMember)
|
|
{
|
|
Status = RPC_S_ACCESS_DENIED;
|
|
}
|
|
}
|
|
// We could not allocate a SID.
|
|
else
|
|
{
|
|
Status = RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if(psidAdministrators)
|
|
FreeSid(psidAdministrators);
|
|
|
|
return Status;
|
|
}
|
|
|
|
const SID LocalSystem = { 1, 1, SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SYSTEM_RID};
|
|
const SID LocalService = { 1, 1, SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SERVICE_RID};
|
|
const SID NetworkService = { 1, 1, SECURITY_NT_AUTHORITY, SECURITY_NETWORK_SERVICE_RID};
|
|
const RPC_SID2 Admin1 = { 1, 2, SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS};
|