lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:   Mon, 29 Aug 2016 04:47:07 -0700
From:   Sargun Dhillon <sargun@...gun.me>
To:     netdev@...r.kernel.org
Cc:     cgroups@...r.kernel.org, linux-security-module@...r.kernel.org,
        daniel@...earbox.net, ast@...com
Subject: [net-next RFC v2 4/9] bpf, security: Add Checmate security LSM and
 BPF program type

This patch adds a minor LSM, Checmate. Checmate is a flexible programmable,
extensible minor LSM that's coupled with cgroups and BPF. It is designed to
enforce container-specific policies. It is also a cgroupv2 controller. By
itself, it doesn't do very much, but in conjunction with a orchestrator
complex policies can be installed on the cgroup hierarchy.

These cgroup programs are tied to the kernel ABI version. If one tries
to load a BPF program compiled against a different kernel version,
an error will be thrown.

Signed-off-by: Sargun Dhillon <sargun@...gun.me>
---
 include/linux/cgroup_subsys.h    |   4 +
 include/linux/checmate.h         | 108 +++++++
 include/uapi/linux/bpf.h         |   1 +
 kernel/bpf/syscall.c             |   2 +-
 security/Kconfig                 |   1 +
 security/Makefile                |   2 +
 security/checmate/Kconfig        |  11 +
 security/checmate/Makefile       |   3 +
 security/checmate/checmate_bpf.c |  68 +++++
 security/checmate/checmate_lsm.c | 610 +++++++++++++++++++++++++++++++++++++++
 10 files changed, 809 insertions(+), 1 deletion(-)
 create mode 100644 include/linux/checmate.h
 create mode 100644 security/checmate/Kconfig
 create mode 100644 security/checmate/Makefile
 create mode 100644 security/checmate/checmate_bpf.c
 create mode 100644 security/checmate/checmate_lsm.c

diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h
index 0df0336a..fbb7aa7 100644
--- a/include/linux/cgroup_subsys.h
+++ b/include/linux/cgroup_subsys.h
@@ -56,6 +56,10 @@ SUBSYS(hugetlb)
 SUBSYS(pids)
 #endif
 
+#if IS_ENABLED(CONFIG_SECURITY_CHECMATE)
+SUBSYS(checmate)
+#endif
+
 /*
  * The following subsystems are not supported on the default hierarchy.
  */
diff --git a/include/linux/checmate.h b/include/linux/checmate.h
new file mode 100644
index 0000000..4c4db4a
--- /dev/null
+++ b/include/linux/checmate.h
@@ -0,0 +1,108 @@
+#ifndef _LINUX_CHECMATE_H_
+#define _LINUX_CHECMATE_H_ 1
+#include <linux/security.h>
+
+enum checmate_hook_num {
+	/* 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_SK_FREE_SECURITY,
+	__CHECMATE_HOOK_MAX,
+};
+
+/* CONFIG_SECURITY_NET contexts */
+struct checmate_unix_stream_connect_ctx {
+	struct sock *sock;
+	struct sock *other;
+	struct sock *newsk;
+};
+
+struct checmate_unix_may_send_ctx {
+	struct socket *sock;
+	struct socket *other;
+};
+
+struct checmate_socket_create_ctx {
+	int family;
+	int type;
+	int protocol;
+	int kern;
+};
+
+struct checmate_socket_bind_ctx {
+	struct socket *sock;
+	struct sockaddr *address;
+	int addrlen;
+};
+
+struct checmate_socket_connect_ctx {
+	struct socket *sock;
+	struct sockaddr *address;
+	int addrlen;
+};
+
+struct checmate_socket_listen_ctx {
+	struct socket *sock;
+	int backlog;
+};
+
+struct checmate_socket_accept_ctx {
+	struct socket *sock;
+	struct socket *newsock;
+};
+
+struct checmate_socket_sendmsg_ctx {
+	struct socket *sock;
+	struct msghdr *msg;
+	int size;
+};
+
+struct checmate_socket_recvmsg_ctx {
+	struct socket *sock;
+	struct msghdr *msg;
+	int size;
+	int flags;
+};
+
+struct checmate_socket_sock_rcv_skb_ctx {
+	struct sock *sk;
+	struct sk_buff *skb;
+};
+
+struct checmate_sk_free_security_ctx {
+	struct sock *sk;
+};
+
+struct checmate_ctx {
+	int hook;
+	union {
+/* CONFIG_SECURITY_NET contexts */
+		struct checmate_unix_stream_connect_ctx	unix_stream_connect;
+		struct checmate_unix_may_send_ctx	unix_may_send;
+		struct checmate_socket_create_ctx	socket_create;
+		struct checmate_socket_bind_ctx		socket_bind;
+		struct checmate_socket_connect_ctx	socket_connect;
+		struct checmate_socket_listen_ctx	socket_listen;
+		struct checmate_socket_accept_ctx	socket_accept;
+		struct checmate_socket_sendmsg_ctx	socket_sendmsg;
+		struct checmate_socket_recvmsg_ctx	socket_recvmsg;
+		struct checmate_socket_sock_rcv_skb_ctx	socket_sock_rcv_skb;
+		struct checmate_sk_free_security_ctx	sk_free_security;
+	};
+};
+
+#endif /* _LINUX_CHECMATE_H_ */
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index e4c5a1b..91bc92f 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.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
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 228f962..6f4f7b0 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -741,7 +741,7 @@ static int bpf_prog_load(union bpf_attr *attr)
 	if (attr->insn_cnt >= BPF_MAXINSNS)
 		return -EINVAL;
 
-	if (type == BPF_PROG_TYPE_KPROBE &&
+	if ((type == BPF_PROG_TYPE_KPROBE || type == BPF_PROG_TYPE_CHECMATE) &&
 	    attr->kern_version != LINUX_VERSION_CODE)
 		return -EINVAL;
 
diff --git a/security/Kconfig b/security/Kconfig
index df28f2b..c819539 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -152,6 +152,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
 
diff --git a/security/Makefile b/security/Makefile
index f2d71cd..6cc3342 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -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
diff --git a/security/checmate/Kconfig b/security/checmate/Kconfig
new file mode 100644
index 0000000..9dc76d1
--- /dev/null
+++ b/security/checmate/Kconfig
@@ -0,0 +1,11 @@
+config SECURITY_CHECMATE
+	bool "Checmate support"
+	depends on SECURITY && SOCK_CGROUP_DATA
+	default n
+	help
+	  This selects Checmate, which is an LSM that works in conjuncion with
+	  cgroups and BPF in order to provide programmable, flexible, and
+	  extensible security policies. Further information can be found in
+	  Documentation/security/Checmate.txt
+
+	  If you are unsure how to answer this question, answer N.
diff --git a/security/checmate/Makefile b/security/checmate/Makefile
new file mode 100644
index 0000000..c676773
--- /dev/null
+++ b/security/checmate/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_SECURITY_CHECMATE) := checmate.o
+
+checmate-y := checmate_bpf.o checmate_lsm.o
diff --git a/security/checmate/checmate_bpf.c b/security/checmate/checmate_bpf.c
new file mode 100644
index 0000000..001225c
--- /dev/null
+++ b/security/checmate/checmate_bpf.c
@@ -0,0 +1,68 @@
+/*
+ * Checmate Linux Security Module
+ *
+ * Copyright (C) 2016 Sargun Dhillon <sargun@...gun.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);
+}
diff --git a/security/checmate/checmate_lsm.c b/security/checmate/checmate_lsm.c
new file mode 100644
index 0000000..ef8514d
--- /dev/null
+++ b/security/checmate/checmate_lsm.c
@@ -0,0 +1,610 @@
+/*
+ * Checmate Linux Security Module
+ *
+ * Copyright (C) 2016 Sargun Dhillon <sargun@...gun.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>
+#include <linux/cgroup.h>
+#include <net/sock.h>
+#include <net/request_sock.h>
+
+#define MAX_CHECMATE_INSTANCES 32
+
+/* Global mutex for any Checmate hook manipulation operations */
+DEFINE_MUTEX(checmate_mutex);
+
+#define CHECMATE_CFTYPE(HOOK, NAME)			\
+	{						\
+		.name		= NAME,			\
+		.private	= HOOK,			\
+		.read_u64	= checmate_read_u64,	\
+		.write_s64	= checmate_write_s64,	\
+		.flags		= CFTYPE_NOT_ON_ROOT	\
+	}
+
+extern void register_checmate_prog_ops(void);
+
+struct checmate_instance {
+	struct list_head	list;
+	struct rcu_head		rcu;
+	struct bpf_prog		*prog;
+};
+
+struct checmate_hook {
+	struct list_head	instances;
+	int			count;
+};
+
+struct checmate_css {
+	struct cgroup_subsys_state	css;
+	struct checmate_hook		hooks[__CHECMATE_HOOK_MAX];
+};
+
+static struct checmate_css *css_checmate(struct cgroup_subsys_state *css)
+{
+	return container_of(css, struct checmate_css, css);
+}
+
+static struct checmate_css *parent_checmate(struct checmate_css *checmate)
+{
+	return css_checmate(checmate->css.parent);
+}
+
+static struct cgroup_subsys_state *
+checmate_css_alloc(struct cgroup_subsys_state *parent)
+{
+	struct checmate_css *checmate;
+	int i;
+
+	checmate = kzalloc(sizeof(*checmate), GFP_KERNEL);
+	if (!checmate)
+		return ERR_PTR(-ENOMEM);
+
+	for (i = 0; i < ARRAY_SIZE(checmate->hooks); i++)
+		INIT_LIST_HEAD(&checmate->hooks[i].instances);
+
+	return &checmate->css;
+}
+
+/*
+ * checmate_hook_free - Deallocate, and release resources for a given hook
+ * @hook: The hook
+ *
+ * Always succeeds. Only to be used when hook is out of use, and therefore
+ * doesn't use the RCU mechanism to cleanup he hook. Only use it for
+ * retirement of a hook.
+ */
+static void checmate_hook_free(struct checmate_hook *hook)
+{
+	struct checmate_instance *instance, *next;
+
+	list_for_each_entry_safe(instance, next, &hook->instances, list) {
+		list_del(&instance->list);
+		bpf_prog_put(instance->prog);
+		kfree(instance);
+	}
+}
+
+/*
+ * checmate_css_free - Callback for css_free
+ * @css: The cgroup_subsys_state to be freed
+ */
+static void checmate_css_free(struct cgroup_subsys_state *css)
+{
+	struct checmate_css *checmate = css_checmate(css);
+	int i;
+
+	mutex_lock(&checmate_mutex);
+	for (i = 0; i < ARRAY_SIZE(checmate->hooks); i++)
+		checmate_hook_free(&checmate->hooks[i]);
+
+	kfree(checmate);
+	mutex_unlock(&checmate_mutex);
+}
+
+/*
+ * checmate_instance_add - Add BPF program instance to a Checmate hook
+ * @hook: The hook
+ * @prog: A checmate BPF program
+ *
+ * Checks if the program is already part of the hook, and only adds new
+ * programs.
+ *
+ * Returns 0 on success. -errno on failure.
+ *
+ * Requires that the Checmate mutex is held during the operation.
+ */
+static int checmate_instance_add(struct checmate_hook *hook,
+				 struct bpf_prog *prog)
+{
+	struct checmate_instance *instance;
+	int rc = 0;
+
+	if (hook->count >= MAX_CHECMATE_INSTANCES)
+		return -ENOSPC;
+
+	list_for_each_entry(instance, &hook->instances, list) {
+		if (instance->prog == prog) {
+			bpf_prog_put(prog);
+			rc = -EEXIST;
+			goto err;
+		}
+	}
+
+	instance = kmalloc(sizeof(*instance), GFP_KERNEL);
+	if (!instance) {
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	instance->prog = prog;
+	list_add_tail_rcu(&instance->list, &hook->instances);
+	hook->count++;
+	return rc;
+
+err:
+	bpf_prog_put(prog);
+	return rc;
+}
+
+/*
+ * checmate_instance_cleanup_rcu - Cleans up a Checmate program instance
+ * @rp: rcu_head pointer to a Checmate instance
+ */
+static void checmate_instance_cleanup_rcu(struct rcu_head *rp)
+{
+	struct checmate_instance *instance;
+
+	instance = container_of(rp, struct checmate_instance, rcu);
+	bpf_prog_put(instance->prog);
+	kfree(instance);
+}
+
+/*
+ * checmate_instance_remove - Remove Checmate program instance from a hook
+ * @hook: The hook
+ * @prog: A Checmate BPF program referred to by the instance.
+ *
+ * Returns 0 on success. -errno on failure.
+ *
+ * Requires that the Checmate mutex is held during the operation.
+ */
+static int checmate_instance_remove(struct checmate_hook *hook,
+				    struct bpf_prog *prog)
+{
+	struct checmate_instance *instance, *next;
+	int rc = -ENOENT;
+
+	list_for_each_entry_safe(instance, next, &hook->instances, list) {
+		if (instance->prog == prog) {
+			list_del_rcu(&instance->list);
+			call_rcu(&instance->rcu, checmate_instance_cleanup_rcu);
+			rc = 0;
+			hook->count--;
+			break;
+		}
+	}
+	bpf_prog_put(prog);
+
+	return rc;
+}
+
+/*
+ * checmate_hook_reset - Remove all program instances from a Checmate hook
+ * @hook: The hook
+ *
+ * Always succeeds.
+ *
+ * Requires that the Checmate mutex is held during the operation.
+ */
+static void checmate_hook_reset(struct checmate_hook *hook)
+{
+	struct checmate_instance *instance, *next;
+
+	list_for_each_entry_safe(instance, next, &hook->instances, list) {
+		list_del_rcu(&instance->list);
+		call_rcu(&instance->rcu, checmate_instance_cleanup_rcu);
+	}
+	hook->count = 0;
+}
+
+/*
+ * checmate_write_s64 - Handle a write to the checmate cgroup control file
+ * @css: The given cgroup state that own's the hook
+ * @cft: The given cftype that is being referenced, used to get the hook id.
+ * @val: The bpf program fd that is involved in the operation, or 0.
+ *
+ * val == 0: Reset all programs in hook.
+ * val > 0: Add the given program.
+ * val < 0: Remove the given program.
+ *
+ * Returns 0 on success. -errno on failure.
+ */
+static int checmate_write_s64(struct cgroup_subsys_state *css,
+			      struct cftype *cft, s64 val)
+{
+	struct checmate_css *checmate = css_checmate(css);
+	struct checmate_hook *hook;
+	struct bpf_prog *prog;
+	int rc = 0;
+
+	hook = &checmate->hooks[cft->private];
+	mutex_lock(&checmate_mutex);
+	if (val == 0) {
+		checmate_hook_reset(hook);
+		goto out;
+	}
+
+	/* If we're not resetting, we have to load, and check the program */
+	prog = bpf_prog_get_type(abs(val), BPF_PROG_TYPE_CHECMATE);
+	if (IS_ERR(prog))
+		return PTR_ERR(prog);
+
+	if (val > 0)
+		rc = checmate_instance_add(hook, prog);
+	else
+		rc = checmate_instance_remove(hook, prog);
+
+out:
+	mutex_unlock(&checmate_mutex);
+	return rc;
+}
+
+/*
+ * checmate_read_u64 - Read the number of programs loaded into a given hook
+ * @css: The given cgroup state that own's the hook
+ * @cft: The given cftype that is being referenced, used to get the hook id.
+ *
+ *
+ * Returns number of programs loaded into hook. Always succeeds.
+ */
+static u64 checmate_read_u64(struct cgroup_subsys_state *css,
+			     struct cftype *cft)
+{
+	struct checmate_css *checmate = css_checmate(css);
+	struct checmate_hook *hook;
+
+	hook = &checmate->hooks[cft->private];
+	return hook->count;
+}
+
+static struct cftype checmate_files[] = {
+#ifdef CONFIG_SECURITY_NETWORK
+	CHECMATE_CFTYPE(CHECMATE_HOOK_UNIX_STREAM_CONNECT,
+			"unix_stream_connect"),
+	CHECMATE_CFTYPE(CHECMATE_HOOK_UNIX_MAY_SEND,
+			"unix_may_send"),
+	CHECMATE_CFTYPE(CHECMATE_HOOK_SOCKET_CREATE, "socket_create"),
+	CHECMATE_CFTYPE(CHECMATE_HOOK_SOCKET_BIND, "socket_bind"),
+	CHECMATE_CFTYPE(CHECMATE_HOOK_SOCKET_CONNECT, "socket_connect"),
+	CHECMATE_CFTYPE(CHECMATE_HOOK_SOCKET_LISTEN, "socket_listen"),
+	CHECMATE_CFTYPE(CHECMATE_HOOK_SOCKET_ACCEPT, "socket_accept"),
+	CHECMATE_CFTYPE(CHECMATE_HOOK_SOCKET_SENDMSG, "socket_sendmsg"),
+	CHECMATE_CFTYPE(CHECMATE_HOOK_SOCKET_RECVMSG, "socket_recvmsg"),
+	CHECMATE_CFTYPE(CHECMATE_HOOK_SOCKET_SHUTDOWN, "socket_shutdown"),
+	CHECMATE_CFTYPE(CHECMATE_HOOK_SOCKET_SOCK_RCV_SKB,
+			"socket_sock_rcv_skb"),
+	CHECMATE_CFTYPE(CHECMATE_HOOK_SK_FREE_SECURITY, "sk_free_security"),
+#endif /* CONFIG_SECURITY_NETWORK */
+	{}
+};
+
+struct cgroup_subsys checmate_cgrp_subsys = {
+	.css_alloc	= checmate_css_alloc,
+	.css_free	= checmate_css_free,
+	.dfl_cftypes	= checmate_files,
+};
+
+/*
+ * check_checmate_filters - Run all the BPF programs associated with a hook
+ * @css: A pointer to the Checmate css
+ * @ctx: A pointer to the Checmate ctx
+ *
+ * Return 0 on success, on first hook returning non-0, the error is returned
+ * to the caller.
+ */
+static int checmate_check_filters(struct checmate_css *checmate,
+				  struct checmate_ctx *ctx)
+{
+	struct checmate_instance *instance;
+	struct checmate_hook *hook;
+	int rc = 0;
+
+	hook = &checmate->hooks[ctx->hook];
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(instance, &hook->instances, list) {
+		rc = BPF_PROG_RUN(instance->prog, (void *)ctx);
+		if (rc)
+			break;
+	}
+	rcu_read_unlock();
+
+	return rc;
+}
+
+/*
+ * call_bpf_int_hook - Walk the cgroup hierarchy, running filters up the chain
+ * @hook: The Hook ID
+ * @css: A pointer to the Checmate css
+ * @cgrp: A pointer to the cgroup we're in, may be null or err
+ *
+ * Return 0 on success, on first hook erroring, the error is returned
+ * to the caller.
+ *
+ * Requires that the context struct is populated before passing, but
+ * the actual ctx->hook number is set by the function.
+ */
+static int call_bpf_int_hook(int hook, struct cgroup_subsys_state *css,
+			     struct checmate_ctx *ctx)
+{
+	struct checmate_css *checmate;
+	int rc = 0;
+
+	/* Fail open if we can't find the css / cgroup */
+	if (unlikely(IS_ERR_OR_NULL(css)))
+		goto out;
+
+	ctx->hook = hook;
+
+	for (checmate = css_checmate(css); parent_checmate(checmate);
+	     checmate = parent_checmate(checmate)) {
+		rc = checmate_check_filters(checmate, ctx);
+		if (rc)
+			break;
+	}
+
+out:
+	return rc;
+}
+
+/*
+ * call_bpf_void_hook - Run all the BPF programs associated with a hook
+ * Wrapper around call_bpf_int_hook.
+ */
+static void call_bpf_void_hook(int hook, struct cgroup_subsys_state *css,
+			       struct checmate_ctx *ctx)
+{
+	call_bpf_int_hook(hook, css, ctx);
+}
+
+/*
+ * css_from_sk - Get the Checmate CSS for an sk
+ * @sk: The struct sock we're trying to get the CSS for.
+ *
+ * Return Checmate CSS on success, or NULL / ERR_PTR on failure. It will try
+ * to return the effective CSS.
+ */
+static struct cgroup_subsys_state *css_from_sk(struct sock *sk)
+{
+	struct cgroup_subsys_state *css;
+	struct cgroup *cgrp;
+
+	if (!sk_fullsock(sk))
+		return ERR_PTR(-EINVAL);
+	cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data);
+
+	rcu_read_lock();
+	do {
+		css = rcu_dereference(cgrp->subsys[checmate_cgrp_id]);
+		if (css)
+			goto out;
+		cgrp = cgroup_parent(cgrp);
+	} while (cgrp);
+
+out:
+	rcu_read_unlock();
+
+	return css;
+}
+
+/*
+ * css_from_sock - Get the Checmate CSS for a socket
+ * @sock: The struct socket we're trying to get the CSS for.
+ *
+ * Return CSS on success. NULL / ERR_PTR on failure. It's a wrapper  around
+ * css_from_sk.
+ */
+static struct cgroup_subsys_state *css_from_sock(struct socket *sock)
+{
+	struct sock *sk;
+
+	sk = sock->sk;
+	if (!sk)
+		return ERR_PTR(-ENOENT);
+
+	return css_from_sk(sk);
+}
+
+/*
+ * css_from_sock - Get the checmate CSS for the current task context.
+ *
+ * Return CSS success on success. ERR_PTR on failure. It checks to see if it's
+ * being called from an interrupt as well.
+ */
+static struct cgroup_subsys_state *css_from_current(void)
+{
+	struct cgroup_subsys_state *css;
+
+	if (unlikely(in_interrupt()))
+		return ERR_PTR(-ENOENT);
+
+	rcu_read_lock();
+	css = task_css(current, checmate_cgrp_id);
+	rcu_read_unlock();
+
+	return css;
+}
+
+/* Checmate hooks */
+#ifdef CONFIG_SECURITY_NETWORK
+static int checmate_unix_stream_connect(struct sock *sock, struct sock *other,
+					struct sock *newsk)
+{
+	struct cgroup_subsys_state *css;
+	struct checmate_ctx ctx;
+
+	css = css_from_sk(sock);
+	ctx.unix_stream_connect.sock = sock;
+	ctx.unix_stream_connect.other = other;
+	ctx.unix_stream_connect.newsk = newsk;
+	return call_bpf_int_hook(CHECMATE_HOOK_UNIX_STREAM_CONNECT, css, &ctx);
+}
+
+static int checmate_unix_may_send(struct socket *sock, struct socket *other)
+{
+	struct cgroup_subsys_state *css;
+	struct checmate_ctx ctx;
+
+	css = css_from_sock(sock);
+	ctx.unix_may_send.sock = sock;
+	ctx.unix_may_send.other = other;
+	return call_bpf_int_hook(CHECMATE_HOOK_UNIX_MAY_SEND, css, &ctx);
+}
+
+static int checmate_socket_create(int family, int type, int protocol, int kern)
+{
+	struct cgroup_subsys_state *css;
+	struct checmate_ctx ctx;
+
+	css = css_from_current();
+	ctx.socket_create.family = family;
+	ctx.socket_create.type = type;
+	ctx.socket_create.protocol = protocol;
+	ctx.socket_create.kern = kern;
+	return call_bpf_int_hook(CHECMATE_HOOK_SOCKET_CREATE, css, &ctx);
+}
+
+static int checmate_socket_bind(struct socket *sock, struct sockaddr *address,
+				int addrlen)
+{
+	struct cgroup_subsys_state *css;
+	struct checmate_ctx ctx;
+
+	css = css_from_sock(sock);
+	ctx.socket_bind.sock = sock;
+	ctx.socket_bind.address = address;
+	ctx.socket_bind.addrlen = addrlen;
+	return call_bpf_int_hook(CHECMATE_HOOK_SOCKET_BIND, css, &ctx);
+}
+
+static int checmate_socket_connect(struct socket *sock,
+				   struct sockaddr *address, int addrlen)
+{
+	struct cgroup_subsys_state *css;
+	struct checmate_ctx ctx;
+
+	css = css_from_sock(sock);
+	ctx.socket_connect.sock = sock;
+	ctx.socket_connect.address = address;
+	ctx.socket_connect.addrlen = addrlen;
+	return call_bpf_int_hook(CHECMATE_HOOK_SOCKET_CONNECT, css, &ctx);
+}
+
+static int checmate_socket_listen(struct socket *sock, int backlog)
+{
+	struct cgroup_subsys_state *css;
+	struct checmate_ctx ctx;
+
+	css = css_from_sock(sock);
+	ctx.socket_listen.sock = sock;
+	ctx.socket_listen.backlog = backlog;
+	return call_bpf_int_hook(CHECMATE_HOOK_SOCKET_LISTEN, css, &ctx);
+}
+
+static int checmate_socket_accept(struct socket *sock, struct socket *newsock)
+{
+	struct cgroup_subsys_state *css;
+	struct checmate_ctx ctx;
+
+	css = css_from_sock(sock);
+	ctx.socket_accept.sock = sock;
+	ctx.socket_accept.newsock = newsock;
+	return call_bpf_int_hook(CHECMATE_HOOK_SOCKET_ACCEPT, css, &ctx);
+}
+
+static int checmate_socket_sendmsg(struct socket *sock, struct msghdr *msg,
+				   int size)
+{
+	struct cgroup_subsys_state *css;
+	struct checmate_ctx ctx;
+
+	css = css_from_sock(sock);
+	ctx.socket_sendmsg.sock = sock;
+	ctx.socket_sendmsg.msg = msg;
+	ctx.socket_sendmsg.size = size;
+	return call_bpf_int_hook(CHECMATE_HOOK_SOCKET_SENDMSG, css, &ctx);
+}
+
+static int checmate_socket_recvmsg(struct socket *sock, struct msghdr *msg,
+				   int size, int flags)
+{
+	struct cgroup_subsys_state *css;
+	struct checmate_ctx ctx;
+
+	css = css_from_sock(sock);
+	ctx.socket_recvmsg.sock = sock;
+	ctx.socket_recvmsg.msg = msg;
+	ctx.socket_recvmsg.size = size;
+	ctx.socket_recvmsg.flags = flags;
+	return call_bpf_int_hook(CHECMATE_HOOK_SOCKET_RECVMSG, css, &ctx);
+}
+
+static int checmate_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+	struct cgroup_subsys_state *css;
+	struct checmate_ctx ctx;
+
+	css = css_from_sk(sk);
+	ctx.socket_sock_rcv_skb.sk = sk;
+	ctx.socket_sock_rcv_skb.skb = skb;
+	return call_bpf_int_hook(CHECMATE_HOOK_SOCKET_SOCK_RCV_SKB, css, &ctx);
+}
+
+static void checmate_sk_free_security(struct sock *sk)
+{
+	struct cgroup_subsys_state *css;
+	struct checmate_ctx ctx;
+
+	css = css_from_sk(sk);
+	ctx.sk_free_security.sk = sk;
+	return call_bpf_void_hook(CHECMATE_HOOK_SK_FREE_SECURITY, css, &ctx);
+}
+
+#endif /* CONFIG_SECURITY_NETWORK */
+
+static struct security_hook_list checmate_hooks[] = {
+#ifdef CONFIG_SECURITY_NETWORK
+	LSM_HOOK_INIT(unix_stream_connect, checmate_unix_stream_connect),
+	LSM_HOOK_INIT(unix_may_send, checmate_unix_may_send),
+	LSM_HOOK_INIT(socket_create, checmate_socket_create),
+	LSM_HOOK_INIT(socket_bind, checmate_socket_bind),
+	LSM_HOOK_INIT(socket_connect, checmate_socket_connect),
+	LSM_HOOK_INIT(socket_listen, checmate_socket_listen),
+	LSM_HOOK_INIT(socket_accept, checmate_socket_accept),
+	LSM_HOOK_INIT(socket_sendmsg, checmate_socket_sendmsg),
+	LSM_HOOK_INIT(socket_recvmsg, checmate_socket_recvmsg),
+	LSM_HOOK_INIT(socket_sock_rcv_skb, checmate_socket_sock_rcv_skb),
+	LSM_HOOK_INIT(sk_free_security, checmate_sk_free_security),
+#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);
-- 
2.7.4

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ