reactos/ntoskrnl/ob/oblife.c

1952 lines
62 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ob/oblife.c
* PURPOSE: Manages the lifetime of an Object, including its creation,
* and deletion, as well as setting or querying any of its
* information while it is active. Since Object Types are also
* Objects, those are also managed here.
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
* Eric Kohl
* Thomas Weidenmueller (w3seek@reactos.org)
*/
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
extern ULONG NtGlobalFlag;
POBJECT_TYPE ObpTypeObjectType = NULL;
KEVENT ObpDefaultObject;
KGUARDED_MUTEX ObpDeviceMapLock;
GENERAL_LOOKASIDE ObpNameBufferLookasideList, ObpCreateInfoLookasideList;
WORK_QUEUE_ITEM ObpReaperWorkItem;
volatile PVOID ObpReaperList;
ULONG ObpObjectsCreated, ObpObjectsWithName, ObpObjectsWithPoolQuota;
ULONG ObpObjectsWithHandleDB, ObpObjectsWithCreatorInfo;
POBJECT_TYPE ObpObjectTypes[32];
/* PRIVATE FUNCTIONS *********************************************************/
VOID
FASTCALL
ObpDeallocateObject(IN PVOID Object)
{
PVOID HeaderLocation;
POBJECT_HEADER Header;
POBJECT_TYPE ObjectType;
POBJECT_HEADER_HANDLE_INFO HandleInfo;
POBJECT_HEADER_NAME_INFO NameInfo;
POBJECT_HEADER_CREATOR_INFO CreatorInfo;
POBJECT_HEADER_QUOTA_INFO QuotaInfo;
ULONG PagedPoolCharge, NonPagedPoolCharge;
PAGED_CODE();
/* Get the header and assume this is what we'll free */
Header = OBJECT_TO_OBJECT_HEADER(Object);
ObjectType = Header->Type;
HeaderLocation = Header;
/* To find the header, walk backwards from how we allocated */
if ((CreatorInfo = OBJECT_HEADER_TO_CREATOR_INFO(Header)))
{
HeaderLocation = CreatorInfo;
}
if ((NameInfo = OBJECT_HEADER_TO_NAME_INFO(Header)))
{
HeaderLocation = NameInfo;
}
if ((HandleInfo = OBJECT_HEADER_TO_HANDLE_INFO(Header)))
{
HeaderLocation = HandleInfo;
}
if ((QuotaInfo = OBJECT_HEADER_TO_QUOTA_INFO(Header)))
{
HeaderLocation = QuotaInfo;
}
/* Decrease the total */
InterlockedDecrement((PLONG)&ObjectType->TotalNumberOfObjects);
/* Check if we have create info */
if (Header->Flags & OB_FLAG_CREATE_INFO)
{
/* Double-check that it exists */
if (Header->ObjectCreateInfo)
{
/* Free it */
ObpFreeObjectCreateInformation(Header->ObjectCreateInfo);
Header->ObjectCreateInfo = NULL;
}
}
else
{
/* Check if it has a quota block */
if (Header->QuotaBlockCharged)
{
/* Check if we have quota information */
if (QuotaInfo)
{
/* Get charges from quota information */
PagedPoolCharge = QuotaInfo->PagedPoolCharge +
QuotaInfo->SecurityDescriptorCharge;
NonPagedPoolCharge = QuotaInfo->NonPagedPoolCharge;
}
else
{
/* Get charges from object type */
PagedPoolCharge = ObjectType->TypeInfo.DefaultPagedPoolCharge;
NonPagedPoolCharge = ObjectType->
TypeInfo.DefaultNonPagedPoolCharge;
/* Add the SD charge too */
if (Header->Flags & OB_FLAG_SECURITY) PagedPoolCharge += 2048;
}
/* Return the quota */
if (Header->QuotaBlockCharged != OBP_SYSTEM_PROCESS_QUOTA)
{
PsReturnSharedPoolQuota(Header->QuotaBlockCharged,
PagedPoolCharge,
NonPagedPoolCharge);
}
}
}
/* Check if a handle database was active */
if ((HandleInfo) && !(Header->Flags & OB_FLAG_SINGLE_PROCESS))
{
/* Free it */
ExFreePool(HandleInfo->HandleCountDatabase);
HandleInfo->HandleCountDatabase = NULL;
}
/* Check if we have a name */
if ((NameInfo) && (NameInfo->Name.Buffer))
{
/* Free it */
ExFreePool(NameInfo->Name.Buffer);
NameInfo->Name.Buffer = NULL;
}
/* Catch invalid access */
Header->Type = (POBJECT_TYPE)(ULONG_PTR)0xBAADB0B0BAADB0B0ULL;
/* Free the object using the same allocation tag */
ExFreePoolWithTag(HeaderLocation, ObjectType->Key);
}
VOID
NTAPI
ObpDeleteObject(IN PVOID Object,
IN BOOLEAN CalledFromWorkerThread)
{
POBJECT_HEADER Header;
POBJECT_TYPE ObjectType;
POBJECT_HEADER_NAME_INFO NameInfo;
POBJECT_HEADER_CREATOR_INFO CreatorInfo;
KIRQL CalloutIrql;
PAGED_CODE();
/* Get the header and type */
Header = OBJECT_TO_OBJECT_HEADER(Object);
ObjectType = Header->Type;
/* Get creator and name information */
NameInfo = OBJECT_HEADER_TO_NAME_INFO(Header);
CreatorInfo = OBJECT_HEADER_TO_CREATOR_INFO(Header);
/* Check if the object is on a type list */
if ((CreatorInfo) && !(IsListEmpty(&CreatorInfo->TypeList)))
{
/* Lock the object type */
ObpEnterObjectTypeMutex(ObjectType);
/* Remove the object from the type list */
RemoveEntryList(&CreatorInfo->TypeList);
/* Release the lock */
ObpLeaveObjectTypeMutex(ObjectType);
}
/* Check if we have a name */
if ((NameInfo) && (NameInfo->Name.Buffer))
{
/* Free it */
ExFreePool(NameInfo->Name.Buffer);
RtlInitEmptyUnicodeString(&NameInfo->Name, NULL, 0);
}
/* Check if we have a security descriptor */
if (Header->SecurityDescriptor)
{
/* Call the security procedure to delete it */
ObpCalloutStart(&CalloutIrql);
ObjectType->TypeInfo.SecurityProcedure(Object,
DeleteSecurityDescriptor,
0,
NULL,
NULL,
&Header->SecurityDescriptor,
0,
NULL);
ObpCalloutEnd(CalloutIrql, "Security", ObjectType, Object);
}
/* Check if we have a delete procedure */
if (ObjectType->TypeInfo.DeleteProcedure)
{
/* Save whether we were deleted from worker thread or not */
if (!CalledFromWorkerThread) Header->Flags |= OB_FLAG_DEFER_DELETE;
/* Call it */
ObpCalloutStart(&CalloutIrql);
ObjectType->TypeInfo.DeleteProcedure(Object);
ObpCalloutEnd(CalloutIrql, "Delete", ObjectType, Object);
}
/* Now de-allocate all object members */
ObpDeallocateObject(Object);
}
VOID
NTAPI
ObpReapObject(IN PVOID Parameter)
{
POBJECT_HEADER ReapObject, NextObject;
/* Start reaping */
do
{
/* Get the reap object */
ReapObject = InterlockedExchangePointer(&ObpReaperList, (PVOID)1);
/* Start deletion loop */
do
{
/* Get the next object */
NextObject = ReapObject->NextToFree;
/* Delete the object */
ObpDeleteObject(&ReapObject->Body, TRUE);
/* Move to the next one */
ReapObject = NextObject;
} while ((ReapObject) && (ReapObject != (PVOID)1));
} while ((ObpReaperList != (PVOID)1) ||
(InterlockedCompareExchange((PLONG)&ObpReaperList, 0, 1) != 1));
}
/*++
* @name ObpSetPermanentObject
*
* The ObpSetPermanentObject routine makes an sets or clears the permanent
* flag of an object, thus making it either permanent or temporary.
*
* @param ObjectBody
* Pointer to the object to make permanent or temporary.
*
* @param Permanent
* Flag specifying which operation to perform.
*
* @return None.
*
* @remarks If the object is being made temporary, then it will be checked
* as a candidate for immediate removal from the namespace.
*
*--*/
VOID
FASTCALL
ObpSetPermanentObject(IN PVOID ObjectBody,
IN BOOLEAN Permanent)
{
POBJECT_HEADER ObjectHeader;
/* Get the header */
ObjectHeader = OBJECT_TO_OBJECT_HEADER(ObjectBody);
/* Acquire object lock */
ObpAcquireObjectLock(ObjectHeader);
/* Check what we're doing to it */
if (Permanent)
{
/* Set it to permanent */
ObjectHeader->Flags |= OB_FLAG_PERMANENT;
/* Release the lock */
ObpReleaseObjectLock(ObjectHeader);
}
else
{
/* Remove the flag */
ObjectHeader->Flags &= ~OB_FLAG_PERMANENT;
/* Release the lock */
ObpReleaseObjectLock(ObjectHeader);
/* Check if we should delete the object now */
ObpDeleteNameCheck(ObjectBody);
}
}
PWCHAR
NTAPI
ObpAllocateObjectNameBuffer(IN ULONG Length,
IN BOOLEAN UseLookaside,
IN OUT PUNICODE_STRING ObjectName)
{
ULONG MaximumLength;
PVOID Buffer;
/* Set the maximum length to the length plus the terminator */
MaximumLength = Length + sizeof(UNICODE_NULL);
/* Check if we should use the lookaside buffer */
if (!(UseLookaside) || (MaximumLength > OBP_NAME_LOOKASIDE_MAX_SIZE))
{
/* Nope, allocate directly from pool */
/* Since we later use MaximumLength to detect that we're not allocating
* from a list, we need at least MaximumLength + sizeof(UNICODE_NULL)
* here.
*
* People do call this with UseLookasideList FALSE so the distinction
* is critical.
*/
if (MaximumLength <= OBP_NAME_LOOKASIDE_MAX_SIZE)
{
MaximumLength = OBP_NAME_LOOKASIDE_MAX_SIZE + sizeof(UNICODE_NULL);
}
Buffer = ExAllocatePoolWithTag(PagedPool,
MaximumLength,
OB_NAME_TAG);
}
else
{
/* Allocate from the lookaside */
MaximumLength = OBP_NAME_LOOKASIDE_MAX_SIZE;
Buffer = ObpAllocateObjectCreateInfoBuffer(LookasideNameBufferList);
}
/* Setup the string */
ObjectName->MaximumLength = (USHORT)MaximumLength;
ObjectName->Length = (USHORT)Length;
ObjectName->Buffer = Buffer;
return Buffer;
}
VOID
NTAPI
ObpFreeObjectNameBuffer(IN PUNICODE_STRING Name)
{
PVOID Buffer = Name->Buffer;
/* We know this is a pool-allocation if the size doesn't match */
if (Name->MaximumLength != OBP_NAME_LOOKASIDE_MAX_SIZE)
{
/*
* Free it from the pool.
*
* We cannot use here ExFreePoolWithTag(..., OB_NAME_TAG); , because
* the object name may have been massaged during operation by different
* object parse routines. If the latter ones have to resolve a symbolic
* link (e.g. as is done by CmpParseKey() and CmpGetSymbolicLink()),
* the original object name is freed and re-allocated from the pool,
* possibly with a different pool tag. At the end of the day, the new
* object name can be reallocated and completely different, but we
* should still be able to free it!
*/
ExFreePool(Buffer);
}
else
{
/* Otherwise, free from the lookaside */
ObpFreeCapturedAttributes(Buffer, LookasideNameBufferList);
}
}
NTSTATUS
NTAPI
ObpCaptureObjectName(IN OUT PUNICODE_STRING CapturedName,
IN PUNICODE_STRING ObjectName,
IN KPROCESSOR_MODE AccessMode,
IN BOOLEAN UseLookaside)
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG StringLength;
PWCHAR _SEH2_VOLATILE StringBuffer = NULL;
UNICODE_STRING LocalName;
PAGED_CODE();
/* Initialize the Input String */
RtlInitEmptyUnicodeString(CapturedName, NULL, 0);
/* Protect everything */
_SEH2_TRY
{
/* Check if we came from user mode */
if (AccessMode != KernelMode)
{
/* First Probe the String */
LocalName = ProbeForReadUnicodeString(ObjectName);
ProbeForRead(LocalName.Buffer, LocalName.Length, sizeof(WCHAR));
}
else
{
/* No probing needed */
LocalName = *ObjectName;
}
/* Make sure there really is a string */
StringLength = LocalName.Length;
if (StringLength)
{
/* Check that the size is a valid WCHAR multiple */
if ((StringLength & (sizeof(WCHAR) - 1)) ||
/* Check that the NULL-termination below will work */
(StringLength == (MAXUSHORT - sizeof(UNICODE_NULL) + 1)))
{
/* PS: Please keep the checks above expanded for clarity */
Status = STATUS_OBJECT_NAME_INVALID;
}
else
{
/* Allocate the string buffer */
StringBuffer = ObpAllocateObjectNameBuffer(StringLength,
UseLookaside,
CapturedName);
if (!StringBuffer)
{
/* Set failure code */
Status = STATUS_INSUFFICIENT_RESOURCES;
}
else
{
/* Copy the name */
RtlCopyMemory(StringBuffer, LocalName.Buffer, StringLength);
StringBuffer[StringLength / sizeof(WCHAR)] = UNICODE_NULL;
}
}
}
}
_SEH2_EXCEPT(ExSystemExceptionFilter())
{
/* Handle exception and free the string buffer */
Status = _SEH2_GetExceptionCode();
if (StringBuffer)
{
ObpFreeObjectNameBuffer(CapturedName);
}
}
_SEH2_END;
/* Return */
return Status;
}
NTSTATUS
NTAPI
ObpCaptureObjectCreateInformation(IN POBJECT_ATTRIBUTES ObjectAttributes,
IN KPROCESSOR_MODE AccessMode,
IN KPROCESSOR_MODE CreatorMode,
IN BOOLEAN AllocateFromLookaside,
IN POBJECT_CREATE_INFORMATION ObjectCreateInfo,
OUT PUNICODE_STRING ObjectName)
{
ULONG SdCharge, QuotaInfoSize;
NTSTATUS Status = STATUS_SUCCESS;
PSECURITY_DESCRIPTOR SecurityDescriptor;
PSECURITY_QUALITY_OF_SERVICE SecurityQos;
PUNICODE_STRING LocalObjectName = NULL;
PAGED_CODE();
/* Zero out the Capture Data */
RtlZeroMemory(ObjectCreateInfo, sizeof(OBJECT_CREATE_INFORMATION));
/* SEH everything here for protection */
_SEH2_TRY
{
/* Check if we got attributes */
if (ObjectAttributes)
{
/* Check if we're in user mode */
if (AccessMode != KernelMode)
{
/* Probe the attributes */
ProbeForRead(ObjectAttributes,
sizeof(OBJECT_ATTRIBUTES),
sizeof(ULONG));
}
/* Validate the Size and Attributes */
if ((ObjectAttributes->Length != sizeof(OBJECT_ATTRIBUTES)) ||
(ObjectAttributes->Attributes & ~OBJ_VALID_KERNEL_ATTRIBUTES))
{
/* Invalid combination, fail */
_SEH2_YIELD(return STATUS_INVALID_PARAMETER);
}
/* Set some Create Info and do not allow user-mode kernel handles */
ObjectCreateInfo->RootDirectory = ObjectAttributes->RootDirectory;
ObjectCreateInfo->Attributes = ObjectAttributes->Attributes & OBJ_VALID_KERNEL_ATTRIBUTES;
if (CreatorMode != KernelMode) ObjectCreateInfo->Attributes &= ~OBJ_KERNEL_HANDLE;
LocalObjectName = ObjectAttributes->ObjectName;
SecurityDescriptor = ObjectAttributes->SecurityDescriptor;
SecurityQos = ObjectAttributes->SecurityQualityOfService;
/* Check if we have a security descriptor */
if (SecurityDescriptor)
{
/* Capture it. Note: This has an implicit memory barrier due
to the function call, so cleanup is safe here.) */
Status = SeCaptureSecurityDescriptor(SecurityDescriptor,
AccessMode,
NonPagedPool,
TRUE,
&ObjectCreateInfo->
SecurityDescriptor);
if (!NT_SUCCESS(Status))
{
/* Capture failed, quit */
ObjectCreateInfo->SecurityDescriptor = NULL;
_SEH2_YIELD(return Status);
}
/*
* By default, assume a SD size of 1024 and allow twice its
* size.
* If SD size happen to be bigger than that, then allow it
*/
SdCharge = 2048;
SeComputeQuotaInformationSize(ObjectCreateInfo->SecurityDescriptor,
&QuotaInfoSize);
if ((2 * QuotaInfoSize) > 2048)
{
SdCharge = 2 * QuotaInfoSize;
}
/* Save the probe mode and security descriptor size */
ObjectCreateInfo->SecurityDescriptorCharge = SdCharge;
ObjectCreateInfo->ProbeMode = AccessMode;
}
/* Check if we have QoS */
if (SecurityQos)
{
/* Check if we came from user mode */
if (AccessMode != KernelMode)
{
/* Validate the QoS */
ProbeForRead(SecurityQos,
sizeof(SECURITY_QUALITY_OF_SERVICE),
sizeof(ULONG));
}
/* Save Info */
ObjectCreateInfo->SecurityQualityOfService = *SecurityQos;
ObjectCreateInfo->SecurityQos =
&ObjectCreateInfo->SecurityQualityOfService;
}
}
else
{
/* We don't have a name */
LocalObjectName = NULL;
}
}
_SEH2_EXCEPT(ExSystemExceptionFilter())
{
/* Cleanup and return the exception code */
ObpReleaseObjectCreateInformation(ObjectCreateInfo);
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
/* Now check if the Object Attributes had an Object Name */
if (LocalObjectName)
{
Status = ObpCaptureObjectName(ObjectName,
LocalObjectName,
AccessMode,
AllocateFromLookaside);
}
else
{
/* Clear the string */
RtlInitEmptyUnicodeString(ObjectName, NULL, 0);
/* It cannot have specified a Root Directory */
if (ObjectCreateInfo->RootDirectory)
{
Status = STATUS_OBJECT_NAME_INVALID;
}
}
/* Cleanup if we failed */
if (!NT_SUCCESS(Status))
{
ObpReleaseObjectCreateInformation(ObjectCreateInfo);
}
/* Return status to caller */
return Status;
}
VOID
NTAPI
ObFreeObjectCreateInfoBuffer(IN POBJECT_CREATE_INFORMATION ObjectCreateInfo)
{
/* Call the macro. We use this function to isolate Ob internals from Io */
ObpFreeCapturedAttributes(ObjectCreateInfo, LookasideCreateInfoList);
}
NTSTATUS
NTAPI
ObpAllocateObject(IN POBJECT_CREATE_INFORMATION ObjectCreateInfo,
IN PUNICODE_STRING ObjectName,
IN POBJECT_TYPE ObjectType,
IN ULONG ObjectSize,
IN KPROCESSOR_MODE PreviousMode,
IN POBJECT_HEADER *ObjectHeader)
{
POBJECT_HEADER Header;
ULONG QuotaSize, HandleSize, NameSize, CreatorSize;
POBJECT_HEADER_HANDLE_INFO HandleInfo;
POBJECT_HEADER_NAME_INFO NameInfo;
POBJECT_HEADER_CREATOR_INFO CreatorInfo;
POBJECT_HEADER_QUOTA_INFO QuotaInfo;
POOL_TYPE PoolType;
ULONG FinalSize;
ULONG Tag;
PAGED_CODE();
/* Accounting */
ObpObjectsCreated++;
/* Check if we don't have an Object Type yet */
if (!ObjectType)
{
/* Use default tag and non-paged pool */
PoolType = NonPagedPool;
Tag = TAG_OBJECT_TYPE;
}
else
{
/* Use the pool and tag given */
PoolType = ObjectType->TypeInfo.PoolType;
Tag = ObjectType->Key;
}
/* Check if we have no create information (ie: we're an object type) */
if (!ObjectCreateInfo)
{
/* Use defaults */
QuotaSize = HandleSize = 0;
NameSize = sizeof(OBJECT_HEADER_NAME_INFO);
CreatorSize = sizeof(OBJECT_HEADER_CREATOR_INFO);
}
else
{
/* Check if we have quota */
if ((((ObjectCreateInfo->PagedPoolCharge !=
ObjectType->TypeInfo.DefaultPagedPoolCharge) ||
(ObjectCreateInfo->NonPagedPoolCharge !=
ObjectType->TypeInfo.DefaultNonPagedPoolCharge) ||
(ObjectCreateInfo->SecurityDescriptorCharge > 2048)) &&
(PsGetCurrentProcess() != PsInitialSystemProcess)) ||
(ObjectCreateInfo->Attributes & OBJ_EXCLUSIVE))
{
/* Set quota size */
QuotaSize = sizeof(OBJECT_HEADER_QUOTA_INFO);
ObpObjectsWithPoolQuota++;
}
else
{
/* No Quota */
QuotaSize = 0;
}
/* Check if we have a handle database */
if (ObjectType->TypeInfo.MaintainHandleCount)
{
/* Set handle database size */
HandleSize = sizeof(OBJECT_HEADER_HANDLE_INFO);
ObpObjectsWithHandleDB++;
}
else
{
/* None */
HandleSize = 0;
}
/* Check if the Object has a name */
if (ObjectName->Buffer)
{
/* Set name size */
NameSize = sizeof(OBJECT_HEADER_NAME_INFO);
ObpObjectsWithName++;
}
else
{
/* No name */
NameSize = 0;
}
/* Check if the Object maintains type lists */
if (ObjectType->TypeInfo.MaintainTypeList)
{
/* Set owner/creator size */
CreatorSize = sizeof(OBJECT_HEADER_CREATOR_INFO);
ObpObjectsWithCreatorInfo++;
}
else
{
/* No info */
CreatorSize = 0;
}
}
/* Set final header size */
FinalSize = QuotaSize +
HandleSize +
NameSize +
CreatorSize +
FIELD_OFFSET(OBJECT_HEADER, Body);
/* Allocate memory for the Object and Header */
Header = ExAllocatePoolWithTag(PoolType, FinalSize + ObjectSize, Tag);
if (!Header) return STATUS_INSUFFICIENT_RESOURCES;
/* Check if we have a quota header */
if (QuotaSize)
{
/* Initialize quota info */
QuotaInfo = (POBJECT_HEADER_QUOTA_INFO)Header;
QuotaInfo->PagedPoolCharge = ObjectCreateInfo->PagedPoolCharge;
QuotaInfo->NonPagedPoolCharge = ObjectCreateInfo->NonPagedPoolCharge;
QuotaInfo->SecurityDescriptorCharge = ObjectCreateInfo->SecurityDescriptorCharge;
QuotaInfo->ExclusiveProcess = NULL;
Header = (POBJECT_HEADER)(QuotaInfo + 1);
}
/* Check if we have a handle database header */
if (HandleSize)
{
/* Initialize Handle Info */
HandleInfo = (POBJECT_HEADER_HANDLE_INFO)Header;
HandleInfo->SingleEntry.HandleCount = 0;
Header = (POBJECT_HEADER)(HandleInfo + 1);
}
/* Check if we have a name header */
if (NameSize)
{
/* Initialize the Object Name Info */
NameInfo = (POBJECT_HEADER_NAME_INFO)Header;
NameInfo->Name = *ObjectName;
NameInfo->Directory = NULL;
NameInfo->QueryReferences = 1;
/* Check if this is a call with the special protection flag */
if ((PreviousMode == KernelMode) &&
(ObjectCreateInfo) &&
(ObjectCreateInfo->Attributes & OBJ_KERNEL_EXCLUSIVE))
{
/* Set flag which will make the object protected from user-mode */
NameInfo->QueryReferences |= OB_FLAG_KERNEL_EXCLUSIVE;
}
/* Set the header pointer */
Header = (POBJECT_HEADER)(NameInfo + 1);
}
/* Check if we have a creator header */
if (CreatorSize)
{
/* Initialize Creator Info */
CreatorInfo = (POBJECT_HEADER_CREATOR_INFO)Header;
CreatorInfo->CreatorBackTraceIndex = 0;
CreatorInfo->CreatorUniqueProcess = PsGetCurrentProcessId();
InitializeListHead(&CreatorInfo->TypeList);
Header = (POBJECT_HEADER)(CreatorInfo + 1);
}
/* Check for quota information */
if (QuotaSize)
{
/* Set the offset */
Header->QuotaInfoOffset = (UCHAR)(QuotaSize +
HandleSize +
NameSize +
CreatorSize);
}
else
{
/* No offset */
Header->QuotaInfoOffset = 0;
}
/* Check for handle information */
if (HandleSize)
{
/* Set the offset */
Header->HandleInfoOffset = (UCHAR)(HandleSize +
NameSize +
CreatorSize);
}
else
{
/* No offset */
Header->HandleInfoOffset = 0;
}
/* Check for name information */
if (NameSize)
{
/* Set the offset */
Header->NameInfoOffset = (UCHAR)(NameSize + CreatorSize);
}
else
{
/* No Name */
Header->NameInfoOffset = 0;
}
/* Set the new object flag */
Header->Flags = OB_FLAG_CREATE_INFO;
/* Remember if we have creator info */
if (CreatorSize) Header->Flags |= OB_FLAG_CREATOR_INFO;
/* Remember if we have handle info */
if (HandleSize) Header->Flags |= OB_FLAG_SINGLE_PROCESS;
/* Initialize the object header */
Header->PointerCount = 1;
Header->HandleCount = 0;
Header->Type = ObjectType;
Header->ObjectCreateInfo = ObjectCreateInfo;
Header->SecurityDescriptor = NULL;
/* Check if this is a permanent object */
if ((ObjectCreateInfo) && (ObjectCreateInfo->Attributes & OBJ_PERMANENT))
{
/* Set the needed flag so we can check */
Header->Flags |= OB_FLAG_PERMANENT;
}
/* Check if this is an exclusive object */
if ((ObjectCreateInfo) && (ObjectCreateInfo->Attributes & OBJ_EXCLUSIVE))
{
/* Set the needed flag so we can check */
Header->Flags |= OB_FLAG_EXCLUSIVE;
}
/* Set kernel-mode flag */
if (PreviousMode == KernelMode) Header->Flags |= OB_FLAG_KERNEL_MODE;
/* Check if we have a type */
if (ObjectType)
{
/* Increase the number of objects of this type */
InterlockedIncrement((PLONG)&ObjectType->TotalNumberOfObjects);
/* Update the high water */
ObjectType->HighWaterNumberOfObjects = max(ObjectType->
TotalNumberOfObjects,
ObjectType->
HighWaterNumberOfObjects);
}
/* Return Header */
*ObjectHeader = Header;
return STATUS_SUCCESS;
}
/**
* @brief
* Queries the name info size of a given resource object.
* The function loops through all the parent directories
* of the object and computes the name size.
*
* @param[in] ObjectHeader
* A pointer to an object header, of which name and
* directory info are to be retrieved.
*
* @return
* Returns the name info size that is pointed by the
* given object by the caller of this function. If
* an object does not have a name or no directories,
* it returns 0.
*/
static
ULONG
ObpQueryNameInfoSize(
_In_ POBJECT_HEADER ObjectHeader)
{
ULONG NameSize = 0;
POBJECT_DIRECTORY ParentDirectory;
POBJECT_HEADER_NAME_INFO NameInfo;
PAGED_CODE();
/* Get the name info */
NameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
if (!NameInfo)
{
return 0;
}
/* Get the parent directory from the object name too */
ParentDirectory = NameInfo->Directory;
if (!ParentDirectory)
{
return 0;
}
/* Take into account the name size of this object and loop for all parent directories */
NameSize = sizeof(OBJ_NAME_PATH_SEPARATOR) + NameInfo->Name.Length;
for (;;)
{
/* Get the name info from the parent directory */
NameInfo = OBJECT_HEADER_TO_NAME_INFO(
OBJECT_TO_OBJECT_HEADER(ParentDirectory));
if (!NameInfo)
{
/* Stop looking if this is the last one */
break;
}
/* Get the parent directory */
ParentDirectory = NameInfo->Directory;
if (!ParentDirectory)
{
/* This is the last directory, stop looking */
break;
}
/*
* Take into account the size of this name info,
* keep looking for other parent directories.
*/
NameSize += sizeof(OBJ_NAME_PATH_SEPARATOR) + NameInfo->Name.Length;
}
/* Include the size of the object name information as well as the NULL terminator */
NameSize += sizeof(OBJECT_NAME_INFORMATION) + sizeof(UNICODE_NULL);
return NameSize;
}
NTSTATUS
NTAPI
ObQueryTypeInfo(IN POBJECT_TYPE ObjectType,
OUT POBJECT_TYPE_INFORMATION ObjectTypeInfo,
IN ULONG Length,
OUT PULONG ReturnLength)
{
NTSTATUS Status = STATUS_SUCCESS;
PWSTR InfoBuffer;
/* Enter SEH */
_SEH2_TRY
{
/* Set return length aligned to 4-byte boundary */
*ReturnLength += sizeof(*ObjectTypeInfo) +
ALIGN_UP(ObjectType->Name.MaximumLength, ULONG);
/* Check if thats too much though. */
if (Length < *ReturnLength)
{
_SEH2_YIELD(return STATUS_INFO_LENGTH_MISMATCH);
}
/* Build the data */
ObjectTypeInfo->TotalNumberOfHandles =
ObjectType->TotalNumberOfHandles;
ObjectTypeInfo->TotalNumberOfObjects =
ObjectType->TotalNumberOfObjects;
ObjectTypeInfo->HighWaterNumberOfHandles =
ObjectType->HighWaterNumberOfHandles;
ObjectTypeInfo->HighWaterNumberOfObjects =
ObjectType->HighWaterNumberOfObjects;
ObjectTypeInfo->PoolType =
ObjectType->TypeInfo.PoolType;
ObjectTypeInfo->DefaultNonPagedPoolCharge =
ObjectType->TypeInfo.DefaultNonPagedPoolCharge;
ObjectTypeInfo->DefaultPagedPoolCharge =
ObjectType->TypeInfo.DefaultPagedPoolCharge;
ObjectTypeInfo->ValidAccessMask =
ObjectType->TypeInfo.ValidAccessMask;
ObjectTypeInfo->SecurityRequired =
ObjectType->TypeInfo.SecurityRequired;
ObjectTypeInfo->InvalidAttributes =
ObjectType->TypeInfo.InvalidAttributes;
ObjectTypeInfo->GenericMapping =
ObjectType->TypeInfo.GenericMapping;
ObjectTypeInfo->MaintainHandleCount =
ObjectType->TypeInfo.MaintainHandleCount;
/* Setup the name buffer */
InfoBuffer = (PWSTR)(ObjectTypeInfo + 1);
ObjectTypeInfo->TypeName.Buffer = InfoBuffer;
ObjectTypeInfo->TypeName.MaximumLength = ObjectType->Name.MaximumLength;
ObjectTypeInfo->TypeName.Length = ObjectType->Name.Length;
/* Copy it */
RtlCopyMemory(InfoBuffer,
ObjectType->Name.Buffer,
ObjectType->Name.Length);
/* Null-terminate it */
(InfoBuffer)[ObjectType->Name.Length / sizeof(WCHAR)] = UNICODE_NULL;
}
_SEH2_EXCEPT(ExSystemExceptionFilter())
{
/* Otherwise, get the exception code */
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
/* Return status to caller */
return Status;
}
/* PUBLIC FUNCTIONS **********************************************************/
NTSTATUS
NTAPI
ObCreateObject(IN KPROCESSOR_MODE ProbeMode OPTIONAL,
IN POBJECT_TYPE Type,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN KPROCESSOR_MODE AccessMode,
IN OUT PVOID ParseContext OPTIONAL,
IN ULONG ObjectSize,
IN ULONG PagedPoolCharge OPTIONAL,
IN ULONG NonPagedPoolCharge OPTIONAL,
OUT PVOID *Object)
{
NTSTATUS Status;
POBJECT_CREATE_INFORMATION ObjectCreateInfo;
UNICODE_STRING ObjectName;
POBJECT_HEADER Header;
/* Allocate a capture buffer */
ObjectCreateInfo = ObpAllocateObjectCreateInfoBuffer(LookasideCreateInfoList);
if (!ObjectCreateInfo) return STATUS_INSUFFICIENT_RESOURCES;
/* Capture all the info */
Status = ObpCaptureObjectCreateInformation(ObjectAttributes,
ProbeMode,
AccessMode,
FALSE,
ObjectCreateInfo,
&ObjectName);
if (NT_SUCCESS(Status))
{
/* Validate attributes */
if (Type->TypeInfo.InvalidAttributes & ObjectCreateInfo->Attributes)
{
/* Fail */
Status = STATUS_INVALID_PARAMETER;
}
else
{
/* Check if we have a paged charge */
if (!PagedPoolCharge)
{
/* Save it */
PagedPoolCharge = Type->TypeInfo.DefaultPagedPoolCharge;
}
/* Check for nonpaged charge */
if (!NonPagedPoolCharge)
{
/* Save it */
NonPagedPoolCharge = Type->TypeInfo.DefaultNonPagedPoolCharge;
}
/* Write the pool charges */
ObjectCreateInfo->PagedPoolCharge = PagedPoolCharge;
ObjectCreateInfo->NonPagedPoolCharge = NonPagedPoolCharge;
/* Allocate the Object */
Status = ObpAllocateObject(ObjectCreateInfo,
&ObjectName,
Type,
ObjectSize,
AccessMode,
&Header);
if (NT_SUCCESS(Status))
{
/* Return the Object */
*Object = &Header->Body;
/* Check if this is a permanent object */
if (Header->Flags & OB_FLAG_PERMANENT)
{
/* Do the privilege check */
if (!SeSinglePrivilegeCheck(SeCreatePermanentPrivilege,
ProbeMode))
{
/* Fail */
ObpDeallocateObject(*Object);
Status = STATUS_PRIVILEGE_NOT_HELD;
}
}
/* Return status */
return Status;
}
}
/* Release the Capture Info, we don't need it */
ObpFreeObjectCreateInformation(ObjectCreateInfo);
if (ObjectName.Buffer) ObpFreeObjectNameBuffer(&ObjectName);
return Status;
}
/* We failed, so release the Buffer */
ObpFreeCapturedAttributes(ObjectCreateInfo, LookasideCreateInfoList);
return Status;
}
NTSTATUS
NTAPI
ObCreateObjectType(IN PUNICODE_STRING TypeName,
IN POBJECT_TYPE_INITIALIZER ObjectTypeInitializer,
IN PVOID Reserved,
OUT POBJECT_TYPE *ObjectType)
{
POBJECT_HEADER Header;
POBJECT_TYPE LocalObjectType;
ULONG HeaderSize;
NTSTATUS Status;
OBP_LOOKUP_CONTEXT Context;
PWCHAR p;
ULONG i;
UNICODE_STRING ObjectName;
ANSI_STRING AnsiName;
POBJECT_HEADER_CREATOR_INFO CreatorInfo;
/* Verify parameters */
if (!(TypeName) ||
!(TypeName->Length) ||
(TypeName->Length % sizeof(WCHAR)) ||
!(ObjectTypeInitializer) ||
(ObjectTypeInitializer->Length != sizeof(*ObjectTypeInitializer)) ||
(ObjectTypeInitializer->InvalidAttributes & ~OBJ_VALID_KERNEL_ATTRIBUTES) ||
(ObjectTypeInitializer->MaintainHandleCount &&
(!(ObjectTypeInitializer->OpenProcedure) &&
!ObjectTypeInitializer->CloseProcedure)) ||
((!ObjectTypeInitializer->UseDefaultObject) &&
(ObjectTypeInitializer->PoolType != NonPagedPool)))
{
/* Fail */
return STATUS_INVALID_PARAMETER;
}
/* Make sure the name doesn't have a separator */
p = TypeName->Buffer;
i = TypeName->Length / sizeof(WCHAR);
while (i--)
{
/* Check for one and fail */
if (*p++ == OBJ_NAME_PATH_SEPARATOR) return STATUS_OBJECT_NAME_INVALID;
}
/* Setup a lookup context */
ObpInitializeLookupContext(&Context);
/* Check if we've already created the directory of types */
if (ObpTypeDirectoryObject)
{
/* Lock the lookup context */
ObpAcquireLookupContextLock(&Context, ObpTypeDirectoryObject);
/* Do the lookup */
if (ObpLookupEntryDirectory(ObpTypeDirectoryObject,
TypeName,
OBJ_CASE_INSENSITIVE,
FALSE,
&Context))
{
/* We have already created it, so fail */
ObpReleaseLookupContext(&Context);
return STATUS_OBJECT_NAME_COLLISION;
}
}
/* Now make a copy of the object name */
ObjectName.Buffer = ExAllocatePoolWithTag(PagedPool,
TypeName->MaximumLength,
OB_NAME_TAG);
if (!ObjectName.Buffer)
{
/* Out of memory, fail */
ObpReleaseLookupContext(&Context);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Set the length and copy the name */
ObjectName.MaximumLength = TypeName->MaximumLength;
RtlCopyUnicodeString(&ObjectName, TypeName);
/* Allocate the Object */
Status = ObpAllocateObject(NULL,
&ObjectName,
ObpTypeObjectType,
sizeof(OBJECT_TYPE),
KernelMode,
&Header);
if (!NT_SUCCESS(Status))
{
/* Free the name and fail */
ObpReleaseLookupContext(&Context);
ExFreePool(ObjectName.Buffer);
return Status;
}
/* Setup the flags and name */
LocalObjectType = (POBJECT_TYPE)&Header->Body;
LocalObjectType->Name = ObjectName;
Header->Flags |= OB_FLAG_KERNEL_MODE | OB_FLAG_PERMANENT;
/* Clear accounting data */
LocalObjectType->TotalNumberOfObjects =
LocalObjectType->TotalNumberOfHandles =
LocalObjectType->HighWaterNumberOfObjects =
LocalObjectType->HighWaterNumberOfHandles = 0;
/* Check if this is the first Object Type */
if (!ObpTypeObjectType)
{
/* It is, so set this as the type object */
ObpTypeObjectType = LocalObjectType;
Header->Type = ObpTypeObjectType;
/* Set the hard-coded key and object count */
LocalObjectType->TotalNumberOfObjects = 1;
LocalObjectType->Key = TAG_OBJECT_TYPE;
}
else
{
/* Convert the tag to ASCII */
Status = RtlUnicodeStringToAnsiString(&AnsiName, TypeName, TRUE);
if (NT_SUCCESS(Status))
{
/* For every missing character, use a space */
for (i = 3; i >= AnsiName.Length; i--) AnsiName.Buffer[i] = ' ';
/* Set the key and free the converted name */
LocalObjectType->Key = *(PULONG)AnsiName.Buffer;
RtlFreeAnsiString(&AnsiName);
}
else
{
/* Just copy the characters */
LocalObjectType->Key = *(PULONG)TypeName->Buffer;
}
}
/* Set up the type information */
LocalObjectType->TypeInfo = *ObjectTypeInitializer;
LocalObjectType->TypeInfo.PoolType = ObjectTypeInitializer->PoolType;
/* Check if we have to maintain a type list */
if (NtGlobalFlag & FLG_MAINTAIN_OBJECT_TYPELIST)
{
/* Enable support */
LocalObjectType->TypeInfo.MaintainTypeList = TRUE;
}
/* Calculate how much space our header'll take up */
HeaderSize = sizeof(OBJECT_HEADER) +
sizeof(OBJECT_HEADER_NAME_INFO) +
(ObjectTypeInitializer->MaintainHandleCount ?
sizeof(OBJECT_HEADER_HANDLE_INFO) : 0);
/* Check the pool type */
if (ObjectTypeInitializer->PoolType == NonPagedPool)
{
/* Update the NonPaged Pool charge */
LocalObjectType->TypeInfo.DefaultNonPagedPoolCharge += HeaderSize;
}
else
{
/* Update the Paged Pool charge */
LocalObjectType->TypeInfo.DefaultPagedPoolCharge += HeaderSize;
}
/* All objects types need a security procedure */
if (!ObjectTypeInitializer->SecurityProcedure)
{
LocalObjectType->TypeInfo.SecurityProcedure = SeDefaultObjectMethod;
}
/* Select the Wait Object */
if (LocalObjectType->TypeInfo.UseDefaultObject)
{
/* Add the SYNCHRONIZE access mask since it's waitable */
LocalObjectType->TypeInfo.ValidAccessMask |= SYNCHRONIZE;
/* Use the "Default Object", a simple event */
LocalObjectType->DefaultObject = &ObpDefaultObject;
}
/* The File Object gets an optimized hack so it can be waited on */
else if ((TypeName->Length == 8) && !(wcscmp(TypeName->Buffer, L"File")))
{
/* Wait on the File Object's event directly */
LocalObjectType->DefaultObject = UlongToPtr(FIELD_OFFSET(FILE_OBJECT,
Event));
}
else if ((TypeName->Length == 24) && !(wcscmp(TypeName->Buffer, L"WaitablePort")))
{
/* Wait on the LPC Port's object directly */
LocalObjectType->DefaultObject = UlongToPtr(FIELD_OFFSET(LPCP_PORT_OBJECT,
WaitEvent));
}
else
{
/* No default Object */
LocalObjectType->DefaultObject = NULL;
}
/* Initialize Object Type components */
ExInitializeResourceLite(&LocalObjectType->Mutex);
for (i = 0; i < 4; i++)
{
/* Initialize the object locks */
ExInitializeResourceLite(&LocalObjectType->ObjectLocks[i]);
}
InitializeListHead(&LocalObjectType->TypeList);
/* Lock the object type */
ObpEnterObjectTypeMutex(ObpTypeObjectType);
/* Get creator info and insert it into the type list */
CreatorInfo = OBJECT_HEADER_TO_CREATOR_INFO(Header);
if (CreatorInfo)
{
InsertTailList(&ObpTypeObjectType->TypeList,
&CreatorInfo->TypeList);
/* CORE-8423: Avoid inserting this a second time if someone creates a
* handle to the object type (bug in Windows 2003) */
Header->Flags &= ~OB_FLAG_CREATE_INFO;
}
/* Set the index and the entry into the object type array */
LocalObjectType->Index = ObpTypeObjectType->TotalNumberOfObjects;
ASSERT(LocalObjectType->Index != 0);
if (LocalObjectType->Index < RTL_NUMBER_OF(ObpObjectTypes))
{
/* It fits, insert it */
ObpObjectTypes[LocalObjectType->Index - 1] = LocalObjectType;
}
/* Release the object type */
ObpLeaveObjectTypeMutex(ObpTypeObjectType);
/* Check if we're actually creating the directory object itself */
if (!(ObpTypeDirectoryObject) ||
(ObpInsertEntryDirectory(ObpTypeDirectoryObject, &Context, Header)))
{
/* Check if the type directory exists */
if (ObpTypeDirectoryObject)
{
/* Reference it */
ObReferenceObject(ObpTypeDirectoryObject);
}
/* Cleanup the lookup context */
ObpReleaseLookupContext(&Context);
/* Return the object type and success */
*ObjectType = LocalObjectType;
return STATUS_SUCCESS;
}
/* If we got here, then we failed */
ObpReleaseLookupContext(&Context);
return STATUS_INSUFFICIENT_RESOURCES;
}
VOID
NTAPI
ObDeleteCapturedInsertInfo(IN PVOID Object)
{
POBJECT_HEADER ObjectHeader;
PAGED_CODE();
/* Check if there is anything to free */
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
if ((ObjectHeader->Flags & OB_FLAG_CREATE_INFO) &&
(ObjectHeader->ObjectCreateInfo != NULL))
{
/* Free the create info */
ObpFreeObjectCreateInformation(ObjectHeader->ObjectCreateInfo);
ObjectHeader->ObjectCreateInfo = NULL;
}
}
VOID
NTAPI
ObpDeleteObjectType(IN PVOID Object)
{
ULONG i;
POBJECT_TYPE ObjectType = (PVOID)Object;
/* Loop our locks */
for (i = 0; i < 4; i++)
{
/* Delete each one */
ExDeleteResourceLite(&ObjectType->ObjectLocks[i]);
}
/* Delete our main mutex */
ExDeleteResourceLite(&ObjectType->Mutex);
}
/*++
* @name ObMakeTemporaryObject
* @implemented NT4
*
* The ObMakeTemporaryObject routine <FILLMEIN>
*
* @param ObjectBody
* <FILLMEIN>
*
* @return None.
*
* @remarks None.
*
*--*/
VOID
NTAPI
ObMakeTemporaryObject(IN PVOID ObjectBody)
{
PAGED_CODE();
/* Call the internal API */
ObpSetPermanentObject(ObjectBody, FALSE);
}
/*++
* @name NtMakeTemporaryObject
* @implemented NT4
*
* The NtMakeTemporaryObject routine <FILLMEIN>
*
* @param ObjectHandle
* <FILLMEIN>
*
* @return STATUS_SUCCESS or appropriate error value.
*
* @remarks None.
*
*--*/
NTSTATUS
NTAPI
NtMakeTemporaryObject(IN HANDLE ObjectHandle)
{
PVOID ObjectBody;
NTSTATUS Status;
PAGED_CODE();
/* Reference the object for DELETE access */
Status = ObReferenceObjectByHandle(ObjectHandle,
DELETE,
NULL,
KeGetPreviousMode(),
&ObjectBody,
NULL);
if (Status != STATUS_SUCCESS) return Status;
/* Set it as temporary and dereference it */
ObpSetPermanentObject(ObjectBody, FALSE);
ObDereferenceObject(ObjectBody);
return STATUS_SUCCESS;
}
/*++
* @name NtMakePermanentObject
* @implemented NT4
*
* The NtMakePermanentObject routine <FILLMEIN>
*
* @param ObjectHandle
* <FILLMEIN>
*
* @return STATUS_SUCCESS or appropriate error value.
*
* @remarks None.
*
*--*/
NTSTATUS
NTAPI
NtMakePermanentObject(IN HANDLE ObjectHandle)
{
PVOID ObjectBody;
NTSTATUS Status;
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
PAGED_CODE();
/* Make sure that the caller has SeCreatePermanentPrivilege */
if (!SeSinglePrivilegeCheck(SeCreatePermanentPrivilege, PreviousMode))
{
return STATUS_PRIVILEGE_NOT_HELD;
}
/* Reference the object */
Status = ObReferenceObjectByHandle(ObjectHandle,
0,
NULL,
PreviousMode,
&ObjectBody,
NULL);
if (Status != STATUS_SUCCESS) return Status;
/* Set it as permanent and dereference it */
ObpSetPermanentObject(ObjectBody, TRUE);
ObDereferenceObject(ObjectBody);
return STATUS_SUCCESS;
}
/*++
* @name NtQueryObject
* @implemented NT4
*
* The NtQueryObject routine <FILLMEIN>
*
* @param ObjectHandle
* <FILLMEIN>
*
* @param ObjectInformationClass
* <FILLMEIN>
*
* @param ObjectInformation
* <FILLMEIN>
*
* @param Length
* <FILLMEIN>
*
* @param ResultLength
* <FILLMEIN>
*
* @return STATUS_SUCCESS or appropriate error value.
*
* @remarks None.
*
*--*/
NTSTATUS
NTAPI
NtQueryObject(IN HANDLE ObjectHandle,
IN OBJECT_INFORMATION_CLASS ObjectInformationClass,
OUT PVOID ObjectInformation,
IN ULONG Length,
OUT PULONG ResultLength OPTIONAL)
{
OBJECT_HANDLE_INFORMATION HandleInfo;
POBJECT_HEADER ObjectHeader = NULL;
POBJECT_HANDLE_ATTRIBUTE_INFORMATION HandleFlags;
POBJECT_BASIC_INFORMATION BasicInfo;
ULONG InfoLength = 0;
PVOID Object = NULL;
NTSTATUS Status;
POBJECT_HEADER_QUOTA_INFO ObjectQuota;
SECURITY_INFORMATION SecurityInformation;
POBJECT_TYPE ObjectType;
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
PAGED_CODE();
/* Check if the caller is from user mode */
if (PreviousMode != KernelMode)
{
/* Protect validation with SEH */
_SEH2_TRY
{
/* Probe the input structure */
ProbeForWrite(ObjectInformation, Length, sizeof(UCHAR));
/* If we have a result length, probe it too */
if (ResultLength) ProbeForWriteUlong(ResultLength);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
/*
* Make sure this isn't a generic type query, since the caller doesn't
* have to give a handle for it
*/
if (ObjectInformationClass != ObjectTypesInformation)
{
/* Reference the object */
Status = ObReferenceObjectByHandle(ObjectHandle,
0,
NULL,
KeGetPreviousMode(),
&Object,
&HandleInfo);
if (!NT_SUCCESS (Status)) return Status;
/* Get the object header */
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
ObjectType = ObjectHeader->Type;
}
_SEH2_TRY
{
/* Check the information class */
switch (ObjectInformationClass)
{
/* Basic info */
case ObjectBasicInformation:
/* Validate length */
InfoLength = sizeof(OBJECT_BASIC_INFORMATION);
if (Length != sizeof(OBJECT_BASIC_INFORMATION))
{
/* Fail */
Status = STATUS_INFO_LENGTH_MISMATCH;
break;
}
/* Fill out the basic information */
BasicInfo = (POBJECT_BASIC_INFORMATION)ObjectInformation;
BasicInfo->Attributes = HandleInfo.HandleAttributes;
BasicInfo->GrantedAccess = HandleInfo.GrantedAccess;
BasicInfo->HandleCount = ObjectHeader->HandleCount;
BasicInfo->PointerCount = ObjectHeader->PointerCount;
/* Permanent/Exclusive Flags are NOT in Handle attributes! */
if (ObjectHeader->Flags & OB_FLAG_EXCLUSIVE)
{
/* Set the flag */
BasicInfo->Attributes |= OBJ_EXCLUSIVE;
}
if (ObjectHeader->Flags & OB_FLAG_PERMANENT)
{
/* Set the flag */
BasicInfo->Attributes |= OBJ_PERMANENT;
}
/* Copy quota information */
ObjectQuota = OBJECT_HEADER_TO_QUOTA_INFO(ObjectHeader);
if (ObjectQuota != NULL)
{
BasicInfo->PagedPoolCharge = ObjectQuota->PagedPoolCharge;
BasicInfo->NonPagedPoolCharge = ObjectQuota->NonPagedPoolCharge;
}
else
{
BasicInfo->PagedPoolCharge = 0;
BasicInfo->NonPagedPoolCharge = 0;
}
/* Copy name information */
BasicInfo->NameInfoSize = ObpQueryNameInfoSize(ObjectHeader);
BasicInfo->TypeInfoSize = sizeof(OBJECT_TYPE_INFORMATION) + ObjectType->Name.Length +
sizeof(UNICODE_NULL);
/* Check if this is a symlink */
if (ObjectHeader->Type == ObpSymbolicLinkObjectType)
{
/* Return the creation time */
BasicInfo->CreationTime.QuadPart =
((POBJECT_SYMBOLIC_LINK)Object)->CreationTime.QuadPart;
}
else
{
/* Otherwise return 0 */
BasicInfo->CreationTime.QuadPart = (ULONGLONG)0;
}
/* Copy security information */
BasicInfo->SecurityDescriptorSize = 0;
if (BooleanFlagOn(HandleInfo.GrantedAccess, READ_CONTROL) &&
ObjectHeader->SecurityDescriptor != NULL)
{
SecurityInformation = OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION |
SACL_SECURITY_INFORMATION;
ObjectType->TypeInfo.SecurityProcedure(Object,
QuerySecurityDescriptor,
&SecurityInformation,
NULL,
&BasicInfo->SecurityDescriptorSize,
&ObjectHeader->SecurityDescriptor,
ObjectType->TypeInfo.PoolType,
&ObjectType->TypeInfo.GenericMapping);
}
/* Break out with success */
Status = STATUS_SUCCESS;
break;
/* Name information */
case ObjectNameInformation:
/* Call the helper and break out */
Status = ObQueryNameString(Object,
(POBJECT_NAME_INFORMATION)
ObjectInformation,
Length,
&InfoLength);
break;
/* Information about this type */
case ObjectTypeInformation:
/* Call the helper and break out */
Status = ObQueryTypeInfo(ObjectHeader->Type,
(POBJECT_TYPE_INFORMATION)
ObjectInformation,
Length,
&InfoLength);
break;
/* Information about all types */
case ObjectTypesInformation:
DPRINT1("NOT IMPLEMENTED!\n");
InfoLength = Length;
Status = STATUS_NOT_IMPLEMENTED;
break;
/* Information about the handle flags */
case ObjectHandleFlagInformation:
/* Validate length */
InfoLength = sizeof (OBJECT_HANDLE_ATTRIBUTE_INFORMATION);
if (Length != sizeof (OBJECT_HANDLE_ATTRIBUTE_INFORMATION))
{
Status = STATUS_INFO_LENGTH_MISMATCH;
break;
}
/* Get the structure */
HandleFlags = (POBJECT_HANDLE_ATTRIBUTE_INFORMATION)
ObjectInformation;
/* Set the flags */
HandleFlags->Inherit = HandleInfo.HandleAttributes & OBJ_INHERIT;
HandleFlags->ProtectFromClose = (HandleInfo.HandleAttributes &
OBJ_PROTECT_CLOSE) != 0;
/* Break out with success */
Status = STATUS_SUCCESS;
break;
/* Anything else */
default:
/* Fail it */
InfoLength = Length;
Status = STATUS_INVALID_INFO_CLASS;
break;
}
/* Check if the caller wanted the return length */
if (ResultLength)
{
/* Write the length */
*ResultLength = InfoLength;
}
}
_SEH2_EXCEPT(ExSystemExceptionFilter())
{
/* Otherwise, get the exception code */
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
/* Dereference the object if we had referenced it */
if (Object) ObDereferenceObject(Object);
/* Return status */
return Status;
}
/*++
* @name NtSetInformationObject
* @implemented NT4
*
* The NtSetInformationObject routine <FILLMEIN>
*
* @param ObjectHandle
* <FILLMEIN>
*
* @param ObjectInformationClass
* <FILLMEIN>
*
* @param ObjectInformation
* <FILLMEIN>
*
* @param Length
* <FILLMEIN>
*
* @return STATUS_SUCCESS or appropriate error value.
*
* @remarks None.
*
*--*/
NTSTATUS
NTAPI
NtSetInformationObject(IN HANDLE ObjectHandle,
IN OBJECT_INFORMATION_CLASS ObjectInformationClass,
IN PVOID ObjectInformation,
IN ULONG Length)
{
NTSTATUS Status;
OBP_SET_HANDLE_ATTRIBUTES_CONTEXT Context;
PVOID ObjectTable;
KAPC_STATE ApcState;
POBJECT_DIRECTORY Directory;
KPROCESSOR_MODE PreviousMode;
BOOLEAN AttachedToProcess = FALSE;
PAGED_CODE();
/* Validate the information class */
switch (ObjectInformationClass)
{
case ObjectHandleFlagInformation:
/* Validate the length */
if (Length != sizeof(OBJECT_HANDLE_ATTRIBUTE_INFORMATION))
{
/* Invalid length */
return STATUS_INFO_LENGTH_MISMATCH;
}
/* Save the previous mode */
Context.PreviousMode = ExGetPreviousMode();
/* Check if we were called from user mode */
if (Context.PreviousMode != KernelMode)
{
/* Enter SEH */
_SEH2_TRY
{
/* Probe and capture the attribute buffer */
ProbeForRead(ObjectInformation,
sizeof(OBJECT_HANDLE_ATTRIBUTE_INFORMATION),
sizeof(BOOLEAN));
Context.Information = *(POBJECT_HANDLE_ATTRIBUTE_INFORMATION)
ObjectInformation;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
else
{
/* Just copy the buffer directly */
Context.Information = *(POBJECT_HANDLE_ATTRIBUTE_INFORMATION)
ObjectInformation;
}
/* Check if this is a kernel handle */
if (ObpIsKernelHandle(ObjectHandle, Context.PreviousMode))
{
/* Get the actual handle */
ObjectHandle = ObKernelHandleToHandle(ObjectHandle);
ObjectTable = ObpKernelHandleTable;
/* Check if we're not in the system process */
if (PsGetCurrentProcess() != PsInitialSystemProcess)
{
/* Attach to it */
KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
AttachedToProcess = TRUE;
}
}
else
{
/* Use the current table */
ObjectTable = PsGetCurrentProcess()->ObjectTable;
}
/* Change the handle attributes */
if (!ExChangeHandle(ObjectTable,
ObjectHandle,
ObpSetHandleAttributes,
(ULONG_PTR)&Context))
{
/* Some failure */
Status = STATUS_ACCESS_DENIED;
}
else
{
/* We are done */
Status = STATUS_SUCCESS;
}
/* De-attach if we were attached, and return status */
if (AttachedToProcess) KeUnstackDetachProcess(&ApcState);
break;
case ObjectSessionInformation:
/* Only a system process can do this */
PreviousMode = ExGetPreviousMode();
if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
{
/* Fail */
DPRINT1("Privilege not held\n");
Status = STATUS_PRIVILEGE_NOT_HELD;
}
else
{
/* Get the object directory */
Status = ObReferenceObjectByHandle(ObjectHandle,
0,
ObpDirectoryObjectType,
PreviousMode,
(PVOID*)&Directory,
NULL);
if (NT_SUCCESS(Status))
{
/* Setup a lookup context */
OBP_LOOKUP_CONTEXT LookupContext;
ObpInitializeLookupContext(&LookupContext);
/* Set the directory session ID */
ObpAcquireDirectoryLockExclusive(Directory, &LookupContext);
Directory->SessionId = PsGetCurrentProcessSessionId();
ObpReleaseDirectoryLock(Directory, &LookupContext);
/* We're done, release the context and dereference the directory */
ObpReleaseLookupContext(&LookupContext);
ObDereferenceObject(Directory);
}
}
break;
default:
/* Unsupported class */
Status = STATUS_INVALID_INFO_CLASS;
break;
}
return Status;
}
/* EOF */