]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
[block] fail SCSI passthrough ioctls on partition devices
authorJoe Jin <joe.jin@oracle.com>
Fri, 23 Dec 2011 01:59:41 +0000 (09:59 +0800)
committerMaxim Uvarov <maxim.uvarov@oracle.com>
Thu, 17 May 2012 17:51:45 +0000 (10:51 -0700)
CVE-2011-4127.

Linux allows executing the SG_IO ioctl on a partition or even on an
LVM volume, and will pass the command to the underlying block device.
This is well-known, but it is also a large security problem when (via
Unix permissions, ACLs, SELinux or a combination thereof) a program or
user needs to be granted access to a particular partition or logical
volume but not to the full device.

This patch limits the ioctls that are forwarded to non-SCSI devices to
a few ones that are harmless.  This restriction includes programs
running with the CAP_SYS_RAWIO.  If for example I let a program access
/dev/sda2 and /dev/sdb, it still should not be able to read/write outside
the boundaries of /dev/sda2 independent of the capabilities.

This patch does not affect the non-libata IDE driver.  That driver however
alreadys test for bd != bd->bd_contains before issuing some ioctl; so,
programs that do not require CAP_SYS_RAWIO are safe.  A workaround is
just to use libata.

Encryption on the host is a mitigating factor, but it does not provide
a full solution.  In particular it doesn't protect against DoS (write
random data), replay attacks (reinstate old ciphertext sectors), or
writes to unencrypted areas including the MBR, the partition table, or
/boot.

Thanks to Daniel Berrange, Milan Broz, Mike Christie, Alasdair Kergon,
Petr Matousek, Jeff Moyer, Mike Snitzer and others for help discussing
this issue.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Joe Jin <joe.jin@oracle.com>
block/scsi_ioctl.c
drivers/scsi/sd.c
include/linux/blkdev.h

index 57ac93754841c454a6f872b6fd21a51f6fd897e1..f132ce2f711ce632de9911ca11420c42b9da95c2 100644 (file)
@@ -691,9 +691,43 @@ int scsi_cmd_ioctl(struct request_queue *q, struct gendisk *bd_disk, fmode_t mod
 }
 EXPORT_SYMBOL(scsi_cmd_ioctl);
 
+int scsi_verify_blk_ioctl(struct block_device *bd, unsigned int cmd)
+{
+       if (bd && bd == bd->bd_contains)
+               return 0;
+
+       /* Actually none of this is particularly useful on a partition
+        * device, but let's play it safe.
+        */
+       switch (cmd) {
+       case SCSI_IOCTL_GET_IDLUN:
+       case SCSI_IOCTL_GET_BUS_NUMBER:
+       case SCSI_IOCTL_GET_PCI:
+       case SCSI_IOCTL_PROBE_HOST:
+       case SG_GET_VERSION_NUM:
+       case SG_SET_TIMEOUT:
+       case SG_GET_TIMEOUT:
+       case SG_GET_RESERVED_SIZE:
+       case SG_SET_RESERVED_SIZE:
+       case SG_EMULATED_HOST:
+               return 0;
+       default:
+               break;
+       }
+       /* In particular, rule out all resets and host-specific ioctls.  */
+       return -ENOTTY;
+}
+EXPORT_SYMBOL(scsi_verify_blk_ioctl);
+
 int scsi_cmd_blk_ioctl(struct block_device *bd, fmode_t mode,
                       unsigned int cmd, void __user *arg)
 {
+       int ret;
+
+       ret = scsi_verify_blk_ioctl(bd, cmd);
+       if (ret < 0)
+               return ret;
+
        return scsi_cmd_ioctl(bd->bd_disk->queue, bd->bd_disk, mode, cmd, arg);
 }
 EXPORT_SYMBOL(scsi_cmd_blk_ioctl);
index c88885d9132969dfaa4fc0212481ee4612a1d411..c7f29defe4f1cc50220e9c6bc3d7ec6d5ec34b86 100644 (file)
@@ -1073,6 +1073,10 @@ static int sd_ioctl(struct block_device *bdev, fmode_t mode,
        SCSI_LOG_IOCTL(1, printk("sd_ioctl: disk=%s, cmd=0x%x\n",
                                                disk->disk_name, cmd));
 
+       error = scsi_verify_blk_ioctl(bdev, cmd);
+       if (error < 0)
+               return error;
+
        /*
         * If we are in the middle of error recovery, don't let anyone
         * else try and use this device.  Also, if error recovery fails, it
@@ -1265,6 +1269,11 @@ static int sd_compat_ioctl(struct block_device *bdev, fmode_t mode,
                           unsigned int cmd, unsigned long arg)
 {
        struct scsi_device *sdev = scsi_disk(bdev->bd_disk)->device;
+       int ret;
+
+       ret = scsi_verify_blk_ioctl(bdev, cmd);
+       if (ret < 0)
+               return ret;
 
        /*
         * If we are in the middle of error recovery, don't let anyone
@@ -1276,8 +1285,6 @@ static int sd_compat_ioctl(struct block_device *bdev, fmode_t mode,
                return -ENODEV;
               
        if (sdev->host->hostt->compat_ioctl) {
-               int ret;
-
                ret = sdev->host->hostt->compat_ioctl(sdev, cmd, (void __user *)arg);
 
                return ret;
index 8ef5ad1f0cd15460ecc3e47cabe296c7113c852f..022aefa62e18fdbc14a0912328a989723dbac777 100644 (file)
@@ -674,6 +674,7 @@ extern int blk_insert_cloned_request(struct request_queue *q,
                                     struct request *rq);
 extern void blk_delay_queue(struct request_queue *, unsigned long);
 extern void blk_recount_segments(struct request_queue *, struct bio *);
+extern int scsi_verify_blk_ioctl(struct block_device *, unsigned int);
 extern int scsi_cmd_blk_ioctl(struct block_device *, fmode_t,
                              unsigned int, void __user *);
 extern int scsi_cmd_ioctl(struct request_queue *, struct gendisk *, fmode_t,