reactos/ntoskrnl/ex/uuid.c
2018-12-20 03:47:46 +01:00

475 lines
11 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;
KPROCESSOR_MODE PreviousMode;
PAGED_CODE();
/* Probe if user mode */
PreviousMode = ExGetPreviousMode();
if (PreviousMode != KernelMode)
{
_SEH2_TRY
{
ProbeForWrite(Time,
sizeof(ULARGE_INTEGER),
sizeof(ULONG));
ProbeForWrite(Range,
sizeof(ULONG),
sizeof(ULONG));
ProbeForWrite(Sequence,
sizeof(ULONG),
sizeof(ULONG));
ProbeForWrite(Seed,
SEED_BUFFER_SIZE,
sizeof(UCHAR));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
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);
/* Write back UUIDs to caller */
_SEH2_TRY
{
Time->QuadPart = IntTime.QuadPart;
*Range = IntRange;
*Sequence = UuidSequence;
RtlCopyMemory(Seed,
UuidSeed,
SEED_BUFFER_SIZE);
Status = STATUS_SUCCESS;
}
_SEH2_EXCEPT(ExSystemExceptionFilter())
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
return Status;
}
/*
* @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, sizeof(UCHAR));
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 */