xref: /reactos/ntoskrnl/ex/uuid.c (revision 3adf4508)
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  *                  Pierre Schweitzer
9  */
10 
11 /* INCLUDES *****************************************************************/
12 
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16 
17 #define SEED_BUFFER_SIZE 6
18 
19 /* Number of 100ns ticks per clock tick. To be safe, assume that the clock
20    resolution is at least 1000 * 100 * (1/1000000) = 1/10 of a second */
21 #define TICKS_PER_CLOCK_TICK 1000
22 #define SECSPERDAY  86400
23 #define TICKSPERSEC 10000000
24 
25 /* UUID system time starts at October 15, 1582 */
26 #define SECS_15_OCT_1582_TO_1601  ((17 + 30 + 31 + 365 * 18 + 5) * SECSPERDAY)
27 #define TICKS_15_OCT_1582_TO_1601 ((ULONGLONG)SECS_15_OCT_1582_TO_1601 * TICKSPERSEC)
28 
29 /* 10000 in 100-ns model = 0.1 microsecond */
30 #define TIME_FRAME 10000
31 
32 
33 /* GLOBALS ****************************************************************/
34 
35 FAST_MUTEX ExpUuidLock;
36 LARGE_INTEGER ExpUuidLastTimeAllocated;
37 ULONG ExpUuidSequenceNumber = 0;
38 BOOLEAN ExpUuidSequenceNumberValid;
39 BOOLEAN ExpUuidSequenceNumberNotSaved = FALSE;
40 UUID_CACHED_VALUES_STRUCT ExpUuidCachedValues = {0ULL, 0xFFFFFFFF, {{0, 0, {0x80, 0x6E, 0x6F, 0x6E, 0x69, 0x63}}}};
41 BOOLEAN ExpUuidCacheValid = FALSE;
42 ULONG ExpLuidIncrement = 1;
43 LARGE_INTEGER ExpLuid = {{0x3e9, 0x0}};
44 
45 /* FUNCTIONS ****************************************************************/
46 
47 /*
48  * @implemented
49  */
50 CODE_SEG("INIT")
51 BOOLEAN
52 NTAPI
ExpUuidInitialization(VOID)53 ExpUuidInitialization(VOID)
54 {
55     ExInitializeFastMutex(&ExpUuidLock);
56 
57     ExpUuidSequenceNumberValid = FALSE;
58     KeQuerySystemTime(&ExpUuidLastTimeAllocated);
59 
60     return TRUE;
61 }
62 
63 
64 /*
65  * @implemented
66  */
67 #define VALUE_BUFFER_SIZE 20
68 static NTSTATUS
ExpUuidLoadSequenceNumber(PULONG Sequence)69 ExpUuidLoadSequenceNumber(PULONG Sequence)
70 {
71     UCHAR ValueBuffer[VALUE_BUFFER_SIZE];
72     PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
73     OBJECT_ATTRIBUTES ObjectAttributes;
74     UNICODE_STRING KeyName, ValueName;
75     HANDLE KeyHandle;
76     ULONG ValueLength;
77     NTSTATUS Status;
78 
79     PAGED_CODE();
80 
81     RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\Software\\Microsoft\\Rpc");
82     RtlInitUnicodeString(&ValueName, L"UuidSequenceNumber");
83 
84     InitializeObjectAttributes(&ObjectAttributes,
85                                &KeyName,
86                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
87                                NULL,
88                                NULL);
89     Status = ZwOpenKey(&KeyHandle, GENERIC_READ, &ObjectAttributes);
90     if (!NT_SUCCESS(Status))
91     {
92         DPRINT("ZwOpenKey() failed (Status %lx)\n", Status);
93         return Status;
94     }
95 
96     ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer;
97     Status = ZwQueryValueKey(KeyHandle,
98                              &ValueName,
99                              KeyValuePartialInformation,
100                              ValueBuffer,
101                              VALUE_BUFFER_SIZE,
102                              &ValueLength);
103     ZwClose(KeyHandle);
104     if (!NT_SUCCESS(Status))
105     {
106         DPRINT("ZwQueryValueKey() failed (Status %lx)\n", Status);
107         return Status;
108     }
109 
110     if (ValueInfo->Type != REG_DWORD || ValueInfo->DataLength != sizeof(DWORD))
111     {
112         return STATUS_UNSUCCESSFUL;
113     }
114 
115     *Sequence = *((PULONG)ValueInfo->Data);
116 
117     DPRINT("Loaded sequence %lx\n", *Sequence);
118 
119     return STATUS_SUCCESS;
120 }
121 #undef VALUE_BUFFER_SIZE
122 
123 /*
124  * @implemented
125  */
126 static NTSTATUS
ExpUuidSaveSequenceNumber(PULONG Sequence)127 ExpUuidSaveSequenceNumber(PULONG Sequence)
128 {
129     OBJECT_ATTRIBUTES ObjectAttributes;
130     UNICODE_STRING KeyName, ValueName;
131     HANDLE KeyHandle;
132     NTSTATUS Status;
133 
134     PAGED_CODE();
135 
136     RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\Software\\Microsoft\\Rpc");
137     RtlInitUnicodeString(&ValueName, L"UuidSequenceNumber");
138 
139     InitializeObjectAttributes(&ObjectAttributes,
140                                &KeyName,
141                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
142                                NULL,
143                                NULL);
144     Status = ZwOpenKey(&KeyHandle,
145                        GENERIC_READ | GENERIC_WRITE,
146                        &ObjectAttributes);
147     if (!NT_SUCCESS(Status))
148     {
149         DPRINT("ZwOpenKey() failed (Status %lx)\n", Status);
150         return Status;
151     }
152 
153     Status = ZwSetValueKey(KeyHandle,
154                            &ValueName,
155                            0,
156                            REG_DWORD,
157                            Sequence,
158                            sizeof(ULONG));
159     ZwClose(KeyHandle);
160     if (!NT_SUCCESS(Status))
161     {
162         DPRINT("ZwSetValueKey() failed (Status %lx)\n", Status);
163     }
164 
165     return Status;
166 }
167 
168 /*
169  * @implemented
170  * Warning! This function must be called
171  * with ExpUuidLock held!
172  */
173 static VOID
ExpUuidSaveSequenceNumberIf(VOID)174 ExpUuidSaveSequenceNumberIf(VOID)
175 {
176     NTSTATUS Status;
177 
178     PAGED_CODE();
179 
180     /* Only save sequence if it has to */
181     if (ExpUuidSequenceNumberNotSaved == TRUE)
182     {
183         Status = ExpUuidSaveSequenceNumber(&ExpUuidSequenceNumber);
184         if (NT_SUCCESS(Status))
185         {
186             ExpUuidSequenceNumberNotSaved = FALSE;
187         }
188     }
189 }
190 
191 /*
192  * @implemented
193  * Warning! This function must be called
194  * with ExpUuidLock held!
195  */
196 static NTSTATUS
ExpAllocateUuids(PULARGE_INTEGER Time,PULONG Range,PULONG Sequence)197 ExpAllocateUuids(PULARGE_INTEGER Time,
198                  PULONG Range,
199                  PULONG Sequence)
200 {
201     NTSTATUS Status;
202     LARGE_INTEGER Counter, Frequency, CurrentTime, TimeDiff, ClockDiff;
203 
204     PAGED_CODE();
205 
206     /* Initialize sequence number */
207     if (!ExpUuidSequenceNumberValid)
208     {
209         /* Try to load sequence number */
210         Status = ExpUuidLoadSequenceNumber(&ExpUuidSequenceNumber);
211         if (NT_SUCCESS(Status))
212         {
213             ++ExpUuidSequenceNumber;
214         }
215         else
216         {
217             /* If we cannot, generate a "true" random */
218             Counter = KeQueryPerformanceCounter(&Frequency);
219             ExpUuidSequenceNumber ^= (ULONG_PTR)&Status ^ (ULONG_PTR)Sequence ^ Counter.LowPart ^ Counter.HighPart;
220         }
221 
222         /* It's valid and to be saved */
223         ExpUuidSequenceNumberValid = TRUE;
224         ExpUuidSequenceNumberNotSaved = TRUE;
225     }
226 
227     KeQuerySystemTime(&CurrentTime);
228     TimeDiff.QuadPart = CurrentTime.QuadPart - ExpUuidLastTimeAllocated.QuadPart;
229     /* If time went backwards, change sequence (see RFC example) */
230     if (TimeDiff.QuadPart < 0)
231     {
232         ++ExpUuidSequenceNumber;
233         TimeDiff.QuadPart = 2 * TIME_FRAME;
234 
235         /* It's to be saved */
236         ExpUuidSequenceNumberNotSaved = TRUE;
237         ExpUuidLastTimeAllocated.QuadPart = CurrentTime.QuadPart - 2 * TIME_FRAME;
238     }
239 
240     if (TimeDiff.QuadPart == 0)
241     {
242         return STATUS_RETRY;
243     }
244 
245     /* If time diff > 0.1ms, squash it to reduce it to keep our clock resolution */
246     if (TimeDiff.HighPart > 0 || TimeDiff.QuadPart > TICKS_PER_CLOCK_TICK * TIME_FRAME)
247     {
248         TimeDiff.QuadPart = TICKS_PER_CLOCK_TICK * TIME_FRAME;
249     }
250 
251     if (TimeDiff.HighPart < 0 || TimeDiff.QuadPart <= TIME_FRAME)
252     {
253         *Range = TimeDiff.QuadPart;
254         ClockDiff.QuadPart = 0LL;
255     }
256     else
257     {
258         *Range = TIME_FRAME;
259         ClockDiff.QuadPart = TimeDiff.QuadPart - TIME_FRAME;
260         --ClockDiff.HighPart;
261     }
262 
263     Time->QuadPart = CurrentTime.QuadPart - *Range - ClockDiff.QuadPart;
264     ExpUuidLastTimeAllocated.QuadPart = CurrentTime.QuadPart - ClockDiff.QuadPart;
265     *Sequence = ExpUuidSequenceNumber;
266 
267     return STATUS_SUCCESS;
268 }
269 
270 /*
271  * @implemented
272  * Warning! This function must be called
273  * with ExpUuidLock held!
274  */
275 static NTSTATUS
ExpUuidGetValues(PUUID_CACHED_VALUES_STRUCT CachedValues)276 ExpUuidGetValues(PUUID_CACHED_VALUES_STRUCT CachedValues)
277 {
278     NTSTATUS Status;
279     ULARGE_INTEGER Time;
280     ULONG Range;
281     ULONG Sequence;
282 
283     PAGED_CODE();
284 
285     /* Allocate UUIDs */
286     Status = ExpAllocateUuids(&Time, &Range, &Sequence);
287     if (Status == STATUS_RETRY)
288     {
289         return Status;
290     }
291 
292     if (!NT_SUCCESS(Status))
293     {
294         return STATUS_NO_MEMORY;
295     }
296 
297     /* We need at least one UUID */
298     ASSERT(Range != 0);
299 
300     /* Set up our internal cache
301      * See format_uuid_v1 in RFC4122 for magic values
302      */
303     CachedValues->ClockSeqLow = Sequence;
304     CachedValues->ClockSeqHiAndReserved = (Sequence & 0x3F00) >> 8;
305     CachedValues->ClockSeqHiAndReserved |= 0x80;
306     CachedValues->AllocatedCount = Range;
307 
308     /*
309      * Time is relative to UUID time
310      * And we set last time range for all the possibly
311      * returnable UUID
312      */
313     Time.QuadPart += TICKS_15_OCT_1582_TO_1601;
314     CachedValues->Time = Time.QuadPart + (Range - 1);
315 
316     return STATUS_SUCCESS;
317 }
318 
319 /*
320  * @implemented
321  */
322 CODE_SEG("INIT")
323 BOOLEAN
324 NTAPI
ExLuidInitialization(VOID)325 ExLuidInitialization(VOID)
326 {
327     return TRUE;
328 }
329 
330 /*
331  * @implemented
332  */
333 VOID
334 NTAPI
ExAllocateLocallyUniqueId(OUT LUID * LocallyUniqueId)335 ExAllocateLocallyUniqueId(OUT LUID *LocallyUniqueId)
336 {
337     /* Atomically increment the luid */
338     *(LONG64*)LocallyUniqueId = InterlockedExchangeAdd64(&ExpLuid.QuadPart,
339                                                          ExpLuidIncrement);
340 }
341 
342 
343 /*
344  * @implemented
345  */
346 NTSTATUS
347 NTAPI
NtAllocateLocallyUniqueId(OUT LUID * LocallyUniqueId)348 NtAllocateLocallyUniqueId(OUT LUID *LocallyUniqueId)
349 {
350     KPROCESSOR_MODE PreviousMode;
351     PAGED_CODE();
352 
353     /* Probe if user mode */
354     PreviousMode = ExGetPreviousMode();
355     if (PreviousMode != KernelMode)
356     {
357         _SEH2_TRY
358         {
359             ProbeForWrite(LocallyUniqueId,
360                           sizeof(LUID),
361                           sizeof(ULONG));
362         }
363         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
364         {
365             _SEH2_YIELD(return _SEH2_GetExceptionCode());
366         }
367         _SEH2_END;
368     }
369 
370     /* Do the allocation */
371     ExAllocateLocallyUniqueId(LocallyUniqueId);
372     return STATUS_SUCCESS;
373 }
374 
375 /*
376  * @implemented
377  */
378 NTSTATUS
379 NTAPI
ExUuidCreate(OUT UUID * Uuid)380 ExUuidCreate(OUT UUID *Uuid)
381 {
382     NTSTATUS Status;
383     LONG AllocatedCount;
384     LARGE_INTEGER Time;
385     BOOLEAN Valid;
386 
387     PAGED_CODE();
388 
389     Status = STATUS_SUCCESS;
390     /* Loop until we have an UUID to return */
391     while (TRUE)
392     {
393         /* Try to gather node values */
394         do
395         {
396             Time.QuadPart = ExpUuidCachedValues.Time;
397 
398             RtlCopyMemory(Uuid->Data4,
399                           ExpUuidCachedValues.GuidInit,
400                           sizeof(Uuid->Data4));
401 
402             Valid = ExpUuidCacheValid;
403             AllocatedCount = InterlockedDecrement(&ExpUuidCachedValues.AllocatedCount);
404         }
405         /* Loop till we can do it without being disturbed */
406         while (Time.QuadPart != ExpUuidCachedValues.Time);
407 
408         /* We have more than an allocated UUID left, that's OK to return! */
409         if (AllocatedCount >= 0)
410         {
411             break;
412         }
413 
414         /*
415          * Here, we're out of UUIDs, we need to allocate more
416          * We need to be alone to do it, so lock the mutex
417          */
418         ExAcquireFastMutex(&ExpUuidLock);
419         if (Time.QuadPart == ExpUuidCachedValues.Time)
420         {
421             /* If allocation fails, bail out! */
422             Status = ExpUuidGetValues(&ExpUuidCachedValues);
423             if (Status != STATUS_SUCCESS)
424             {
425                 ExReleaseFastMutex(&ExpUuidLock);
426                 return Status;
427             }
428 
429             /* Save our current sequence if changed */
430             ExpUuidSaveSequenceNumberIf();
431         }
432         ExReleaseFastMutex(&ExpUuidLock);
433     }
434 
435     /*
436      * Once here, we've got an UUID to return
437      * But, if our init wasn't sane, then, make
438      * sure it's only used locally
439      */
440     if (!Valid)
441     {
442         Status = RPC_NT_UUID_LOCAL_ONLY;
443     }
444 
445     /* Set our timestamp - see RFC4211 */
446     Time.QuadPart -= AllocatedCount;
447     Uuid->Data1 = Time.LowPart;
448     Uuid->Data2 = Time.HighPart;
449     /* We also set the bit for GUIDv1 */
450     Uuid->Data3 = ((Time.HighPart >> 16) & 0x0FFF) | 0x1000;
451 
452     return Status;
453 }
454 
455 /*
456  * @implemented
457  */
458 NTSTATUS
459 NTAPI
NtAllocateUuids(OUT PULARGE_INTEGER Time,OUT PULONG Range,OUT PULONG Sequence,OUT PUCHAR Seed)460 NtAllocateUuids(OUT PULARGE_INTEGER Time,
461                 OUT PULONG Range,
462                 OUT PULONG Sequence,
463                 OUT PUCHAR Seed)
464 {
465     ULARGE_INTEGER IntTime;
466     ULONG IntRange, IntSequence;
467     NTSTATUS Status;
468     KPROCESSOR_MODE PreviousMode;
469 
470     PAGED_CODE();
471 
472     /* Probe if user mode */
473     PreviousMode = ExGetPreviousMode();
474     if (PreviousMode != KernelMode)
475     {
476         _SEH2_TRY
477         {
478             ProbeForWrite(Time,
479                           sizeof(ULARGE_INTEGER),
480                           sizeof(ULONG));
481 
482             ProbeForWrite(Range,
483                           sizeof(ULONG),
484                           sizeof(ULONG));
485 
486             ProbeForWrite(Sequence,
487                           sizeof(ULONG),
488                           sizeof(ULONG));
489 
490             ProbeForWrite(Seed,
491                           SEED_BUFFER_SIZE,
492                           sizeof(UCHAR));
493         }
494         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
495         {
496             _SEH2_YIELD(return _SEH2_GetExceptionCode());
497         }
498         _SEH2_END;
499     }
500 
501     /* During allocation we must be alone */
502     ExAcquireFastMutex(&ExpUuidLock);
503 
504     Status = ExpAllocateUuids(&IntTime,
505                               &IntRange,
506                               &IntSequence);
507     if (!NT_SUCCESS(Status))
508     {
509         ExReleaseFastMutex(&ExpUuidLock);
510         return Status;
511     }
512 
513     /* If sequence number was changed, save it */
514     ExpUuidSaveSequenceNumberIf();
515 
516     /* Allocation done, so we can release */
517     ExReleaseFastMutex(&ExpUuidLock);
518 
519     /* Write back UUIDs to caller */
520     _SEH2_TRY
521     {
522         Time->QuadPart = IntTime.QuadPart;
523         *Range = IntRange;
524         *Sequence = IntSequence;
525 
526         RtlCopyMemory(Seed,
527                       &ExpUuidCachedValues.NodeId[0],
528                       SEED_BUFFER_SIZE);
529 
530         Status = STATUS_SUCCESS;
531     }
532     _SEH2_EXCEPT(ExSystemExceptionFilter())
533     {
534         Status = _SEH2_GetExceptionCode();
535     }
536     _SEH2_END;
537 
538     return Status;
539 }
540 
541 
542 /*
543  * @implemented
544  */
545 NTSTATUS
546 NTAPI
NtSetUuidSeed(IN PUCHAR Seed)547 NtSetUuidSeed(IN PUCHAR Seed)
548 {
549     NTSTATUS Status;
550     BOOLEAN GotContext;
551     PACCESS_TOKEN Token;
552     SECURITY_SUBJECT_CONTEXT SubjectContext;
553     LUID CallerLuid, SystemLuid = SYSTEM_LUID;
554 
555     PAGED_CODE();
556 
557     /* Should only be done by umode */
558     ASSERT(KeGetPreviousMode() != KernelMode);
559 
560     /* No context to release */
561     GotContext = FALSE;
562     _SEH2_TRY
563     {
564         /* Get our caller context and remember to release it */
565         SeCaptureSubjectContext(&SubjectContext);
566         GotContext = TRUE;
567 
568         /* Get caller access token and its associated ID */
569         Token = SeQuerySubjectContextToken(&SubjectContext);
570         Status = SeQueryAuthenticationIdToken(Token, &CallerLuid);
571         if (!NT_SUCCESS(Status))
572         {
573             RtlRaiseStatus(Status);
574         }
575 
576         /* This call is only allowed for SYSTEM */
577         if (!RtlEqualLuid(&CallerLuid, &SystemLuid))
578         {
579             RtlRaiseStatus(STATUS_ACCESS_DENIED);
580         }
581 
582         /* Check for buffer validity and then copy it to our seed */
583         ProbeForRead(Seed, SEED_BUFFER_SIZE, sizeof(UCHAR));
584         RtlCopyMemory(&ExpUuidCachedValues.NodeId[0], Seed, SEED_BUFFER_SIZE);
585 
586         /*
587          * According to RFC 4122, UUID seed is based on MAC addresses
588          * If it is randomly set, then, it must have its multicast be set
589          * to be valid and avoid collisions
590          * Reflect it here
591          */
592         ExpUuidCacheValid = ~(*Seed >> 7) & 1;
593 
594         Status = STATUS_SUCCESS;
595     }
596     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
597     {
598         Status = _SEH2_GetExceptionCode();
599     }
600     _SEH2_END;
601 
602     /* Release context if required */
603     if (GotContext)
604     {
605         SeReleaseSubjectContext(&SubjectContext);
606     }
607 
608     return Status;
609 }
610 
611 /* EOF */
612