470 lines
13 KiB
C
470 lines
13 KiB
C
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
IoDevObj.c
|
|
|
|
Abstract:
|
|
|
|
This module contains functions for managing device objects.
|
|
|
|
Author:
|
|
|
|
Adrian J. Oney - April 21, 2002
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "WlDef.h"
|
|
#include "IopDevObj.h"
|
|
#pragma hdrstop
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, IoDevObjCreateDeviceSecure)
|
|
#pragma alloc_text(PAGE, IopDevObjAdjustNewDeviceParameters)
|
|
#pragma alloc_text(PAGE, IopDevObjApplyPostCreationSettings)
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
IoDevObjCreateDeviceSecure(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN ULONG DeviceExtensionSize,
|
|
IN PUNICODE_STRING DeviceName OPTIONAL,
|
|
IN DEVICE_TYPE DeviceType,
|
|
IN ULONG DeviceCharacteristics,
|
|
IN BOOLEAN Exclusive,
|
|
IN PCUNICODE_STRING DefaultSDDLString,
|
|
IN LPCGUID DeviceClassGuid OPTIONAL,
|
|
OUT PDEVICE_OBJECT *DeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates a securable named device object. The security settings
|
|
for the device object are retrieved from the registry or constructed using
|
|
the passed in defaults if registry overrides are not available.
|
|
|
|
It should be used
|
|
1. To secure legacy device objects
|
|
2. To secure raw PnP PDOs
|
|
|
|
It should not be used to create FDOs, non-raw PDOs, or any unnamed objects.
|
|
For those operations, IoCreateDevice should be used.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - A pointer to the driver object for this device.
|
|
|
|
DeviceExtensionSize - Size, in bytes, of extension to device object;
|
|
i.e., the size of the driver-specific data for this device object.
|
|
|
|
DeviceName - Optional name that should be associated with this device.
|
|
If the DeviceCharacteristics has the FILE_AUTOGENERATED_DEVICE_NAME
|
|
flag set, this parameter is ignored.
|
|
|
|
DeviceType - The type of device that the device object should represent.
|
|
Possibly overriden by registry.
|
|
|
|
DeviceCharacteristics - The characteristics for the device. Additional
|
|
flags may be supplied by the registry.
|
|
|
|
Exclusive - Indicates that the device object should be created with using
|
|
the exclusive object attribute. Possibly overriden by registry.
|
|
|
|
NOTE: This flag should not be used for WDM drivers. Since only the
|
|
PDO is named, it is the only device object in a devnode attachment
|
|
stack that is openable. However, since this device object is created
|
|
by the underlying bus driver (which has no knowledge about what type
|
|
of device this is), there is no way to know whether this flag should
|
|
be set. Therefore, this parameter should always be FALSE for WDM
|
|
drivers. Drivers attached to the PDO (e.g., the function driver) must
|
|
enforce any exclusivity rules.
|
|
|
|
DefaultSDDLString - In the absense of registry settings, this string
|
|
specifies the security to supply for the device object.
|
|
|
|
Only the subset of the SDDL format is currently supported. The format
|
|
is:
|
|
D:P(ACE)(ACE)(ACE), where (ACE) is (AceType;;Access;;;SID)
|
|
|
|
Where:
|
|
|
|
AceType - Only Allow ("A") is supported.
|
|
Access - Rights specified in either hex format (0xnnnnnnnn), or via the
|
|
SDDL Generic/Standard abbreviations
|
|
SID - Abbreviated security ID
|
|
(WD, BA, SY, IU, RC, AU, NU, AN, BG, BU, LS, NS)
|
|
The S-w-x-y-z form for SIDs is not supported
|
|
|
|
The unimplemented ace fields are:
|
|
|
|
AceFlags - Describes features such as inheritance for sub-objects
|
|
(ie files) and containers (ie keys/folders). An example SDDL
|
|
AceFlag string would be ("OICI"). While control over
|
|
inheritance is crucial for registry keys and files, it's
|
|
irrelevant for device objects. As such, this function supports
|
|
no ACE flags.
|
|
|
|
ObjectGuid - Used for describing rights that transcent the 32bit
|
|
mask supplied by the OS. Typically used for Active Directory
|
|
objects.
|
|
|
|
InheritObjectGuid - - Used for describing rights that transcent the
|
|
32bit mask supplied by the OS. Typically used for Active
|
|
Directory objects.
|
|
|
|
Example -
|
|
"D:P(A;;GA;;;SY)" which is Allow System to have Generic All access.
|
|
|
|
DeviceClassGuid - Supplies a device install class GUID. This class is
|
|
looked up in the registry to see if any potential overrides exist.
|
|
For legacy device objects, the caller may need to invent an appropriate
|
|
class GUID (see IoCreateDeviceSecure documention on how to properly
|
|
install a full class).
|
|
|
|
Note that if no registry override exists, the registry will
|
|
automatically be updated to *reflect* the default SDDL string.
|
|
Therefore it is a very bad idea to use the same device class GUID with
|
|
different DefaultSDDLString values (objects needing different default
|
|
security should have different classes, or be secured via INFs where
|
|
possible).
|
|
|
|
DeviceObject - Pointer to the device object pointer this routine will
|
|
return.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
{
|
|
PSECURITY_DESCRIPTOR securityDescriptor;
|
|
STACK_CREATION_SETTINGS stackSettings, updateSettings;
|
|
PDEVICE_OBJECT newDeviceObject;
|
|
UNICODE_STRING classKeyName;
|
|
DEVICE_TYPE finalDeviceType;
|
|
ULONG finalCharacteristics;
|
|
BOOLEAN finalExclusivity;
|
|
ULONG disposition;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Preinit for failure
|
|
//
|
|
*DeviceObject = NULL;
|
|
newDeviceObject = NULL;
|
|
|
|
//
|
|
// The device object is securable only if it has a name. Therefore, we fail
|
|
// the create call if the device doesn't have a name.
|
|
//
|
|
if (!(ARGUMENT_PRESENT(DeviceName) ||
|
|
(DeviceCharacteristics & FILE_AUTOGENERATED_DEVICE_NAME))) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(DeviceClassGuid)) {
|
|
|
|
//
|
|
// Try to find the appropriate security descriptor for the device. First
|
|
// look for an override in the registry using the class GUID. We will
|
|
// create a section in the registry if one doesn't exist as well. This is
|
|
// a clue to the system administrator that there is something to lock down
|
|
// in the system.
|
|
//
|
|
status = PpRegStateReadCreateClassCreationSettings(
|
|
DeviceClassGuid,
|
|
DriverObject,
|
|
&stackSettings
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
return status;
|
|
}
|
|
|
|
} else {
|
|
|
|
PpRegStateInitEmptyCreationSettings(&stackSettings);
|
|
}
|
|
|
|
//
|
|
// If a registry setting wasn't specified, parse the default SDDL string.
|
|
//
|
|
if (!(stackSettings.Flags & DSIFLAG_SECURITY_DESCRIPTOR)) {
|
|
|
|
//
|
|
// Parse the SDDL string into a security descriptor, and mark it
|
|
// "default" as well. SE_DACL_DEFAULT means the DACL came from a
|
|
// "default" mechanism, typically implying parental inheritance or
|
|
// object default security. In our case, the "default source" is the
|
|
// library as opposed to the user or an INF.
|
|
//
|
|
status = SeSddlSecurityDescriptorFromSDDL(
|
|
DefaultSDDLString,
|
|
TRUE,
|
|
&securityDescriptor
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
PpRegStateLoadSecurityDescriptor(
|
|
securityDescriptor,
|
|
&stackSettings
|
|
);
|
|
|
|
if (ARGUMENT_PRESENT(DeviceClassGuid)) {
|
|
|
|
//
|
|
// Update the registry with the default SDDL string so that the
|
|
// admin knows what settings are being used for this class. Note
|
|
// that we don't free updateSettings, as the security descriptor
|
|
// is also used by stackSettings.
|
|
//
|
|
PpRegStateInitEmptyCreationSettings(&updateSettings);
|
|
|
|
PpRegStateLoadSecurityDescriptor(
|
|
securityDescriptor,
|
|
&updateSettings
|
|
);
|
|
|
|
status = PpRegStateUpdateStackCreationSettings(
|
|
DeviceClassGuid,
|
|
&updateSettings
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Fill out the default values
|
|
//
|
|
finalDeviceType = DeviceType;
|
|
finalCharacteristics = DeviceCharacteristics;
|
|
finalExclusivity = Exclusive;
|
|
|
|
//
|
|
// Adjust the parameters based on the registry overrides
|
|
//
|
|
IopDevObjAdjustNewDeviceParameters(
|
|
&stackSettings,
|
|
&finalDeviceType,
|
|
&finalCharacteristics,
|
|
&finalExclusivity
|
|
);
|
|
|
|
//
|
|
// Create the device object. The newly created object should have the
|
|
// DO_DEVICE_INITIALIZING flag on it, meaning it cannot be opened. We
|
|
// therefore still have an opportunity to apply security.
|
|
//
|
|
status = IoCreateDevice(
|
|
DriverObject,
|
|
DeviceExtensionSize,
|
|
DeviceName,
|
|
finalDeviceType,
|
|
finalCharacteristics,
|
|
finalExclusivity,
|
|
&newDeviceObject
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
ASSERT(newDeviceObject->Flags & DO_DEVICE_INITIALIZING);
|
|
|
|
status = IopDevObjApplyPostCreationSettings(
|
|
newDeviceObject,
|
|
&stackSettings
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
IoDeleteDevice(newDeviceObject);
|
|
goto Exit;
|
|
}
|
|
|
|
*DeviceObject = newDeviceObject;
|
|
|
|
Exit:
|
|
|
|
//
|
|
// Clean up the security descriptor as appropriate.
|
|
//
|
|
PpRegStateFreeStackCreationSettings(&stackSettings);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID
|
|
IopDevObjAdjustNewDeviceParameters(
|
|
IN PSTACK_CREATION_SETTINGS StackCreationSettings,
|
|
IN OUT PDEVICE_TYPE DeviceType,
|
|
IN OUT PULONG DeviceCharacteristics,
|
|
IN OUT PBOOLEAN Exclusive
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine adjusts a newly created device object to reflect the passed
|
|
in stack creation settings.
|
|
|
|
Arguments:
|
|
|
|
StackCreationSettings - Information reflecting the settings to apply.
|
|
|
|
DeviceType - On input, the device type specified by the caller. This field
|
|
is updated to reflect any changes specified in the registry.
|
|
|
|
DeviceCharacteristics - On input, the characteristics specified by the
|
|
caller. This field is updated to reflect any changes specified in the
|
|
registry.
|
|
|
|
Exclusive - On input, the exclusivity specified by the caller. This field
|
|
is updated to reflect any changes specified in the registry.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (StackCreationSettings->Flags & DSIFLAG_DEVICE_TYPE) {
|
|
|
|
*DeviceType = StackCreationSettings->DeviceType;
|
|
}
|
|
|
|
if (StackCreationSettings->Flags & DSIFLAG_CHARACTERISTICS) {
|
|
|
|
*DeviceCharacteristics = StackCreationSettings->Characteristics;
|
|
}
|
|
|
|
if (StackCreationSettings->Flags & DSIFLAG_EXCLUSIVE) {
|
|
|
|
*Exclusive = (BOOLEAN) StackCreationSettings->Exclusivity;
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IopDevObjApplyPostCreationSettings(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PSTACK_CREATION_SETTINGS StackCreationSettings
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine adjusts a newly created device object to reflect the passed
|
|
in stack creation settings.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Device object who's settings to adjust.
|
|
|
|
StackCreationSettings - Information reflecting the settings to apply.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
{
|
|
SECURITY_INFORMATION securityInformation;
|
|
ACCESS_MASK desiredAccess;
|
|
BOOLEAN fromDefaultSource;
|
|
NTSTATUS status;
|
|
HANDLE handle;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (!(StackCreationSettings->Flags & DSIFLAG_SECURITY_DESCRIPTOR)) {
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Get the corresponding securityInformation from the descriptor.
|
|
//
|
|
status = SeUtilSecurityInfoFromSecurityDescriptor(
|
|
StackCreationSettings->SecurityDescriptor,
|
|
&fromDefaultSource,
|
|
&securityInformation
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
return status;
|
|
}
|
|
|
|
#ifdef _KERNELIMPLEMENTATION_
|
|
|
|
status = ObSetSecurityObjectByPointer(
|
|
DeviceObject,
|
|
securityInformation,
|
|
StackCreationSettings->SecurityDescriptor
|
|
);
|
|
|
|
#else
|
|
|
|
//
|
|
// Since ObSetSecurityObjectByPointer isn't available on Win2K, we have to
|
|
// use a rather sneaky trick. The device technically isn't openable yet.
|
|
// However, ObOpenObjectByPointer doesn't bother doing any parse stuff.
|
|
// Therefore, we can get a quick handle to the object, set the security
|
|
// descriptor, and then dump the handle without the driver being any wiser.
|
|
//
|
|
SeSetSecurityAccessMask(securityInformation, &desiredAccess);
|
|
|
|
status = ObOpenObjectByPointer(
|
|
DeviceObject,
|
|
OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
desiredAccess,
|
|
*IoDeviceObjectType,
|
|
KernelMode,
|
|
&handle
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
return status;
|
|
}
|
|
|
|
status = ZwSetSecurityObject(
|
|
handle,
|
|
securityInformation,
|
|
StackCreationSettings->SecurityDescriptor
|
|
);
|
|
|
|
ZwClose(handle);
|
|
|
|
#endif // _KERNELIMPLEMENTATION_
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|