Driver security responsibility (Windows security model)

This article describes driver security responsibility in the Windows security model.

The drivers for a device are responsible for ensuring that unauthorized users do not have access to the device. Ensuring device security involves the following:

  • Creating secure device objects.
  • Securing the device namespace.
  • Specifying device characteristics and security settings in INF files.
  • Defining and handling IOCTLs securely.

Create secure device objects

Every device object has a security descriptor, which contains an ACL that controls access to the device. In general, the security descriptor is created at the same time as the device object, and the ACL is specified in the INF file for the device, but the details vary depending on the position of the driver in the device stack and the type of device it controls. The following sections describe the specific requirements for:

  • Bus drivers
  • Other Plug and Play and Windows Driver Model (WDM) drivers
  • Legacy devices

Bus drivers

A WDM bus driver for a device that can be run in raw mode must use the IoCreateDeviceSecure routine to create its physical device object (PDO) and set a strong default ACL. For example, the ACL for a raw-mode device PDO might set SDDL_DEVOBJ_SYS_ALL, which allows access for the System (SY) but denies access for everyone else: “D:P(A;;GA;;;SY)”.

This ACL blocks even the Administrator from accessing the device. However, the INF file for the device can loosen the ACL to allow access by the Administrator or other valid users. Because a raw-mode device can be started without an INF file, specifying a strong default ACL when creating the device object is critical. Otherwise, if the device has no INF file, a user could gain access to the device without any security checks whatsoever.

If the device cannot be used in raw mode, the driver can call either the IoCreateDevice or the IoCreateDeviceSecure routine to create the device object. If the driver uses IoCreateDevice, the PnP Manager applies a default security descriptor just as it does for other WDM device objects. However, if the PDO requires stricter security settings than the default provides, the INF file should supply a security descriptor.

Plug and Play and WDM drivers

Plug and Play (PnP) and WDM drivers (except for bus drivers, as described in “Bus drivers”) call the IoCreateDevice routine to create an unnamed device object. The PnP Manager applies a default security descriptor to each such unnamed device object.

The INF file for the device should specify the device-specific ACL. The PnP Manager ensures that the ACL is applied to all device objects in the device stack, thereby securing the entire stack before allowing other processes any access to the device.

In the INF file, ACLs are specified in SDDL and appear in an AddReg section. The AddReg section can also set device characteristics, such as FILE_DEVICE_SECURE_OPEN.

Plug and Play and WDM drivers must not use the IoCreateDeviceSecure routine to create device objects that attach to the device stack. However, some Plug and Play drivers create named control device objects, which do not attach to the device stacks. Such control device objects must be created with the IoCreateDeviceSecure routine.

Legacy devices

A legacy device is a device that is not controlled by the PnP Manager. Legacy drivers must create at least one named device object in the \Device object directory to receive I/O requests.

To secure the device object for a legacy device, its driver must call the IoCreateDeviceSecure routine to create a named device object and to set the default security descriptor and class GUID for the device. The security descriptor should specify a strong default ACL, such as SDDL_DEVOBJ_SYS_ALL. This setting allows kernel-mode code and user-mode code running as System to access the device object. At run time, the driver’s service (running as System) can open the device to individual users by using the SetFileSecurity user-mode function.

Secure the device namespace

The driver is responsible for checking security when a caller tries to open an object in the device namespace. A caller can specify an object in any of the following formats.

PathDescription
\Device\DeviceName Device object for DeviceName
\Device\DeviceName\ Top-level directory on DeviceName
\Device\DeviceName\File File on DeviceName

 

The I/O Manager is considered the type owner for device objects; thus, the I/O Manager is responsible for checking security whenever a caller specifies a name in the form \Device\DeviceName.

The driver is considered the type owner for objects in its namespace. The namespace includes the top-level directory for the device (\Device\DeviceName\) and any objects subordinate to this directory (\Device\DeviceName\File).

Open devices securely

A user opens a device by specifying the device name. For example:

\Device\Serial0

When the I/O Manager receives an open request, it parses the target device name and checks the rights in the process’s access token against the ACL of the target device object. By denying traversal rights to its device object, a driver can deny all access to its device.

If a device stack has two named device objects, a process can use either of them to open the device stack. In this case, IRPs intended for either device object are sent to the same stack, so the ACLs for the two device objects must agree.

As a general rule, the PDO should be the only named device object in a stack. Drivers should not name FDOs unless necessary, but there are a few exceptions. For example, the FDO for a storage device that has a Volume Parameter Block (VPB) must have a name.

An exclusive device is a device for which only one handle can be open at a time. WDM drivers designate a device as exclusive in an AddReg directive in the INF file. As a result of the AddReg directive, the system sets the DO_EXCLUSIVE bit in the Flags field of the device object. If DO_EXCLUSIVE is set, the I/O Manager enforces exclusivity by checking open requests against the device namespace. If DO_EXCLUSIVE is not set, the I/O manager does not check open requests against the device namespace; instead, the driver must do so.

Even if the DO_EXCLUSIVE bit is set for a device object, an application that has a handle to the device stack nevertheless might be able to obtain additional handles by opening “” relative to the existing handle. To prevent this problem, the driver should fail any IRP_MJ_CREATE requests for related file objects. The driver should check the value of the RelatedFileObject field in the IRP_MJ_CREATE request as follows:


if ( IrpSp->FileObject->RelatedFileObject != NULL)

A non-NULL value in the RelatedFileObject field indicates that another handle is already open. The driver must fail the request if this value is not NULL.

Open files securely

Open requests in the following forms specify files or other objects in the device namespace:

\Device\Floppy0\Readme.txt
\Device\Mup\Server\Share\File.txt
\Device\Serial0\
Note  The trailing backslash in \Device\Serial0\ denotes the top-level directory on the device Serial0.
 

By default, the driver is responsible for checking security for objects within its device namespace. If the driver does not support a device namespace, it should simply set the FILE_DEVICE_SECURE_OPEN characteristic in the device object. If this flag is set, the operating system applies the security descriptor of the device object to all open requests in the device namespace. If the ACL for the device object does not allow access, the I/O Manager will fail the request.

Any driver that does not support a device namespace, and does not set the FILE_DEVICE_SECURE_OPEN flag must fail any IRP_MJ_CREATE requests for objects within the object’s namespace. The driver should check the file name length in the IRP_MJ_CREATE request as follows:


if ( IrpSp->FileObject->Filename.Length != 0 )


If the length is nonzero, the driver must fail the request with a status indicating that the device does not support file opens

In general, drivers that support a device namespace should also set the FILE_DEVICE_SECURE_OPEN characteristic. The only exception occurs when the device namespace requires different security from the device object; a volume device object of a file system is one such exception. Drivers that support a namespace but do not set this flag must check the ACL of the target file against the access token for the requesting process.

A driver can set the FILE_DEVICE_SECURE_OPEN flag in its call to the IoCreateDevice or IoCreateDeviceSecure routine or in the INF file for the device. The I/O Manager checks this field at the top of the device stack. Therefore, filter drivers must copy the Characteristics field from the next lower driver when they attach to the stack.

Specify device characteristics and security settings in INF files

Most drivers should specify device characteristics and security settings in the device INF file. The values in the INF file override the defaults for the security descriptor for the device class. For WDM drivers, specifying security settings in the INF file is the preferred method. Before starting a WDM device stack, the PnP Manager propagates the security settings specified in the INF file to the security descriptors for the drivers in the stack.

The following sample, part of an INF file, shows how to set device characteristics and security:


[MyDevice.NTx86]
CopyFiles = ...
[MyDevice.NTx86.Services]
AddService = ...
[MyDevice.NTx86.HW]
AddReg = MyDevice.Security
[MyDevice.Security]
HKR,,DeviceCharacteristics,0x10001,0x100
HKR,,Security,,”D:P(A;;GA;;;SY)”

In the preceding sample, the AddReg directive in the MyDevice.NTx86.HW section specifies the Security section named MyDevice.Security. In the MyDevice.Security section, the DeviceCharacteristics entry specifies the flag 0x10001 to indicate that the next value is a DWORD, and sets 0x100, which specifies the FILE_DEVICE_SECURE_OPEN characteristic. The Security entry, formatted in SDDL, allows system-only access. Such a security string is appropriate for a bus driver that creates a raw PDO.

Define and handle IOCTLs securely

Drivers must properly define IOCTL control codes and must properly handle and validate I/O requests received in IOCTLs.

An IOCTL control code has the following format:

IOCTL control code format

Bits 15 and 14 of the I/O control code contain a mask describing the requested access. The access mask can specify the following rights:

  • FILE_ANY_ACCESS: Allows access to any caller that has a handle to the file object specified in the request. (On Windows Server 2003, the caller must have at least one validated right.)
  • FILE_READ_DATA: Allows the caller to request data from the file object.
  • FILE_WRITE_DATA: Allows the caller to write data to the file object.
  • FILE_READ_DATA OR’ed with FILE_WRITE_DATA: Allows the caller to read and write data to the object.

When a caller sends an IOCTL, the I/O Manager checks the required access specified in the control code against the rights granted to the caller, which are stored in the object handle. If the caller has adequate rights, the I/O Manager forwards the IRP to the device stack. If the caller has insufficient rights, the I/O Manager fails the IRP.

Define secure IOCTLs

When defining an IOCTL for user-mode callers, a driver should always specify a required access value in the CTL_CODE macro. In the past, many drivers specified FILE_ANY_ACCESS as the required access value. However, this value allows virtually unrestricted access to the device. Unless you are absolutely certain that allowing unrestricted access to your device does not leave the system vulnerable to a malicious user, do not specify FILE_ANY_ACCESS in the RequiredAccess field. Instead, specify FILE_READ_DATA or FILE_WRITE_DATA or the union of these two values, as appropriate.

Handle IOCTLs securely

The I/O Manager checks access rights for all IRPs that contain IOCTLs. Rights granted to the caller are stored in the object handle, which is opaque to drivers. If the caller has insufficient rights, the I/O Manager does not send the IOCTL to the device stack.

Some system-defined and many driver-defined IOCTLs are defined with FILE_ANY_ACCESS as the required access value. To tighten security when such IOCTLs are sent by user-mode callers, a driver can use the IoValidateDeviceIoControlAccess function. This function allows a driver to check access rights.

Note  IoValidateDeviceIoControlAccess is documented in the Windows Driver Kit (WDK) and is available on Windows Server 2003 and later operating systems. Drivers that must also work for Windows 2000 and Windows XP must link to wdmsec.lib to use this routine.
 

Upon receiving an IOCTL, a driver can call IoValidateDeviceIoControlAccess, specifying FILE_READ_ACCESS, FILE_WRITE_ACCESS, or both. In response, the I/O Manager checks the access rights granted to the caller. If the caller does not have the specified rights, the driver can fail the IRP with an appropriate status.

A driver can also check system-wide privileges. For example, the Swenum.sys driver tests the Load/Unload Driver privilege before it forwards an IRP down the device stack. Drivers should check privileges when passing an IRP down the device stack. When the IRP is returning back up the device stack, checking privilege is unnecessary because the I/O is already complete.

 

 

Send comments about this topic to Microsoft

Show: