From d522842c22f2e0cd60e3b5627abcc46f3a5353bd Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sun, 8 Jan 2012 23:56:42 +0000 Subject: [PATCH] Restrict ioctl forwarding on partitions and logical volumes (CVE-2011-4127) svn path=/dists/sid/linux-2.6/; revision=18480 --- debian/changelog | 1 + .../all/add-scsi_cmd_blk_ioctl-wrapper.patch | 158 ++++++++++++++++++ ...ioctls-forwarded-to-non-scsi-devices.patch | 125 ++++++++++++++ ...treat-lvs-on-one-pv-like-a-partition.patch | 81 +++++++++ debian/patches/series/base | 3 + 5 files changed, 368 insertions(+) create mode 100644 debian/patches/bugfix/all/add-scsi_cmd_blk_ioctl-wrapper.patch create mode 100644 debian/patches/bugfix/all/limit-ioctls-forwarded-to-non-scsi-devices.patch create mode 100644 debian/patches/bugfix/all/treat-lvs-on-one-pv-like-a-partition.patch diff --git a/debian/changelog b/debian/changelog index 00aacd820..47b1aac50 100644 --- a/debian/changelog +++ b/debian/changelog @@ -25,6 +25,7 @@ linux-2.6 (3.1.8-1) UNRELEASED; urgency=low * Update Russian debconf template translations (Yuri Kozlov) (Closes: #653716) * v4l2-ioctl: integer overflow in video_usercopy() + * Restrict ioctl forwarding on partitions and logical volumes (CVE-2011-4127) [ Jonathan Nieder ] * prerm: Print an error message when aborting removal of the running diff --git a/debian/patches/bugfix/all/add-scsi_cmd_blk_ioctl-wrapper.patch b/debian/patches/bugfix/all/add-scsi_cmd_blk_ioctl-wrapper.patch new file mode 100644 index 000000000..c5cfa68b9 --- /dev/null +++ b/debian/patches/bugfix/all/add-scsi_cmd_blk_ioctl-wrapper.patch @@ -0,0 +1,158 @@ +From: Paolo Bonzini +Subject: [PATCH 1/3] block: add and use scsi_blk_cmd_ioctl +Date: Thu, 22 Dec 2011 19:02:17 +0100 + +Introduce a wrapper around scsi_cmd_ioctl that takes a block device. +The function will then be enhanced to detect partition block devices and, +in that case, subject the ioctls to whitelisting. + +Signed-off-by: Paolo Bonzini +--- + block/scsi_ioctl.c | 7 +++++++ + drivers/block/cciss.c | 6 +++--- + drivers/block/ub.c | 14 +------------- + drivers/block/virtio_blk.c | 4 ++-- + drivers/cdrom/cdrom.c | 3 +-- + drivers/ide/ide-floppy_ioctl.c | 3 +-- + drivers/scsi/sd.c | 2 +- + include/linux/blkdev.h | 2 ++ + 8 files changed, 18 insertions(+), 23 deletions(-) + +diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c +index e5b1001..48dfbe7 100644 +--- a/block/scsi_ioctl.c ++++ b/block/scsi_ioctl.c +@@ -675,6 +675,13 @@ int scsi_cmd_ioctl(struct request_queue *q, struct gendisk *bd_disk, fmode_t mod + } + EXPORT_SYMBOL(scsi_cmd_ioctl); + ++int scsi_cmd_blk_ioctl(struct block_device *bd, fmode_t mode, ++ unsigned int cmd, void __user *arg) ++{ ++ return scsi_cmd_ioctl(bd->bd_disk->queue, bd->bd_disk, mode, cmd, arg); ++} ++EXPORT_SYMBOL(scsi_cmd_blk_ioctl); ++ + static int __init blk_scsi_ioctl_init(void) + { + blk_set_cmd_filter_defaults(&blk_default_cmd_filter); +diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c +index b8f2c02..ca4b52a 100644 +--- a/drivers/block/cciss.c ++++ b/drivers/block/cciss.c +@@ -1703,7 +1703,7 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode, + case CCISS_BIG_PASSTHRU: + return cciss_bigpassthru(h, argp); + +- /* scsi_cmd_ioctl handles these, below, though some are not */ ++ /* scsi_cmd_blk_ioctl handles these, below, though some are not */ + /* very meaningful for cciss. SG_IO is the main one people want. */ + + case SG_GET_VERSION_NUM: +@@ -1714,9 +1714,9 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode, + case SG_EMULATED_HOST: + case SG_IO: + case SCSI_IOCTL_SEND_COMMAND: +- return scsi_cmd_ioctl(disk->queue, disk, mode, cmd, argp); ++ return scsi_cmd_blk_ioctl(bdev, mode, cmd, argp); + +- /* scsi_cmd_ioctl would normally handle these, below, but */ ++ /* scsi_cmd_blk_ioctl would normally handle these, below, but */ + /* they aren't a good fit for cciss, as CD-ROMs are */ + /* not supported, and we don't have any bus/target/lun */ + /* which we present to the kernel. */ +diff --git a/drivers/block/ub.c b/drivers/block/ub.c +index 761ee6d..d3b15ff 100644 +--- a/drivers/block/ub.c ++++ b/drivers/block/ub.c +@@ -1721,12 +1721,11 @@ static int ub_bd_release(struct gendisk *disk, fmode_t mode) + static int ub_bd_ioctl(struct block_device *bdev, fmode_t mode, + unsigned int cmd, unsigned long arg) + { +- struct gendisk *disk = bdev->bd_disk; + void __user *usermem = (void __user *) arg; + int ret; + + mutex_lock(&ub_mutex); +- ret = scsi_cmd_ioctl(disk->queue, disk, mode, cmd, usermem); ++ ret = scsi_cmd_blk_ioctl(bdev, mode, cmd, usermem); + mutex_unlock(&ub_mutex); + + return ret; +diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c +index cc4f6ad..14f9a2a 100644 +--- a/drivers/block/virtio_blk.c ++++ b/drivers/block/virtio_blk.c +@@ -233,8 +233,8 @@ static int virtblk_ioctl(struct block_device *bdev, fmode_t mode, + if (!virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_SCSI)) + return -ENOTTY; + +- return scsi_cmd_ioctl(disk->queue, disk, mode, cmd, +- (void __user *)data); ++ return scsi_cmd_blk_ioctl(bdev, mode, cmd, ++ (void __user *)data); + } + + /* We provide getgeo only to please some old bootloader/partitioning tools */ +diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c +index 3bba1a2..20f5157 100644 +--- a/drivers/cdrom/cdrom.c ++++ b/drivers/cdrom/cdrom.c +@@ -2688,12 +2688,11 @@ int cdrom_ioctl(struct cdrom_device_info *cdi, struct block_device *bdev, + { + void __user *argp = (void __user *)arg; + int ret; +- struct gendisk *disk = bdev->bd_disk; + + /* + * Try the generic SCSI command ioctl's first. + */ +- ret = scsi_cmd_ioctl(disk->queue, disk, mode, cmd, argp); ++ ret = scsi_cmd_blk_ioctl(bdev, mode, cmd, argp); + if (ret != -ENOTTY) + return ret; + +diff --git a/drivers/ide/ide-floppy_ioctl.c b/drivers/ide/ide-floppy_ioctl.c +index 9c22882..05f024c 100644 +--- a/drivers/ide/ide-floppy_ioctl.c ++++ b/drivers/ide/ide-floppy_ioctl.c +@@ -287,8 +287,7 @@ int ide_floppy_ioctl(ide_drive_t *drive, struct block_device *bdev, + * and CDROM_SEND_PACKET (legacy) ioctls + */ + if (cmd != CDROM_SEND_PACKET && cmd != SCSI_IOCTL_SEND_COMMAND) +- err = scsi_cmd_ioctl(bdev->bd_disk->queue, bdev->bd_disk, +- mode, cmd, argp); ++ err = scsi_cmd_blk_ioctl(bdev, mode, cmd, argp); + + if (err == -ENOTTY) + err = generic_ide_ioctl(drive, bdev, cmd, arg); +diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c +index 5de155d..c6c449a 100644 +--- a/drivers/scsi/sd.c ++++ b/drivers/scsi/sd.c +@@ -1079,7 +1079,7 @@ static int sd_ioctl(struct block_device *bdev, fmode_t mode, + error = scsi_ioctl(sdp, cmd, p); + break; + default: +- error = scsi_cmd_ioctl(disk->queue, disk, mode, cmd, p); ++ error = scsi_cmd_blk_ioctl(bdev, mode, cmd, p); + if (error != -ENOTTY) + break; + error = scsi_ioctl(sdp, cmd, p); +diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h +index a7f9867..03a00a6 100644 +--- a/include/linux/blkdev.h ++++ b/include/linux/blkdev.h +@@ -761,6 +761,8 @@ extern void blk_plug_device(struct request_queue *); + 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_cmd_blk_ioctl(struct block_device *, fmode_t, ++ unsigned int, void __user *); + extern int scsi_cmd_ioctl(struct request_queue *, struct gendisk *, fmode_t, + unsigned int, void __user *); + extern int sg_scsi_ioctl(struct request_queue *, struct gendisk *, fmode_t, +-- +1.7.7.1 + + diff --git a/debian/patches/bugfix/all/limit-ioctls-forwarded-to-non-scsi-devices.patch b/debian/patches/bugfix/all/limit-ioctls-forwarded-to-non-scsi-devices.patch new file mode 100644 index 000000000..830365caa --- /dev/null +++ b/debian/patches/bugfix/all/limit-ioctls-forwarded-to-non-scsi-devices.patch @@ -0,0 +1,125 @@ +From: Paolo Bonzini +Subject: [PATCH 2/3] block: fail SCSI passthrough ioctls on partition devices +Date: Thu, 22 Dec 2011 19:02:18 +0100 + +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 +already tests for bd != bd->bd_contains before issuing some ioctl; so, +programs that do not require CAP_SYS_ADMIN or CAP_SYS_RAWIO are safe. +Whenever possible a workaround is just to use libata, of course. + +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 +[bwh: Adjust context for 3.1] +--- +--- a/block/scsi_ioctl.c ++++ b/block/scsi_ioctl.c +@@ -675,9 +675,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); +--- a/drivers/scsi/sd.c ++++ b/drivers/scsi/sd.c +@@ -1058,6 +1058,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 +@@ -1228,6 +1232,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 == -ENOTTY ? -ENOIOCTLCMD : ret; + + /* + * If we are in the middle of error recovery, don't let anyone +@@ -1239,8 +1248,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; +--- a/include/linux/blkdev.h ++++ b/include/linux/blkdev.h +@@ -761,6 +761,7 @@ extern void blk_plug_device(struct request_queue *); + 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, diff --git a/debian/patches/bugfix/all/treat-lvs-on-one-pv-like-a-partition.patch b/debian/patches/bugfix/all/treat-lvs-on-one-pv-like-a-partition.patch new file mode 100644 index 000000000..770798f7a --- /dev/null +++ b/debian/patches/bugfix/all/treat-lvs-on-one-pv-like-a-partition.patch @@ -0,0 +1,81 @@ +From: Paolo Bonzini +Subject: [PATCH 3/3] dm: do not forward ioctls from logical volumes to the underlying device +Date: Thu, 22 Dec 2011 19:02:19 +0100 + +A logical volume can map to just part of underlying physical volume. +In this case, it must be treated like a partition. + +Based on a patch from Alasdair G Kergon. + +Signed-off-by: Paolo Bonzini +--- + drivers/md/dm-flakey.c | 11 ++++++++++- + drivers/md/dm-linear.c | 12 +++++++++++- + drivers/md/dm-mpath.c | 6 ++++++ + 3 files changed, 27 insertions(+), 2 deletions(-) + +diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c +index f84c080..9fb18c1 100644 +--- a/drivers/md/dm-flakey.c ++++ b/drivers/md/dm-flakey.c +@@ -368,8 +368,17 @@ static int flakey_status(struct dm_target *ti, status_type_t type, + static int flakey_ioctl(struct dm_target *ti, unsigned int cmd, unsigned long arg) + { + struct flakey_c *fc = ti->private; ++ struct dm_dev *dev = fc->dev; ++ int r = 0; + +- return __blkdev_driver_ioctl(fc->dev->bdev, fc->dev->mode, cmd, arg); ++ /* ++ * Only pass ioctls through if the device sizes match exactly. ++ */ ++ if (fc->start || ++ ti->len != i_size_read(dev->bdev->bd_inode) >> SECTOR_SHIFT) ++ r = scsi_verify_blk_ioctl(NULL, cmd); ++ ++ return r ? : __blkdev_driver_ioctl(dev->bdev, dev->mode, cmd, arg); + } + + static int flakey_merge(struct dm_target *ti, struct bvec_merge_data *bvm, +diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c +index 3921e3b..9728839 100644 +--- a/drivers/md/dm-linear.c ++++ b/drivers/md/dm-linear.c +@@ -116,7 +116,17 @@ static int linear_ioctl(struct dm_target *ti, unsigned int cmd, + unsigned long arg) + { + struct linear_c *lc = (struct linear_c *) ti->private; +- return __blkdev_driver_ioctl(lc->dev->bdev, lc->dev->mode, cmd, arg); ++ struct dm_dev *dev = lc->dev; ++ int r = 0; ++ ++ /* ++ * Only pass ioctls through if the device sizes match exactly. ++ */ ++ if (lc->start || ++ ti->len != i_size_read(dev->bdev->bd_inode) >> SECTOR_SHIFT) ++ r = scsi_verify_blk_ioctl(NULL, cmd); ++ ++ return r ? : __blkdev_driver_ioctl(dev->bdev, dev->mode, cmd, arg); + } + + static int linear_merge(struct dm_target *ti, struct bvec_merge_data *bvm, +diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c +index 8ab377f..2bfbdb9 100644 +--- a/drivers/md/dm-mpath.c ++++ b/drivers/md/dm-mpath.c +@@ -1521,6 +1521,12 @@ static int multipath_ioctl(struct dm_target *ti, unsigned int cmd, + + spin_unlock_irqrestore(&m->lock, flags); + ++ /* ++ * Only pass ioctls through if the device sizes match exactly. ++ */ ++ if (!r && ti->len != i_size_read(bdev->bd_inode) >> SECTOR_SHIFT) ++ r = scsi_verify_blk_ioctl(NULL, cmd); ++ + return r ? : __blkdev_driver_ioctl(bdev, mode, cmd, arg); + } + +-- +1.7.7.1 diff --git a/debian/patches/series/base b/debian/patches/series/base index e39f1c3b1..d5e827db3 100644 --- a/debian/patches/series/base +++ b/debian/patches/series/base @@ -92,3 +92,6 @@ + debian/block-Restore-blk_init_allocated_queue_node-for-ABI-.patch + debian/sparc-Change-io_remap_pfn_range-back-into-an-extern-.patch + bugfix/all/media-V4L-DVB-v4l2-ioctl-integer-overflow-in-video_usercopy.patch ++ bugfix/all/add-scsi_cmd_blk_ioctl-wrapper.patch ++ bugfix/all/limit-ioctls-forwarded-to-non-scsi-devices.patch ++ bugfix/all/treat-lvs-on-one-pv-like-a-partition.patch