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 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 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 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 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 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 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 325 ExLuidInitialization(VOID) 326 { 327 return TRUE; 328 } 329 330 /* 331 * @implemented 332 */ 333 VOID 334 NTAPI 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 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 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 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