WindowsXP/Source/XPSP1/NT/base/wdmlib/wdmsec/io/iodevobj.c
2024-08-03 16:30:48 +02:00

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;
}