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