pci.sys upper filter driver named object security

Hi,

somewhere here I’ve seen somebody say that an upper filter driver for a bus shouldn’t allow direct access via a named object but instead use another driver in an entirely different stack for userspace access.

Can anyone explain to me the security concerns that arise there? Also, I’ve seen an example driver implement the two-driver-construct using a named device object in the filter driver but I can’t see how it restricts access to other kernel drivers only. I’ve tried really hard to understand the problem by looking at the documentation but so far I can’t comprehend it. I’ve tried to get away with just a single filter driver and create a named device for access using IoCreateDeviceSecure to limit access, but my SDDL doesn’t have any effect: everybody can access my device. Is there a connection somehow?

https://www.osr.com/nt-insider/2017-issue1/making-device-objects-accessible-safe/

Read that and post any remaining questions.

1 Like

Thank you, that clarified some things but confuses me slightly more about the object naming.

I am naming several devices because I need IOCTLs to be run on them. I thought of naming to be simpler than using an interface GUID.

  1. One device is a non-pnp software device using KMDF and it is accessed using CreateFile using such a string “\\?\GLOBALROOT\Device\myDevice”. I do not have an inf file, I use “sc create” to install the module so I can not specify things via the inf file.
    1a) Is WdfDeviceInitAssignSDDLString my only way to specify access restrictions? If I do not assign an SDDL string, my device is r/w fully-open, world accessible.
    1b) Why would I want a symbolic link? It just looks like an extra, unnecessary step that imposes restrictions on my name.
    1c) What difference does the interface GUID do? In the end I always call CreateFile from userspace, regardless of an internal name, symbolic link or class and from there all code flows are the same.

  2. Another device is the pci.sys upper filter driver where I borrowed code from an example and it is WDM.
    2a) The same questions about naming, symlink and interface class apply, because I name my device and access it via that GLOBALROOT name.
    2b) I specify the system device class because it looks like my device belongs there. So if I don’t want that devclasses default access restrictions, then I still can specify a per-device SDDL string in the inf file and it will always take precedence, or are there more special cases?
    2c) Your blog post is very detailed about PDOs and FDOs, I am a filter driver, though. Are there any things different or do I just have to imagine the same rules applied to a larger stack.

  3. I still do not understand why it is best practice to not expose my upper filter driver to user space.
    3a) I’ve now seen examples and a thread in this forum where the upper filter driver just creates a name so another kernel module can open the device and use it to get a bus interface standard and expose it’s own name to receive IOCTLs. Why is it dangerous if my filter driver exposes a name that could be used to access the pci express root complex?
    3b) If I do the two driver construct, there still is a name on the pci.sys filter driver object that my other driver uses and userspace could open the device, too, if I do not lock it down using registry keys. Am I misunderstanding something here?

Thank you very much for untangling these things and providing simple information where MS fails to do so…

Wow… a LOT of questions.

I use “sc create” to install the module so I can not specify things via the inf file

That is a mistake. Create an INF file. That’s the proper way to install any driver, including non-PnP drivers.

it is accessed using CreateFile using such a string “\?\GLOBALROOT\Device\myDevice”

Hmmmm… yeah… that’s not “the right way” to do things. You’re basically violating Windows layering there.

Why would I want a symbolic link? It just looks like an extra, unnecessary step that imposes restrictions on my name.

Well, no. It doesn’t impose any “restrictions” on your name.

Windows differentiates between internal device names and device names that are exported for easy access to user-mode apps.

As you would no doubt learned from the article to which I pointed you, “best practice” is to not name the FDO… this applies equally to FDOs created by your Filter Driver (what some people – not me – call Filter Device Objects or FiDOs). The symbolic link points to the PDO (as you know from the article) and thus you have a single point of protection for the devnode.

I specify the system device class because it looks like my device belongs there

But it probably doesn’t… According to the docs: “This class includes HALs, system buses, system bridges, the system ACPI driver, and the system volume manager driver.”

Your blog post is very detailed about PDOs and FDOs, I am a filter driver, though.

Your Device Object is considered an FDO for this purpose.

What difference does the interface GUID do?

It’s a way for user-mode apps to locate devices by INTERFACE CONTRACT as opposed to by name. It also has the (rather wonderful) advantage of allowing users (both user-mode and kernel-mode) to register callbacks so that they can be informed of the arrival or departure of a given Device Interface by GUID.

I still do not understand why it is best practice to not expose my upper filter driver to user space.

As you no doubt understand, unless you’re creating a separate, out-of-stack, Control Device Object (which is, in fact, best practice for sending Requests to your filter)… if the device you’re filtering has more restrictive access than the access that you’re provided for your Filter’s Device Object, you’re changing the protection that’s allowed on the dev node to be less restrictive. Somebody could open your device, which is layered above the (pre-existing) FDO and PDO and send Requests…

Again, the RIGHT solution here, if you want to provide command/control operations on your filter, is to create a separate Device object, called a “Control Device Object” that’s outside the stack that you’re filtering.

Why is it dangerous if my filter driver exposes a name that could be used to access the pci express root complex?

Because… ah… somebody who shouldn’t be accessing the PCI Express Root Complex might access it??

There are Bad People in the world, you know.

3b) If I do the two driver construct,

I don’t know what this means.

Hope that helps,

Peter

One device is a non-pnp software device using KMDF and it is accessed using CreateFile using such a string “\?\GLOBALROOT\Device\myDevice”

So, you are assuming that every computer has exactly one PCIExpress root complex. I’m not sure that’s universally true, and you aren’t either.

Hi,
thank you @“Peter_Viscarola_(OSR)” for helping me understand the whole thing. No questions left any more.

@Tim_Roberts No I am not and I’ve even got hardware to test it :slight_smile: