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