Re: [091/129] block: fail SCSI passthrough ioctls on partition devices

From: Sven Joachim
Date: Tue Jan 24 2012 - 09:10:12 EST


On 2012-01-24 14:01 +0100, Paolo Bonzini wrote:

> You need to return -ENOTTY from scsi_verify_blk_ioctl and -ENOIOCTLCMD from
> sd_compat_ioctl, because -ENOIOCTLCMD will not be handled correctly by
> block/ioctl.c. This would break BLKROSET and BLKFLSBUF done by non-root
> but with the appropriate capabilities.

I assume this is the reason why I suddenly got lots of ioctl32 warnings
in dmesg with 3.2.2-rc1?

,----
| $ dmesg | grep ioctl | head
| [ 0.815394] ioctl32(blkid:150): Unknown cmd fd(3) cmd(00005331){t:'S';sz:0} arg(00000000) on /dev/sda1
| [ 0.815812] ioctl32(blkid:154): Unknown cmd fd(3) cmd(00005331){t:'S';sz:0} arg(00000000) on /dev/sda6
| [ 0.816184] ioctl32(blkid:151): Unknown cmd fd(3) cmd(00005331){t:'S';sz:0} arg(00000000) on /dev/sda5
| [ 0.816559] ioctl32(blkid:155): Unknown cmd fd(3) cmd(00005331){t:'S';sz:0} arg(00000000) on /dev/sda9
| [ 0.816997] ioctl32(blkid:157): Unknown cmd fd(3) cmd(00005331){t:'S';sz:0} arg(00000000) on /dev/sda8
| [ 0.817371] ioctl32(blkid:153): Unknown cmd fd(3) cmd(00005331){t:'S';sz:0} arg(00000000) on /dev/sda3
| [ 0.817692] ioctl32(blkid:156): Unknown cmd fd(3) cmd(00005331){t:'S';sz:0} arg(00000000) on /dev/sda2
| [ 0.818063] ioctl32(blkid:152): Unknown cmd fd(3) cmd(00005331){t:'S';sz:0} arg(00000000) on /dev/sda7
| [ 2.824909] ioctl32(findfs:204): Unknown cmd fd(3) cmd(00005331){t:'S';sz:0} arg(00000000) on /dev/sda6
| [ 5.545235] ioctl32(blkid:435): Unknown cmd fd(3) cmd(00005331){t:'S';sz:0} arg(00000000) on /dev/sda3
`----

> Fixed patch follows. If you prefer that I send an interdiff, let me know.

Going to try that.

> Paolo
>
> -------- 8< ---------
> From: Paolo Bonzini <pbonzini@xxxxxxxxxx>
> Subject: [PATCH] block: fail SCSI passthrough ioctls on partition devices
>
> commit 0bfc96cb77224736dfa35c3c555d37b3646ef35e upstream.
>
> 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.
>
> Cc: linux-scsi@xxxxxxxxxxxxxxx
> Cc: Jens Axboe <axboe@xxxxxxxxx>
> Cc: James Bottomley <JBottomley@xxxxxxxxxxxxx>
> Signed-off-by: Paolo Bonzini <pbonzini@xxxxxxxxxx>
> [ Make it also print the command name when warning - Linus ]
> Signed-off-by: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx>
>
> [ Changes with respect to 3.3: return -ENOTTY from scsi_verify_blk_ioctl
> and -ENOIOCTLCMD from sd_compat_ioctl. ]
>
> ---
> block/scsi_ioctl.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
> drivers/scsi/sd.c | 11 +++++++++--
> include/linux/blkdev.h | 1 +
> 3 files changed, 55 insertions(+), 2 deletions(-)
>
> --- 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>
> @@ -690,9 +691,53 @@ int scsi_cmd_ioctl(struct request_queue
> }
> 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);
> --- a/drivers/scsi/sd.c
> +++ b/drivers/scsi/sd.c
> @@ -1074,6 +1074,10 @@ static int sd_ioctl(struct block_device
> SCSI_LOG_IOCTL(1, sd_printk(KERN_INFO, sdkp, "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
> @@ -1266,6 +1270,11 @@ static int sd_compat_ioctl(struct block_
> 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
> @@ -1277,8 +1286,6 @@ static int sd_compat_ioctl(struct block_
> 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
> @@ -675,6 +675,7 @@ extern int blk_insert_cloned_request(str
> 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,
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/