8c8270a3db9e996a45ddb5de75d252d2c21469af
[reactos.git] / ntoskrnl / ex / uuid.c
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 = TimeDiff.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 */