reactos/ntoskrnl/ex/uuid.c
Pierre Schweitzer 6ca1c55c6e
[NTOSKRNL] Only allow SYSTEM to call NtSetUuidSeed()
Also, validate input buffer before attempting any operation on it.
This will avoid userland applications to be able to trigger an invalid
read in the kernel (and thus a BSOD).

Regarding access restriction, see:
https://stackoverflow.com/questions/1254244/need-access-to-ntsetuuidseed-from-a-non-localsystem-process

CORE-15460
2018-12-18 23:12:41 +01:00

435 lines
10 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: ntoskrnl/ex/uuid.c
* PURPOSE: UUID generator
*
* PROGRAMMERS: Eric Kohl
Thomas Weidenmueller
*/
/* INCLUDES *****************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
#define SEED_BUFFER_SIZE 6
/* Number of 100ns ticks per clock tick. To be safe, assume that the clock
resolution is at least 1000 * 100 * (1/1000000) = 1/10 of a second */
#define TICKS_PER_CLOCK_TICK 1000
#define SECSPERDAY 86400
#define TICKSPERSEC 10000000
/* UUID system time starts at October 15, 1582 */
#define SECS_15_OCT_1582_TO_1601 ((17 + 30 + 31 + 365 * 18 + 5) * SECSPERDAY)
#define TICKS_15_OCT_1582_TO_1601 ((ULONGLONG)SECS_15_OCT_1582_TO_1601 * TICKSPERSEC)
#if defined (ALLOC_PRAGMA)
#pragma alloc_text(INIT, ExpInitUuids)
#endif
/* GLOBALS ****************************************************************/
static FAST_MUTEX UuidMutex;
static ULARGE_INTEGER UuidLastTime;
static ULONG UuidSequence;
static BOOLEAN UuidSequenceInitialized = FALSE;
static BOOLEAN UuidSequenceChanged = FALSE;
static UCHAR UuidSeed[SEED_BUFFER_SIZE];
static ULONG UuidCount;
static LARGE_INTEGER LuidIncrement;
static LARGE_INTEGER LuidValue;
/* FUNCTIONS ****************************************************************/
VOID
INIT_FUNCTION
NTAPI
ExpInitUuids(VOID)
{
ExInitializeFastMutex(&UuidMutex);
KeQuerySystemTime((PLARGE_INTEGER)&UuidLastTime);
UuidLastTime.QuadPart += TICKS_15_OCT_1582_TO_1601;
UuidCount = TICKS_PER_CLOCK_TICK;
RtlZeroMemory(UuidSeed, SEED_BUFFER_SIZE);
}
#define VALUE_BUFFER_SIZE 256
static NTSTATUS
ExpLoadUuidSequence(PULONG Sequence)
{
UCHAR ValueBuffer[VALUE_BUFFER_SIZE];
PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING Name;
HANDLE KeyHandle;
ULONG ValueLength;
NTSTATUS Status;
RtlInitUnicodeString(&Name,
L"\\Registry\\Machine\\Software\\Microsoft\\Rpc");
InitializeObjectAttributes(&ObjectAttributes,
&Name,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = ZwOpenKey(&KeyHandle,
KEY_QUERY_VALUE,
&ObjectAttributes);
if (!NT_SUCCESS(Status))
{
DPRINT("ZwOpenKey() failed (Status %lx)\n", Status);
return Status;
}
RtlInitUnicodeString(&Name, L"UuidSequenceNumber");
ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer;
Status = ZwQueryValueKey(KeyHandle,
&Name,
KeyValuePartialInformation,
ValueBuffer,
VALUE_BUFFER_SIZE,
&ValueLength);
ZwClose(KeyHandle);
if (!NT_SUCCESS(Status))
{
DPRINT("ZwQueryValueKey() failed (Status %lx)\n", Status);
return Status;
}
*Sequence = *((PULONG)ValueInfo->Data);
DPRINT("Loaded sequence %lx\n", *Sequence);
return STATUS_SUCCESS;
}
#undef VALUE_BUFFER_SIZE
static NTSTATUS
ExpSaveUuidSequence(PULONG Sequence)
{
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING Name;
HANDLE KeyHandle;
NTSTATUS Status;
RtlInitUnicodeString(&Name,
L"\\Registry\\Machine\\Software\\Microsoft\\Rpc");
InitializeObjectAttributes(&ObjectAttributes,
&Name,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = ZwOpenKey(&KeyHandle,
KEY_SET_VALUE,
&ObjectAttributes);
if (!NT_SUCCESS(Status))
{
DPRINT("ZwOpenKey() failed (Status %lx)\n", Status);
return Status;
}
RtlInitUnicodeString(&Name, L"UuidSequenceNumber");
Status = ZwSetValueKey(KeyHandle,
&Name,
0,
REG_DWORD,
Sequence,
sizeof(ULONG));
ZwClose(KeyHandle);
if (!NT_SUCCESS(Status))
{
DPRINT("ZwSetValueKey() failed (Status %lx)\n", Status);
}
return Status;
}
static VOID
ExpGetRandomUuidSequence(PULONG Sequence)
{
LARGE_INTEGER Counter;
LARGE_INTEGER Frequency;
ULONG Value;
Counter = KeQueryPerformanceCounter(&Frequency);
Value = Counter.u.LowPart ^ Counter.u.HighPart;
*Sequence = *Sequence ^ Value;
DPRINT("Sequence %lx\n", *Sequence);
}
static NTSTATUS
ExpCreateUuids(PULARGE_INTEGER Time,
PULONG Range,
PULONG Sequence)
{
/*
* Generate time element of the UUID. Account for going faster
* than our clock as well as the clock going backwards.
*/
while (1)
{
KeQuerySystemTime((PLARGE_INTEGER)Time);
Time->QuadPart += TICKS_15_OCT_1582_TO_1601;
if (Time->QuadPart > UuidLastTime.QuadPart)
{
UuidCount = 0;
break;
}
if (Time->QuadPart < UuidLastTime.QuadPart)
{
(*Sequence)++;
UuidSequenceChanged = TRUE;
UuidCount = 0;
break;
}
if (UuidCount < TICKS_PER_CLOCK_TICK)
{
UuidCount++;
break;
}
}
UuidLastTime.QuadPart = Time->QuadPart;
Time->QuadPart += UuidCount;
*Range = 10000; /* What does this mean? Ticks per millisecond?*/
return STATUS_SUCCESS;
}
VOID
INIT_FUNCTION
NTAPI
ExpInitLuid(VOID)
{
LUID DummyLuidValue = SYSTEM_LUID;
LuidValue.u.HighPart = DummyLuidValue.HighPart;
LuidValue.u.LowPart = DummyLuidValue.LowPart;
LuidIncrement.QuadPart = 1;
}
VOID
NTAPI
ExAllocateLocallyUniqueId(OUT LUID *LocallyUniqueId)
{
LARGE_INTEGER NewLuid, PrevLuid;
/* atomically increment the luid */
do
{
PrevLuid = LuidValue;
NewLuid = RtlLargeIntegerAdd(PrevLuid,
LuidIncrement);
} while(ExInterlockedCompareExchange64(&LuidValue.QuadPart,
&NewLuid.QuadPart,
&PrevLuid.QuadPart,
NULL) != PrevLuid.QuadPart);
LocallyUniqueId->LowPart = NewLuid.u.LowPart;
LocallyUniqueId->HighPart = NewLuid.u.HighPart;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
NtAllocateLocallyUniqueId(OUT LUID *LocallyUniqueId)
{
LUID NewLuid;
KPROCESSOR_MODE PreviousMode;
NTSTATUS Status;
PAGED_CODE();
/* Probe if user mode */
PreviousMode = ExGetPreviousMode();
if (PreviousMode != KernelMode)
{
_SEH2_TRY
{
ProbeForWrite(LocallyUniqueId,
sizeof(LUID),
sizeof(ULONG));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
/* Do the allocation */
ExAllocateLocallyUniqueId(&NewLuid);
Status = STATUS_SUCCESS;
/* Write back LUID to caller */
_SEH2_TRY
{
*LocallyUniqueId = NewLuid;
}
_SEH2_EXCEPT(ExSystemExceptionFilter())
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
return Status;
}
/*
* @unimplemented
*/
NTSTATUS
NTAPI
ExUuidCreate(OUT UUID *Uuid)
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @unimplemented
*/
NTSTATUS
NTAPI
NtAllocateUuids(OUT PULARGE_INTEGER Time,
OUT PULONG Range,
OUT PULONG Sequence,
OUT PUCHAR Seed)
{
ULARGE_INTEGER IntTime;
ULONG IntRange;
NTSTATUS Status;
PAGED_CODE();
ExAcquireFastMutex(&UuidMutex);
if (!UuidSequenceInitialized)
{
Status = ExpLoadUuidSequence(&UuidSequence);
if (NT_SUCCESS(Status))
{
UuidSequence++;
}
else
{
ExpGetRandomUuidSequence(&UuidSequence);
}
UuidSequenceInitialized = TRUE;
UuidSequenceChanged = TRUE;
}
Status = ExpCreateUuids(&IntTime,
&IntRange,
&UuidSequence);
if (!NT_SUCCESS(Status))
{
ExReleaseFastMutex(&UuidMutex);
return Status;
}
if (UuidSequenceChanged)
{
Status = ExpSaveUuidSequence(&UuidSequence);
if (NT_SUCCESS(Status))
UuidSequenceChanged = FALSE;
}
ExReleaseFastMutex(&UuidMutex);
Time->QuadPart = IntTime.QuadPart;
*Range = IntRange;
*Sequence = UuidSequence;
RtlCopyMemory(Seed,
UuidSeed,
SEED_BUFFER_SIZE);
return STATUS_SUCCESS;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
NtSetUuidSeed(IN PUCHAR Seed)
{
NTSTATUS Status;
BOOLEAN GotContext;
PACCESS_TOKEN Token;
SECURITY_SUBJECT_CONTEXT SubjectContext;
LUID CallerLuid, SystemLuid = SYSTEM_LUID;
PAGED_CODE();
/* Should only be done by umode */
ASSERT(KeGetPreviousMode() != KernelMode);
/* No context to release */
GotContext = FALSE;
_SEH2_TRY
{
/* Get our caller context and remember to release it */
SeCaptureSubjectContext(&SubjectContext);
GotContext = TRUE;
/* Get caller access token and its associated ID */
Token = SeQuerySubjectContextToken(&SubjectContext);
Status = SeQueryAuthenticationIdToken(Token, &CallerLuid);
if (!NT_SUCCESS(Status))
{
RtlRaiseStatus(Status);
}
/* This call is only allowed for SYSTEM */
if (!RtlEqualLuid(&CallerLuid, &SystemLuid))
{
RtlRaiseStatus(STATUS_ACCESS_DENIED);
}
/* Check for buffer validity and then copy it to our seed */
ProbeForRead(Seed, SEED_BUFFER_SIZE, 1);
RtlCopyMemory(UuidSeed, Seed, SEED_BUFFER_SIZE);
Status = STATUS_SUCCESS;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
/* Release context if required */
if (GotContext)
{
SeReleaseSubjectContext(&SubjectContext);
}
return Status;
}
/* EOF */