Re: SG_IO and security

From: Jeff Garzik
Date: Fri Aug 13 2004 - 14:47:44 EST


Peter Jones wrote:
On Thu, 12 Aug 2004 22:22:36 +0300 (EEST), Kai Makisara
<kai.makisara@xxxxxxxxxxx> wrote:

On Thu, 12 Aug 2004, Linus Torvalds wrote:

Let's see now:

brw-rw---- 1 root disk 3, 0 Jan 30 2003 /dev/hda

would you put people you don't trust with your disk in the "disk" group?


This protects disks in practice but SG_IO is currently supported by other
devices, at least SCSI tapes. It is reasonable in some organizations to
give r/w access to ordinary users so that they can read/write tapes. I
would be worried if this would enable the users, for instance, to mess up
the mode page contents of the drive or change the firmware.


Sure, but for that we need command based filtering.

We have that now (sigh). See attached patch, which is in BK...

A similar approach could be applied to tape as well.

Though in general I think command-based filtering is not scalable... at the very least I would prefer a list loaded from userspace at boot.

Jeff


# ChangeSet
# 2004/08/12 17:51:15-07:00 torvalds@xxxxxxxxxxxxxxx
# Allow non-root users certain raw commands if they are deemed safe.
#
# We allow more commands if the disk was opened read-write.
#
diff -Nru a/drivers/block/scsi_ioctl.c b/drivers/block/scsi_ioctl.c
--- a/drivers/block/scsi_ioctl.c 2004-08-13 15:35:28 -04:00
+++ b/drivers/block/scsi_ioctl.c 2004-08-13 15:35:28 -04:00
@@ -105,8 +105,80 @@
return put_user(1, p);
}

-static int sg_io(request_queue_t *q, struct gendisk *bd_disk,
- struct sg_io_hdr *hdr)
+#define CMD_READ_SAFE 0x01
+#define CMD_WRITE_SAFE 0x02
+#define safe_for_read(cmd) [cmd] = CMD_READ_SAFE
+#define safe_for_write(cmd) [cmd] = CMD_WRITE_SAFE
+
+static int verify_command(struct file *file, unsigned char *cmd)
+{
+ static const unsigned char cmd_type[256] = {
+
+ /* Basic read-only commands */
+ safe_for_read(TEST_UNIT_READY),
+ safe_for_read(REQUEST_SENSE),
+ safe_for_read(READ_6),
+ safe_for_read(READ_10),
+ safe_for_read(READ_12),
+ safe_for_read(READ_16),
+ safe_for_read(READ_BUFFER),
+ safe_for_read(READ_LONG),
+ safe_for_read(INQUIRY),
+ safe_for_read(MODE_SENSE),
+ safe_for_read(MODE_SENSE_10),
+ safe_for_read(START_STOP),
+
+ /* Audio CD commands */
+ safe_for_read(GPCMD_PLAY_CD),
+ safe_for_read(GPCMD_PLAY_AUDIO_10),
+ safe_for_read(GPCMD_PLAY_AUDIO_MSF),
+ safe_for_read(GPCMD_PLAY_AUDIO_TI),
+
+ /* CD/DVD data reading */
+ safe_for_read(GPCMD_READ_CD),
+ safe_for_read(GPCMD_READ_CD_MSF),
+ safe_for_read(GPCMD_READ_DISC_INFO),
+ safe_for_read(GPCMD_READ_CDVD_CAPACITY),
+ safe_for_read(GPCMD_READ_DVD_STRUCTURE),
+ safe_for_read(GPCMD_READ_HEADER),
+ safe_for_read(GPCMD_READ_TRACK_RZONE_INFO),
+ safe_for_read(GPCMD_READ_SUBCHANNEL),
+ safe_for_read(GPCMD_READ_TOC_PMA_ATIP),
+ safe_for_read(GPCMD_REPORT_KEY),
+ safe_for_read(GPCMD_SCAN),
+
+ /* Basic writing commands */
+ safe_for_write(WRITE_6),
+ safe_for_write(WRITE_10),
+ safe_for_write(WRITE_VERIFY),
+ safe_for_write(WRITE_12),
+ safe_for_write(WRITE_VERIFY_12),
+ safe_for_write(WRITE_16),
+ safe_for_write(WRITE_BUFFER),
+ safe_for_write(WRITE_LONG),
+ };
+ unsigned char type = cmd_type[cmd[0]];
+
+ /* Anybody who can open the device can do a read-safe command */
+ if (type & CMD_READ_SAFE)
+ return 0;
+
+ /* Write-safe commands just require a writable open.. */
+ if (type & CMD_WRITE_SAFE) {
+ if (file->f_mode & FMODE_WRITE)
+ return 0;
+ }
+
+ /* And root can do any command.. */
+ if (capable(CAP_SYS_RAWIO))
+ return 0;
+
+ /* Otherwise fail it with an "Operation not permitted" */
+ return -EPERM;
+}
+
+static int sg_io(struct file *file, request_queue_t *q,
+ struct gendisk *bd_disk, struct sg_io_hdr *hdr)
{
unsigned long start_time;
int reading, writing;
@@ -115,14 +187,14 @@
char sense[SCSI_SENSE_BUFFERSIZE];
unsigned char cmd[BLK_MAX_CDB];

- if (!capable(CAP_SYS_RAWIO))
- return -EPERM;
if (hdr->interface_id != 'S')
return -EINVAL;
if (hdr->cmd_len > BLK_MAX_CDB)
return -EINVAL;
if (copy_from_user(cmd, hdr->cmdp, hdr->cmd_len))
return -EFAULT;
+ if (verify_command(file, cmd))
+ return -EPERM;

/*
* we'll do that later
@@ -228,15 +300,13 @@
#define READ_DEFECT_DATA_TIMEOUT (60 * HZ )
#define OMAX_SB_LEN 16 /* For backward compatibility */

-static int sg_scsi_ioctl(request_queue_t *q, struct gendisk *bd_disk,
- Scsi_Ioctl_Command __user *sic)
+static int sg_scsi_ioctl(struct file *file, request_queue_t *q,
+ struct gendisk *bd_disk, Scsi_Ioctl_Command __user *sic)
{
struct request *rq;
int err, in_len, out_len, bytes, opcode, cmdlen;
char *buffer = NULL, sense[SCSI_SENSE_BUFFERSIZE];

- if (!capable(CAP_SYS_RAWIO))
- return -EPERM;
/*
* get in an out lengths, verify they don't exceed a page worth of data
*/
@@ -273,6 +343,10 @@
if (copy_from_user(buffer, sic->data + cmdlen, in_len))
goto error;

+ err = verify_command(file, rq->cmd);
+ if (err)
+ goto error;
+
switch (opcode) {
case SEND_DIAGNOSTIC:
case FORMAT_UNIT:
@@ -370,7 +444,7 @@
err = -EFAULT;
if (copy_from_user(&hdr, arg, sizeof(hdr)))
break;
- err = sg_io(q, bd_disk, &hdr);
+ err = sg_io(file, q, bd_disk, &hdr);
if (err == -EFAULT)
break;

@@ -418,7 +492,7 @@
hdr.cmdp = ((struct cdrom_generic_command __user*) arg)->cmd;
hdr.cmd_len = sizeof(cgc.cmd);

- err = sg_io(q, bd_disk, &hdr);
+ err = sg_io(file, q, bd_disk, &hdr);
if (err == -EFAULT)
break;

@@ -441,7 +515,7 @@
if (!arg)
break;

- err = sg_scsi_ioctl(q, bd_disk, arg);
+ err = sg_scsi_ioctl(file, q, bd_disk, arg);
break;
case CDROMCLOSETRAY:
close = 1;