Windows Security Model: What Every Driver Writer Needs to Know
Updated: July 7, 2004
On This Page
This paper provides information about writing secure kernel-mode drivers for the Microsoft Windows family of operating systems. It describes how the Windows security model applies to drivers and explains what driver writers must do to ensure the security of their devices.
The Windows security model is based on securable objects. Each component of the operating system must ensure the security of the objects for which it is responsible. Drivers, therefore, must safeguard the security of their devices and device objects.
This paper summarizes how the Windows security model applies to kernel-mode drivers and what drivers must do to ensure the security of their devices. For some types of devices, additional device-specific requirements apply. See the device-specific documentation in the Windows Driver Kit (WDK) for details.
Note: For current documentation on routines and issues discussed in this paper, see the most recent version of the Windows Driver Kit (WDK).
Windows Security Model
The Windows security model is based primarily on per-object rights, with a small number of system-wide privileges. Objects that can be secured includebut are not limited toprocesses, threads, events and other synchronization objects, as well as files, directories, and devices.
For each type of object, the generic read, write, and execute rights map into detailed object-specific rights. For example, for files and directories, possible rights include the right to read or write the file or directory, the right to read or write extended file attributes, the right to traverse a directory, and the right to write an objects security descriptor. For more information, including a complete list of rights, see Security (General) in the Security section of the MSDN Library.
The security model involves the following concepts:
Security Identifiers (SIDs)
A security identifier (SID, also called a principal) identifies a user, a group, or a logon session. Each user has a unique SID, which is retrieved by the operating system at logon.
SIDs are issued by an authority such as the operating system or a domain server. Some SIDs are well-known and have names as well as identifiers. For example, the SID S-1-1-0 identifies Everyone (or World).
Every process has an access token. The access token describes the complete security context of the process. It contains the SID of the user, the SID of the groups to which the user belongs, and the SID of the logon session, as well as a list of the system-wide privileges granted to the user.
By default, the system uses the primary access token for a process whenever a thread of the process interacts with a securable object. However, a thread can impersonate a client account. When a thread impersonates, it has an impersonation token in addition to its own primary token. The impersonation token describes the security context of the user account that the thread is impersonating. Impersonation is especially common in Remote Procedure Call (RPC) handling.
An access token that describes a restricted security context for a thread or process is called a restricted token. The SIDs in a restricted token can be set only to deny access, not to allow access, to securable objects. In addition, the token can describe a limited set of system-wide privileges. The users SID and identity remain the same, but the users access rights are limited while the process is using the restricted token. The CreateRestrictedToken function creates a restricted token.
Restricted tokens are useful for running untrusted code, such as email attachments. Microsoft Windows XP uses a restricted token when you right-click an executable file, select Run As, and select "Protect my computer and data from unauthorized program activity."
Every named Windows object has a security descriptor; some unnamed objects do, too. The security descriptor describes the owner and group SIDs for the object along with its ACLs.
An objects security descriptor is usually created by the function that creates the object. When a driver calls the IoCreateDevice or IoCreateDeviceSecure routine to create a device object, the system applies a security descriptor to the created device object and sets ACLs for the object. For most devices, ACLs are specified in the device Information (INF) file.
Access Control Lists
Access Control Lists (ACLs) enable fine-grained control over access to objects. An ACL is part of the security descriptor for each object.
Each ACL contains zero or more access control entries (ACE). Each ACE, in turn, contains a single SID that identifies a user, group, or computer and a list of rights that are denied or allowed for that SID.
ACLs for Device Objects
The ACL for a device object can be set in any of three ways:
All new drivers should use SDDL in the INF file to specify ACLs for their device objects.
SDDL is an extensible description language that enables components to create ACLs in a string format. SDDL is used by both user-mode and kernel-mode code. Figure 1 shows the format of SDDL strings for device objects.
Figure 1. SDDL Strings for Device Objects
The Access value specifies the type of access allowed. The SID value specifies a security identifier that determines to whom the Access value applies (for example, a user or group).
For example, the following SDDL string allows the System (SY) access to everything and allows everyone else (WD) only read access:
The header file wdmsec.h also includes a set of predefined SDDL strings that are suitable for device objects. For example, the header file defines SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_RWX as follows:
The first segment of this string allows the kernel and operating system (SY) complete control over the device. The second segment allows anyone in the built-in Administrators group (BA) to access the entire device, but not to change the ACL. The third segment allows everyone (WD) to read or write to the device, and the fourth segment grants the same rights to untrusted code (RC). Drivers can use the predefined strings as is or as models for device-object-specific strings.
All device objects in a stack should have the same ACLs. Changing the ACLs on one device object in the stack changes the ACLs on the entire device stack.
However, adding a new device object to the stack does not change any ACLs, either those of the new device object (if it has ACLs) or those of any existing device objects in the stack. When a driver creates a new device object and attaches it to the top of the stack, the driver should copy the ACLs for the stack to the new device object by copying the DeviceObject.Characteristics field from the next lower driver.
The IoCreateDeviceSecure routine supports a subset of SDDL strings that use predefined SIDs such as WD and SY. User-mode APIs and INF files support the full SDDL syntax.
Security Checks Using ACLs
When a process requests access to an object, security checks compare the ACLs for the object against the SIDs in the callers access token.
The system compares the ACEs in a strict top-down order and stops on the first relevant match. Therefore, when creating an ACL, you should always put denial ACEs above the corresponding grant ACEs. The following examples show how the comparison proceeds.
Example 1: Comparing an ACL to an Access Token
Example 1 shows how the system compares an ACL to the access token for a callers process. Assume that the caller wants to open a file that has the ACL shown in Table 1.
Table 1. Sample File ACL
This ACL has four ACEs, which apply specifically to the Accounting, Sales, Legal, and Everyone groups.
Next, assume the access token for the requesting process contains SIDs for one user and three groups, in the following order:
User Jim (S-1-5-21 )
Group Accounting (S-1-5-22 )
Group Legal (S-1-5-23 )
Group Everyone (S-1-1-0)
When comparing a file ACL to an access token, the system first looks for an ACE for user Jim in the files ACL. None appears, so next it looks for an ACE for the Accounting group. As Table 1 shows, an ACE for the Accounting group appears as the first entry in the files ACL, so Jims process is granted the right to write or delete the file and the comparison stops. If the ACE for the Legal group instead preceded the ACE for the Accounting group in the ACL, the process would be denied write, append, and delete access to the file.
Example 2: Comparing an ACL to a Restricted Token
The system compares an ACL to a restricted token in the same way that it compares those in a token that is not restricted. However, a denial SID in a restricted token can match only a Deny ACE in an ACL.
Example 2 shows how the system compares a files ACL with a restricted token. Assume the file has the same ACL shown previously in Table 1. In this example, however, the process has a restricted token that contains the following SIDs:
User Jim (S-1-5-21 ) Deny
Group Accounting (S-1-5-22 ) Deny
Group Legal (S-1-5-23 ) Deny
Group Everyone (S-1-1-0)
The files ACL does not list Jims SID, so the system proceeds to the Accounting group SID. Although the files ACL has an ACE for the Accounting group, this ACE allows access; therefore, it does not match the SID in the processs restricted token, which denies access. As a result, the system proceeds to the Legal group SID. The ACL for the file contains an ACE for the Legal group that denies access, so the process cannot write, append, or delete the file.
A privilege is the right for a user to perform a system-related operation on the local computer, such as loading a driver, changing the time, or shutting down the system.
Privileges are different from access rights because they apply to system-related tasks and resources rather than objects, and because they are assigned to a user or group by a system administrator, rather than by the operating system.
The access token for each process contains a list of the privileges granted to the process. Privileges must be specifically enabled before use. An administrator enables and audits the use of privileges by using Administrative Tools in the Windows Control Panel; privileges can also be enabled programmatically.
Security Scenario: Creating a File
The system uses the security constructs described in Windows Security Model whenever a process creates a handle to a file or object. Figure 2 shows the security-related actions that are triggered when a user-mode process attempts to create a file.
Figure 2. Processing a CreateFile Request
Figure 2 shows how the system responds when a user-mode application calls the CreateFile function. The following notes refer to the circled numbers in the figure:
Security Checks in the Object Manager
The responsibility for checking access rights belongs to the highest-level component that can perform such checks. If the Object Manager can verify the callers access rights, it does so. If not, the Object Manager passes the request to the component responsible for the underlying object type. That component, in turn, verifies access, if it can; if it cannot, it passes the request to a still-lower component, such as a driver.
The Object Manager checks ACLs for simple object types, such as events and mutex locks. For objects that have a namespace, the type owner performs security checks. For example, the I/O Manager is considered the type owner for device objects and file objects. If the Object Manager finds the name of a device object or file object when parsing a name, it hands off the name to the I/O Manager, as in the file creation scenario presented in Security Scenario: Creating a File. The I/O Manager then checks the access rights if it can. If the name specifies an object within a device namespace, the I/O Manager in turn hands off the name to the device (or file system) driver, and that driver is responsible for validating the requested access.
Security Checks in the I/O Manager
When the I/O Manager creates a handle, it checks the objects rights against the processs access token and then stores the rights granted to the user as part of the handle. When later I/O requests arrive, the I/O Manager checks the rights recorded in the handle to ensure that the process has the right to perform the requested I/O operation. For example, if the process later requests a write operation, the I/O Manager checks the rights in the handle to ensure that the caller has write access to the object.
If the handle is duplicated, rights can be removed from the copy, but not added to it.
When the I/O Manager creates an object, it converts generic Win32 access modes to object-specific rights. For example, the following rights apply to files and directories.
To create a file, a process must have traversal rights to the parent directories in the target path. For example, to create \Device\Floppy0\Directory\File.txt, a process must have the right to traverse \Device, \Device\Floppy0, and \Device\Floppy0\Directory. The I/O Manager checks only the traversal rights for these directories.
The I/O Manager checks traversal rights when it parses the file name. If the file name is a symbolic link, the I/O Manager resolves it to a full path and then checks traversal rights, starting from the root. For example, assume the symbolic link \DosDevices\A maps to the Windows NT device name \Device\Floppy0. The process must have traversal rights to the \Device directory.
Security Checks in the Driver
The operating system kernel treats every driver, in effect, as a file system with its own namespace. Consequently, when a caller attempts to create an object in the device namespace, the I/O Manager checks that the process has traversal rights to the directories in the path. By default, however, the I/O Manager does not perform security checks against the namespace. The driver is responsible for ensuring the security of its namespace.
Driver Security Responsibility
The drivers for a device are responsible for ensuring that unauthorized users do not have access to the device. Ensuring device security involves:
Creating 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:
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:
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.
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 drivers service (running as System) can open the device to individual users by using the SetFileSecurity user-mode function.
Securing 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.
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).
Opening Devices Securely
A user opens a device by specifying the device name. For example:
When the I/O Manager receives an open request, it parses the target device name and checks the rights in the processs 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.
Opening 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 objects 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.
Specifying 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.
Defining and Handling 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:
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:
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.
Defining 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.
Handling 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 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.
Call to Action and Resources
For more information, see: