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 /* GLOBALS ****************************************************************/ 33 34 FAST_MUTEX ExpUuidLock; 35 LARGE_INTEGER ExpUuidLastTimeAllocated; 36 ULONG ExpUuidSequenceNumber = 0; 37 BOOLEAN ExpUuidSequenceNumberValid; 38 BOOLEAN ExpUuidSequenceNumberNotSaved = FALSE; 39 UUID_CACHED_VALUES_STRUCT ExpUuidCachedValues = {0ULL, 0xFFFFFFFF, {{0, 0, {0x80, 0x6E, 0x6F, 0x6E, 0x69, 0x63}}}}; 40 BOOLEAN ExpUuidCacheValid = FALSE; 41 ULONG ExpLuidIncrement = 1; 42 LARGE_INTEGER ExpLuid = {{0x3e9, 0x0}}; 43 44 /* FUNCTIONS ****************************************************************/ 45 46 /* 47 * @implemented 48 */ 49 CODE_SEG("INIT") 50 BOOLEAN 51 NTAPI 52 ExpUuidInitialization(VOID) 53 { 54 ExInitializeFastMutex(&ExpUuidLock); 55 56 ExpUuidSequenceNumberValid = FALSE; 57 KeQuerySystemTime(&ExpUuidLastTimeAllocated); 58 59 return TRUE; 60 } 61 62 63 /* 64 * @implemented 65 */ 66 #define VALUE_BUFFER_SIZE 20 67 static NTSTATUS 68 ExpUuidLoadSequenceNumber(PULONG Sequence) 69 { 70 UCHAR ValueBuffer[VALUE_BUFFER_SIZE]; 71 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo; 72 OBJECT_ATTRIBUTES ObjectAttributes; 73 UNICODE_STRING KeyName, ValueName; 74 HANDLE KeyHandle; 75 ULONG ValueLength; 76 NTSTATUS Status; 77 78 PAGED_CODE(); 79 80 RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\Software\\Microsoft\\Rpc"); 81 RtlInitUnicodeString(&ValueName, L"UuidSequenceNumber"); 82 83 InitializeObjectAttributes(&ObjectAttributes, 84 &KeyName, 85 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 86 NULL, 87 NULL); 88 Status = ZwOpenKey(&KeyHandle, GENERIC_READ, &ObjectAttributes); 89 if (!NT_SUCCESS(Status)) 90 { 91 DPRINT("ZwOpenKey() failed (Status %lx)\n", Status); 92 return Status; 93 } 94 95 ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer; 96 Status = ZwQueryValueKey(KeyHandle, 97 &ValueName, 98 KeyValuePartialInformation, 99 ValueBuffer, 100 VALUE_BUFFER_SIZE, 101 &ValueLength); 102 ZwClose(KeyHandle); 103 if (!NT_SUCCESS(Status)) 104 { 105 DPRINT("ZwQueryValueKey() failed (Status %lx)\n", Status); 106 return Status; 107 } 108 109 if (ValueInfo->Type != REG_DWORD || ValueInfo->DataLength != sizeof(DWORD)) 110 { 111 return STATUS_UNSUCCESSFUL; 112 } 113 114 *Sequence = *((PULONG)ValueInfo->Data); 115 116 DPRINT("Loaded sequence %lx\n", *Sequence); 117 118 return STATUS_SUCCESS; 119 } 120 #undef VALUE_BUFFER_SIZE 121 122 /* 123 * @implemented 124 */ 125 static NTSTATUS 126 ExpUuidSaveSequenceNumber(PULONG Sequence) 127 { 128 OBJECT_ATTRIBUTES ObjectAttributes; 129 UNICODE_STRING KeyName, ValueName; 130 HANDLE KeyHandle; 131 NTSTATUS Status; 132 133 PAGED_CODE(); 134 135 RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\Software\\Microsoft\\Rpc"); 136 RtlInitUnicodeString(&ValueName, L"UuidSequenceNumber"); 137 138 InitializeObjectAttributes(&ObjectAttributes, 139 &KeyName, 140 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 141 NULL, 142 NULL); 143 Status = ZwOpenKey(&KeyHandle, 144 GENERIC_READ | GENERIC_WRITE, 145 &ObjectAttributes); 146 if (!NT_SUCCESS(Status)) 147 { 148 DPRINT("ZwOpenKey() failed (Status %lx)\n", Status); 149 return Status; 150 } 151 152 Status = ZwSetValueKey(KeyHandle, 153 &ValueName, 154 0, 155 REG_DWORD, 156 Sequence, 157 sizeof(ULONG)); 158 ZwClose(KeyHandle); 159 if (!NT_SUCCESS(Status)) 160 { 161 DPRINT("ZwSetValueKey() failed (Status %lx)\n", Status); 162 } 163 164 return Status; 165 } 166 167 /* 168 * @implemented 169 * Warning! This function must be called 170 * with ExpUuidLock held! 171 */ 172 static VOID 173 ExpUuidSaveSequenceNumberIf(VOID) 174 { 175 NTSTATUS Status; 176 177 PAGED_CODE(); 178 179 /* Only save sequence if it has to */ 180 if (ExpUuidSequenceNumberNotSaved == TRUE) 181 { 182 Status = ExpUuidSaveSequenceNumber(&ExpUuidSequenceNumber); 183 if (NT_SUCCESS(Status)) 184 { 185 ExpUuidSequenceNumberNotSaved = FALSE; 186 } 187 } 188 } 189 190 /* 191 * @implemented 192 * Warning! This function must be called 193 * with ExpUuidLock held! 194 */ 195 static NTSTATUS 196 ExpAllocateUuids(PULARGE_INTEGER Time, 197 PULONG Range, 198 PULONG Sequence) 199 { 200 NTSTATUS Status; 201 LARGE_INTEGER Counter, Frequency, CurrentTime, TimeDiff, ClockDiff; 202 203 PAGED_CODE(); 204 205 /* Initialize sequence number */ 206 if (!ExpUuidSequenceNumberValid) 207 { 208 /* Try to load sequence number */ 209 Status = ExpUuidLoadSequenceNumber(&ExpUuidSequenceNumber); 210 if (NT_SUCCESS(Status)) 211 { 212 ++ExpUuidSequenceNumber; 213 } 214 else 215 { 216 /* If we cannot, generate a "true" random */ 217 Counter = KeQueryPerformanceCounter(&Frequency); 218 ExpUuidSequenceNumber ^= (ULONG_PTR)&Status ^ (ULONG_PTR)Sequence ^ Counter.LowPart ^ Counter.HighPart; 219 } 220 221 /* It's valid and to be saved */ 222 ExpUuidSequenceNumberValid = TRUE; 223 ExpUuidSequenceNumberNotSaved = TRUE; 224 } 225 226 KeQuerySystemTime(&CurrentTime); 227 TimeDiff.QuadPart = CurrentTime.QuadPart - ExpUuidLastTimeAllocated.QuadPart; 228 /* If time went backwards, change sequence (see RFC example) */ 229 if (TimeDiff.QuadPart < 0) 230 { 231 ++ExpUuidSequenceNumber; 232 TimeDiff.QuadPart = 2 * TIME_FRAME; 233 234 /* It's to be saved */ 235 ExpUuidSequenceNumberNotSaved = TRUE; 236 ExpUuidLastTimeAllocated.QuadPart = CurrentTime.QuadPart - 2 * TIME_FRAME; 237 } 238 239 if (TimeDiff.QuadPart == 0) 240 { 241 return STATUS_RETRY; 242 } 243 244 /* If time diff > 0.1ms, squash it to reduce it to keep our clock resolution */ 245 if (TimeDiff.HighPart > 0 || TimeDiff.QuadPart > TICKS_PER_CLOCK_TICK * TIME_FRAME) 246 { 247 TimeDiff.QuadPart = TICKS_PER_CLOCK_TICK * TIME_FRAME; 248 } 249 250 if (TimeDiff.HighPart < 0 || TimeDiff.QuadPart <= TIME_FRAME) 251 { 252 *Range = TimeDiff.QuadPart; 253 ClockDiff.QuadPart = 0LL; 254 } 255 else 256 { 257 *Range = TIME_FRAME; 258 ClockDiff.QuadPart = TimeDiff.QuadPart - TIME_FRAME; 259 --ClockDiff.HighPart; 260 } 261 262 Time->QuadPart = CurrentTime.QuadPart - *Range - ClockDiff.QuadPart; 263 ExpUuidLastTimeAllocated.QuadPart = CurrentTime.QuadPart - ClockDiff.QuadPart; 264 *Sequence = ExpUuidSequenceNumber; 265 266 return STATUS_SUCCESS; 267 } 268 269 /* 270 * @implemented 271 * Warning! This function must be called 272 * with ExpUuidLock held! 273 */ 274 static NTSTATUS 275 ExpUuidGetValues(PUUID_CACHED_VALUES_STRUCT CachedValues) 276 { 277 NTSTATUS Status; 278 ULARGE_INTEGER Time; 279 ULONG Range; 280 ULONG Sequence; 281 282 PAGED_CODE(); 283 284 /* Allocate UUIDs */ 285 Status = ExpAllocateUuids(&Time, &Range, &Sequence); 286 if (Status == STATUS_RETRY) 287 { 288 return Status; 289 } 290 291 if (!NT_SUCCESS(Status)) 292 { 293 return STATUS_NO_MEMORY; 294 } 295 296 /* We need at least one UUID */ 297 ASSERT(Range != 0); 298 299 /* Set up our internal cache 300 * See format_uuid_v1 in RFC4122 for magic values 301 */ 302 CachedValues->ClockSeqLow = Sequence; 303 CachedValues->ClockSeqHiAndReserved = (Sequence & 0x3F00) >> 8; 304 CachedValues->ClockSeqHiAndReserved |= 0x80; 305 CachedValues->AllocatedCount = Range; 306 307 /* 308 * Time is relative to UUID time 309 * And we set last time range for all the possibly 310 * returnable UUID 311 */ 312 Time.QuadPart += TICKS_15_OCT_1582_TO_1601; 313 CachedValues->Time = Time.QuadPart + (Range - 1); 314 315 return STATUS_SUCCESS; 316 } 317 318 /* 319 * @implemented 320 */ 321 CODE_SEG("INIT") 322 BOOLEAN 323 NTAPI 324 ExLuidInitialization(VOID) 325 { 326 return TRUE; 327 } 328 329 /* 330 * @implemented 331 */ 332 VOID 333 NTAPI 334 ExAllocateLocallyUniqueId(OUT LUID *LocallyUniqueId) 335 { 336 /* Atomically increment the luid */ 337 *(LONG64*)LocallyUniqueId = InterlockedExchangeAdd64(&ExpLuid.QuadPart, 338 ExpLuidIncrement); 339 } 340 341 342 /* 343 * @implemented 344 */ 345 NTSTATUS 346 NTAPI 347 NtAllocateLocallyUniqueId(OUT LUID *LocallyUniqueId) 348 { 349 KPROCESSOR_MODE PreviousMode; 350 PAGED_CODE(); 351 352 /* Probe if user mode */ 353 PreviousMode = ExGetPreviousMode(); 354 if (PreviousMode != KernelMode) 355 { 356 _SEH2_TRY 357 { 358 ProbeForWrite(LocallyUniqueId, 359 sizeof(LUID), 360 sizeof(ULONG)); 361 } 362 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 363 { 364 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 365 } 366 _SEH2_END; 367 } 368 369 /* Do the allocation */ 370 ExAllocateLocallyUniqueId(LocallyUniqueId); 371 return STATUS_SUCCESS; 372 } 373 374 /* 375 * @implemented 376 */ 377 NTSTATUS 378 NTAPI 379 ExUuidCreate(OUT UUID *Uuid) 380 { 381 NTSTATUS Status; 382 LONG AllocatedCount; 383 LARGE_INTEGER Time; 384 BOOLEAN Valid; 385 386 PAGED_CODE(); 387 388 Status = STATUS_SUCCESS; 389 /* Loop until we have an UUID to return */ 390 while (TRUE) 391 { 392 /* Try to gather node values */ 393 do 394 { 395 Time.QuadPart = ExpUuidCachedValues.Time; 396 397 C_ASSERT(sizeof(ExpUuidCachedValues.GuidInit) == sizeof(Uuid->Data4)); 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 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 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