1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/ob/obname.c 5 * PURPOSE: Manages all functions related to the Object Manager name- 6 * space, such as finding objects or querying their names. 7 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 8 * Eric Kohl 9 * Thomas Weidenmueller (w3seek@reactos.org) 10 */ 11 12 /* INCLUDES ******************************************************************/ 13 14 #include <ntoskrnl.h> 15 #define NDEBUG 16 #include <debug.h> 17 18 BOOLEAN ObpCaseInsensitive = TRUE; 19 POBJECT_DIRECTORY ObpRootDirectoryObject; 20 POBJECT_DIRECTORY ObpTypeDirectoryObject; 21 22 /* DOS Device Prefix \??\ and \?? */ 23 ALIGNEDNAME ObpDosDevicesShortNamePrefix = {{L'\\',L'?',L'?',L'\\'}}; 24 ALIGNEDNAME ObpDosDevicesShortNameRoot = {{L'\\',L'?',L'?',L'\0'}}; 25 UNICODE_STRING ObpDosDevicesShortName = 26 { 27 sizeof(ObpDosDevicesShortNamePrefix), 28 sizeof(ObpDosDevicesShortNamePrefix), 29 (PWSTR)&ObpDosDevicesShortNamePrefix 30 }; 31 32 WCHAR ObpUnsecureGlobalNamesBuffer[128] = {0}; 33 ULONG ObpUnsecureGlobalNamesLength = sizeof(ObpUnsecureGlobalNamesBuffer); 34 35 /* PRIVATE FUNCTIONS *********************************************************/ 36 37 INIT_FUNCTION 38 NTSTATUS 39 NTAPI 40 ObpCreateGlobalDosDevicesSD(OUT PSECURITY_DESCRIPTOR *SecurityDescriptor) 41 { 42 PSECURITY_DESCRIPTOR Sd = NULL; 43 PACL Dacl; 44 ULONG AclSize, SdSize; 45 NTSTATUS Status; 46 47 AclSize = sizeof(ACL) + 48 sizeof(ACE) + RtlLengthSid(SeWorldSid) + 49 sizeof(ACE) + RtlLengthSid(SeLocalSystemSid) + 50 sizeof(ACE) + RtlLengthSid(SeWorldSid) + 51 sizeof(ACE) + RtlLengthSid(SeAliasAdminsSid) + 52 sizeof(ACE) + RtlLengthSid(SeLocalSystemSid) + 53 sizeof(ACE) + RtlLengthSid(SeCreatorOwnerSid); 54 55 SdSize = sizeof(SECURITY_DESCRIPTOR) + AclSize; 56 57 /* Allocate the SD and ACL */ 58 Sd = ExAllocatePoolWithTag(PagedPool, SdSize, TAG_SD); 59 if (Sd == NULL) 60 { 61 return STATUS_INSUFFICIENT_RESOURCES; 62 } 63 64 /* Initialize the SD */ 65 Status = RtlCreateSecurityDescriptor(Sd, 66 SECURITY_DESCRIPTOR_REVISION); 67 if (!NT_SUCCESS(Status)) 68 return Status; 69 70 Dacl = (PACL)((INT_PTR)Sd + sizeof(SECURITY_DESCRIPTOR)); 71 72 /* Initialize the DACL */ 73 RtlCreateAcl(Dacl, AclSize, ACL_REVISION); 74 75 /* Add the ACEs */ 76 RtlAddAccessAllowedAce(Dacl, 77 ACL_REVISION, 78 GENERIC_READ | GENERIC_EXECUTE, 79 SeWorldSid); 80 81 RtlAddAccessAllowedAce(Dacl, 82 ACL_REVISION, 83 GENERIC_ALL, 84 SeLocalSystemSid); 85 86 RtlAddAccessAllowedAceEx(Dacl, 87 ACL_REVISION, 88 INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, 89 GENERIC_EXECUTE, 90 SeWorldSid); 91 92 RtlAddAccessAllowedAceEx(Dacl, 93 ACL_REVISION, 94 INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, 95 GENERIC_ALL, 96 SeAliasAdminsSid); 97 98 RtlAddAccessAllowedAceEx(Dacl, 99 ACL_REVISION, 100 INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, 101 GENERIC_ALL, 102 SeLocalSystemSid); 103 104 RtlAddAccessAllowedAceEx(Dacl, 105 ACL_REVISION, 106 INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, 107 GENERIC_ALL, 108 SeCreatorOwnerSid); 109 110 /* Attach the DACL to the SD */ 111 Status = RtlSetDaclSecurityDescriptor(Sd, 112 TRUE, 113 Dacl, 114 FALSE); 115 if (!NT_SUCCESS(Status)) 116 goto done; 117 118 *SecurityDescriptor = Sd; 119 120 done: 121 if (!NT_SUCCESS(Status)) 122 { 123 if (Sd != NULL) 124 ExFreePoolWithTag(Sd, TAG_SD); 125 } 126 127 return Status; 128 } 129 130 INIT_FUNCTION 131 NTSTATUS 132 NTAPI 133 ObpCreateDosDevicesDirectory(VOID) 134 { 135 OBJECT_ATTRIBUTES ObjectAttributes; 136 UNICODE_STRING RootName, TargetName, LinkName; 137 HANDLE Handle, SymHandle; 138 PSECURITY_DESCRIPTOR DosDevicesSD = NULL; 139 NTSTATUS Status; 140 141 /* Create a custom security descriptor for the global DosDevices directory */ 142 Status = ObpCreateGlobalDosDevicesSD(&DosDevicesSD); 143 if (!NT_SUCCESS(Status)) 144 return Status; 145 146 /* Create the global DosDevices directory \?? */ 147 RtlInitUnicodeString(&RootName, L"\\GLOBAL??"); 148 InitializeObjectAttributes(&ObjectAttributes, 149 &RootName, 150 OBJ_PERMANENT, 151 NULL, 152 DosDevicesSD); 153 Status = NtCreateDirectoryObject(&Handle, 154 DIRECTORY_ALL_ACCESS, 155 &ObjectAttributes); 156 ExFreePoolWithTag(DosDevicesSD, TAG_SD); 157 if (!NT_SUCCESS(Status)) return Status; 158 159 /* Create the system device map */ 160 Status = ObpCreateDeviceMap(Handle); 161 if (!NT_SUCCESS(Status)) 162 return Status; 163 164 /*********************************************\ 165 |*** HACK until we support device mappings ***| 166 |*** Add a symlink \??\ <--> \GLOBAL??\ ***| 167 \*********************************************/ 168 RtlInitUnicodeString(&LinkName, L"\\??"); 169 InitializeObjectAttributes(&ObjectAttributes, 170 &LinkName, 171 OBJ_PERMANENT, 172 NULL, 173 NULL); 174 Status = NtCreateSymbolicLinkObject(&SymHandle, 175 SYMBOLIC_LINK_ALL_ACCESS, 176 &ObjectAttributes, 177 &RootName); 178 if (NT_SUCCESS(Status)) NtClose(SymHandle); 179 /*********************************************\ 180 \*********************************************/ 181 182 // FIXME: Create a device mapping for the global \?? directory 183 184 /* 185 * Initialize the \??\GLOBALROOT symbolic link 186 * pointing to the root directory \ . 187 */ 188 RtlInitUnicodeString(&LinkName, L"GLOBALROOT"); 189 RtlInitUnicodeString(&TargetName, L""); 190 InitializeObjectAttributes(&ObjectAttributes, 191 &LinkName, 192 OBJ_PERMANENT, 193 Handle, 194 NULL); 195 Status = NtCreateSymbolicLinkObject(&SymHandle, 196 SYMBOLIC_LINK_ALL_ACCESS, 197 &ObjectAttributes, 198 &TargetName); 199 if (NT_SUCCESS(Status)) NtClose(SymHandle); 200 201 /* 202 * Initialize the \??\Global symbolic link pointing to the global 203 * DosDevices directory \?? . It is used to access the global \?? 204 * by user-mode components which, by default, use a per-session 205 * DosDevices directory. 206 */ 207 RtlInitUnicodeString(&LinkName, L"Global"); 208 InitializeObjectAttributes(&ObjectAttributes, 209 &LinkName, 210 OBJ_PERMANENT, 211 Handle, 212 NULL); 213 Status = NtCreateSymbolicLinkObject(&SymHandle, 214 SYMBOLIC_LINK_ALL_ACCESS, 215 &ObjectAttributes, 216 &RootName); 217 if (NT_SUCCESS(Status)) NtClose(SymHandle); 218 219 /* Close the directory handle */ 220 NtClose(Handle); 221 if (!NT_SUCCESS(Status)) return Status; 222 223 /* 224 * Initialize the \DosDevices symbolic link pointing to the global 225 * DosDevices directory \?? , for backward compatibility with 226 * Windows NT-2000 systems. 227 */ 228 RtlCreateUnicodeString(&LinkName, L"\\DosDevices"); 229 RtlInitUnicodeString(&RootName, (PCWSTR)&ObpDosDevicesShortNameRoot); 230 InitializeObjectAttributes(&ObjectAttributes, 231 &LinkName, 232 OBJ_PERMANENT, 233 NULL, 234 NULL); 235 Status = NtCreateSymbolicLinkObject(&SymHandle, 236 SYMBOLIC_LINK_ALL_ACCESS, 237 &ObjectAttributes, 238 &RootName); 239 if (NT_SUCCESS(Status)) NtClose(SymHandle); 240 241 /* Return status */ 242 return Status; 243 } 244 245 /*++ 246 * @name ObpDeleteNameCheck 247 * 248 * The ObpDeleteNameCheck routine checks if a named object should be 249 * removed from the object directory namespace. 250 * 251 * @param Object 252 * Pointer to the object to check for possible removal. 253 * 254 * @return None. 255 * 256 * @remarks An object is removed if the following 4 criteria are met: 257 * 1) The object has 0 handles open 258 * 2) The object is in the directory namespace and has a name 259 * 3) The object is not permanent 260 * 261 *--*/ 262 VOID 263 NTAPI 264 ObpDeleteNameCheck(IN PVOID Object) 265 { 266 POBJECT_HEADER ObjectHeader; 267 OBP_LOOKUP_CONTEXT Context; 268 POBJECT_HEADER_NAME_INFO ObjectNameInfo; 269 POBJECT_TYPE ObjectType; 270 PVOID Directory = NULL; 271 272 /* Get object structures */ 273 ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object); 274 ObjectNameInfo = ObpReferenceNameInfo(ObjectHeader); 275 ObjectType = ObjectHeader->Type; 276 277 /* 278 * Check if the handle count is 0, if the object is named, 279 * and if the object isn't a permanent object. 280 */ 281 if (!(ObjectHeader->HandleCount) && 282 (ObjectNameInfo) && 283 (ObjectNameInfo->Name.Length) && 284 (ObjectNameInfo->Directory) && 285 !(ObjectHeader->Flags & OB_FLAG_PERMANENT)) 286 { 287 /* Setup a lookup context */ 288 ObpInitializeLookupContext(&Context); 289 290 /* Lock the directory */ 291 ObpAcquireDirectoryLockExclusive(ObjectNameInfo->Directory, &Context); 292 293 /* Do the lookup */ 294 Object = ObpLookupEntryDirectory(ObjectNameInfo->Directory, 295 &ObjectNameInfo->Name, 296 0, 297 FALSE, 298 &Context); 299 if (Object) 300 { 301 /* Lock the object */ 302 ObpAcquireObjectLock(ObjectHeader); 303 304 /* Make sure we can still delete the object */ 305 if (!(ObjectHeader->HandleCount) && 306 !(ObjectHeader->Flags & OB_FLAG_PERMANENT)) 307 { 308 /* First delete it from the directory */ 309 ObpDeleteEntryDirectory(&Context); 310 311 /* Check if this is a symbolic link */ 312 if (ObjectType == ObpSymbolicLinkObjectType) 313 { 314 /* Remove internal name */ 315 ObpDeleteSymbolicLinkName(Object); 316 } 317 318 /* Check if the kernel exclusive is set */ 319 ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader); 320 if ((ObjectNameInfo) && 321 (ObjectNameInfo->QueryReferences & OB_FLAG_KERNEL_EXCLUSIVE)) 322 { 323 /* Remove protection flag */ 324 InterlockedExchangeAdd((PLONG)&ObjectNameInfo->QueryReferences, 325 -OB_FLAG_KERNEL_EXCLUSIVE); 326 } 327 328 /* Get the directory */ 329 Directory = ObjectNameInfo->Directory; 330 } 331 332 /* Release the lock */ 333 ObpReleaseObjectLock(ObjectHeader); 334 } 335 336 /* Cleanup after lookup */ 337 ObpReleaseLookupContext(&Context); 338 339 /* Remove another query reference since we added one on top */ 340 ObpDereferenceNameInfo(ObjectNameInfo); 341 342 /* Check if we were inserted in a directory */ 343 if (Directory) 344 { 345 /* We were, so first remove the extra reference we had added */ 346 ObpDereferenceNameInfo(ObjectNameInfo); 347 348 /* Now dereference the object as well */ 349 ObDereferenceObject(Object); 350 } 351 } 352 else 353 { 354 /* Remove the reference we added */ 355 ObpDereferenceNameInfo(ObjectNameInfo); 356 } 357 } 358 359 BOOLEAN 360 NTAPI 361 ObpIsUnsecureName(IN PUNICODE_STRING ObjectName, 362 IN BOOLEAN CaseInSensitive) 363 { 364 BOOLEAN Unsecure; 365 PWSTR UnsecureBuffer; 366 UNICODE_STRING UnsecureName; 367 368 /* No unsecure names known, quit */ 369 if (ObpUnsecureGlobalNamesBuffer[0] == UNICODE_NULL) 370 { 371 return FALSE; 372 } 373 374 /* By default, we have a secure name */ 375 Unsecure = FALSE; 376 /* We will browse the whole string */ 377 UnsecureBuffer = &ObpUnsecureGlobalNamesBuffer[0]; 378 while (TRUE) 379 { 380 /* Initialize the unicode string */ 381 RtlInitUnicodeString(&UnsecureName, UnsecureBuffer); 382 /* We're at the end of the multisz string! */ 383 if (UnsecureName.Length == 0) 384 { 385 break; 386 } 387 388 /* 389 * Does the unsecure name prefix the object name? 390 * If so, that's an unsecure name, and return so 391 */ 392 if (RtlPrefixUnicodeString(&UnsecureName, ObjectName, CaseInSensitive)) 393 { 394 Unsecure = TRUE; 395 break; 396 } 397 398 /* 399 * Move to the next string. As a reminder, ObpUnsecureGlobalNamesBuffer is 400 * a multisz, so we move the string next to the current UNICODE_NULL char 401 */ 402 UnsecureBuffer = (PWSTR)((ULONG_PTR)UnsecureBuffer + UnsecureName.Length + sizeof(UNICODE_NULL)); 403 } 404 405 /* Return our findings */ 406 return Unsecure; 407 } 408 409 NTSTATUS 410 NTAPI 411 ObpLookupObjectName(IN HANDLE RootHandle OPTIONAL, 412 IN OUT PUNICODE_STRING ObjectName, 413 IN ULONG Attributes, 414 IN POBJECT_TYPE ObjectType, 415 IN KPROCESSOR_MODE AccessMode, 416 IN OUT PVOID ParseContext, 417 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL, 418 IN PVOID InsertObject OPTIONAL, 419 IN OUT PACCESS_STATE AccessState, 420 OUT POBP_LOOKUP_CONTEXT LookupContext, 421 OUT PVOID *FoundObject) 422 { 423 PVOID Object; 424 POBJECT_HEADER ObjectHeader; 425 UNICODE_STRING ComponentName, RemainingName; 426 BOOLEAN Reparse = FALSE, SymLink = FALSE; 427 POBJECT_DIRECTORY Directory = NULL, ParentDirectory = NULL, RootDirectory; 428 POBJECT_DIRECTORY ReferencedDirectory = NULL, ReferencedParentDirectory = NULL; 429 KIRQL CalloutIrql; 430 OB_PARSE_METHOD ParseRoutine; 431 NTSTATUS Status; 432 KPROCESSOR_MODE AccessCheckMode; 433 PWCHAR NewName; 434 POBJECT_HEADER_NAME_INFO ObjectNameInfo; 435 ULONG MaxReparse = 30; 436 PAGED_CODE(); 437 OBTRACE(OB_NAMESPACE_DEBUG, 438 "%s - Finding Object: %wZ. Expecting: %p\n", 439 __FUNCTION__, 440 ObjectName, 441 InsertObject); 442 443 /* Initialize starting state */ 444 ObpInitializeLookupContext(LookupContext); 445 *FoundObject = NULL; 446 Status = STATUS_SUCCESS; 447 Object = NULL; 448 449 /* Check if case-insensitivity is checked */ 450 if (ObpCaseInsensitive) 451 { 452 /* Check if the object type requests this */ 453 if (!(ObjectType) || (ObjectType->TypeInfo.CaseInsensitive)) 454 { 455 /* Add the flag to disable case sensitivity */ 456 Attributes |= OBJ_CASE_INSENSITIVE; 457 } 458 } 459 460 /* Check if this is a access checks are being forced */ 461 AccessCheckMode = (Attributes & OBJ_FORCE_ACCESS_CHECK) ? 462 UserMode : AccessMode; 463 464 /* Check if we got a Root Directory */ 465 if (RootHandle) 466 { 467 /* We did. Reference it */ 468 Status = ObReferenceObjectByHandle(RootHandle, 469 0, 470 NULL, 471 AccessMode, 472 (PVOID*)&RootDirectory, 473 NULL); 474 if (!NT_SUCCESS(Status)) return Status; 475 476 /* Get the header */ 477 ObjectHeader = OBJECT_TO_OBJECT_HEADER(RootDirectory); 478 479 /* The name cannot start with a separator, unless this is a file */ 480 if ((ObjectName->Buffer) && 481 (ObjectName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR) && 482 (ObjectHeader->Type != IoFileObjectType)) 483 { 484 /* The syntax is bad, so fail this request */ 485 ObDereferenceObject(RootDirectory); 486 return STATUS_OBJECT_PATH_SYNTAX_BAD; 487 } 488 489 /* Don't parse a Directory */ 490 if (ObjectHeader->Type != ObpDirectoryObjectType) 491 { 492 /* Make sure the Object Type has a parse routine */ 493 ParseRoutine = ObjectHeader->Type->TypeInfo.ParseProcedure; 494 if (!ParseRoutine) 495 { 496 /* We can't parse a name if we don't have a parse routine */ 497 ObDereferenceObject(RootDirectory); 498 return STATUS_INVALID_HANDLE; 499 } 500 501 /* Set default parse count */ 502 MaxReparse = 30; 503 504 /* Now parse */ 505 while (TRUE) 506 { 507 /* Start with the full name */ 508 RemainingName = *ObjectName; 509 510 /* Call the Parse Procedure */ 511 ObpCalloutStart(&CalloutIrql); 512 Status = ParseRoutine(RootDirectory, 513 ObjectType, 514 AccessState, 515 AccessCheckMode, 516 Attributes, 517 ObjectName, 518 &RemainingName, 519 ParseContext, 520 SecurityQos, 521 &Object); 522 ObpCalloutEnd(CalloutIrql, "Parse", ObjectHeader->Type, Object); 523 524 /* Check for success or failure, so not reparse */ 525 if ((Status != STATUS_REPARSE) && 526 (Status != STATUS_REPARSE_OBJECT)) 527 { 528 /* Check for failure */ 529 if (!NT_SUCCESS(Status)) 530 { 531 /* Parse routine might not have cleared this, do it */ 532 Object = NULL; 533 } 534 else if (!Object) 535 { 536 /* Modify status to reflect failure inside Ob */ 537 Status = STATUS_OBJECT_NAME_NOT_FOUND; 538 } 539 540 /* We're done, return the status and object */ 541 *FoundObject = Object; 542 ObDereferenceObject(RootDirectory); 543 return Status; 544 } 545 else if ((!ObjectName->Length) || 546 (!ObjectName->Buffer) || 547 (ObjectName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)) 548 { 549 /* Reparsed to the root directory, so start over */ 550 ObDereferenceObject(RootDirectory); 551 RootDirectory = ObpRootDirectoryObject; 552 553 /* Don't use this anymore, since we're starting at root */ 554 RootHandle = NULL; 555 goto ParseFromRoot; 556 } 557 else if (--MaxReparse) 558 { 559 /* Try reparsing again */ 560 continue; 561 } 562 else 563 { 564 /* Reparsed too many times */ 565 ObDereferenceObject(RootDirectory); 566 567 /* Return the object and normalized status */ 568 *FoundObject = Object; 569 if (!Object) Status = STATUS_OBJECT_NAME_NOT_FOUND; 570 return Status; 571 } 572 } 573 } 574 else if (!(ObjectName->Length) || !(ObjectName->Buffer)) 575 { 576 /* Just return the Root Directory if we didn't get a name */ 577 Status = ObReferenceObjectByPointer(RootDirectory, 578 0, 579 ObjectType, 580 AccessMode); 581 if (NT_SUCCESS(Status)) Object = RootDirectory; 582 583 /* Remove the first reference we added and return the object */ 584 ObDereferenceObject(RootDirectory); 585 *FoundObject = Object; 586 return Status; 587 } 588 } 589 else 590 { 591 /* We did not get a Root Directory, so use the root */ 592 RootDirectory = ObpRootDirectoryObject; 593 594 /* It must start with a path separator */ 595 if (!(ObjectName->Length) || 596 !(ObjectName->Buffer) || 597 (ObjectName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR)) 598 { 599 /* This name is invalid, so fail */ 600 return STATUS_OBJECT_PATH_SYNTAX_BAD; 601 } 602 603 /* Check if the name is only the path separator */ 604 if (ObjectName->Length == sizeof(OBJ_NAME_PATH_SEPARATOR)) 605 { 606 /* So the caller only wants the root directory; do we have one? */ 607 if (!RootDirectory) 608 { 609 /* This must be the first time we're creating it... right? */ 610 if (InsertObject) 611 { 612 /* Yes, so return it to ObInsert so that it can create it */ 613 Status = ObReferenceObjectByPointer(InsertObject, 614 0, 615 ObjectType, 616 AccessMode); 617 if (NT_SUCCESS(Status)) *FoundObject = InsertObject; 618 return Status; 619 } 620 else 621 { 622 /* This should never really happen */ 623 ASSERT(FALSE); 624 return STATUS_INVALID_PARAMETER; 625 } 626 } 627 else 628 { 629 /* We do have the root directory, so just return it */ 630 Status = ObReferenceObjectByPointer(RootDirectory, 631 0, 632 ObjectType, 633 AccessMode); 634 if (NT_SUCCESS(Status)) *FoundObject = RootDirectory; 635 return Status; 636 } 637 } 638 else 639 { 640 ParseFromRoot: 641 /* FIXME: Check if we have a device map */ 642 643 /* Check if this is a possible DOS name */ 644 if (!((ULONG_PTR)(ObjectName->Buffer) & 7)) 645 { 646 /* 647 * This could be one. Does it match the prefix? 648 * Note that as an optimization, the match is done as 64-bit 649 * compare since the prefix is "\??\" which is exactly 8 bytes. 650 * 651 * In the second branch, we test for "\??" which is also valid. 652 * This time, we use a 32-bit compare followed by a Unicode 653 * character compare (16-bit), since the sum is 6 bytes. 654 */ 655 if ((ObjectName->Length >= ObpDosDevicesShortName.Length) && 656 (*(PULONGLONG)(ObjectName->Buffer) == 657 ObpDosDevicesShortNamePrefix.Alignment.QuadPart)) 658 { 659 /* FIXME! */ 660 } 661 else if ((ObjectName->Length == ObpDosDevicesShortName.Length - 662 sizeof(WCHAR)) && 663 (*(PULONG)(ObjectName->Buffer) == 664 ObpDosDevicesShortNameRoot.Alignment.LowPart) && 665 (*((PWCHAR)(ObjectName->Buffer) + 2) == 666 (WCHAR)(ObpDosDevicesShortNameRoot.Alignment.HighPart))) 667 { 668 /* FIXME! */ 669 } 670 } 671 } 672 } 673 674 /* Check if we were reparsing a symbolic link */ 675 if (!SymLink) 676 { 677 /* Allow reparse */ 678 Reparse = TRUE; 679 MaxReparse = 30; 680 } 681 682 /* Reparse */ 683 while (Reparse && MaxReparse) 684 { 685 /* Get the name */ 686 RemainingName = *ObjectName; 687 688 /* Disable reparsing again */ 689 Reparse = FALSE; 690 691 /* Start parse loop */ 692 while (TRUE) 693 { 694 /* Clear object */ 695 Object = NULL; 696 697 /* Check if the name starts with a path separator */ 698 if ((RemainingName.Length) && 699 (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR)) 700 { 701 /* Skip the path separator */ 702 RemainingName.Buffer++; 703 RemainingName.Length -= sizeof(OBJ_NAME_PATH_SEPARATOR); 704 } 705 706 /* Find the next Part Name */ 707 ComponentName = RemainingName; 708 while (RemainingName.Length) 709 { 710 /* Break if we found the \ ending */ 711 if (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR) break; 712 713 /* Move on */ 714 RemainingName.Buffer++; 715 RemainingName.Length -= sizeof(OBJ_NAME_PATH_SEPARATOR); 716 } 717 718 /* Get its size and make sure it's valid */ 719 ComponentName.Length -= RemainingName.Length; 720 if (!ComponentName.Length) 721 { 722 /* Invalid size, fail */ 723 Status = STATUS_OBJECT_NAME_INVALID; 724 break; 725 } 726 727 /* Check if we're in the root */ 728 if (!Directory) Directory = RootDirectory; 729 730 /* Check if this is a user-mode call that needs to traverse */ 731 if ((AccessCheckMode != KernelMode) && 732 !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE)) 733 { 734 /* We shouldn't have referenced a directory yet */ 735 ASSERT(ReferencedDirectory == NULL); 736 737 /* Reference the directory */ 738 ObReferenceObject(Directory); 739 ReferencedDirectory = Directory; 740 741 /* Check if we have a parent directory */ 742 if (ParentDirectory) 743 { 744 /* Check for traverse access */ 745 if (!ObpCheckTraverseAccess(ParentDirectory, 746 DIRECTORY_TRAVERSE, 747 AccessState, 748 FALSE, 749 AccessCheckMode, 750 &Status)) 751 { 752 /* We don't have it, fail */ 753 break; 754 } 755 } 756 } 757 758 /* Check if we don't have a remaining name yet */ 759 if (!RemainingName.Length) 760 { 761 /* Check if we don't have a referenced directory yet */ 762 if (!ReferencedDirectory) 763 { 764 /* Reference it */ 765 ObReferenceObject(Directory); 766 ReferencedDirectory = Directory; 767 } 768 769 /* Check if we are inserting an object */ 770 if (InsertObject) 771 { 772 /* Lock the directory */ 773 ObpAcquireDirectoryLockExclusive(Directory, LookupContext); 774 } 775 } 776 777 /* Do the lookup */ 778 Object = ObpLookupEntryDirectory(Directory, 779 &ComponentName, 780 Attributes, 781 InsertObject ? FALSE : TRUE, 782 LookupContext); 783 if (!Object) 784 { 785 /* We didn't find it... do we still have a path? */ 786 if (RemainingName.Length) 787 { 788 /* Then tell the caller the path wasn't found */ 789 Status = STATUS_OBJECT_PATH_NOT_FOUND; 790 break; 791 } 792 else if (!InsertObject) 793 { 794 /* Otherwise, we have a path, but the name isn't valid */ 795 Status = STATUS_OBJECT_NAME_NOT_FOUND; 796 break; 797 } 798 799 /* Check create access for the object */ 800 if (!ObCheckCreateObjectAccess(Directory, 801 ObjectType == ObpDirectoryObjectType ? 802 DIRECTORY_CREATE_SUBDIRECTORY : 803 DIRECTORY_CREATE_OBJECT, 804 AccessState, 805 &ComponentName, 806 FALSE, 807 AccessCheckMode, 808 &Status)) 809 { 810 /* We don't have create access, fail */ 811 break; 812 } 813 814 /* Get the object header */ 815 ObjectHeader = OBJECT_TO_OBJECT_HEADER(InsertObject); 816 817 /* 818 * Deny object creation if: 819 * That's a section object or a symbolic link 820 * Which isn't in the same section that root directory 821 * That doesn't have the SeCreateGlobalPrivilege 822 * And that is not a known unsecure name 823 */ 824 if (RootDirectory->SessionId != -1) 825 { 826 if (ObjectHeader->Type == MmSectionObjectType || 827 ObjectHeader->Type == ObpSymbolicLinkObjectType) 828 { 829 if (RootDirectory->SessionId != PsGetCurrentProcessSessionId() && 830 !SeSinglePrivilegeCheck(SeCreateGlobalPrivilege, AccessCheckMode) && 831 !ObpIsUnsecureName(&ComponentName, BooleanFlagOn(Attributes, OBJ_CASE_INSENSITIVE))) 832 { 833 Status = STATUS_ACCESS_DENIED; 834 break; 835 } 836 } 837 } 838 839 /* Create Object Name */ 840 NewName = ExAllocatePoolWithTag(PagedPool, 841 ComponentName.Length, 842 OB_NAME_TAG); 843 if (!(NewName) || 844 !(ObpInsertEntryDirectory(Directory, 845 LookupContext, 846 ObjectHeader))) 847 { 848 /* Either couldn't allocate the name, or insert failed */ 849 if (NewName) ExFreePoolWithTag(NewName, OB_NAME_TAG); 850 851 /* Fail due to memory reasons */ 852 Status = STATUS_INSUFFICIENT_RESOURCES; 853 break; 854 } 855 856 /* Reference newly to be inserted object */ 857 ObReferenceObject(InsertObject); 858 859 /* Get the name information */ 860 ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader); 861 862 /* Reference the directory */ 863 ObReferenceObject(Directory); 864 865 /* Copy the Name */ 866 RtlCopyMemory(NewName, 867 ComponentName.Buffer, 868 ComponentName.Length); 869 870 /* Check if we had an old name */ 871 if (ObjectNameInfo->Name.Buffer) 872 { 873 /* Free it */ 874 ExFreePoolWithTag(ObjectNameInfo->Name.Buffer, OB_NAME_TAG); 875 } 876 877 /* Write new one */ 878 ObjectNameInfo->Name.Buffer = NewName; 879 ObjectNameInfo->Name.Length = ComponentName.Length; 880 ObjectNameInfo->Name.MaximumLength = ComponentName.Length; 881 882 /* Return Status and the Expected Object */ 883 Status = STATUS_SUCCESS; 884 Object = InsertObject; 885 886 /* Get out of here */ 887 break; 888 } 889 890 ReparseObject: 891 /* We found it, so now get its header */ 892 ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object); 893 894 /* 895 * Check for a parse Procedure, but don't bother to parse for an insert 896 * unless it's a Symbolic Link, in which case we MUST parse 897 */ 898 ParseRoutine = ObjectHeader->Type->TypeInfo.ParseProcedure; 899 if ((ParseRoutine) && 900 (!(InsertObject) || (ParseRoutine == ObpParseSymbolicLink))) 901 { 902 /* Use the Root Directory next time */ 903 Directory = NULL; 904 905 /* Increment the pointer count */ 906 InterlockedExchangeAddSizeT(&ObjectHeader->PointerCount, 1); 907 908 /* Cleanup from the first lookup */ 909 ObpReleaseLookupContext(LookupContext); 910 911 /* Check if we have a referenced directory */ 912 if (ReferencedDirectory) 913 { 914 /* We do, dereference it */ 915 ObDereferenceObject(ReferencedDirectory); 916 ReferencedDirectory = NULL; 917 } 918 919 /* Check if we have a referenced parent directory */ 920 if (ReferencedParentDirectory) 921 { 922 /* We do, dereference it */ 923 ObDereferenceObject(ReferencedParentDirectory); 924 ReferencedParentDirectory = NULL; 925 } 926 927 /* Call the Parse Procedure */ 928 ObpCalloutStart(&CalloutIrql); 929 Status = ParseRoutine(Object, 930 ObjectType, 931 AccessState, 932 AccessCheckMode, 933 Attributes, 934 ObjectName, 935 &RemainingName, 936 ParseContext, 937 SecurityQos, 938 &Object); 939 ObpCalloutEnd(CalloutIrql, "Parse", ObjectHeader->Type, Object); 940 941 /* Remove our extra reference */ 942 ObDereferenceObject(&ObjectHeader->Body); 943 944 /* Check if we have to reparse */ 945 if ((Status == STATUS_REPARSE) || 946 (Status == STATUS_REPARSE_OBJECT)) 947 { 948 /* Reparse again */ 949 Reparse = TRUE; 950 --MaxReparse; 951 if (MaxReparse == 0) 952 { 953 Object = NULL; 954 break; 955 } 956 957 /* Start over from root if we got sent back there */ 958 if ((Status == STATUS_REPARSE_OBJECT) || 959 (ObjectName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)) 960 { 961 /* Check if we got a root directory */ 962 if (RootHandle) 963 { 964 /* Stop using it, because we have a new directory now */ 965 ObDereferenceObject(RootDirectory); 966 RootHandle = NULL; 967 } 968 969 /* Start at Root */ 970 ParentDirectory = NULL; 971 RootDirectory = ObpRootDirectoryObject; 972 973 /* Check for reparse status */ 974 if (Status == STATUS_REPARSE_OBJECT) 975 { 976 /* Don't reparse again */ 977 Reparse = FALSE; 978 979 /* Did we actually get an object to which to reparse? */ 980 if (!Object) 981 { 982 /* We didn't, so set a failure status */ 983 Status = STATUS_OBJECT_NAME_NOT_FOUND; 984 } 985 else 986 { 987 /* We did, so we're free to parse the new object */ 988 goto ReparseObject; 989 } 990 } 991 else 992 { 993 /* This is a symbolic link */ 994 SymLink = TRUE; 995 goto ParseFromRoot; 996 } 997 } 998 else if (RootDirectory == ObpRootDirectoryObject) 999 { 1000 /* We got STATUS_REPARSE but are at the Root Directory */ 1001 Object = NULL; 1002 Status = STATUS_OBJECT_NAME_NOT_FOUND; 1003 Reparse = FALSE; 1004 } 1005 } 1006 else if (!NT_SUCCESS(Status)) 1007 { 1008 /* Total failure */ 1009 Object = NULL; 1010 } 1011 else if (!Object) 1012 { 1013 /* We didn't reparse but we didn't find the Object Either */ 1014 Status = STATUS_OBJECT_NAME_NOT_FOUND; 1015 } 1016 1017 /* Break out of the loop */ 1018 break; 1019 } 1020 else 1021 { 1022 /* No parse routine...do we still have a remaining name? */ 1023 if (!RemainingName.Length) 1024 { 1025 /* Are we creating an object? */ 1026 if (!InsertObject) 1027 { 1028 /* Check if this is a user-mode call that needs to traverse */ 1029 if ((AccessCheckMode != KernelMode) && 1030 !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE)) 1031 { 1032 /* Check if we can get it */ 1033 if (!ObpCheckTraverseAccess(Directory, 1034 DIRECTORY_TRAVERSE, 1035 AccessState, 1036 FALSE, 1037 AccessCheckMode, 1038 &Status)) 1039 { 1040 /* We don't have access, fail */ 1041 Object = NULL; 1042 break; 1043 } 1044 } 1045 1046 /* Reference the Object */ 1047 Status = ObReferenceObjectByPointer(Object, 1048 0, 1049 ObjectType, 1050 AccessMode); 1051 if (!NT_SUCCESS(Status)) Object = NULL; 1052 } 1053 1054 /* And get out of the reparse loop */ 1055 break; 1056 } 1057 else 1058 { 1059 /* We still have a name; check if this is a directory object */ 1060 if (ObjectHeader->Type == ObpDirectoryObjectType) 1061 { 1062 /* Check if we have a referenced parent directory */ 1063 if (ReferencedParentDirectory) 1064 { 1065 /* Dereference it */ 1066 ObDereferenceObject(ReferencedParentDirectory); 1067 } 1068 1069 /* Restart the lookup from this directory */ 1070 ReferencedParentDirectory = ReferencedDirectory; 1071 ParentDirectory = Directory; 1072 Directory = Object; 1073 ReferencedDirectory = NULL; 1074 } 1075 else 1076 { 1077 /* We still have a name, but no parse routine for it */ 1078 Status = STATUS_OBJECT_TYPE_MISMATCH; 1079 Object = NULL; 1080 break; 1081 } 1082 } 1083 } 1084 } 1085 } 1086 1087 /* Check if we failed */ 1088 if (!NT_SUCCESS(Status)) 1089 { 1090 /* Cleanup after lookup */ 1091 ObpReleaseLookupContext(LookupContext); 1092 } 1093 1094 /* Check if we have a device map and dereference it if so */ 1095 //if (DeviceMap) ObfDereferenceDeviceMap(DeviceMap); 1096 1097 /* Check if we have a referenced directory and dereference it if so */ 1098 if (ReferencedDirectory) ObDereferenceObject(ReferencedDirectory); 1099 1100 /* Check if we have a referenced parent directory */ 1101 if (ReferencedParentDirectory) 1102 { 1103 /* We do, dereference it */ 1104 ObDereferenceObject(ReferencedParentDirectory); 1105 } 1106 1107 /* Set the found object and check if we got one */ 1108 *FoundObject = Object; 1109 if (!Object) 1110 { 1111 /* Nothing was found. Did we reparse or get success? */ 1112 if ((Status == STATUS_REPARSE) || (NT_SUCCESS(Status))) 1113 { 1114 /* Set correct failure */ 1115 Status = STATUS_OBJECT_NAME_NOT_FOUND; 1116 } 1117 } 1118 1119 /* Check if we had a root directory */ 1120 if (RootHandle) ObDereferenceObject(RootDirectory); 1121 1122 /* Return status to caller */ 1123 OBTRACE(OB_NAMESPACE_DEBUG, 1124 "%s - Found Object: %p. Expected: %p\n", 1125 __FUNCTION__, 1126 *FoundObject, 1127 InsertObject); 1128 return Status; 1129 } 1130 1131 /* PUBLIC FUNCTIONS *********************************************************/ 1132 1133 NTSTATUS 1134 NTAPI 1135 ObQueryNameString(IN PVOID Object, 1136 OUT POBJECT_NAME_INFORMATION ObjectNameInfo, 1137 IN ULONG Length, 1138 OUT PULONG ReturnLength) 1139 { 1140 POBJECT_HEADER_NAME_INFO LocalInfo; 1141 POBJECT_HEADER ObjectHeader; 1142 POBJECT_DIRECTORY ParentDirectory; 1143 ULONG NameSize; 1144 PWCH ObjectName; 1145 BOOLEAN ObjectIsNamed; 1146 NTSTATUS Status = STATUS_SUCCESS; 1147 1148 /* Get the Kernel Meta-Structures */ 1149 ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object); 1150 LocalInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader); 1151 1152 /* Check if a Query Name Procedure is available */ 1153 if (ObjectHeader->Type->TypeInfo.QueryNameProcedure) 1154 { 1155 /* Call the procedure inside SEH */ 1156 ObjectIsNamed = ((LocalInfo) && (LocalInfo->Name.Length > 0)); 1157 1158 _SEH2_TRY 1159 { 1160 Status = ObjectHeader->Type->TypeInfo.QueryNameProcedure(Object, 1161 ObjectIsNamed, 1162 ObjectNameInfo, 1163 Length, 1164 ReturnLength, 1165 KernelMode); 1166 } 1167 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1168 { 1169 /* Return the exception code */ 1170 Status = _SEH2_GetExceptionCode(); 1171 } 1172 _SEH2_END; 1173 1174 return Status; 1175 } 1176 1177 /* Check if the object doesn't even have a name */ 1178 if (!(LocalInfo) || !(LocalInfo->Name.Buffer)) 1179 { 1180 Status = STATUS_SUCCESS; 1181 1182 _SEH2_TRY 1183 { 1184 /* We're returning the name structure */ 1185 *ReturnLength = sizeof(OBJECT_NAME_INFORMATION); 1186 1187 /* Check if we were given enough space */ 1188 if (*ReturnLength > Length) 1189 { 1190 Status = STATUS_INFO_LENGTH_MISMATCH; 1191 } 1192 else 1193 { 1194 /* Return an empty buffer */ 1195 RtlInitEmptyUnicodeString(&ObjectNameInfo->Name, NULL, 0); 1196 } 1197 } 1198 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1199 { 1200 /* Return the exception code */ 1201 Status = _SEH2_GetExceptionCode(); 1202 } 1203 _SEH2_END; 1204 1205 return Status; 1206 } 1207 1208 /* 1209 * Find the size needed for the name. We won't do 1210 * this during the Name Creation loop because we want 1211 * to let the caller know that the buffer isn't big 1212 * enough right at the beginning, not work our way through 1213 * and find out at the end 1214 */ 1215 _SEH2_TRY 1216 { 1217 if (Object == ObpRootDirectoryObject) 1218 { 1219 /* Size of the '\' string */ 1220 NameSize = sizeof(OBJ_NAME_PATH_SEPARATOR); 1221 } 1222 else 1223 { 1224 /* Get the Object Directory and add name of Object */ 1225 ParentDirectory = LocalInfo->Directory; 1226 NameSize = sizeof(OBJ_NAME_PATH_SEPARATOR) + LocalInfo->Name.Length; 1227 1228 /* Loop inside the directory to get the top-most one (meaning root) */ 1229 while ((ParentDirectory != ObpRootDirectoryObject) && (ParentDirectory)) 1230 { 1231 /* Get the Name Information */ 1232 LocalInfo = OBJECT_HEADER_TO_NAME_INFO( 1233 OBJECT_TO_OBJECT_HEADER(ParentDirectory)); 1234 1235 /* Add the size of the Directory Name */ 1236 if (LocalInfo && LocalInfo->Directory) 1237 { 1238 /* Size of the '\' string + Directory Name */ 1239 NameSize += sizeof(OBJ_NAME_PATH_SEPARATOR) + 1240 LocalInfo->Name.Length; 1241 1242 /* Move to next parent Directory */ 1243 ParentDirectory = LocalInfo->Directory; 1244 } 1245 else 1246 { 1247 /* Directory with no name. We append "...\" */ 1248 NameSize += sizeof(L"...") + sizeof(OBJ_NAME_PATH_SEPARATOR); 1249 break; 1250 } 1251 } 1252 } 1253 1254 /* Finally, add the name of the structure and the null char */ 1255 *ReturnLength = NameSize + 1256 sizeof(OBJECT_NAME_INFORMATION) + 1257 sizeof(UNICODE_NULL); 1258 1259 /* Check if we were given enough space */ 1260 if (*ReturnLength > Length) _SEH2_YIELD(return STATUS_INFO_LENGTH_MISMATCH); 1261 1262 /* 1263 * Now we will actually create the name. We work backwards because 1264 * it's easier to start off from the Name we have and walk up the 1265 * parent directories. We use the same logic as Name Length calculation. 1266 */ 1267 LocalInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader); 1268 ObjectName = (PWCH)((ULONG_PTR)ObjectNameInfo + *ReturnLength); 1269 *--ObjectName = UNICODE_NULL; 1270 1271 /* Check if the object is actually the Root directory */ 1272 if (Object == ObpRootDirectoryObject) 1273 { 1274 /* This is already the Root Directory, return "\\" */ 1275 *--ObjectName = OBJ_NAME_PATH_SEPARATOR; 1276 ObjectNameInfo->Name.Length = (USHORT)NameSize; 1277 ObjectNameInfo->Name.MaximumLength = (USHORT)(NameSize + 1278 sizeof(UNICODE_NULL)); 1279 ObjectNameInfo->Name.Buffer = ObjectName; 1280 Status = STATUS_SUCCESS; 1281 } 1282 else 1283 { 1284 /* Start by adding the Object's Name */ 1285 ObjectName = (PWCH)((ULONG_PTR)ObjectName - 1286 LocalInfo->Name.Length); 1287 RtlCopyMemory(ObjectName, 1288 LocalInfo->Name.Buffer, 1289 LocalInfo->Name.Length); 1290 1291 /* Now parse the Parent directories until we reach the top */ 1292 ParentDirectory = LocalInfo->Directory; 1293 while ((ParentDirectory != ObpRootDirectoryObject) && (ParentDirectory)) 1294 { 1295 /* Get the name information */ 1296 LocalInfo = OBJECT_HEADER_TO_NAME_INFO( 1297 OBJECT_TO_OBJECT_HEADER(ParentDirectory)); 1298 1299 /* Add the "\" */ 1300 *(--ObjectName) = OBJ_NAME_PATH_SEPARATOR; 1301 1302 /* Add the Parent Directory's Name */ 1303 if (LocalInfo && LocalInfo->Name.Buffer) 1304 { 1305 /* Add the name */ 1306 ObjectName = (PWCH)((ULONG_PTR)ObjectName - 1307 LocalInfo->Name.Length); 1308 RtlCopyMemory(ObjectName, 1309 LocalInfo->Name.Buffer, 1310 LocalInfo->Name.Length); 1311 1312 /* Move to next parent */ 1313 ParentDirectory = LocalInfo->Directory; 1314 } 1315 else 1316 { 1317 /* Directory without a name, we add "..." */ 1318 ObjectName = (PWCH)((ULONG_PTR)ObjectName - 1319 sizeof(L"...") + 1320 sizeof(UNICODE_NULL)); 1321 RtlCopyMemory(ObjectName, L"...", sizeof(L"...")); 1322 break; 1323 } 1324 } 1325 1326 /* Add Root Directory Name */ 1327 *(--ObjectName) = OBJ_NAME_PATH_SEPARATOR; 1328 ObjectNameInfo->Name.Length = (USHORT)NameSize; 1329 ObjectNameInfo->Name.MaximumLength = 1330 (USHORT)(NameSize + sizeof(UNICODE_NULL)); 1331 ObjectNameInfo->Name.Buffer = ObjectName; 1332 } 1333 } 1334 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1335 { 1336 /* Return the exception code */ 1337 Status = _SEH2_GetExceptionCode(); 1338 } 1339 _SEH2_END; 1340 1341 /* Return success */ 1342 return Status; 1343 } 1344 1345 /* EOF */ 1346