OSRLogo
OSRLogoOSRLogoOSRLogo x Subscribe to The NT Insider
OSRLogo
x

Everything Windows Driver Development

x
x
x
GoToHomePage xLoginx
 
 

    Thu, 14 Mar 2019     118020 members

   Login
   Join


 
 
Contents
  Online Dump Analyzer
OSR Dev Blog
The NT Insider
The Basics
File Systems
Downloads
ListServer / Forum
  Express Links
  · The NT Insider Digital Edition - May-June 2016 Now Available!
  · Windows 8.1 Update: VS Express Now Supported
  · HCK Client install on Windows N versions
  · There's a WDFSTRING?
  · When CAN You Call WdfIoQueueP...ously

Of SDs, ACLs & INFs: The INs and OUTs of Device Object Protection

Responsible engineering practice dictates that whenever you write a function driver, you must ensure that the protection applied to the device stack in which your function driver exists is both appropriate and sufficient. After all, when you create a function driver you're authoring the primary driver in the device stack that makes the device functional. There's rarely a driver in the device stack that knows better than the function driver which users should have access to the device and what access controls the device stack requires.

If you haven't already read Go Ahead: Name Your Device Objects, you should do so now. This article assumes you've read, and understood, the architecture and the issues described in that article.

Given its importance, you'd think that applying access controls to a WDM device stack would be well understood, straight forward, and well documented. But, surprise! Like so many other things in Windows, things aren't nearly as simple as you might hope. In this article, we'll try to carefully lead you through the minefield that is WDM and KMDF device protection. When we refer to device stack access protection in this article, we're talking about a security Descriptor (SD) that can be uniformly applied to all the device objects in your device stack.

Six Ways

There are at least six ways that access controls can be established for the devices in your device stack:

  1. The I/O Manager supplies default access controls for each device object that's created. The SD that's used depends on the type of device (DeviceObject->DeviceType) that's being created. For most device types, this is a fairly open DACL that allows read and write access to all users on the system, including restricted users.
  2. The bus driver specifies protection for the PDOs it creates in your device stack. Some bus drivers specify protection for the PDOs they create, others rely on the I/O Manager's defaults.
  3. A user-mode application does some user-mode thing (such as calling SetKernelObjectSecurity) to specify access controls for your device stack.
  4. The INF file that defines your device installation class specifies a default security descriptor for all devices within that installation class.
  5. The INF file for your device specifies a default security descriptor for the device object(s) that your driver creates.
  6. Your driver specifies a security descriptor when it creates its device object(s).

What the I/O Manager and bus driver do are far beyond our control, so we won't concern ourselves with them in this article. Further, since nobody here at OSR knows anything about user-mode programming, we'll ignore item 3, above, as well. This leaves us with three mechanisms to consider: Specifying access controls via the device class INF file, specifying access controls via your device-specific INF file, and specifying access controls in your driver. We'll discuss some of the overall issues first, and then turn our attention to each of the specific protection options. Finally, we'll discuss how each of the various protection schemes interact.

THE Place to Specify SDs: The INF File

Your first and best option for specifying protection for the device objects in your device stack is using the INF file. Note that an end-user will not be able to change the protection you specify in your INF file as long as your driver install package is signed. Signing your driver package doesn't mean that you have to WHQL your driver or pass any of the Designed for Windows logo tests. You can (and should) sign your driver package using your company's Authenticode signature to attest to the authenticity of the package in any case.

When you specify access controls in your INF, the protection that you specify will be propagated throughout the device stack. Thus, when you specify protection in your INF, that same protection will be applied to the PDOs, FDOs, and all filter driver device objects that appear in your device stack.

There are numerous reasons that we recommend specifying the access controls you require in your INF instead of specifying them in your driver's code. The primary reason is that it's much easier to get the specification correct (and working as you anticipate) via the INF. Also, INF based protections in some cases will override those that you specify in your driver code. We'll talk about some of these concerns later in this article.

INF Class-Wide Access Controls

Probably the most common way that a specific security descriptor (SD) is set on a device stack is through the use of class-wide access controls. These are specified in the INF that defines the device install class via the Security value in the addreg section pointed to from the ClassInstall32 section. These default class-wide access controls are supplied by Microsoft for standard install classes, or specified by you for new install classes that you define. Figure 1 contains an extract from an INF file that defines the OsrExample install class and specifies a default SD for the class.

[Version]
Signature="$WINDOWS NT$"
Class=OsrExample
ClassGuid={dfb15040
-5cc7-11d3-b194-
0060b0efd4af}
Provider="OSR Open Systems Resources, Inc."
DriverVer=12/17/2007,5.0.0
catalogfile=wdfdio.cat

[ClassInstall32]
Addreg=OsrHwClass

[OsrHwClass]
HKR,,,,%ClassName%
HKR,,Icon,,"-5"
HKR,,Security,,"D:P(A;;GA;;;SY)(A;;GA;;;BA)" ;System and Admin only access

 

Figure 1 -- Specifying Access Controls for an Entire Setup Class of Devices

The SD supplied in the INF is defined using Security Descriptor Definition Language (SDDL). See the sidebar  at the end of this article About SDDL for information on how to interpret the SD.

When you specify a class-wide SD in your INF, the SD is stored in the registry, in the Security value of the Properties key under the software (A.K.A. driver) key for your driver. Your device's software key will be:

HKLM\SYSTEM\CCS\CONTROL\CLASS\class-guid\instance

Look under this key for the key named "Properties." You will need to change the access to the properties key to be able to see the value named Security. Yes, this is true even if you're an administrator on the box.

INF Per-Device Access Controls

If the device for which your driver is being installed requires different access controls from those specified for your installation class, you can specify a per-device SD in your INF file. A per-device SD is specified in the addreg section invoked from the ddinstall.HW section of your INF. Figure 2 (below) contains an extract from an INF file that defines an SD for a particular device within a larger class. You notice that this security descriptor is also defined using SDDL.

[MfgDeviceSection]
%DeviceDesc% = WdfDio, PCI\VEN_135E&DEV_8008&SUBSYS_8008135E&REV_01
%DeviceDesc% = WdfDio, PCI\VEN_135E&DEV_8018&SUBSYS_8018135E&REV_01

[WdfDio]
CopyFiles=@WdfDio.sys

[WDFDIO.HW]

addreg=DIOSD

[DIOSD]

HKR,,Security,,"D:P(A;;GR;;WD)(A;;GA;;BU)(A;;GA;;;SY)(A;;GR;;;WD)"

[WdfDio.Services]
AddService = WdfDio,%SPSVCINST_ASSOCSERVICE%,wdfDio_Service_Inst

Figure 2 -- Specifying Access Controls for a Particular Device in the DDINSTALL.HW Section

When you specify a per-device SD in your INF, the SD is stored in the Security value of the Properties key of your device's hardware (A.K.A device) key. Your device's device key will be:

HKLM\SYSTEM\CCS\ENUM\enumerator\device-id

Again, you'll need to change the protection on the Properties key to be able to view this entry.

Note that specifying per-device access controls in your INF overrides for your device only any class-wide access controls that might have been specified when the device class was defined. This is true regardless of whether the SD you supply is more or less secure than the default protection. Thus, specifying a per-device SD for a device within a Microsoft defined installation class allows you to specify precisely the protection that your device should have, without affecting the devices created by any other drivers in the class.

Access Controls in Your Code

Your last resort for specifying access controls for the device object(s) your driver creates is doing so from your driver code. There are numerous ways to do this, but by far the most common is to specify protection when the device object is created. When you read the details below, I'm pretty sure you'll agree it's a whole heck of a lot easier to specify the SD in your INF.

If you're writing your driver using WDM, you can specify the default protection associated with your device object when you call IoCreateDeviceSecure. This is a rather complicated function, both to use and in its implementation. But to use it properly you must do at least three things:

  1. Specify a name for the device object that you're creating, either in the DeviceName parameter or by specifying FILE_AUTOGENERATED_ DEVICE_NAME in the DeviceCharacteristics parameter. If the device object you create is not named, IoCreate DeviceSecure will return the error code STATUS_ INVALID_PARAMETER.
  2. Specify your device's install class GUID in the DeviceClassGuid parameter. Failure to do this will result in the protection you specify not being propagated throughout your device stack. And getting the protection correct, throughout every device in your devnode, is what this whole exercise is about.
  3. Specify a protection string for your device, in the DefaultSDDLString parameter.

The SD you specify with IoCreateDeviceSecure is best thought of as the default protection you want to applied to the device objects in your device stack. As described in the WDK docs, if class-based access controls have been specified (typically via the INF file) those will be used in place of those specified by your driver when calling IoCreateDeviceSecure. This is true even if the class-based ACLs are less secure than those specified in the SD provided by driver. If there is no class-based SD present in the Registry when your driver calls IoCreateDeviceSecure, the SD you specify will be stored in your driver's software key.

Finally, if there's no class-based SD present when you call IoCreateDeviceSecure, thus resulting in the SD you specify being used as the protection for your device object, according to our tests the SD isn't propagated to the other devices in your device stack until after you reboot your system. This surprised us, and we spoke to some friends in Redmond (including the dev who owns the code) about this observation. They were still investigating at press time, but it's certainly never a mistake to be sure the security settings in your device object are propagated throughout the device stack. See the sidebar entitled Propagating Security Throughout the Device Stack at the end of this article for a description of how to do this easily.

Things are a bit more confusing if you write your driver using KMDF and you want to specify an SD for the device objects in your device stack. The first thing to note is that if you name your FDO (by calling WdfDeviceInitAssignName), KMDF will provide a default SD for your device object that allows system and administrators full access to your device, but does not allow access by any other group. This is really an excellent feature as it solves the "two entry points into the device stack" problem by ensuring that the entry point you create via a named FDO has very stringent access controls by default. However, you should note that what KMDF provides by default applies only to your device object and is not propagated throughout the device stack.

To specify a default SD for your device object that's propagated throughout your device stack, you'll need to take the following four steps:

  1. Call WdfDeviceInitAssignName to name your FDO.
  2. Call WdfDeviceInitAssignSetDeviceClass to specify the install class associated with your device.
  3. Call WdfDeviceInitAssignSddlString to specify the SD, in SDDL, to be used as the default protection for your FDO.
  4. Call WdfDeviceCreate to create your WDFDEVICE, and the underlying WDM device object associated with it.

Internally, KMDF calls IoCreateDeviceSecure to create its FDOs. Thus, all the issues described previously that apply to calling IoCreateDeviceSecure apply equally to specifying device object protection from a KMDF driver. Specifically, it's important to note that the SD you specify with WdfDeviceInitAssignSddlString won't be used if there's a class-wide SD already specified for your device class. Also, take note of our observation above concerning the SD applied by IoCreateDeviceSecure not being propagated throughout the device stack until the system is rebooted.

A Bigger Hammer

You might have noticed that whether you specify a class-wide SD in your INF file, a per-device SD in your INF file, or you specify an SD in your driver when you call IoCreateDeviceSecure (or KMDF equivalent) your SD settings eventually make their way into the registry. This is intentional. The idea is that by placing the SDs in the Registry, they can be manipulated by system managers using some application (Aside: Not that I know of any application that does this. Let me know if you do, because I've been wondering). This allows system managers to be able to set policy on their devices. And, when you come right down to it, policy decisions belong in the hands of system managers and administrators and not in the hands of driver developers.

Yes, yes... I hear you saying now: "I don't want system managers to be able to change my device's protection" or more likely "I don't want the protection that I specify to be able to be overridden by a less-informed, and perhaps less restrictive, SD that's been defined by the bus driver."

Fortunately, there are several relatively easy methods available to you that'll let you establish a security descriptor for your device object. Probably the easiest is to call IoCreateDeviceSecure without specifying an install class GUID (WDM), or to not call WdfDeviceInitAssignSetDevice Class before calling WdfDeviceCreate (KMDF). The good part of this approach (assuming this is the behavior you're seeking) is that it results in IoCreateDeviceSecure not looking for any class-wide override to your specified security settings and not storing the settings you specify in the registry. The bad part is that your specified security settings are not automatically propagated throughout the device stack. You'll have to propagate the SD yourself to ensure that all the device objects in your device stack have the same SDs. See the sidebar Propagating Security Throughout the Device Stack for a relatively painless way to propagate your security settings.

Another, more direct, approach is for you to create your own SD and then call ZwSetSecurityObject to set your chosen protection on your device object. The advantage of this approach is that calling ZwSetSecurityObject on your device object after it has been attached to the device stack will result in whatever security you set being propagated throughout the device stack. The disadvantage is that, while there are numerous methods that you can use to create an appropriate SD from your driver, none of them are particularly convenient. That's why we tend to prefer the approach of calling IoCreateDeviceSecure without specifying an install class, and later propagating the resulting SD throughout the device stack.

Remember, regardless of which method you choose, the key points are:

  1. It is the responsibility of the function driver dev to determine appropriate access controls for the device objects in the device stack;
  2. The function driver dev is responsible for ensuring these access controls are applied to every device object in the device stack

Failure to do either of these things leaves your device potentially vulnerable to misuse. That's not likely to be a good thing, either for you or your users.

Not Hard

I hope you understand now that it is very important that you ensure that your device object, and every device object in your device stack, is properly protected. Like so many topics in Windows driver development, there's really no simple "cookbook" type approach that'll work for every dev or every driver. We hope that this article has explained the basic issues involved, and helped you make choices that are appropriate for you and your projects.

Sidebar: About SDDL

 

Security Descriptor Definition Language (SDDL) is fully documented in the SDK, and there's even a mini-description in the WDK under the section entitled "SDDL for Device Objects." While SDDL may look impossible to read at first glance, it's actually quite simple once you get the hang of it. For example, the SDDL string from the example in Figure 1 is:

 

D:P(A;;GA;;;SY)(A;;GA;;;BA)

 

All this says is that this is a DACL (that's the D), and it's protected (that's the P) which means that it won't inherit from any DACLs above it. This SD contains two Access Control Entries (ACEs), one in each set of parentheses. The first ACE specifies that the system group (SY) has "generic all" access (GA). The second ACE specifies that the built-in administrator account on the system (BA) also has "generic all" access (GA again). Nobody else has any access to the devices in this class. Ugly, but ultimately understandable, right?

 

Check the aforementioned section of the WDK, or Google "SDDL" for the MSDN pages that describe the language in detail.

SideBar: Propagating Security Throughout the Device Stack

 

One of the key points of this article is that if you want your device to be secure, you need to be sure that every device object in your device stack is secure.

 

A simple way to ensure that the SD present in your device object is propagated throughout your device stack is to use the following code (we hope it's self-descriptive -- ignore the first KMDF-specific part if you just want a WDM solution). Note that for this to work properly, your device object must be attached to the device stack before you execute this code.

 

//
// Propagate the security through the devnode
//
PDEVICE_OBJECT wdmDeviceObject;
HANDLE fileHandle;

//
// Get the FDO from our WDFDEVICE
//
wdmDeviceObject = WdfDeviceWdmGetDeviceObject(device);

if (wdmDeviceObject == NULL) {

DbgPrint("WdfDeviceWdmGetDeviceObject returned NULL???\n");

} else {

//
// Given the pointer to the FDO, get a handle
//
status = ObOpenObjectByPointer(wdmDeviceObject,
OBJ_KERNEL_HANDLE,
NULL,
WRITE_DAC,
0,
KernelMode,
&fileHandle);

if (!NT_SUCCESS(status)) {

DbgPrint("ObOpenObjectByPointer returned 0x%0x???\n",status);

} else {

//
// Set the security that's already in the FDO onto the
// FDO... thus kicking the security method to propagate
// the ACL up and down the device stack.
//
status = ZwSetSecurityObject(fileHandle,
DACL_SECURITY_INFORMATION,
wdmDeviceObject->SecurityDescriptor);

if (!NT_SUCCESS(status)) {

DbgPrint("ZwSetSecurityObject returned 0x%0x???\n",status);

} else {

DbgPrint("looks like the it all worked \n");

}

//
// Close the handle to the FDO
//
ZwClose(fileHandle);
}

}

User Comments
Rate this article and give us feedback. Do you find anything missing? Share your opinion with the community!
Post Your Comment

"a line is missing in this article"
The most important line of code in Figure 1 is missing. In the printed version it is correct.

In Figure 1 after HKR,,Icon,,"-5" this line needs to be inserted: HKR,,Security,,"D:P(A;;GA;;;SY)(A;;GA;;;BA)" ;System and Admin only access

Rating:
26-Aug-09, Hans Meyer


Post Your Comments.
Print this article.
Email this article.
bottom nav links