/* * PROJECT: ReactOS Kernel * LICENSE: GPL - See COPYING in the top level directory * FILE: ntoskrnl/ob/obname.c * PURPOSE: Manages all functions related to the Object Manager name- * space, such as finding objects or querying their names. * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) * Eric Kohl * Thomas Weidenmueller (w3seek@reactos.org) */ /* INCLUDES ******************************************************************/ #include #define NDEBUG #include BOOLEAN ObpCaseInsensitive = TRUE; POBJECT_DIRECTORY ObpRootDirectoryObject; POBJECT_DIRECTORY ObpTypeDirectoryObject; /* DOS Device Prefix \??\ and \?? */ ALIGNEDNAME ObpDosDevicesShortNamePrefix = {{L'\\',L'?',L'?',L'\\'}}; ALIGNEDNAME ObpDosDevicesShortNameRoot = {{L'\\',L'?',L'?',L'\0'}}; UNICODE_STRING ObpDosDevicesShortName = { sizeof(ObpDosDevicesShortNamePrefix), sizeof(ObpDosDevicesShortNamePrefix), (PWSTR)&ObpDosDevicesShortNamePrefix }; WCHAR ObpUnsecureGlobalNamesBuffer[128] = {0}; ULONG ObpUnsecureGlobalNamesLength = sizeof(ObpUnsecureGlobalNamesBuffer); /* PRIVATE FUNCTIONS *********************************************************/ INIT_FUNCTION NTSTATUS NTAPI ObpCreateGlobalDosDevicesSD(OUT PSECURITY_DESCRIPTOR *SecurityDescriptor) { PSECURITY_DESCRIPTOR Sd = NULL; PACL Dacl; ULONG AclSize, SdSize; NTSTATUS Status; AclSize = sizeof(ACL) + sizeof(ACE) + RtlLengthSid(SeWorldSid) + sizeof(ACE) + RtlLengthSid(SeLocalSystemSid) + sizeof(ACE) + RtlLengthSid(SeWorldSid) + sizeof(ACE) + RtlLengthSid(SeAliasAdminsSid) + sizeof(ACE) + RtlLengthSid(SeLocalSystemSid) + sizeof(ACE) + RtlLengthSid(SeCreatorOwnerSid); SdSize = sizeof(SECURITY_DESCRIPTOR) + AclSize; /* Allocate the SD and ACL */ Sd = ExAllocatePoolWithTag(PagedPool, SdSize, TAG_SD); if (Sd == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } /* Initialize the SD */ Status = RtlCreateSecurityDescriptor(Sd, SECURITY_DESCRIPTOR_REVISION); if (!NT_SUCCESS(Status)) return Status; Dacl = (PACL)((INT_PTR)Sd + sizeof(SECURITY_DESCRIPTOR)); /* Initialize the DACL */ RtlCreateAcl(Dacl, AclSize, ACL_REVISION); /* Add the ACEs */ RtlAddAccessAllowedAce(Dacl, ACL_REVISION, GENERIC_READ | GENERIC_EXECUTE, SeWorldSid); RtlAddAccessAllowedAce(Dacl, ACL_REVISION, GENERIC_ALL, SeLocalSystemSid); RtlAddAccessAllowedAceEx(Dacl, ACL_REVISION, INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, GENERIC_EXECUTE, SeWorldSid); RtlAddAccessAllowedAceEx(Dacl, ACL_REVISION, INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, GENERIC_ALL, SeAliasAdminsSid); RtlAddAccessAllowedAceEx(Dacl, ACL_REVISION, INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, GENERIC_ALL, SeLocalSystemSid); RtlAddAccessAllowedAceEx(Dacl, ACL_REVISION, INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, GENERIC_ALL, SeCreatorOwnerSid); /* Attach the DACL to the SD */ Status = RtlSetDaclSecurityDescriptor(Sd, TRUE, Dacl, FALSE); if (!NT_SUCCESS(Status)) goto done; *SecurityDescriptor = Sd; done: if (!NT_SUCCESS(Status)) { if (Sd != NULL) ExFreePoolWithTag(Sd, TAG_SD); } return Status; } INIT_FUNCTION NTSTATUS NTAPI ObpCreateDosDevicesDirectory(VOID) { OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING RootName, TargetName, LinkName; HANDLE Handle, SymHandle; PSECURITY_DESCRIPTOR DosDevicesSD = NULL; NTSTATUS Status; /* Create a custom security descriptor for the global DosDevices directory */ Status = ObpCreateGlobalDosDevicesSD(&DosDevicesSD); if (!NT_SUCCESS(Status)) return Status; /* Create the global DosDevices directory \?? */ RtlInitUnicodeString(&RootName, L"\\GLOBAL??"); InitializeObjectAttributes(&ObjectAttributes, &RootName, OBJ_PERMANENT, NULL, DosDevicesSD); Status = NtCreateDirectoryObject(&Handle, DIRECTORY_ALL_ACCESS, &ObjectAttributes); ExFreePoolWithTag(DosDevicesSD, TAG_SD); if (!NT_SUCCESS(Status)) return Status; /* Create the system device map */ Status = ObpCreateDeviceMap(Handle); if (!NT_SUCCESS(Status)) return Status; /*********************************************\ |*** HACK until we support device mappings ***| |*** Add a symlink \??\ <--> \GLOBAL??\ ***| \*********************************************/ RtlInitUnicodeString(&LinkName, L"\\??"); InitializeObjectAttributes(&ObjectAttributes, &LinkName, OBJ_PERMANENT, NULL, NULL); Status = NtCreateSymbolicLinkObject(&SymHandle, SYMBOLIC_LINK_ALL_ACCESS, &ObjectAttributes, &RootName); if (NT_SUCCESS(Status)) NtClose(SymHandle); /*********************************************\ \*********************************************/ // FIXME: Create a device mapping for the global \?? directory /* * Initialize the \??\GLOBALROOT symbolic link * pointing to the root directory \ . */ RtlInitUnicodeString(&LinkName, L"GLOBALROOT"); RtlInitUnicodeString(&TargetName, L""); InitializeObjectAttributes(&ObjectAttributes, &LinkName, OBJ_PERMANENT, Handle, NULL); Status = NtCreateSymbolicLinkObject(&SymHandle, SYMBOLIC_LINK_ALL_ACCESS, &ObjectAttributes, &TargetName); if (NT_SUCCESS(Status)) NtClose(SymHandle); /* * Initialize the \??\Global symbolic link pointing to the global * DosDevices directory \?? . It is used to access the global \?? * by user-mode components which, by default, use a per-session * DosDevices directory. */ RtlInitUnicodeString(&LinkName, L"Global"); InitializeObjectAttributes(&ObjectAttributes, &LinkName, OBJ_PERMANENT, Handle, NULL); Status = NtCreateSymbolicLinkObject(&SymHandle, SYMBOLIC_LINK_ALL_ACCESS, &ObjectAttributes, &RootName); if (NT_SUCCESS(Status)) NtClose(SymHandle); /* Close the directory handle */ NtClose(Handle); if (!NT_SUCCESS(Status)) return Status; /* * Initialize the \DosDevices symbolic link pointing to the global * DosDevices directory \?? , for backward compatibility with * Windows NT-2000 systems. */ RtlCreateUnicodeString(&LinkName, L"\\DosDevices"); RtlInitUnicodeString(&RootName, (PCWSTR)&ObpDosDevicesShortNameRoot); InitializeObjectAttributes(&ObjectAttributes, &LinkName, OBJ_PERMANENT, NULL, NULL); Status = NtCreateSymbolicLinkObject(&SymHandle, SYMBOLIC_LINK_ALL_ACCESS, &ObjectAttributes, &RootName); if (NT_SUCCESS(Status)) NtClose(SymHandle); /* Return status */ return Status; } /*++ * @name ObpDeleteNameCheck * * The ObpDeleteNameCheck routine checks if a named object should be * removed from the object directory namespace. * * @param Object * Pointer to the object to check for possible removal. * * @return None. * * @remarks An object is removed if the following 4 criteria are met: * 1) The object has 0 handles open * 2) The object is in the directory namespace and has a name * 3) The object is not permanent * *--*/ VOID NTAPI ObpDeleteNameCheck(IN PVOID Object) { POBJECT_HEADER ObjectHeader; OBP_LOOKUP_CONTEXT Context; POBJECT_HEADER_NAME_INFO ObjectNameInfo; POBJECT_TYPE ObjectType; PVOID Directory = NULL; /* Get object structures */ ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object); ObjectNameInfo = ObpReferenceNameInfo(ObjectHeader); ObjectType = ObjectHeader->Type; /* * Check if the handle count is 0, if the object is named, * and if the object isn't a permanent object. */ if (!(ObjectHeader->HandleCount) && (ObjectNameInfo) && (ObjectNameInfo->Name.Length) && (ObjectNameInfo->Directory) && !(ObjectHeader->Flags & OB_FLAG_PERMANENT)) { /* Setup a lookup context */ ObpInitializeLookupContext(&Context); /* Lock the directory */ ObpAcquireDirectoryLockExclusive(ObjectNameInfo->Directory, &Context); /* Do the lookup */ Object = ObpLookupEntryDirectory(ObjectNameInfo->Directory, &ObjectNameInfo->Name, 0, FALSE, &Context); if (Object) { /* Lock the object */ ObpAcquireObjectLock(ObjectHeader); /* Make sure we can still delete the object */ if (!(ObjectHeader->HandleCount) && !(ObjectHeader->Flags & OB_FLAG_PERMANENT)) { /* First delete it from the directory */ ObpDeleteEntryDirectory(&Context); /* Check if this is a symbolic link */ if (ObjectType == ObpSymbolicLinkObjectType) { /* Remove internal name */ ObpDeleteSymbolicLinkName(Object); } /* Check if the kernel exclusive is set */ ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader); if ((ObjectNameInfo) && (ObjectNameInfo->QueryReferences & OB_FLAG_KERNEL_EXCLUSIVE)) { /* Remove protection flag */ InterlockedExchangeAdd((PLONG)&ObjectNameInfo->QueryReferences, -OB_FLAG_KERNEL_EXCLUSIVE); } /* Get the directory */ Directory = ObjectNameInfo->Directory; } /* Release the lock */ ObpReleaseObjectLock(ObjectHeader); } /* Cleanup after lookup */ ObpReleaseLookupContext(&Context); /* Remove another query reference since we added one on top */ ObpDereferenceNameInfo(ObjectNameInfo); /* Check if we were inserted in a directory */ if (Directory) { /* We were, so first remove the extra reference we had added */ ObpDereferenceNameInfo(ObjectNameInfo); /* Now dereference the object as well */ ObDereferenceObject(Object); } } else { /* Remove the reference we added */ ObpDereferenceNameInfo(ObjectNameInfo); } } BOOLEAN NTAPI ObpIsUnsecureName(IN PUNICODE_STRING ObjectName, IN BOOLEAN CaseInSensitive) { BOOLEAN Unsecure; PWSTR UnsecureBuffer; UNICODE_STRING UnsecureName; /* No unsecure names known, quit */ if (ObpUnsecureGlobalNamesBuffer[0] == UNICODE_NULL) { return FALSE; } /* By default, we have a secure name */ Unsecure = FALSE; /* We will browse the whole string */ UnsecureBuffer = &ObpUnsecureGlobalNamesBuffer[0]; while (TRUE) { /* Initialize the unicode string */ RtlInitUnicodeString(&UnsecureName, UnsecureBuffer); /* We're at the end of the multisz string! */ if (UnsecureName.Length == 0) { break; } /* * Does the unsecure name prefix the object name? * If so, that's an unsecure name, and return so */ if (RtlPrefixUnicodeString(&UnsecureName, ObjectName, CaseInSensitive)) { Unsecure = TRUE; break; } /* * Move to the next string. As a reminder, ObpUnsecureGlobalNamesBuffer is * a multisz, so we move the string next to the current UNICODE_NULL char */ UnsecureBuffer = (PWSTR)((ULONG_PTR)UnsecureBuffer + UnsecureName.Length + sizeof(UNICODE_NULL)); } /* Return our findings */ return Unsecure; } NTSTATUS NTAPI ObpLookupObjectName(IN HANDLE RootHandle OPTIONAL, IN OUT PUNICODE_STRING ObjectName, IN ULONG Attributes, IN POBJECT_TYPE ObjectType, IN KPROCESSOR_MODE AccessMode, IN OUT PVOID ParseContext, IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL, IN PVOID InsertObject OPTIONAL, IN OUT PACCESS_STATE AccessState, OUT POBP_LOOKUP_CONTEXT LookupContext, OUT PVOID *FoundObject) { PVOID Object; POBJECT_HEADER ObjectHeader; UNICODE_STRING ComponentName, RemainingName; BOOLEAN Reparse = FALSE, SymLink = FALSE; POBJECT_DIRECTORY Directory = NULL, ParentDirectory = NULL, RootDirectory; POBJECT_DIRECTORY ReferencedDirectory = NULL, ReferencedParentDirectory = NULL; KIRQL CalloutIrql; OB_PARSE_METHOD ParseRoutine; NTSTATUS Status; KPROCESSOR_MODE AccessCheckMode; PWCHAR NewName; POBJECT_HEADER_NAME_INFO ObjectNameInfo; ULONG MaxReparse = 30; PAGED_CODE(); OBTRACE(OB_NAMESPACE_DEBUG, "%s - Finding Object: %wZ. Expecting: %p\n", __FUNCTION__, ObjectName, InsertObject); /* Initialize starting state */ ObpInitializeLookupContext(LookupContext); *FoundObject = NULL; Status = STATUS_SUCCESS; Object = NULL; /* Check if case-insensitivity is checked */ if (ObpCaseInsensitive) { /* Check if the object type requests this */ if (!(ObjectType) || (ObjectType->TypeInfo.CaseInsensitive)) { /* Add the flag to disable case sensitivity */ Attributes |= OBJ_CASE_INSENSITIVE; } } /* Check if this is a access checks are being forced */ AccessCheckMode = (Attributes & OBJ_FORCE_ACCESS_CHECK) ? UserMode : AccessMode; /* Check if we got a Root Directory */ if (RootHandle) { /* We did. Reference it */ Status = ObReferenceObjectByHandle(RootHandle, 0, NULL, AccessMode, (PVOID*)&RootDirectory, NULL); if (!NT_SUCCESS(Status)) return Status; /* Get the header */ ObjectHeader = OBJECT_TO_OBJECT_HEADER(RootDirectory); /* The name cannot start with a separator, unless this is a file */ if ((ObjectName->Buffer) && (ObjectName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR) && (ObjectHeader->Type != IoFileObjectType)) { /* The syntax is bad, so fail this request */ ObDereferenceObject(RootDirectory); return STATUS_OBJECT_PATH_SYNTAX_BAD; } /* Don't parse a Directory */ if (ObjectHeader->Type != ObpDirectoryObjectType) { /* Make sure the Object Type has a parse routine */ ParseRoutine = ObjectHeader->Type->TypeInfo.ParseProcedure; if (!ParseRoutine) { /* We can't parse a name if we don't have a parse routine */ ObDereferenceObject(RootDirectory); return STATUS_INVALID_HANDLE; } /* Set default parse count */ MaxReparse = 30; /* Now parse */ while (TRUE) { /* Start with the full name */ RemainingName = *ObjectName; /* Call the Parse Procedure */ ObpCalloutStart(&CalloutIrql); Status = ParseRoutine(RootDirectory, ObjectType, AccessState, AccessCheckMode, Attributes, ObjectName, &RemainingName, ParseContext, SecurityQos, &Object); ObpCalloutEnd(CalloutIrql, "Parse", ObjectHeader->Type, Object); /* Check for success or failure, so not reparse */ if ((Status != STATUS_REPARSE) && (Status != STATUS_REPARSE_OBJECT)) { /* Check for failure */ if (!NT_SUCCESS(Status)) { /* Parse routine might not have cleared this, do it */ Object = NULL; } else if (!Object) { /* Modify status to reflect failure inside Ob */ Status = STATUS_OBJECT_NAME_NOT_FOUND; } /* We're done, return the status and object */ *FoundObject = Object; ObDereferenceObject(RootDirectory); return Status; } else if ((!ObjectName->Length) || (!ObjectName->Buffer) || (ObjectName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)) { /* Reparsed to the root directory, so start over */ ObDereferenceObject(RootDirectory); RootDirectory = ObpRootDirectoryObject; /* Don't use this anymore, since we're starting at root */ RootHandle = NULL; goto ParseFromRoot; } else if (--MaxReparse) { /* Try reparsing again */ continue; } else { /* Reparsed too many times */ ObDereferenceObject(RootDirectory); /* Return the object and normalized status */ *FoundObject = Object; if (!Object) Status = STATUS_OBJECT_NAME_NOT_FOUND; return Status; } } } else if (!(ObjectName->Length) || !(ObjectName->Buffer)) { /* Just return the Root Directory if we didn't get a name */ Status = ObReferenceObjectByPointer(RootDirectory, 0, ObjectType, AccessMode); if (NT_SUCCESS(Status)) Object = RootDirectory; /* Remove the first reference we added and return the object */ ObDereferenceObject(RootDirectory); *FoundObject = Object; return Status; } } else { /* We did not get a Root Directory, so use the root */ RootDirectory = ObpRootDirectoryObject; /* It must start with a path separator */ if (!(ObjectName->Length) || !(ObjectName->Buffer) || (ObjectName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR)) { /* This name is invalid, so fail */ return STATUS_OBJECT_PATH_SYNTAX_BAD; } /* Check if the name is only the path separator */ if (ObjectName->Length == sizeof(OBJ_NAME_PATH_SEPARATOR)) { /* So the caller only wants the root directory; do we have one? */ if (!RootDirectory) { /* This must be the first time we're creating it... right? */ if (InsertObject) { /* Yes, so return it to ObInsert so that it can create it */ Status = ObReferenceObjectByPointer(InsertObject, 0, ObjectType, AccessMode); if (NT_SUCCESS(Status)) *FoundObject = InsertObject; return Status; } else { /* This should never really happen */ ASSERT(FALSE); return STATUS_INVALID_PARAMETER; } } else { /* We do have the root directory, so just return it */ Status = ObReferenceObjectByPointer(RootDirectory, 0, ObjectType, AccessMode); if (NT_SUCCESS(Status)) *FoundObject = RootDirectory; return Status; } } else { ParseFromRoot: /* FIXME: Check if we have a device map */ /* Check if this is a possible DOS name */ if (!((ULONG_PTR)(ObjectName->Buffer) & 7)) { /* * This could be one. Does it match the prefix? * Note that as an optimization, the match is done as 64-bit * compare since the prefix is "\??\" which is exactly 8 bytes. * * In the second branch, we test for "\??" which is also valid. * This time, we use a 32-bit compare followed by a Unicode * character compare (16-bit), since the sum is 6 bytes. */ if ((ObjectName->Length >= ObpDosDevicesShortName.Length) && (*(PULONGLONG)(ObjectName->Buffer) == ObpDosDevicesShortNamePrefix.Alignment.QuadPart)) { /* FIXME! */ } else if ((ObjectName->Length == ObpDosDevicesShortName.Length - sizeof(WCHAR)) && (*(PULONG)(ObjectName->Buffer) == ObpDosDevicesShortNameRoot.Alignment.LowPart) && (*((PWCHAR)(ObjectName->Buffer) + 2) == (WCHAR)(ObpDosDevicesShortNameRoot.Alignment.HighPart))) { /* FIXME! */ } } } } /* Check if we were reparsing a symbolic link */ if (!SymLink) { /* Allow reparse */ Reparse = TRUE; MaxReparse = 30; } /* Reparse */ while (Reparse && MaxReparse) { /* Get the name */ RemainingName = *ObjectName; /* Disable reparsing again */ Reparse = FALSE; /* Start parse loop */ while (TRUE) { /* Clear object */ Object = NULL; /* Check if the name starts with a path separator */ if ((RemainingName.Length) && (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR)) { /* Skip the path separator */ RemainingName.Buffer++; RemainingName.Length -= sizeof(OBJ_NAME_PATH_SEPARATOR); } /* Find the next Part Name */ ComponentName = RemainingName; while (RemainingName.Length) { /* Break if we found the \ ending */ if (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR) break; /* Move on */ RemainingName.Buffer++; RemainingName.Length -= sizeof(OBJ_NAME_PATH_SEPARATOR); } /* Get its size and make sure it's valid */ ComponentName.Length -= RemainingName.Length; if (!ComponentName.Length) { /* Invalid size, fail */ Status = STATUS_OBJECT_NAME_INVALID; break; } /* Check if we're in the root */ if (!Directory) Directory = RootDirectory; /* Check if this is a user-mode call that needs to traverse */ if ((AccessCheckMode != KernelMode) && !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE)) { /* We shouldn't have referenced a directory yet */ ASSERT(ReferencedDirectory == NULL); /* Reference the directory */ ObReferenceObject(Directory); ReferencedDirectory = Directory; /* Check if we have a parent directory */ if (ParentDirectory) { /* Check for traverse access */ if (!ObpCheckTraverseAccess(ParentDirectory, DIRECTORY_TRAVERSE, AccessState, FALSE, AccessCheckMode, &Status)) { /* We don't have it, fail */ break; } } } /* Check if we don't have a remaining name yet */ if (!RemainingName.Length) { /* Check if we don't have a referenced directory yet */ if (!ReferencedDirectory) { /* Reference it */ ObReferenceObject(Directory); ReferencedDirectory = Directory; } /* Check if we are inserting an object */ if (InsertObject) { /* Lock the directory */ ObpAcquireDirectoryLockExclusive(Directory, LookupContext); } } /* Do the lookup */ Object = ObpLookupEntryDirectory(Directory, &ComponentName, Attributes, InsertObject ? FALSE : TRUE, LookupContext); if (!Object) { /* We didn't find it... do we still have a path? */ if (RemainingName.Length) { /* Then tell the caller the path wasn't found */ Status = STATUS_OBJECT_PATH_NOT_FOUND; break; } else if (!InsertObject) { /* Otherwise, we have a path, but the name isn't valid */ Status = STATUS_OBJECT_NAME_NOT_FOUND; break; } /* Check create access for the object */ if (!ObCheckCreateObjectAccess(Directory, ObjectType == ObpDirectoryObjectType ? DIRECTORY_CREATE_SUBDIRECTORY : DIRECTORY_CREATE_OBJECT, AccessState, &ComponentName, FALSE, AccessCheckMode, &Status)) { /* We don't have create access, fail */ break; } /* Get the object header */ ObjectHeader = OBJECT_TO_OBJECT_HEADER(InsertObject); /* * Deny object creation if: * That's a section object or a symbolic link * Which isn't in the same section that root directory * That doesn't have the SeCreateGlobalPrivilege * And that is not a known unsecure name */ if (RootDirectory->SessionId != -1) { if (ObjectHeader->Type == MmSectionObjectType || ObjectHeader->Type == ObpSymbolicLinkObjectType) { if (RootDirectory->SessionId != PsGetCurrentProcessSessionId() && !SeSinglePrivilegeCheck(SeCreateGlobalPrivilege, AccessCheckMode) && !ObpIsUnsecureName(&ComponentName, BooleanFlagOn(Attributes, OBJ_CASE_INSENSITIVE))) { Status = STATUS_ACCESS_DENIED; break; } } } /* Create Object Name */ NewName = ExAllocatePoolWithTag(PagedPool, ComponentName.Length, OB_NAME_TAG); if (!(NewName) || !(ObpInsertEntryDirectory(Directory, LookupContext, ObjectHeader))) { /* Either couldn't allocate the name, or insert failed */ if (NewName) ExFreePoolWithTag(NewName, OB_NAME_TAG); /* Fail due to memory reasons */ Status = STATUS_INSUFFICIENT_RESOURCES; break; } /* Reference newly to be inserted object */ ObReferenceObject(InsertObject); /* Get the name information */ ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader); /* Reference the directory */ ObReferenceObject(Directory); /* Copy the Name */ RtlCopyMemory(NewName, ComponentName.Buffer, ComponentName.Length); /* Check if we had an old name */ if (ObjectNameInfo->Name.Buffer) { /* Free it */ ExFreePoolWithTag(ObjectNameInfo->Name.Buffer, OB_NAME_TAG); } /* Write new one */ ObjectNameInfo->Name.Buffer = NewName; ObjectNameInfo->Name.Length = ComponentName.Length; ObjectNameInfo->Name.MaximumLength = ComponentName.Length; /* Return Status and the Expected Object */ Status = STATUS_SUCCESS; Object = InsertObject; /* Get out of here */ break; } ReparseObject: /* We found it, so now get its header */ ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object); /* * Check for a parse Procedure, but don't bother to parse for an insert * unless it's a Symbolic Link, in which case we MUST parse */ ParseRoutine = ObjectHeader->Type->TypeInfo.ParseProcedure; if ((ParseRoutine) && (!(InsertObject) || (ParseRoutine == ObpParseSymbolicLink))) { /* Use the Root Directory next time */ Directory = NULL; /* Increment the pointer count */ InterlockedExchangeAddSizeT(&ObjectHeader->PointerCount, 1); /* Cleanup from the first lookup */ ObpReleaseLookupContext(LookupContext); /* Check if we have a referenced directory */ if (ReferencedDirectory) { /* We do, dereference it */ ObDereferenceObject(ReferencedDirectory); ReferencedDirectory = NULL; } /* Check if we have a referenced parent directory */ if (ReferencedParentDirectory) { /* We do, dereference it */ ObDereferenceObject(ReferencedParentDirectory); ReferencedParentDirectory = NULL; } /* Call the Parse Procedure */ ObpCalloutStart(&CalloutIrql); Status = ParseRoutine(Object, ObjectType, AccessState, AccessCheckMode, Attributes, ObjectName, &RemainingName, ParseContext, SecurityQos, &Object); ObpCalloutEnd(CalloutIrql, "Parse", ObjectHeader->Type, Object); /* Remove our extra reference */ ObDereferenceObject(&ObjectHeader->Body); /* Check if we have to reparse */ if ((Status == STATUS_REPARSE) || (Status == STATUS_REPARSE_OBJECT)) { /* Reparse again */ Reparse = TRUE; --MaxReparse; if (MaxReparse == 0) { Object = NULL; break; } /* Start over from root if we got sent back there */ if ((Status == STATUS_REPARSE_OBJECT) || (ObjectName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)) { /* Check if we got a root directory */ if (RootHandle) { /* Stop using it, because we have a new directory now */ ObDereferenceObject(RootDirectory); RootHandle = NULL; } /* Start at Root */ ParentDirectory = NULL; RootDirectory = ObpRootDirectoryObject; /* Check for reparse status */ if (Status == STATUS_REPARSE_OBJECT) { /* Don't reparse again */ Reparse = FALSE; /* Did we actually get an object to which to reparse? */ if (!Object) { /* We didn't, so set a failure status */ Status = STATUS_OBJECT_NAME_NOT_FOUND; } else { /* We did, so we're free to parse the new object */ goto ReparseObject; } } else { /* This is a symbolic link */ SymLink = TRUE; goto ParseFromRoot; } } else if (RootDirectory == ObpRootDirectoryObject) { /* We got STATUS_REPARSE but are at the Root Directory */ Object = NULL; Status = STATUS_OBJECT_NAME_NOT_FOUND; Reparse = FALSE; } } else if (!NT_SUCCESS(Status)) { /* Total failure */ Object = NULL; } else if (!Object) { /* We didn't reparse but we didn't find the Object Either */ Status = STATUS_OBJECT_NAME_NOT_FOUND; } /* Break out of the loop */ break; } else { /* No parse routine...do we still have a remaining name? */ if (!RemainingName.Length) { /* Are we creating an object? */ if (!InsertObject) { /* Check if this is a user-mode call that needs to traverse */ if ((AccessCheckMode != KernelMode) && !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE)) { /* Check if we can get it */ if (!ObpCheckTraverseAccess(Directory, DIRECTORY_TRAVERSE, AccessState, FALSE, AccessCheckMode, &Status)) { /* We don't have access, fail */ Object = NULL; break; } } /* Reference the Object */ Status = ObReferenceObjectByPointer(Object, 0, ObjectType, AccessMode); if (!NT_SUCCESS(Status)) Object = NULL; } /* And get out of the reparse loop */ break; } else { /* We still have a name; check if this is a directory object */ if (ObjectHeader->Type == ObpDirectoryObjectType) { /* Check if we have a referenced parent directory */ if (ReferencedParentDirectory) { /* Dereference it */ ObDereferenceObject(ReferencedParentDirectory); } /* Restart the lookup from this directory */ ReferencedParentDirectory = ReferencedDirectory; ParentDirectory = Directory; Directory = Object; ReferencedDirectory = NULL; } else { /* We still have a name, but no parse routine for it */ Status = STATUS_OBJECT_TYPE_MISMATCH; Object = NULL; break; } } } } } /* Check if we failed */ if (!NT_SUCCESS(Status)) { /* Cleanup after lookup */ ObpReleaseLookupContext(LookupContext); } /* Check if we have a device map and dereference it if so */ //if (DeviceMap) ObfDereferenceDeviceMap(DeviceMap); /* Check if we have a referenced directory and dereference it if so */ if (ReferencedDirectory) ObDereferenceObject(ReferencedDirectory); /* Check if we have a referenced parent directory */ if (ReferencedParentDirectory) { /* We do, dereference it */ ObDereferenceObject(ReferencedParentDirectory); } /* Set the found object and check if we got one */ *FoundObject = Object; if (!Object) { /* Nothing was found. Did we reparse or get success? */ if ((Status == STATUS_REPARSE) || (NT_SUCCESS(Status))) { /* Set correct failure */ Status = STATUS_OBJECT_NAME_NOT_FOUND; } } /* Check if we had a root directory */ if (RootHandle) ObDereferenceObject(RootDirectory); /* Return status to caller */ OBTRACE(OB_NAMESPACE_DEBUG, "%s - Found Object: %p. Expected: %p\n", __FUNCTION__, *FoundObject, InsertObject); return Status; } /* PUBLIC FUNCTIONS *********************************************************/ NTSTATUS NTAPI ObQueryNameString(IN PVOID Object, OUT POBJECT_NAME_INFORMATION ObjectNameInfo, IN ULONG Length, OUT PULONG ReturnLength) { POBJECT_HEADER_NAME_INFO LocalInfo; POBJECT_HEADER ObjectHeader; POBJECT_DIRECTORY ParentDirectory; ULONG NameSize; PWCH ObjectName; BOOLEAN ObjectIsNamed; NTSTATUS Status = STATUS_SUCCESS; /* Get the Kernel Meta-Structures */ ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object); LocalInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader); /* Check if a Query Name Procedure is available */ if (ObjectHeader->Type->TypeInfo.QueryNameProcedure) { /* Call the procedure inside SEH */ ObjectIsNamed = ((LocalInfo) && (LocalInfo->Name.Length > 0)); _SEH2_TRY { Status = ObjectHeader->Type->TypeInfo.QueryNameProcedure(Object, ObjectIsNamed, ObjectNameInfo, Length, ReturnLength, KernelMode); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Return the exception code */ Status = _SEH2_GetExceptionCode(); } _SEH2_END; return Status; } /* Check if the object doesn't even have a name */ if (!(LocalInfo) || !(LocalInfo->Name.Buffer)) { Status = STATUS_SUCCESS; _SEH2_TRY { /* We're returning the name structure */ *ReturnLength = sizeof(OBJECT_NAME_INFORMATION); /* Check if we were given enough space */ if (*ReturnLength > Length) { Status = STATUS_INFO_LENGTH_MISMATCH; } else { /* Return an empty buffer */ RtlInitEmptyUnicodeString(&ObjectNameInfo->Name, NULL, 0); } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Return the exception code */ Status = _SEH2_GetExceptionCode(); } _SEH2_END; return Status; } /* * Find the size needed for the name. We won't do * this during the Name Creation loop because we want * to let the caller know that the buffer isn't big * enough right at the beginning, not work our way through * and find out at the end */ _SEH2_TRY { if (Object == ObpRootDirectoryObject) { /* Size of the '\' string */ NameSize = sizeof(OBJ_NAME_PATH_SEPARATOR); } else { /* Get the Object Directory and add name of Object */ ParentDirectory = LocalInfo->Directory; NameSize = sizeof(OBJ_NAME_PATH_SEPARATOR) + LocalInfo->Name.Length; /* Loop inside the directory to get the top-most one (meaning root) */ while ((ParentDirectory != ObpRootDirectoryObject) && (ParentDirectory)) { /* Get the Name Information */ LocalInfo = OBJECT_HEADER_TO_NAME_INFO( OBJECT_TO_OBJECT_HEADER(ParentDirectory)); /* Add the size of the Directory Name */ if (LocalInfo && LocalInfo->Directory) { /* Size of the '\' string + Directory Name */ NameSize += sizeof(OBJ_NAME_PATH_SEPARATOR) + LocalInfo->Name.Length; /* Move to next parent Directory */ ParentDirectory = LocalInfo->Directory; } else { /* Directory with no name. We append "...\" */ NameSize += sizeof(L"...") + sizeof(OBJ_NAME_PATH_SEPARATOR); break; } } } /* Finally, add the name of the structure and the null char */ *ReturnLength = NameSize + sizeof(OBJECT_NAME_INFORMATION) + sizeof(UNICODE_NULL); /* Check if we were given enough space */ if (*ReturnLength > Length) _SEH2_YIELD(return STATUS_INFO_LENGTH_MISMATCH); /* * Now we will actually create the name. We work backwards because * it's easier to start off from the Name we have and walk up the * parent directories. We use the same logic as Name Length calculation. */ LocalInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader); ObjectName = (PWCH)((ULONG_PTR)ObjectNameInfo + *ReturnLength); *--ObjectName = UNICODE_NULL; /* Check if the object is actually the Root directory */ if (Object == ObpRootDirectoryObject) { /* This is already the Root Directory, return "\\" */ *--ObjectName = OBJ_NAME_PATH_SEPARATOR; ObjectNameInfo->Name.Length = (USHORT)NameSize; ObjectNameInfo->Name.MaximumLength = (USHORT)(NameSize + sizeof(UNICODE_NULL)); ObjectNameInfo->Name.Buffer = ObjectName; Status = STATUS_SUCCESS; } else { /* Start by adding the Object's Name */ ObjectName = (PWCH)((ULONG_PTR)ObjectName - LocalInfo->Name.Length); RtlCopyMemory(ObjectName, LocalInfo->Name.Buffer, LocalInfo->Name.Length); /* Now parse the Parent directories until we reach the top */ ParentDirectory = LocalInfo->Directory; while ((ParentDirectory != ObpRootDirectoryObject) && (ParentDirectory)) { /* Get the name information */ LocalInfo = OBJECT_HEADER_TO_NAME_INFO( OBJECT_TO_OBJECT_HEADER(ParentDirectory)); /* Add the "\" */ *(--ObjectName) = OBJ_NAME_PATH_SEPARATOR; /* Add the Parent Directory's Name */ if (LocalInfo && LocalInfo->Name.Buffer) { /* Add the name */ ObjectName = (PWCH)((ULONG_PTR)ObjectName - LocalInfo->Name.Length); RtlCopyMemory(ObjectName, LocalInfo->Name.Buffer, LocalInfo->Name.Length); /* Move to next parent */ ParentDirectory = LocalInfo->Directory; } else { /* Directory without a name, we add "..." */ ObjectName = (PWCH)((ULONG_PTR)ObjectName - sizeof(L"...") + sizeof(UNICODE_NULL)); RtlCopyMemory(ObjectName, L"...", sizeof(L"...")); break; } } /* Add Root Directory Name */ *(--ObjectName) = OBJ_NAME_PATH_SEPARATOR; ObjectNameInfo->Name.Length = (USHORT)NameSize; ObjectNameInfo->Name.MaximumLength = (USHORT)(NameSize + sizeof(UNICODE_NULL)); ObjectNameInfo->Name.Buffer = ObjectName; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Return the exception code */ Status = _SEH2_GetExceptionCode(); } _SEH2_END; /* Return success */ return Status; } /* EOF */