[RFC 2/3] WhiteEgret: Add device driver.

From: Masanobu Koike
Date: Tue May 30 2017 - 07:19:51 EST


WhiteEgret supports communication between kernel space and
user space using device driver.
This RFC provides the driver implementation.

Build the kernel with CONFIG_WHITEEGRET_DRIVER=y.
This option is defined automatically when
CONFIG_SECURITY_WHITEEGRET_DRIVER=y is set.
Then the loadable kernel module we_driver.ko is created into
/lib/modules/$(uname -r)/build/drivers/security/whiteegret/.

Next step is
insmod we_driver.ko
command. This command creates a special file /dev/wecom.

Signed-off-by: Masanobu Koike <masanobu2.koike@xxxxxxxxxxxxx>
---
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/security/Kconfig | 1 +
drivers/security/Makefile | 1 +
drivers/security/whiteegret/Kconfig | 10 ++
drivers/security/whiteegret/Makefile | 3 +
drivers/security/whiteegret/we_driver.c | 295 ++++++++++++++++++++++++++++++++
drivers/security/whiteegret/we_driver.h | 32 ++++
8 files changed, 345 insertions(+)
create mode 100644 drivers/security/Kconfig
create mode 100644 drivers/security/Makefile
create mode 100644 drivers/security/whiteegret/Kconfig
create mode 100644 drivers/security/whiteegret/Makefile
create mode 100644 drivers/security/whiteegret/we_driver.c
create mode 100644 drivers/security/whiteegret/we_driver.h

diff --git a/drivers/Kconfig b/drivers/Kconfig
index ba2901e..8922bc7 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -206,4 +206,6 @@ source "drivers/fsi/Kconfig"

source "drivers/tee/Kconfig"

+source "drivers/security/whiteegret/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index cfabd14..fc1feeb 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -181,3 +181,4 @@ obj-$(CONFIG_NVMEM) += nvmem/
obj-$(CONFIG_FPGA) += fpga/
obj-$(CONFIG_FSI) += fsi/
obj-$(CONFIG_TEE) += tee/
+obj-$(CONFIG_SECURITY) += security/
diff --git a/drivers/security/Kconfig b/drivers/security/Kconfig
new file mode 100644
index 0000000..5633ac7
--- /dev/null
+++ b/drivers/security/Kconfig
@@ -0,0 +1 @@
+source "security/whiteegret/Kconfig"
diff --git a/drivers/security/Makefile b/drivers/security/Makefile
new file mode 100644
index 0000000..c17ec8a
--- /dev/null
+++ b/drivers/security/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_WHITEEGRET_DRIVER) += whiteegret/
diff --git a/drivers/security/whiteegret/Kconfig b/drivers/security/whiteegret/Kconfig
new file mode 100644
index 0000000..912b690
--- /dev/null
+++ b/drivers/security/whiteegret/Kconfig
@@ -0,0 +1,10 @@
+config WHITEEGRET_DRIVER
+ depends on SECURITY_WHITEEGRET_DRIVER
+ tristate "Driver for WhiteEgret LSM module"
+ default m
+ help
+ This driver adds communication functionality between user space
+ and kernel space of the WhiteEgret LSM module.
+ Building with this option, we_driver.ko is created in this
+ directory. Then insmod we_driver.ko command create a special
+ file /dev/wecom.
diff --git a/drivers/security/whiteegret/Makefile b/drivers/security/whiteegret/Makefile
new file mode 100644
index 0000000..7078407
--- /dev/null
+++ b/drivers/security/whiteegret/Makefile
@@ -0,0 +1,3 @@
+ccflags-y += -I$(srctree)/security/whiteegret
+
+obj-$(CONFIG_WHITEEGRET_DRIVER) += we_driver.o
diff --git a/drivers/security/whiteegret/we_driver.c b/drivers/security/whiteegret/we_driver.c
new file mode 100644
index 0000000..f1e1c72
--- /dev/null
+++ b/drivers/security/whiteegret/we_driver.c
@@ -0,0 +1,295 @@
+/*
+ * WhiteEgret Linux Security Module
+ *
+ * Copyright (C) 2017 Toshiba Corporation
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/cdev.h>
+#include "dd_com.h"
+
+/*
+ * This option informs we_driver.h that this file is built as
+ * loadable kernel module.
+ */
+#define WE_LKM
+#include "we_driver.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Toshiba");
+
+#define static_assert(constexpr) \
+ char dummy[(constexpr) ? 1 : -1] __attribute__((unused))
+
+#define WE_COPY_TO_USER(to, from, ret) \
+ do { \
+ static_assert(sizeof((to)) == sizeof((from))); \
+ (ret) = copy_to_user(&(to), &(from), sizeof(to)); \
+ } while (0)
+
+#define WE_COPY_FROM_USER(to, from, ret) \
+ do { \
+ static_assert(sizeof((to)) == sizeof((from))); \
+ (ret) = copy_from_user(&(to), &(from), sizeof(to)); \
+ } while (0)
+
+#define SUCCESS 0
+
+#define WE_MINOR 1
+#define WE_CLASS_NAME "we_class"
+
+static int we_major;
+static struct cdev we_cdev;
+static struct class *we_class;
+
+static rwlock_t resource_lock;
+static struct we_req_q_head *root;
+
+static struct we_req_q *get_alive_we_req(struct we_req_q_head *root)
+{
+ struct list_head *p;
+ struct we_req_q *req, *ret = NULL;
+
+ read_lock(&root->lock);
+ list_for_each(p, &root->head) {
+ req = list_entry(p, struct we_req_q, queue);
+ if (req->finish_flag == STOP_EXEC) {
+ ret = req;
+ break;
+ }
+ }
+ read_unlock(&root->lock);
+
+ return ret;
+}
+
+static struct we_req_q *we_req_search(struct we_req_q_head *root,
+ pid_t ppid)
+{
+ struct list_head *p;
+ struct we_req_q *req, *ret = NULL;
+
+ read_lock(&root->lock);
+ list_for_each(p, &root->head) {
+ req = list_entry(p, struct we_req_q, queue);
+ if (req->data.we_obj_info->ppid == ppid) {
+ ret = req;
+ break;
+ }
+ }
+ read_unlock(&root->lock);
+
+ return ret;
+}
+
+static int check_we_pathsize(struct we_req_q *we_req, int size)
+{
+ if (size - sizeof(*we_req)
+ > we_req->data.we_obj_info->pathsize)
+ return 0;
+ else
+ return -1;
+}
+
+static unsigned long set_we_req_info(struct we_req_user *user,
+ struct we_obj_info *info)
+{
+ unsigned long ret;
+
+ WE_COPY_TO_USER(user->pid, info->pid, ret);
+ if (ret != 0)
+ return -EFAULT;
+
+ WE_COPY_TO_USER(user->ppid, info->ppid, ret);
+ if (ret != 0)
+ return -EFAULT;
+ WE_COPY_TO_USER(user->shortname, info->shortname, ret);
+ if (ret != 0)
+ return -EFAULT;
+ WE_COPY_TO_USER(user->pathsize, info->pathsize, ret);
+ if (ret != 0)
+ return -EFAULT;
+ ret = copy_to_user(user->path, info->path, info->pathsize + 1);
+ if (ret != 0)
+ return -EFAULT;
+ return 0;
+}
+
+static ssize_t we_driver_read(struct file *file, char *buf,
+ size_t size, loff_t *off)
+{
+ int ret;
+ struct we_req_q *we_req;
+ struct we_req_user *user;
+
+ while (1) {
+ ret = wait_event_interruptible(root->waitq,
+ (we_req = get_alive_we_req(root)));
+ if (ret < 0) {
+ pr_info("WhiteEgret: %s: signal (%d)", __func__, ret);
+ return 0;
+ }
+
+ if (we_req) {
+ user = (struct we_req_user *)((void *)(buf));
+ if (check_we_pathsize(we_req, size)) {
+ pr_err("WhiteEgret: ");
+ pr_err("Path length of exec is too long (%d).\n",
+ we_req->data.we_obj_info->pathsize);
+ return -EPERM;
+ }
+
+ set_we_req_info(user,
+ we_req->data.we_obj_info);
+ break;
+ }
+
+ pr_warn("WhiteEgret: %s: can not find we_req.\n", __func__);
+ }
+
+ pr_info("WhiteEgret: read %s.", we_req->data.we_obj_info->path);
+
+ return sizeof(*user) + user->pathsize + 1;
+}
+
+static unsigned long set_we_ack(struct we_ack *to, struct we_ack *from)
+{
+ unsigned long ret;
+
+ WE_COPY_FROM_USER(to->ppid, from->ppid, ret);
+ if (ret != 0)
+ return -EFAULT;
+ WE_COPY_FROM_USER(to->permit, from->permit, ret);
+ if (ret != 0)
+ return -EFAULT;
+
+ return 0;
+}
+
+static size_t send_ack(struct we_req_q *req, struct we_ack *ack)
+{
+ if (!req) {
+ pr_warn("WhiteEgret: %s: can not find we_req.\n", __func__);
+ return -EPERM;
+ }
+ req->permit = ack->permit;
+ req->finish_flag = START_EXEC;
+ wake_up_interruptible(&req->waitq);
+ return sizeof(*ack);
+}
+
+static ssize_t we_driver_write(struct file *file, const char *buf,
+ size_t size, loff_t *off)
+{
+ size_t ret;
+ struct we_req_q *we_req;
+ struct we_ack ack;
+
+ set_we_ack(&ack, (struct we_ack *)((void *)buf));
+ we_req = we_req_search(root, ack.ppid);
+ ret = send_ack(we_req, &ack);
+ pr_info("WhiteEgret: write %s.", we_req->data.we_obj_info->path);
+ return ret;
+}
+
+static long we_driver_ioctl(struct file *file,
+ unsigned int arg0, unsigned long arg1)
+{
+ return SUCCESS;
+}
+
+static int we_driver_release(struct inode *inode, struct file *filp)
+{
+ int ret = 0;
+
+ ret = stop_we();
+ pr_info("WhiteEgret: we_driver closed (%d)\n", ret);
+ return ret;
+}
+
+static int we_driver_open(struct inode *inode, struct file *filp)
+{
+ root = start_we();
+ if (!root)
+ return -EPERM;
+ return SUCCESS;
+}
+
+static const struct file_operations we_driver_fops = {
+ .owner = THIS_MODULE,
+ .read = we_driver_read,
+ .write = we_driver_write,
+ .unlocked_ioctl = we_driver_ioctl,
+ .open = we_driver_open,
+ .release = we_driver_release,
+};
+
+static int we_driver_init(void)
+{
+ int ret;
+ dev_t we_dev;
+ struct device *we_device;
+
+ ret = alloc_chrdev_region(&we_dev, 0, WE_MINOR, WE_DEV_NAME);
+ if (ret < 0) {
+ pr_err("WhiteEgret: ");
+ pr_err("alloc_chrdev_region error: can not allocate chrdev.\n");
+ return ret;
+ }
+ we_major = MAJOR(we_dev);
+
+ we_class = class_create(THIS_MODULE, WE_CLASS_NAME);
+ if (IS_ERR(we_class)) {
+ pr_err("WhiteEgret: class_create error.\n");
+ ret = PTR_ERR(we_class);
+ goto failure_register;
+ }
+
+ we_device = device_create(we_class, NULL, we_dev, NULL, WE_DEV_NAME);
+ if (IS_ERR(we_device)) {
+ pr_err("WhiteEgret: device_create error.\n");
+ ret = PTR_ERR(we_device);
+ goto failure_class;
+ }
+
+ cdev_init(&we_cdev, &we_driver_fops);
+ we_cdev.owner = THIS_MODULE;
+ ret = cdev_add(&we_cdev, we_dev, WE_MINOR);
+ if (ret < 0) {
+ pr_err("WhiteEgret: cdev_add error: can not register chrdev.\n");
+ goto failure_device;
+ }
+
+ pr_info("WhiteEgret: we_driver is installed.\n");
+ rwlock_init(&resource_lock);
+ return 0;
+
+failure_device:
+ device_destroy(we_class, we_dev);
+failure_class:
+ class_destroy(we_class);
+failure_register:
+ unregister_chrdev_region(we_dev, WE_MINOR);
+
+ return ret;
+}
+
+static void we_driver_exit(void)
+{
+ dev_t we_dev;
+
+ we_dev = MKDEV(we_major, WE_MINOR);
+ device_destroy(we_class, we_dev);
+ class_destroy(we_class);
+ unregister_chrdev_region(we_dev, WE_MINOR);
+ pr_info("WhiteEgret: we_driver is removed.\n");
+}
+
+module_init(we_driver_init);
+module_exit(we_driver_exit);
diff --git a/drivers/security/whiteegret/we_driver.h b/drivers/security/whiteegret/we_driver.h
new file mode 100644
index 0000000..5907dfa
--- /dev/null
+++ b/drivers/security/whiteegret/we_driver.h
@@ -0,0 +1,32 @@
+/*
+ * WhiteEgret Linux Security Module
+ *
+ * Copyright (C) 2017 Toshiba Corporation
+ */
+
+#ifndef _WE_DRIVER_H
+#define _WE_DRIVER_H
+
+#ifndef WE_LKM
+#include <sys/types.h>
+#endif
+
+#define WE_DEV_NAME "wecom"
+#define WE_DEV_PATH "/dev/"WE_DEV_NAME
+
+#define SHORTNAMELENGTH 256
+
+struct we_req_user {
+ pid_t pid;
+ pid_t ppid;
+ char shortname[SHORTNAMELENGTH];
+ int pathsize;
+ char path[0];
+};
+
+struct we_ack {
+ int permit;
+ pid_t ppid;
+};
+
+#endif /* _WE_DRIVER_H */
--
2.9.3