3

I'm trying to interface with a driver for creating TUN interfaces (WinTun), but in order to send and receive data from them I need to register a ring buffer. The code I'm using looks something like this (I omitted the part where I create the device with SetupApi, as that seems to be working):

#include <windows.h>
#include <winioctl.h>
#include <stdio.h>

#define REGISTER_RINGS_IOCTL CTL_CODE(51820U, 0x970U, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)

#define BUF_CAPACITY 0x20000
#define BUF_TRAILING 0x10000

typedef struct {
    volatile ULONG Head;
    volatile ULONG Tail;
    volatile LONG Alertable;
    UCHAR Data[BUF_CAPACITY + BUF_TRAILING];
} TUN_RING;

typedef struct {
    ULONG Size;
    UCHAR Data;
} TUN_PACKET;

typedef struct {
    struct {
        ULONG RingSize;
        TUN_RING *Ring;
        HANDLE TailMoved;
    } Send, Receive;
} TUN_REGISTER_RINGS;

int main() {

    HANDLE device = CreateFileW(
        L"\\\\?\\ROOT#NET#0006#{cac88484-7515-4c03-82e6-71a87abac361}",
        // ^^ This comes from the omitted code ^^
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        NULL,
        OPEN_EXISTING,
        0,
        NULL
    );

    if(device == INVALID_HANDLE_VALUE) {
        printf("The device does not exist\n");
        return -1;
    }

    TUN_RING Send, Receive = {0};

    TUN_REGISTER_RINGS params = {
        .Send.RingSize = sizeof(TUN_RING),
        .Send.Ring = &Send,
        .Send.TailMoved = CreateEventW(NULL, FALSE, FALSE, NULL),

        .Receive.RingSize = sizeof(TUN_RING),
        .Receive.Ring = &Receive,
        .Receive.TailMoved = CreateEventW(NULL, FALSE, FALSE, NULL),
    };

    DWORD bytes;

    BOOL ret = DeviceIoControl(
        device,
        REGISTER_RINGS_IOCTL,
        &params,
        sizeof(TUN_REGISTER_RINGS),
        NULL,
        0,
        &bytes,
        NULL
    );

    if(ret == 0) {
        printf("Err: %d\n", GetLastError());
        return -2;
    }

    return 0;
}

My problem is that this code fails at DeviceIoControl with error 5, which corresponds to ERROR_ACCESS_DENIED.

I have no idea why that is happening as the program is already running with admin privileges and the device handle has been opened with the recommended attributes (as you can see here). Sorry for the lack of extra information but I don't have much experience when it comes to windows drivers and don't know how to debug this any further.

I think the problem might have to do with this check in the source code of the driver, as it appears to be stopping before checking the input buffer (it should rerurn INVALID_PARAMETER when I put garbage in the input buffer, but that doesn't happen).

Again, sorry if I misunderstood something or missed something critical but I am learning all of this as I go, thanks in advance!

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Davide Mor
  • 83
  • 7
  • 1
    from [TunInitializeDispatchSecurityDescriptor](https://github.com/WireGuard/wintun/blob/6d6e2190801b278b3aab84d910737925b8df5c2f/wintun.c#L784) clear visible that it allow `FILE_ALL_ACCESS` only for *LocalSystem* account. so only *LocalSystem* can send some ioctl to device. you and must got `ERROR_ACCESS_DENIED` if you run as admin but not *LocalSystem* – RbMm May 02 '20 at 18:59
  • @RbMm Yes! You are right! I tried running the executable as LocalSystem and it finally worked, I guess it is there to make sure every application that talks to the driver is a service? Anyways, thank you! – Davide Mor May 02 '20 at 19:46
  • 1
    @ErykSun - if i not mistake `FILE_OPEN_FOR_BACKUP_INTENT` ( `FILE_FLAG_BACKUP_SEMANTICS` for win32) have effect only inside create/open file operation. but we have problem inside ioct which direct call `SeAccessCheck` [here](https://github.com/WireGuard/wintun/blob/6d6e2190801b278b3aab84d910737925b8df5c2f/wintun.c#L862) - so not help – RbMm May 02 '20 at 20:01
  • 1
    in call `SeAccessCheck` [here](https://github.com/WireGuard/wintun/blob/6d6e2190801b278b3aab84d910737925b8df5c2f/wintun.c#L862) at all not used `FileObject` from *Irp* but only security context of the calling thread. so anyway we need be *LocalSystem* at this point and SeBackupPrivilege and SeRestorePrivilege not help – RbMm May 02 '20 at 20:06
  • 2
    @RbMm, sorry for the noise, clearly the previous `CreateFileW` call succeeded. It's weird to do another security check in the IOCTL. Typically it's up to the I/O manager, based on the access granted to the handle and the read/write access required by the IOCTL. I guess they need to work around the I/O manager's privilege check in `IoCreateFileEx` such that only SYSTEM will be allowed to use the IOCTL, but there are simpler ways to do that without this extra security descriptor. Maybe they provide a way for code to replace or extend the security descriptor. – Eryk Sun May 02 '20 at 20:15

1 Answers1

2

Found the solution. As @RbMm described, the code that creates the security descriptor only allows access to LocalSystem. That means that it is the only account allowed to talk to the driver.

Davide Mor
  • 83
  • 7