How to restrict IOCTLs to admin users only in the KMDF driver?

I'm checking out a KMDF driver sample for VS 2022. I added a function to process IOCTLs from the user land. I'm wondering how do I allow those IOCTLs to be sent only by the users that belong to the build-it admin group?

Here's the IOCTL handler:

VOID
TestEvtIoDeviceControl(
    _In_ WDFQUEUE Queue,
    _In_ WDFREQUEST Request,
    _In_ size_t OutputBufferLength,
    _In_ size_t InputBufferLength,
    _In_ ULONG IoControlCode
    )
{
    NTSTATUS status = STATUS_SUCCESS;

    if(IoControlCode == IOCTL_MY_TEST)
    {
            REQ_TEST* pReq = NULL;
            size_t szcbRead = 0;
            status = WdfRequestRetrieveInputBuffer(Request, sizeof(*pReq), &pReq, &szcbRead);
            if(NT_SUCCESS(status) &&
                 szcbRead == sizeof(*pReq))
            {
                //Process data received in pReq ...

            }
    }

    WdfRequestComplete(Request, status);
}

where:

#define IOCTL_MY_TEST CTL_CODE(0x8000, 0x800, METHOD_BUFFERED, FILE_WRITE_ACCESS)

typedef struct _REQ_TEST
{
    ULONG Param1;
    int Param2;
}REQ_TEST, *PREQ_TEST;

did you change the default security descriptor on the device to allow non admins to open the device?

It sounds like using WdfDeviceInitAssignSDDLString and WdfDeviceInitAssignName before the WdfDeviceCreate call is not recommended, right. I tried and it didn't do much. (My guess is that the SD is read from the registry if it's there.) Thus I'm assuming that you meant changing it in the INF file. Tbh, I'm not sure how to do it for the KMDF project. I tried adding this line to it:

HKR,Security,“D:P(A;;GA;;;BA)(A;;GA;;;SY)” ;

but then it seems like it requires ClassInstall32 section, which I can't add to KMDF or a device driver.

As you can see the structure of the INF files is not my strong suit.

What's the format to add it to the sample KMDF project for VS 2022?

my point is by you need admin access to open the device. are you trying to open up access to all users but restrict access to certain IOCTLs to admins?

@Doron_Holan ideally, restrict access to the IOCTL_MY_TEST only to admins, and let other users access other IOCTLs (not shown in my sample above.) But if that is not possible - to restrict access to all IOCTLs to admins only.

There are multiple ways to do what you’re asking. But it’s unusual in Windows. Typically, you put an ACL on your device object that says (for example) anybody can open this device for read, but you need to be an admin to be able to open it for write. Then you define your IOCTLs with the CTL_CODE macro to require FILE_WRITE_ACCESS for those operations that can only be done by admins

Another mechanism I’ve seen used is to create one device object that allows all access, then a separate (control) device object that only admins can open… and have your restricted operations take place only via the secure device.

There are many ways to get more fancy and specific than this. For example, you could use SeAccessCheck to check the security context of the issuer of a given request against a Security Descriptor of your own creation. This isn’t trivial to get right, and I’ve only rarely seen it done.

I want to second and strongly reinforce that you should not roll your own access check. Let the kernel do it for it with creating a security descriptor like Peter describes and assigning access by read or write. There is absolutely no other implication for doing this unless you support read and write irps, which almost all pnp drivers do not support.

It should be noted that there are many very fine grained permissions that a standard ACL can control

I understand the requirement of the original poster because I've a driver with similar requirements.

A kernel driver for PCI cards with an on-board GPS/GNSS disciplined high quality oscillator which can be used to accurately synchronize the system time. All data exchange is done via IOCTL calls that read data structures from or send data structures to the device.

Every user account has permission to read the current time and status from the card, but only privileged users have the rights to write configuration changes to the card which can affect the timekeeping on the card and thus (indirectly) the computer's system time.

On the other hand, the device also provides hardware signal outputs which can e.g. generate pulses at predefined points in time, and a frequency synthesizer the output frequency and phase can be changed by configurations via specific IOCTL calls. Since the calls that configure these output signals don't affect the on-board time, no specific permissions are required for these write IOCTLs.

In theory, there could be even more levels of permission, e.g. whether an account is allowed to read the current geographic position from the card, which may be forbidden in certain scenarios.

The only way I see to properly implement this in the driver is to do a permission check for those IOCTL calls who need it, whether the calling process has sufficient permissions, or not.

As @PeterGV mentioned, you can repurpose the 'read/write' bits in the IOCTL definition to determine which IOCTLs are admin only. It does not matter at all what the IOCTL does, the bits are ignored as far as the processing of the request is concerned. Data can flow in either or both directions.

For your driver to receive any IOCTL, the OS has already checked security twice. The first and more extensive check is at HANDLE creation time. And then at the time of submittal. Anything more that your driver does is at least wasteful.

When opening a HANDLE, the caller's identity (or impersonation context) is evaluated against the ACL and requested access. Usually the GENERIC_READ / GENERIC_WRITE bits are requested, but they get translated into specific rights. It is possible to make many other levels / types of access, but it hardly ever makes sense.

When issuing an IOCTL, the control code is verified versus the access mask in the HANDLE.

And if your driver somehow has a need for yet more control, exposing multiple device objects with different ACLs is always a choice. The built in security model offers an almost unlimited level of control

So where exactly do you recommend setting up the device ACL?

You should set it in the INF file.

@Peter_Viscarola_OSR, or anyone else, would you please post an example how to set up an ACL via the INF file? Say to D:P(A;;GA;;;SY)(A;;GA;;;BA).

I'm not good with the INF files and all the examples that I find online don't work with the KMDF driver.

From THE (old) article on Device Object protection, that will tell you more about ACLs on Device Objects than anybody should ever want to know, there's this example:

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)”

Thanks @MBond2 and @Mark_Roddy, I wasn't aware of the details you mentioned. I'll have a deeper look at this once more.