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