reactos/ntoskrnl/ob/obname.c
Pierre Schweitzer f13b6e025f [NTOSKRNL] Implement support for device maps in ObpLookupObjectName
This allows getting rid of the ?? hack in the kernel but this doesn't
allow enabling LUID device maps as ReactOS can no longer open a
session with them enabled. So, we must remain with device maps at
root

CORE-16114
2019-06-30 23:07:54 +02:00

1420 lines
52 KiB
C

/*
* 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 <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
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
ObpGetDosDevicesProtection(OUT PSECURITY_DESCRIPTOR SecurityDescriptor)
{
PACL Dacl;
ULONG AclSize;
NTSTATUS Status;
/* Initialize the SD */
Status = RtlCreateSecurityDescriptor(SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
ASSERT(NT_SUCCESS(Status));
if (ObpProtectionMode & 1)
{
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);
/* Allocate the ACL */
Dacl = ExAllocatePoolWithTag(PagedPool, AclSize, 'lcaD');
if (Dacl == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Initialize the DACL */
Status = RtlCreateAcl(Dacl, AclSize, ACL_REVISION);
ASSERT(NT_SUCCESS(Status));
/* Add the ACEs */
Status = RtlAddAccessAllowedAce(Dacl,
ACL_REVISION,
GENERIC_READ | GENERIC_EXECUTE,
SeWorldSid);
ASSERT(NT_SUCCESS(Status));
Status = RtlAddAccessAllowedAce(Dacl,
ACL_REVISION,
GENERIC_ALL,
SeLocalSystemSid);
ASSERT(NT_SUCCESS(Status));
Status = RtlAddAccessAllowedAceEx(Dacl,
ACL_REVISION,
INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
GENERIC_EXECUTE,
SeWorldSid);
ASSERT(NT_SUCCESS(Status));
Status = RtlAddAccessAllowedAceEx(Dacl,
ACL_REVISION,
INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
GENERIC_ALL,
SeAliasAdminsSid);
ASSERT(NT_SUCCESS(Status));
Status = RtlAddAccessAllowedAceEx(Dacl,
ACL_REVISION,
INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
GENERIC_ALL,
SeLocalSystemSid);
ASSERT(NT_SUCCESS(Status));
Status = RtlAddAccessAllowedAceEx(Dacl,
ACL_REVISION,
INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
GENERIC_ALL,
SeCreatorOwnerSid);
ASSERT(NT_SUCCESS(Status));
}
else
{
AclSize = sizeof(ACL) +
sizeof(ACE) + RtlLengthSid(SeLocalSystemSid) +
sizeof(ACE) + RtlLengthSid(SeWorldSid) +
sizeof(ACE) + RtlLengthSid(SeLocalSystemSid);
/* Allocate the ACL */
Dacl = ExAllocatePoolWithTag(PagedPool, AclSize, 'lcaD');
if (Dacl == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Initialize the DACL */
Status = RtlCreateAcl(Dacl, AclSize, ACL_REVISION);
ASSERT(NT_SUCCESS(Status));
/* Add the ACEs */
Status = RtlAddAccessAllowedAce(Dacl,
ACL_REVISION,
GENERIC_READ | GENERIC_EXECUTE | GENERIC_WRITE,
SeWorldSid);
ASSERT(NT_SUCCESS(Status));
Status = RtlAddAccessAllowedAce(Dacl,
ACL_REVISION,
GENERIC_ALL,
SeLocalSystemSid);
ASSERT(NT_SUCCESS(Status));
Status = RtlAddAccessAllowedAceEx(Dacl,
ACL_REVISION,
INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
GENERIC_ALL,
SeWorldSid);
ASSERT(NT_SUCCESS(Status));
}
/* Attach the DACL to the SD */
Status = RtlSetDaclSecurityDescriptor(SecurityDescriptor, TRUE, Dacl, FALSE);
ASSERT(NT_SUCCESS(Status));
return STATUS_SUCCESS;
}
INIT_FUNCTION
VOID
NTAPI
ObpFreeDosDevicesProtection(OUT PSECURITY_DESCRIPTOR SecurityDescriptor)
{
PACL Dacl;
NTSTATUS Status;
BOOLEAN DaclPresent, DaclDefaulted;
Status = RtlGetDaclSecurityDescriptor(SecurityDescriptor, &DaclPresent, &Dacl, &DaclDefaulted);
ASSERT(NT_SUCCESS(Status));
ASSERT(DaclPresent);
ASSERT(Dacl != NULL);
ExFreePoolWithTag(Dacl, 'lcaD');
}
INIT_FUNCTION
NTSTATUS
NTAPI
ObpCreateDosDevicesDirectory(VOID)
{
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING RootName, TargetName, LinkName;
HANDLE Handle, SymHandle;
SECURITY_DESCRIPTOR DosDevicesSD;
NTSTATUS Status;
/*
* Enable LUID mappings only if not explicitely disabled
* and if protection mode is set
*/
if (ObpProtectionMode == 0 || ObpLUIDDeviceMapsDisabled != 0)
ObpLUIDDeviceMapsEnabled = 0;
else
ObpLUIDDeviceMapsEnabled = 1;
/* Create a custom security descriptor for the global DosDevices directory */
Status = ObpGetDosDevicesProtection(&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);
if (!NT_SUCCESS(Status))
goto done;
/* Create the system device map */
Status = ObSetDeviceMap(NULL, Handle);
if (!NT_SUCCESS(Status))
goto done;
/*
* Initialize the \??\GLOBALROOT symbolic link
* pointing to the root directory \ .
*/
RtlInitUnicodeString(&LinkName, L"GLOBALROOT");
RtlInitUnicodeString(&TargetName, L"");
InitializeObjectAttributes(&ObjectAttributes,
&LinkName,
OBJ_PERMANENT,
Handle,
&DosDevicesSD);
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,
&DosDevicesSD);
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))
goto done;
/*
* 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,
&DosDevicesSD);
Status = NtCreateSymbolicLinkObject(&SymHandle,
SYMBOLIC_LINK_ALL_ACCESS,
&ObjectAttributes,
&RootName);
if (NT_SUCCESS(Status)) NtClose(SymHandle);
done:
ObpFreeDosDevicesProtection(&DosDevicesSD);
/* 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;
PDEVICE_MAP DeviceMap = NULL;
UNICODE_STRING LocalName;
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;
}
LocalName = *ObjectName;
}
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:
LocalName = *ObjectName;
/* Deference the device map if we already have one */
if (DeviceMap != NULL)
{
ObfDereferenceDeviceMap(DeviceMap);
DeviceMap = NULL;
}
/* 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))
{
DeviceMap = ObpReferenceDeviceMap();
/* We have a local mapping, drop the ?? prefix */
if (DeviceMap != NULL && DeviceMap->DosDevicesDirectory != NULL)
{
LocalName.Length -= ObpDosDevicesShortName.Length;
LocalName.MaximumLength -= ObpDosDevicesShortName.Length;
LocalName.Buffer += (ObpDosDevicesShortName.Length / sizeof(WCHAR));
/* We'll browse that local directory */
Directory = DeviceMap->DosDevicesDirectory;
}
}
else if ((ObjectName->Length == ObpDosDevicesShortName.Length -
sizeof(WCHAR)) &&
(*(PULONG)(ObjectName->Buffer) ==
ObpDosDevicesShortNameRoot.Alignment.LowPart) &&
(*((PWCHAR)(ObjectName->Buffer) + 2) ==
(WCHAR)(ObpDosDevicesShortNameRoot.Alignment.HighPart)))
{
DeviceMap = ObpReferenceDeviceMap();
/* Caller is looking for the directory itself */
if (DeviceMap != NULL && DeviceMap->DosDevicesDirectory != NULL)
{
Status = ObReferenceObjectByPointer(DeviceMap->DosDevicesDirectory,
0,
ObjectType,
AccessMode);
if (NT_SUCCESS(Status))
{
*FoundObject = DeviceMap->DosDevicesDirectory;
}
ObfDereferenceDeviceMap(DeviceMap);
return Status;
}
}
}
}
}
/* Check if we were reparsing a symbolic link */
if (!SymLink)
{
/* Allow reparse */
Reparse = TRUE;
MaxReparse = 30;
}
/* Reparse */
while (Reparse && MaxReparse)
{
/* Get the name */
RemainingName = LocalName;
/* 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 */