xref: /reactos/ntoskrnl/ex/uuid.c (revision 01e5cb0c)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS kernel
4  * FILE:            ntoskrnl/ex/uuid.c
5  * PURPOSE:         UUID generator
6  * PROGRAMMERS:     Eric Kohl
7  *                  Thomas Weidenmueller
8  */
9 
10 /* INCLUDES *****************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 #define SEED_BUFFER_SIZE 6
17 
18 /* Number of 100ns ticks per clock tick. To be safe, assume that the clock
19    resolution is at least 1000 * 100 * (1/1000000) = 1/10 of a second */
20 #define TICKS_PER_CLOCK_TICK 1000
21 #define SECSPERDAY  86400
22 #define TICKSPERSEC 10000000
23 
24 /* UUID system time starts at October 15, 1582 */
25 #define SECS_15_OCT_1582_TO_1601  ((17 + 30 + 31 + 365 * 18 + 5) * SECSPERDAY)
26 #define TICKS_15_OCT_1582_TO_1601 ((ULONGLONG)SECS_15_OCT_1582_TO_1601 * TICKSPERSEC)
27 
28 #if defined (ALLOC_PRAGMA)
29 #pragma alloc_text(INIT, ExpInitUuids)
30 #endif
31 
32 
33 /* GLOBALS ****************************************************************/
34 
35 static FAST_MUTEX UuidMutex;
36 static ULARGE_INTEGER UuidLastTime;
37 static ULONG UuidSequence;
38 static BOOLEAN UuidSequenceInitialized = FALSE;
39 static BOOLEAN UuidSequenceChanged = FALSE;
40 static UCHAR UuidSeed[SEED_BUFFER_SIZE];
41 static ULONG UuidCount;
42 static LARGE_INTEGER LuidIncrement;
43 static LARGE_INTEGER LuidValue;
44 
45 /* FUNCTIONS ****************************************************************/
46 
47 VOID
48 INIT_FUNCTION
49 NTAPI
50 ExpInitUuids(VOID)
51 {
52     ExInitializeFastMutex(&UuidMutex);
53 
54     KeQuerySystemTime((PLARGE_INTEGER)&UuidLastTime);
55     UuidLastTime.QuadPart += TICKS_15_OCT_1582_TO_1601;
56 
57     UuidCount = TICKS_PER_CLOCK_TICK;
58     RtlZeroMemory(UuidSeed, SEED_BUFFER_SIZE);
59 }
60 
61 
62 #define VALUE_BUFFER_SIZE 256
63 
64 static NTSTATUS
65 ExpLoadUuidSequence(PULONG Sequence)
66 {
67     UCHAR ValueBuffer[VALUE_BUFFER_SIZE];
68     PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
69     OBJECT_ATTRIBUTES ObjectAttributes;
70     UNICODE_STRING Name;
71     HANDLE KeyHandle;
72     ULONG ValueLength;
73     NTSTATUS Status;
74 
75     RtlInitUnicodeString(&Name,
76         L"\\Registry\\Machine\\Software\\Microsoft\\Rpc");
77     InitializeObjectAttributes(&ObjectAttributes,
78                                &Name,
79                                OBJ_CASE_INSENSITIVE,
80                                NULL,
81                                NULL);
82     Status = ZwOpenKey(&KeyHandle,
83                        KEY_QUERY_VALUE,
84                        &ObjectAttributes);
85     if (!NT_SUCCESS(Status))
86     {
87         DPRINT("ZwOpenKey() failed (Status %lx)\n", Status);
88         return Status;
89     }
90 
91     RtlInitUnicodeString(&Name, L"UuidSequenceNumber");
92 
93     ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer;
94     Status = ZwQueryValueKey(KeyHandle,
95                              &Name,
96                              KeyValuePartialInformation,
97                              ValueBuffer,
98                              VALUE_BUFFER_SIZE,
99                              &ValueLength);
100     ZwClose(KeyHandle);
101     if (!NT_SUCCESS(Status))
102     {
103         DPRINT("ZwQueryValueKey() failed (Status %lx)\n", Status);
104         return Status;
105     }
106 
107     *Sequence = *((PULONG)ValueInfo->Data);
108 
109     DPRINT("Loaded sequence %lx\n", *Sequence);
110 
111     return STATUS_SUCCESS;
112 }
113 #undef VALUE_BUFFER_SIZE
114 
115 
116 static NTSTATUS
117 ExpSaveUuidSequence(PULONG Sequence)
118 {
119     OBJECT_ATTRIBUTES ObjectAttributes;
120     UNICODE_STRING Name;
121     HANDLE KeyHandle;
122     NTSTATUS Status;
123 
124     RtlInitUnicodeString(&Name,
125         L"\\Registry\\Machine\\Software\\Microsoft\\Rpc");
126     InitializeObjectAttributes(&ObjectAttributes,
127                                &Name,
128                                OBJ_CASE_INSENSITIVE,
129                                NULL,
130                                NULL);
131     Status = ZwOpenKey(&KeyHandle,
132                        KEY_SET_VALUE,
133                        &ObjectAttributes);
134     if (!NT_SUCCESS(Status))
135     {
136         DPRINT("ZwOpenKey() failed (Status %lx)\n", Status);
137         return Status;
138     }
139 
140     RtlInitUnicodeString(&Name, L"UuidSequenceNumber");
141     Status = ZwSetValueKey(KeyHandle,
142                            &Name,
143                            0,
144                            REG_DWORD,
145                            Sequence,
146                            sizeof(ULONG));
147     ZwClose(KeyHandle);
148     if (!NT_SUCCESS(Status))
149     {
150         DPRINT("ZwSetValueKey() failed (Status %lx)\n", Status);
151     }
152 
153     return Status;
154 }
155 
156 
157 static VOID
158 ExpGetRandomUuidSequence(PULONG Sequence)
159 {
160     LARGE_INTEGER Counter;
161     LARGE_INTEGER Frequency;
162     ULONG Value;
163 
164     Counter = KeQueryPerformanceCounter(&Frequency);
165     Value = Counter.u.LowPart ^ Counter.u.HighPart;
166 
167     *Sequence = *Sequence ^ Value;
168 
169     DPRINT("Sequence %lx\n", *Sequence);
170 }
171 
172 
173 static NTSTATUS
174 ExpCreateUuids(PULARGE_INTEGER Time,
175                PULONG Range,
176                PULONG Sequence)
177 {
178     /*
179     * Generate time element of the UUID. Account for going faster
180     * than our clock as well as the clock going backwards.
181     */
182     while (1)
183     {
184         KeQuerySystemTime((PLARGE_INTEGER)Time);
185         Time->QuadPart += TICKS_15_OCT_1582_TO_1601;
186 
187         if (Time->QuadPart > UuidLastTime.QuadPart)
188         {
189             UuidCount = 0;
190             break;
191         }
192 
193         if (Time->QuadPart < UuidLastTime.QuadPart)
194         {
195             (*Sequence)++;
196             UuidSequenceChanged = TRUE;
197             UuidCount = 0;
198             break;
199         }
200 
201         if (UuidCount < TICKS_PER_CLOCK_TICK)
202         {
203             UuidCount++;
204             break;
205         }
206     }
207 
208     UuidLastTime.QuadPart = Time->QuadPart;
209     Time->QuadPart += UuidCount;
210 
211     *Range = 10000; /* What does this mean? Ticks per millisecond?*/
212 
213     return STATUS_SUCCESS;
214 }
215 
216 VOID
217 INIT_FUNCTION
218 NTAPI
219 ExpInitLuid(VOID)
220 {
221     LUID DummyLuidValue = SYSTEM_LUID;
222 
223     LuidValue.u.HighPart = DummyLuidValue.HighPart;
224     LuidValue.u.LowPart = DummyLuidValue.LowPart;
225     LuidIncrement.QuadPart = 1;
226 }
227 
228 
229 VOID
230 NTAPI
231 ExAllocateLocallyUniqueId(OUT LUID *LocallyUniqueId)
232 {
233     LARGE_INTEGER NewLuid, PrevLuid;
234 
235     /* atomically increment the luid */
236     do
237     {
238         PrevLuid = LuidValue;
239         NewLuid = RtlLargeIntegerAdd(PrevLuid,
240                                      LuidIncrement);
241     } while(ExInterlockedCompareExchange64(&LuidValue.QuadPart,
242                                            &NewLuid.QuadPart,
243                                            &PrevLuid.QuadPart,
244                                            NULL) != PrevLuid.QuadPart);
245 
246     LocallyUniqueId->LowPart = NewLuid.u.LowPart;
247     LocallyUniqueId->HighPart = NewLuid.u.HighPart;
248 }
249 
250 
251 /*
252  * @implemented
253  */
254 NTSTATUS
255 NTAPI
256 NtAllocateLocallyUniqueId(OUT LUID *LocallyUniqueId)
257 {
258     LUID NewLuid;
259     KPROCESSOR_MODE PreviousMode;
260     NTSTATUS Status;
261     PAGED_CODE();
262 
263     /* Probe if user mode */
264     PreviousMode = ExGetPreviousMode();
265     if (PreviousMode != KernelMode)
266     {
267         _SEH2_TRY
268         {
269             ProbeForWrite(LocallyUniqueId,
270                           sizeof(LUID),
271                           sizeof(ULONG));
272         }
273         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
274         {
275             _SEH2_YIELD(return _SEH2_GetExceptionCode());
276         }
277         _SEH2_END;
278     }
279 
280     /* Do the allocation */
281     ExAllocateLocallyUniqueId(&NewLuid);
282     Status = STATUS_SUCCESS;
283 
284     /* Write back LUID to caller */
285     _SEH2_TRY
286     {
287         *LocallyUniqueId = NewLuid;
288     }
289     _SEH2_EXCEPT(ExSystemExceptionFilter())
290     {
291         Status = _SEH2_GetExceptionCode();
292     }
293     _SEH2_END;
294     return Status;
295 }
296 
297 /*
298  * @unimplemented
299  */
300 NTSTATUS
301 NTAPI
302 ExUuidCreate(OUT UUID *Uuid)
303 {
304     UNIMPLEMENTED;
305     return FALSE;
306 }
307 
308 /*
309  * @unimplemented
310  */
311 NTSTATUS
312 NTAPI
313 NtAllocateUuids(OUT PULARGE_INTEGER Time,
314                 OUT PULONG Range,
315                 OUT PULONG Sequence,
316                 OUT PUCHAR Seed)
317 {
318     ULARGE_INTEGER IntTime;
319     ULONG IntRange;
320     NTSTATUS Status;
321     KPROCESSOR_MODE PreviousMode;
322 
323     PAGED_CODE();
324 
325     /* Probe if user mode */
326     PreviousMode = ExGetPreviousMode();
327     if (PreviousMode != KernelMode)
328     {
329         _SEH2_TRY
330         {
331             ProbeForWrite(Time,
332                           sizeof(ULARGE_INTEGER),
333                           sizeof(ULONG));
334 
335             ProbeForWrite(Range,
336                           sizeof(ULONG),
337                           sizeof(ULONG));
338 
339             ProbeForWrite(Sequence,
340                           sizeof(ULONG),
341                           sizeof(ULONG));
342 
343             ProbeForWrite(Seed,
344                           SEED_BUFFER_SIZE,
345                           sizeof(UCHAR));
346         }
347         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
348         {
349             _SEH2_YIELD(return _SEH2_GetExceptionCode());
350         }
351         _SEH2_END;
352     }
353 
354     ExAcquireFastMutex(&UuidMutex);
355 
356     if (!UuidSequenceInitialized)
357     {
358         Status = ExpLoadUuidSequence(&UuidSequence);
359         if (NT_SUCCESS(Status))
360         {
361             UuidSequence++;
362         }
363         else
364         {
365             ExpGetRandomUuidSequence(&UuidSequence);
366         }
367 
368         UuidSequenceInitialized = TRUE;
369         UuidSequenceChanged = TRUE;
370     }
371 
372     Status = ExpCreateUuids(&IntTime,
373                             &IntRange,
374                             &UuidSequence);
375     if (!NT_SUCCESS(Status))
376     {
377         ExReleaseFastMutex(&UuidMutex);
378         return Status;
379     }
380 
381     if (UuidSequenceChanged)
382     {
383         Status = ExpSaveUuidSequence(&UuidSequence);
384         if (NT_SUCCESS(Status))
385             UuidSequenceChanged = FALSE;
386     }
387 
388     ExReleaseFastMutex(&UuidMutex);
389 
390     /* Write back UUIDs to caller */
391     _SEH2_TRY
392     {
393         Time->QuadPart = IntTime.QuadPart;
394         *Range = IntRange;
395         *Sequence = UuidSequence;
396 
397         RtlCopyMemory(Seed,
398                       UuidSeed,
399                       SEED_BUFFER_SIZE);
400 
401         Status = STATUS_SUCCESS;
402     }
403     _SEH2_EXCEPT(ExSystemExceptionFilter())
404     {
405         Status = _SEH2_GetExceptionCode();
406     }
407     _SEH2_END;
408 
409     return Status;
410 }
411 
412 
413 /*
414  * @implemented
415  */
416 NTSTATUS
417 NTAPI
418 NtSetUuidSeed(IN PUCHAR Seed)
419 {
420     NTSTATUS Status;
421     BOOLEAN GotContext;
422     PACCESS_TOKEN Token;
423     SECURITY_SUBJECT_CONTEXT SubjectContext;
424     LUID CallerLuid, SystemLuid = SYSTEM_LUID;
425 
426     PAGED_CODE();
427 
428     /* Should only be done by umode */
429     ASSERT(KeGetPreviousMode() != KernelMode);
430 
431     /* No context to release */
432     GotContext = FALSE;
433     _SEH2_TRY
434     {
435         /* Get our caller context and remember to release it */
436         SeCaptureSubjectContext(&SubjectContext);
437         GotContext = TRUE;
438 
439         /* Get caller access token and its associated ID */
440         Token = SeQuerySubjectContextToken(&SubjectContext);
441         Status = SeQueryAuthenticationIdToken(Token, &CallerLuid);
442         if (!NT_SUCCESS(Status))
443         {
444             RtlRaiseStatus(Status);
445         }
446 
447         /* This call is only allowed for SYSTEM */
448         if (!RtlEqualLuid(&CallerLuid, &SystemLuid))
449         {
450             RtlRaiseStatus(STATUS_ACCESS_DENIED);
451         }
452 
453         /* Check for buffer validity and then copy it to our seed */
454         ProbeForRead(Seed, SEED_BUFFER_SIZE, sizeof(UCHAR));
455         RtlCopyMemory(UuidSeed, Seed, SEED_BUFFER_SIZE);
456 
457         Status = STATUS_SUCCESS;
458     }
459     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
460     {
461         Status = _SEH2_GetExceptionCode();
462     }
463     _SEH2_END;
464 
465     /* Release context if required */
466     if (GotContext)
467     {
468         SeReleaseSubjectContext(&SubjectContext);
469     }
470 
471     return Status;
472 }
473 
474 /* EOF */
475