Setting a security descriptor on a legacy device object

Setting the security descriptor allows you to control who can open a handle to the device object.  Typically you can call IoCreateDeviceSecure to create the device object and have the correct DACL from the start.  One issue with IoCreateDeviceSecure is that the SDDL string is limited to what it can describe, primarily you can only specify predefined SIDs.  If you need a SID that is not predefined you must build the security descriptor by hand and the code to do it is not very easty to implement (let alone figure out which DDIs you need to call in the first place). 

Well, Paul Sliwowicz kindly donated this code snippet which will set a security descriptor on a legacy device object.  It does require that you are able to open a handle to the device object first, so it might be a good idea to initially create the device with a very restrictive SDDL string that limits opens to the system only and then relax it to your needs with the following code which works in either a WDM or KMDF driver.   This can also work on device objects that are created by other drivers, but you have a built in race between when the device is created and when you set the security descriptor that you must also account for.

 NTSTATUS
SetDeviceDacl(
    PDEVICE_OBJECT DeviceObject
    )
{
    SECURITY_DESCRIPTOR sd = { 0 };
    ULONG aclSize = 0;
    PACL pAcl = NULL;
     NTSTATUS status;

    status = ObOpenObjectByPointer(DeviceObject,
                                   OBJ_KERNEL_HANDLE,
                                   NULL,
                                   WRITE_DAC,
                                   0,
                                   KernelMode,
                                   &fileHandle);

    if (!NT_SUCCESS(status)) {
        goto Exit;
    } 

    //
    // Calculate how big our ACL needs to be to support one ACE (in
    // this case we'll use a predefined Se export for local system
    // as the SID)
    //
    aclSize = sizeof(ACL);
    aclSize += RtlLengthSid(SeExports->SeLocalSystemSid);
    aclSize += RtlLengthSid(SeExports->SeAliasAdminsSid);
    aclSize += RtlLengthSid(SeExports->SeAliasUsersSid);
   
    //
    // Room for 3 ACEs (one for each SID above); note we don't
    // include the end of the structure as we've accounted for that
    // length in the SID lengths already.
    //
    aclSize += 3 * FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart);

    pAcl = (PACL) ExAllocatePoolWithTag(PagedPool, aclSize, TAG);

    if (pAcl == NULL) {
        status = STATUS_INSUFFICIENT_RESOURCES;
        goto Exit;
    }

    status = RtlCreateAcl(pAcl, aclSize, ACL_REVISION);
    if (!NT_SUCCESS(status)) {
        goto Exit;
    }

    status = RtlAddAccessAllowedAce(pAcl,
                                    ACL_REVISION,
                                    GENERIC_READ | GENERIC_WRITE | DELETE,
                                    SeExports->SeLocalSystemSid);
    if (!NT_SUCCESS(status)) {
        goto Exit;
    }

    status = RtlAddAccessAllowedAce(pAcl,
                                    ACL_REVISION,
                                    GENERIC_READ | GENERIC_WRITE | DELETE,
                                    SeExports->SeAliasAdminsSid );
    if (!NT_SUCCESS(status)) {
        goto Exit;
    }

    status = RtlAddAccessAllowedAce(pAcl,
                                    ACL_REVISION,
                                    GENERIC_READ,
                                    SeExports->SeAliasUsersSid );
     if (!NT_SUCCESS(status)) {
        goto Exit;
    }
 
    //
    // Create a security descriptor
    //
    status = RtlCreateSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
    if (!NT_SUCCESS(status)) {
        goto Exit;
    }

    //
    // Associate the above pAcl with the security descriptor
    //
    status = RtlSetDaclSecurityDescriptor(&sd, TRUE, pAcl, FALSE);
    if (!NT_SUCCESS(status)) {
        goto Exit;
    }

    //
    // Set security on the object
    //
    status = ZwSetSecurityObject(fileHandle, DACL_SECURITY_INFORMATION, &sd);
    if (!NT_SUCCESS(status)) {
        goto Exit;
    }

Exit:
    ZwClose(fileHandle);
    fileHandle = NULL;

    if (pAcl != NULL) {
        ExFreePool(pAcl);
        pAcl = NULL;
    }

    return status;
}