new file mode 100644
@@ -0,0 +1,38 @@
+#ifndef _LINUX_CHECMATE_H_
+#define _LINUX_CHECMATE_H_ 1
+#include <uapi/linux/checmate.h>
+#include <linux/security.h>
+
+/* Miscellanious contexts */
+struct checmate_file_open_ctx {
+ struct file *file;
+ const struct cred *cred;
+};
+
+struct checmate_task_create_ctx {
+ unsigned long clone_flags;
+};
+
+struct checmate_task_free_ctx {
+ struct task_struct *task;
+};
+
+struct checmate_socket_connect_ctx {
+ struct socket *sock;
+ struct sockaddr *address;
+ int addrlen;
+};
+
+struct checmate_ctx {
+ int hook;
+ union {
+ /* Miscellanious contexts */
+ struct checmate_file_open_ctx file_open_ctx;
+ struct checmate_task_create_ctx task_create_ctx;
+ struct checmate_task_free_ctx task_free_ctx;
+ /* CONFIG_SECURITY_NET contexts */
+ struct checmate_socket_connect_ctx socket_connect_ctx;
+ };
+};
+
+#endif
@@ -82,6 +82,7 @@ header-y += cciss_defs.h
header-y += cciss_ioctl.h
header-y += cdrom.h
header-y += cgroupstats.h
+header-y += checmate.h
header-y += chio.h
header-y += cm4000_cs.h
header-y += cn_proc.h
@@ -95,6 +95,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_SCHED_ACT,
BPF_PROG_TYPE_TRACEPOINT,
BPF_PROG_TYPE_XDP,
+ BPF_PROG_TYPE_CHECMATE,
};
#define BPF_PSEUDO_MAP_FD 1
new file mode 100644
@@ -0,0 +1,65 @@
+#ifndef _UAPI__LINUX_CHECMATE_H__
+#define _UAPI__LINUX_CHECMATE_H__
+
+#define CHECMATE_INSTALL_HOOK 1
+#define CHECMATE_DENY_RESET 2
+#define CHECMATE_RESET 3
+
+enum checmate_hook {
+ CHECMATE_HOOK_UNSPEC,
+ /* CONFIG_SECURITY_NET hooks */
+ CHECMATE_HOOK_UNIX_STREAM_CONNECT,
+ CHECMATE_HOOK_UNIX_MAY_SEND,
+ CHECMATE_HOOK_SOCKET_CREATE,
+ CHECMATE_HOOK_SOCKET_POST_CREATE,
+ CHECMATE_HOOK_SOCKET_BIND,
+ CHECMATE_HOOK_SOCKET_CONNECT,
+ CHECMATE_HOOK_SOCKET_LISTEN,
+ CHECMATE_HOOK_SOCKET_ACCEPT,
+ CHECMATE_HOOK_SOCKET_SENDMSG,
+ CHECMATE_HOOK_SOCKET_RECVMSG,
+ CHECMATE_HOOK_SOCKET_GETSOCKNAME,
+ CHECMATE_HOOK_SOCKET_GETPEERNAME,
+ CHECMATE_HOOK_SOCKET_GETSOCKOPT,
+ CHECMATE_HOOK_SOCKET_SETSOCKOPT,
+ CHECMATE_HOOK_SOCKET_SHUTDOWN,
+ CHECMATE_HOOK_SOCKET_SOCK_RCV_SKB,
+ CHECMATE_HOOK_SOCKET_GETPEERSEC_STREAM,
+ CHECMATE_HOOK_SOCKET_GETPEERSEC_DGRAM,
+ CHECMATE_HOOK_SK_ALLOC_SECURITY,
+ CHECMATE_HOOK_SK_FREE_SECURITY,
+ CHECMATE_HOOK_SK_CLONE_SECURITY,
+ CHECMATE_HOOK_SK_GETSECID,
+ CHECMATE_HOOK_SOCK_GRAFT,
+ CHECMATE_HOOK_INET_CONN_REQUEST,
+ CHECMATE_HOOK_INET_CSK_CLONE,
+ CHECMATE_HOOK_INET_CONN_ESTABLISHED,
+ CHECMATE_HOOK_SECMARK_RELABEL_PACKET,
+ CHECMATE_HOOK_SECMARK_REFCOUNT_INC,
+ CHECMATE_HOOK_SECMARK_REFCOUNT_DEC,
+ CHECMATE_HOOK_REQ_CLASSIFY_FLOW,
+ CHECMATE_HOOK_TUN_DEV_ALLOC_SECURITY,
+ CHECMATE_HOOK_TUN_DEV_FREE_SECURITY,
+ CHECMATE_HOOK_TUN_DEV_CREATE,
+ CHECMATE_HOOK_TUN_DEV_ATTACH_QUEUE,
+ CHECMATE_HOOK_TUN_DEV_ATTACH,
+ CHECMATE_HOOK_TUN_DEV_OPEN,
+ /* CONFIG_SECURITY_PATH hooks */
+ CHECMATE_HOOK_PATH_UNLINK,
+ CHECMATE_HOOK_PATH_MKDIR,
+ CHECMATE_HOOK_PATH_RMDIR,
+ CHECMATE_HOOK_PATH_MKNOD,
+ CHECMATE_HOOK_PATH_TRUNCATE,
+ CHECMATE_HOOK_PATH_SYMLINK,
+ CHECMATE_HOOK_PATH_LINK,
+ CHECMATE_HOOK_PATH_RENAME,
+ CHECMATE_HOOK_PATH_CHMOD,
+ CHECMATE_HOOK_PATH_CHOWN,
+ CHECMATE_HOOK_PATH_CHROOT,
+ /* Other hooks */
+ CHECMATE_HOOK_FILE_OPEN,
+ CHECMATE_HOOK_TASK_CREATE,
+ CHECMATE_HOOK_TASK_FREE,
+ __CHECMATE_HOOK_MAX,
+};
+#endif
@@ -197,4 +197,7 @@ struct prctl_mm_map {
# define PR_CAP_AMBIENT_LOWER 3
# define PR_CAP_AMBIENT_CLEAR_ALL 4
+/* (CHEC)MATE operations */
+#define PR_CHECMATE 0x43484543
+
#endif /* _LINUX_PRCTL_H */
@@ -124,6 +124,7 @@ source security/tomoyo/Kconfig
source security/apparmor/Kconfig
source security/loadpin/Kconfig
source security/yama/Kconfig
+source security/checmate/Kconfig
source security/integrity/Kconfig
@@ -8,6 +8,7 @@ subdir-$(CONFIG_SECURITY_SMACK) += smack
subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo
subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor
subdir-$(CONFIG_SECURITY_YAMA) += yama
+subdir-$(CONFIG_SECURITY_CHECMATE) += checmate
subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin
# always enable default capabilities
@@ -25,6 +26,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/
obj-$(CONFIG_SECURITY_YAMA) += yama/
obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
+obj-$(CONFIG_SECURITY_CHECMATE) += checmate/
# Object integrity file lists
subdir-$(CONFIG_INTEGRITY) += integrity
new file mode 100644
@@ -0,0 +1,6 @@
+config SECURITY_CHECMATE
+ bool "Checmate support"
+ depends on SECURITY
+ default n
+ help
+ This turns on checmate
new file mode 100644
@@ -0,0 +1,3 @@
+obj-$(CONFIG_SECURITY_CHECMATE) := checmate.o
+
+checmate-y := checmate_bpf.o checmate_lsm.o
new file mode 100644
new file mode 100644
@@ -0,0 +1,67 @@
+/*
+ * Checmate Linux Security Module
+ *
+ * Copyright (C) 2016 Sargun Dhillon <sargun@sargun.me>
+ *
+ * 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/bpf.h>
+#include <linux/checmate.h>
+
+static const struct bpf_func_proto *checmate_prog_func_proto(enum bpf_func_id func_id)
+{
+ switch (func_id) {
+ case BPF_FUNC_map_lookup_elem:
+ return &bpf_map_lookup_elem_proto;
+ case BPF_FUNC_map_update_elem:
+ return &bpf_map_update_elem_proto;
+ case BPF_FUNC_map_delete_elem:
+ return &bpf_map_delete_elem_proto;
+ case BPF_FUNC_probe_read:
+ return &bpf_probe_read_proto;
+ case BPF_FUNC_tail_call:
+ return &bpf_tail_call_proto;
+ case BPF_FUNC_get_current_pid_tgid:
+ return &bpf_get_current_pid_tgid_proto;
+ case BPF_FUNC_get_current_task:
+ return &bpf_get_current_task_proto;
+ case BPF_FUNC_get_current_uid_gid:
+ return &bpf_get_current_uid_gid_proto;
+ case BPF_FUNC_get_current_comm:
+ return &bpf_get_current_comm_proto;
+ case BPF_FUNC_trace_printk:
+ return bpf_get_trace_printk_proto();
+ default:
+ return NULL;
+ }
+}
+
+static bool checmate_prog_is_valid_access(int off, int size,
+ enum bpf_access_type type,
+ enum bpf_reg_type *reg_type)
+{
+ if (type != BPF_READ)
+ return false;
+ if (off < 0 || off >= sizeof(struct checmate_ctx))
+ return false;
+ return true;
+}
+
+static const struct bpf_verifier_ops checmate_prog_ops = {
+ .get_func_proto = checmate_prog_func_proto,
+ .is_valid_access = checmate_prog_is_valid_access,
+};
+
+static struct bpf_prog_type_list checmate_tl = {
+ .ops = &checmate_prog_ops,
+ .type = BPF_PROG_TYPE_CHECMATE,
+};
+
+void register_checmate_prog_ops(void)
+{
+ bpf_register_prog_type(&checmate_tl);
+}
new file mode 100644
@@ -0,0 +1,281 @@
+/*
+ * Checmate Linux Security Module
+ *
+ * Copyright (C) 2016 Sargun Dhillon <sargun@sargun.me>
+ *
+ * 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/prctl.h>
+#include <linux/checmate.h>
+#include <linux/lsm_hooks.h>
+#include <linux/mutex.h>
+#include <linux/bpf.h>
+#include <linux/filter.h>
+
+#define HOOK_INIT(HOOK_NUM) \
+ LIST_HEAD_INIT(checmate_bpf_hooks[HOOK_NUM].hook_list)
+
+#define CHECMATE_HOOK(HOOK_NUM) \
+ [HOOK_NUM] = \
+ { \
+ .enabled = true, \
+ .hook_list = HOOK_INIT(HOOK_NUM), \
+ }
+void register_checmate_prog_ops(void);
+
+/*
+ * Global write lock for all BPF program installations. This shouldn't
+ * see much contention, so locking is global, but it allows us to
+ * preserve that BPF hooks are unique.
+ */
+static DEFINE_MUTEX(checmate_write_lock);
+
+struct checmate_bpf_hook {
+ bool enabled;
+ bool deny_reset;
+ struct list_head hook_list;
+};
+
+struct checmate_bpf_hook_instance {
+ struct list_head list;
+ struct bpf_prog *prog;
+ struct rcu_head rcu;
+};
+
+static struct checmate_bpf_hook checmate_bpf_hooks[__CHECMATE_HOOK_MAX] = {
+ CHECMATE_HOOK(CHECMATE_HOOK_FILE_OPEN),
+ CHECMATE_HOOK(CHECMATE_HOOK_TASK_CREATE),
+ CHECMATE_HOOK(CHECMATE_HOOK_TASK_FREE),
+ CHECMATE_HOOK(CHECMATE_HOOK_SOCKET_CONNECT),
+};
+
+/*
+ * checmate_task_prctl_install_hook - Install a checmate hook
+ * @hook: Hook ID
+ * @prog_fd: BPF prog fd
+ *
+ * Return 0 on success
+ */
+static int checmate_prctl_install_hook(int hook, int prog_fd)
+{
+ int rc = 0;
+ struct bpf_prog *prog;
+ struct checmate_bpf_hook_instance *hook_instance;
+
+ prog = bpf_prog_get_type(prog_fd, BPF_PROG_TYPE_CHECMATE);
+ if (IS_ERR(prog))
+ return PTR_ERR(prog);
+
+ mutex_lock(&checmate_write_lock);
+ list_for_each_entry(hook_instance,
+ &checmate_bpf_hooks[hook].hook_list, list) {
+ if (hook_instance->prog == prog) {
+ rc = -EEXIST;
+ goto err;
+ }
+ }
+ hook_instance = kmalloc(sizeof(*hook_instance), GFP_KERNEL);
+
+ if (!hook_instance) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ hook_instance->prog = prog;
+ list_add_tail_rcu(&hook_instance->list,
+ &checmate_bpf_hooks[hook].hook_list);
+ mutex_unlock(&checmate_write_lock);
+ return rc;
+
+err:
+ mutex_unlock(&checmate_write_lock);
+ bpf_prog_put(prog);
+ return rc;
+}
+
+static int checmate_prctl_deny_reset(int hook)
+{
+ int rc = 0;
+
+ mutex_lock(&checmate_write_lock);
+ checmate_bpf_hooks[hook].deny_reset = true;
+ mutex_unlock(&checmate_write_lock);
+ return rc;
+}
+
+static void free_hook_instance(struct checmate_bpf_hook_instance *hook_instance)
+{
+ bpf_prog_put(hook_instance->prog);
+ kfree(hook_instance);
+}
+
+static void free_hook_instance_rcu(struct rcu_head *head)
+{
+ struct checmate_bpf_hook_instance *hook_instance;
+
+ hook_instance = container_of(head, struct checmate_bpf_hook_instance,
+ rcu);
+
+ free_hook_instance(hook_instance);
+}
+
+static int checmate_reset(int hook)
+{
+ int rc = 0;
+ struct checmate_bpf_hook_instance *hook_instance;
+
+ mutex_lock(&checmate_write_lock);
+ if (checmate_bpf_hooks[hook].deny_reset) {
+ rc = -EPERM;
+ goto out;
+ }
+ list_for_each_entry(hook_instance, &checmate_bpf_hooks[hook].hook_list,
+ list) {
+ list_del_rcu(&hook_instance->list);
+ call_rcu(&hook_instance->rcu, free_hook_instance_rcu);
+ }
+out:
+ mutex_unlock(&checmate_write_lock);
+ return rc;
+}
+
+/*
+ * checmate_task_prctl - check for Checmate-specific prctl operations
+ * @option: operation - supports CHECMATE_INSTALL_HOOK
+ * @arg2: argument - Option ID
+ * @arg3: argument - Hook ID
+ * @arg4: argument - BPF Prog FD, if installing
+ * @arg5: argument
+ *
+ * Return 0 on success, -ve on error. -ENOSYS is returned when checmate
+ * does not handle the given option.
+ *
+ * The only API available today is to install a hook, to mirror the
+ * rest of the LSMs APIs. They don't allow unloading of security
+ * modules.
+ */
+static int checmate_task_prctl(int option, unsigned long arg2,
+ unsigned long hook, unsigned long ufd,
+ unsigned long arg5)
+{
+ int rc = -ENOSYS;
+
+ switch (option) {
+ case PR_CHECMATE:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (!(hook > 0 && hook < __CHECMATE_HOOK_MAX))
+ return -EINVAL;
+ if (!checmate_bpf_hooks[hook].enabled)
+ return -ENOTSUPP;
+
+ if (arg2 == CHECMATE_INSTALL_HOOK)
+ rc = checmate_prctl_install_hook(hook, ufd);
+ else if (arg2 == CHECMATE_DENY_RESET)
+ rc = checmate_prctl_deny_reset(hook);
+ else if (arg2 == CHECMATE_RESET)
+ rc = checmate_reset(hook);
+ else
+ return -ENOTSUPP;
+ break;
+ }
+ return rc;
+}
+
+static int call_bpf_int_hook(int hook, struct checmate_ctx *ctx)
+{
+ int rc = 0;
+ struct checmate_bpf_hook_instance *hook_instance;
+
+ ctx->hook = hook;
+
+ preempt_disable();
+ rcu_read_lock();
+ list_for_each_entry_rcu(hook_instance,
+ &checmate_bpf_hooks[hook].hook_list, list) {
+ rc = BPF_PROG_RUN(hook_instance->prog, (void *)ctx);
+ if (rc != 0)
+ goto out;
+ }
+out:
+ rcu_read_unlock();
+ preempt_enable();
+ return rc;
+}
+
+static void call_bpf_void_hook(int hook, struct checmate_ctx *ctx)
+{
+ struct checmate_bpf_hook_instance *hook_instance;
+
+ ctx->hook = hook;
+ preempt_disable();
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(hook_instance,
+ &checmate_bpf_hooks[hook].hook_list, list) {
+ BPF_PROG_RUN(hook_instance->prog, (void *)ctx);
+ }
+ rcu_read_unlock();
+ preempt_enable();
+}
+
+static int checmate_file_open(struct file *file, const struct cred *cred)
+{
+ struct checmate_ctx ctx;
+
+ ctx.file_open_ctx.file = file;
+ ctx.file_open_ctx.cred = cred;
+ return call_bpf_int_hook(CHECMATE_HOOK_FILE_OPEN, &ctx);
+}
+
+static int checmate_task_create(unsigned long clone_flags)
+{
+ struct checmate_ctx ctx;
+
+ ctx.task_create_ctx.clone_flags = clone_flags;
+ return call_bpf_int_hook(CHECMATE_HOOK_TASK_CREATE, &ctx);
+}
+
+static void checmate_task_free(struct task_struct *task)
+{
+ struct checmate_ctx ctx;
+
+ ctx.task_free_ctx.task = task;
+ call_bpf_void_hook(CHECMATE_HOOK_TASK_FREE, &ctx);
+}
+
+#ifdef CONFIG_SECURITY_NETWORK
+static int checmate_socket_connect(struct socket *sock,
+ struct sockaddr *address, int addrlen)
+{
+ struct checmate_ctx ctx;
+
+ ctx.socket_connect_ctx.sock = sock;
+ ctx.socket_connect_ctx.address = address;
+ ctx.socket_connect_ctx.addrlen = addrlen;
+ return call_bpf_int_hook(CHECMATE_HOOK_SOCKET_CONNECT, &ctx);
+}
+
+#endif /* CONFIG_SECURITY_NETWORK */
+static struct security_hook_list checmate_hooks[] = {
+ LSM_HOOK_INIT(task_prctl, checmate_task_prctl),
+ LSM_HOOK_INIT(file_open, checmate_file_open),
+ LSM_HOOK_INIT(task_create, checmate_task_create),
+ LSM_HOOK_INIT(task_free, checmate_task_free),
+#ifdef CONFIG_SECURITY_NETWORK
+ LSM_HOOK_INIT(socket_connect, checmate_socket_connect),
+#endif /* CONFIG_SECURITY_NETWORK */
+};
+
+static int __init checmate_setup(void)
+{
+ pr_info("Checmate activating.\n");
+ register_checmate_prog_ops();
+ security_add_hooks(checmate_hooks, ARRAY_SIZE(checmate_hooks));
+ return 0;
+}
+late_initcall(checmate_setup);
This adds the minor LSM Checmate. The purpose of Checmate is to act as an extensible LSM in which you can load security modules. The module has a simple API, as it's meant to have most of the logic in BPF hooks. It has three APIs that are accessible via prctl. As follows: * Install hook: This appends a new BPF program to a given hook. Hook programs themselves must be unique BPF programs. * Reset hook: This detaches all bpf programs asssociated with a hook. * Deny Reset: This locks a hook, preventing reset. In production operation, it's expected that the user would lock their hooks. Signed-off-by: Sargun Dhillon <sargun@sargun.me> --- include/linux/checmate.h | 38 ++++++ include/uapi/linux/Kbuild | 1 + include/uapi/linux/bpf.h | 1 + include/uapi/linux/checmate.h | 65 +++++++++ include/uapi/linux/prctl.h | 3 + security/Kconfig | 1 + security/Makefile | 2 + security/checmate/Kconfig | 6 + security/checmate/Makefile | 3 + security/checmate/checmate.c | 0 security/checmate/checmate_bpf.c | 67 ++++++++++ security/checmate/checmate_lsm.c | 281 +++++++++++++++++++++++++++++++++++++++ 12 files changed, 468 insertions(+) create mode 100644 include/linux/checmate.h create mode 100644 include/uapi/linux/checmate.h create mode 100644 security/checmate/Kconfig create mode 100644 security/checmate/Makefile create mode 100644 security/checmate/checmate.c create mode 100644 security/checmate/checmate_bpf.c create mode 100644 security/checmate/checmate_lsm.c