Re: SG_IO and security

Jeff Garzik
2004-08-13T19:42:39Z

[-- Attachment #1: Type: text/plain, Size: 1039 bytes --]

Peter Jones wrote:
> On Thu, 12 Aug 2004 22:22:36 +0300 (EEST), Kai Makisara
> <kai.makisara@kolumbus.fi> 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

[-- Attachment #2: patch --]
[-- Type: text/plain, Size: 4666 bytes --]

# ChangeSet
# 2004/08/12 17:51:15-07:00 torvalds@ppc970.osdl.org
# 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;