History

xen/privcmd: add IOCTL_PRIVCMD_RESTRICT

The purpose if this ioctl is to allow a user of privcmd to restrict its operation such that it will no longer service arbitrary hypercalls via IOCTL_PRIVCMD_HYPERCALL, and will check for a matching domid when servicing IOCTL_PRIVCMD_DM_OP or IOCTL_PRIVCMD_MMAP*. The aim of this is to limit the attack surface for a compromised device model.

Paul Durrant committed 9 years ago

Commit 4610d241 parent ab520be2 files · +81 −9Browse files

Unified · Split

2 files
  1. drivers/xen/privcmd.c +79 −9
  2. include/uapi/xen/privcmd.h +2 −0
drivers/xen/privcmd.c+79−9
@@ -56,16 +56,25 @@ module_param_named(dm_op_buf_max_size, privcmd_dm_op_buf_max_size, uint, · 55 unmodified lines
5656 MODULE_PARM_DESC(dm_op_buf_max_size,
5757 "Maximum size of a dm_op hypercall buffer");
5858
59+struct privcmd_data {
60+ domid_t domid;
61+};
62+
5963 static int privcmd_vma_range_is_mapped(
6064 struct vm_area_struct *vma,
6165 unsigned long addr,
6266 unsigned long nr_pages);
6367
64−static long privcmd_ioctl_hypercall(void __user *udata)
68+static long privcmd_ioctl_hypercall(struct file *file, void __user *udata)
6569 {
70+ struct privcmd_data *data = file->private_data;
6671 struct privcmd_hypercall hypercall;
6772 long ret;
6873
74+ /* Disallow arbitrary hypercalls if restricted */
75+ if (data->domid != DOMID_INVALID)
76+ return -EPERM;
77+
6978 if (copy_from_user(&hypercall, udata, sizeof(hypercall)))
7079 return -EFAULT;
7180
@@ -242,8 +251,9 @@ static int mmap_gfn_range(void *data, void *state) · 170 unmodified lines
242251 return 0;
243252 }
244253
245−static long privcmd_ioctl_mmap(void __user *udata)
254+static long privcmd_ioctl_mmap(struct file *file, void __user *udata)
246255 {
256+ struct privcmd_data *data = file->private_data;
247257 struct privcmd_mmap mmapcmd;
248258 struct mm_struct *mm = current->mm;
249259 struct vm_area_struct *vma;
@@ -258,6 +268,10 @@ static long privcmd_ioctl_mmap(void __user *udata) · 8 unmodified lines
258268 if (copy_from_user(&mmapcmd, udata, sizeof(mmapcmd)))
259269 return -EFAULT;
260270
271+ /* If restriction is in place, check the domid matches */
272+ if (data->domid != DOMID_INVALID && data->domid != mmapcmd.dom)
273+ return -EPERM;
274+
261275 rc = gather_array(&pagelist,
262276 mmapcmd.num, sizeof(struct privcmd_mmap_entry),
263277 mmapcmd.entry);
@@ -429,8 +443,10 @@ static int alloc_empty_pages(struct vm_area_struct *vma, int numpgs) · 165 unmodified lines
429443
430444 static const struct vm_operations_struct privcmd_vm_ops;
431445
432−static long privcmd_ioctl_mmap_batch(void __user *udata, int version)
446+static long privcmd_ioctl_mmap_batch(
447+ struct file *file, void __user *udata, int version)
433448 {
449+ struct privcmd_data *data = file->private_data;
434450 int ret;
435451 struct privcmd_mmapbatch_v2 m;
436452 struct mm_struct *mm = current->mm;
@@ -459,6 +475,10 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version) · 22 unmodified lines
459475 return -EINVAL;
460476 }
461477
478+ /* If restriction is in place, check the domid matches */
479+ if (data->domid != DOMID_INVALID && data->domid != m.dom)
480+ return -EPERM;
481+
462482 nr_pages = DIV_ROUND_UP(m.num, XEN_PFN_PER_PAGE);
463483 if ((m.num <= 0) || (nr_pages > (LONG_MAX >> PAGE_SHIFT)))
464484 return -EINVAL;
@@ -603,8 +623,9 @@ static void unlock_pages(struct page *pages[], unsigned int nr_pages) · 138 unmodified lines
603623 }
604624 }
605625
606−static long privcmd_ioctl_dm_op(void __user *udata)
626+static long privcmd_ioctl_dm_op(struct file *file, void __user *udata)
607627 {
628+ struct privcmd_data *data = file->private_data;
608629 struct privcmd_dm_op kdata;
609630 struct privcmd_dm_op_buf *kbufs;
610631 unsigned int nr_pages = 0;
@@ -616,6 +637,10 @@ static long privcmd_ioctl_dm_op(void __user *udata) · 5 unmodified lines
616637 if (copy_from_user(&kdata, udata, sizeof(kdata)))
617638 return -EFAULT;
618639
640+ /* If restriction is in place, check the domid matches */
641+ if (data->domid != DOMID_INVALID && data->domid != kdata.dom)
642+ return -EPERM;
643+
619644 if (kdata.num == 0)
620645 return 0;
621646
@@ -683,6 +708,23 @@ out: · 61 unmodified lines
683708 return rc;
684709 }
685710
711+static long privcmd_ioctl_restrict(struct file *file, void __user *udata)
712+{
713+ struct privcmd_data *data = file->private_data;
714+ domid_t dom;
715+
716+ if (copy_from_user(&dom, udata, sizeof(dom)))
717+ return -EFAULT;
718+
719+ /* Set restriction to the specified domain, or check it matches */
720+ if (data->domid == DOMID_INVALID)
721+ data->domid = dom;
722+ else if (data->domid != dom)
723+ return -EINVAL;
724+
725+ return 0;
726+}
727+
686728 static long privcmd_ioctl(struct file *file,
687729 unsigned int cmd, unsigned long data)
688730 {
@@ -691,23 +733,27 @@ static long privcmd_ioctl(struct file *file, · 2 unmodified lines
691733
692734 switch (cmd) {
693735 case IOCTL_PRIVCMD_HYPERCALL:
694− ret = privcmd_ioctl_hypercall(udata);
736+ ret = privcmd_ioctl_hypercall(file, udata);
695737 break;
696738
697739 case IOCTL_PRIVCMD_MMAP:
698− ret = privcmd_ioctl_mmap(udata);
740+ ret = privcmd_ioctl_mmap(file, udata);
699741 break;
700742
701743 case IOCTL_PRIVCMD_MMAPBATCH:
702− ret = privcmd_ioctl_mmap_batch(udata, 1);
744+ ret = privcmd_ioctl_mmap_batch(file, udata, 1);
703745 break;
704746
705747 case IOCTL_PRIVCMD_MMAPBATCH_V2:
706− ret = privcmd_ioctl_mmap_batch(udata, 2);
748+ ret = privcmd_ioctl_mmap_batch(file, udata, 2);
707749 break;
708750
709751 case IOCTL_PRIVCMD_DM_OP:
710− ret = privcmd_ioctl_dm_op(udata);
752+ ret = privcmd_ioctl_dm_op(file, udata);
753+ break;
754+
755+ case IOCTL_PRIVCMD_RESTRICT:
756+ ret = privcmd_ioctl_restrict(file, udata);
711757 break;
712758
713759 default:
@@ -717,6 +763,28 @@ static long privcmd_ioctl(struct file *file, · 3 unmodified lines
717763 return ret;
718764 }
719765
766+static int privcmd_open(struct inode *ino, struct file *file)
767+{
768+ struct privcmd_data *data = kzalloc(sizeof(*data), GFP_KERNEL);
769+
770+ if (!data)
771+ return -ENOMEM;
772+
773+ /* DOMID_INVALID implies no restriction */
774+ data->domid = DOMID_INVALID;
775+
776+ file->private_data = data;
777+ return 0;
778+}
779+
780+static int privcmd_release(struct inode *ino, struct file *file)
781+{
782+ struct privcmd_data *data = file->private_data;
783+
784+ kfree(data);
785+ return 0;
786+}
787+
720788 static void privcmd_close(struct vm_area_struct *vma)
721789 {
722790 struct page **pages = vma->vm_private_data;
@@ -785,6 +853,8 @@ static int privcmd_vma_range_is_mapped( · 62 unmodified lines
785853 const struct file_operations xen_privcmd_fops = {
786854 .owner = THIS_MODULE,
787855 .unlocked_ioctl = privcmd_ioctl,
856+ .open = privcmd_open,
857+ .release = privcmd_release,
788858 .mmap = privcmd_mmap,
789859 };
790860 EXPORT_SYMBOL_GPL(xen_privcmd_fops);
include/uapi/xen/privcmd.h+2−0
@@ -111,5 +111,7 @@ struct privcmd_dm_op { · 110 unmodified lines
111111 _IOC(_IOC_NONE, 'P', 4, sizeof(struct privcmd_mmapbatch_v2))
112112 #define IOCTL_PRIVCMD_DM_OP \
113113 _IOC(_IOC_NONE, 'P', 5, sizeof(struct privcmd_dm_op))
114+#define IOCTL_PRIVCMD_RESTRICT \
115+ _IOC(_IOC_NONE, 'P', 6, sizeof(domid_t))
114116
115117 #endif /* __LINUX_PUBLIC_PRIVCMD_H__ */