1599 lines
47 KiB
C
1599 lines
47 KiB
C
/*++
|
|
|
|
Copyright (c) 1997-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Secure.c
|
|
|
|
Abstract:
|
|
|
|
Security implementation for WMI objects
|
|
|
|
WMI security is guid based, that is each guid can be assigned a security
|
|
descriptor. There is also a default security descriptor that applies
|
|
to any guid that does not have its own specific security descriptor.
|
|
Security is enforced by relying upon the object manager. We define the
|
|
WmiGuid object type and require all WMI requests to have a handle to the
|
|
WmiGuid object. In this way the guid is opened with a specific ACCESS_MASK
|
|
and if the caller is permitted those rights (as specified in the specific
|
|
or default security descriptor) then a handle is returned. When the
|
|
caller wants to do an operation he must pass the handle and before the
|
|
operation is performed we check that the handle has the allowed access.
|
|
|
|
Guid security descriptors are serialized as REG_BINARY values under the
|
|
registry key HKLM\CurrentControlSet\Control\Wmi\Security. If no specific
|
|
or default security descriptor for a guid exists then the all access
|
|
is available for anyone. For this reason this registry key must be
|
|
protected.
|
|
|
|
WMI implements its own security method for the WmiGuid object type to
|
|
allow it to intercept any changes to an objects security descriptor. By
|
|
doing this we allow the standard security apis
|
|
(Get/SetKernelObjectSecurity) to query and set the WMI security
|
|
descriptors.
|
|
|
|
A guid security descriptor contains the following specific rights:
|
|
|
|
WMIGUID_QUERY 0x0001
|
|
WMIGUID_SET 0x0002
|
|
WMIGUID_NOTIFICATION 0x0004
|
|
WMIGUID_READ_DESCRIPTION 0x0008
|
|
WMIGUID_EXECUTE 0x0010
|
|
TRACELOG_CREATE_REALTIME 0x0020
|
|
TRACELOG_CREATE_ONDISK 0x0040
|
|
TRACELOG_GUID_ENABLE 0x0080
|
|
TRACELOG_ACCESS_KERNEL_LOGGER 0x0100
|
|
|
|
|
|
Security is only implemented for NT and not MEMPHIS
|
|
|
|
Author:
|
|
|
|
AlanWar
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#ifndef MEMPHIS
|
|
|
|
#include "wmikmp.h"
|
|
|
|
NTSTATUS
|
|
WmipSecurityMethod (
|
|
IN PVOID Object,
|
|
IN SECURITY_OPERATION_CODE OperationCode,
|
|
IN PSECURITY_INFORMATION SecurityInformation,
|
|
IN OUT PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
IN OUT PULONG CapturedLength,
|
|
IN OUT PSECURITY_DESCRIPTOR *ObjectsSecurityDescriptor,
|
|
IN POOL_TYPE PoolType,
|
|
IN PGENERIC_MAPPING GenericMapping
|
|
);
|
|
|
|
VOID WmipDeleteMethod(
|
|
IN PVOID Object
|
|
);
|
|
|
|
VOID WmipCloseMethod(
|
|
IN PEPROCESS Process OPTIONAL,
|
|
IN PVOID Object,
|
|
IN ACCESS_MASK GrantedAccess,
|
|
IN ULONG ProcessHandleCount,
|
|
IN ULONG SystemHandleCount
|
|
);
|
|
|
|
NTSTATUS
|
|
WmipGetGuidSecurityDescriptor(
|
|
PUNICODE_STRING GuidName,
|
|
PSECURITY_DESCRIPTOR *SecurityDescriptor
|
|
);
|
|
|
|
NTSTATUS
|
|
WmipSaveGuidSecurityDescriptor(
|
|
PUNICODE_STRING GuidName,
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor
|
|
);
|
|
|
|
NTSTATUS
|
|
WmipSDRegistryQueryRoutine(
|
|
IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext
|
|
);
|
|
|
|
NTSTATUS
|
|
WmipCreateGuidObject(
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN LPGUID Guid,
|
|
OUT PHANDLE CreatorHandle,
|
|
OUT PWMIGUIDOBJECT *Object
|
|
);
|
|
|
|
NTSTATUS
|
|
WmipUuidFromString (
|
|
IN PWCHAR StringUuid,
|
|
OUT LPGUID Uuid
|
|
);
|
|
|
|
BOOLEAN
|
|
WmipHexStringToDword(
|
|
IN PWCHAR lpsz,
|
|
OUT PULONG RetValue,
|
|
IN ULONG cDigits,
|
|
IN WCHAR chDelim
|
|
);
|
|
|
|
VOID WmipCloseMethod(
|
|
IN PEPROCESS Process OPTIONAL,
|
|
IN PVOID Object,
|
|
IN ACCESS_MASK GrantedAccess,
|
|
IN ULONG ProcessHandleCount,
|
|
IN ULONG SystemHandleCount
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT,WmipInitializeSecurity)
|
|
#pragma alloc_text(PAGE,WmipGetGuidSecurityDescriptor)
|
|
#pragma alloc_text(PAGE,WmipSaveGuidSecurityDescriptor)
|
|
#pragma alloc_text(PAGE,WmipOpenGuidObject)
|
|
#pragma alloc_text(PAGE,WmipCheckGuidAccess)
|
|
#pragma alloc_text(PAGE,WmipSDRegistryQueryRoutine)
|
|
#pragma alloc_text(PAGE,WmipSecurityMethod)
|
|
#pragma alloc_text(PAGE,WmipDeleteMethod)
|
|
#pragma alloc_text(PAGE,WmipCreateGuidObject)
|
|
#pragma alloc_text(PAGE,WmipUuidFromString)
|
|
#pragma alloc_text(PAGE,WmipHexStringToDword)
|
|
#pragma alloc_text(PAGE,WmipCloseMethod)
|
|
#endif
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma const_seg("PAGECONST")
|
|
#pragma data_seg("PAGEDATA")
|
|
#endif
|
|
//
|
|
// Subject context for the System process, captured at boot
|
|
SECURITY_SUBJECT_CONTEXT WmipSystemSubjectContext;
|
|
|
|
//
|
|
// Object type object created by Ob when registering WmiGuid object type
|
|
POBJECT_TYPE WmipGuidObjectType;
|
|
|
|
//
|
|
// SD attached to a guid when no specific or default SD exists in the
|
|
// registry. Created at boot, it allows all WMI access to WORLD and full
|
|
// access to System and Administrators group.
|
|
SECURITY_DESCRIPTOR WmipAnyoneAccessSecurityDescriptor;
|
|
PSECURITY_DESCRIPTOR WmipAnyoneAccessSd;
|
|
|
|
//
|
|
// Generic mapping for specific rights
|
|
const GENERIC_MAPPING WmipGenericMapping =
|
|
{
|
|
STANDARD_RIGHTS_READ | // GENERIC_READ <--> WMIGUID_QUERY
|
|
WMIGUID_QUERY,
|
|
STANDARD_RIGHTS_WRITE | // GENERIC_WRUTE <--> WMIGUID_SET
|
|
WMIGUID_SET,
|
|
STANDARD_RIGHTS_EXECUTE | // GENERIC_EXECUTE <--> WMIGUID_EXECUTE
|
|
WMIGUID_EXECUTE,
|
|
WMIGUID_ALL_ACCESS | STANDARD_RIGHTS_READ
|
|
};
|
|
|
|
|
|
NTSTATUS
|
|
WmipSecurityMethod (
|
|
IN PVOID Object,
|
|
IN SECURITY_OPERATION_CODE OperationCode,
|
|
IN PSECURITY_INFORMATION SecurityInformation,
|
|
IN OUT PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
IN OUT PULONG CapturedLength,
|
|
IN OUT PSECURITY_DESCRIPTOR *ObjectsSecurityDescriptor,
|
|
IN POOL_TYPE PoolType,
|
|
IN PGENERIC_MAPPING GenericMapping
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the WMI security method for objects. It is responsible
|
|
for either retrieving, setting, and deleting the security descriptor of
|
|
an object. It is not used to assign the original security descriptor
|
|
to an object (use SeAssignSecurity for that purpose).
|
|
|
|
|
|
IT IS ASSUMED THAT THE OBJECT MANAGER HAS ALREADY DONE THE ACCESS
|
|
VALIDATIONS NECESSARY TO ALLOW THE REQUESTED OPERATIONS TO BE PERFORMED.
|
|
|
|
This code stolen directly from SeDefaultObjectMethod in
|
|
\nt\private\ntos\se\semethod.c. It does not do anything special except
|
|
serialize any SD that is being set for an object.
|
|
|
|
Arguments:
|
|
|
|
Object - Supplies a pointer to the object being used.
|
|
|
|
OperationCode - Indicates if the operation is for setting, querying, or
|
|
deleting the object's security descriptor.
|
|
|
|
SecurityInformation - Indicates which security information is being
|
|
queried or set. This argument is ignored for the delete operation.
|
|
|
|
SecurityDescriptor - The meaning of this parameter depends on the
|
|
OperationCode:
|
|
|
|
QuerySecurityDescriptor - For the query operation this supplies the
|
|
buffer to copy the descriptor into. The security descriptor is
|
|
assumed to have been probed up to the size passed in in Length.
|
|
Since it still points into user space, it must always be
|
|
accessed in a try clause in case it should suddenly disappear.
|
|
|
|
SetSecurityDescriptor - For a set operation this supplies the
|
|
security descriptor to copy into the object. The security
|
|
descriptor must be captured before this routine is called.
|
|
|
|
DeleteSecurityDescriptor - It is ignored when deleting a security
|
|
descriptor.
|
|
|
|
AssignSecurityDescriptor - For assign operations this is the
|
|
security descriptor that will be assigned to the object.
|
|
It is assumed to be in kernel space, and is therefore not
|
|
probed or captured.
|
|
|
|
CapturedLength - For the query operation this specifies the length, in
|
|
bytes, of the security descriptor buffer, and upon return contains
|
|
the number of bytes needed to store the descriptor. If the length
|
|
needed is greater than the length supplied the operation will fail.
|
|
It is ignored in the set and delete operation.
|
|
|
|
This parameter is assumed to be captured and probed as appropriate.
|
|
|
|
ObjectsSecurityDescriptor - For the Set operation this supplies the address
|
|
of a pointer to the object's current security descriptor. This routine
|
|
will either modify the security descriptor in place or allocate a new
|
|
security descriptor and use this variable to indicate its new location.
|
|
For the query operation it simply supplies the security descriptor
|
|
being queried. The caller is responsible for freeing the old security
|
|
descriptor.
|
|
|
|
PoolType - For the set operation this specifies the pool type to use if
|
|
a new security descriptor needs to be allocated. It is ignored
|
|
in the query and delete operation.
|
|
|
|
the mapping of generic to specific/standard access types for the object
|
|
being accessed. This mapping structure is expected to be safe to
|
|
access (i.e., captured if necessary) prior to be passed to this routine.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - STATUS_SUCCESS if the operation is successful and an
|
|
appropriate error status otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// If the object's security descriptor is null, then object is not
|
|
// one that has security information associated with it. Return
|
|
// an error.
|
|
//
|
|
|
|
//
|
|
// Make sure the common parts of our input are proper
|
|
//
|
|
|
|
ASSERT( (OperationCode == SetSecurityDescriptor) ||
|
|
(OperationCode == QuerySecurityDescriptor) ||
|
|
(OperationCode == AssignSecurityDescriptor) ||
|
|
(OperationCode == DeleteSecurityDescriptor) );
|
|
|
|
//
|
|
// This routine simply cases off of the operation code to decide
|
|
// which support routine to call
|
|
//
|
|
|
|
switch (OperationCode) {
|
|
|
|
case SetSecurityDescriptor:
|
|
{
|
|
UNICODE_STRING GuidName;
|
|
WCHAR GuidBuffer[38];
|
|
LPGUID Guid;
|
|
SECURITY_INFORMATION LocalSecInfo;
|
|
PSECURITY_DESCRIPTOR SecurityDescriptorCopy;
|
|
ULONG SecurityDescriptorLength;
|
|
ULONG Status2;
|
|
|
|
ASSERT( (PoolType == PagedPool) || (PoolType == NonPagedPool) );
|
|
|
|
Status = ObReferenceObjectByPointer(Object,
|
|
0,
|
|
WmipGuidObjectType,
|
|
KernelMode);
|
|
ASSERT(Status == STATUS_SUCCESS);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = ObSetSecurityDescriptorInfo( Object,
|
|
SecurityInformation,
|
|
SecurityDescriptor,
|
|
ObjectsSecurityDescriptor,
|
|
PoolType,
|
|
GenericMapping
|
|
);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// Serialize the guid's new security descriptor in
|
|
// the registry. But first we need to get a copy of
|
|
// it.
|
|
|
|
SecurityDescriptorLength = 1024;
|
|
do
|
|
{
|
|
SecurityDescriptorCopy = ExAllocatePoolWithTag(
|
|
PoolType,
|
|
SecurityDescriptorLength,
|
|
WMIPOOLTAG);
|
|
|
|
if (SecurityDescriptorCopy == NULL)
|
|
{
|
|
Status2 = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
LocalSecInfo = 0xffffffff;
|
|
Status2 = ObQuerySecurityDescriptorInfo( Object,
|
|
&LocalSecInfo,
|
|
SecurityDescriptorCopy,
|
|
&SecurityDescriptorLength,
|
|
ObjectsSecurityDescriptor);
|
|
|
|
|
|
if (Status2 == STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
ExFreePool(SecurityDescriptorCopy);
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
} while (TRUE);
|
|
|
|
|
|
if (NT_SUCCESS(Status2))
|
|
{
|
|
Guid = &((PWMIGUIDOBJECT)Object)->Guid;
|
|
swprintf(GuidBuffer,
|
|
L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
|
Guid->Data1, Guid->Data2,
|
|
Guid->Data3,
|
|
Guid->Data4[0], Guid->Data4[1],
|
|
Guid->Data4[2], Guid->Data4[3],
|
|
Guid->Data4[4], Guid->Data4[5],
|
|
Guid->Data4[6], Guid->Data4[7]);
|
|
|
|
RtlInitUnicodeString(&GuidName, GuidBuffer);
|
|
|
|
WmipSaveGuidSecurityDescriptor(&GuidName,
|
|
SecurityDescriptorCopy);
|
|
}
|
|
|
|
if (SecurityDescriptorCopy != NULL)
|
|
{
|
|
ExFreePool(SecurityDescriptorCopy);
|
|
}
|
|
|
|
}
|
|
|
|
ObDereferenceObject(Object);
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
case QuerySecurityDescriptor:
|
|
{
|
|
|
|
//
|
|
// check the rest of our input and call the default query security
|
|
// method
|
|
//
|
|
|
|
ASSERT( CapturedLength != NULL );
|
|
|
|
|
|
return ObQuerySecurityDescriptorInfo( Object,
|
|
SecurityInformation,
|
|
SecurityDescriptor,
|
|
CapturedLength,
|
|
ObjectsSecurityDescriptor );
|
|
|
|
return(Status);
|
|
}
|
|
|
|
case DeleteSecurityDescriptor:
|
|
{
|
|
|
|
//
|
|
// call the default delete security method
|
|
//
|
|
|
|
Status = ObDeassignSecurity(ObjectsSecurityDescriptor);
|
|
return(Status);
|
|
}
|
|
|
|
case AssignSecurityDescriptor:
|
|
|
|
ObAssignObjectSecurityDescriptor( Object,
|
|
SecurityDescriptor,
|
|
PoolType );
|
|
return( STATUS_SUCCESS );
|
|
|
|
default:
|
|
|
|
//
|
|
// Bugcheck on any other operation code, We won't get here if
|
|
// the earlier asserts are still checked.
|
|
//
|
|
|
|
KeBugCheckEx( SECURITY_SYSTEM, 1, STATUS_INVALID_PARAMETER, 0, 0 );
|
|
return (0); // bash compiler
|
|
}
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS WmipInitializeSecurity(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will initialize WMI security subsystem. Basically we
|
|
create the WMIGUID object type, obtain the SECURITY_SUBJECT_CONTEXT for
|
|
the System process and establish a SD that allows all access that is used
|
|
when no default or specific SD is assigned to a guid.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
NT Status code
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING ObjectTypeName;
|
|
POBJECT_TYPE ObjectType;
|
|
OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE Handle;
|
|
ULONG DaclLength;
|
|
PACL AnyoneAccessDacl;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Establish a SD for those guids with no specific or default SD
|
|
DaclLength = (ULONG)sizeof(ACL) +
|
|
(3*((ULONG)sizeof(ACCESS_ALLOWED_ACE))) +
|
|
SeLengthSid( SeLocalSystemSid ) +
|
|
SeLengthSid( SeAliasAdminsSid ) +
|
|
SeLengthSid( SeWorldSid ) +
|
|
8; // The 8 is just for good measure
|
|
|
|
|
|
AnyoneAccessDacl = (PACL)ExAllocatePoolWithTag(PagedPool,
|
|
DaclLength,
|
|
WMIPOOLTAG);
|
|
if (AnyoneAccessDacl == NULL)
|
|
{
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
Status = RtlCreateAcl( AnyoneAccessDacl,
|
|
DaclLength,
|
|
ACL_REVISION2);
|
|
if (! NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = RtlAddAccessAllowedAce (
|
|
AnyoneAccessDacl,
|
|
ACL_REVISION2,
|
|
(STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL),
|
|
SeLocalSystemSid
|
|
);
|
|
if (! NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = RtlAddAccessAllowedAce (
|
|
AnyoneAccessDacl,
|
|
ACL_REVISION2,
|
|
(STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL),
|
|
SeAliasAdminsSid
|
|
);
|
|
if (! NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = RtlAddAccessAllowedAce (
|
|
AnyoneAccessDacl,
|
|
ACL_REVISION2,
|
|
WMIGUID_ALL_ACCESS,
|
|
SeWorldSid
|
|
);
|
|
if (! NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
WmipAnyoneAccessSd = &WmipAnyoneAccessSecurityDescriptor;
|
|
Status = RtlCreateSecurityDescriptor(
|
|
WmipAnyoneAccessSd,
|
|
SECURITY_DESCRIPTOR_REVISION1
|
|
);
|
|
|
|
Status = RtlSetDaclSecurityDescriptor(
|
|
WmipAnyoneAccessSd,
|
|
TRUE, // DaclPresent
|
|
AnyoneAccessDacl,
|
|
FALSE // DaclDefaulted
|
|
);
|
|
if (! NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = RtlSetOwnerSecurityDescriptor(WmipAnyoneAccessSd,
|
|
SeAliasAdminsSid,
|
|
FALSE);
|
|
if (! NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = RtlSetGroupSecurityDescriptor(WmipAnyoneAccessSd,
|
|
SeAliasAdminsSid,
|
|
FALSE);
|
|
if (! NT_SUCCESS(Status))
|
|
{
|
|
Cleanup:
|
|
ExFreePool(AnyoneAccessDacl);
|
|
WmipAnyoneAccessSd = NULL;
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// Remember System process subject context
|
|
SeCaptureSubjectContext(&WmipSystemSubjectContext);
|
|
|
|
//
|
|
// Establish WmiGuid object type
|
|
RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
|
|
|
|
ObjectTypeInitializer.Length = sizeof(OBJECT_TYPE_INITIALIZER);
|
|
ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
|
|
ObjectTypeInitializer.GenericMapping = WmipGenericMapping;
|
|
ObjectTypeInitializer.ValidAccessMask = WMIGUID_ALL_ACCESS | STANDARD_RIGHTS_ALL;
|
|
|
|
//
|
|
// All named objects may (must ?) have security descriptors attached
|
|
// to them. If unnamed objects also must have security descriptors
|
|
// attached then this must be TRUE
|
|
ObjectTypeInitializer.SecurityRequired = TRUE;
|
|
|
|
//
|
|
// Tracks # handles open for object within a process
|
|
ObjectTypeInitializer.MaintainHandleCount = FALSE;
|
|
|
|
//
|
|
// Need to be in non paged pool since KEVENT contained within the
|
|
// object must be in non paged pool
|
|
//
|
|
ObjectTypeInitializer.PoolType = NonPagedPool;
|
|
|
|
ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(WMIGUIDOBJECT);
|
|
|
|
//
|
|
// Use a custom security procedure so that we can serialize any
|
|
// changes to the security descriptor.
|
|
ObjectTypeInitializer.SecurityProcedure = WmipSecurityMethod;
|
|
|
|
//
|
|
// We need to know when an object is being deleted
|
|
//
|
|
ObjectTypeInitializer.DeleteProcedure = WmipDeleteMethod;
|
|
ObjectTypeInitializer.CloseProcedure = WmipCloseMethod;
|
|
RtlInitUnicodeString(&ObjectTypeName, L"WmiGuid");
|
|
|
|
Status = ObCreateObjectType(&ObjectTypeName,
|
|
&ObjectTypeInitializer,
|
|
NULL,
|
|
&WmipGuidObjectType);
|
|
|
|
if (! NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
#if 0
|
|
//
|
|
// don't need to create obejct dir
|
|
//
|
|
RtlInitUnicodeString( &ObjectTypeName, L"\\WmiGuid" );
|
|
InitializeObjectAttributes( &ObjectAttributes,
|
|
&ObjectTypeName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
|
|
NULL,
|
|
SePublicDefaultSd );
|
|
|
|
Status = NtCreateDirectoryObject( &Handle,
|
|
DIRECTORY_ALL_ACCESS,
|
|
&ObjectAttributes );
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
NtClose(Handle);
|
|
}
|
|
#endif
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS WmipSDRegistryQueryRoutine(
|
|
IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Registry query values callback routine for reading SDs for guids
|
|
|
|
Arguments:
|
|
|
|
ValueName - the name of the value
|
|
|
|
ValueType - the type of the value
|
|
|
|
ValueData - the data in the value (unicode string data)
|
|
|
|
ValueLength - the number of bytes in the value data
|
|
|
|
Context - Not used
|
|
|
|
EntryContext - Pointer to PSECURITTY_DESCRIPTOR to store a pointer to
|
|
store the security descriptor read from the registry value
|
|
|
|
Return Value:
|
|
|
|
NT Status code
|
|
|
|
--*/
|
|
{
|
|
PSECURITY_DESCRIPTOR *SecurityDescriptor;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
Status = STATUS_SUCCESS;
|
|
if ((ValueType == REG_BINARY) &&
|
|
(ValueData != NULL))
|
|
{
|
|
//
|
|
// If a SD is specified in the registry then copy it
|
|
SecurityDescriptor = (PSECURITY_DESCRIPTOR *)EntryContext;
|
|
*SecurityDescriptor = ExAllocatePoolWithTag(PagedPool,
|
|
ValueLength,
|
|
WMIPOOLTAG);
|
|
if (*SecurityDescriptor != NULL)
|
|
{
|
|
RtlCopyMemory(*SecurityDescriptor,
|
|
ValueData,
|
|
ValueLength);
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS WmipSaveGuidSecurityDescriptor(
|
|
PUNICODE_STRING GuidName,
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will serialize the security descriptor associated with a
|
|
guid.
|
|
|
|
Security descriptors are maintained as REG_BINARY values named by the guid
|
|
in the registry under
|
|
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Wmi\Security
|
|
|
|
Arguments:
|
|
|
|
GuidName is a pointer to a unicode string that represents the guid
|
|
|
|
SecurityDescriptor points at a self relative security descriptor
|
|
|
|
Return Value:
|
|
|
|
NT Status code
|
|
|
|
--*/
|
|
{
|
|
ULONG SecurityDescriptorLength;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
SecurityDescriptorLength = RtlLengthSecurityDescriptor(SecurityDescriptor);
|
|
Status = RtlWriteRegistryValue(RTL_REGISTRY_CONTROL,
|
|
L"WMI\\Security",
|
|
GuidName->Buffer,
|
|
REG_BINARY,
|
|
SecurityDescriptor,
|
|
SecurityDescriptorLength);
|
|
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS WmipGetGuidSecurityDescriptor(
|
|
PUNICODE_STRING GuidName,
|
|
PSECURITY_DESCRIPTOR *SecurityDescriptor
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will retrieve the security descriptor associated with a
|
|
guid. First it looks for a security descriptor specifically for the
|
|
guid and if not found then looks for the default security descriptor.
|
|
|
|
Security descriptors are maintained as REG_BINARY values named by the guid
|
|
in the registry under
|
|
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Wmi\Security
|
|
|
|
Arguments:
|
|
|
|
GuidName is a pointer to a unicode string that represents the guid
|
|
|
|
*SecurityDescriptor returns the security descriptor for the guid. It
|
|
must be freed back to pool unless it is the same value as that in
|
|
WmipAnyoneAccessSd which must NOT be freed.
|
|
|
|
Return Value:
|
|
|
|
NT Status code
|
|
|
|
--*/
|
|
{
|
|
RTL_QUERY_REGISTRY_TABLE QueryRegistryTable[3];
|
|
NTSTATUS Status;
|
|
PSECURITY_DESCRIPTOR GuidSecurityDescriptor = NULL;
|
|
PSECURITY_DESCRIPTOR DefaultSecurityDescriptor = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
RtlZeroMemory(QueryRegistryTable, sizeof(QueryRegistryTable));
|
|
|
|
QueryRegistryTable[0].QueryRoutine = WmipSDRegistryQueryRoutine;
|
|
QueryRegistryTable[0].EntryContext = &GuidSecurityDescriptor;
|
|
QueryRegistryTable[0].Name = GuidName->Buffer;
|
|
QueryRegistryTable[0].DefaultType = REG_BINARY;
|
|
|
|
QueryRegistryTable[1].QueryRoutine = WmipSDRegistryQueryRoutine;
|
|
QueryRegistryTable[1].Flags = 0;
|
|
QueryRegistryTable[1].EntryContext = &DefaultSecurityDescriptor;
|
|
QueryRegistryTable[1].Name = DefaultSecurityGuidName;
|
|
QueryRegistryTable[1].DefaultType = REG_BINARY;
|
|
|
|
Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
|
|
L"WMI\\Security",
|
|
QueryRegistryTable,
|
|
NULL,
|
|
NULL);
|
|
|
|
*SecurityDescriptor = NULL;
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// If there is a guid specific SD then choose that and free any
|
|
// default SD. Else we use the default SD unless that doesn't
|
|
// exist and so there is no security
|
|
if (GuidSecurityDescriptor != NULL)
|
|
{
|
|
*SecurityDescriptor = GuidSecurityDescriptor;
|
|
if (DefaultSecurityDescriptor != NULL)
|
|
{
|
|
ExFreePool(DefaultSecurityDescriptor);
|
|
}
|
|
} else if (DefaultSecurityDescriptor != NULL) {
|
|
*SecurityDescriptor = DefaultSecurityDescriptor;
|
|
}
|
|
} else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
if (*SecurityDescriptor == NULL)
|
|
{
|
|
*SecurityDescriptor = WmipAnyoneAccessSd;
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS WmipOpenGuidObject(
|
|
IN POBJECT_ATTRIBUTES CapturedObjectAttributes,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN KPROCESSOR_MODE AccessMode,
|
|
OUT PHANDLE Handle,
|
|
OUT PWMIGUIDOBJECT *ObjectPtr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will open a handle to a WmiGuid object with the access rights
|
|
specified. WmiGuid objects are temporary objects that are created on an
|
|
as needed basis. We will always create a new unnamed guid object each time
|
|
a guid is opened.
|
|
|
|
Arguments:
|
|
|
|
GuidString is the string representation for the guid that refers to
|
|
the object to open. Note that this parameter has NOT been probed.
|
|
Parse UUID such as \WmiGuid\00000000-0000-0000-0000-000000000000
|
|
|
|
DesiredAccess specifies the access requested
|
|
|
|
*Handle returns a handle to the guid object
|
|
|
|
*ObjectPtr returns containing a pointer to the object. This object
|
|
will have a reference attached to it that must be derefed by
|
|
the calling code.
|
|
|
|
Return Value:
|
|
|
|
NT Status code
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
GUID Guid;
|
|
PWMIGUIDOBJECT GuidObject;
|
|
HANDLE CreatorHandle;
|
|
PUNICODE_STRING CapturedGuidString;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Validate guid object name passed by insuring that it is in the
|
|
// correct object directory and the correct format for a uuid
|
|
CapturedGuidString = CapturedObjectAttributes->ObjectName;
|
|
|
|
if (RtlEqualMemory(CapturedGuidString->Buffer,
|
|
WmiGuidObjectDirectory,
|
|
(WmiGuidObjectDirectoryLength-1) * sizeof(WCHAR)) == 0)
|
|
{
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
Status = WmipUuidFromString(&CapturedGuidString->Buffer[9], &Guid);
|
|
if (! NT_SUCCESS(Status))
|
|
{
|
|
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Invalid uuid format for guid object %ws\n", CapturedGuidString->Buffer));
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// If it does not exist then create an object for the guid ....
|
|
//
|
|
Status = WmipCreateGuidObject(CapturedObjectAttributes,
|
|
DesiredAccess,
|
|
&Guid,
|
|
&CreatorHandle,
|
|
&GuidObject);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// .... and try again to open it
|
|
//
|
|
Status = ObOpenObjectByPointer(GuidObject,
|
|
0,
|
|
NULL,
|
|
DesiredAccess,
|
|
WmipGuidObjectType,
|
|
AccessMode,
|
|
Handle);
|
|
|
|
if (! NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// Remove extra ref count taken by ObInsertObject since we
|
|
// are returning an error
|
|
//
|
|
ObDereferenceObject(GuidObject);
|
|
}
|
|
|
|
//
|
|
// Make sure to close handle obtained in creating object. We
|
|
// attach to the system process since the handle was created in
|
|
// its handle table.
|
|
//
|
|
KeAttachProcess( &PsInitialSystemProcess->Pcb );
|
|
ZwClose(CreatorHandle);
|
|
KeDetachProcess( );
|
|
*ObjectPtr = GuidObject;
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS WmipCreateGuidObject(
|
|
IN OUT POBJECT_ATTRIBUTES ObjectAttributes,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN LPGUID Guid,
|
|
OUT PHANDLE CreatorHandle,
|
|
OUT PWMIGUIDOBJECT *Object
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will create a new guid object for
|
|
the guid passed. The handle returned is the handle issued to the creator
|
|
of the object and should be closed after the object is opened.
|
|
|
|
Guid Objects are created on the fly, but
|
|
|
|
Arguments:
|
|
|
|
ObjectAttributes - Describes object being created. ObjectAttributes
|
|
is modified in this call.
|
|
|
|
Guid is the guid for which the object is being created
|
|
|
|
*CreatorHandle returns a handle to the created guid object. This handle
|
|
is in the system process handle table
|
|
|
|
*Object returns with a pointer to the object
|
|
|
|
Return Value:
|
|
|
|
NT Status code
|
|
|
|
--*/
|
|
{
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor;
|
|
UNICODE_STRING UnicodeString;
|
|
WCHAR *ObjectNameBuffer;
|
|
WCHAR *GuidBuffer;
|
|
NTSTATUS Status;
|
|
ACCESS_STATE LocalAccessState;
|
|
AUX_ACCESS_DATA AuxData;
|
|
SECURITY_SUBJECT_CONTEXT SavedSubjectContext;
|
|
PSECURITY_SUBJECT_CONTEXT SubjectContext;
|
|
PWMIGUIDOBJECT NewObject;
|
|
OBJECT_ATTRIBUTES UnnamedObjectAttributes;
|
|
|
|
PAGED_CODE();
|
|
|
|
ObjectNameBuffer = ObjectAttributes->ObjectName->Buffer;
|
|
GuidBuffer = &ObjectNameBuffer[9];
|
|
RtlInitUnicodeString(&UnicodeString, GuidBuffer);
|
|
|
|
//
|
|
// Obtain security descriptor associated with the guid
|
|
Status = WmipGetGuidSecurityDescriptor(&UnicodeString,
|
|
&SecurityDescriptor);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
WmipAssert(SecurityDescriptor != NULL);
|
|
|
|
//
|
|
// Establish ObjectAttributes for the newly created object
|
|
RtlInitUnicodeString(&UnicodeString, ObjectNameBuffer);
|
|
|
|
UnnamedObjectAttributes = *ObjectAttributes;
|
|
UnnamedObjectAttributes.Attributes = OBJ_OPENIF;
|
|
UnnamedObjectAttributes.SecurityDescriptor = SecurityDescriptor;
|
|
UnnamedObjectAttributes.ObjectName = NULL;
|
|
|
|
|
|
//
|
|
// Create an AccessState and wack on the token
|
|
Status = SeCreateAccessState(&LocalAccessState,
|
|
&AuxData,
|
|
DesiredAccess,
|
|
(PGENERIC_MAPPING)&WmipGenericMapping);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
SubjectContext = &LocalAccessState.SubjectSecurityContext;
|
|
SavedSubjectContext = *SubjectContext;
|
|
*SubjectContext = WmipSystemSubjectContext;
|
|
|
|
//
|
|
// Attach to system process so that the initial handle created
|
|
// by ObInsertObject is not available to user mode. This handle
|
|
// allows full access to the object.
|
|
KeAttachProcess( &PsInitialSystemProcess->Pcb );
|
|
|
|
Status = ObCreateObject(KernelMode,
|
|
WmipGuidObjectType,
|
|
&UnnamedObjectAttributes,
|
|
KernelMode,
|
|
NULL,
|
|
sizeof(WMIGUIDOBJECT),
|
|
0,
|
|
0,
|
|
(PVOID *)Object);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// Initialize WMIGUIDOBJECT structure
|
|
//
|
|
RtlZeroMemory(*Object, sizeof(WMIGUIDOBJECT));
|
|
|
|
KeInitializeEvent(&(*Object)->Event,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
(*Object)->HiPriority.MaxBufferSize = 0x1000;
|
|
(*Object)->LoPriority.MaxBufferSize = 0x1000;
|
|
(*Object)->Guid = *Guid;
|
|
|
|
//
|
|
// Take an extra refcount when inserting the object. We
|
|
// need this ref count so that we can ensure that the
|
|
// object will stick around while we are using it, but
|
|
// after a handle has been made available to user mode
|
|
// code. User mode can guess the handle and close it
|
|
// even before we return it.
|
|
//
|
|
Status = ObInsertObject(*Object,
|
|
&LocalAccessState,
|
|
DesiredAccess,
|
|
1,
|
|
&NewObject,
|
|
CreatorHandle);
|
|
|
|
WmipAssert(Status != STATUS_OBJECT_NAME_EXISTS);
|
|
}
|
|
|
|
*SubjectContext = SavedSubjectContext;
|
|
SeDeleteAccessState(&LocalAccessState);
|
|
|
|
KeDetachProcess( );
|
|
}
|
|
|
|
if (SecurityDescriptor != WmipAnyoneAccessSd)
|
|
{
|
|
ExFreePool(SecurityDescriptor);
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
VOID WmipCloseMethod(
|
|
IN PEPROCESS Process OPTIONAL,
|
|
IN PVOID Object,
|
|
IN ACCESS_MASK GrantedAccess,
|
|
IN ULONG ProcessHandleCount,
|
|
IN ULONG SystemHandleCount
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called whenever a guid object handle is closed. We
|
|
only need to worry about this for reply object and then only when the
|
|
last handle to it is closed.
|
|
|
|
Arguments:
|
|
|
|
Process
|
|
|
|
Object
|
|
|
|
GrantedAccess
|
|
|
|
ProcessHandleCount
|
|
|
|
SystemHandleCount
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PWMIGUIDOBJECT ReplyObject, RequestObject;
|
|
PLIST_ENTRY RequestList;
|
|
PMBREQUESTS MBRequest;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (SystemHandleCount == 1)
|
|
{
|
|
//
|
|
// Only clean up if there are no more valid handles left
|
|
//
|
|
ReplyObject = (PWMIGUIDOBJECT)Object;
|
|
|
|
if (ReplyObject->Flags & WMIGUID_FLAG_REPLY_OBJECT)
|
|
{
|
|
//
|
|
// When a reply object is closed we need to make sure that
|
|
// any referenece to it by a request object is cleaned up
|
|
//
|
|
ASSERT(ReplyObject->GuidEntry == NULL);
|
|
|
|
WmipEnterSMCritSection();
|
|
RequestList = ReplyObject->RequestListHead.Flink;
|
|
|
|
while (RequestList != &ReplyObject->RequestListHead)
|
|
{
|
|
//
|
|
//
|
|
MBRequest = CONTAINING_RECORD(RequestList,
|
|
MBREQUESTS,
|
|
RequestListEntry);
|
|
|
|
if (MBRequest->ReplyObject == ReplyObject)
|
|
{
|
|
RemoveEntryList(&MBRequest->RequestListEntry);
|
|
MBRequest->ReplyObject = NULL;
|
|
ObDereferenceObject(ReplyObject);
|
|
break;
|
|
}
|
|
|
|
RequestList = RequestList->Flink;
|
|
}
|
|
|
|
WmipLeaveSMCritSection();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID WmipDeleteMethod(
|
|
IN PVOID Object
|
|
)
|
|
{
|
|
PIRP Irp;
|
|
PWMIGUIDOBJECT GuidObject, ReplyObject;
|
|
PMBREQUESTS MBRequest;
|
|
WNODE_HEADER Wnode;
|
|
PREGENTRY RegEntry;
|
|
PBDATASOURCE DataSource;
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
|
|
GuidObject = (PWMIGUIDOBJECT)Object;
|
|
|
|
|
|
if (GuidObject->Flags & WMIGUID_FLAG_REQUEST_OBJECT)
|
|
{
|
|
//
|
|
// This is a request object that is going away so we need to
|
|
//
|
|
ASSERT(GuidObject->GuidEntry == NULL);
|
|
|
|
//
|
|
// First reply to all reply objects that are waiting for
|
|
// a reply
|
|
//
|
|
WmipEnterSMCritSection();
|
|
for (i = 0; i < MAXREQREPLYSLOTS; i++)
|
|
{
|
|
MBRequest = &GuidObject->MBRequests[i];
|
|
|
|
ReplyObject = MBRequest->ReplyObject;
|
|
if (ReplyObject != NULL)
|
|
{
|
|
Wnode.BufferSize = sizeof(WNODE_HEADER);
|
|
Wnode.Flags = WNODE_FLAG_INTERNAL;
|
|
Wnode.ProviderId = WmiRequestDied;
|
|
WmipWriteWnodeToObject(ReplyObject,
|
|
&Wnode,
|
|
TRUE);
|
|
|
|
RemoveEntryList(&MBRequest->RequestListEntry);
|
|
MBRequest->ReplyObject = NULL;
|
|
ObDereferenceObject(ReplyObject);
|
|
}
|
|
}
|
|
|
|
//
|
|
// next, unreference the regentry which will cause the regentry
|
|
// to get a ref count of 0 and then ultimately remove the
|
|
// DATASOURCE and all related data structures. But first make
|
|
// sure to remove the pointer from the datasource to the
|
|
// regentry
|
|
//
|
|
RegEntry = GuidObject->RegEntry;
|
|
if (RegEntry != NULL)
|
|
{
|
|
DataSource = RegEntry->DataSource;
|
|
if (DataSource != NULL)
|
|
{
|
|
DataSource->RequestObject = NULL;
|
|
}
|
|
|
|
RegEntry->Flags |= (REGENTRY_FLAG_RUNDOWN |
|
|
REGENTRY_FLAG_NOT_ACCEPTING_IRPS);
|
|
WmipUnreferenceRegEntry(RegEntry);
|
|
}
|
|
WmipLeaveSMCritSection();
|
|
|
|
} else if (GuidObject->Flags & WMIGUID_FLAG_REPLY_OBJECT) {
|
|
//
|
|
// This is a reply obejct that is going away
|
|
//
|
|
ASSERT(GuidObject->GuidEntry == NULL);
|
|
} else if (GuidObject->GuidEntry != NULL) {
|
|
//
|
|
// If there is a guid entry associated with the object
|
|
// then we need to see if we should disable collection
|
|
// or events and then remove the obejct from the
|
|
// guidentry list and finally remove the refcount on the guid
|
|
// entry held by the object
|
|
//
|
|
if (GuidObject->EnableRequestSent)
|
|
{
|
|
WmipDisableCollectOrEvent(GuidObject->GuidEntry,
|
|
GuidObject->Type,
|
|
0);
|
|
}
|
|
|
|
WmipEnterSMCritSection();
|
|
RemoveEntryList(&GuidObject->GEObjectList);
|
|
WmipLeaveSMCritSection();
|
|
|
|
WmipUnreferenceGE(GuidObject->GuidEntry);
|
|
}
|
|
|
|
if ((GuidObject->Flags & WMIGUID_FLAG_KERNEL_NOTIFICATION) == 0)
|
|
{
|
|
//
|
|
// Clean up any queued events and irps for UM objects
|
|
//
|
|
if (GuidObject->HiPriority.Buffer != NULL)
|
|
{
|
|
WmipFree(GuidObject->HiPriority.Buffer);
|
|
}
|
|
|
|
if (GuidObject->LoPriority.Buffer != NULL)
|
|
{
|
|
WmipFree(GuidObject->LoPriority.Buffer);
|
|
}
|
|
|
|
WmipEnterSMCritSection();
|
|
|
|
if (GuidObject->EventQueueAction == RECEIVE_ACTION_NONE)
|
|
{
|
|
Irp = GuidObject->Irp;
|
|
|
|
if (Irp != NULL)
|
|
{
|
|
//
|
|
// Since this object is going away and there is an irp waiting for
|
|
// we need to make sure that the object is removed from the
|
|
// irp's list.
|
|
//
|
|
WmipClearIrpObjectList(Irp);
|
|
|
|
if (IoSetCancelRoutine(Irp, NULL))
|
|
{
|
|
//
|
|
// If the irp has not been completed yet then we
|
|
// complete it now with an error
|
|
//
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = STATUS_INVALID_HANDLE;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
}
|
|
} else if (GuidObject->EventQueueAction == RECEIVE_ACTION_CREATE_THREAD) {
|
|
//
|
|
// If the object is going away and is part of a list of
|
|
// objects waiting for an event to start a thread, all we
|
|
// need to do is to removed the object from the list
|
|
//
|
|
WmipAssert(GuidObject->UserModeProcess != NULL);
|
|
WmipAssert(GuidObject->UserModeCallback != NULL);
|
|
WmipClearObjectFromThreadList(GuidObject);
|
|
}
|
|
WmipLeaveSMCritSection();
|
|
}
|
|
}
|
|
|
|
//
|
|
// The routines below were blantenly stolen without remorse from the ole
|
|
// sources in \nt\private\ole32\com\class\compapi.cxx. They are copied here
|
|
// so that WMI doesn't need to load in ole32 only to convert a guid string
|
|
// into its binary representation.
|
|
//
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: HexStringToDword (private)
|
|
//
|
|
// Synopsis: scan lpsz for a number of hex digits (at most 8); update lpsz
|
|
// return value in Value; check for chDelim;
|
|
//
|
|
// Arguments: [lpsz] - the hex string to convert
|
|
// [Value] - the returned value
|
|
// [cDigits] - count of digits
|
|
//
|
|
// Returns: TRUE for success
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
BOOLEAN
|
|
WmipHexStringToDword(
|
|
IN PWCHAR lpsz,
|
|
OUT PULONG RetValue,
|
|
IN ULONG cDigits,
|
|
IN WCHAR chDelim
|
|
)
|
|
{
|
|
ULONG Count;
|
|
ULONG Value;
|
|
|
|
PAGED_CODE();
|
|
|
|
Value = 0;
|
|
for (Count = 0; Count < cDigits; Count++, lpsz++)
|
|
{
|
|
if (*lpsz >= '0' && *lpsz <= '9')
|
|
Value = (Value << 4) + *lpsz - '0';
|
|
else if (*lpsz >= 'A' && *lpsz <= 'F')
|
|
Value = (Value << 4) + *lpsz - 'A' + 10;
|
|
else if (*lpsz >= 'a' && *lpsz <= 'f')
|
|
Value = (Value << 4) + *lpsz - 'a' + 10;
|
|
else
|
|
return(FALSE);
|
|
}
|
|
|
|
*RetValue = Value;
|
|
|
|
if (chDelim != 0)
|
|
return *lpsz++ == chDelim;
|
|
else
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
WmipUuidFromString (
|
|
IN PWCHAR StringUuid,
|
|
OUT LPGUID Uuid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We convert a UUID from its string representation into the binary
|
|
representation. Parse UUID such as 00000000-0000-0000-0000-000000000000
|
|
|
|
Arguments:
|
|
|
|
StringUuid - supplies the string representation of the UUID. It is
|
|
assumed that this parameter has been probed and captured
|
|
|
|
Uuid - Returns the binary representation of the UUID.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS or STATUS_INVALID_PARAMETER
|
|
|
|
--*/
|
|
{
|
|
ULONG dw;
|
|
PWCHAR lpsz = StringUuid;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (!WmipHexStringToDword(lpsz, &Uuid->Data1, sizeof(ULONG)*2, '-'))
|
|
{
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
lpsz += sizeof(ULONG)*2 + 1;
|
|
|
|
|
|
if (!WmipHexStringToDword(lpsz, &dw, sizeof(USHORT)*2, '-'))
|
|
{
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
Uuid->Data2 = (USHORT)dw;
|
|
lpsz += sizeof(USHORT)*2 + 1;
|
|
|
|
|
|
if (!WmipHexStringToDword(lpsz, &dw, sizeof(USHORT)*2, '-'))
|
|
{
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
Uuid->Data3 = (USHORT)dw;
|
|
lpsz += sizeof(USHORT)*2 + 1;
|
|
|
|
|
|
if (!WmipHexStringToDword(lpsz, &dw, sizeof(UCHAR)*2, 0))
|
|
{
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
Uuid->Data4[0] = (UCHAR)dw;
|
|
lpsz += sizeof(UCHAR)*2;
|
|
|
|
|
|
if (!WmipHexStringToDword(lpsz, &dw, sizeof(UCHAR)*2, '-'))
|
|
{
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
Uuid->Data4[1] = (UCHAR)dw;
|
|
lpsz += sizeof(UCHAR)*2+1;
|
|
|
|
|
|
if (!WmipHexStringToDword(lpsz, &dw, sizeof(UCHAR)*2, 0))
|
|
{
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
Uuid->Data4[2] = (UCHAR)dw;
|
|
lpsz += sizeof(UCHAR)*2;
|
|
|
|
|
|
if (!WmipHexStringToDword(lpsz, &dw, sizeof(UCHAR)*2, 0))
|
|
{
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
Uuid->Data4[3] = (UCHAR)dw;
|
|
lpsz += sizeof(UCHAR)*2;
|
|
|
|
|
|
if (!WmipHexStringToDword(lpsz, &dw, sizeof(UCHAR)*2, 0))
|
|
{
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
Uuid->Data4[4] = (UCHAR)dw;
|
|
lpsz += sizeof(UCHAR)*2;
|
|
|
|
if (!WmipHexStringToDword(lpsz, &dw, sizeof(UCHAR)*2, 0))
|
|
{
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
Uuid->Data4[5] = (UCHAR)dw;
|
|
lpsz += sizeof(UCHAR)*2;
|
|
|
|
|
|
if (!WmipHexStringToDword(lpsz, &dw, sizeof(UCHAR)*2, 0))
|
|
{
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
Uuid->Data4[6] = (UCHAR)dw;
|
|
lpsz += sizeof(UCHAR)*2;
|
|
|
|
|
|
if (!WmipHexStringToDword(lpsz, &dw, sizeof(UCHAR)*2, 0))
|
|
{
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
Uuid->Data4[7] = (UCHAR)dw;
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
NTSTATUS
|
|
WmipCheckGuidAccess(
|
|
IN LPGUID Guid,
|
|
IN ACCESS_MASK DesiredAccess
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allows checking if the current user has the rights to access a guid.
|
|
|
|
Arguments:
|
|
|
|
Guid is the guid whose security is to be checked
|
|
|
|
DesiredAccess is the access that is desired by the user.
|
|
NOTE: This does not support GENERIC_* mappings or
|
|
ASSIGN_SYSTEM_SECURITY
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS or error
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN Granted;
|
|
ACCESS_MASK PreviousGrantedAccess = 0;
|
|
NTSTATUS Status;
|
|
ACCESS_MASK GrantedAccess;
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor;
|
|
UNICODE_STRING GuidString;
|
|
WCHAR GuidBuffer[38];
|
|
SECURITY_SUBJECT_CONTEXT SecuritySubjectContext;
|
|
|
|
PAGED_CODE();
|
|
|
|
swprintf(GuidBuffer,
|
|
L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
|
Guid->Data1, Guid->Data2,
|
|
Guid->Data3,
|
|
Guid->Data4[0], Guid->Data4[1],
|
|
Guid->Data4[2], Guid->Data4[3],
|
|
Guid->Data4[4], Guid->Data4[5],
|
|
Guid->Data4[6], Guid->Data4[7]);
|
|
RtlInitUnicodeString(&GuidString, GuidBuffer);
|
|
|
|
Status = WmipGetGuidSecurityDescriptor(&GuidString,
|
|
&SecurityDescriptor);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
SeCaptureSubjectContext(&SecuritySubjectContext);
|
|
|
|
Granted = SeAccessCheck (SecurityDescriptor,
|
|
&SecuritySubjectContext,
|
|
FALSE,
|
|
DesiredAccess,
|
|
PreviousGrantedAccess,
|
|
NULL,
|
|
(PGENERIC_MAPPING)&WmipGenericMapping,
|
|
UserMode,
|
|
&GrantedAccess,
|
|
&Status);
|
|
|
|
SeReleaseSubjectContext(&SecuritySubjectContext);
|
|
|
|
if (SecurityDescriptor != WmipAnyoneAccessSd)
|
|
{
|
|
ExFreePool(SecurityDescriptor);
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
#endif
|
|
|