[NTOSKRNL] Use the appropriated security descriptor when creating a device
[reactos.git] / ntoskrnl / io / iomgr / device.c
index a0e1961..7d17ce9 100644 (file)
@@ -1,11 +1,12 @@
 /*
  * PROJECT:         ReactOS Kernel
  * LICENSE:         GPL - See COPYING in the top level directory
- * FILE:            ntoskrnl/io/device.c
+ * FILE:            ntoskrnl/io/iomgr/device.c
  * PURPOSE:         Device Object Management, including Notifications and Queues.
  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
  *                  Filip Navara (navaraf@reactos.org)
- *                  HervĂ© Poussineau (hpoussin@reactos.org)
+ *                  HervĂ© Poussineau (hpoussin@reactos.org)
+ *                  Pierre Schweitzer
  */
 
 /* INCLUDES *******************************************************************/
@@ -24,6 +25,8 @@ extern LIST_ENTRY IopCdRomFileSystemQueueHead;
 extern LIST_ENTRY IopTapeFileSystemQueueHead;
 extern ERESOURCE IopDatabaseResource;
 
+#define DACL_SET 4
+
 /* PRIVATE FUNCTIONS **********************************************************/
 
 VOID
@@ -333,6 +336,10 @@ IopEditDeviceList(IN PDRIVER_OBJECT DriverObject,
                   IN IOP_DEVICE_LIST_OPERATION Type)
 {
     PDEVICE_OBJECT Previous;
+    KIRQL OldIrql;
+
+    /* Lock the Device list while we edit it */
+    OldIrql = KeAcquireQueuedSpinLock(LockQueueIoDatabaseLock);
 
     /* Check the type of operation */
     if (Type == IopRemove)
@@ -351,6 +358,15 @@ IopEditDeviceList(IN PDRIVER_OBJECT DriverObject,
             while (Previous->NextDevice != DeviceObject)
             {
                 /* Not this one, keep moving */
+                if (!Previous->NextDevice)
+                {
+                    DPRINT1("Failed to remove PDO %p (not found)\n",
+                            DeviceObject);
+
+                    ASSERT(FALSE);
+                    KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
+                    return;
+                }
                 Previous = Previous->NextDevice;
             }
 
@@ -364,6 +380,9 @@ IopEditDeviceList(IN PDRIVER_OBJECT DriverObject,
         DeviceObject->NextDevice = DriverObject->DeviceObject;
         DriverObject->DeviceObject = DeviceObject;
     }
+
+    /* Release the device list lock */
+    KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
 }
 
 VOID
@@ -391,8 +410,8 @@ IopUnloadDevice(IN PDEVICE_OBJECT DeviceObject)
         /* Check if we have a Security Descriptor */
         if (DeviceObject->SecurityDescriptor)
         {
-            /* Free it */
-            ExFreePoolWithTag(DeviceObject->SecurityDescriptor, TAG_SD);
+            /* Dereference it */
+            ObDereferenceSecurityDescriptor(DeviceObject->SecurityDescriptor, 1);
         }
 
         /* Remove the device from the list */
@@ -671,6 +690,204 @@ IopGetRelatedTargetDevice(IN PFILE_OBJECT FileObject,
     return Status;
 }
 
+BOOLEAN
+NTAPI
+IopVerifyDeviceObjectOnStack(IN PDEVICE_OBJECT BaseDeviceObject,
+                             IN PDEVICE_OBJECT TopDeviceObjectHint)
+{
+    KIRQL OldIrql;
+    BOOLEAN Result;
+    PDEVICE_OBJECT LoopObject;
+
+    ASSERT(BaseDeviceObject != NULL);
+
+    Result = FALSE;
+    /* Simply loop on the device stack and try to find our hint */
+    OldIrql = KeAcquireQueuedSpinLock(LockQueueIoDatabaseLock);
+    for (LoopObject = BaseDeviceObject; ; LoopObject = LoopObject->AttachedDevice)
+    {
+        /* It was found, it's a success */
+        if (LoopObject == TopDeviceObjectHint)
+        {
+            Result = TRUE;
+            break;
+        }
+
+        /* End of the stack, that's a failure - default */
+        if (LoopObject == NULL)
+        {
+            break;
+        }
+    }
+    KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
+
+    return Result;
+}
+
+NTSTATUS
+NTAPI
+IopCreateSecurityDescriptorPerType(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+                                   IN ULONG Type,
+                                   OUT PULONG OutputFlags)
+{
+    PACL Dacl;
+    NTSTATUS Status;
+
+    /* Select the DACL the caller wants */
+    switch (Type)
+    {
+        case RestrictedPublic:
+            Dacl = SePublicDefaultDacl;
+            break;
+
+        case UnrestrictedPublic:
+            Dacl = SePublicDefaultUnrestrictedDacl;
+            break;
+
+        case RestrictedPublicOpen:
+            Dacl = SePublicOpenDacl;
+            break;
+
+        case UnrestrictedPublicOpen:
+            Dacl = SePublicOpenUnrestrictedDacl;
+            break;
+
+        case SystemDefault:
+            Dacl = SeSystemDefaultDacl;
+            break;
+
+        default:
+            ASSERT(FALSE);
+            return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Create the SD and set the DACL caller wanted */
+    Status = RtlCreateSecurityDescriptor(SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
+    ASSERT(NT_SUCCESS(Status));
+    Status = RtlSetDaclSecurityDescriptor(SecurityDescriptor, TRUE, Dacl, FALSE);
+
+    /* We've set DACL */
+    if (OutputFlags) *OutputFlags |= DACL_SET;
+
+    /* Done */
+    return Status;
+}
+
+PSECURITY_DESCRIPTOR
+NTAPI
+IopCreateDefaultDeviceSecurityDescriptor(IN DEVICE_TYPE DeviceType,
+                                         IN ULONG DeviceCharacteristics,
+                                         IN BOOLEAN HasDeviceName,
+                                         IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+                                         OUT PACL * OutputDacl,
+                                         OUT PULONG OutputFlags)
+{
+    PACL Dacl;
+    ULONG AceId;
+    NTSTATUS Status;
+    PACCESS_ALLOWED_ACE Ace;
+    BOOLEAN AdminsSet, WorldSet;
+
+    PAGED_CODE();
+
+    /* Zero our output vars */
+    if (OutputFlags) *OutputFlags = 0;
+
+    *OutputDacl = NULL;
+
+    /* For FSD, easy use SePublicDefaultUnrestrictedDacl */
+    if (DeviceType == FILE_DEVICE_TAPE_FILE_SYSTEM ||
+        DeviceType == FILE_DEVICE_CD_ROM_FILE_SYSTEM ||
+        DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM ||
+        DeviceType == FILE_DEVICE_FILE_SYSTEM)
+    {
+        Status = IopCreateSecurityDescriptorPerType(SecurityDescriptor,
+                                                    UnrestrictedPublic,
+                                                    OutputFlags);
+        goto Quit;
+    }
+    /* For storage devices with a name and floppy attribute,
+     * use SePublicOpenUnrestrictedDacl
+     */
+    else if ((DeviceType != FILE_DEVICE_VIRTUAL_DISK &&
+              DeviceType != FILE_DEVICE_MASS_STORAGE &&
+              DeviceType != FILE_DEVICE_CD_ROM &&
+              DeviceType != FILE_DEVICE_DISK &&
+              DeviceType != FILE_DEVICE_DFS_FILE_SYSTEM &&
+              DeviceType != FILE_DEVICE_NETWORK &&
+              DeviceType != FILE_DEVICE_NETWORK_FILE_SYSTEM) ||
+              (HasDeviceName && BooleanFlagOn(DeviceCharacteristics, FILE_FLOPPY_DISKETTE)))
+    {
+        Status = IopCreateSecurityDescriptorPerType(SecurityDescriptor,
+                                                    UnrestrictedPublicOpen,
+                                                    OutputFlags);
+        goto Quit;
+    }
+
+    /* The rest...
+     * We will rely on SePublicDefaultUnrestrictedDacl as well
+     */
+    Dacl = ExAllocatePoolWithTag(PagedPool, SePublicDefaultUnrestrictedDacl->AclSize, 'eSoI');
+    if (Dacl == NULL)
+    {
+        return NULL;
+    }
+
+    /* Copy our DACL */
+    RtlCopyMemory(Dacl, SePublicDefaultUnrestrictedDacl, SePublicDefaultUnrestrictedDacl->AclSize);
+
+    /* Now, browse the DACL to make sure we have everything we want in them,
+     * including permissions
+     */
+    AceId = 0;
+    AdminsSet = FALSE;
+    WorldSet = FALSE;
+    while (NT_SUCCESS(RtlGetAce(Dacl, AceId, (PVOID *)&Ace)))
+    {
+        /* Admins must acess and in RWX, set it */
+        if (RtlEqualSid(SeAliasAdminsSid, &Ace->SidStart))
+        {
+            SetFlag(Ace->Mask, (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE));
+            AdminsSet = TRUE;
+        }
+
+        /* World can read a CD_ROM device */
+        if (DeviceType == FILE_DEVICE_CD_ROM && RtlEqualSid(SeWorldSid, &Ace->SidStart))
+        {
+            SetFlag(Ace->Mask, GENERIC_READ);
+            WorldSet = TRUE;
+        }
+
+        ++AceId;
+    }
+
+    /* AdminSid was present and set (otherwise, we have big trouble) */
+    ASSERT(AdminsSet);
+
+    /* If CD_ROM device, we've set world permissions */
+    if (DeviceType == FILE_DEVICE_CD_ROM) ASSERT(WorldSet);
+
+    /* Now our DACL is correct, setup the security descriptor */
+    RtlCreateSecurityDescriptor(SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
+    RtlSetDaclSecurityDescriptor(SecurityDescriptor, TRUE, Dacl, FALSE);
+
+    /* We've set DACL */
+    if (OutputFlags) *OutputFlags |= DACL_SET;
+
+    /* Return DACL to allow later freeing */
+    *OutputDacl = Dacl;
+    Status = STATUS_SUCCESS;
+
+Quit:
+    /* Only return SD if we succeed */
+    if (!NT_SUCCESS(Status))
+    {
+        return NULL;
+    }
+
+    return SecurityDescriptor;
+}
+
 /* PUBLIC FUNCTIONS ***********************************************************/
 
 /*
@@ -828,6 +1045,8 @@ IoCreateDevice(IN PDRIVER_OBJECT DriverObject,
     ULONG AlignedDeviceExtensionSize;
     ULONG TotalSize;
     HANDLE TempHandle;
+    PACL Dacl;
+    SECURITY_DESCRIPTOR SecurityDescriptor, *ReturnedSD;
     PAGED_CODE();
 
     /* Check if we have to generate a name */
@@ -841,14 +1060,22 @@ IoCreateDevice(IN PDRIVER_OBJECT DriverObject,
         /* Initialize the name */
         RtlInitUnicodeString(&AutoName, AutoNameBuffer);
         DeviceName = &AutoName;
-   }
+    }
+
+    /* Get the security descriptor */
+    ReturnedSD = IopCreateDefaultDeviceSecurityDescriptor(DeviceType,
+                                                          DeviceCharacteristics,
+                                                          DeviceName != NULL,
+                                                          &SecurityDescriptor,
+                                                          &Dacl,
+                                                          NULL);
 
     /* Initialize the Object Attributes */
     InitializeObjectAttributes(&ObjectAttributes,
                                DeviceName,
                                OBJ_KERNEL_HANDLE,
                                NULL,
-                               NULL);
+                               ReturnedSD);
 
     /* Honor exclusive flag */
     if (Exclusive) ObjectAttributes.Attributes |= OBJ_EXCLUSIVE;
@@ -875,7 +1102,12 @@ IoCreateDevice(IN PDRIVER_OBJECT DriverObject,
                             0,
                             0,
                             (PVOID*)&CreatedDeviceObject);
-    if (!NT_SUCCESS(Status)) return Status;
+    if (!NT_SUCCESS(Status))
+    {
+        if (Dacl != NULL) ExFreePoolWithTag(Dacl, 'eSoI');
+
+        return Status;
+    }
 
     /* Clear the whole Object and extension so we don't null stuff manually */
     RtlZeroMemory(CreatedDeviceObject, TotalSize);
@@ -927,6 +1159,8 @@ IoCreateDevice(IN PDRIVER_OBJECT DriverObject,
         Status = IopCreateVpb(CreatedDeviceObject);
         if (!NT_SUCCESS(Status))
         {
+            if (Dacl != NULL) ExFreePoolWithTag(Dacl, 'eSoI');
+
             /* Dereference the device object and fail */
             ObDereferenceObject(CreatedDeviceObject);
             return Status;
@@ -980,7 +1214,12 @@ IoCreateDevice(IN PDRIVER_OBJECT DriverObject,
                             1,
                             (PVOID*)&CreatedDeviceObject,
                             &TempHandle);
-    if (!NT_SUCCESS(Status)) return Status;
+    if (!NT_SUCCESS(Status))
+    {
+        if (Dacl != NULL) ExFreePoolWithTag(Dacl, 'eSoI');
+
+        return Status;
+    }
 
     /* Now do the final linking */
     ObReferenceObject(DriverObject);
@@ -994,6 +1233,9 @@ IoCreateDevice(IN PDRIVER_OBJECT DriverObject,
     /* Close the temporary handle and return to caller */
     ObCloseHandle(TempHandle, KernelMode);
     *DeviceObject = CreatedDeviceObject;
+
+    if (Dacl != NULL) ExFreePoolWithTag(Dacl, 'eSoI');
+
     return STATUS_SUCCESS;
 }
 
@@ -1083,6 +1325,10 @@ IoEnumerateDeviceObjectList(IN  PDRIVER_OBJECT DriverObject,
 {
     ULONG ActualDevices = 1;
     PDEVICE_OBJECT CurrentDevice = DriverObject->DeviceObject;
+    KIRQL OldIrql;
+
+    /* Lock the Device list while we enumerate it */
+    OldIrql = KeAcquireQueuedSpinLock(LockQueueIoDatabaseLock);
 
     /* Find out how many devices we'll enumerate */
     while ((CurrentDevice = CurrentDevice->NextDevice)) ActualDevices++;
@@ -1094,13 +1340,14 @@ IoEnumerateDeviceObjectList(IN  PDRIVER_OBJECT DriverObject,
     *ActualNumberDeviceObjects = ActualDevices;
 
     /* Check if we can support so many */
-    if ((ActualDevices * 4) > DeviceObjectListSize)
+    if ((ActualDevices * sizeof(PDEVICE_OBJECT)) > DeviceObjectListSize)
     {
         /* Fail because the buffer was too small */
+        KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
         return STATUS_BUFFER_TOO_SMALL;
     }
 
-    /* Check if the caller only wanted the size */
+    /* Check if the caller wanted the device list */
     if (DeviceObjectList)
     {
         /* Loop through all the devices */
@@ -1119,6 +1366,9 @@ IoEnumerateDeviceObjectList(IN  PDRIVER_OBJECT DriverObject,
         }
     }
 
+    /* Release the device list lock */
+    KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
+
     /* Return the status */
     return STATUS_SUCCESS;
 }
@@ -1324,9 +1574,19 @@ IoGetRelatedDeviceObject(IN PFILE_OBJECT FileObject)
             /* Check if the extension is really present */
             if (FileObject->FileObjectExtension)
             {
-                /* FIXME: Unhandled yet */
-                DPRINT1("FOEs not supported\n");
-                ASSERT(FALSE);
+                PFILE_OBJECT_EXTENSION FileObjectExtension;
+
+                /* Cast the buffer to something we understand */
+                FileObjectExtension = FileObject->FileObjectExtension;
+
+                /* Check if have a valid replacement top level device */
+                if (FileObjectExtension->TopDeviceObjectHint &&
+                    IopVerifyDeviceObjectOnStack(DeviceObject,
+                                                 FileObjectExtension->TopDeviceObjectHint))
+                {
+                    /* Use this instead of returning the top level device */
+                    return FileObjectExtension->TopDeviceObjectHint;
+                }
             }
         }