166 lines
5.9 KiB
Diff
166 lines
5.9 KiB
Diff
From e773daff833c61e0ce22d62b7d1bb7b82f4222d0 Mon Sep 17 00:00:00 2001
|
|
From: Paolo Bonzini <pbonzini@redhat.com>
|
|
Date: Mon, 16 Jan 2012 17:12:59 +0100
|
|
Subject: [PATCH 2/3] block: fail SCSI passthrough ioctls on partition devices
|
|
|
|
Linux allows executing the SG_IO ioctl on a partition or 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 only to part of the disk.
|
|
|
|
This patch lets partitions forward a small set of harmless ioctls;
|
|
others are logged with printk so that we can see which ioctls are
|
|
actually sent. In my tests only CDROM_GET_CAPABILITY actually occurred.
|
|
Of course it was being sent to a (partition on a) hard disk, so it would
|
|
have failed with ENOTTY and the patch isn't changing anything in
|
|
practice. Still, I'm treating it specially to avoid spamming the logs.
|
|
|
|
In principle, this restriction should include programs running with
|
|
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. However, for
|
|
now programs with CAP_SYS_RAWIO will still be allowed to send the
|
|
ioctls. Their actions will still be logged.
|
|
|
|
This patch does not affect the non-libata IDE driver. That driver
|
|
however already tests for bd != bd->bd_contains before issuing some
|
|
ioctl; it could be restricted further to forbid these ioctls even for
|
|
programs running with CAP_SYS_ADMIN/CAP_SYS_RAWIO.
|
|
|
|
[ Cherry picked from 3ed4e7ba4be8c72051d87dcb2dec279d97a18d41
|
|
|
|
Changes with respect to 3.3: return -ENOTTY from scsi_verify_blk_ioctl
|
|
and -ENOIOCTLCMD from sd_compat_ioctl. ]
|
|
|
|
Cc: stable@kernel.org
|
|
Cc: linux-scsi@vger.kernel.org
|
|
Cc: Jens Axboe <axboe@kernel.dk>
|
|
Cc: James Bottomley <JBottomley@parallels.com>
|
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
|
[ Make it also print the command name when warning - Linus ]
|
|
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
|
|
---
|
|
block/scsi_ioctl.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
|
|
drivers/scsi/sd.c | 11 +++++++++--
|
|
include/linux/blkdev.h | 1 +
|
|
3 files changed, 55 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c
|
|
index 57ac937..5ef1f4c 100644
|
|
--- a/block/scsi_ioctl.c
|
|
+++ b/block/scsi_ioctl.c
|
|
@@ -24,6 +24,7 @@
|
|
#include <linux/capability.h>
|
|
#include <linux/completion.h>
|
|
#include <linux/cdrom.h>
|
|
+#include <linux/ratelimit.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/times.h>
|
|
#include <asm/uaccess.h>
|
|
@@ -691,9 +692,53 @@ 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 these is particularly useful on a partition,
|
|
+ * but they are 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;
|
|
+ case CDROM_GET_CAPABILITY:
|
|
+ /* Keep this until we remove the printk below. udev sends it
|
|
+ * and we do not want to spam dmesg about it. CD-ROMs do
|
|
+ * not have partitions, so we get here only for disks.
|
|
+ */
|
|
+ return -ENOTTY;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* In particular, rule out all resets and host-specific ioctls. */
|
|
+ printk_ratelimited(KERN_WARNING
|
|
+ "%s: sending ioctl %x to a partition!\n", current->comm, cmd);
|
|
+
|
|
+ return capable(CAP_SYS_RAWIO) ? 0 : -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);
|
|
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
|
|
index c88885d..7d8b5d8 100644
|
|
--- a/drivers/scsi/sd.c
|
|
+++ b/drivers/scsi/sd.c
|
|
@@ -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 -ENOIOCTLCMD;
|
|
|
|
/*
|
|
* 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;
|
|
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
|
|
index aa829a4..8b7a19e 100644
|
|
--- a/include/linux/blkdev.h
|
|
+++ b/include/linux/blkdev.h
|
|
@@ -675,6 +675,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,
|
|
--
|
|
1.7.7.5
|
|
|