blob: b86c1af773bf3f4794ce91085db71efb2c572686 [file] [log] [blame]
/*
* Control device for requesting jails
*
* Copyright (C) 2016 Google, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/export.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <uapi/linux/device_jail.h>
#include "jail_device.h"
#include "jail_request.h"
#define JAIL_CONTROL_NAME "jail-control"
static int add_device(const char __user *path, u32 __user *devnum)
{
dev_t dev;
u32 dev_encoded;
int ret;
ret = add_jail_device(path, &dev);
if (ret < 0 && ret != -EEXIST)
return ret;
dev_encoded = new_encode_dev(dev);
if (copy_to_user(devnum, &dev_encoded, sizeof(*devnum)))
return -EFAULT;
return ret;
}
static int remove_device(u32 __user *arg)
{
u32 dev_encoded;
if (copy_from_user(&dev_encoded, arg, sizeof(dev_encoded)))
return -EFAULT;
return remove_jail_device(new_decode_dev(dev_encoded));
}
static long jail_control_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
switch (cmd) {
case JAIL_CONTROL_ADD_DEVICE: {
struct jail_control_add_dev karg;
struct jail_control_add_dev __user *uarg =
(struct jail_control_add_dev __user *) arg;
if (copy_from_user(&karg, uarg, sizeof(karg)))
return -EFAULT;
return add_device(karg.path, &uarg->devnum);
}
case JAIL_CONTROL_REMOVE_DEVICE:
return remove_device((u32 __user *) arg);
default:
return -ENOTTY;
}
}
/* for compat_ioctl */
struct jail_control_add_dev32 {
__u32 path; /* pointer */
__u32 devnum;
};
#define JAIL_CONTROL_ADD_DEVICE32 _IOWR('C', 0, struct jail_control_add_dev32)
static long jail_control_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
switch (cmd) {
case JAIL_CONTROL_ADD_DEVICE32: {
struct jail_control_add_dev32 karg;
struct jail_control_add_dev32 __user *uarg =
(struct jail_control_add_dev32 __user *) arg;
if (copy_from_user(&karg, uarg, sizeof(karg)))
return -EFAULT;
return add_device((const char __user *) (uintptr_t) karg.path,
&uarg->devnum);
}
case JAIL_CONTROL_REMOVE_DEVICE:
return remove_device((u32 __user *) arg);
default:
return -ENOTTY;
}
}
static const struct file_operations jail_control_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.compat_ioctl = jail_control_compat_ioctl,
.unlocked_ioctl = jail_control_ioctl,
.llseek = noop_llseek,
};
static struct miscdevice jail_control_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = JAIL_CONTROL_NAME,
.fops = &jail_control_fops,
};
static int __init jailed_cdev_init(void)
{
int ret;
ret = jail_device_setup();
if (ret < 0) {
pr_err(JAIL_CONTROL_NAME ": failed to set up jails\n");
return ret;
}
ret = jail_request_setup();
if (ret < 0) {
pr_err(JAIL_CONTROL_NAME ": failed to set up request device\n");
goto err_setup_request;
}
ret = misc_register(&jail_control_dev);
if (ret < 0) {
pr_err(JAIL_CONTROL_NAME ": failed to create control device\n");
goto err_register;
}
return 0;
err_register:
jail_request_teardown();
err_setup_request:
jail_device_teardown();
return ret;
}
static void __exit jailed_cdev_exit(void)
{
misc_deregister(&jail_control_dev);
jail_request_teardown();
jail_device_teardown();
}
module_init(jailed_cdev_init);
module_exit(jailed_cdev_exit);
MODULE_AUTHOR("Eric Caruso <ejcaruso@chromium.org>");
MODULE_DESCRIPTION("Character device jailed using Permission Broker");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");