All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next v2 0/5] bpf: security: New file mode and LSM hooks for eBPF object permission control
@ 2017-10-09 22:20 ` Chenbo Feng
  0 siblings, 0 replies; 39+ messages in thread
From: Chenbo Feng @ 2017-10-09 22:20 UTC (permalink / raw)
  To: linux-security-module, netdev, SELinux
  Cc: Jeffrey Vander Stoep, Alexei Starovoitov, lorenzo,
	Daniel Borkmann, Stephen Smalley, Chenbo Feng

From: Chenbo Feng <fengc@google.com>

Much like files and sockets, eBPF objects are accessed, controlled, and
shared via a file descriptor (FD). Unlike files and sockets, the
existing mechanism for eBPF object access control is very limited.
Currently there are two options for granting accessing to eBPF
operations: grant access to all processes, or only CAP_SYS_ADMIN
processes. The CAP_SYS_ADMIN-only mode is not ideal because most users
do not have this capability and granting a user CAP_SYS_ADMIN grants too
many other security-sensitive permissions. It also unnecessarily allows
all CAP_SYS_ADMIN processes access to eBPF functionality. Allowing all
processes to access to eBPF objects is also undesirable since it has
potential to allow unprivileged processes to consume kernel memory, and
opens up attack surface to the kernel.

Adding LSM hooks maintains the status quo for systems which do not use
an LSM, preserving compatibility with userspace, while allowing security
modules to choose how best to handle permissions on eBPF objects. Here
is a possible use case for the lsm hooks with selinux module:

The network-control daemon (netd) creates and loads an eBPF object for
network packet filtering and analysis. It passes the object FD to an
unprivileged network monitor app (netmonitor), which is not allowed to
create, modify or load eBPF objects, but is allowed to read the traffic
stats from the map.

Selinux could use these hooks to grant the following permissions:
allow netd self:bpf_map { create read write};
allow netmonitor netd:fd use;
allow netmonitor netd:bpf_map read;

In this patch series, A file mode is added to bpf map to store the
accessing mode. With this file mode flags, the map can be obtained read
only, write only or read and write. With the help of this file mode,
several security hooks can be added to the eBPF syscall implementations
to do permissions checks. These LSM hooks are mainly focused on checking
the process privileges before it obtains the fd for a specific bpf
object. No matter from a file location or from a eBPF id. Besides that,
a general check hook is also implemented at the start of bpf syscalls so
that each security module can have their own implementation on the reset
of bpf object related functionalities.

In order to store the ownership and security information about eBPF
maps, a security field pointer is added to the struct bpf_map. And the
last two patch set are implementation of selinux check on these hooks
introduced, plus an additional check when eBPF object is passed between
processes using unix socket as well as binder IPC.

Change since V1:

 - Whitelist the new bpf flags in the map allocate check.
 - Added bpf selftest for the new flags.
 - Added two new security hooks for copying the security information from
   the bpf object security struct to file security struct
 - Simplified the checking action when bpf fd is passed between processes.

Chenbo Feng (5):
  bpf: Add file mode configuration into bpf maps
  bpf: Add tests for eBPF file mode
  security: bpf: Add LSM hooks for bpf object related syscall
  selinux: bpf: Add selinux check for eBPF syscall operations
  selinux: bpf: Add addtional check for bpf object file receive

 include/linux/bpf.h                     |  15 ++-
 include/linux/lsm_hooks.h               |  71 +++++++++++++
 include/linux/security.h                |  54 ++++++++++
 include/uapi/linux/bpf.h                |   6 ++
 kernel/bpf/arraymap.c                   |   7 +-
 kernel/bpf/devmap.c                     |   5 +-
 kernel/bpf/hashtab.c                    |   5 +-
 kernel/bpf/inode.c                      |  15 ++-
 kernel/bpf/lpm_trie.c                   |   3 +-
 kernel/bpf/sockmap.c                    |   5 +-
 kernel/bpf/stackmap.c                   |   5 +-
 kernel/bpf/syscall.c                    | 112 ++++++++++++++++++---
 security/security.c                     |  40 ++++++++
 security/selinux/hooks.c                | 172 ++++++++++++++++++++++++++++++++
 security/selinux/include/classmap.h     |   2 +
 security/selinux/include/objsec.h       |   4 +
 tools/testing/selftests/bpf/test_maps.c |  48 +++++++++
 17 files changed, 542 insertions(+), 27 deletions(-)

-- 
2.14.2.920.gcf0c67979c-goog


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH net-next v2 0/5] bpf: security: New file mode and LSM hooks for eBPF object permission control
@ 2017-10-09 22:20 ` Chenbo Feng
  0 siblings, 0 replies; 39+ messages in thread
From: Chenbo Feng @ 2017-10-09 22:20 UTC (permalink / raw)
  To: linux-security-module

From: Chenbo Feng <fengc@google.com>

Much like files and sockets, eBPF objects are accessed, controlled, and
shared via a file descriptor (FD). Unlike files and sockets, the
existing mechanism for eBPF object access control is very limited.
Currently there are two options for granting accessing to eBPF
operations: grant access to all processes, or only CAP_SYS_ADMIN
processes. The CAP_SYS_ADMIN-only mode is not ideal because most users
do not have this capability and granting a user CAP_SYS_ADMIN grants too
many other security-sensitive permissions. It also unnecessarily allows
all CAP_SYS_ADMIN processes access to eBPF functionality. Allowing all
processes to access to eBPF objects is also undesirable since it has
potential to allow unprivileged processes to consume kernel memory, and
opens up attack surface to the kernel.

Adding LSM hooks maintains the status quo for systems which do not use
an LSM, preserving compatibility with userspace, while allowing security
modules to choose how best to handle permissions on eBPF objects. Here
is a possible use case for the lsm hooks with selinux module:

The network-control daemon (netd) creates and loads an eBPF object for
network packet filtering and analysis. It passes the object FD to an
unprivileged network monitor app (netmonitor), which is not allowed to
create, modify or load eBPF objects, but is allowed to read the traffic
stats from the map.

Selinux could use these hooks to grant the following permissions:
allow netd self:bpf_map { create read write};
allow netmonitor netd:fd use;
allow netmonitor netd:bpf_map read;

In this patch series, A file mode is added to bpf map to store the
accessing mode. With this file mode flags, the map can be obtained read
only, write only or read and write. With the help of this file mode,
several security hooks can be added to the eBPF syscall implementations
to do permissions checks. These LSM hooks are mainly focused on checking
the process privileges before it obtains the fd for a specific bpf
object. No matter from a file location or from a eBPF id. Besides that,
a general check hook is also implemented at the start of bpf syscalls so
that each security module can have their own implementation on the reset
of bpf object related functionalities.

In order to store the ownership and security information about eBPF
maps, a security field pointer is added to the struct bpf_map. And the
last two patch set are implementation of selinux check on these hooks
introduced, plus an additional check when eBPF object is passed between
processes using unix socket as well as binder IPC.

Change since V1:

 - Whitelist the new bpf flags in the map allocate check.
 - Added bpf selftest for the new flags.
 - Added two new security hooks for copying the security information from
   the bpf object security struct to file security struct
 - Simplified the checking action when bpf fd is passed between processes.

Chenbo Feng (5):
  bpf: Add file mode configuration into bpf maps
  bpf: Add tests for eBPF file mode
  security: bpf: Add LSM hooks for bpf object related syscall
  selinux: bpf: Add selinux check for eBPF syscall operations
  selinux: bpf: Add addtional check for bpf object file receive

 include/linux/bpf.h                     |  15 ++-
 include/linux/lsm_hooks.h               |  71 +++++++++++++
 include/linux/security.h                |  54 ++++++++++
 include/uapi/linux/bpf.h                |   6 ++
 kernel/bpf/arraymap.c                   |   7 +-
 kernel/bpf/devmap.c                     |   5 +-
 kernel/bpf/hashtab.c                    |   5 +-
 kernel/bpf/inode.c                      |  15 ++-
 kernel/bpf/lpm_trie.c                   |   3 +-
 kernel/bpf/sockmap.c                    |   5 +-
 kernel/bpf/stackmap.c                   |   5 +-
 kernel/bpf/syscall.c                    | 112 ++++++++++++++++++---
 security/security.c                     |  40 ++++++++
 security/selinux/hooks.c                | 172 ++++++++++++++++++++++++++++++++
 security/selinux/include/classmap.h     |   2 +
 security/selinux/include/objsec.h       |   4 +
 tools/testing/selftests/bpf/test_maps.c |  48 +++++++++
 17 files changed, 542 insertions(+), 27 deletions(-)

-- 
2.14.2.920.gcf0c67979c-goog

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH net-next v2 1/5] bpf: Add file mode configuration into bpf maps
  2017-10-09 22:20 ` Chenbo Feng
@ 2017-10-09 22:20   ` Chenbo Feng
  -1 siblings, 0 replies; 39+ messages in thread
From: Chenbo Feng @ 2017-10-09 22:20 UTC (permalink / raw)
  To: linux-security-module, netdev, SELinux
  Cc: Jeffrey Vander Stoep, Alexei Starovoitov, lorenzo,
	Daniel Borkmann, Stephen Smalley, Chenbo Feng

From: Chenbo Feng <fengc@google.com>

Introduce the map read/write flags to the eBPF syscalls that returns the
map fd. The flags is used to set up the file mode when construct a new
file descriptor for bpf maps. To not break the backward capability, the
f_flags is set to O_RDWR if the flag passed by syscall is 0. Otherwise
it should be O_RDONLY or O_WRONLY. When the userspace want to modify or
read the map content, it will check the file mode to see if it is
allowed to make the change.

Signed-off-by: Chenbo Feng <fengc@google.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
---
 include/linux/bpf.h      |  6 ++--
 include/uapi/linux/bpf.h |  6 ++++
 kernel/bpf/arraymap.c    |  7 +++--
 kernel/bpf/devmap.c      |  5 ++-
 kernel/bpf/hashtab.c     |  5 +--
 kernel/bpf/inode.c       | 15 ++++++---
 kernel/bpf/lpm_trie.c    |  3 +-
 kernel/bpf/sockmap.c     |  5 ++-
 kernel/bpf/stackmap.c    |  5 ++-
 kernel/bpf/syscall.c     | 80 +++++++++++++++++++++++++++++++++++++++++++-----
 10 files changed, 114 insertions(+), 23 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index bc7da2ddfcaf..0e9ca2555d7f 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -308,11 +308,11 @@ void bpf_map_area_free(void *base);
 
 extern int sysctl_unprivileged_bpf_disabled;
 
-int bpf_map_new_fd(struct bpf_map *map);
+int bpf_map_new_fd(struct bpf_map *map, int flags);
 int bpf_prog_new_fd(struct bpf_prog *prog);
 
 int bpf_obj_pin_user(u32 ufd, const char __user *pathname);
-int bpf_obj_get_user(const char __user *pathname);
+int bpf_obj_get_user(const char __user *pathname, int flags);
 
 int bpf_percpu_hash_copy(struct bpf_map *map, void *key, void *value);
 int bpf_percpu_array_copy(struct bpf_map *map, void *key, void *value);
@@ -331,6 +331,8 @@ int bpf_fd_htab_map_update_elem(struct bpf_map *map, struct file *map_file,
 				void *key, void *value, u64 map_flags);
 int bpf_fd_htab_map_lookup_elem(struct bpf_map *map, void *key, u32 *value);
 
+int bpf_get_file_flag(int flags);
+
 /* memcpy that is used with 8-byte aligned pointers, power-of-8 size and
  * forced to use 'long' read/writes to try to atomically copy long counters.
  * Best-effort only.  No barriers here, since it _will_ race with concurrent
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 6db9e1d679cd..9cb50a228c39 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -217,6 +217,10 @@ enum bpf_attach_type {
 
 #define BPF_OBJ_NAME_LEN 16U
 
+/* Flags for accessing BPF object */
+#define BPF_F_RDONLY		(1U << 3)
+#define BPF_F_WRONLY		(1U << 4)
+
 union bpf_attr {
 	struct { /* anonymous struct used by BPF_MAP_CREATE command */
 		__u32	map_type;	/* one of enum bpf_map_type */
@@ -259,6 +263,7 @@ union bpf_attr {
 	struct { /* anonymous struct used by BPF_OBJ_* commands */
 		__aligned_u64	pathname;
 		__u32		bpf_fd;
+		__u32		file_flags;
 	};
 
 	struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */
@@ -286,6 +291,7 @@ union bpf_attr {
 			__u32		map_id;
 		};
 		__u32		next_id;
+		__u32		open_flags;
 	};
 
 	struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index 68d866628be0..f869e48ef2f6 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -19,6 +19,9 @@
 
 #include "map_in_map.h"
 
+#define ARRAY_CREATE_FLAG_MASK \
+	(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
+
 static void bpf_array_free_percpu(struct bpf_array *array)
 {
 	int i;
@@ -56,8 +59,8 @@ static struct bpf_map *array_map_alloc(union bpf_attr *attr)
 
 	/* check sanity of attributes */
 	if (attr->max_entries == 0 || attr->key_size != 4 ||
-	    attr->value_size == 0 || attr->map_flags & ~BPF_F_NUMA_NODE ||
-	    (percpu && numa_node != NUMA_NO_NODE))
+	    attr->value_size == 0 || attr->map_flags &
+	    ~ARRAY_CREATE_FLAG_MASK || (percpu && numa_node != NUMA_NO_NODE))
 		return ERR_PTR(-EINVAL);
 
 	if (attr->value_size > KMALLOC_MAX_SIZE)
diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c
index e093d9a2c4dd..e5d3de7cff2e 100644
--- a/kernel/bpf/devmap.c
+++ b/kernel/bpf/devmap.c
@@ -50,6 +50,9 @@
 #include <linux/bpf.h>
 #include <linux/filter.h>
 
+#define DEV_CREATE_FLAG_MASK \
+	(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
+
 struct bpf_dtab_netdev {
 	struct net_device *dev;
 	struct bpf_dtab *dtab;
@@ -80,7 +83,7 @@ static struct bpf_map *dev_map_alloc(union bpf_attr *attr)
 
 	/* check sanity of attributes */
 	if (attr->max_entries == 0 || attr->key_size != 4 ||
-	    attr->value_size != 4 || attr->map_flags & ~BPF_F_NUMA_NODE)
+	    attr->value_size != 4 || attr->map_flags & ~DEV_CREATE_FLAG_MASK)
 		return ERR_PTR(-EINVAL);
 
 	dtab = kzalloc(sizeof(*dtab), GFP_USER);
diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c
index 431126f31ea3..919955236e63 100644
--- a/kernel/bpf/hashtab.c
+++ b/kernel/bpf/hashtab.c
@@ -18,8 +18,9 @@
 #include "bpf_lru_list.h"
 #include "map_in_map.h"
 
-#define HTAB_CREATE_FLAG_MASK \
-	(BPF_F_NO_PREALLOC | BPF_F_NO_COMMON_LRU | BPF_F_NUMA_NODE)
+#define HTAB_CREATE_FLAG_MASK						\
+	(BPF_F_NO_PREALLOC | BPF_F_NO_COMMON_LRU | BPF_F_NUMA_NODE |	\
+	 BPF_F_RDONLY | BPF_F_WRONLY)
 
 struct bucket {
 	struct hlist_nulls_head head;
diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c
index e833ed914358..7d8c6dd8dd5d 100644
--- a/kernel/bpf/inode.c
+++ b/kernel/bpf/inode.c
@@ -295,7 +295,7 @@ int bpf_obj_pin_user(u32 ufd, const char __user *pathname)
 }
 
 static void *bpf_obj_do_get(const struct filename *pathname,
-			    enum bpf_type *type)
+			    enum bpf_type *type, int flags)
 {
 	struct inode *inode;
 	struct path path;
@@ -307,7 +307,7 @@ static void *bpf_obj_do_get(const struct filename *pathname,
 		return ERR_PTR(ret);
 
 	inode = d_backing_inode(path.dentry);
-	ret = inode_permission(inode, MAY_WRITE);
+	ret = inode_permission(inode, ACC_MODE(flags));
 	if (ret)
 		goto out;
 
@@ -326,18 +326,23 @@ static void *bpf_obj_do_get(const struct filename *pathname,
 	return ERR_PTR(ret);
 }
 
-int bpf_obj_get_user(const char __user *pathname)
+int bpf_obj_get_user(const char __user *pathname, int flags)
 {
 	enum bpf_type type = BPF_TYPE_UNSPEC;
 	struct filename *pname;
 	int ret = -ENOENT;
+	int f_flags;
 	void *raw;
 
+	f_flags = bpf_get_file_flag(flags);
+	if (f_flags < 0)
+		return f_flags;
+
 	pname = getname(pathname);
 	if (IS_ERR(pname))
 		return PTR_ERR(pname);
 
-	raw = bpf_obj_do_get(pname, &type);
+	raw = bpf_obj_do_get(pname, &type, f_flags);
 	if (IS_ERR(raw)) {
 		ret = PTR_ERR(raw);
 		goto out;
@@ -346,7 +351,7 @@ int bpf_obj_get_user(const char __user *pathname)
 	if (type == BPF_TYPE_PROG)
 		ret = bpf_prog_new_fd(raw);
 	else if (type == BPF_TYPE_MAP)
-		ret = bpf_map_new_fd(raw);
+		ret = bpf_map_new_fd(raw, f_flags);
 	else
 		goto out;
 
diff --git a/kernel/bpf/lpm_trie.c b/kernel/bpf/lpm_trie.c
index 34d8a690ea05..885e45479680 100644
--- a/kernel/bpf/lpm_trie.c
+++ b/kernel/bpf/lpm_trie.c
@@ -495,7 +495,8 @@ static int trie_delete_elem(struct bpf_map *map, void *_key)
 #define LPM_KEY_SIZE_MAX	LPM_KEY_SIZE(LPM_DATA_SIZE_MAX)
 #define LPM_KEY_SIZE_MIN	LPM_KEY_SIZE(LPM_DATA_SIZE_MIN)
 
-#define LPM_CREATE_FLAG_MASK	(BPF_F_NO_PREALLOC | BPF_F_NUMA_NODE)
+#define LPM_CREATE_FLAG_MASK	(BPF_F_NO_PREALLOC | BPF_F_NUMA_NODE |	\
+				 BPF_F_RDONLY | BPF_F_WRONLY)
 
 static struct bpf_map *trie_alloc(union bpf_attr *attr)
 {
diff --git a/kernel/bpf/sockmap.c b/kernel/bpf/sockmap.c
index a298d6666698..86ec846f2d5e 100644
--- a/kernel/bpf/sockmap.c
+++ b/kernel/bpf/sockmap.c
@@ -40,6 +40,9 @@
 #include <linux/list.h>
 #include <net/strparser.h>
 
+#define SOCK_CREATE_FLAG_MASK \
+	(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
+
 struct bpf_stab {
 	struct bpf_map map;
 	struct sock **sock_map;
@@ -489,7 +492,7 @@ static struct bpf_map *sock_map_alloc(union bpf_attr *attr)
 
 	/* check sanity of attributes */
 	if (attr->max_entries == 0 || attr->key_size != 4 ||
-	    attr->value_size != 4 || attr->map_flags & ~BPF_F_NUMA_NODE)
+	    attr->value_size != 4 || attr->map_flags & ~SOCK_CREATE_FLAG_MASK)
 		return ERR_PTR(-EINVAL);
 
 	if (attr->value_size > KMALLOC_MAX_SIZE)
diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c
index 135be433e9a0..a15bc636cc98 100644
--- a/kernel/bpf/stackmap.c
+++ b/kernel/bpf/stackmap.c
@@ -11,6 +11,9 @@
 #include <linux/perf_event.h>
 #include "percpu_freelist.h"
 
+#define STACK_CREATE_FLAG_MASK \
+	(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
+
 struct stack_map_bucket {
 	struct pcpu_freelist_node fnode;
 	u32 hash;
@@ -60,7 +63,7 @@ static struct bpf_map *stack_map_alloc(union bpf_attr *attr)
 	if (!capable(CAP_SYS_ADMIN))
 		return ERR_PTR(-EPERM);
 
-	if (attr->map_flags & ~BPF_F_NUMA_NODE)
+	if (attr->map_flags & ~STACK_CREATE_FLAG_MASK)
 		return ERR_PTR(-EINVAL);
 
 	/* check sanity of attributes */
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index d124e702e040..b02582ead9a4 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -294,17 +294,48 @@ static void bpf_map_show_fdinfo(struct seq_file *m, struct file *filp)
 }
 #endif
 
+static ssize_t bpf_dummy_read(struct file *filp, char __user *buf, size_t siz,
+			      loff_t *ppos)
+{
+	/* We need this handler such that alloc_file() enables
+	 * f_mode with FMODE_CAN_READ.
+	 */
+	return -EINVAL;
+}
+
+static ssize_t bpf_dummy_write(struct file *filp, const char __user *buf,
+			       size_t siz, loff_t *ppos)
+{
+	/* We need this handler such that alloc_file() enables
+	 * f_mode with FMODE_CAN_WRITE.
+	 */
+	return -EINVAL;
+}
+
 static const struct file_operations bpf_map_fops = {
 #ifdef CONFIG_PROC_FS
 	.show_fdinfo	= bpf_map_show_fdinfo,
 #endif
 	.release	= bpf_map_release,
+	.read		= bpf_dummy_read,
+	.write		= bpf_dummy_write,
 };
 
-int bpf_map_new_fd(struct bpf_map *map)
+int bpf_map_new_fd(struct bpf_map *map, int flags)
 {
 	return anon_inode_getfd("bpf-map", &bpf_map_fops, map,
-				O_RDWR | O_CLOEXEC);
+				flags | O_CLOEXEC);
+}
+
+int bpf_get_file_flag(int flags)
+{
+	if ((flags & BPF_F_RDONLY) && (flags & BPF_F_WRONLY))
+		return -EINVAL;
+	if (flags & BPF_F_RDONLY)
+		return O_RDONLY;
+	if (flags & BPF_F_WRONLY)
+		return O_WRONLY;
+	return O_RDWR;
 }
 
 /* helper macro to check that unused fields 'union bpf_attr' are zero */
@@ -344,12 +375,17 @@ static int map_create(union bpf_attr *attr)
 {
 	int numa_node = bpf_map_attr_numa_node(attr);
 	struct bpf_map *map;
+	int f_flags;
 	int err;
 
 	err = CHECK_ATTR(BPF_MAP_CREATE);
 	if (err)
 		return -EINVAL;
 
+	f_flags = bpf_get_file_flag(attr->map_flags);
+	if (f_flags < 0)
+		return f_flags;
+
 	if (numa_node != NUMA_NO_NODE &&
 	    ((unsigned int)numa_node >= nr_node_ids ||
 	     !node_online(numa_node)))
@@ -375,7 +411,7 @@ static int map_create(union bpf_attr *attr)
 	if (err)
 		goto free_map;
 
-	err = bpf_map_new_fd(map);
+	err = bpf_map_new_fd(map, f_flags);
 	if (err < 0) {
 		/* failed to allocate fd.
 		 * bpf_map_put() is needed because the above
@@ -490,6 +526,11 @@ static int map_lookup_elem(union bpf_attr *attr)
 	if (IS_ERR(map))
 		return PTR_ERR(map);
 
+	if (!(f.file->f_mode & FMODE_CAN_READ)) {
+		err = -EPERM;
+		goto err_put;
+	}
+
 	key = memdup_user(ukey, map->key_size);
 	if (IS_ERR(key)) {
 		err = PTR_ERR(key);
@@ -570,6 +611,11 @@ static int map_update_elem(union bpf_attr *attr)
 	if (IS_ERR(map))
 		return PTR_ERR(map);
 
+	if (!(f.file->f_mode & FMODE_CAN_WRITE)) {
+		err = -EPERM;
+		goto err_put;
+	}
+
 	key = memdup_user(ukey, map->key_size);
 	if (IS_ERR(key)) {
 		err = PTR_ERR(key);
@@ -653,6 +699,11 @@ static int map_delete_elem(union bpf_attr *attr)
 	if (IS_ERR(map))
 		return PTR_ERR(map);
 
+	if (!(f.file->f_mode & FMODE_CAN_WRITE)) {
+		err = -EPERM;
+		goto err_put;
+	}
+
 	key = memdup_user(ukey, map->key_size);
 	if (IS_ERR(key)) {
 		err = PTR_ERR(key);
@@ -696,6 +747,11 @@ static int map_get_next_key(union bpf_attr *attr)
 	if (IS_ERR(map))
 		return PTR_ERR(map);
 
+	if (!(f.file->f_mode & FMODE_CAN_READ)) {
+		err = -EPERM;
+		goto err_put;
+	}
+
 	if (ukey) {
 		key = memdup_user(ukey, map->key_size);
 		if (IS_ERR(key)) {
@@ -902,6 +958,8 @@ static const struct file_operations bpf_prog_fops = {
 	.show_fdinfo	= bpf_prog_show_fdinfo,
 #endif
 	.release	= bpf_prog_release,
+	.read		= bpf_dummy_read,
+	.write		= bpf_dummy_write,
 };
 
 int bpf_prog_new_fd(struct bpf_prog *prog)
@@ -1111,11 +1169,11 @@ static int bpf_prog_load(union bpf_attr *attr)
 	return err;
 }
 
-#define BPF_OBJ_LAST_FIELD bpf_fd
+#define BPF_OBJ_LAST_FIELD file_flags
 
 static int bpf_obj_pin(const union bpf_attr *attr)
 {
-	if (CHECK_ATTR(BPF_OBJ))
+	if (CHECK_ATTR(BPF_OBJ) || attr->file_flags != 0)
 		return -EINVAL;
 
 	return bpf_obj_pin_user(attr->bpf_fd, u64_to_user_ptr(attr->pathname));
@@ -1126,7 +1184,8 @@ static int bpf_obj_get(const union bpf_attr *attr)
 	if (CHECK_ATTR(BPF_OBJ) || attr->bpf_fd != 0)
 		return -EINVAL;
 
-	return bpf_obj_get_user(u64_to_user_ptr(attr->pathname));
+	return bpf_obj_get_user(u64_to_user_ptr(attr->pathname),
+				attr->file_flags);
 }
 
 #ifdef CONFIG_CGROUP_BPF
@@ -1386,12 +1445,13 @@ static int bpf_prog_get_fd_by_id(const union bpf_attr *attr)
 	return fd;
 }
 
-#define BPF_MAP_GET_FD_BY_ID_LAST_FIELD map_id
+#define BPF_MAP_GET_FD_BY_ID_LAST_FIELD open_flags
 
 static int bpf_map_get_fd_by_id(const union bpf_attr *attr)
 {
 	struct bpf_map *map;
 	u32 id = attr->map_id;
+	int f_flags;
 	int fd;
 
 	if (CHECK_ATTR(BPF_MAP_GET_FD_BY_ID))
@@ -1400,6 +1460,10 @@ static int bpf_map_get_fd_by_id(const union bpf_attr *attr)
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 
+	f_flags = bpf_get_file_flag(attr->open_flags);
+	if (f_flags < 0)
+		return f_flags;
+
 	spin_lock_bh(&map_idr_lock);
 	map = idr_find(&map_idr, id);
 	if (map)
@@ -1411,7 +1475,7 @@ static int bpf_map_get_fd_by_id(const union bpf_attr *attr)
 	if (IS_ERR(map))
 		return PTR_ERR(map);
 
-	fd = bpf_map_new_fd(map);
+	fd = bpf_map_new_fd(map, f_flags);
 	if (fd < 0)
 		bpf_map_put(map);
 
-- 
2.14.2.920.gcf0c67979c-goog

^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH net-next v2 1/5] bpf: Add file mode configuration into bpf maps
@ 2017-10-09 22:20   ` Chenbo Feng
  0 siblings, 0 replies; 39+ messages in thread
From: Chenbo Feng @ 2017-10-09 22:20 UTC (permalink / raw)
  To: linux-security-module

From: Chenbo Feng <fengc@google.com>

Introduce the map read/write flags to the eBPF syscalls that returns the
map fd. The flags is used to set up the file mode when construct a new
file descriptor for bpf maps. To not break the backward capability, the
f_flags is set to O_RDWR if the flag passed by syscall is 0. Otherwise
it should be O_RDONLY or O_WRONLY. When the userspace want to modify or
read the map content, it will check the file mode to see if it is
allowed to make the change.

Signed-off-by: Chenbo Feng <fengc@google.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
---
 include/linux/bpf.h      |  6 ++--
 include/uapi/linux/bpf.h |  6 ++++
 kernel/bpf/arraymap.c    |  7 +++--
 kernel/bpf/devmap.c      |  5 ++-
 kernel/bpf/hashtab.c     |  5 +--
 kernel/bpf/inode.c       | 15 ++++++---
 kernel/bpf/lpm_trie.c    |  3 +-
 kernel/bpf/sockmap.c     |  5 ++-
 kernel/bpf/stackmap.c    |  5 ++-
 kernel/bpf/syscall.c     | 80 +++++++++++++++++++++++++++++++++++++++++++-----
 10 files changed, 114 insertions(+), 23 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index bc7da2ddfcaf..0e9ca2555d7f 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -308,11 +308,11 @@ void bpf_map_area_free(void *base);
 
 extern int sysctl_unprivileged_bpf_disabled;
 
-int bpf_map_new_fd(struct bpf_map *map);
+int bpf_map_new_fd(struct bpf_map *map, int flags);
 int bpf_prog_new_fd(struct bpf_prog *prog);
 
 int bpf_obj_pin_user(u32 ufd, const char __user *pathname);
-int bpf_obj_get_user(const char __user *pathname);
+int bpf_obj_get_user(const char __user *pathname, int flags);
 
 int bpf_percpu_hash_copy(struct bpf_map *map, void *key, void *value);
 int bpf_percpu_array_copy(struct bpf_map *map, void *key, void *value);
@@ -331,6 +331,8 @@ int bpf_fd_htab_map_update_elem(struct bpf_map *map, struct file *map_file,
 				void *key, void *value, u64 map_flags);
 int bpf_fd_htab_map_lookup_elem(struct bpf_map *map, void *key, u32 *value);
 
+int bpf_get_file_flag(int flags);
+
 /* memcpy that is used with 8-byte aligned pointers, power-of-8 size and
  * forced to use 'long' read/writes to try to atomically copy long counters.
  * Best-effort only.  No barriers here, since it _will_ race with concurrent
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 6db9e1d679cd..9cb50a228c39 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -217,6 +217,10 @@ enum bpf_attach_type {
 
 #define BPF_OBJ_NAME_LEN 16U
 
+/* Flags for accessing BPF object */
+#define BPF_F_RDONLY		(1U << 3)
+#define BPF_F_WRONLY		(1U << 4)
+
 union bpf_attr {
 	struct { /* anonymous struct used by BPF_MAP_CREATE command */
 		__u32	map_type;	/* one of enum bpf_map_type */
@@ -259,6 +263,7 @@ union bpf_attr {
 	struct { /* anonymous struct used by BPF_OBJ_* commands */
 		__aligned_u64	pathname;
 		__u32		bpf_fd;
+		__u32		file_flags;
 	};
 
 	struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */
@@ -286,6 +291,7 @@ union bpf_attr {
 			__u32		map_id;
 		};
 		__u32		next_id;
+		__u32		open_flags;
 	};
 
 	struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index 68d866628be0..f869e48ef2f6 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -19,6 +19,9 @@
 
 #include "map_in_map.h"
 
+#define ARRAY_CREATE_FLAG_MASK \
+	(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
+
 static void bpf_array_free_percpu(struct bpf_array *array)
 {
 	int i;
@@ -56,8 +59,8 @@ static struct bpf_map *array_map_alloc(union bpf_attr *attr)
 
 	/* check sanity of attributes */
 	if (attr->max_entries == 0 || attr->key_size != 4 ||
-	    attr->value_size == 0 || attr->map_flags & ~BPF_F_NUMA_NODE ||
-	    (percpu && numa_node != NUMA_NO_NODE))
+	    attr->value_size == 0 || attr->map_flags &
+	    ~ARRAY_CREATE_FLAG_MASK || (percpu && numa_node != NUMA_NO_NODE))
 		return ERR_PTR(-EINVAL);
 
 	if (attr->value_size > KMALLOC_MAX_SIZE)
diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c
index e093d9a2c4dd..e5d3de7cff2e 100644
--- a/kernel/bpf/devmap.c
+++ b/kernel/bpf/devmap.c
@@ -50,6 +50,9 @@
 #include <linux/bpf.h>
 #include <linux/filter.h>
 
+#define DEV_CREATE_FLAG_MASK \
+	(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
+
 struct bpf_dtab_netdev {
 	struct net_device *dev;
 	struct bpf_dtab *dtab;
@@ -80,7 +83,7 @@ static struct bpf_map *dev_map_alloc(union bpf_attr *attr)
 
 	/* check sanity of attributes */
 	if (attr->max_entries == 0 || attr->key_size != 4 ||
-	    attr->value_size != 4 || attr->map_flags & ~BPF_F_NUMA_NODE)
+	    attr->value_size != 4 || attr->map_flags & ~DEV_CREATE_FLAG_MASK)
 		return ERR_PTR(-EINVAL);
 
 	dtab = kzalloc(sizeof(*dtab), GFP_USER);
diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c
index 431126f31ea3..919955236e63 100644
--- a/kernel/bpf/hashtab.c
+++ b/kernel/bpf/hashtab.c
@@ -18,8 +18,9 @@
 #include "bpf_lru_list.h"
 #include "map_in_map.h"
 
-#define HTAB_CREATE_FLAG_MASK \
-	(BPF_F_NO_PREALLOC | BPF_F_NO_COMMON_LRU | BPF_F_NUMA_NODE)
+#define HTAB_CREATE_FLAG_MASK						\
+	(BPF_F_NO_PREALLOC | BPF_F_NO_COMMON_LRU | BPF_F_NUMA_NODE |	\
+	 BPF_F_RDONLY | BPF_F_WRONLY)
 
 struct bucket {
 	struct hlist_nulls_head head;
diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c
index e833ed914358..7d8c6dd8dd5d 100644
--- a/kernel/bpf/inode.c
+++ b/kernel/bpf/inode.c
@@ -295,7 +295,7 @@ int bpf_obj_pin_user(u32 ufd, const char __user *pathname)
 }
 
 static void *bpf_obj_do_get(const struct filename *pathname,
-			    enum bpf_type *type)
+			    enum bpf_type *type, int flags)
 {
 	struct inode *inode;
 	struct path path;
@@ -307,7 +307,7 @@ static void *bpf_obj_do_get(const struct filename *pathname,
 		return ERR_PTR(ret);
 
 	inode = d_backing_inode(path.dentry);
-	ret = inode_permission(inode, MAY_WRITE);
+	ret = inode_permission(inode, ACC_MODE(flags));
 	if (ret)
 		goto out;
 
@@ -326,18 +326,23 @@ static void *bpf_obj_do_get(const struct filename *pathname,
 	return ERR_PTR(ret);
 }
 
-int bpf_obj_get_user(const char __user *pathname)
+int bpf_obj_get_user(const char __user *pathname, int flags)
 {
 	enum bpf_type type = BPF_TYPE_UNSPEC;
 	struct filename *pname;
 	int ret = -ENOENT;
+	int f_flags;
 	void *raw;
 
+	f_flags = bpf_get_file_flag(flags);
+	if (f_flags < 0)
+		return f_flags;
+
 	pname = getname(pathname);
 	if (IS_ERR(pname))
 		return PTR_ERR(pname);
 
-	raw = bpf_obj_do_get(pname, &type);
+	raw = bpf_obj_do_get(pname, &type, f_flags);
 	if (IS_ERR(raw)) {
 		ret = PTR_ERR(raw);
 		goto out;
@@ -346,7 +351,7 @@ int bpf_obj_get_user(const char __user *pathname)
 	if (type == BPF_TYPE_PROG)
 		ret = bpf_prog_new_fd(raw);
 	else if (type == BPF_TYPE_MAP)
-		ret = bpf_map_new_fd(raw);
+		ret = bpf_map_new_fd(raw, f_flags);
 	else
 		goto out;
 
diff --git a/kernel/bpf/lpm_trie.c b/kernel/bpf/lpm_trie.c
index 34d8a690ea05..885e45479680 100644
--- a/kernel/bpf/lpm_trie.c
+++ b/kernel/bpf/lpm_trie.c
@@ -495,7 +495,8 @@ static int trie_delete_elem(struct bpf_map *map, void *_key)
 #define LPM_KEY_SIZE_MAX	LPM_KEY_SIZE(LPM_DATA_SIZE_MAX)
 #define LPM_KEY_SIZE_MIN	LPM_KEY_SIZE(LPM_DATA_SIZE_MIN)
 
-#define LPM_CREATE_FLAG_MASK	(BPF_F_NO_PREALLOC | BPF_F_NUMA_NODE)
+#define LPM_CREATE_FLAG_MASK	(BPF_F_NO_PREALLOC | BPF_F_NUMA_NODE |	\
+				 BPF_F_RDONLY | BPF_F_WRONLY)
 
 static struct bpf_map *trie_alloc(union bpf_attr *attr)
 {
diff --git a/kernel/bpf/sockmap.c b/kernel/bpf/sockmap.c
index a298d6666698..86ec846f2d5e 100644
--- a/kernel/bpf/sockmap.c
+++ b/kernel/bpf/sockmap.c
@@ -40,6 +40,9 @@
 #include <linux/list.h>
 #include <net/strparser.h>
 
+#define SOCK_CREATE_FLAG_MASK \
+	(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
+
 struct bpf_stab {
 	struct bpf_map map;
 	struct sock **sock_map;
@@ -489,7 +492,7 @@ static struct bpf_map *sock_map_alloc(union bpf_attr *attr)
 
 	/* check sanity of attributes */
 	if (attr->max_entries == 0 || attr->key_size != 4 ||
-	    attr->value_size != 4 || attr->map_flags & ~BPF_F_NUMA_NODE)
+	    attr->value_size != 4 || attr->map_flags & ~SOCK_CREATE_FLAG_MASK)
 		return ERR_PTR(-EINVAL);
 
 	if (attr->value_size > KMALLOC_MAX_SIZE)
diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c
index 135be433e9a0..a15bc636cc98 100644
--- a/kernel/bpf/stackmap.c
+++ b/kernel/bpf/stackmap.c
@@ -11,6 +11,9 @@
 #include <linux/perf_event.h>
 #include "percpu_freelist.h"
 
+#define STACK_CREATE_FLAG_MASK \
+	(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
+
 struct stack_map_bucket {
 	struct pcpu_freelist_node fnode;
 	u32 hash;
@@ -60,7 +63,7 @@ static struct bpf_map *stack_map_alloc(union bpf_attr *attr)
 	if (!capable(CAP_SYS_ADMIN))
 		return ERR_PTR(-EPERM);
 
-	if (attr->map_flags & ~BPF_F_NUMA_NODE)
+	if (attr->map_flags & ~STACK_CREATE_FLAG_MASK)
 		return ERR_PTR(-EINVAL);
 
 	/* check sanity of attributes */
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index d124e702e040..b02582ead9a4 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -294,17 +294,48 @@ static void bpf_map_show_fdinfo(struct seq_file *m, struct file *filp)
 }
 #endif
 
+static ssize_t bpf_dummy_read(struct file *filp, char __user *buf, size_t siz,
+			      loff_t *ppos)
+{
+	/* We need this handler such that alloc_file() enables
+	 * f_mode with FMODE_CAN_READ.
+	 */
+	return -EINVAL;
+}
+
+static ssize_t bpf_dummy_write(struct file *filp, const char __user *buf,
+			       size_t siz, loff_t *ppos)
+{
+	/* We need this handler such that alloc_file() enables
+	 * f_mode with FMODE_CAN_WRITE.
+	 */
+	return -EINVAL;
+}
+
 static const struct file_operations bpf_map_fops = {
 #ifdef CONFIG_PROC_FS
 	.show_fdinfo	= bpf_map_show_fdinfo,
 #endif
 	.release	= bpf_map_release,
+	.read		= bpf_dummy_read,
+	.write		= bpf_dummy_write,
 };
 
-int bpf_map_new_fd(struct bpf_map *map)
+int bpf_map_new_fd(struct bpf_map *map, int flags)
 {
 	return anon_inode_getfd("bpf-map", &bpf_map_fops, map,
-				O_RDWR | O_CLOEXEC);
+				flags | O_CLOEXEC);
+}
+
+int bpf_get_file_flag(int flags)
+{
+	if ((flags & BPF_F_RDONLY) && (flags & BPF_F_WRONLY))
+		return -EINVAL;
+	if (flags & BPF_F_RDONLY)
+		return O_RDONLY;
+	if (flags & BPF_F_WRONLY)
+		return O_WRONLY;
+	return O_RDWR;
 }
 
 /* helper macro to check that unused fields 'union bpf_attr' are zero */
@@ -344,12 +375,17 @@ static int map_create(union bpf_attr *attr)
 {
 	int numa_node = bpf_map_attr_numa_node(attr);
 	struct bpf_map *map;
+	int f_flags;
 	int err;
 
 	err = CHECK_ATTR(BPF_MAP_CREATE);
 	if (err)
 		return -EINVAL;
 
+	f_flags = bpf_get_file_flag(attr->map_flags);
+	if (f_flags < 0)
+		return f_flags;
+
 	if (numa_node != NUMA_NO_NODE &&
 	    ((unsigned int)numa_node >= nr_node_ids ||
 	     !node_online(numa_node)))
@@ -375,7 +411,7 @@ static int map_create(union bpf_attr *attr)
 	if (err)
 		goto free_map;
 
-	err = bpf_map_new_fd(map);
+	err = bpf_map_new_fd(map, f_flags);
 	if (err < 0) {
 		/* failed to allocate fd.
 		 * bpf_map_put() is needed because the above
@@ -490,6 +526,11 @@ static int map_lookup_elem(union bpf_attr *attr)
 	if (IS_ERR(map))
 		return PTR_ERR(map);
 
+	if (!(f.file->f_mode & FMODE_CAN_READ)) {
+		err = -EPERM;
+		goto err_put;
+	}
+
 	key = memdup_user(ukey, map->key_size);
 	if (IS_ERR(key)) {
 		err = PTR_ERR(key);
@@ -570,6 +611,11 @@ static int map_update_elem(union bpf_attr *attr)
 	if (IS_ERR(map))
 		return PTR_ERR(map);
 
+	if (!(f.file->f_mode & FMODE_CAN_WRITE)) {
+		err = -EPERM;
+		goto err_put;
+	}
+
 	key = memdup_user(ukey, map->key_size);
 	if (IS_ERR(key)) {
 		err = PTR_ERR(key);
@@ -653,6 +699,11 @@ static int map_delete_elem(union bpf_attr *attr)
 	if (IS_ERR(map))
 		return PTR_ERR(map);
 
+	if (!(f.file->f_mode & FMODE_CAN_WRITE)) {
+		err = -EPERM;
+		goto err_put;
+	}
+
 	key = memdup_user(ukey, map->key_size);
 	if (IS_ERR(key)) {
 		err = PTR_ERR(key);
@@ -696,6 +747,11 @@ static int map_get_next_key(union bpf_attr *attr)
 	if (IS_ERR(map))
 		return PTR_ERR(map);
 
+	if (!(f.file->f_mode & FMODE_CAN_READ)) {
+		err = -EPERM;
+		goto err_put;
+	}
+
 	if (ukey) {
 		key = memdup_user(ukey, map->key_size);
 		if (IS_ERR(key)) {
@@ -902,6 +958,8 @@ static const struct file_operations bpf_prog_fops = {
 	.show_fdinfo	= bpf_prog_show_fdinfo,
 #endif
 	.release	= bpf_prog_release,
+	.read		= bpf_dummy_read,
+	.write		= bpf_dummy_write,
 };
 
 int bpf_prog_new_fd(struct bpf_prog *prog)
@@ -1111,11 +1169,11 @@ static int bpf_prog_load(union bpf_attr *attr)
 	return err;
 }
 
-#define BPF_OBJ_LAST_FIELD bpf_fd
+#define BPF_OBJ_LAST_FIELD file_flags
 
 static int bpf_obj_pin(const union bpf_attr *attr)
 {
-	if (CHECK_ATTR(BPF_OBJ))
+	if (CHECK_ATTR(BPF_OBJ) || attr->file_flags != 0)
 		return -EINVAL;
 
 	return bpf_obj_pin_user(attr->bpf_fd, u64_to_user_ptr(attr->pathname));
@@ -1126,7 +1184,8 @@ static int bpf_obj_get(const union bpf_attr *attr)
 	if (CHECK_ATTR(BPF_OBJ) || attr->bpf_fd != 0)
 		return -EINVAL;
 
-	return bpf_obj_get_user(u64_to_user_ptr(attr->pathname));
+	return bpf_obj_get_user(u64_to_user_ptr(attr->pathname),
+				attr->file_flags);
 }
 
 #ifdef CONFIG_CGROUP_BPF
@@ -1386,12 +1445,13 @@ static int bpf_prog_get_fd_by_id(const union bpf_attr *attr)
 	return fd;
 }
 
-#define BPF_MAP_GET_FD_BY_ID_LAST_FIELD map_id
+#define BPF_MAP_GET_FD_BY_ID_LAST_FIELD open_flags
 
 static int bpf_map_get_fd_by_id(const union bpf_attr *attr)
 {
 	struct bpf_map *map;
 	u32 id = attr->map_id;
+	int f_flags;
 	int fd;
 
 	if (CHECK_ATTR(BPF_MAP_GET_FD_BY_ID))
@@ -1400,6 +1460,10 @@ static int bpf_map_get_fd_by_id(const union bpf_attr *attr)
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 
+	f_flags = bpf_get_file_flag(attr->open_flags);
+	if (f_flags < 0)
+		return f_flags;
+
 	spin_lock_bh(&map_idr_lock);
 	map = idr_find(&map_idr, id);
 	if (map)
@@ -1411,7 +1475,7 @@ static int bpf_map_get_fd_by_id(const union bpf_attr *attr)
 	if (IS_ERR(map))
 		return PTR_ERR(map);
 
-	fd = bpf_map_new_fd(map);
+	fd = bpf_map_new_fd(map, f_flags);
 	if (fd < 0)
 		bpf_map_put(map);
 
-- 
2.14.2.920.gcf0c67979c-goog

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info@ http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH net-next v2 2/5] bpf: Add tests for eBPF file mode
  2017-10-09 22:20 ` Chenbo Feng
@ 2017-10-09 22:20   ` Chenbo Feng
  -1 siblings, 0 replies; 39+ messages in thread
From: Chenbo Feng @ 2017-10-09 22:20 UTC (permalink / raw)
  To: linux-security-module, netdev, SELinux
  Cc: Jeffrey Vander Stoep, Alexei Starovoitov, lorenzo,
	Daniel Borkmann, Stephen Smalley, Chenbo Feng

From: Chenbo Feng <fengc@google.com>

Two related tests are added into bpf selftest to test read only map and
write only map. The tests verified the read only and write only flags
are working on hash maps.

Signed-off-by: Chenbo Feng <fengc@google.com>
---
 tools/testing/selftests/bpf/test_maps.c | 48 +++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c
index fe3a443a1102..896f23cfe918 100644
--- a/tools/testing/selftests/bpf/test_maps.c
+++ b/tools/testing/selftests/bpf/test_maps.c
@@ -1033,6 +1033,51 @@ static void test_map_parallel(void)
 	assert(bpf_map_get_next_key(fd, &key, &key) == -1 && errno == ENOENT);
 }
 
+static void test_map_rdonly(void)
+{
+	int i, fd, key = 0, value = 0;
+
+	fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
+			    MAP_SIZE, map_flags | BPF_F_RDONLY);
+	if (fd < 0) {
+		printf("Failed to create map for read only test '%s'!\n",
+		       strerror(errno));
+		exit(1);
+	}
+
+	key = 1;
+	value = 1234;
+	/* Insert key=1 element. */
+	assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == -1 &&
+	       errno == EPERM);
+
+	/* Check that key=2 is not found. */
+	assert(bpf_map_lookup_elem(fd, &key, &value) == -1 && errno == ENOENT);
+	assert(bpf_map_get_next_key(fd, &key, &value) == -1 && errno == ENOENT);
+}
+
+static void test_map_wronly(void)
+{
+	int i, fd, key = 0, value = 0;
+
+	fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
+			    MAP_SIZE, map_flags | BPF_F_WRONLY);
+	if (fd < 0) {
+		printf("Failed to create map for read only test '%s'!\n",
+		       strerror(errno));
+		exit(1);
+	}
+
+	key = 1;
+	value = 1234;
+	/* Insert key=1 element. */
+	assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0)
+
+	/* Check that key=2 is not found. */
+	assert(bpf_map_lookup_elem(fd, &key, &value) == -1 && errno == EPERM);
+	assert(bpf_map_get_next_key(fd, &key, &value) == -1 && errno == EPERM);
+}
+
 static void run_all_tests(void)
 {
 	test_hashmap(0, NULL);
@@ -1050,6 +1095,9 @@ static void run_all_tests(void)
 	test_map_large();
 	test_map_parallel();
 	test_map_stress();
+
+	test_map_rdonly();
+	test_map_wronly();
 }
 
 int main(void)
-- 
2.14.2.920.gcf0c67979c-goog


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH net-next v2 2/5] bpf: Add tests for eBPF file mode
@ 2017-10-09 22:20   ` Chenbo Feng
  0 siblings, 0 replies; 39+ messages in thread
From: Chenbo Feng @ 2017-10-09 22:20 UTC (permalink / raw)
  To: linux-security-module

From: Chenbo Feng <fengc@google.com>

Two related tests are added into bpf selftest to test read only map and
write only map. The tests verified the read only and write only flags
are working on hash maps.

Signed-off-by: Chenbo Feng <fengc@google.com>
---
 tools/testing/selftests/bpf/test_maps.c | 48 +++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c
index fe3a443a1102..896f23cfe918 100644
--- a/tools/testing/selftests/bpf/test_maps.c
+++ b/tools/testing/selftests/bpf/test_maps.c
@@ -1033,6 +1033,51 @@ static void test_map_parallel(void)
 	assert(bpf_map_get_next_key(fd, &key, &key) == -1 && errno == ENOENT);
 }
 
+static void test_map_rdonly(void)
+{
+	int i, fd, key = 0, value = 0;
+
+	fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
+			    MAP_SIZE, map_flags | BPF_F_RDONLY);
+	if (fd < 0) {
+		printf("Failed to create map for read only test '%s'!\n",
+		       strerror(errno));
+		exit(1);
+	}
+
+	key = 1;
+	value = 1234;
+	/* Insert key=1 element. */
+	assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == -1 &&
+	       errno == EPERM);
+
+	/* Check that key=2 is not found. */
+	assert(bpf_map_lookup_elem(fd, &key, &value) == -1 && errno == ENOENT);
+	assert(bpf_map_get_next_key(fd, &key, &value) == -1 && errno == ENOENT);
+}
+
+static void test_map_wronly(void)
+{
+	int i, fd, key = 0, value = 0;
+
+	fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
+			    MAP_SIZE, map_flags | BPF_F_WRONLY);
+	if (fd < 0) {
+		printf("Failed to create map for read only test '%s'!\n",
+		       strerror(errno));
+		exit(1);
+	}
+
+	key = 1;
+	value = 1234;
+	/* Insert key=1 element. */
+	assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0)
+
+	/* Check that key=2 is not found. */
+	assert(bpf_map_lookup_elem(fd, &key, &value) == -1 && errno == EPERM);
+	assert(bpf_map_get_next_key(fd, &key, &value) == -1 && errno == EPERM);
+}
+
 static void run_all_tests(void)
 {
 	test_hashmap(0, NULL);
@@ -1050,6 +1095,9 @@ static void run_all_tests(void)
 	test_map_large();
 	test_map_parallel();
 	test_map_stress();
+
+	test_map_rdonly();
+	test_map_wronly();
 }
 
 int main(void)
-- 
2.14.2.920.gcf0c67979c-goog

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info@ http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH net-next v2 3/5] security: bpf: Add LSM hooks for bpf object related syscall
  2017-10-09 22:20 ` Chenbo Feng
@ 2017-10-09 22:20   ` Chenbo Feng
  -1 siblings, 0 replies; 39+ messages in thread
From: Chenbo Feng @ 2017-10-09 22:20 UTC (permalink / raw)
  To: linux-security-module, netdev, SELinux
  Cc: Jeffrey Vander Stoep, Alexei Starovoitov, lorenzo,
	Daniel Borkmann, Stephen Smalley, Chenbo Feng

From: Chenbo Feng <fengc@google.com>

Introduce several LSM hooks for the syscalls that will allow the
userspace to access to eBPF object such as eBPF programs and eBPF maps.
The security check is aimed to enforce a per object security protection
for eBPF object so only processes with the right priviliges can
read/write to a specific map or use a specific eBPF program. Besides
that, a general security hook is added before the multiplexer of bpf
syscall to check the cmd and the attribute used for the command. The
actual security module can decide which command need to be checked and
how the cmd should be checked.

Signed-off-by: Chenbo Feng <fengc@google.com>
---
 include/linux/bpf.h       |  6 ++++++
 include/linux/lsm_hooks.h | 54 +++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/security.h  | 45 +++++++++++++++++++++++++++++++++++++++
 kernel/bpf/syscall.c      | 28 ++++++++++++++++++++++--
 security/security.c       | 32 ++++++++++++++++++++++++++++
 5 files changed, 163 insertions(+), 2 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 0e9ca2555d7f..225740688ab7 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -57,6 +57,9 @@ struct bpf_map {
 	atomic_t usercnt;
 	struct bpf_map *inner_map_meta;
 	char name[BPF_OBJ_NAME_LEN];
+#ifdef CONFIG_SECURITY
+	void *security;
+#endif
 };
 
 /* function argument constraints */
@@ -190,6 +193,9 @@ struct bpf_prog_aux {
 	struct user_struct *user;
 	u64 load_time; /* ns since boottime */
 	char name[BPF_OBJ_NAME_LEN];
+#ifdef CONFIG_SECURITY
+	void *security;
+#endif
 	union {
 		struct work_struct work;
 		struct rcu_head	rcu;
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index c9258124e417..7161d8e7ee79 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1351,6 +1351,40 @@
  *	@inode we wish to get the security context of.
  *	@ctx is a pointer in which to place the allocated security context.
  *	@ctxlen points to the place to put the length of @ctx.
+ *
+ * Security hooks for using the eBPF maps and programs functionalities through
+ * eBPF syscalls.
+ *
+ * @bpf:
+ *	Do a initial check for all bpf syscalls after the attribute is copied
+ *	into the kernel. The actual security module can implement their own
+ *	rules to check the specific cmd they need.
+ *
+ * @bpf_map:
+ *	Do a check when the kernel generate and return a file descriptor for
+ *	eBPF maps.
+ *
+ *	@map: bpf map that we want to access
+ *	@mask: the access flags
+ *
+ * @bpf_prog:
+ *	Do a check when the kernel generate and return a file descriptor for
+ *	eBPF programs.
+ *
+ *	@prog: bpf prog that userspace want to use.
+ *
+ * @bpf_map_alloc_security:
+ *	Initialize the security field inside bpf map.
+ *
+ * @bpf_map_free_security:
+ *	Clean up the security information stored inside bpf map.
+ *
+ * @bpf_prog_alloc_security:
+ *	Initialize the security field inside bpf program.
+ *
+ * @bpf_prog_free_security:
+ *	Clean up the security information stored inside bpf prog.
+ *
  */
 union security_list_options {
 	int (*binder_set_context_mgr)(struct task_struct *mgr);
@@ -1682,6 +1716,17 @@ union security_list_options {
 				struct audit_context *actx);
 	void (*audit_rule_free)(void *lsmrule);
 #endif /* CONFIG_AUDIT */
+
+#ifdef CONFIG_BPF_SYSCALL
+	int (*bpf)(int cmd, union bpf_attr *attr,
+				 unsigned int size);
+	int (*bpf_map)(struct bpf_map *map, fmode_t fmode);
+	int (*bpf_prog)(struct bpf_prog *prog);
+	int (*bpf_map_alloc_security)(struct bpf_map *map);
+	void (*bpf_map_free_security)(struct bpf_map *map);
+	int (*bpf_prog_alloc_security)(struct bpf_prog_aux *aux);
+	void (*bpf_prog_free_security)(struct bpf_prog_aux *aux);
+#endif /* CONFIG_BPF_SYSCALL */
 };
 
 struct security_hook_heads {
@@ -1901,6 +1946,15 @@ struct security_hook_heads {
 	struct list_head audit_rule_match;
 	struct list_head audit_rule_free;
 #endif /* CONFIG_AUDIT */
+#ifdef CONFIG_BPF_SYSCALL
+	struct list_head bpf;
+	struct list_head bpf_map;
+	struct list_head bpf_prog;
+	struct list_head bpf_map_alloc_security;
+	struct list_head bpf_map_free_security;
+	struct list_head bpf_prog_alloc_security;
+	struct list_head bpf_prog_free_security;
+#endif /* CONFIG_BPF_SYSCALL */
 } __randomize_layout;
 
 /*
diff --git a/include/linux/security.h b/include/linux/security.h
index ce6265960d6c..18800b0911e5 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -31,6 +31,7 @@
 #include <linux/string.h>
 #include <linux/mm.h>
 #include <linux/fs.h>
+#include <linux/bpf.h>
 
 struct linux_binprm;
 struct cred;
@@ -1730,6 +1731,50 @@ static inline void securityfs_remove(struct dentry *dentry)
 
 #endif
 
+#ifdef CONFIG_BPF_SYSCALL
+#ifdef CONFIG_SECURITY
+extern int security_bpf(int cmd, union bpf_attr *attr, unsigned int size);
+extern int security_bpf_map(struct bpf_map *map, fmode_t fmode);
+extern int security_bpf_prog(struct bpf_prog *prog);
+extern int security_bpf_map_alloc(struct bpf_map *map);
+extern void security_bpf_map_free(struct bpf_map *map);
+extern int security_bpf_prog_alloc(struct bpf_prog_aux *aux);
+extern void security_bpf_prog_free(struct bpf_prog_aux *aux);
+#else
+static inline int security_bpf(int cmd, union bpf_attr *attr,
+					     unsigned int size)
+{
+	return 0;
+}
+
+static inline int security_bpf_map(struct bpf_map *map, fmode_t fmode)
+{
+	return 0;
+}
+
+static inline int security_bpf_prog(struct bpf_prog *prog)
+{
+	return 0;
+}
+
+static inline int security_bpf_map_alloc(struct bpf_map *map)
+{
+	return 0;
+}
+
+static inline void security_bpf_map_free(struct bpf_map *map)
+{ }
+
+static inline int security_bpf_prog_alloc(struct bpf_prog_aux *aux)
+{
+	return 0;
+}
+
+static inline void security_bpf_prog_free(struct bpf_prog_aux *aux)
+{ }
+#endif /* CONFIG_SECURITY */
+#endif /* CONFIG_BPF_SYSCALL */
+
 #ifdef CONFIG_SECURITY
 
 static inline char *alloc_secdata(void)
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index b02582ead9a4..1cf31ddd7616 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -210,6 +210,7 @@ static void bpf_map_free_deferred(struct work_struct *work)
 	struct bpf_map *map = container_of(work, struct bpf_map, work);
 
 	bpf_map_uncharge_memlock(map);
+	security_bpf_map_free(map);
 	/* implementation dependent freeing */
 	map->ops->map_free(map);
 }
@@ -323,6 +324,9 @@ static const struct file_operations bpf_map_fops = {
 
 int bpf_map_new_fd(struct bpf_map *map, int flags)
 {
+	if (security_bpf_map(map, OPEN_FMODE(flags)))
+		return -EPERM;
+
 	return anon_inode_getfd("bpf-map", &bpf_map_fops, map,
 				flags | O_CLOEXEC);
 }
@@ -403,10 +407,14 @@ static int map_create(union bpf_attr *attr)
 	atomic_set(&map->refcnt, 1);
 	atomic_set(&map->usercnt, 1);
 
-	err = bpf_map_charge_memlock(map);
+	err = security_bpf_map_alloc(map);
 	if (err)
 		goto free_map_nouncharge;
 
+	err = bpf_map_charge_memlock(map);
+	if (err)
+		goto free_map_sec;
+
 	err = bpf_map_alloc_id(map);
 	if (err)
 		goto free_map;
@@ -428,6 +436,8 @@ static int map_create(union bpf_attr *attr)
 
 free_map:
 	bpf_map_uncharge_memlock(map);
+free_map_sec:
+	security_bpf_map_free(map);
 free_map_nouncharge:
 	map->ops->map_free(map);
 	return err;
@@ -906,6 +916,7 @@ static void __bpf_prog_put_rcu(struct rcu_head *rcu)
 
 	free_used_maps(aux);
 	bpf_prog_uncharge_memlock(aux->prog);
+	security_bpf_prog_free(aux);
 	bpf_prog_free(aux->prog);
 }
 
@@ -964,6 +975,9 @@ static const struct file_operations bpf_prog_fops = {
 
 int bpf_prog_new_fd(struct bpf_prog *prog)
 {
+	if (security_bpf_prog(prog))
+		return -EPERM;
+
 	return anon_inode_getfd("bpf-prog", &bpf_prog_fops, prog,
 				O_RDWR | O_CLOEXEC);
 }
@@ -1103,10 +1117,14 @@ static int bpf_prog_load(union bpf_attr *attr)
 	if (!prog)
 		return -ENOMEM;
 
-	err = bpf_prog_charge_memlock(prog);
+	err = security_bpf_prog_alloc(prog->aux);
 	if (err)
 		goto free_prog_nouncharge;
 
+	err = bpf_prog_charge_memlock(prog);
+	if (err)
+		goto free_prog_sec;
+
 	prog->len = attr->insn_cnt;
 
 	err = -EFAULT;
@@ -1164,6 +1182,8 @@ static int bpf_prog_load(union bpf_attr *attr)
 	free_used_maps(prog->aux);
 free_prog:
 	bpf_prog_uncharge_memlock(prog);
+free_prog_sec:
+	security_bpf_prog_free(prog->aux);
 free_prog_nouncharge:
 	bpf_prog_free(prog);
 	return err;
@@ -1630,6 +1650,10 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
 	if (copy_from_user(&attr, uattr, size) != 0)
 		return -EFAULT;
 
+	err = security_bpf(cmd, &attr, size);
+	if (err)
+		return -EPERM;
+
 	switch (cmd) {
 	case BPF_MAP_CREATE:
 		err = map_create(&attr);
diff --git a/security/security.c b/security/security.c
index 4bf0f571b4ef..1cd8526cb0b7 100644
--- a/security/security.c
+++ b/security/security.c
@@ -12,6 +12,7 @@
  *	(at your option) any later version.
  */
 
+#include <linux/bpf.h>
 #include <linux/capability.h>
 #include <linux/dcache.h>
 #include <linux/module.h>
@@ -1703,3 +1704,34 @@ int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule,
 				actx);
 }
 #endif /* CONFIG_AUDIT */
+
+#ifdef CONFIG_BPF_SYSCALL
+int security_bpf(int cmd, union bpf_attr *attr, unsigned int size)
+{
+	return call_int_hook(bpf, 0, cmd, attr, size);
+}
+int security_bpf_map(struct bpf_map *map, fmode_t fmode)
+{
+	return call_int_hook(bpf_map, 0, map, fmode);
+}
+int security_bpf_prog(struct bpf_prog *prog)
+{
+	return call_int_hook(bpf_prog, 0, prog);
+}
+int security_bpf_map_alloc(struct bpf_map *map)
+{
+	return call_int_hook(bpf_map_alloc_security, 0, map);
+}
+int security_bpf_prog_alloc(struct bpf_prog_aux *aux)
+{
+	return call_int_hook(bpf_prog_alloc_security, 0, aux);
+}
+void security_bpf_map_free(struct bpf_map *map)
+{
+	call_void_hook(bpf_map_free_security, map);
+}
+void security_bpf_prog_free(struct bpf_prog_aux *aux)
+{
+	call_void_hook(bpf_prog_free_security, aux);
+}
+#endif /* CONFIG_BPF_SYSCALL */
-- 
2.14.2.920.gcf0c67979c-goog

^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH net-next v2 3/5] security: bpf: Add LSM hooks for bpf object related syscall
@ 2017-10-09 22:20   ` Chenbo Feng
  0 siblings, 0 replies; 39+ messages in thread
From: Chenbo Feng @ 2017-10-09 22:20 UTC (permalink / raw)
  To: linux-security-module

From: Chenbo Feng <fengc@google.com>

Introduce several LSM hooks for the syscalls that will allow the
userspace to access to eBPF object such as eBPF programs and eBPF maps.
The security check is aimed to enforce a per object security protection
for eBPF object so only processes with the right priviliges can
read/write to a specific map or use a specific eBPF program. Besides
that, a general security hook is added before the multiplexer of bpf
syscall to check the cmd and the attribute used for the command. The
actual security module can decide which command need to be checked and
how the cmd should be checked.

Signed-off-by: Chenbo Feng <fengc@google.com>
---
 include/linux/bpf.h       |  6 ++++++
 include/linux/lsm_hooks.h | 54 +++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/security.h  | 45 +++++++++++++++++++++++++++++++++++++++
 kernel/bpf/syscall.c      | 28 ++++++++++++++++++++++--
 security/security.c       | 32 ++++++++++++++++++++++++++++
 5 files changed, 163 insertions(+), 2 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 0e9ca2555d7f..225740688ab7 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -57,6 +57,9 @@ struct bpf_map {
 	atomic_t usercnt;
 	struct bpf_map *inner_map_meta;
 	char name[BPF_OBJ_NAME_LEN];
+#ifdef CONFIG_SECURITY
+	void *security;
+#endif
 };
 
 /* function argument constraints */
@@ -190,6 +193,9 @@ struct bpf_prog_aux {
 	struct user_struct *user;
 	u64 load_time; /* ns since boottime */
 	char name[BPF_OBJ_NAME_LEN];
+#ifdef CONFIG_SECURITY
+	void *security;
+#endif
 	union {
 		struct work_struct work;
 		struct rcu_head	rcu;
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index c9258124e417..7161d8e7ee79 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1351,6 +1351,40 @@
  *	@inode we wish to get the security context of.
  *	@ctx is a pointer in which to place the allocated security context.
  *	@ctxlen points to the place to put the length of @ctx.
+ *
+ * Security hooks for using the eBPF maps and programs functionalities through
+ * eBPF syscalls.
+ *
+ * @bpf:
+ *	Do a initial check for all bpf syscalls after the attribute is copied
+ *	into the kernel. The actual security module can implement their own
+ *	rules to check the specific cmd they need.
+ *
+ * @bpf_map:
+ *	Do a check when the kernel generate and return a file descriptor for
+ *	eBPF maps.
+ *
+ *	@map: bpf map that we want to access
+ *	@mask: the access flags
+ *
+ * @bpf_prog:
+ *	Do a check when the kernel generate and return a file descriptor for
+ *	eBPF programs.
+ *
+ *	@prog: bpf prog that userspace want to use.
+ *
+ * @bpf_map_alloc_security:
+ *	Initialize the security field inside bpf map.
+ *
+ * @bpf_map_free_security:
+ *	Clean up the security information stored inside bpf map.
+ *
+ * @bpf_prog_alloc_security:
+ *	Initialize the security field inside bpf program.
+ *
+ * @bpf_prog_free_security:
+ *	Clean up the security information stored inside bpf prog.
+ *
  */
 union security_list_options {
 	int (*binder_set_context_mgr)(struct task_struct *mgr);
@@ -1682,6 +1716,17 @@ union security_list_options {
 				struct audit_context *actx);
 	void (*audit_rule_free)(void *lsmrule);
 #endif /* CONFIG_AUDIT */
+
+#ifdef CONFIG_BPF_SYSCALL
+	int (*bpf)(int cmd, union bpf_attr *attr,
+				 unsigned int size);
+	int (*bpf_map)(struct bpf_map *map, fmode_t fmode);
+	int (*bpf_prog)(struct bpf_prog *prog);
+	int (*bpf_map_alloc_security)(struct bpf_map *map);
+	void (*bpf_map_free_security)(struct bpf_map *map);
+	int (*bpf_prog_alloc_security)(struct bpf_prog_aux *aux);
+	void (*bpf_prog_free_security)(struct bpf_prog_aux *aux);
+#endif /* CONFIG_BPF_SYSCALL */
 };
 
 struct security_hook_heads {
@@ -1901,6 +1946,15 @@ struct security_hook_heads {
 	struct list_head audit_rule_match;
 	struct list_head audit_rule_free;
 #endif /* CONFIG_AUDIT */
+#ifdef CONFIG_BPF_SYSCALL
+	struct list_head bpf;
+	struct list_head bpf_map;
+	struct list_head bpf_prog;
+	struct list_head bpf_map_alloc_security;
+	struct list_head bpf_map_free_security;
+	struct list_head bpf_prog_alloc_security;
+	struct list_head bpf_prog_free_security;
+#endif /* CONFIG_BPF_SYSCALL */
 } __randomize_layout;
 
 /*
diff --git a/include/linux/security.h b/include/linux/security.h
index ce6265960d6c..18800b0911e5 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -31,6 +31,7 @@
 #include <linux/string.h>
 #include <linux/mm.h>
 #include <linux/fs.h>
+#include <linux/bpf.h>
 
 struct linux_binprm;
 struct cred;
@@ -1730,6 +1731,50 @@ static inline void securityfs_remove(struct dentry *dentry)
 
 #endif
 
+#ifdef CONFIG_BPF_SYSCALL
+#ifdef CONFIG_SECURITY
+extern int security_bpf(int cmd, union bpf_attr *attr, unsigned int size);
+extern int security_bpf_map(struct bpf_map *map, fmode_t fmode);
+extern int security_bpf_prog(struct bpf_prog *prog);
+extern int security_bpf_map_alloc(struct bpf_map *map);
+extern void security_bpf_map_free(struct bpf_map *map);
+extern int security_bpf_prog_alloc(struct bpf_prog_aux *aux);
+extern void security_bpf_prog_free(struct bpf_prog_aux *aux);
+#else
+static inline int security_bpf(int cmd, union bpf_attr *attr,
+					     unsigned int size)
+{
+	return 0;
+}
+
+static inline int security_bpf_map(struct bpf_map *map, fmode_t fmode)
+{
+	return 0;
+}
+
+static inline int security_bpf_prog(struct bpf_prog *prog)
+{
+	return 0;
+}
+
+static inline int security_bpf_map_alloc(struct bpf_map *map)
+{
+	return 0;
+}
+
+static inline void security_bpf_map_free(struct bpf_map *map)
+{ }
+
+static inline int security_bpf_prog_alloc(struct bpf_prog_aux *aux)
+{
+	return 0;
+}
+
+static inline void security_bpf_prog_free(struct bpf_prog_aux *aux)
+{ }
+#endif /* CONFIG_SECURITY */
+#endif /* CONFIG_BPF_SYSCALL */
+
 #ifdef CONFIG_SECURITY
 
 static inline char *alloc_secdata(void)
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index b02582ead9a4..1cf31ddd7616 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -210,6 +210,7 @@ static void bpf_map_free_deferred(struct work_struct *work)
 	struct bpf_map *map = container_of(work, struct bpf_map, work);
 
 	bpf_map_uncharge_memlock(map);
+	security_bpf_map_free(map);
 	/* implementation dependent freeing */
 	map->ops->map_free(map);
 }
@@ -323,6 +324,9 @@ static const struct file_operations bpf_map_fops = {
 
 int bpf_map_new_fd(struct bpf_map *map, int flags)
 {
+	if (security_bpf_map(map, OPEN_FMODE(flags)))
+		return -EPERM;
+
 	return anon_inode_getfd("bpf-map", &bpf_map_fops, map,
 				flags | O_CLOEXEC);
 }
@@ -403,10 +407,14 @@ static int map_create(union bpf_attr *attr)
 	atomic_set(&map->refcnt, 1);
 	atomic_set(&map->usercnt, 1);
 
-	err = bpf_map_charge_memlock(map);
+	err = security_bpf_map_alloc(map);
 	if (err)
 		goto free_map_nouncharge;
 
+	err = bpf_map_charge_memlock(map);
+	if (err)
+		goto free_map_sec;
+
 	err = bpf_map_alloc_id(map);
 	if (err)
 		goto free_map;
@@ -428,6 +436,8 @@ static int map_create(union bpf_attr *attr)
 
 free_map:
 	bpf_map_uncharge_memlock(map);
+free_map_sec:
+	security_bpf_map_free(map);
 free_map_nouncharge:
 	map->ops->map_free(map);
 	return err;
@@ -906,6 +916,7 @@ static void __bpf_prog_put_rcu(struct rcu_head *rcu)
 
 	free_used_maps(aux);
 	bpf_prog_uncharge_memlock(aux->prog);
+	security_bpf_prog_free(aux);
 	bpf_prog_free(aux->prog);
 }
 
@@ -964,6 +975,9 @@ static const struct file_operations bpf_prog_fops = {
 
 int bpf_prog_new_fd(struct bpf_prog *prog)
 {
+	if (security_bpf_prog(prog))
+		return -EPERM;
+
 	return anon_inode_getfd("bpf-prog", &bpf_prog_fops, prog,
 				O_RDWR | O_CLOEXEC);
 }
@@ -1103,10 +1117,14 @@ static int bpf_prog_load(union bpf_attr *attr)
 	if (!prog)
 		return -ENOMEM;
 
-	err = bpf_prog_charge_memlock(prog);
+	err = security_bpf_prog_alloc(prog->aux);
 	if (err)
 		goto free_prog_nouncharge;
 
+	err = bpf_prog_charge_memlock(prog);
+	if (err)
+		goto free_prog_sec;
+
 	prog->len = attr->insn_cnt;
 
 	err = -EFAULT;
@@ -1164,6 +1182,8 @@ static int bpf_prog_load(union bpf_attr *attr)
 	free_used_maps(prog->aux);
 free_prog:
 	bpf_prog_uncharge_memlock(prog);
+free_prog_sec:
+	security_bpf_prog_free(prog->aux);
 free_prog_nouncharge:
 	bpf_prog_free(prog);
 	return err;
@@ -1630,6 +1650,10 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
 	if (copy_from_user(&attr, uattr, size) != 0)
 		return -EFAULT;
 
+	err = security_bpf(cmd, &attr, size);
+	if (err)
+		return -EPERM;
+
 	switch (cmd) {
 	case BPF_MAP_CREATE:
 		err = map_create(&attr);
diff --git a/security/security.c b/security/security.c
index 4bf0f571b4ef..1cd8526cb0b7 100644
--- a/security/security.c
+++ b/security/security.c
@@ -12,6 +12,7 @@
  *	(at your option) any later version.
  */
 
+#include <linux/bpf.h>
 #include <linux/capability.h>
 #include <linux/dcache.h>
 #include <linux/module.h>
@@ -1703,3 +1704,34 @@ int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule,
 				actx);
 }
 #endif /* CONFIG_AUDIT */
+
+#ifdef CONFIG_BPF_SYSCALL
+int security_bpf(int cmd, union bpf_attr *attr, unsigned int size)
+{
+	return call_int_hook(bpf, 0, cmd, attr, size);
+}
+int security_bpf_map(struct bpf_map *map, fmode_t fmode)
+{
+	return call_int_hook(bpf_map, 0, map, fmode);
+}
+int security_bpf_prog(struct bpf_prog *prog)
+{
+	return call_int_hook(bpf_prog, 0, prog);
+}
+int security_bpf_map_alloc(struct bpf_map *map)
+{
+	return call_int_hook(bpf_map_alloc_security, 0, map);
+}
+int security_bpf_prog_alloc(struct bpf_prog_aux *aux)
+{
+	return call_int_hook(bpf_prog_alloc_security, 0, aux);
+}
+void security_bpf_map_free(struct bpf_map *map)
+{
+	call_void_hook(bpf_map_free_security, map);
+}
+void security_bpf_prog_free(struct bpf_prog_aux *aux)
+{
+	call_void_hook(bpf_prog_free_security, aux);
+}
+#endif /* CONFIG_BPF_SYSCALL */
-- 
2.14.2.920.gcf0c67979c-goog

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH net-next v2 4/5] selinux: bpf: Add selinux check for eBPF syscall operations
  2017-10-09 22:20 ` Chenbo Feng
@ 2017-10-09 22:20   ` Chenbo Feng
  -1 siblings, 0 replies; 39+ messages in thread
From: Chenbo Feng @ 2017-10-09 22:20 UTC (permalink / raw)
  To: linux-security-module, netdev, SELinux
  Cc: Jeffrey Vander Stoep, Alexei Starovoitov, lorenzo,
	Daniel Borkmann, Stephen Smalley, Chenbo Feng

From: Chenbo Feng <fengc@google.com>

Implement the actual checks introduced to eBPF related syscalls. This
implementation use the security field inside bpf object to store a sid that
identify the bpf object. And when processes try to access the object,
selinux will check if processes have the right privileges. The creation
of eBPF object are also checked at the general bpf check hook and new
cmd introduced to eBPF domain can also be checked there.

Signed-off-by: Chenbo Feng <fengc@google.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
---
 security/selinux/hooks.c            | 111 ++++++++++++++++++++++++++++++++++++
 security/selinux/include/classmap.h |   2 +
 security/selinux/include/objsec.h   |   4 ++
 3 files changed, 117 insertions(+)

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index f5d304736852..41aba4e3d57c 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -85,6 +85,7 @@
 #include <linux/export.h>
 #include <linux/msg.h>
 #include <linux/shm.h>
+#include <linux/bpf.h>
 
 #include "avc.h"
 #include "objsec.h"
@@ -6252,6 +6253,106 @@ static void selinux_ib_free_security(void *ib_sec)
 }
 #endif
 
+#ifdef CONFIG_BPF_SYSCALL
+static int selinux_bpf(int cmd, union bpf_attr *attr,
+				     unsigned int size)
+{
+	u32 sid = current_sid();
+	int ret;
+
+	switch (cmd) {
+	case BPF_MAP_CREATE:
+		ret = avc_has_perm(sid, sid, SECCLASS_BPF_MAP, BPF_MAP__CREATE,
+				   NULL);
+		break;
+	case BPF_PROG_LOAD:
+		ret = avc_has_perm(sid, sid, SECCLASS_BPF_PROG, BPF_PROG__LOAD,
+				   NULL);
+		break;
+	default:
+		ret = 0;
+		break;
+	}
+
+	return ret;
+}
+
+static u32 bpf_map_fmode_to_av(fmode_t fmode)
+{
+	u32 av = 0;
+
+	if (f_mode & FMODE_READ)
+		av |= BPF_MAP__READ;
+	if (f_mode & FMODE_WRITE)
+		av |= BPF_MAP__WRITE;
+	return av;
+}
+
+static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
+{
+	u32 sid = current_sid();
+	struct bpf_security_struct *bpfsec;
+
+	bpfsec = map->security;
+	return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF_MAP,
+			    bpf_map_fmode_to_av(fmode), NULL);
+}
+
+static int selinux_bpf_prog(struct bpf_prog *prog)
+{
+	u32 sid = current_sid();
+	struct bpf_security_struct *bpfsec;
+
+	bpfsec = prog->aux->security;
+	return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF_PROG,
+			    BPF_PROG__USE, NULL);
+}
+
+static int selinux_bpf_map_alloc(struct bpf_map *map)
+{
+	struct bpf_security_struct *bpfsec;
+
+	bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
+	if (!bpfsec)
+		return -ENOMEM;
+
+	bpfsec->sid = current_sid();
+	map->security = bpfsec;
+
+	return 0;
+}
+
+static void selinux_bpf_map_free(struct bpf_map *map)
+{
+	struct bpf_security_struct *bpfsec = map->security;
+
+	map->security = NULL;
+	kfree(bpfsec);
+}
+
+static int selinux_bpf_prog_alloc(struct bpf_prog_aux *aux)
+{
+	struct bpf_security_struct *bpfsec;
+
+	bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
+	if (!bpfsec)
+		return -ENOMEM;
+
+	bpfsec->sid = current_sid();
+	aux->security = bpfsec;
+
+	return 0;
+}
+
+static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
+{
+	struct bpf_security_struct *bpfsec = aux->security;
+
+	aux->security = NULL;
+	kfree(bpfsec);
+}
+#endif
+
 static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(binder_set_context_mgr, selinux_binder_set_context_mgr),
 	LSM_HOOK_INIT(binder_transaction, selinux_binder_transaction),
@@ -6471,6 +6572,16 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(audit_rule_match, selinux_audit_rule_match),
 	LSM_HOOK_INIT(audit_rule_free, selinux_audit_rule_free),
 #endif
+
+#ifdef CONFIG_BPF_SYSCALL
+	LSM_HOOK_INIT(bpf, selinux_bpf),
+	LSM_HOOK_INIT(bpf_map, selinux_bpf_map),
+	LSM_HOOK_INIT(bpf_prog, selinux_bpf_prog),
+	LSM_HOOK_INIT(bpf_map_alloc_security, selinux_bpf_map_alloc),
+	LSM_HOOK_INIT(bpf_prog_alloc_security, selinux_bpf_prog_alloc),
+	LSM_HOOK_INIT(bpf_map_free_security, selinux_bpf_map_free),
+	LSM_HOOK_INIT(bpf_prog_free_security, selinux_bpf_prog_free),
+#endif
 };
 
 static __init int selinux_init(void)
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index 35ffb29a69cb..7253c5eea59c 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -237,6 +237,8 @@ struct security_class_mapping secclass_map[] = {
 	  { "access", NULL } },
 	{ "infiniband_endport",
 	  { "manage_subnet", NULL } },
+	{ "bpf_map", {"create", "read", "write"} },
+	{ "bpf_prog", {"load", "use"} },
 	{ NULL }
   };
 
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 1649cd18eb0b..3d54468ce334 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -150,6 +150,10 @@ struct pkey_security_struct {
 	u32	sid;	/* SID of pkey */
 };
 
+struct bpf_security_struct {
+	u32 sid;  /*SID of bpf obj creater*/
+};
+
 extern unsigned int selinux_checkreqprot;
 
 #endif /* _SELINUX_OBJSEC_H_ */
-- 
2.14.2.920.gcf0c67979c-goog

^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH net-next v2 4/5] selinux: bpf: Add selinux check for eBPF syscall operations
@ 2017-10-09 22:20   ` Chenbo Feng
  0 siblings, 0 replies; 39+ messages in thread
From: Chenbo Feng @ 2017-10-09 22:20 UTC (permalink / raw)
  To: linux-security-module

From: Chenbo Feng <fengc@google.com>

Implement the actual checks introduced to eBPF related syscalls. This
implementation use the security field inside bpf object to store a sid that
identify the bpf object. And when processes try to access the object,
selinux will check if processes have the right privileges. The creation
of eBPF object are also checked at the general bpf check hook and new
cmd introduced to eBPF domain can also be checked there.

Signed-off-by: Chenbo Feng <fengc@google.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
---
 security/selinux/hooks.c            | 111 ++++++++++++++++++++++++++++++++++++
 security/selinux/include/classmap.h |   2 +
 security/selinux/include/objsec.h   |   4 ++
 3 files changed, 117 insertions(+)

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index f5d304736852..41aba4e3d57c 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -85,6 +85,7 @@
 #include <linux/export.h>
 #include <linux/msg.h>
 #include <linux/shm.h>
+#include <linux/bpf.h>
 
 #include "avc.h"
 #include "objsec.h"
@@ -6252,6 +6253,106 @@ static void selinux_ib_free_security(void *ib_sec)
 }
 #endif
 
+#ifdef CONFIG_BPF_SYSCALL
+static int selinux_bpf(int cmd, union bpf_attr *attr,
+				     unsigned int size)
+{
+	u32 sid = current_sid();
+	int ret;
+
+	switch (cmd) {
+	case BPF_MAP_CREATE:
+		ret = avc_has_perm(sid, sid, SECCLASS_BPF_MAP, BPF_MAP__CREATE,
+				   NULL);
+		break;
+	case BPF_PROG_LOAD:
+		ret = avc_has_perm(sid, sid, SECCLASS_BPF_PROG, BPF_PROG__LOAD,
+				   NULL);
+		break;
+	default:
+		ret = 0;
+		break;
+	}
+
+	return ret;
+}
+
+static u32 bpf_map_fmode_to_av(fmode_t fmode)
+{
+	u32 av = 0;
+
+	if (f_mode & FMODE_READ)
+		av |= BPF_MAP__READ;
+	if (f_mode & FMODE_WRITE)
+		av |= BPF_MAP__WRITE;
+	return av;
+}
+
+static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
+{
+	u32 sid = current_sid();
+	struct bpf_security_struct *bpfsec;
+
+	bpfsec = map->security;
+	return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF_MAP,
+			    bpf_map_fmode_to_av(fmode), NULL);
+}
+
+static int selinux_bpf_prog(struct bpf_prog *prog)
+{
+	u32 sid = current_sid();
+	struct bpf_security_struct *bpfsec;
+
+	bpfsec = prog->aux->security;
+	return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF_PROG,
+			    BPF_PROG__USE, NULL);
+}
+
+static int selinux_bpf_map_alloc(struct bpf_map *map)
+{
+	struct bpf_security_struct *bpfsec;
+
+	bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
+	if (!bpfsec)
+		return -ENOMEM;
+
+	bpfsec->sid = current_sid();
+	map->security = bpfsec;
+
+	return 0;
+}
+
+static void selinux_bpf_map_free(struct bpf_map *map)
+{
+	struct bpf_security_struct *bpfsec = map->security;
+
+	map->security = NULL;
+	kfree(bpfsec);
+}
+
+static int selinux_bpf_prog_alloc(struct bpf_prog_aux *aux)
+{
+	struct bpf_security_struct *bpfsec;
+
+	bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
+	if (!bpfsec)
+		return -ENOMEM;
+
+	bpfsec->sid = current_sid();
+	aux->security = bpfsec;
+
+	return 0;
+}
+
+static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
+{
+	struct bpf_security_struct *bpfsec = aux->security;
+
+	aux->security = NULL;
+	kfree(bpfsec);
+}
+#endif
+
 static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(binder_set_context_mgr, selinux_binder_set_context_mgr),
 	LSM_HOOK_INIT(binder_transaction, selinux_binder_transaction),
@@ -6471,6 +6572,16 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(audit_rule_match, selinux_audit_rule_match),
 	LSM_HOOK_INIT(audit_rule_free, selinux_audit_rule_free),
 #endif
+
+#ifdef CONFIG_BPF_SYSCALL
+	LSM_HOOK_INIT(bpf, selinux_bpf),
+	LSM_HOOK_INIT(bpf_map, selinux_bpf_map),
+	LSM_HOOK_INIT(bpf_prog, selinux_bpf_prog),
+	LSM_HOOK_INIT(bpf_map_alloc_security, selinux_bpf_map_alloc),
+	LSM_HOOK_INIT(bpf_prog_alloc_security, selinux_bpf_prog_alloc),
+	LSM_HOOK_INIT(bpf_map_free_security, selinux_bpf_map_free),
+	LSM_HOOK_INIT(bpf_prog_free_security, selinux_bpf_prog_free),
+#endif
 };
 
 static __init int selinux_init(void)
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index 35ffb29a69cb..7253c5eea59c 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -237,6 +237,8 @@ struct security_class_mapping secclass_map[] = {
 	  { "access", NULL } },
 	{ "infiniband_endport",
 	  { "manage_subnet", NULL } },
+	{ "bpf_map", {"create", "read", "write"} },
+	{ "bpf_prog", {"load", "use"} },
 	{ NULL }
   };
 
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 1649cd18eb0b..3d54468ce334 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -150,6 +150,10 @@ struct pkey_security_struct {
 	u32	sid;	/* SID of pkey */
 };
 
+struct bpf_security_struct {
+	u32 sid;  /*SID of bpf obj creater*/
+};
+
 extern unsigned int selinux_checkreqprot;
 
 #endif /* _SELINUX_OBJSEC_H_ */
-- 
2.14.2.920.gcf0c67979c-goog

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH net-next v2 5/5] selinux: bpf: Add addtional check for bpf object file receive
  2017-10-09 22:20 ` Chenbo Feng
@ 2017-10-09 22:20   ` Chenbo Feng
  -1 siblings, 0 replies; 39+ messages in thread
From: Chenbo Feng @ 2017-10-09 22:20 UTC (permalink / raw)
  To: linux-security-module, netdev, SELinux
  Cc: Jeffrey Vander Stoep, Alexei Starovoitov, lorenzo,
	Daniel Borkmann, Stephen Smalley, Chenbo Feng

From: Chenbo Feng <fengc@google.com>

Introduce a bpf object related check when sending and receiving files
through unix domain socket as well as binder. It checks if the receiving
process have privilege to read/write the bpf map or use the bpf program.
This check is necessary because the bpf maps and programs are using a
anonymous inode as their shared inode so the normal way of checking the
files and sockets when passing between processes cannot work properly on
eBPF object. This check only works when the BPF_SYSCALL is configured.
The information stored inside the file security struct is the same as
the information in bpf object security struct.

Signed-off-by: Chenbo Feng <fengc@google.com>
---
 include/linux/bpf.h       |  3 +++
 include/linux/lsm_hooks.h | 17 +++++++++++++
 include/linux/security.h  |  9 +++++++
 kernel/bpf/syscall.c      |  4 ++--
 security/security.c       |  8 +++++++
 security/selinux/hooks.c  | 61 +++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 100 insertions(+), 2 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 225740688ab7..81d6c01b8825 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -285,6 +285,9 @@ int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs,
 #ifdef CONFIG_BPF_SYSCALL
 DECLARE_PER_CPU(int, bpf_prog_active);
 
+extern const struct file_operations bpf_map_fops;
+extern const struct file_operations bpf_prog_fops;
+
 #define BPF_PROG_TYPE(_id, _ops) \
 	extern const struct bpf_verifier_ops _ops;
 #define BPF_MAP_TYPE(_id, _ops) \
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 7161d8e7ee79..517dea60b87b 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1385,6 +1385,19 @@
  * @bpf_prog_free_security:
  *	Clean up the security information stored inside bpf prog.
  *
+ * @bpf_map_file:
+ *	When creating a bpf map fd, set up the file security information with
+ *	the bpf security information stored in the map struct. So when the map
+ *	fd is passed between processes, the security module can directly read
+ *	the security information from file security struct rather than the bpf
+ *	security struct.
+ *
+ * @bpf_prog_file:
+ *	When creating a bpf prog fd, set up the file security information with
+ *	the bpf security information stored in the prog struct. So when the prog
+ *	fd is passed between processes, the security module can directly read
+ *	the security information from file security struct rather than the bpf
+ *	security struct.
  */
 union security_list_options {
 	int (*binder_set_context_mgr)(struct task_struct *mgr);
@@ -1726,6 +1739,8 @@ union security_list_options {
 	void (*bpf_map_free_security)(struct bpf_map *map);
 	int (*bpf_prog_alloc_security)(struct bpf_prog_aux *aux);
 	void (*bpf_prog_free_security)(struct bpf_prog_aux *aux);
+	void (*bpf_map_file)(struct bpf_map *map, struct file *file);
+	void (*bpf_prog_file)(struct bpf_prog_aux *aux, struct file *file);
 #endif /* CONFIG_BPF_SYSCALL */
 };
 
@@ -1954,6 +1969,8 @@ struct security_hook_heads {
 	struct list_head bpf_map_free_security;
 	struct list_head bpf_prog_alloc_security;
 	struct list_head bpf_prog_free_security;
+	struct list_head bpf_map_file;
+	struct list_head bpf_prog_file;
 #endif /* CONFIG_BPF_SYSCALL */
 } __randomize_layout;
 
diff --git a/include/linux/security.h b/include/linux/security.h
index 18800b0911e5..57573b794e2d 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1740,6 +1740,8 @@ extern int security_bpf_map_alloc(struct bpf_map *map);
 extern void security_bpf_map_free(struct bpf_map *map);
 extern int security_bpf_prog_alloc(struct bpf_prog_aux *aux);
 extern void security_bpf_prog_free(struct bpf_prog_aux *aux);
+extern void security_bpf_map_file(struct bpf_map *map, struct file *file);
+extern void security_bpf_prog_file(struct bpf_prog_aux *aux, struct file *file);
 #else
 static inline int security_bpf(int cmd, union bpf_attr *attr,
 					     unsigned int size)
@@ -1772,6 +1774,13 @@ static inline int security_bpf_prog_alloc(struct bpf_prog_aux *aux)
 
 static inline void security_bpf_prog_free(struct bpf_prog_aux *aux)
 { }
+
+static inline void security_bpf_map_file(struct bpf_map *map, struct file *file)
+{ }
+
+static inline void security_bpf_prog_file(struct bpf_prog_aux *aux,
+					  struct file *file)
+{ }
 #endif /* CONFIG_SECURITY */
 #endif /* CONFIG_BPF_SYSCALL */
 
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 1cf31ddd7616..b144181d3f3a 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -313,7 +313,7 @@ static ssize_t bpf_dummy_write(struct file *filp, const char __user *buf,
 	return -EINVAL;
 }
 
-static const struct file_operations bpf_map_fops = {
+const struct file_operations bpf_map_fops = {
 #ifdef CONFIG_PROC_FS
 	.show_fdinfo	= bpf_map_show_fdinfo,
 #endif
@@ -964,7 +964,7 @@ static void bpf_prog_show_fdinfo(struct seq_file *m, struct file *filp)
 }
 #endif
 
-static const struct file_operations bpf_prog_fops = {
+const struct file_operations bpf_prog_fops = {
 #ifdef CONFIG_PROC_FS
 	.show_fdinfo	= bpf_prog_show_fdinfo,
 #endif
diff --git a/security/security.c b/security/security.c
index 1cd8526cb0b7..dacf649b8cfa 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1734,4 +1734,12 @@ void security_bpf_prog_free(struct bpf_prog_aux *aux)
 {
 	call_void_hook(bpf_prog_free_security, aux);
 }
+void security_bpf_map_file(struct bpf_map *map, struct file *file)
+{
+	call_void_hook(bpf_map_file, map, file);
+}
+void security_bpf_prog_file(struct bpf_prog_aux *aux, struct file *file)
+{
+	call_void_hook(bpf_prog_file, aux, file);
+}
 #endif /* CONFIG_BPF_SYSCALL */
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 41aba4e3d57c..fea88655e0ee 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1815,6 +1815,10 @@ static inline int file_path_has_perm(const struct cred *cred,
 	return inode_has_perm(cred, file_inode(file), av, &ad);
 }
 
+#ifdef CONFIG_BPF_SYSCALL
+static int bpf_file_check(struct file *file, u32 sid);
+#endif
+
 /* Check whether a task can use an open file descriptor to
    access an inode in a given way.  Check access to the
    descriptor itself, and then use dentry_has_perm to
@@ -1845,6 +1849,12 @@ static int file_has_perm(const struct cred *cred,
 			goto out;
 	}
 
+#ifdef CONFIG_BPF_SYSCALL
+	rc = bpf_file_check(file, cred_sid(cred));
+	if (rc)
+		goto out;
+#endif
+
 	/* av is zero if only checking access to the descriptor. */
 	rc = 0;
 	if (av)
@@ -2165,6 +2175,12 @@ static int selinux_binder_transfer_file(struct task_struct *from,
 			return rc;
 	}
 
+#ifdef CONFIG_BPF_SYSCALL
+	rc = bpf_file_check(file, sid);
+	if (rc)
+		return rc;
+#endif
+
 	if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
 		return 0;
 
@@ -6288,6 +6304,33 @@ static u32 bpf_map_fmode_to_av(fmode_t fmode)
 	return av;
 }
 
+/* This function will check the file pass through unix socket or binder to see
+ * if it is a bpf related object. And apply correspinding checks on the bpf
+ * object based on the type. The bpf maps and programs, not like other files and
+ * socket, are using a shared anonymous inode inside the kernel as their inode.
+ * So checking that inode cannot identify if the process have privilege to
+ * access the bpf object and that's why we have to add this additional check in
+ * selinux_file_receive and selinux_binder_transfer_files.
+ */
+static int bpf_file_check(struct file *file, u32 sid)
+{
+	struct file_security_struct *fsec = file->f_security;
+	int ret;
+
+	if (file->f_op == &bpf_map_fops) {
+		ret = avc_has_perm(sid, fsec->sid, SECCLASS_BPF_MAP,
+				   bpf_map_fmode_to_av(file->f_mode), NULL);
+		if (ret)
+			return ret;
+	} else if (file->f_op == &bpf_prog_fops) {
+		ret = avc_has_perm(sid, fsec->sid, SECCLASS_BPF_PROG,
+				   BPF_PROG__USE, NULL);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
 static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
 {
 	u32 sid = current_sid();
@@ -6351,6 +6394,22 @@ static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
 	aux->security = NULL;
 	kfree(bpfsec);
 }
+
+static void selinux_bpf_map_file(struct bpf_map *map, struct file *file)
+{
+	struct bpf_security_struct *bpfsec = map->security;
+	struct file_security_struct *fsec = file->f_security;
+
+	fsec->sid = bpfsec->sid;
+}
+
+static void selinux_bpf_prog_file(struct bpf_prog_aux *aux, struct file *file)
+{
+	struct bpf_security_struct *bpfsec = aux->security;
+	struct file_security_struct *fsec = file->f_security;
+
+	fsec->sid = bpfsec->sid;
+}
 #endif
 
 static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
@@ -6581,6 +6640,8 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(bpf_prog_alloc_security, selinux_bpf_prog_alloc),
 	LSM_HOOK_INIT(bpf_map_free_security, selinux_bpf_map_free),
 	LSM_HOOK_INIT(bpf_prog_free_security, selinux_bpf_prog_free),
+	LSM_HOOK_INIT(bpf_map_file, selinux_bpf_map_file),
+	LSM_HOOK_INIT(bpf_prog_file, selinux_bpf_prog_file),
 #endif
 };
 
-- 
2.14.2.920.gcf0c67979c-goog

^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH net-next v2 5/5] selinux: bpf: Add addtional check for bpf object file receive
@ 2017-10-09 22:20   ` Chenbo Feng
  0 siblings, 0 replies; 39+ messages in thread
From: Chenbo Feng @ 2017-10-09 22:20 UTC (permalink / raw)
  To: linux-security-module

From: Chenbo Feng <fengc@google.com>

Introduce a bpf object related check when sending and receiving files
through unix domain socket as well as binder. It checks if the receiving
process have privilege to read/write the bpf map or use the bpf program.
This check is necessary because the bpf maps and programs are using a
anonymous inode as their shared inode so the normal way of checking the
files and sockets when passing between processes cannot work properly on
eBPF object. This check only works when the BPF_SYSCALL is configured.
The information stored inside the file security struct is the same as
the information in bpf object security struct.

Signed-off-by: Chenbo Feng <fengc@google.com>
---
 include/linux/bpf.h       |  3 +++
 include/linux/lsm_hooks.h | 17 +++++++++++++
 include/linux/security.h  |  9 +++++++
 kernel/bpf/syscall.c      |  4 ++--
 security/security.c       |  8 +++++++
 security/selinux/hooks.c  | 61 +++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 100 insertions(+), 2 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 225740688ab7..81d6c01b8825 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -285,6 +285,9 @@ int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs,
 #ifdef CONFIG_BPF_SYSCALL
 DECLARE_PER_CPU(int, bpf_prog_active);
 
+extern const struct file_operations bpf_map_fops;
+extern const struct file_operations bpf_prog_fops;
+
 #define BPF_PROG_TYPE(_id, _ops) \
 	extern const struct bpf_verifier_ops _ops;
 #define BPF_MAP_TYPE(_id, _ops) \
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 7161d8e7ee79..517dea60b87b 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1385,6 +1385,19 @@
  * @bpf_prog_free_security:
  *	Clean up the security information stored inside bpf prog.
  *
+ * @bpf_map_file:
+ *	When creating a bpf map fd, set up the file security information with
+ *	the bpf security information stored in the map struct. So when the map
+ *	fd is passed between processes, the security module can directly read
+ *	the security information from file security struct rather than the bpf
+ *	security struct.
+ *
+ * @bpf_prog_file:
+ *	When creating a bpf prog fd, set up the file security information with
+ *	the bpf security information stored in the prog struct. So when the prog
+ *	fd is passed between processes, the security module can directly read
+ *	the security information from file security struct rather than the bpf
+ *	security struct.
  */
 union security_list_options {
 	int (*binder_set_context_mgr)(struct task_struct *mgr);
@@ -1726,6 +1739,8 @@ union security_list_options {
 	void (*bpf_map_free_security)(struct bpf_map *map);
 	int (*bpf_prog_alloc_security)(struct bpf_prog_aux *aux);
 	void (*bpf_prog_free_security)(struct bpf_prog_aux *aux);
+	void (*bpf_map_file)(struct bpf_map *map, struct file *file);
+	void (*bpf_prog_file)(struct bpf_prog_aux *aux, struct file *file);
 #endif /* CONFIG_BPF_SYSCALL */
 };
 
@@ -1954,6 +1969,8 @@ struct security_hook_heads {
 	struct list_head bpf_map_free_security;
 	struct list_head bpf_prog_alloc_security;
 	struct list_head bpf_prog_free_security;
+	struct list_head bpf_map_file;
+	struct list_head bpf_prog_file;
 #endif /* CONFIG_BPF_SYSCALL */
 } __randomize_layout;
 
diff --git a/include/linux/security.h b/include/linux/security.h
index 18800b0911e5..57573b794e2d 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1740,6 +1740,8 @@ extern int security_bpf_map_alloc(struct bpf_map *map);
 extern void security_bpf_map_free(struct bpf_map *map);
 extern int security_bpf_prog_alloc(struct bpf_prog_aux *aux);
 extern void security_bpf_prog_free(struct bpf_prog_aux *aux);
+extern void security_bpf_map_file(struct bpf_map *map, struct file *file);
+extern void security_bpf_prog_file(struct bpf_prog_aux *aux, struct file *file);
 #else
 static inline int security_bpf(int cmd, union bpf_attr *attr,
 					     unsigned int size)
@@ -1772,6 +1774,13 @@ static inline int security_bpf_prog_alloc(struct bpf_prog_aux *aux)
 
 static inline void security_bpf_prog_free(struct bpf_prog_aux *aux)
 { }
+
+static inline void security_bpf_map_file(struct bpf_map *map, struct file *file)
+{ }
+
+static inline void security_bpf_prog_file(struct bpf_prog_aux *aux,
+					  struct file *file)
+{ }
 #endif /* CONFIG_SECURITY */
 #endif /* CONFIG_BPF_SYSCALL */
 
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 1cf31ddd7616..b144181d3f3a 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -313,7 +313,7 @@ static ssize_t bpf_dummy_write(struct file *filp, const char __user *buf,
 	return -EINVAL;
 }
 
-static const struct file_operations bpf_map_fops = {
+const struct file_operations bpf_map_fops = {
 #ifdef CONFIG_PROC_FS
 	.show_fdinfo	= bpf_map_show_fdinfo,
 #endif
@@ -964,7 +964,7 @@ static void bpf_prog_show_fdinfo(struct seq_file *m, struct file *filp)
 }
 #endif
 
-static const struct file_operations bpf_prog_fops = {
+const struct file_operations bpf_prog_fops = {
 #ifdef CONFIG_PROC_FS
 	.show_fdinfo	= bpf_prog_show_fdinfo,
 #endif
diff --git a/security/security.c b/security/security.c
index 1cd8526cb0b7..dacf649b8cfa 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1734,4 +1734,12 @@ void security_bpf_prog_free(struct bpf_prog_aux *aux)
 {
 	call_void_hook(bpf_prog_free_security, aux);
 }
+void security_bpf_map_file(struct bpf_map *map, struct file *file)
+{
+	call_void_hook(bpf_map_file, map, file);
+}
+void security_bpf_prog_file(struct bpf_prog_aux *aux, struct file *file)
+{
+	call_void_hook(bpf_prog_file, aux, file);
+}
 #endif /* CONFIG_BPF_SYSCALL */
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 41aba4e3d57c..fea88655e0ee 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1815,6 +1815,10 @@ static inline int file_path_has_perm(const struct cred *cred,
 	return inode_has_perm(cred, file_inode(file), av, &ad);
 }
 
+#ifdef CONFIG_BPF_SYSCALL
+static int bpf_file_check(struct file *file, u32 sid);
+#endif
+
 /* Check whether a task can use an open file descriptor to
    access an inode in a given way.  Check access to the
    descriptor itself, and then use dentry_has_perm to
@@ -1845,6 +1849,12 @@ static int file_has_perm(const struct cred *cred,
 			goto out;
 	}
 
+#ifdef CONFIG_BPF_SYSCALL
+	rc = bpf_file_check(file, cred_sid(cred));
+	if (rc)
+		goto out;
+#endif
+
 	/* av is zero if only checking access to the descriptor. */
 	rc = 0;
 	if (av)
@@ -2165,6 +2175,12 @@ static int selinux_binder_transfer_file(struct task_struct *from,
 			return rc;
 	}
 
+#ifdef CONFIG_BPF_SYSCALL
+	rc = bpf_file_check(file, sid);
+	if (rc)
+		return rc;
+#endif
+
 	if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
 		return 0;
 
@@ -6288,6 +6304,33 @@ static u32 bpf_map_fmode_to_av(fmode_t fmode)
 	return av;
 }
 
+/* This function will check the file pass through unix socket or binder to see
+ * if it is a bpf related object. And apply correspinding checks on the bpf
+ * object based on the type. The bpf maps and programs, not like other files and
+ * socket, are using a shared anonymous inode inside the kernel as their inode.
+ * So checking that inode cannot identify if the process have privilege to
+ * access the bpf object and that's why we have to add this additional check in
+ * selinux_file_receive and selinux_binder_transfer_files.
+ */
+static int bpf_file_check(struct file *file, u32 sid)
+{
+	struct file_security_struct *fsec = file->f_security;
+	int ret;
+
+	if (file->f_op == &bpf_map_fops) {
+		ret = avc_has_perm(sid, fsec->sid, SECCLASS_BPF_MAP,
+				   bpf_map_fmode_to_av(file->f_mode), NULL);
+		if (ret)
+			return ret;
+	} else if (file->f_op == &bpf_prog_fops) {
+		ret = avc_has_perm(sid, fsec->sid, SECCLASS_BPF_PROG,
+				   BPF_PROG__USE, NULL);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
 static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
 {
 	u32 sid = current_sid();
@@ -6351,6 +6394,22 @@ static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
 	aux->security = NULL;
 	kfree(bpfsec);
 }
+
+static void selinux_bpf_map_file(struct bpf_map *map, struct file *file)
+{
+	struct bpf_security_struct *bpfsec = map->security;
+	struct file_security_struct *fsec = file->f_security;
+
+	fsec->sid = bpfsec->sid;
+}
+
+static void selinux_bpf_prog_file(struct bpf_prog_aux *aux, struct file *file)
+{
+	struct bpf_security_struct *bpfsec = aux->security;
+	struct file_security_struct *fsec = file->f_security;
+
+	fsec->sid = bpfsec->sid;
+}
 #endif
 
 static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
@@ -6581,6 +6640,8 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(bpf_prog_alloc_security, selinux_bpf_prog_alloc),
 	LSM_HOOK_INIT(bpf_map_free_security, selinux_bpf_map_free),
 	LSM_HOOK_INIT(bpf_prog_free_security, selinux_bpf_prog_free),
+	LSM_HOOK_INIT(bpf_map_file, selinux_bpf_map_file),
+	LSM_HOOK_INIT(bpf_prog_file, selinux_bpf_prog_file),
 #endif
 };
 
-- 
2.14.2.920.gcf0c67979c-goog

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 39+ messages in thread

* Re: [PATCH net-next v2 1/5] bpf: Add file mode configuration into bpf maps
  2017-10-09 22:20   ` Chenbo Feng
@ 2017-10-09 23:07     ` Alexei Starovoitov
  -1 siblings, 0 replies; 39+ messages in thread
From: Alexei Starovoitov @ 2017-10-09 23:07 UTC (permalink / raw)
  To: Chenbo Feng
  Cc: linux-security-module, netdev, SELinux, Jeffrey Vander Stoep,
	lorenzo, Daniel Borkmann, Stephen Smalley, Chenbo Feng

On Mon, Oct 09, 2017 at 03:20:24PM -0700, Chenbo Feng wrote:
> From: Chenbo Feng <fengc@google.com>
> 
> Introduce the map read/write flags to the eBPF syscalls that returns the
> map fd. The flags is used to set up the file mode when construct a new
> file descriptor for bpf maps. To not break the backward capability, the
> f_flags is set to O_RDWR if the flag passed by syscall is 0. Otherwise
> it should be O_RDONLY or O_WRONLY. When the userspace want to modify or
> read the map content, it will check the file mode to see if it is
> allowed to make the change.
> 
> Signed-off-by: Chenbo Feng <fengc@google.com>
> Acked-by: Alexei Starovoitov <ast@kernel.org>
> ---
>  include/linux/bpf.h      |  6 ++--
>  include/uapi/linux/bpf.h |  6 ++++
>  kernel/bpf/arraymap.c    |  7 +++--
>  kernel/bpf/devmap.c      |  5 ++-
>  kernel/bpf/hashtab.c     |  5 +--
>  kernel/bpf/inode.c       | 15 ++++++---
>  kernel/bpf/lpm_trie.c    |  3 +-
>  kernel/bpf/sockmap.c     |  5 ++-
>  kernel/bpf/stackmap.c    |  5 ++-
>  kernel/bpf/syscall.c     | 80 +++++++++++++++++++++++++++++++++++++++++++-----
>  10 files changed, 114 insertions(+), 23 deletions(-)
> 
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index bc7da2ddfcaf..0e9ca2555d7f 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
> @@ -308,11 +308,11 @@ void bpf_map_area_free(void *base);
>  
>  extern int sysctl_unprivileged_bpf_disabled;
>  
> -int bpf_map_new_fd(struct bpf_map *map);
> +int bpf_map_new_fd(struct bpf_map *map, int flags);
>  int bpf_prog_new_fd(struct bpf_prog *prog);
>  
>  int bpf_obj_pin_user(u32 ufd, const char __user *pathname);
> -int bpf_obj_get_user(const char __user *pathname);
> +int bpf_obj_get_user(const char __user *pathname, int flags);
>  
>  int bpf_percpu_hash_copy(struct bpf_map *map, void *key, void *value);
>  int bpf_percpu_array_copy(struct bpf_map *map, void *key, void *value);
> @@ -331,6 +331,8 @@ int bpf_fd_htab_map_update_elem(struct bpf_map *map, struct file *map_file,
>  				void *key, void *value, u64 map_flags);
>  int bpf_fd_htab_map_lookup_elem(struct bpf_map *map, void *key, u32 *value);
>  
> +int bpf_get_file_flag(int flags);
> +
>  /* memcpy that is used with 8-byte aligned pointers, power-of-8 size and
>   * forced to use 'long' read/writes to try to atomically copy long counters.
>   * Best-effort only.  No barriers here, since it _will_ race with concurrent
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index 6db9e1d679cd..9cb50a228c39 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -217,6 +217,10 @@ enum bpf_attach_type {
>  
>  #define BPF_OBJ_NAME_LEN 16U
>  
> +/* Flags for accessing BPF object */
> +#define BPF_F_RDONLY		(1U << 3)
> +#define BPF_F_WRONLY		(1U << 4)
> +
>  union bpf_attr {
>  	struct { /* anonymous struct used by BPF_MAP_CREATE command */
>  		__u32	map_type;	/* one of enum bpf_map_type */
> @@ -259,6 +263,7 @@ union bpf_attr {
>  	struct { /* anonymous struct used by BPF_OBJ_* commands */
>  		__aligned_u64	pathname;
>  		__u32		bpf_fd;
> +		__u32		file_flags;
>  	};
>  
>  	struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */
> @@ -286,6 +291,7 @@ union bpf_attr {
>  			__u32		map_id;
>  		};
>  		__u32		next_id;
> +		__u32		open_flags;
>  	};
>  
>  	struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */
> diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
> index 68d866628be0..f869e48ef2f6 100644
> --- a/kernel/bpf/arraymap.c
> +++ b/kernel/bpf/arraymap.c
> @@ -19,6 +19,9 @@
>  
>  #include "map_in_map.h"
>  
> +#define ARRAY_CREATE_FLAG_MASK \
> +	(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
> +
>  static void bpf_array_free_percpu(struct bpf_array *array)
>  {
>  	int i;
> @@ -56,8 +59,8 @@ static struct bpf_map *array_map_alloc(union bpf_attr *attr)
>  
>  	/* check sanity of attributes */
>  	if (attr->max_entries == 0 || attr->key_size != 4 ||
> -	    attr->value_size == 0 || attr->map_flags & ~BPF_F_NUMA_NODE ||
> -	    (percpu && numa_node != NUMA_NO_NODE))
> +	    attr->value_size == 0 || attr->map_flags &
> +	    ~ARRAY_CREATE_FLAG_MASK || (percpu && numa_node != NUMA_NO_NODE))

that's very non-standard way of breaking lines.
Did you run checkpatch ? did it complain?


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH net-next v2 1/5] bpf: Add file mode configuration into bpf maps
@ 2017-10-09 23:07     ` Alexei Starovoitov
  0 siblings, 0 replies; 39+ messages in thread
From: Alexei Starovoitov @ 2017-10-09 23:07 UTC (permalink / raw)
  To: linux-security-module

On Mon, Oct 09, 2017 at 03:20:24PM -0700, Chenbo Feng wrote:
> From: Chenbo Feng <fengc@google.com>
> 
> Introduce the map read/write flags to the eBPF syscalls that returns the
> map fd. The flags is used to set up the file mode when construct a new
> file descriptor for bpf maps. To not break the backward capability, the
> f_flags is set to O_RDWR if the flag passed by syscall is 0. Otherwise
> it should be O_RDONLY or O_WRONLY. When the userspace want to modify or
> read the map content, it will check the file mode to see if it is
> allowed to make the change.
> 
> Signed-off-by: Chenbo Feng <fengc@google.com>
> Acked-by: Alexei Starovoitov <ast@kernel.org>
> ---
>  include/linux/bpf.h      |  6 ++--
>  include/uapi/linux/bpf.h |  6 ++++
>  kernel/bpf/arraymap.c    |  7 +++--
>  kernel/bpf/devmap.c      |  5 ++-
>  kernel/bpf/hashtab.c     |  5 +--
>  kernel/bpf/inode.c       | 15 ++++++---
>  kernel/bpf/lpm_trie.c    |  3 +-
>  kernel/bpf/sockmap.c     |  5 ++-
>  kernel/bpf/stackmap.c    |  5 ++-
>  kernel/bpf/syscall.c     | 80 +++++++++++++++++++++++++++++++++++++++++++-----
>  10 files changed, 114 insertions(+), 23 deletions(-)
> 
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index bc7da2ddfcaf..0e9ca2555d7f 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
> @@ -308,11 +308,11 @@ void bpf_map_area_free(void *base);
>  
>  extern int sysctl_unprivileged_bpf_disabled;
>  
> -int bpf_map_new_fd(struct bpf_map *map);
> +int bpf_map_new_fd(struct bpf_map *map, int flags);
>  int bpf_prog_new_fd(struct bpf_prog *prog);
>  
>  int bpf_obj_pin_user(u32 ufd, const char __user *pathname);
> -int bpf_obj_get_user(const char __user *pathname);
> +int bpf_obj_get_user(const char __user *pathname, int flags);
>  
>  int bpf_percpu_hash_copy(struct bpf_map *map, void *key, void *value);
>  int bpf_percpu_array_copy(struct bpf_map *map, void *key, void *value);
> @@ -331,6 +331,8 @@ int bpf_fd_htab_map_update_elem(struct bpf_map *map, struct file *map_file,
>  				void *key, void *value, u64 map_flags);
>  int bpf_fd_htab_map_lookup_elem(struct bpf_map *map, void *key, u32 *value);
>  
> +int bpf_get_file_flag(int flags);
> +
>  /* memcpy that is used with 8-byte aligned pointers, power-of-8 size and
>   * forced to use 'long' read/writes to try to atomically copy long counters.
>   * Best-effort only.  No barriers here, since it _will_ race with concurrent
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index 6db9e1d679cd..9cb50a228c39 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -217,6 +217,10 @@ enum bpf_attach_type {
>  
>  #define BPF_OBJ_NAME_LEN 16U
>  
> +/* Flags for accessing BPF object */
> +#define BPF_F_RDONLY		(1U << 3)
> +#define BPF_F_WRONLY		(1U << 4)
> +
>  union bpf_attr {
>  	struct { /* anonymous struct used by BPF_MAP_CREATE command */
>  		__u32	map_type;	/* one of enum bpf_map_type */
> @@ -259,6 +263,7 @@ union bpf_attr {
>  	struct { /* anonymous struct used by BPF_OBJ_* commands */
>  		__aligned_u64	pathname;
>  		__u32		bpf_fd;
> +		__u32		file_flags;
>  	};
>  
>  	struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */
> @@ -286,6 +291,7 @@ union bpf_attr {
>  			__u32		map_id;
>  		};
>  		__u32		next_id;
> +		__u32		open_flags;
>  	};
>  
>  	struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */
> diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
> index 68d866628be0..f869e48ef2f6 100644
> --- a/kernel/bpf/arraymap.c
> +++ b/kernel/bpf/arraymap.c
> @@ -19,6 +19,9 @@
>  
>  #include "map_in_map.h"
>  
> +#define ARRAY_CREATE_FLAG_MASK \
> +	(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
> +
>  static void bpf_array_free_percpu(struct bpf_array *array)
>  {
>  	int i;
> @@ -56,8 +59,8 @@ static struct bpf_map *array_map_alloc(union bpf_attr *attr)
>  
>  	/* check sanity of attributes */
>  	if (attr->max_entries == 0 || attr->key_size != 4 ||
> -	    attr->value_size == 0 || attr->map_flags & ~BPF_F_NUMA_NODE ||
> -	    (percpu && numa_node != NUMA_NO_NODE))
> +	    attr->value_size == 0 || attr->map_flags &
> +	    ~ARRAY_CREATE_FLAG_MASK || (percpu && numa_node != NUMA_NO_NODE))

that's very non-standard way of breaking lines.
Did you run checkpatch ? did it complain?

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH net-next v2 1/5] bpf: Add file mode configuration into bpf maps
  2017-10-09 23:07     ` Alexei Starovoitov
@ 2017-10-09 23:31       ` Chenbo Feng
  -1 siblings, 0 replies; 39+ messages in thread
From: Chenbo Feng @ 2017-10-09 23:31 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: Chenbo Feng, linux-security-module, netdev, SELinux,
	Jeffrey Vander Stoep, Lorenzo Colitti, Daniel Borkmann,
	Stephen Smalley

On Mon, Oct 9, 2017 at 4:07 PM, Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
> On Mon, Oct 09, 2017 at 03:20:24PM -0700, Chenbo Feng wrote:
>> From: Chenbo Feng <fengc@google.com>
>>
>> Introduce the map read/write flags to the eBPF syscalls that returns the
>> map fd. The flags is used to set up the file mode when construct a new
>> file descriptor for bpf maps. To not break the backward capability, the
>> f_flags is set to O_RDWR if the flag passed by syscall is 0. Otherwise
>> it should be O_RDONLY or O_WRONLY. When the userspace want to modify or
>> read the map content, it will check the file mode to see if it is
>> allowed to make the change.
>>
>> Signed-off-by: Chenbo Feng <fengc@google.com>
>> Acked-by: Alexei Starovoitov <ast@kernel.org>
>> ---
>>  include/linux/bpf.h      |  6 ++--
>>  include/uapi/linux/bpf.h |  6 ++++
>>  kernel/bpf/arraymap.c    |  7 +++--
>>  kernel/bpf/devmap.c      |  5 ++-
>>  kernel/bpf/hashtab.c     |  5 +--
>>  kernel/bpf/inode.c       | 15 ++++++---
>>  kernel/bpf/lpm_trie.c    |  3 +-
>>  kernel/bpf/sockmap.c     |  5 ++-
>>  kernel/bpf/stackmap.c    |  5 ++-
>>  kernel/bpf/syscall.c     | 80 +++++++++++++++++++++++++++++++++++++++++++-----
>>  10 files changed, 114 insertions(+), 23 deletions(-)
>>
>> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
>> index bc7da2ddfcaf..0e9ca2555d7f 100644
>> --- a/include/linux/bpf.h
>> +++ b/include/linux/bpf.h
>> @@ -308,11 +308,11 @@ void bpf_map_area_free(void *base);
>>
>>  extern int sysctl_unprivileged_bpf_disabled;
>>
>> -int bpf_map_new_fd(struct bpf_map *map);
>> +int bpf_map_new_fd(struct bpf_map *map, int flags);
>>  int bpf_prog_new_fd(struct bpf_prog *prog);
>>
>>  int bpf_obj_pin_user(u32 ufd, const char __user *pathname);
>> -int bpf_obj_get_user(const char __user *pathname);
>> +int bpf_obj_get_user(const char __user *pathname, int flags);
>>
>>  int bpf_percpu_hash_copy(struct bpf_map *map, void *key, void *value);
>>  int bpf_percpu_array_copy(struct bpf_map *map, void *key, void *value);
>> @@ -331,6 +331,8 @@ int bpf_fd_htab_map_update_elem(struct bpf_map *map, struct file *map_file,
>>                               void *key, void *value, u64 map_flags);
>>  int bpf_fd_htab_map_lookup_elem(struct bpf_map *map, void *key, u32 *value);
>>
>> +int bpf_get_file_flag(int flags);
>> +
>>  /* memcpy that is used with 8-byte aligned pointers, power-of-8 size and
>>   * forced to use 'long' read/writes to try to atomically copy long counters.
>>   * Best-effort only.  No barriers here, since it _will_ race with concurrent
>> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
>> index 6db9e1d679cd..9cb50a228c39 100644
>> --- a/include/uapi/linux/bpf.h
>> +++ b/include/uapi/linux/bpf.h
>> @@ -217,6 +217,10 @@ enum bpf_attach_type {
>>
>>  #define BPF_OBJ_NAME_LEN 16U
>>
>> +/* Flags for accessing BPF object */
>> +#define BPF_F_RDONLY         (1U << 3)
>> +#define BPF_F_WRONLY         (1U << 4)
>> +
>>  union bpf_attr {
>>       struct { /* anonymous struct used by BPF_MAP_CREATE command */
>>               __u32   map_type;       /* one of enum bpf_map_type */
>> @@ -259,6 +263,7 @@ union bpf_attr {
>>       struct { /* anonymous struct used by BPF_OBJ_* commands */
>>               __aligned_u64   pathname;
>>               __u32           bpf_fd;
>> +             __u32           file_flags;
>>       };
>>
>>       struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */
>> @@ -286,6 +291,7 @@ union bpf_attr {
>>                       __u32           map_id;
>>               };
>>               __u32           next_id;
>> +             __u32           open_flags;
>>       };
>>
>>       struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */
>> diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
>> index 68d866628be0..f869e48ef2f6 100644
>> --- a/kernel/bpf/arraymap.c
>> +++ b/kernel/bpf/arraymap.c
>> @@ -19,6 +19,9 @@
>>
>>  #include "map_in_map.h"
>>
>> +#define ARRAY_CREATE_FLAG_MASK \
>> +     (BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
>> +
>>  static void bpf_array_free_percpu(struct bpf_array *array)
>>  {
>>       int i;
>> @@ -56,8 +59,8 @@ static struct bpf_map *array_map_alloc(union bpf_attr *attr)
>>
>>       /* check sanity of attributes */
>>       if (attr->max_entries == 0 || attr->key_size != 4 ||
>> -         attr->value_size == 0 || attr->map_flags & ~BPF_F_NUMA_NODE ||
>> -         (percpu && numa_node != NUMA_NO_NODE))
>> +         attr->value_size == 0 || attr->map_flags &
>> +         ~ARRAY_CREATE_FLAG_MASK || (percpu && numa_node != NUMA_NO_NODE))
>
> that's very non-standard way of breaking lines.
> Did you run checkpatch ? did it complain?
>
Will fix in next revision, checkpatch didn't say anything about
this....0 error and 0 warning for this patch series.

^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH net-next v2 1/5] bpf: Add file mode configuration into bpf maps
@ 2017-10-09 23:31       ` Chenbo Feng
  0 siblings, 0 replies; 39+ messages in thread
From: Chenbo Feng @ 2017-10-09 23:31 UTC (permalink / raw)
  To: linux-security-module

On Mon, Oct 9, 2017 at 4:07 PM, Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
> On Mon, Oct 09, 2017 at 03:20:24PM -0700, Chenbo Feng wrote:
>> From: Chenbo Feng <fengc@google.com>
>>
>> Introduce the map read/write flags to the eBPF syscalls that returns the
>> map fd. The flags is used to set up the file mode when construct a new
>> file descriptor for bpf maps. To not break the backward capability, the
>> f_flags is set to O_RDWR if the flag passed by syscall is 0. Otherwise
>> it should be O_RDONLY or O_WRONLY. When the userspace want to modify or
>> read the map content, it will check the file mode to see if it is
>> allowed to make the change.
>>
>> Signed-off-by: Chenbo Feng <fengc@google.com>
>> Acked-by: Alexei Starovoitov <ast@kernel.org>
>> ---
>>  include/linux/bpf.h      |  6 ++--
>>  include/uapi/linux/bpf.h |  6 ++++
>>  kernel/bpf/arraymap.c    |  7 +++--
>>  kernel/bpf/devmap.c      |  5 ++-
>>  kernel/bpf/hashtab.c     |  5 +--
>>  kernel/bpf/inode.c       | 15 ++++++---
>>  kernel/bpf/lpm_trie.c    |  3 +-
>>  kernel/bpf/sockmap.c     |  5 ++-
>>  kernel/bpf/stackmap.c    |  5 ++-
>>  kernel/bpf/syscall.c     | 80 +++++++++++++++++++++++++++++++++++++++++++-----
>>  10 files changed, 114 insertions(+), 23 deletions(-)
>>
>> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
>> index bc7da2ddfcaf..0e9ca2555d7f 100644
>> --- a/include/linux/bpf.h
>> +++ b/include/linux/bpf.h
>> @@ -308,11 +308,11 @@ void bpf_map_area_free(void *base);
>>
>>  extern int sysctl_unprivileged_bpf_disabled;
>>
>> -int bpf_map_new_fd(struct bpf_map *map);
>> +int bpf_map_new_fd(struct bpf_map *map, int flags);
>>  int bpf_prog_new_fd(struct bpf_prog *prog);
>>
>>  int bpf_obj_pin_user(u32 ufd, const char __user *pathname);
>> -int bpf_obj_get_user(const char __user *pathname);
>> +int bpf_obj_get_user(const char __user *pathname, int flags);
>>
>>  int bpf_percpu_hash_copy(struct bpf_map *map, void *key, void *value);
>>  int bpf_percpu_array_copy(struct bpf_map *map, void *key, void *value);
>> @@ -331,6 +331,8 @@ int bpf_fd_htab_map_update_elem(struct bpf_map *map, struct file *map_file,
>>                               void *key, void *value, u64 map_flags);
>>  int bpf_fd_htab_map_lookup_elem(struct bpf_map *map, void *key, u32 *value);
>>
>> +int bpf_get_file_flag(int flags);
>> +
>>  /* memcpy that is used with 8-byte aligned pointers, power-of-8 size and
>>   * forced to use 'long' read/writes to try to atomically copy long counters.
>>   * Best-effort only.  No barriers here, since it _will_ race with concurrent
>> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
>> index 6db9e1d679cd..9cb50a228c39 100644
>> --- a/include/uapi/linux/bpf.h
>> +++ b/include/uapi/linux/bpf.h
>> @@ -217,6 +217,10 @@ enum bpf_attach_type {
>>
>>  #define BPF_OBJ_NAME_LEN 16U
>>
>> +/* Flags for accessing BPF object */
>> +#define BPF_F_RDONLY         (1U << 3)
>> +#define BPF_F_WRONLY         (1U << 4)
>> +
>>  union bpf_attr {
>>       struct { /* anonymous struct used by BPF_MAP_CREATE command */
>>               __u32   map_type;       /* one of enum bpf_map_type */
>> @@ -259,6 +263,7 @@ union bpf_attr {
>>       struct { /* anonymous struct used by BPF_OBJ_* commands */
>>               __aligned_u64   pathname;
>>               __u32           bpf_fd;
>> +             __u32           file_flags;
>>       };
>>
>>       struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */
>> @@ -286,6 +291,7 @@ union bpf_attr {
>>                       __u32           map_id;
>>               };
>>               __u32           next_id;
>> +             __u32           open_flags;
>>       };
>>
>>       struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */
>> diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
>> index 68d866628be0..f869e48ef2f6 100644
>> --- a/kernel/bpf/arraymap.c
>> +++ b/kernel/bpf/arraymap.c
>> @@ -19,6 +19,9 @@
>>
>>  #include "map_in_map.h"
>>
>> +#define ARRAY_CREATE_FLAG_MASK \
>> +     (BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
>> +
>>  static void bpf_array_free_percpu(struct bpf_array *array)
>>  {
>>       int i;
>> @@ -56,8 +59,8 @@ static struct bpf_map *array_map_alloc(union bpf_attr *attr)
>>
>>       /* check sanity of attributes */
>>       if (attr->max_entries == 0 || attr->key_size != 4 ||
>> -         attr->value_size == 0 || attr->map_flags & ~BPF_F_NUMA_NODE ||
>> -         (percpu && numa_node != NUMA_NO_NODE))
>> +         attr->value_size == 0 || attr->map_flags &
>> +         ~ARRAY_CREATE_FLAG_MASK || (percpu && numa_node != NUMA_NO_NODE))
>
> that's very non-standard way of breaking lines.
> Did you run checkpatch ? did it complain?
>
Will fix in next revision, checkpatch didn't say anything about
this....0 error and 0 warning for this patch series.
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH net-next v2 4/5] selinux: bpf: Add selinux check for eBPF syscall operations
  2017-10-09 22:20   ` Chenbo Feng
@ 2017-10-10 14:18     ` Stephen Smalley
  -1 siblings, 0 replies; 39+ messages in thread
From: Stephen Smalley @ 2017-10-10 14:18 UTC (permalink / raw)
  To: Chenbo Feng, linux-security-module, netdev, SELinux
  Cc: Jeffrey Vander Stoep, Alexei Starovoitov, lorenzo,
	Daniel Borkmann, Chenbo Feng

On Mon, 2017-10-09 at 15:20 -0700, Chenbo Feng wrote:
> From: Chenbo Feng <fengc@google.com>
> 
> Implement the actual checks introduced to eBPF related syscalls. This
> implementation use the security field inside bpf object to store a
> sid that
> identify the bpf object. And when processes try to access the object,
> selinux will check if processes have the right privileges. The
> creation
> of eBPF object are also checked at the general bpf check hook and new
> cmd introduced to eBPF domain can also be checked there.
> 
> Signed-off-by: Chenbo Feng <fengc@google.com>
> Acked-by: Alexei Starovoitov <ast@kernel.org>
> ---
>  security/selinux/hooks.c            | 111
> ++++++++++++++++++++++++++++++++++++
>  security/selinux/include/classmap.h |   2 +
>  security/selinux/include/objsec.h   |   4 ++
>  3 files changed, 117 insertions(+)
> 
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index f5d304736852..41aba4e3d57c 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -85,6 +85,7 @@
>  #include <linux/export.h>
>  #include <linux/msg.h>
>  #include <linux/shm.h>
> +#include <linux/bpf.h>
>  
>  #include "avc.h"
>  #include "objsec.h"
> @@ -6252,6 +6253,106 @@ static void selinux_ib_free_security(void
> *ib_sec)
>  }
>  #endif
>  
> +#ifdef CONFIG_BPF_SYSCALL
> +static int selinux_bpf(int cmd, union bpf_attr *attr,
> +				     unsigned int size)
> +{
> +	u32 sid = current_sid();
> +	int ret;
> +
> +	switch (cmd) {
> +	case BPF_MAP_CREATE:
> +		ret = avc_has_perm(sid, sid, SECCLASS_BPF_MAP,
> BPF_MAP__CREATE,
> +				   NULL);
> +		break;
> +	case BPF_PROG_LOAD:
> +		ret = avc_has_perm(sid, sid, SECCLASS_BPF_PROG,
> BPF_PROG__LOAD,
> +				   NULL);
> +		break;
> +	default:
> +		ret = 0;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static u32 bpf_map_fmode_to_av(fmode_t fmode)
> +{
> +	u32 av = 0;
> +
> +	if (f_mode & FMODE_READ)
> +		av |= BPF_MAP__READ;
> +	if (f_mode & FMODE_WRITE)
> +		av |= BPF_MAP__WRITE;
> +	return av;
> +}
> +
> +static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
> +{
> +	u32 sid = current_sid();
> +	struct bpf_security_struct *bpfsec;
> +
> +	bpfsec = map->security;
> +	return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF_MAP,
> +			    bpf_map_fmode_to_av(fmode), NULL);
> +}
> +
> +static int selinux_bpf_prog(struct bpf_prog *prog)
> +{
> +	u32 sid = current_sid();
> +	struct bpf_security_struct *bpfsec;
> +
> +	bpfsec = prog->aux->security;
> +	return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF_PROG,
> +			    BPF_PROG__USE, NULL);
> +}
> +
> +static int selinux_bpf_map_alloc(struct bpf_map *map)
> +{
> +	struct bpf_security_struct *bpfsec;
> +
> +	bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> +	if (!bpfsec)
> +		return -ENOMEM;
> +
> +	bpfsec->sid = current_sid();
> +	map->security = bpfsec;
> +
> +	return 0;
> +}
> +
> +static void selinux_bpf_map_free(struct bpf_map *map)
> +{
> +	struct bpf_security_struct *bpfsec = map->security;
> +
> +	map->security = NULL;
> +	kfree(bpfsec);
> +}
> +
> +static int selinux_bpf_prog_alloc(struct bpf_prog_aux *aux)
> +{
> +	struct bpf_security_struct *bpfsec;
> +
> +	bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> +	if (!bpfsec)
> +		return -ENOMEM;
> +
> +	bpfsec->sid = current_sid();
> +	aux->security = bpfsec;
> +
> +	return 0;
> +}
> +
> +static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
> +{
> +	struct bpf_security_struct *bpfsec = aux->security;
> +
> +	aux->security = NULL;
> +	kfree(bpfsec);
> +}
> +#endif
> +
>  static struct security_hook_list selinux_hooks[] __lsm_ro_after_init
> = {
>  	LSM_HOOK_INIT(binder_set_context_mgr,
> selinux_binder_set_context_mgr),
>  	LSM_HOOK_INIT(binder_transaction,
> selinux_binder_transaction),
> @@ -6471,6 +6572,16 @@ static struct security_hook_list
> selinux_hooks[] __lsm_ro_after_init = {
>  	LSM_HOOK_INIT(audit_rule_match, selinux_audit_rule_match),
>  	LSM_HOOK_INIT(audit_rule_free, selinux_audit_rule_free),
>  #endif
> +
> +#ifdef CONFIG_BPF_SYSCALL
> +	LSM_HOOK_INIT(bpf, selinux_bpf),
> +	LSM_HOOK_INIT(bpf_map, selinux_bpf_map),
> +	LSM_HOOK_INIT(bpf_prog, selinux_bpf_prog),
> +	LSM_HOOK_INIT(bpf_map_alloc_security,
> selinux_bpf_map_alloc),
> +	LSM_HOOK_INIT(bpf_prog_alloc_security,
> selinux_bpf_prog_alloc),
> +	LSM_HOOK_INIT(bpf_map_free_security, selinux_bpf_map_free),
> +	LSM_HOOK_INIT(bpf_prog_free_security,
> selinux_bpf_prog_free),
> +#endif
>  };
>  
>  static __init int selinux_init(void)
> diff --git a/security/selinux/include/classmap.h
> b/security/selinux/include/classmap.h
> index 35ffb29a69cb..7253c5eea59c 100644
> --- a/security/selinux/include/classmap.h
> +++ b/security/selinux/include/classmap.h
> @@ -237,6 +237,8 @@ struct security_class_mapping secclass_map[] = {
>  	  { "access", NULL } },
>  	{ "infiniband_endport",
>  	  { "manage_subnet", NULL } },
> +	{ "bpf_map", {"create", "read", "write"} },
> +	{ "bpf_prog", {"load", "use"} },

Again I have to ask: do you truly need/want two separate classes, or
would a single class with distinct permissions suffice, ala:
        { "bpf", { "create_map", "read_map", "write_map", "prog_load",
"prog_use" } },

and then allow A self:bpf { create_map read_map write_map prog_load
prog_use }; would be stored in a single policy avtab rule, and be
cached in a single AVC entry.

>  	{ NULL }
>    };
>  
> diff --git a/security/selinux/include/objsec.h
> b/security/selinux/include/objsec.h
> index 1649cd18eb0b..3d54468ce334 100644
> --- a/security/selinux/include/objsec.h
> +++ b/security/selinux/include/objsec.h
> @@ -150,6 +150,10 @@ struct pkey_security_struct {
>  	u32	sid;	/* SID of pkey */
>  };
>  
> +struct bpf_security_struct {
> +	u32 sid;  /*SID of bpf obj creater*/
> +};
> +
>  extern unsigned int selinux_checkreqprot;
>  
>  #endif /* _SELINUX_OBJSEC_H_ */

^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH net-next v2 4/5] selinux: bpf: Add selinux check for eBPF syscall operations
@ 2017-10-10 14:18     ` Stephen Smalley
  0 siblings, 0 replies; 39+ messages in thread
From: Stephen Smalley @ 2017-10-10 14:18 UTC (permalink / raw)
  To: linux-security-module

On Mon, 2017-10-09 at 15:20 -0700, Chenbo Feng wrote:
> From: Chenbo Feng <fengc@google.com>
> 
> Implement the actual checks introduced to eBPF related syscalls. This
> implementation use the security field inside bpf object to store a
> sid that
> identify the bpf object. And when processes try to access the object,
> selinux will check if processes have the right privileges. The
> creation
> of eBPF object are also checked at the general bpf check hook and new
> cmd introduced to eBPF domain can also be checked there.
> 
> Signed-off-by: Chenbo Feng <fengc@google.com>
> Acked-by: Alexei Starovoitov <ast@kernel.org>
> ---
> ?security/selinux/hooks.c????????????| 111
> ++++++++++++++++++++++++++++++++++++
> ?security/selinux/include/classmap.h |???2 +
> ?security/selinux/include/objsec.h???|???4 ++
> ?3 files changed, 117 insertions(+)
> 
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index f5d304736852..41aba4e3d57c 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -85,6 +85,7 @@
> ?#include <linux/export.h>
> ?#include <linux/msg.h>
> ?#include <linux/shm.h>
> +#include <linux/bpf.h>
> ?
> ?#include "avc.h"
> ?#include "objsec.h"
> @@ -6252,6 +6253,106 @@ static void selinux_ib_free_security(void
> *ib_sec)
> ?}
> ?#endif
> ?
> +#ifdef CONFIG_BPF_SYSCALL
> +static int selinux_bpf(int cmd, union bpf_attr *attr,
> +				?????unsigned int size)
> +{
> +	u32 sid = current_sid();
> +	int ret;
> +
> +	switch (cmd) {
> +	case BPF_MAP_CREATE:
> +		ret = avc_has_perm(sid, sid, SECCLASS_BPF_MAP,
> BPF_MAP__CREATE,
> +				???NULL);
> +		break;
> +	case BPF_PROG_LOAD:
> +		ret = avc_has_perm(sid, sid, SECCLASS_BPF_PROG,
> BPF_PROG__LOAD,
> +				???NULL);
> +		break;
> +	default:
> +		ret = 0;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static u32 bpf_map_fmode_to_av(fmode_t fmode)
> +{
> +	u32 av = 0;
> +
> +	if (f_mode & FMODE_READ)
> +		av |= BPF_MAP__READ;
> +	if (f_mode & FMODE_WRITE)
> +		av |= BPF_MAP__WRITE;
> +	return av;
> +}
> +
> +static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
> +{
> +	u32 sid = current_sid();
> +	struct bpf_security_struct *bpfsec;
> +
> +	bpfsec = map->security;
> +	return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF_MAP,
> +			????bpf_map_fmode_to_av(fmode), NULL);
> +}
> +
> +static int selinux_bpf_prog(struct bpf_prog *prog)
> +{
> +	u32 sid = current_sid();
> +	struct bpf_security_struct *bpfsec;
> +
> +	bpfsec = prog->aux->security;
> +	return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF_PROG,
> +			????BPF_PROG__USE, NULL);
> +}
> +
> +static int selinux_bpf_map_alloc(struct bpf_map *map)
> +{
> +	struct bpf_security_struct *bpfsec;
> +
> +	bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> +	if (!bpfsec)
> +		return -ENOMEM;
> +
> +	bpfsec->sid = current_sid();
> +	map->security = bpfsec;
> +
> +	return 0;
> +}
> +
> +static void selinux_bpf_map_free(struct bpf_map *map)
> +{
> +	struct bpf_security_struct *bpfsec = map->security;
> +
> +	map->security = NULL;
> +	kfree(bpfsec);
> +}
> +
> +static int selinux_bpf_prog_alloc(struct bpf_prog_aux *aux)
> +{
> +	struct bpf_security_struct *bpfsec;
> +
> +	bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> +	if (!bpfsec)
> +		return -ENOMEM;
> +
> +	bpfsec->sid = current_sid();
> +	aux->security = bpfsec;
> +
> +	return 0;
> +}
> +
> +static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
> +{
> +	struct bpf_security_struct *bpfsec = aux->security;
> +
> +	aux->security = NULL;
> +	kfree(bpfsec);
> +}
> +#endif
> +
> ?static struct security_hook_list selinux_hooks[] __lsm_ro_after_init
> = {
> ?	LSM_HOOK_INIT(binder_set_context_mgr,
> selinux_binder_set_context_mgr),
> ?	LSM_HOOK_INIT(binder_transaction,
> selinux_binder_transaction),
> @@ -6471,6 +6572,16 @@ static struct security_hook_list
> selinux_hooks[] __lsm_ro_after_init = {
> ?	LSM_HOOK_INIT(audit_rule_match, selinux_audit_rule_match),
> ?	LSM_HOOK_INIT(audit_rule_free, selinux_audit_rule_free),
> ?#endif
> +
> +#ifdef CONFIG_BPF_SYSCALL
> +	LSM_HOOK_INIT(bpf, selinux_bpf),
> +	LSM_HOOK_INIT(bpf_map, selinux_bpf_map),
> +	LSM_HOOK_INIT(bpf_prog, selinux_bpf_prog),
> +	LSM_HOOK_INIT(bpf_map_alloc_security,
> selinux_bpf_map_alloc),
> +	LSM_HOOK_INIT(bpf_prog_alloc_security,
> selinux_bpf_prog_alloc),
> +	LSM_HOOK_INIT(bpf_map_free_security, selinux_bpf_map_free),
> +	LSM_HOOK_INIT(bpf_prog_free_security,
> selinux_bpf_prog_free),
> +#endif
> ?};
> ?
> ?static __init int selinux_init(void)
> diff --git a/security/selinux/include/classmap.h
> b/security/selinux/include/classmap.h
> index 35ffb29a69cb..7253c5eea59c 100644
> --- a/security/selinux/include/classmap.h
> +++ b/security/selinux/include/classmap.h
> @@ -237,6 +237,8 @@ struct security_class_mapping secclass_map[] = {
> ?	??{ "access", NULL } },
> ?	{ "infiniband_endport",
> ?	??{ "manage_subnet", NULL } },
> +	{ "bpf_map", {"create", "read", "write"} },
> +	{ "bpf_prog", {"load", "use"} },

Again I have to ask: do you truly need/want two separate classes, or
would a single class with distinct permissions suffice, ala:
????????{ "bpf", { "create_map", "read_map", "write_map", "prog_load",
"prog_use" } },

and then allow A self:bpf { create_map read_map write_map prog_load
prog_use }; would be stored in a single policy avtab rule, and be
cached in a single AVC entry.

> ?	{ NULL }
> ???};
> ?
> diff --git a/security/selinux/include/objsec.h
> b/security/selinux/include/objsec.h
> index 1649cd18eb0b..3d54468ce334 100644
> --- a/security/selinux/include/objsec.h
> +++ b/security/selinux/include/objsec.h
> @@ -150,6 +150,10 @@ struct pkey_security_struct {
> ?	u32	sid;	/* SID of pkey */
> ?};
> ?
> +struct bpf_security_struct {
> +	u32 sid;??/*SID of bpf obj creater*/
> +};
> +
> ?extern unsigned int selinux_checkreqprot;
> ?
> ?#endif /* _SELINUX_OBJSEC_H_ */
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH net-next v2 5/5] selinux: bpf: Add addtional check for bpf object file receive
  2017-10-09 22:20   ` Chenbo Feng
@ 2017-10-10 14:24     ` Stephen Smalley
  -1 siblings, 0 replies; 39+ messages in thread
From: Stephen Smalley @ 2017-10-10 14:24 UTC (permalink / raw)
  To: Chenbo Feng, linux-security-module, netdev, SELinux
  Cc: Daniel Borkmann, Chenbo Feng, Alexei Starovoitov, lorenzo

On Mon, 2017-10-09 at 15:20 -0700, Chenbo Feng wrote:
> From: Chenbo Feng <fengc@google.com>
> 
> Introduce a bpf object related check when sending and receiving files
> through unix domain socket as well as binder. It checks if the
> receiving
> process have privilege to read/write the bpf map or use the bpf
> program.
> This check is necessary because the bpf maps and programs are using a
> anonymous inode as their shared inode so the normal way of checking
> the
> files and sockets when passing between processes cannot work properly
> on
> eBPF object. This check only works when the BPF_SYSCALL is
> configured.
> The information stored inside the file security struct is the same as
> the information in bpf object security struct.
> 
> Signed-off-by: Chenbo Feng <fengc@google.com>
> ---
>  include/linux/bpf.h       |  3 +++
>  include/linux/lsm_hooks.h | 17 +++++++++++++
>  include/linux/security.h  |  9 +++++++
>  kernel/bpf/syscall.c      |  4 ++--
>  security/security.c       |  8 +++++++
>  security/selinux/hooks.c  | 61
> +++++++++++++++++++++++++++++++++++++++++++++++
>  6 files changed, 100 insertions(+), 2 deletions(-)
> 
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index 225740688ab7..81d6c01b8825 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
> @@ -285,6 +285,9 @@ int bpf_prog_array_copy_to_user(struct
> bpf_prog_array __rcu *progs,
>  #ifdef CONFIG_BPF_SYSCALL
>  DECLARE_PER_CPU(int, bpf_prog_active);
>  
> +extern const struct file_operations bpf_map_fops;
> +extern const struct file_operations bpf_prog_fops;
> +
>  #define BPF_PROG_TYPE(_id, _ops) \
>  	extern const struct bpf_verifier_ops _ops;
>  #define BPF_MAP_TYPE(_id, _ops) \
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index 7161d8e7ee79..517dea60b87b 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -1385,6 +1385,19 @@
>   * @bpf_prog_free_security:
>   *	Clean up the security information stored inside bpf prog.
>   *
> + * @bpf_map_file:
> + *	When creating a bpf map fd, set up the file security
> information with
> + *	the bpf security information stored in the map struct. So
> when the map
> + *	fd is passed between processes, the security module can
> directly read
> + *	the security information from file security struct rather
> than the bpf
> + *	security struct.
> + *
> + * @bpf_prog_file:
> + *	When creating a bpf prog fd, set up the file security
> information with
> + *	the bpf security information stored in the prog struct. So
> when the prog
> + *	fd is passed between processes, the security module can
> directly read
> + *	the security information from file security struct rather
> than the bpf
> + *	security struct.
>   */
>  union security_list_options {
>  	int (*binder_set_context_mgr)(struct task_struct *mgr);
> @@ -1726,6 +1739,8 @@ union security_list_options {
>  	void (*bpf_map_free_security)(struct bpf_map *map);
>  	int (*bpf_prog_alloc_security)(struct bpf_prog_aux *aux);
>  	void (*bpf_prog_free_security)(struct bpf_prog_aux *aux);
> +	void (*bpf_map_file)(struct bpf_map *map, struct file
> *file);
> +	void (*bpf_prog_file)(struct bpf_prog_aux *aux, struct file
> *file);
>  #endif /* CONFIG_BPF_SYSCALL */
>  };
>  
> @@ -1954,6 +1969,8 @@ struct security_hook_heads {
>  	struct list_head bpf_map_free_security;
>  	struct list_head bpf_prog_alloc_security;
>  	struct list_head bpf_prog_free_security;
> +	struct list_head bpf_map_file;
> +	struct list_head bpf_prog_file;
>  #endif /* CONFIG_BPF_SYSCALL */
>  } __randomize_layout;
>  
> diff --git a/include/linux/security.h b/include/linux/security.h
> index 18800b0911e5..57573b794e2d 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -1740,6 +1740,8 @@ extern int security_bpf_map_alloc(struct
> bpf_map *map);
>  extern void security_bpf_map_free(struct bpf_map *map);
>  extern int security_bpf_prog_alloc(struct bpf_prog_aux *aux);
>  extern void security_bpf_prog_free(struct bpf_prog_aux *aux);
> +extern void security_bpf_map_file(struct bpf_map *map, struct file
> *file);
> +extern void security_bpf_prog_file(struct bpf_prog_aux *aux, struct
> file *file);
>  #else
>  static inline int security_bpf(int cmd, union bpf_attr *attr,
>  					     unsigned int size)
> @@ -1772,6 +1774,13 @@ static inline int
> security_bpf_prog_alloc(struct bpf_prog_aux *aux)
>  
>  static inline void security_bpf_prog_free(struct bpf_prog_aux *aux)
>  { }
> +
> +static inline void security_bpf_map_file(struct bpf_map *map, struct
> file *file)
> +{ }
> +
> +static inline void security_bpf_prog_file(struct bpf_prog_aux *aux,
> +					  struct file *file)
> +{ }
>  #endif /* CONFIG_SECURITY */
>  #endif /* CONFIG_BPF_SYSCALL */
>  
> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> index 1cf31ddd7616..b144181d3f3a 100644
> --- a/kernel/bpf/syscall.c
> +++ b/kernel/bpf/syscall.c
> @@ -313,7 +313,7 @@ static ssize_t bpf_dummy_write(struct file *filp,
> const char __user *buf,
>  	return -EINVAL;
>  }
>  
> -static const struct file_operations bpf_map_fops = {
> +const struct file_operations bpf_map_fops = {
>  #ifdef CONFIG_PROC_FS
>  	.show_fdinfo	= bpf_map_show_fdinfo,
>  #endif
> @@ -964,7 +964,7 @@ static void bpf_prog_show_fdinfo(struct seq_file
> *m, struct file *filp)
>  }
>  #endif
>  
> -static const struct file_operations bpf_prog_fops = {
> +const struct file_operations bpf_prog_fops = {
>  #ifdef CONFIG_PROC_FS
>  	.show_fdinfo	= bpf_prog_show_fdinfo,
>  #endif
> diff --git a/security/security.c b/security/security.c
> index 1cd8526cb0b7..dacf649b8cfa 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -1734,4 +1734,12 @@ void security_bpf_prog_free(struct
> bpf_prog_aux *aux)
>  {
>  	call_void_hook(bpf_prog_free_security, aux);
>  }
> +void security_bpf_map_file(struct bpf_map *map, struct file *file)
> +{
> +	call_void_hook(bpf_map_file, map, file);
> +}
> +void security_bpf_prog_file(struct bpf_prog_aux *aux, struct file
> *file)
> +{
> +	call_void_hook(bpf_prog_file, aux, file);
> +}
>  #endif /* CONFIG_BPF_SYSCALL */
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 41aba4e3d57c..fea88655e0ee 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -1815,6 +1815,10 @@ static inline int file_path_has_perm(const
> struct cred *cred,
>  	return inode_has_perm(cred, file_inode(file), av, &ad);
>  }
>  
> +#ifdef CONFIG_BPF_SYSCALL
> +static int bpf_file_check(struct file *file, u32 sid);
> +#endif
> +
>  /* Check whether a task can use an open file descriptor to
>     access an inode in a given way.  Check access to the
>     descriptor itself, and then use dentry_has_perm to
> @@ -1845,6 +1849,12 @@ static int file_has_perm(const struct cred
> *cred,
>  			goto out;
>  	}
>  
> +#ifdef CONFIG_BPF_SYSCALL
> +	rc = bpf_file_check(file, cred_sid(cred));
> +	if (rc)
> +		goto out;
> +#endif
> +
>  	/* av is zero if only checking access to the descriptor. */
>  	rc = 0;
>  	if (av)
> @@ -2165,6 +2175,12 @@ static int selinux_binder_transfer_file(struct
> task_struct *from,
>  			return rc;
>  	}
>  
> +#ifdef CONFIG_BPF_SYSCALL
> +	rc = bpf_file_check(file, sid);
> +	if (rc)
> +		return rc;
> +#endif
> +
>  	if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
>  		return 0;
>  
> @@ -6288,6 +6304,33 @@ static u32 bpf_map_fmode_to_av(fmode_t fmode)
>  	return av;
>  }
>  
> +/* This function will check the file pass through unix socket or
> binder to see
> + * if it is a bpf related object. And apply correspinding checks on
> the bpf
> + * object based on the type. The bpf maps and programs, not like
> other files and
> + * socket, are using a shared anonymous inode inside the kernel as
> their inode.
> + * So checking that inode cannot identify if the process have
> privilege to
> + * access the bpf object and that's why we have to add this
> additional check in
> + * selinux_file_receive and selinux_binder_transfer_files.
> + */
> +static int bpf_file_check(struct file *file, u32 sid)
> +{
> +	struct file_security_struct *fsec = file->f_security;
> +	int ret;
> +
> +	if (file->f_op == &bpf_map_fops) {
> +		ret = avc_has_perm(sid, fsec->sid, SECCLASS_BPF_MAP,
> +				   bpf_map_fmode_to_av(file-
> >f_mode), NULL);
> +		if (ret)
> +			return ret;
> +	} else if (file->f_op == &bpf_prog_fops) {
> +		ret = avc_has_perm(sid, fsec->sid,
> SECCLASS_BPF_PROG,
> +				   BPF_PROG__USE, NULL);
> +		if (ret)
> +			return ret;
> +	}
> +	return 0;
> +}
> +
>  static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
>  {
>  	u32 sid = current_sid();
> @@ -6351,6 +6394,22 @@ static void selinux_bpf_prog_free(struct
> bpf_prog_aux *aux)
>  	aux->security = NULL;
>  	kfree(bpfsec);
>  }
> +
> +static void selinux_bpf_map_file(struct bpf_map *map, struct file
> *file)
> +{
> +	struct bpf_security_struct *bpfsec = map->security;
> +	struct file_security_struct *fsec = file->f_security;
> +
> +	fsec->sid = bpfsec->sid;
> +}
> +
> +static void selinux_bpf_prog_file(struct bpf_prog_aux *aux, struct
> file *file)
> +{
> +	struct bpf_security_struct *bpfsec = aux->security;
> +	struct file_security_struct *fsec = file->f_security;
> +
> +	fsec->sid = bpfsec->sid;

I could be wrong, but isn't it the case that fsec->sid already will
equal bpfsec->sid, because they are both created by the same thread
during the same system call, and they each inherit the SID of the
current task?

What I expected you to do was to add and set a flags field in the
file_security_struct to indicate that this is a bpf map or prog, and
then test for that in your bpf_file_check() function instead of having
to export and test the fops structures.


> +}
>  #endif
>  
>  static struct security_hook_list selinux_hooks[] __lsm_ro_after_init
> = {
> @@ -6581,6 +6640,8 @@ static struct security_hook_list
> selinux_hooks[] __lsm_ro_after_init = {
>  	LSM_HOOK_INIT(bpf_prog_alloc_security,
> selinux_bpf_prog_alloc),
>  	LSM_HOOK_INIT(bpf_map_free_security, selinux_bpf_map_free),
>  	LSM_HOOK_INIT(bpf_prog_free_security,
> selinux_bpf_prog_free),
> +	LSM_HOOK_INIT(bpf_map_file, selinux_bpf_map_file),
> +	LSM_HOOK_INIT(bpf_prog_file, selinux_bpf_prog_file),
>  #endif
>  };
>  

^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH net-next v2 5/5] selinux: bpf: Add addtional check for bpf object file receive
@ 2017-10-10 14:24     ` Stephen Smalley
  0 siblings, 0 replies; 39+ messages in thread
From: Stephen Smalley @ 2017-10-10 14:24 UTC (permalink / raw)
  To: linux-security-module

On Mon, 2017-10-09 at 15:20 -0700, Chenbo Feng wrote:
> From: Chenbo Feng <fengc@google.com>
> 
> Introduce a bpf object related check when sending and receiving files
> through unix domain socket as well as binder. It checks if the
> receiving
> process have privilege to read/write the bpf map or use the bpf
> program.
> This check is necessary because the bpf maps and programs are using a
> anonymous inode as their shared inode so the normal way of checking
> the
> files and sockets when passing between processes cannot work properly
> on
> eBPF object. This check only works when the BPF_SYSCALL is
> configured.
> The information stored inside the file security struct is the same as
> the information in bpf object security struct.
> 
> Signed-off-by: Chenbo Feng <fengc@google.com>
> ---
> ?include/linux/bpf.h???????|??3 +++
> ?include/linux/lsm_hooks.h | 17 +++++++++++++
> ?include/linux/security.h??|??9 +++++++
> ?kernel/bpf/syscall.c??????|??4 ++--
> ?security/security.c???????|??8 +++++++
> ?security/selinux/hooks.c??| 61
> +++++++++++++++++++++++++++++++++++++++++++++++
> ?6 files changed, 100 insertions(+), 2 deletions(-)
> 
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index 225740688ab7..81d6c01b8825 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
> @@ -285,6 +285,9 @@ int bpf_prog_array_copy_to_user(struct
> bpf_prog_array __rcu *progs,
> ?#ifdef CONFIG_BPF_SYSCALL
> ?DECLARE_PER_CPU(int, bpf_prog_active);
> ?
> +extern const struct file_operations bpf_map_fops;
> +extern const struct file_operations bpf_prog_fops;
> +
> ?#define BPF_PROG_TYPE(_id, _ops) \
> ?	extern const struct bpf_verifier_ops _ops;
> ?#define BPF_MAP_TYPE(_id, _ops) \
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index 7161d8e7ee79..517dea60b87b 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -1385,6 +1385,19 @@
> ? * @bpf_prog_free_security:
> ? *	Clean up the security information stored inside bpf prog.
> ? *
> + * @bpf_map_file:
> + *	When creating a bpf map fd, set up the file security
> information with
> + *	the bpf security information stored in the map struct. So
> when the map
> + *	fd is passed between processes, the security module can
> directly read
> + *	the security information from file security struct rather
> than the bpf
> + *	security struct.
> + *
> + * @bpf_prog_file:
> + *	When creating a bpf prog fd, set up the file security
> information with
> + *	the bpf security information stored in the prog struct. So
> when the prog
> + *	fd is passed between processes, the security module can
> directly read
> + *	the security information from file security struct rather
> than the bpf
> + *	security struct.
> ? */
> ?union security_list_options {
> ?	int (*binder_set_context_mgr)(struct task_struct *mgr);
> @@ -1726,6 +1739,8 @@ union security_list_options {
> ?	void (*bpf_map_free_security)(struct bpf_map *map);
> ?	int (*bpf_prog_alloc_security)(struct bpf_prog_aux *aux);
> ?	void (*bpf_prog_free_security)(struct bpf_prog_aux *aux);
> +	void (*bpf_map_file)(struct bpf_map *map, struct file
> *file);
> +	void (*bpf_prog_file)(struct bpf_prog_aux *aux, struct file
> *file);
> ?#endif /* CONFIG_BPF_SYSCALL */
> ?};
> ?
> @@ -1954,6 +1969,8 @@ struct security_hook_heads {
> ?	struct list_head bpf_map_free_security;
> ?	struct list_head bpf_prog_alloc_security;
> ?	struct list_head bpf_prog_free_security;
> +	struct list_head bpf_map_file;
> +	struct list_head bpf_prog_file;
> ?#endif /* CONFIG_BPF_SYSCALL */
> ?} __randomize_layout;
> ?
> diff --git a/include/linux/security.h b/include/linux/security.h
> index 18800b0911e5..57573b794e2d 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -1740,6 +1740,8 @@ extern int security_bpf_map_alloc(struct
> bpf_map *map);
> ?extern void security_bpf_map_free(struct bpf_map *map);
> ?extern int security_bpf_prog_alloc(struct bpf_prog_aux *aux);
> ?extern void security_bpf_prog_free(struct bpf_prog_aux *aux);
> +extern void security_bpf_map_file(struct bpf_map *map, struct file
> *file);
> +extern void security_bpf_prog_file(struct bpf_prog_aux *aux, struct
> file *file);
> ?#else
> ?static inline int security_bpf(int cmd, union bpf_attr *attr,
> ?					?????unsigned int size)
> @@ -1772,6 +1774,13 @@ static inline int
> security_bpf_prog_alloc(struct bpf_prog_aux *aux)
> ?
> ?static inline void security_bpf_prog_free(struct bpf_prog_aux *aux)
> ?{ }
> +
> +static inline void security_bpf_map_file(struct bpf_map *map, struct
> file *file)
> +{ }
> +
> +static inline void security_bpf_prog_file(struct bpf_prog_aux *aux,
> +					??struct file *file)
> +{ }
> ?#endif /* CONFIG_SECURITY */
> ?#endif /* CONFIG_BPF_SYSCALL */
> ?
> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> index 1cf31ddd7616..b144181d3f3a 100644
> --- a/kernel/bpf/syscall.c
> +++ b/kernel/bpf/syscall.c
> @@ -313,7 +313,7 @@ static ssize_t bpf_dummy_write(struct file *filp,
> const char __user *buf,
> ?	return -EINVAL;
> ?}
> ?
> -static const struct file_operations bpf_map_fops = {
> +const struct file_operations bpf_map_fops = {
> ?#ifdef CONFIG_PROC_FS
> ?	.show_fdinfo	= bpf_map_show_fdinfo,
> ?#endif
> @@ -964,7 +964,7 @@ static void bpf_prog_show_fdinfo(struct seq_file
> *m, struct file *filp)
> ?}
> ?#endif
> ?
> -static const struct file_operations bpf_prog_fops = {
> +const struct file_operations bpf_prog_fops = {
> ?#ifdef CONFIG_PROC_FS
> ?	.show_fdinfo	= bpf_prog_show_fdinfo,
> ?#endif
> diff --git a/security/security.c b/security/security.c
> index 1cd8526cb0b7..dacf649b8cfa 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -1734,4 +1734,12 @@ void security_bpf_prog_free(struct
> bpf_prog_aux *aux)
> ?{
> ?	call_void_hook(bpf_prog_free_security, aux);
> ?}
> +void security_bpf_map_file(struct bpf_map *map, struct file *file)
> +{
> +	call_void_hook(bpf_map_file, map, file);
> +}
> +void security_bpf_prog_file(struct bpf_prog_aux *aux, struct file
> *file)
> +{
> +	call_void_hook(bpf_prog_file, aux, file);
> +}
> ?#endif /* CONFIG_BPF_SYSCALL */
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 41aba4e3d57c..fea88655e0ee 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -1815,6 +1815,10 @@ static inline int file_path_has_perm(const
> struct cred *cred,
> ?	return inode_has_perm(cred, file_inode(file), av, &ad);
> ?}
> ?
> +#ifdef CONFIG_BPF_SYSCALL
> +static int bpf_file_check(struct file *file, u32 sid);
> +#endif
> +
> ?/* Check whether a task can use an open file descriptor to
> ????access an inode in a given way.??Check access to the
> ????descriptor itself, and then use dentry_has_perm to
> @@ -1845,6 +1849,12 @@ static int file_has_perm(const struct cred
> *cred,
> ?			goto out;
> ?	}
> ?
> +#ifdef CONFIG_BPF_SYSCALL
> +	rc = bpf_file_check(file, cred_sid(cred));
> +	if (rc)
> +		goto out;
> +#endif
> +
> ?	/* av is zero if only checking access to the descriptor. */
> ?	rc = 0;
> ?	if (av)
> @@ -2165,6 +2175,12 @@ static int selinux_binder_transfer_file(struct
> task_struct *from,
> ?			return rc;
> ?	}
> ?
> +#ifdef CONFIG_BPF_SYSCALL
> +	rc = bpf_file_check(file, sid);
> +	if (rc)
> +		return rc;
> +#endif
> +
> ?	if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> ?		return 0;
> ?
> @@ -6288,6 +6304,33 @@ static u32 bpf_map_fmode_to_av(fmode_t fmode)
> ?	return av;
> ?}
> ?
> +/* This function will check the file pass through unix socket or
> binder to see
> + * if it is a bpf related object. And apply correspinding checks on
> the bpf
> + * object based on the type. The bpf maps and programs, not like
> other files and
> + * socket, are using a shared anonymous inode inside the kernel as
> their inode.
> + * So checking that inode cannot identify if the process have
> privilege to
> + * access the bpf object and that's why we have to add this
> additional check in
> + * selinux_file_receive and selinux_binder_transfer_files.
> + */
> +static int bpf_file_check(struct file *file, u32 sid)
> +{
> +	struct file_security_struct *fsec = file->f_security;
> +	int ret;
> +
> +	if (file->f_op == &bpf_map_fops) {
> +		ret = avc_has_perm(sid, fsec->sid, SECCLASS_BPF_MAP,
> +				???bpf_map_fmode_to_av(file-
> >f_mode), NULL);
> +		if (ret)
> +			return ret;
> +	} else if (file->f_op == &bpf_prog_fops) {
> +		ret = avc_has_perm(sid, fsec->sid,
> SECCLASS_BPF_PROG,
> +				???BPF_PROG__USE, NULL);
> +		if (ret)
> +			return ret;
> +	}
> +	return 0;
> +}
> +
> ?static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
> ?{
> ?	u32 sid = current_sid();
> @@ -6351,6 +6394,22 @@ static void selinux_bpf_prog_free(struct
> bpf_prog_aux *aux)
> ?	aux->security = NULL;
> ?	kfree(bpfsec);
> ?}
> +
> +static void selinux_bpf_map_file(struct bpf_map *map, struct file
> *file)
> +{
> +	struct bpf_security_struct *bpfsec = map->security;
> +	struct file_security_struct *fsec = file->f_security;
> +
> +	fsec->sid = bpfsec->sid;
> +}
> +
> +static void selinux_bpf_prog_file(struct bpf_prog_aux *aux, struct
> file *file)
> +{
> +	struct bpf_security_struct *bpfsec = aux->security;
> +	struct file_security_struct *fsec = file->f_security;
> +
> +	fsec->sid = bpfsec->sid;

I could be wrong, but isn't it the case that fsec->sid already will
equal bpfsec->sid, because they are both created by the same thread
during the same system call, and they each inherit the SID of the
current task?

What I expected you to do was to add and set a flags field in the
file_security_struct to indicate that this is a bpf map or prog, and
then test for that in your bpf_file_check() function instead of having
to export and test the fops structures.


> +}
> ?#endif
> ?
> ?static struct security_hook_list selinux_hooks[] __lsm_ro_after_init
> = {
> @@ -6581,6 +6640,8 @@ static struct security_hook_list
> selinux_hooks[] __lsm_ro_after_init = {
> ?	LSM_HOOK_INIT(bpf_prog_alloc_security,
> selinux_bpf_prog_alloc),
> ?	LSM_HOOK_INIT(bpf_map_free_security, selinux_bpf_map_free),
> ?	LSM_HOOK_INIT(bpf_prog_free_security,
> selinux_bpf_prog_free),
> +	LSM_HOOK_INIT(bpf_map_file, selinux_bpf_map_file),
> +	LSM_HOOK_INIT(bpf_prog_file, selinux_bpf_prog_file),
> ?#endif
> ?};
> ?
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH net-next v2 4/5] selinux: bpf: Add selinux check for eBPF syscall operations
  2017-10-10 14:18     ` Stephen Smalley
  (?)
@ 2017-10-10 14:52         ` Stephen Smalley
  -1 siblings, 0 replies; 39+ messages in thread
From: Stephen Smalley @ 2017-10-10 14:52 UTC (permalink / raw)
  To: Chenbo Feng, linux-security-module-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA, SELinux
  Cc: Chenbo Feng, Alexei Starovoitov, Daniel Borkmann,
	lorenzo-hpIqsD4AKlfQT0dZR+AlfA

On Tue, 2017-10-10 at 10:18 -0400, Stephen Smalley wrote:
> On Mon, 2017-10-09 at 15:20 -0700, Chenbo Feng wrote:
> > From: Chenbo Feng <fengc-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
> > 
> > Implement the actual checks introduced to eBPF related syscalls.
> > This
> > implementation use the security field inside bpf object to store a
> > sid that
> > identify the bpf object. And when processes try to access the
> > object,
> > selinux will check if processes have the right privileges. The
> > creation
> > of eBPF object are also checked at the general bpf check hook and
> > new
> > cmd introduced to eBPF domain can also be checked there.
> > 
> > Signed-off-by: Chenbo Feng <fengc-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
> > Acked-by: Alexei Starovoitov <ast-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> > ---
> >  security/selinux/hooks.c            | 111
> > ++++++++++++++++++++++++++++++++++++
> >  security/selinux/include/classmap.h |   2 +
> >  security/selinux/include/objsec.h   |   4 ++
> >  3 files changed, 117 insertions(+)
> > 
> > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> > index f5d304736852..41aba4e3d57c 100644
> > --- a/security/selinux/hooks.c
> > +++ b/security/selinux/hooks.c
> > @@ -85,6 +85,7 @@
> >  #include <linux/export.h>
> >  #include <linux/msg.h>
> >  #include <linux/shm.h>
> > +#include <linux/bpf.h>
> >  
> >  #include "avc.h"
> >  #include "objsec.h"
> > @@ -6252,6 +6253,106 @@ static void selinux_ib_free_security(void
> > *ib_sec)
> >  }
> >  #endif
> >  
> > +#ifdef CONFIG_BPF_SYSCALL
> > +static int selinux_bpf(int cmd, union bpf_attr *attr,
> > +				     unsigned int size)
> > +{
> > +	u32 sid = current_sid();
> > +	int ret;
> > +
> > +	switch (cmd) {
> > +	case BPF_MAP_CREATE:
> > +		ret = avc_has_perm(sid, sid, SECCLASS_BPF_MAP,
> > BPF_MAP__CREATE,
> > +				   NULL);
> > +		break;
> > +	case BPF_PROG_LOAD:
> > +		ret = avc_has_perm(sid, sid, SECCLASS_BPF_PROG,
> > BPF_PROG__LOAD,
> > +				   NULL);
> > +		break;
> > +	default:
> > +		ret = 0;
> > +		break;
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +static u32 bpf_map_fmode_to_av(fmode_t fmode)
> > +{
> > +	u32 av = 0;
> > +
> > +	if (f_mode & FMODE_READ)
> > +		av |= BPF_MAP__READ;
> > +	if (f_mode & FMODE_WRITE)
> > +		av |= BPF_MAP__WRITE;
> > +	return av;
> > +}
> > +
> > +static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
> > +{
> > +	u32 sid = current_sid();
> > +	struct bpf_security_struct *bpfsec;
> > +
> > +	bpfsec = map->security;
> > +	return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF_MAP,
> > +			    bpf_map_fmode_to_av(fmode), NULL);
> > +}
> > +
> > +static int selinux_bpf_prog(struct bpf_prog *prog)
> > +{
> > +	u32 sid = current_sid();
> > +	struct bpf_security_struct *bpfsec;
> > +
> > +	bpfsec = prog->aux->security;
> > +	return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF_PROG,
> > +			    BPF_PROG__USE, NULL);
> > +}
> > +
> > +static int selinux_bpf_map_alloc(struct bpf_map *map)
> > +{
> > +	struct bpf_security_struct *bpfsec;
> > +
> > +	bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> > +	if (!bpfsec)
> > +		return -ENOMEM;
> > +
> > +	bpfsec->sid = current_sid();
> > +	map->security = bpfsec;
> > +
> > +	return 0;
> > +}
> > +
> > +static void selinux_bpf_map_free(struct bpf_map *map)
> > +{
> > +	struct bpf_security_struct *bpfsec = map->security;
> > +
> > +	map->security = NULL;
> > +	kfree(bpfsec);
> > +}
> > +
> > +static int selinux_bpf_prog_alloc(struct bpf_prog_aux *aux)
> > +{
> > +	struct bpf_security_struct *bpfsec;
> > +
> > +	bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> > +	if (!bpfsec)
> > +		return -ENOMEM;
> > +
> > +	bpfsec->sid = current_sid();
> > +	aux->security = bpfsec;
> > +
> > +	return 0;
> > +}
> > +
> > +static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
> > +{
> > +	struct bpf_security_struct *bpfsec = aux->security;
> > +
> > +	aux->security = NULL;
> > +	kfree(bpfsec);
> > +}
> > +#endif
> > +
> >  static struct security_hook_list selinux_hooks[]
> > __lsm_ro_after_init
> > = {
> >  	LSM_HOOK_INIT(binder_set_context_mgr,
> > selinux_binder_set_context_mgr),
> >  	LSM_HOOK_INIT(binder_transaction,
> > selinux_binder_transaction),
> > @@ -6471,6 +6572,16 @@ static struct security_hook_list
> > selinux_hooks[] __lsm_ro_after_init = {
> >  	LSM_HOOK_INIT(audit_rule_match, selinux_audit_rule_match),
> >  	LSM_HOOK_INIT(audit_rule_free, selinux_audit_rule_free),
> >  #endif
> > +
> > +#ifdef CONFIG_BPF_SYSCALL
> > +	LSM_HOOK_INIT(bpf, selinux_bpf),
> > +	LSM_HOOK_INIT(bpf_map, selinux_bpf_map),
> > +	LSM_HOOK_INIT(bpf_prog, selinux_bpf_prog),
> > +	LSM_HOOK_INIT(bpf_map_alloc_security,
> > selinux_bpf_map_alloc),
> > +	LSM_HOOK_INIT(bpf_prog_alloc_security,
> > selinux_bpf_prog_alloc),
> > +	LSM_HOOK_INIT(bpf_map_free_security,
> > selinux_bpf_map_free),
> > +	LSM_HOOK_INIT(bpf_prog_free_security,
> > selinux_bpf_prog_free),
> > +#endif
> >  };
> >  
> >  static __init int selinux_init(void)
> > diff --git a/security/selinux/include/classmap.h
> > b/security/selinux/include/classmap.h
> > index 35ffb29a69cb..7253c5eea59c 100644
> > --- a/security/selinux/include/classmap.h
> > +++ b/security/selinux/include/classmap.h
> > @@ -237,6 +237,8 @@ struct security_class_mapping secclass_map[] =
> > {
> >  	  { "access", NULL } },
> >  	{ "infiniband_endport",
> >  	  { "manage_subnet", NULL } },
> > +	{ "bpf_map", {"create", "read", "write"} },
> > +	{ "bpf_prog", {"load", "use"} },
> 
> Again I have to ask: do you truly need/want two separate classes, or
> would a single class with distinct permissions suffice, ala:
>         { "bpf", { "create_map", "read_map", "write_map",
> "prog_load",
> "prog_use" } },
> 
> and then allow A self:bpf { create_map read_map write_map prog_load
> prog_use }; would be stored in a single policy avtab rule, and be
> cached in a single AVC entry.

I guess for consistency in naming it should be either:
        { "bpf", { "map_create", "map_read", "map_write", "prog_load",
"prog_use" } },
 
or:

        { "bpf", { "create_map", "read_map", "write_map", "load_prog",
"use_prog" } },
 

> >  	{ NULL }
> >    };
> >  
> > diff --git a/security/selinux/include/objsec.h
> > b/security/selinux/include/objsec.h
> > index 1649cd18eb0b..3d54468ce334 100644
> > --- a/security/selinux/include/objsec.h
> > +++ b/security/selinux/include/objsec.h
> > @@ -150,6 +150,10 @@ struct pkey_security_struct {
> >  	u32	sid;	/* SID of pkey */
> >  };
> >  
> > +struct bpf_security_struct {
> > +	u32 sid;  /*SID of bpf obj creater*/
> > +};
> > +
> >  extern unsigned int selinux_checkreqprot;
> >  
> >  #endif /* _SELINUX_OBJSEC_H_ */

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH net-next v2 4/5] selinux: bpf: Add selinux check for eBPF syscall operations
@ 2017-10-10 14:52         ` Stephen Smalley
  0 siblings, 0 replies; 39+ messages in thread
From: Stephen Smalley @ 2017-10-10 14:52 UTC (permalink / raw)
  To: Chenbo Feng, linux-security-module, netdev, SELinux
  Cc: Chenbo Feng, Alexei Starovoitov, Daniel Borkmann, lorenzo

On Tue, 2017-10-10 at 10:18 -0400, Stephen Smalley wrote:
> On Mon, 2017-10-09 at 15:20 -0700, Chenbo Feng wrote:
> > From: Chenbo Feng <fengc@google.com>
> > 
> > Implement the actual checks introduced to eBPF related syscalls.
> > This
> > implementation use the security field inside bpf object to store a
> > sid that
> > identify the bpf object. And when processes try to access the
> > object,
> > selinux will check if processes have the right privileges. The
> > creation
> > of eBPF object are also checked at the general bpf check hook and
> > new
> > cmd introduced to eBPF domain can also be checked there.
> > 
> > Signed-off-by: Chenbo Feng <fengc@google.com>
> > Acked-by: Alexei Starovoitov <ast@kernel.org>
> > ---
> >  security/selinux/hooks.c            | 111
> > ++++++++++++++++++++++++++++++++++++
> >  security/selinux/include/classmap.h |   2 +
> >  security/selinux/include/objsec.h   |   4 ++
> >  3 files changed, 117 insertions(+)
> > 
> > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> > index f5d304736852..41aba4e3d57c 100644
> > --- a/security/selinux/hooks.c
> > +++ b/security/selinux/hooks.c
> > @@ -85,6 +85,7 @@
> >  #include <linux/export.h>
> >  #include <linux/msg.h>
> >  #include <linux/shm.h>
> > +#include <linux/bpf.h>
> >  
> >  #include "avc.h"
> >  #include "objsec.h"
> > @@ -6252,6 +6253,106 @@ static void selinux_ib_free_security(void
> > *ib_sec)
> >  }
> >  #endif
> >  
> > +#ifdef CONFIG_BPF_SYSCALL
> > +static int selinux_bpf(int cmd, union bpf_attr *attr,
> > +				     unsigned int size)
> > +{
> > +	u32 sid = current_sid();
> > +	int ret;
> > +
> > +	switch (cmd) {
> > +	case BPF_MAP_CREATE:
> > +		ret = avc_has_perm(sid, sid, SECCLASS_BPF_MAP,
> > BPF_MAP__CREATE,
> > +				   NULL);
> > +		break;
> > +	case BPF_PROG_LOAD:
> > +		ret = avc_has_perm(sid, sid, SECCLASS_BPF_PROG,
> > BPF_PROG__LOAD,
> > +				   NULL);
> > +		break;
> > +	default:
> > +		ret = 0;
> > +		break;
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +static u32 bpf_map_fmode_to_av(fmode_t fmode)
> > +{
> > +	u32 av = 0;
> > +
> > +	if (f_mode & FMODE_READ)
> > +		av |= BPF_MAP__READ;
> > +	if (f_mode & FMODE_WRITE)
> > +		av |= BPF_MAP__WRITE;
> > +	return av;
> > +}
> > +
> > +static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
> > +{
> > +	u32 sid = current_sid();
> > +	struct bpf_security_struct *bpfsec;
> > +
> > +	bpfsec = map->security;
> > +	return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF_MAP,
> > +			    bpf_map_fmode_to_av(fmode), NULL);
> > +}
> > +
> > +static int selinux_bpf_prog(struct bpf_prog *prog)
> > +{
> > +	u32 sid = current_sid();
> > +	struct bpf_security_struct *bpfsec;
> > +
> > +	bpfsec = prog->aux->security;
> > +	return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF_PROG,
> > +			    BPF_PROG__USE, NULL);
> > +}
> > +
> > +static int selinux_bpf_map_alloc(struct bpf_map *map)
> > +{
> > +	struct bpf_security_struct *bpfsec;
> > +
> > +	bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> > +	if (!bpfsec)
> > +		return -ENOMEM;
> > +
> > +	bpfsec->sid = current_sid();
> > +	map->security = bpfsec;
> > +
> > +	return 0;
> > +}
> > +
> > +static void selinux_bpf_map_free(struct bpf_map *map)
> > +{
> > +	struct bpf_security_struct *bpfsec = map->security;
> > +
> > +	map->security = NULL;
> > +	kfree(bpfsec);
> > +}
> > +
> > +static int selinux_bpf_prog_alloc(struct bpf_prog_aux *aux)
> > +{
> > +	struct bpf_security_struct *bpfsec;
> > +
> > +	bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> > +	if (!bpfsec)
> > +		return -ENOMEM;
> > +
> > +	bpfsec->sid = current_sid();
> > +	aux->security = bpfsec;
> > +
> > +	return 0;
> > +}
> > +
> > +static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
> > +{
> > +	struct bpf_security_struct *bpfsec = aux->security;
> > +
> > +	aux->security = NULL;
> > +	kfree(bpfsec);
> > +}
> > +#endif
> > +
> >  static struct security_hook_list selinux_hooks[]
> > __lsm_ro_after_init
> > = {
> >  	LSM_HOOK_INIT(binder_set_context_mgr,
> > selinux_binder_set_context_mgr),
> >  	LSM_HOOK_INIT(binder_transaction,
> > selinux_binder_transaction),
> > @@ -6471,6 +6572,16 @@ static struct security_hook_list
> > selinux_hooks[] __lsm_ro_after_init = {
> >  	LSM_HOOK_INIT(audit_rule_match, selinux_audit_rule_match),
> >  	LSM_HOOK_INIT(audit_rule_free, selinux_audit_rule_free),
> >  #endif
> > +
> > +#ifdef CONFIG_BPF_SYSCALL
> > +	LSM_HOOK_INIT(bpf, selinux_bpf),
> > +	LSM_HOOK_INIT(bpf_map, selinux_bpf_map),
> > +	LSM_HOOK_INIT(bpf_prog, selinux_bpf_prog),
> > +	LSM_HOOK_INIT(bpf_map_alloc_security,
> > selinux_bpf_map_alloc),
> > +	LSM_HOOK_INIT(bpf_prog_alloc_security,
> > selinux_bpf_prog_alloc),
> > +	LSM_HOOK_INIT(bpf_map_free_security,
> > selinux_bpf_map_free),
> > +	LSM_HOOK_INIT(bpf_prog_free_security,
> > selinux_bpf_prog_free),
> > +#endif
> >  };
> >  
> >  static __init int selinux_init(void)
> > diff --git a/security/selinux/include/classmap.h
> > b/security/selinux/include/classmap.h
> > index 35ffb29a69cb..7253c5eea59c 100644
> > --- a/security/selinux/include/classmap.h
> > +++ b/security/selinux/include/classmap.h
> > @@ -237,6 +237,8 @@ struct security_class_mapping secclass_map[] =
> > {
> >  	  { "access", NULL } },
> >  	{ "infiniband_endport",
> >  	  { "manage_subnet", NULL } },
> > +	{ "bpf_map", {"create", "read", "write"} },
> > +	{ "bpf_prog", {"load", "use"} },
> 
> Again I have to ask: do you truly need/want two separate classes, or
> would a single class with distinct permissions suffice, ala:
>         { "bpf", { "create_map", "read_map", "write_map",
> "prog_load",
> "prog_use" } },
> 
> and then allow A self:bpf { create_map read_map write_map prog_load
> prog_use }; would be stored in a single policy avtab rule, and be
> cached in a single AVC entry.

I guess for consistency in naming it should be either:
        { "bpf", { "map_create", "map_read", "map_write", "prog_load",
"prog_use" } },
 
or:

        { "bpf", { "create_map", "read_map", "write_map", "load_prog",
"use_prog" } },
 

> >  	{ NULL }
> >    };
> >  
> > diff --git a/security/selinux/include/objsec.h
> > b/security/selinux/include/objsec.h
> > index 1649cd18eb0b..3d54468ce334 100644
> > --- a/security/selinux/include/objsec.h
> > +++ b/security/selinux/include/objsec.h
> > @@ -150,6 +150,10 @@ struct pkey_security_struct {
> >  	u32	sid;	/* SID of pkey */
> >  };
> >  
> > +struct bpf_security_struct {
> > +	u32 sid;  /*SID of bpf obj creater*/
> > +};
> > +
> >  extern unsigned int selinux_checkreqprot;
> >  
> >  #endif /* _SELINUX_OBJSEC_H_ */

^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH net-next v2 4/5] selinux: bpf: Add selinux check for eBPF syscall operations
@ 2017-10-10 14:52         ` Stephen Smalley
  0 siblings, 0 replies; 39+ messages in thread
From: Stephen Smalley @ 2017-10-10 14:52 UTC (permalink / raw)
  To: linux-security-module

On Tue, 2017-10-10 at 10:18 -0400, Stephen Smalley wrote:
> On Mon, 2017-10-09 at 15:20 -0700, Chenbo Feng wrote:
> > From: Chenbo Feng <fengc@google.com>
> > 
> > Implement the actual checks introduced to eBPF related syscalls.
> > This
> > implementation use the security field inside bpf object to store a
> > sid that
> > identify the bpf object. And when processes try to access the
> > object,
> > selinux will check if processes have the right privileges. The
> > creation
> > of eBPF object are also checked at the general bpf check hook and
> > new
> > cmd introduced to eBPF domain can also be checked there.
> > 
> > Signed-off-by: Chenbo Feng <fengc@google.com>
> > Acked-by: Alexei Starovoitov <ast@kernel.org>
> > ---
> > ?security/selinux/hooks.c????????????| 111
> > ++++++++++++++++++++++++++++++++++++
> > ?security/selinux/include/classmap.h |???2 +
> > ?security/selinux/include/objsec.h???|???4 ++
> > ?3 files changed, 117 insertions(+)
> > 
> > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> > index f5d304736852..41aba4e3d57c 100644
> > --- a/security/selinux/hooks.c
> > +++ b/security/selinux/hooks.c
> > @@ -85,6 +85,7 @@
> > ?#include <linux/export.h>
> > ?#include <linux/msg.h>
> > ?#include <linux/shm.h>
> > +#include <linux/bpf.h>
> > ?
> > ?#include "avc.h"
> > ?#include "objsec.h"
> > @@ -6252,6 +6253,106 @@ static void selinux_ib_free_security(void
> > *ib_sec)
> > ?}
> > ?#endif
> > ?
> > +#ifdef CONFIG_BPF_SYSCALL
> > +static int selinux_bpf(int cmd, union bpf_attr *attr,
> > +				?????unsigned int size)
> > +{
> > +	u32 sid = current_sid();
> > +	int ret;
> > +
> > +	switch (cmd) {
> > +	case BPF_MAP_CREATE:
> > +		ret = avc_has_perm(sid, sid, SECCLASS_BPF_MAP,
> > BPF_MAP__CREATE,
> > +				???NULL);
> > +		break;
> > +	case BPF_PROG_LOAD:
> > +		ret = avc_has_perm(sid, sid, SECCLASS_BPF_PROG,
> > BPF_PROG__LOAD,
> > +				???NULL);
> > +		break;
> > +	default:
> > +		ret = 0;
> > +		break;
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +static u32 bpf_map_fmode_to_av(fmode_t fmode)
> > +{
> > +	u32 av = 0;
> > +
> > +	if (f_mode & FMODE_READ)
> > +		av |= BPF_MAP__READ;
> > +	if (f_mode & FMODE_WRITE)
> > +		av |= BPF_MAP__WRITE;
> > +	return av;
> > +}
> > +
> > +static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
> > +{
> > +	u32 sid = current_sid();
> > +	struct bpf_security_struct *bpfsec;
> > +
> > +	bpfsec = map->security;
> > +	return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF_MAP,
> > +			????bpf_map_fmode_to_av(fmode), NULL);
> > +}
> > +
> > +static int selinux_bpf_prog(struct bpf_prog *prog)
> > +{
> > +	u32 sid = current_sid();
> > +	struct bpf_security_struct *bpfsec;
> > +
> > +	bpfsec = prog->aux->security;
> > +	return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF_PROG,
> > +			????BPF_PROG__USE, NULL);
> > +}
> > +
> > +static int selinux_bpf_map_alloc(struct bpf_map *map)
> > +{
> > +	struct bpf_security_struct *bpfsec;
> > +
> > +	bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> > +	if (!bpfsec)
> > +		return -ENOMEM;
> > +
> > +	bpfsec->sid = current_sid();
> > +	map->security = bpfsec;
> > +
> > +	return 0;
> > +}
> > +
> > +static void selinux_bpf_map_free(struct bpf_map *map)
> > +{
> > +	struct bpf_security_struct *bpfsec = map->security;
> > +
> > +	map->security = NULL;
> > +	kfree(bpfsec);
> > +}
> > +
> > +static int selinux_bpf_prog_alloc(struct bpf_prog_aux *aux)
> > +{
> > +	struct bpf_security_struct *bpfsec;
> > +
> > +	bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> > +	if (!bpfsec)
> > +		return -ENOMEM;
> > +
> > +	bpfsec->sid = current_sid();
> > +	aux->security = bpfsec;
> > +
> > +	return 0;
> > +}
> > +
> > +static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
> > +{
> > +	struct bpf_security_struct *bpfsec = aux->security;
> > +
> > +	aux->security = NULL;
> > +	kfree(bpfsec);
> > +}
> > +#endif
> > +
> > ?static struct security_hook_list selinux_hooks[]
> > __lsm_ro_after_init
> > = {
> > ?	LSM_HOOK_INIT(binder_set_context_mgr,
> > selinux_binder_set_context_mgr),
> > ?	LSM_HOOK_INIT(binder_transaction,
> > selinux_binder_transaction),
> > @@ -6471,6 +6572,16 @@ static struct security_hook_list
> > selinux_hooks[] __lsm_ro_after_init = {
> > ?	LSM_HOOK_INIT(audit_rule_match, selinux_audit_rule_match),
> > ?	LSM_HOOK_INIT(audit_rule_free, selinux_audit_rule_free),
> > ?#endif
> > +
> > +#ifdef CONFIG_BPF_SYSCALL
> > +	LSM_HOOK_INIT(bpf, selinux_bpf),
> > +	LSM_HOOK_INIT(bpf_map, selinux_bpf_map),
> > +	LSM_HOOK_INIT(bpf_prog, selinux_bpf_prog),
> > +	LSM_HOOK_INIT(bpf_map_alloc_security,
> > selinux_bpf_map_alloc),
> > +	LSM_HOOK_INIT(bpf_prog_alloc_security,
> > selinux_bpf_prog_alloc),
> > +	LSM_HOOK_INIT(bpf_map_free_security,
> > selinux_bpf_map_free),
> > +	LSM_HOOK_INIT(bpf_prog_free_security,
> > selinux_bpf_prog_free),
> > +#endif
> > ?};
> > ?
> > ?static __init int selinux_init(void)
> > diff --git a/security/selinux/include/classmap.h
> > b/security/selinux/include/classmap.h
> > index 35ffb29a69cb..7253c5eea59c 100644
> > --- a/security/selinux/include/classmap.h
> > +++ b/security/selinux/include/classmap.h
> > @@ -237,6 +237,8 @@ struct security_class_mapping secclass_map[] =
> > {
> > ?	??{ "access", NULL } },
> > ?	{ "infiniband_endport",
> > ?	??{ "manage_subnet", NULL } },
> > +	{ "bpf_map", {"create", "read", "write"} },
> > +	{ "bpf_prog", {"load", "use"} },
> 
> Again I have to ask: do you truly need/want two separate classes, or
> would a single class with distinct permissions suffice, ala:
> ????????{ "bpf", { "create_map", "read_map", "write_map",
> "prog_load",
> "prog_use" } },
> 
> and then allow A self:bpf { create_map read_map write_map prog_load
> prog_use }; would be stored in a single policy avtab rule, and be
> cached in a single AVC entry.

I guess for consistency in naming it should be either:
????????{ "bpf", { "map_create", "map_read", "map_write", "prog_load",
"prog_use" } },
?
or:

????????{ "bpf", { "create_map", "read_map", "write_map", "load_prog",
"use_prog" } },
?

> > ?	{ NULL }
> > ???};
> > ?
> > diff --git a/security/selinux/include/objsec.h
> > b/security/selinux/include/objsec.h
> > index 1649cd18eb0b..3d54468ce334 100644
> > --- a/security/selinux/include/objsec.h
> > +++ b/security/selinux/include/objsec.h
> > @@ -150,6 +150,10 @@ struct pkey_security_struct {
> > ?	u32	sid;	/* SID of pkey */
> > ?};
> > ?
> > +struct bpf_security_struct {
> > +	u32 sid;??/*SID of bpf obj creater*/
> > +};
> > +
> > ?extern unsigned int selinux_checkreqprot;
> > ?
> > ?#endif /* _SELINUX_OBJSEC_H_ */
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH net-next v2 5/5] selinux: bpf: Add addtional check for bpf object file receive
  2017-10-10 14:24     ` Stephen Smalley
@ 2017-10-10 17:48       ` Chenbo Feng
  -1 siblings, 0 replies; 39+ messages in thread
From: Chenbo Feng @ 2017-10-10 17:48 UTC (permalink / raw)
  To: Stephen Smalley
  Cc: Chenbo Feng, linux-security-module, netdev, SELinux,
	Daniel Borkmann, Alexei Starovoitov, Lorenzo Colitti

On Tue, Oct 10, 2017 at 7:24 AM, Stephen Smalley <sds@tycho.nsa.gov> wrote:
> On Mon, 2017-10-09 at 15:20 -0700, Chenbo Feng wrote:
>> From: Chenbo Feng <fengc@google.com>
>>
>> Introduce a bpf object related check when sending and receiving files
>> through unix domain socket as well as binder. It checks if the
>> receiving
>> process have privilege to read/write the bpf map or use the bpf
>> program.
>> This check is necessary because the bpf maps and programs are using a
>> anonymous inode as their shared inode so the normal way of checking
>> the
>> files and sockets when passing between processes cannot work properly
>> on
>> eBPF object. This check only works when the BPF_SYSCALL is
>> configured.
>> The information stored inside the file security struct is the same as
>> the information in bpf object security struct.
>>
>> Signed-off-by: Chenbo Feng <fengc@google.com>
>> ---
>>  include/linux/bpf.h       |  3 +++
>>  include/linux/lsm_hooks.h | 17 +++++++++++++
>>  include/linux/security.h  |  9 +++++++
>>  kernel/bpf/syscall.c      |  4 ++--
>>  security/security.c       |  8 +++++++
>>  security/selinux/hooks.c  | 61
>> +++++++++++++++++++++++++++++++++++++++++++++++
>>  6 files changed, 100 insertions(+), 2 deletions(-)
>>
>> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
>> index 225740688ab7..81d6c01b8825 100644
>> --- a/include/linux/bpf.h
>> +++ b/include/linux/bpf.h
>> @@ -285,6 +285,9 @@ int bpf_prog_array_copy_to_user(struct
>> bpf_prog_array __rcu *progs,
>>  #ifdef CONFIG_BPF_SYSCALL
>>  DECLARE_PER_CPU(int, bpf_prog_active);
>>
>> +extern const struct file_operations bpf_map_fops;
>> +extern const struct file_operations bpf_prog_fops;
>> +
>>  #define BPF_PROG_TYPE(_id, _ops) \
>>       extern const struct bpf_verifier_ops _ops;
>>  #define BPF_MAP_TYPE(_id, _ops) \
>> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
>> index 7161d8e7ee79..517dea60b87b 100644
>> --- a/include/linux/lsm_hooks.h
>> +++ b/include/linux/lsm_hooks.h
>> @@ -1385,6 +1385,19 @@
>>   * @bpf_prog_free_security:
>>   *   Clean up the security information stored inside bpf prog.
>>   *
>> + * @bpf_map_file:
>> + *   When creating a bpf map fd, set up the file security
>> information with
>> + *   the bpf security information stored in the map struct. So
>> when the map
>> + *   fd is passed between processes, the security module can
>> directly read
>> + *   the security information from file security struct rather
>> than the bpf
>> + *   security struct.
>> + *
>> + * @bpf_prog_file:
>> + *   When creating a bpf prog fd, set up the file security
>> information with
>> + *   the bpf security information stored in the prog struct. So
>> when the prog
>> + *   fd is passed between processes, the security module can
>> directly read
>> + *   the security information from file security struct rather
>> than the bpf
>> + *   security struct.
>>   */
>>  union security_list_options {
>>       int (*binder_set_context_mgr)(struct task_struct *mgr);
>> @@ -1726,6 +1739,8 @@ union security_list_options {
>>       void (*bpf_map_free_security)(struct bpf_map *map);
>>       int (*bpf_prog_alloc_security)(struct bpf_prog_aux *aux);
>>       void (*bpf_prog_free_security)(struct bpf_prog_aux *aux);
>> +     void (*bpf_map_file)(struct bpf_map *map, struct file
>> *file);
>> +     void (*bpf_prog_file)(struct bpf_prog_aux *aux, struct file
>> *file);
>>  #endif /* CONFIG_BPF_SYSCALL */
>>  };
>>
>> @@ -1954,6 +1969,8 @@ struct security_hook_heads {
>>       struct list_head bpf_map_free_security;
>>       struct list_head bpf_prog_alloc_security;
>>       struct list_head bpf_prog_free_security;
>> +     struct list_head bpf_map_file;
>> +     struct list_head bpf_prog_file;
>>  #endif /* CONFIG_BPF_SYSCALL */
>>  } __randomize_layout;
>>
>> diff --git a/include/linux/security.h b/include/linux/security.h
>> index 18800b0911e5..57573b794e2d 100644
>> --- a/include/linux/security.h
>> +++ b/include/linux/security.h
>> @@ -1740,6 +1740,8 @@ extern int security_bpf_map_alloc(struct
>> bpf_map *map);
>>  extern void security_bpf_map_free(struct bpf_map *map);
>>  extern int security_bpf_prog_alloc(struct bpf_prog_aux *aux);
>>  extern void security_bpf_prog_free(struct bpf_prog_aux *aux);
>> +extern void security_bpf_map_file(struct bpf_map *map, struct file
>> *file);
>> +extern void security_bpf_prog_file(struct bpf_prog_aux *aux, struct
>> file *file);
>>  #else
>>  static inline int security_bpf(int cmd, union bpf_attr *attr,
>>                                            unsigned int size)
>> @@ -1772,6 +1774,13 @@ static inline int
>> security_bpf_prog_alloc(struct bpf_prog_aux *aux)
>>
>>  static inline void security_bpf_prog_free(struct bpf_prog_aux *aux)
>>  { }
>> +
>> +static inline void security_bpf_map_file(struct bpf_map *map, struct
>> file *file)
>> +{ }
>> +
>> +static inline void security_bpf_prog_file(struct bpf_prog_aux *aux,
>> +                                       struct file *file)
>> +{ }
>>  #endif /* CONFIG_SECURITY */
>>  #endif /* CONFIG_BPF_SYSCALL */
>>
>> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
>> index 1cf31ddd7616..b144181d3f3a 100644
>> --- a/kernel/bpf/syscall.c
>> +++ b/kernel/bpf/syscall.c
>> @@ -313,7 +313,7 @@ static ssize_t bpf_dummy_write(struct file *filp,
>> const char __user *buf,
>>       return -EINVAL;
>>  }
>>
>> -static const struct file_operations bpf_map_fops = {
>> +const struct file_operations bpf_map_fops = {
>>  #ifdef CONFIG_PROC_FS
>>       .show_fdinfo    = bpf_map_show_fdinfo,
>>  #endif
>> @@ -964,7 +964,7 @@ static void bpf_prog_show_fdinfo(struct seq_file
>> *m, struct file *filp)
>>  }
>>  #endif
>>
>> -static const struct file_operations bpf_prog_fops = {
>> +const struct file_operations bpf_prog_fops = {
>>  #ifdef CONFIG_PROC_FS
>>       .show_fdinfo    = bpf_prog_show_fdinfo,
>>  #endif
>> diff --git a/security/security.c b/security/security.c
>> index 1cd8526cb0b7..dacf649b8cfa 100644
>> --- a/security/security.c
>> +++ b/security/security.c
>> @@ -1734,4 +1734,12 @@ void security_bpf_prog_free(struct
>> bpf_prog_aux *aux)
>>  {
>>       call_void_hook(bpf_prog_free_security, aux);
>>  }
>> +void security_bpf_map_file(struct bpf_map *map, struct file *file)
>> +{
>> +     call_void_hook(bpf_map_file, map, file);
>> +}
>> +void security_bpf_prog_file(struct bpf_prog_aux *aux, struct file
>> *file)
>> +{
>> +     call_void_hook(bpf_prog_file, aux, file);
>> +}
>>  #endif /* CONFIG_BPF_SYSCALL */
>> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
>> index 41aba4e3d57c..fea88655e0ee 100644
>> --- a/security/selinux/hooks.c
>> +++ b/security/selinux/hooks.c
>> @@ -1815,6 +1815,10 @@ static inline int file_path_has_perm(const
>> struct cred *cred,
>>       return inode_has_perm(cred, file_inode(file), av, &ad);
>>  }
>>
>> +#ifdef CONFIG_BPF_SYSCALL
>> +static int bpf_file_check(struct file *file, u32 sid);
>> +#endif
>> +
>>  /* Check whether a task can use an open file descriptor to
>>     access an inode in a given way.  Check access to the
>>     descriptor itself, and then use dentry_has_perm to
>> @@ -1845,6 +1849,12 @@ static int file_has_perm(const struct cred
>> *cred,
>>                       goto out;
>>       }
>>
>> +#ifdef CONFIG_BPF_SYSCALL
>> +     rc = bpf_file_check(file, cred_sid(cred));
>> +     if (rc)
>> +             goto out;
>> +#endif
>> +
>>       /* av is zero if only checking access to the descriptor. */
>>       rc = 0;
>>       if (av)
>> @@ -2165,6 +2175,12 @@ static int selinux_binder_transfer_file(struct
>> task_struct *from,
>>                       return rc;
>>       }
>>
>> +#ifdef CONFIG_BPF_SYSCALL
>> +     rc = bpf_file_check(file, sid);
>> +     if (rc)
>> +             return rc;
>> +#endif
>> +
>>       if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
>>               return 0;
>>
>> @@ -6288,6 +6304,33 @@ static u32 bpf_map_fmode_to_av(fmode_t fmode)
>>       return av;
>>  }
>>
>> +/* This function will check the file pass through unix socket or
>> binder to see
>> + * if it is a bpf related object. And apply correspinding checks on
>> the bpf
>> + * object based on the type. The bpf maps and programs, not like
>> other files and
>> + * socket, are using a shared anonymous inode inside the kernel as
>> their inode.
>> + * So checking that inode cannot identify if the process have
>> privilege to
>> + * access the bpf object and that's why we have to add this
>> additional check in
>> + * selinux_file_receive and selinux_binder_transfer_files.
>> + */
>> +static int bpf_file_check(struct file *file, u32 sid)
>> +{
>> +     struct file_security_struct *fsec = file->f_security;
>> +     int ret;
>> +
>> +     if (file->f_op == &bpf_map_fops) {
>> +             ret = avc_has_perm(sid, fsec->sid, SECCLASS_BPF_MAP,
>> +                                bpf_map_fmode_to_av(file-
>> >f_mode), NULL);
>> +             if (ret)
>> +                     return ret;
>> +     } else if (file->f_op == &bpf_prog_fops) {
>> +             ret = avc_has_perm(sid, fsec->sid,
>> SECCLASS_BPF_PROG,
>> +                                BPF_PROG__USE, NULL);
>> +             if (ret)
>> +                     return ret;
>> +     }
>> +     return 0;
>> +}
>> +
>>  static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
>>  {
>>       u32 sid = current_sid();
>> @@ -6351,6 +6394,22 @@ static void selinux_bpf_prog_free(struct
>> bpf_prog_aux *aux)
>>       aux->security = NULL;
>>       kfree(bpfsec);
>>  }
>> +
>> +static void selinux_bpf_map_file(struct bpf_map *map, struct file
>> *file)
>> +{
>> +     struct bpf_security_struct *bpfsec = map->security;
>> +     struct file_security_struct *fsec = file->f_security;
>> +
>> +     fsec->sid = bpfsec->sid;
>> +}
>> +
>> +static void selinux_bpf_prog_file(struct bpf_prog_aux *aux, struct
>> file *file)
>> +{
>> +     struct bpf_security_struct *bpfsec = aux->security;
>> +     struct file_security_struct *fsec = file->f_security;
>> +
>> +     fsec->sid = bpfsec->sid;
>
> I could be wrong, but isn't it the case that fsec->sid already will
> equal bpfsec->sid, because they are both created by the same thread
> during the same system call, and they each inherit the SID of the
> current task?
>
This is true when bpf object is created by the same process that
obtains the fd. But there are other ways of getting a bpf object fd
from the kernel such as bpf_obj_get and bpf_get_obj_fd_by_id. These
action will ask the kernel to allocate a new file for the bpf object
and the file sid would be the process ask for fd while the bpfsec->sid
is the sid when bpf object get created. These two could be different.
> What I expected you to do was to add and set a flags field in the
> file_security_struct to indicate that this is a bpf map or prog, and
> then test for that in your bpf_file_check() function instead of having
> to export and test the fops structures.
>
>
>> +}
>>  #endif
>>
>>  static struct security_hook_list selinux_hooks[] __lsm_ro_after_init
>> = {
>> @@ -6581,6 +6640,8 @@ static struct security_hook_list
>> selinux_hooks[] __lsm_ro_after_init = {
>>       LSM_HOOK_INIT(bpf_prog_alloc_security,
>> selinux_bpf_prog_alloc),
>>       LSM_HOOK_INIT(bpf_map_free_security, selinux_bpf_map_free),
>>       LSM_HOOK_INIT(bpf_prog_free_security,
>> selinux_bpf_prog_free),
>> +     LSM_HOOK_INIT(bpf_map_file, selinux_bpf_map_file),
>> +     LSM_HOOK_INIT(bpf_prog_file, selinux_bpf_prog_file),
>>  #endif
>>  };
>>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH net-next v2 5/5] selinux: bpf: Add addtional check for bpf object file receive
@ 2017-10-10 17:48       ` Chenbo Feng
  0 siblings, 0 replies; 39+ messages in thread
From: Chenbo Feng @ 2017-10-10 17:48 UTC (permalink / raw)
  To: linux-security-module

On Tue, Oct 10, 2017 at 7:24 AM, Stephen Smalley <sds@tycho.nsa.gov> wrote:
> On Mon, 2017-10-09 at 15:20 -0700, Chenbo Feng wrote:
>> From: Chenbo Feng <fengc@google.com>
>>
>> Introduce a bpf object related check when sending and receiving files
>> through unix domain socket as well as binder. It checks if the
>> receiving
>> process have privilege to read/write the bpf map or use the bpf
>> program.
>> This check is necessary because the bpf maps and programs are using a
>> anonymous inode as their shared inode so the normal way of checking
>> the
>> files and sockets when passing between processes cannot work properly
>> on
>> eBPF object. This check only works when the BPF_SYSCALL is
>> configured.
>> The information stored inside the file security struct is the same as
>> the information in bpf object security struct.
>>
>> Signed-off-by: Chenbo Feng <fengc@google.com>
>> ---
>>  include/linux/bpf.h       |  3 +++
>>  include/linux/lsm_hooks.h | 17 +++++++++++++
>>  include/linux/security.h  |  9 +++++++
>>  kernel/bpf/syscall.c      |  4 ++--
>>  security/security.c       |  8 +++++++
>>  security/selinux/hooks.c  | 61
>> +++++++++++++++++++++++++++++++++++++++++++++++
>>  6 files changed, 100 insertions(+), 2 deletions(-)
>>
>> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
>> index 225740688ab7..81d6c01b8825 100644
>> --- a/include/linux/bpf.h
>> +++ b/include/linux/bpf.h
>> @@ -285,6 +285,9 @@ int bpf_prog_array_copy_to_user(struct
>> bpf_prog_array __rcu *progs,
>>  #ifdef CONFIG_BPF_SYSCALL
>>  DECLARE_PER_CPU(int, bpf_prog_active);
>>
>> +extern const struct file_operations bpf_map_fops;
>> +extern const struct file_operations bpf_prog_fops;
>> +
>>  #define BPF_PROG_TYPE(_id, _ops) \
>>       extern const struct bpf_verifier_ops _ops;
>>  #define BPF_MAP_TYPE(_id, _ops) \
>> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
>> index 7161d8e7ee79..517dea60b87b 100644
>> --- a/include/linux/lsm_hooks.h
>> +++ b/include/linux/lsm_hooks.h
>> @@ -1385,6 +1385,19 @@
>>   * @bpf_prog_free_security:
>>   *   Clean up the security information stored inside bpf prog.
>>   *
>> + * @bpf_map_file:
>> + *   When creating a bpf map fd, set up the file security
>> information with
>> + *   the bpf security information stored in the map struct. So
>> when the map
>> + *   fd is passed between processes, the security module can
>> directly read
>> + *   the security information from file security struct rather
>> than the bpf
>> + *   security struct.
>> + *
>> + * @bpf_prog_file:
>> + *   When creating a bpf prog fd, set up the file security
>> information with
>> + *   the bpf security information stored in the prog struct. So
>> when the prog
>> + *   fd is passed between processes, the security module can
>> directly read
>> + *   the security information from file security struct rather
>> than the bpf
>> + *   security struct.
>>   */
>>  union security_list_options {
>>       int (*binder_set_context_mgr)(struct task_struct *mgr);
>> @@ -1726,6 +1739,8 @@ union security_list_options {
>>       void (*bpf_map_free_security)(struct bpf_map *map);
>>       int (*bpf_prog_alloc_security)(struct bpf_prog_aux *aux);
>>       void (*bpf_prog_free_security)(struct bpf_prog_aux *aux);
>> +     void (*bpf_map_file)(struct bpf_map *map, struct file
>> *file);
>> +     void (*bpf_prog_file)(struct bpf_prog_aux *aux, struct file
>> *file);
>>  #endif /* CONFIG_BPF_SYSCALL */
>>  };
>>
>> @@ -1954,6 +1969,8 @@ struct security_hook_heads {
>>       struct list_head bpf_map_free_security;
>>       struct list_head bpf_prog_alloc_security;
>>       struct list_head bpf_prog_free_security;
>> +     struct list_head bpf_map_file;
>> +     struct list_head bpf_prog_file;
>>  #endif /* CONFIG_BPF_SYSCALL */
>>  } __randomize_layout;
>>
>> diff --git a/include/linux/security.h b/include/linux/security.h
>> index 18800b0911e5..57573b794e2d 100644
>> --- a/include/linux/security.h
>> +++ b/include/linux/security.h
>> @@ -1740,6 +1740,8 @@ extern int security_bpf_map_alloc(struct
>> bpf_map *map);
>>  extern void security_bpf_map_free(struct bpf_map *map);
>>  extern int security_bpf_prog_alloc(struct bpf_prog_aux *aux);
>>  extern void security_bpf_prog_free(struct bpf_prog_aux *aux);
>> +extern void security_bpf_map_file(struct bpf_map *map, struct file
>> *file);
>> +extern void security_bpf_prog_file(struct bpf_prog_aux *aux, struct
>> file *file);
>>  #else
>>  static inline int security_bpf(int cmd, union bpf_attr *attr,
>>                                            unsigned int size)
>> @@ -1772,6 +1774,13 @@ static inline int
>> security_bpf_prog_alloc(struct bpf_prog_aux *aux)
>>
>>  static inline void security_bpf_prog_free(struct bpf_prog_aux *aux)
>>  { }
>> +
>> +static inline void security_bpf_map_file(struct bpf_map *map, struct
>> file *file)
>> +{ }
>> +
>> +static inline void security_bpf_prog_file(struct bpf_prog_aux *aux,
>> +                                       struct file *file)
>> +{ }
>>  #endif /* CONFIG_SECURITY */
>>  #endif /* CONFIG_BPF_SYSCALL */
>>
>> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
>> index 1cf31ddd7616..b144181d3f3a 100644
>> --- a/kernel/bpf/syscall.c
>> +++ b/kernel/bpf/syscall.c
>> @@ -313,7 +313,7 @@ static ssize_t bpf_dummy_write(struct file *filp,
>> const char __user *buf,
>>       return -EINVAL;
>>  }
>>
>> -static const struct file_operations bpf_map_fops = {
>> +const struct file_operations bpf_map_fops = {
>>  #ifdef CONFIG_PROC_FS
>>       .show_fdinfo    = bpf_map_show_fdinfo,
>>  #endif
>> @@ -964,7 +964,7 @@ static void bpf_prog_show_fdinfo(struct seq_file
>> *m, struct file *filp)
>>  }
>>  #endif
>>
>> -static const struct file_operations bpf_prog_fops = {
>> +const struct file_operations bpf_prog_fops = {
>>  #ifdef CONFIG_PROC_FS
>>       .show_fdinfo    = bpf_prog_show_fdinfo,
>>  #endif
>> diff --git a/security/security.c b/security/security.c
>> index 1cd8526cb0b7..dacf649b8cfa 100644
>> --- a/security/security.c
>> +++ b/security/security.c
>> @@ -1734,4 +1734,12 @@ void security_bpf_prog_free(struct
>> bpf_prog_aux *aux)
>>  {
>>       call_void_hook(bpf_prog_free_security, aux);
>>  }
>> +void security_bpf_map_file(struct bpf_map *map, struct file *file)
>> +{
>> +     call_void_hook(bpf_map_file, map, file);
>> +}
>> +void security_bpf_prog_file(struct bpf_prog_aux *aux, struct file
>> *file)
>> +{
>> +     call_void_hook(bpf_prog_file, aux, file);
>> +}
>>  #endif /* CONFIG_BPF_SYSCALL */
>> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
>> index 41aba4e3d57c..fea88655e0ee 100644
>> --- a/security/selinux/hooks.c
>> +++ b/security/selinux/hooks.c
>> @@ -1815,6 +1815,10 @@ static inline int file_path_has_perm(const
>> struct cred *cred,
>>       return inode_has_perm(cred, file_inode(file), av, &ad);
>>  }
>>
>> +#ifdef CONFIG_BPF_SYSCALL
>> +static int bpf_file_check(struct file *file, u32 sid);
>> +#endif
>> +
>>  /* Check whether a task can use an open file descriptor to
>>     access an inode in a given way.  Check access to the
>>     descriptor itself, and then use dentry_has_perm to
>> @@ -1845,6 +1849,12 @@ static int file_has_perm(const struct cred
>> *cred,
>>                       goto out;
>>       }
>>
>> +#ifdef CONFIG_BPF_SYSCALL
>> +     rc = bpf_file_check(file, cred_sid(cred));
>> +     if (rc)
>> +             goto out;
>> +#endif
>> +
>>       /* av is zero if only checking access to the descriptor. */
>>       rc = 0;
>>       if (av)
>> @@ -2165,6 +2175,12 @@ static int selinux_binder_transfer_file(struct
>> task_struct *from,
>>                       return rc;
>>       }
>>
>> +#ifdef CONFIG_BPF_SYSCALL
>> +     rc = bpf_file_check(file, sid);
>> +     if (rc)
>> +             return rc;
>> +#endif
>> +
>>       if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
>>               return 0;
>>
>> @@ -6288,6 +6304,33 @@ static u32 bpf_map_fmode_to_av(fmode_t fmode)
>>       return av;
>>  }
>>
>> +/* This function will check the file pass through unix socket or
>> binder to see
>> + * if it is a bpf related object. And apply correspinding checks on
>> the bpf
>> + * object based on the type. The bpf maps and programs, not like
>> other files and
>> + * socket, are using a shared anonymous inode inside the kernel as
>> their inode.
>> + * So checking that inode cannot identify if the process have
>> privilege to
>> + * access the bpf object and that's why we have to add this
>> additional check in
>> + * selinux_file_receive and selinux_binder_transfer_files.
>> + */
>> +static int bpf_file_check(struct file *file, u32 sid)
>> +{
>> +     struct file_security_struct *fsec = file->f_security;
>> +     int ret;
>> +
>> +     if (file->f_op == &bpf_map_fops) {
>> +             ret = avc_has_perm(sid, fsec->sid, SECCLASS_BPF_MAP,
>> +                                bpf_map_fmode_to_av(file-
>> >f_mode), NULL);
>> +             if (ret)
>> +                     return ret;
>> +     } else if (file->f_op == &bpf_prog_fops) {
>> +             ret = avc_has_perm(sid, fsec->sid,
>> SECCLASS_BPF_PROG,
>> +                                BPF_PROG__USE, NULL);
>> +             if (ret)
>> +                     return ret;
>> +     }
>> +     return 0;
>> +}
>> +
>>  static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
>>  {
>>       u32 sid = current_sid();
>> @@ -6351,6 +6394,22 @@ static void selinux_bpf_prog_free(struct
>> bpf_prog_aux *aux)
>>       aux->security = NULL;
>>       kfree(bpfsec);
>>  }
>> +
>> +static void selinux_bpf_map_file(struct bpf_map *map, struct file
>> *file)
>> +{
>> +     struct bpf_security_struct *bpfsec = map->security;
>> +     struct file_security_struct *fsec = file->f_security;
>> +
>> +     fsec->sid = bpfsec->sid;
>> +}
>> +
>> +static void selinux_bpf_prog_file(struct bpf_prog_aux *aux, struct
>> file *file)
>> +{
>> +     struct bpf_security_struct *bpfsec = aux->security;
>> +     struct file_security_struct *fsec = file->f_security;
>> +
>> +     fsec->sid = bpfsec->sid;
>
> I could be wrong, but isn't it the case that fsec->sid already will
> equal bpfsec->sid, because they are both created by the same thread
> during the same system call, and they each inherit the SID of the
> current task?
>
This is true when bpf object is created by the same process that
obtains the fd. But there are other ways of getting a bpf object fd
from the kernel such as bpf_obj_get and bpf_get_obj_fd_by_id. These
action will ask the kernel to allocate a new file for the bpf object
and the file sid would be the process ask for fd while the bpfsec->sid
is the sid when bpf object get created. These two could be different.
> What I expected you to do was to add and set a flags field in the
> file_security_struct to indicate that this is a bpf map or prog, and
> then test for that in your bpf_file_check() function instead of having
> to export and test the fops structures.
>
>
>> +}
>>  #endif
>>
>>  static struct security_hook_list selinux_hooks[] __lsm_ro_after_init
>> = {
>> @@ -6581,6 +6640,8 @@ static struct security_hook_list
>> selinux_hooks[] __lsm_ro_after_init = {
>>       LSM_HOOK_INIT(bpf_prog_alloc_security,
>> selinux_bpf_prog_alloc),
>>       LSM_HOOK_INIT(bpf_map_free_security, selinux_bpf_map_free),
>>       LSM_HOOK_INIT(bpf_prog_free_security,
>> selinux_bpf_prog_free),
>> +     LSM_HOOK_INIT(bpf_map_file, selinux_bpf_map_file),
>> +     LSM_HOOK_INIT(bpf_prog_file, selinux_bpf_prog_file),
>>  #endif
>>  };
>>
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH net-next v2 4/5] selinux: bpf: Add selinux check for eBPF syscall operations
  2017-10-10 14:52         ` Stephen Smalley
@ 2017-10-10 17:54           ` Chenbo Feng
  -1 siblings, 0 replies; 39+ messages in thread
From: Chenbo Feng @ 2017-10-10 17:54 UTC (permalink / raw)
  To: Stephen Smalley
  Cc: Chenbo Feng, linux-security-module, netdev, SELinux,
	Alexei Starovoitov, Daniel Borkmann, Lorenzo Colitti

On Tue, Oct 10, 2017 at 7:52 AM, Stephen Smalley <sds@tycho.nsa.gov> wrote:
> On Tue, 2017-10-10 at 10:18 -0400, Stephen Smalley wrote:
>> On Mon, 2017-10-09 at 15:20 -0700, Chenbo Feng wrote:
>> > From: Chenbo Feng <fengc@google.com>
>> >
>> > Implement the actual checks introduced to eBPF related syscalls.
>> > This
>> > implementation use the security field inside bpf object to store a
>> > sid that
>> > identify the bpf object. And when processes try to access the
>> > object,
>> > selinux will check if processes have the right privileges. The
>> > creation
>> > of eBPF object are also checked at the general bpf check hook and
>> > new
>> > cmd introduced to eBPF domain can also be checked there.
>> >
>> > Signed-off-by: Chenbo Feng <fengc@google.com>
>> > Acked-by: Alexei Starovoitov <ast@kernel.org>
>> > ---
>> >  security/selinux/hooks.c            | 111
>> > ++++++++++++++++++++++++++++++++++++
>> >  security/selinux/include/classmap.h |   2 +
>> >  security/selinux/include/objsec.h   |   4 ++
>> >  3 files changed, 117 insertions(+)
>> >
>> > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
>> > index f5d304736852..41aba4e3d57c 100644
>> > --- a/security/selinux/hooks.c
>> > +++ b/security/selinux/hooks.c
>> > @@ -85,6 +85,7 @@
>> >  #include <linux/export.h>
>> >  #include <linux/msg.h>
>> >  #include <linux/shm.h>
>> > +#include <linux/bpf.h>
>> >
>> >  #include "avc.h"
>> >  #include "objsec.h"
>> > @@ -6252,6 +6253,106 @@ static void selinux_ib_free_security(void
>> > *ib_sec)
>> >  }
>> >  #endif
>> >
>> > +#ifdef CONFIG_BPF_SYSCALL
>> > +static int selinux_bpf(int cmd, union bpf_attr *attr,
>> > +                                unsigned int size)
>> > +{
>> > +   u32 sid = current_sid();
>> > +   int ret;
>> > +
>> > +   switch (cmd) {
>> > +   case BPF_MAP_CREATE:
>> > +           ret = avc_has_perm(sid, sid, SECCLASS_BPF_MAP,
>> > BPF_MAP__CREATE,
>> > +                              NULL);
>> > +           break;
>> > +   case BPF_PROG_LOAD:
>> > +           ret = avc_has_perm(sid, sid, SECCLASS_BPF_PROG,
>> > BPF_PROG__LOAD,
>> > +                              NULL);
>> > +           break;
>> > +   default:
>> > +           ret = 0;
>> > +           break;
>> > +   }
>> > +
>> > +   return ret;
>> > +}
>> > +
>> > +static u32 bpf_map_fmode_to_av(fmode_t fmode)
>> > +{
>> > +   u32 av = 0;
>> > +
>> > +   if (f_mode & FMODE_READ)
>> > +           av |= BPF_MAP__READ;
>> > +   if (f_mode & FMODE_WRITE)
>> > +           av |= BPF_MAP__WRITE;
>> > +   return av;
>> > +}
>> > +
>> > +static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
>> > +{
>> > +   u32 sid = current_sid();
>> > +   struct bpf_security_struct *bpfsec;
>> > +
>> > +   bpfsec = map->security;
>> > +   return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF_MAP,
>> > +                       bpf_map_fmode_to_av(fmode), NULL);
>> > +}
>> > +
>> > +static int selinux_bpf_prog(struct bpf_prog *prog)
>> > +{
>> > +   u32 sid = current_sid();
>> > +   struct bpf_security_struct *bpfsec;
>> > +
>> > +   bpfsec = prog->aux->security;
>> > +   return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF_PROG,
>> > +                       BPF_PROG__USE, NULL);
>> > +}
>> > +
>> > +static int selinux_bpf_map_alloc(struct bpf_map *map)
>> > +{
>> > +   struct bpf_security_struct *bpfsec;
>> > +
>> > +   bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
>> > +   if (!bpfsec)
>> > +           return -ENOMEM;
>> > +
>> > +   bpfsec->sid = current_sid();
>> > +   map->security = bpfsec;
>> > +
>> > +   return 0;
>> > +}
>> > +
>> > +static void selinux_bpf_map_free(struct bpf_map *map)
>> > +{
>> > +   struct bpf_security_struct *bpfsec = map->security;
>> > +
>> > +   map->security = NULL;
>> > +   kfree(bpfsec);
>> > +}
>> > +
>> > +static int selinux_bpf_prog_alloc(struct bpf_prog_aux *aux)
>> > +{
>> > +   struct bpf_security_struct *bpfsec;
>> > +
>> > +   bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
>> > +   if (!bpfsec)
>> > +           return -ENOMEM;
>> > +
>> > +   bpfsec->sid = current_sid();
>> > +   aux->security = bpfsec;
>> > +
>> > +   return 0;
>> > +}
>> > +
>> > +static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
>> > +{
>> > +   struct bpf_security_struct *bpfsec = aux->security;
>> > +
>> > +   aux->security = NULL;
>> > +   kfree(bpfsec);
>> > +}
>> > +#endif
>> > +
>> >  static struct security_hook_list selinux_hooks[]
>> > __lsm_ro_after_init
>> > = {
>> >     LSM_HOOK_INIT(binder_set_context_mgr,
>> > selinux_binder_set_context_mgr),
>> >     LSM_HOOK_INIT(binder_transaction,
>> > selinux_binder_transaction),
>> > @@ -6471,6 +6572,16 @@ static struct security_hook_list
>> > selinux_hooks[] __lsm_ro_after_init = {
>> >     LSM_HOOK_INIT(audit_rule_match, selinux_audit_rule_match),
>> >     LSM_HOOK_INIT(audit_rule_free, selinux_audit_rule_free),
>> >  #endif
>> > +
>> > +#ifdef CONFIG_BPF_SYSCALL
>> > +   LSM_HOOK_INIT(bpf, selinux_bpf),
>> > +   LSM_HOOK_INIT(bpf_map, selinux_bpf_map),
>> > +   LSM_HOOK_INIT(bpf_prog, selinux_bpf_prog),
>> > +   LSM_HOOK_INIT(bpf_map_alloc_security,
>> > selinux_bpf_map_alloc),
>> > +   LSM_HOOK_INIT(bpf_prog_alloc_security,
>> > selinux_bpf_prog_alloc),
>> > +   LSM_HOOK_INIT(bpf_map_free_security,
>> > selinux_bpf_map_free),
>> > +   LSM_HOOK_INIT(bpf_prog_free_security,
>> > selinux_bpf_prog_free),
>> > +#endif
>> >  };
>> >
>> >  static __init int selinux_init(void)
>> > diff --git a/security/selinux/include/classmap.h
>> > b/security/selinux/include/classmap.h
>> > index 35ffb29a69cb..7253c5eea59c 100644
>> > --- a/security/selinux/include/classmap.h
>> > +++ b/security/selinux/include/classmap.h
>> > @@ -237,6 +237,8 @@ struct security_class_mapping secclass_map[] =
>> > {
>> >       { "access", NULL } },
>> >     { "infiniband_endport",
>> >       { "manage_subnet", NULL } },
>> > +   { "bpf_map", {"create", "read", "write"} },
>> > +   { "bpf_prog", {"load", "use"} },
>>
>> Again I have to ask: do you truly need/want two separate classes, or
>> would a single class with distinct permissions suffice, ala:
>>         { "bpf", { "create_map", "read_map", "write_map",
>> "prog_load",
>> "prog_use" } },
>>
>> and then allow A self:bpf { create_map read_map write_map prog_load
>> prog_use }; would be stored in a single policy avtab rule, and be
>> cached in a single AVC entry.
>
Sorry I missed to reply on this, I keep it that way because sometimes
we need to grant the permission of accessing eBPF maps and programs
separately. But keep them in a single class definitely works for me.
> I guess for consistency in naming it should be either:
>         { "bpf", { "map_create", "map_read", "map_write", "prog_load",
> "prog_use" } },
>
> or:
>
>         { "bpf", { "create_map", "read_map", "write_map", "load_prog",
> "use_prog" } },
>
>
>> >     { NULL }
>> >    };
>> >
>> > diff --git a/security/selinux/include/objsec.h
>> > b/security/selinux/include/objsec.h
>> > index 1649cd18eb0b..3d54468ce334 100644
>> > --- a/security/selinux/include/objsec.h
>> > +++ b/security/selinux/include/objsec.h
>> > @@ -150,6 +150,10 @@ struct pkey_security_struct {
>> >     u32     sid;    /* SID of pkey */
>> >  };
>> >
>> > +struct bpf_security_struct {
>> > +   u32 sid;  /*SID of bpf obj creater*/
>> > +};
>> > +
>> >  extern unsigned int selinux_checkreqprot;
>> >
>> >  #endif /* _SELINUX_OBJSEC_H_ */

^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH net-next v2 4/5] selinux: bpf: Add selinux check for eBPF syscall operations
@ 2017-10-10 17:54           ` Chenbo Feng
  0 siblings, 0 replies; 39+ messages in thread
From: Chenbo Feng @ 2017-10-10 17:54 UTC (permalink / raw)
  To: linux-security-module

On Tue, Oct 10, 2017 at 7:52 AM, Stephen Smalley <sds@tycho.nsa.gov> wrote:
> On Tue, 2017-10-10 at 10:18 -0400, Stephen Smalley wrote:
>> On Mon, 2017-10-09 at 15:20 -0700, Chenbo Feng wrote:
>> > From: Chenbo Feng <fengc@google.com>
>> >
>> > Implement the actual checks introduced to eBPF related syscalls.
>> > This
>> > implementation use the security field inside bpf object to store a
>> > sid that
>> > identify the bpf object. And when processes try to access the
>> > object,
>> > selinux will check if processes have the right privileges. The
>> > creation
>> > of eBPF object are also checked at the general bpf check hook and
>> > new
>> > cmd introduced to eBPF domain can also be checked there.
>> >
>> > Signed-off-by: Chenbo Feng <fengc@google.com>
>> > Acked-by: Alexei Starovoitov <ast@kernel.org>
>> > ---
>> >  security/selinux/hooks.c            | 111
>> > ++++++++++++++++++++++++++++++++++++
>> >  security/selinux/include/classmap.h |   2 +
>> >  security/selinux/include/objsec.h   |   4 ++
>> >  3 files changed, 117 insertions(+)
>> >
>> > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
>> > index f5d304736852..41aba4e3d57c 100644
>> > --- a/security/selinux/hooks.c
>> > +++ b/security/selinux/hooks.c
>> > @@ -85,6 +85,7 @@
>> >  #include <linux/export.h>
>> >  #include <linux/msg.h>
>> >  #include <linux/shm.h>
>> > +#include <linux/bpf.h>
>> >
>> >  #include "avc.h"
>> >  #include "objsec.h"
>> > @@ -6252,6 +6253,106 @@ static void selinux_ib_free_security(void
>> > *ib_sec)
>> >  }
>> >  #endif
>> >
>> > +#ifdef CONFIG_BPF_SYSCALL
>> > +static int selinux_bpf(int cmd, union bpf_attr *attr,
>> > +                                unsigned int size)
>> > +{
>> > +   u32 sid = current_sid();
>> > +   int ret;
>> > +
>> > +   switch (cmd) {
>> > +   case BPF_MAP_CREATE:
>> > +           ret = avc_has_perm(sid, sid, SECCLASS_BPF_MAP,
>> > BPF_MAP__CREATE,
>> > +                              NULL);
>> > +           break;
>> > +   case BPF_PROG_LOAD:
>> > +           ret = avc_has_perm(sid, sid, SECCLASS_BPF_PROG,
>> > BPF_PROG__LOAD,
>> > +                              NULL);
>> > +           break;
>> > +   default:
>> > +           ret = 0;
>> > +           break;
>> > +   }
>> > +
>> > +   return ret;
>> > +}
>> > +
>> > +static u32 bpf_map_fmode_to_av(fmode_t fmode)
>> > +{
>> > +   u32 av = 0;
>> > +
>> > +   if (f_mode & FMODE_READ)
>> > +           av |= BPF_MAP__READ;
>> > +   if (f_mode & FMODE_WRITE)
>> > +           av |= BPF_MAP__WRITE;
>> > +   return av;
>> > +}
>> > +
>> > +static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
>> > +{
>> > +   u32 sid = current_sid();
>> > +   struct bpf_security_struct *bpfsec;
>> > +
>> > +   bpfsec = map->security;
>> > +   return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF_MAP,
>> > +                       bpf_map_fmode_to_av(fmode), NULL);
>> > +}
>> > +
>> > +static int selinux_bpf_prog(struct bpf_prog *prog)
>> > +{
>> > +   u32 sid = current_sid();
>> > +   struct bpf_security_struct *bpfsec;
>> > +
>> > +   bpfsec = prog->aux->security;
>> > +   return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF_PROG,
>> > +                       BPF_PROG__USE, NULL);
>> > +}
>> > +
>> > +static int selinux_bpf_map_alloc(struct bpf_map *map)
>> > +{
>> > +   struct bpf_security_struct *bpfsec;
>> > +
>> > +   bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
>> > +   if (!bpfsec)
>> > +           return -ENOMEM;
>> > +
>> > +   bpfsec->sid = current_sid();
>> > +   map->security = bpfsec;
>> > +
>> > +   return 0;
>> > +}
>> > +
>> > +static void selinux_bpf_map_free(struct bpf_map *map)
>> > +{
>> > +   struct bpf_security_struct *bpfsec = map->security;
>> > +
>> > +   map->security = NULL;
>> > +   kfree(bpfsec);
>> > +}
>> > +
>> > +static int selinux_bpf_prog_alloc(struct bpf_prog_aux *aux)
>> > +{
>> > +   struct bpf_security_struct *bpfsec;
>> > +
>> > +   bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
>> > +   if (!bpfsec)
>> > +           return -ENOMEM;
>> > +
>> > +   bpfsec->sid = current_sid();
>> > +   aux->security = bpfsec;
>> > +
>> > +   return 0;
>> > +}
>> > +
>> > +static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
>> > +{
>> > +   struct bpf_security_struct *bpfsec = aux->security;
>> > +
>> > +   aux->security = NULL;
>> > +   kfree(bpfsec);
>> > +}
>> > +#endif
>> > +
>> >  static struct security_hook_list selinux_hooks[]
>> > __lsm_ro_after_init
>> > = {
>> >     LSM_HOOK_INIT(binder_set_context_mgr,
>> > selinux_binder_set_context_mgr),
>> >     LSM_HOOK_INIT(binder_transaction,
>> > selinux_binder_transaction),
>> > @@ -6471,6 +6572,16 @@ static struct security_hook_list
>> > selinux_hooks[] __lsm_ro_after_init = {
>> >     LSM_HOOK_INIT(audit_rule_match, selinux_audit_rule_match),
>> >     LSM_HOOK_INIT(audit_rule_free, selinux_audit_rule_free),
>> >  #endif
>> > +
>> > +#ifdef CONFIG_BPF_SYSCALL
>> > +   LSM_HOOK_INIT(bpf, selinux_bpf),
>> > +   LSM_HOOK_INIT(bpf_map, selinux_bpf_map),
>> > +   LSM_HOOK_INIT(bpf_prog, selinux_bpf_prog),
>> > +   LSM_HOOK_INIT(bpf_map_alloc_security,
>> > selinux_bpf_map_alloc),
>> > +   LSM_HOOK_INIT(bpf_prog_alloc_security,
>> > selinux_bpf_prog_alloc),
>> > +   LSM_HOOK_INIT(bpf_map_free_security,
>> > selinux_bpf_map_free),
>> > +   LSM_HOOK_INIT(bpf_prog_free_security,
>> > selinux_bpf_prog_free),
>> > +#endif
>> >  };
>> >
>> >  static __init int selinux_init(void)
>> > diff --git a/security/selinux/include/classmap.h
>> > b/security/selinux/include/classmap.h
>> > index 35ffb29a69cb..7253c5eea59c 100644
>> > --- a/security/selinux/include/classmap.h
>> > +++ b/security/selinux/include/classmap.h
>> > @@ -237,6 +237,8 @@ struct security_class_mapping secclass_map[] =
>> > {
>> >       { "access", NULL } },
>> >     { "infiniband_endport",
>> >       { "manage_subnet", NULL } },
>> > +   { "bpf_map", {"create", "read", "write"} },
>> > +   { "bpf_prog", {"load", "use"} },
>>
>> Again I have to ask: do you truly need/want two separate classes, or
>> would a single class with distinct permissions suffice, ala:
>>         { "bpf", { "create_map", "read_map", "write_map",
>> "prog_load",
>> "prog_use" } },
>>
>> and then allow A self:bpf { create_map read_map write_map prog_load
>> prog_use }; would be stored in a single policy avtab rule, and be
>> cached in a single AVC entry.
>
Sorry I missed to reply on this, I keep it that way because sometimes
we need to grant the permission of accessing eBPF maps and programs
separately. But keep them in a single class definitely works for me.
> I guess for consistency in naming it should be either:
>         { "bpf", { "map_create", "map_read", "map_write", "prog_load",
> "prog_use" } },
>
> or:
>
>         { "bpf", { "create_map", "read_map", "write_map", "load_prog",
> "use_prog" } },
>
>
>> >     { NULL }
>> >    };
>> >
>> > diff --git a/security/selinux/include/objsec.h
>> > b/security/selinux/include/objsec.h
>> > index 1649cd18eb0b..3d54468ce334 100644
>> > --- a/security/selinux/include/objsec.h
>> > +++ b/security/selinux/include/objsec.h
>> > @@ -150,6 +150,10 @@ struct pkey_security_struct {
>> >     u32     sid;    /* SID of pkey */
>> >  };
>> >
>> > +struct bpf_security_struct {
>> > +   u32 sid;  /*SID of bpf obj creater*/
>> > +};
>> > +
>> >  extern unsigned int selinux_checkreqprot;
>> >
>> >  #endif /* _SELINUX_OBJSEC_H_ */
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH net-next v2 4/5] selinux: bpf: Add selinux check for eBPF syscall operations
  2017-10-09 22:20   ` Chenbo Feng
@ 2017-10-10 17:59     ` kbuild test robot
  -1 siblings, 0 replies; 39+ messages in thread
From: kbuild test robot @ 2017-10-10 17:59 UTC (permalink / raw)
  To: Chenbo Feng
  Cc: kbuild-all, linux-security-module, netdev, SELinux,
	Jeffrey Vander Stoep, Alexei Starovoitov, lorenzo,
	Daniel Borkmann, Stephen Smalley, Chenbo Feng

[-- Attachment #1: Type: text/plain, Size: 1370 bytes --]

Hi Chenbo,

[auto build test ERROR on net-next/master]

url:    https://github.com/0day-ci/linux/commits/Chenbo-Feng/bpf-security-New-file-mode-and-LSM-hooks-for-eBPF-object-permission-control/20171011-010349
config: xtensa-allyesconfig (attached as .config)
compiler: xtensa-linux-gcc (GCC) 4.9.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=xtensa 

All errors (new ones prefixed by >>):

   security//selinux/hooks.c: In function 'bpf_map_fmode_to_av':
>> security//selinux/hooks.c:6284:6: error: 'f_mode' undeclared (first use in this function)
     if (f_mode & FMODE_READ)
         ^
   security//selinux/hooks.c:6284:6: note: each undeclared identifier is reported only once for each function it appears in

vim +/f_mode +6284 security//selinux/hooks.c

  6279	
  6280	static u32 bpf_map_fmode_to_av(fmode_t fmode)
  6281	{
  6282		u32 av = 0;
  6283	
> 6284		if (f_mode & FMODE_READ)
  6285			av |= BPF_MAP__READ;
  6286		if (f_mode & FMODE_WRITE)
  6287			av |= BPF_MAP__WRITE;
  6288		return av;
  6289	}
  6290	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 51658 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH net-next v2 4/5] selinux: bpf: Add selinux check for eBPF syscall operations
@ 2017-10-10 17:59     ` kbuild test robot
  0 siblings, 0 replies; 39+ messages in thread
From: kbuild test robot @ 2017-10-10 17:59 UTC (permalink / raw)
  To: linux-security-module

Hi Chenbo,

[auto build test ERROR on net-next/master]

url:    https://github.com/0day-ci/linux/commits/Chenbo-Feng/bpf-security-New-file-mode-and-LSM-hooks-for-eBPF-object-permission-control/20171011-010349
config: xtensa-allyesconfig (attached as .config)
compiler: xtensa-linux-gcc (GCC) 4.9.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=xtensa 

All errors (new ones prefixed by >>):

   security//selinux/hooks.c: In function 'bpf_map_fmode_to_av':
>> security//selinux/hooks.c:6284:6: error: 'f_mode' undeclared (first use in this function)
     if (f_mode & FMODE_READ)
         ^
   security//selinux/hooks.c:6284:6: note: each undeclared identifier is reported only once for each function it appears in

vim +/f_mode +6284 security//selinux/hooks.c

  6279	
  6280	static u32 bpf_map_fmode_to_av(fmode_t fmode)
  6281	{
  6282		u32 av = 0;
  6283	
> 6284		if (f_mode & FMODE_READ)
  6285			av |= BPF_MAP__READ;
  6286		if (f_mode & FMODE_WRITE)
  6287			av |= BPF_MAP__WRITE;
  6288		return av;
  6289	}
  6290	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Non-DoD Source] Re: [PATCH net-next v2 5/5] selinux: bpf: Add addtional check for bpf object file receive
  2017-10-10 17:48       ` Chenbo Feng
  (?)
@ 2017-10-10 19:23         ` Stephen Smalley
  -1 siblings, 0 replies; 39+ messages in thread
From: Stephen Smalley @ 2017-10-10 19:23 UTC (permalink / raw)
  To: Chenbo Feng
  Cc: Chenbo Feng, linux-security-module, netdev, SELinux,
	Daniel Borkmann, Alexei Starovoitov, Lorenzo Colitti

On Tue, 2017-10-10 at 10:48 -0700, Chenbo Feng wrote:
> On Tue, Oct 10, 2017 at 7:24 AM, Stephen Smalley <sds@tycho.nsa.gov>
> wrote:
> > On Mon, 2017-10-09 at 15:20 -0700, Chenbo Feng wrote:
> > > From: Chenbo Feng <fengc@google.com>
> > > 
> > > Introduce a bpf object related check when sending and receiving
> > > files
> > > through unix domain socket as well as binder. It checks if the
> > > receiving
> > > process have privilege to read/write the bpf map or use the bpf
> > > program.
> > > This check is necessary because the bpf maps and programs are
> > > using a
> > > anonymous inode as their shared inode so the normal way of
> > > checking
> > > the
> > > files and sockets when passing between processes cannot work
> > > properly
> > > on
> > > eBPF object. This check only works when the BPF_SYSCALL is
> > > configured.
> > > The information stored inside the file security struct is the
> > > same as
> > > the information in bpf object security struct.
> > > 
> > > Signed-off-by: Chenbo Feng <fengc@google.com>
> > > ---
> > >  include/linux/bpf.h       |  3 +++
> > >  include/linux/lsm_hooks.h | 17 +++++++++++++
> > >  include/linux/security.h  |  9 +++++++
> > >  kernel/bpf/syscall.c      |  4 ++--
> > >  security/security.c       |  8 +++++++
> > >  security/selinux/hooks.c  | 61
> > > +++++++++++++++++++++++++++++++++++++++++++++++
> > >  6 files changed, 100 insertions(+), 2 deletions(-)
> > > 
> > > diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> > > index 225740688ab7..81d6c01b8825 100644
> > > --- a/include/linux/bpf.h
> > > +++ b/include/linux/bpf.h
> > > @@ -285,6 +285,9 @@ int bpf_prog_array_copy_to_user(struct
> > > bpf_prog_array __rcu *progs,
> > >  #ifdef CONFIG_BPF_SYSCALL
> > >  DECLARE_PER_CPU(int, bpf_prog_active);
> > > 
> > > +extern const struct file_operations bpf_map_fops;
> > > +extern const struct file_operations bpf_prog_fops;
> > > +
> > >  #define BPF_PROG_TYPE(_id, _ops) \
> > >       extern const struct bpf_verifier_ops _ops;
> > >  #define BPF_MAP_TYPE(_id, _ops) \
> > > diff --git a/include/linux/lsm_hooks.h
> > > b/include/linux/lsm_hooks.h
> > > index 7161d8e7ee79..517dea60b87b 100644
> > > --- a/include/linux/lsm_hooks.h
> > > +++ b/include/linux/lsm_hooks.h
> > > @@ -1385,6 +1385,19 @@
> > >   * @bpf_prog_free_security:
> > >   *   Clean up the security information stored inside bpf prog.
> > >   *
> > > + * @bpf_map_file:
> > > + *   When creating a bpf map fd, set up the file security
> > > information with
> > > + *   the bpf security information stored in the map struct. So
> > > when the map
> > > + *   fd is passed between processes, the security module can
> > > directly read
> > > + *   the security information from file security struct rather
> > > than the bpf
> > > + *   security struct.
> > > + *
> > > + * @bpf_prog_file:
> > > + *   When creating a bpf prog fd, set up the file security
> > > information with
> > > + *   the bpf security information stored in the prog struct. So
> > > when the prog
> > > + *   fd is passed between processes, the security module can
> > > directly read
> > > + *   the security information from file security struct rather
> > > than the bpf
> > > + *   security struct.
> > >   */
> > >  union security_list_options {
> > >       int (*binder_set_context_mgr)(struct task_struct *mgr);
> > > @@ -1726,6 +1739,8 @@ union security_list_options {
> > >       void (*bpf_map_free_security)(struct bpf_map *map);
> > >       int (*bpf_prog_alloc_security)(struct bpf_prog_aux *aux);
> > >       void (*bpf_prog_free_security)(struct bpf_prog_aux *aux);
> > > +     void (*bpf_map_file)(struct bpf_map *map, struct file
> > > *file);
> > > +     void (*bpf_prog_file)(struct bpf_prog_aux *aux, struct file
> > > *file);
> > >  #endif /* CONFIG_BPF_SYSCALL */
> > >  };
> > > 
> > > @@ -1954,6 +1969,8 @@ struct security_hook_heads {
> > >       struct list_head bpf_map_free_security;
> > >       struct list_head bpf_prog_alloc_security;
> > >       struct list_head bpf_prog_free_security;
> > > +     struct list_head bpf_map_file;
> > > +     struct list_head bpf_prog_file;
> > >  #endif /* CONFIG_BPF_SYSCALL */
> > >  } __randomize_layout;
> > > 
> > > diff --git a/include/linux/security.h b/include/linux/security.h
> > > index 18800b0911e5..57573b794e2d 100644
> > > --- a/include/linux/security.h
> > > +++ b/include/linux/security.h
> > > @@ -1740,6 +1740,8 @@ extern int security_bpf_map_alloc(struct
> > > bpf_map *map);
> > >  extern void security_bpf_map_free(struct bpf_map *map);
> > >  extern int security_bpf_prog_alloc(struct bpf_prog_aux *aux);
> > >  extern void security_bpf_prog_free(struct bpf_prog_aux *aux);
> > > +extern void security_bpf_map_file(struct bpf_map *map, struct
> > > file
> > > *file);
> > > +extern void security_bpf_prog_file(struct bpf_prog_aux *aux,
> > > struct
> > > file *file);
> > >  #else
> > >  static inline int security_bpf(int cmd, union bpf_attr *attr,
> > >                                            unsigned int size)
> > > @@ -1772,6 +1774,13 @@ static inline int
> > > security_bpf_prog_alloc(struct bpf_prog_aux *aux)
> > > 
> > >  static inline void security_bpf_prog_free(struct bpf_prog_aux
> > > *aux)
> > >  { }
> > > +
> > > +static inline void security_bpf_map_file(struct bpf_map *map,
> > > struct
> > > file *file)
> > > +{ }
> > > +
> > > +static inline void security_bpf_prog_file(struct bpf_prog_aux
> > > *aux,
> > > +                                       struct file *file)
> > > +{ }
> > >  #endif /* CONFIG_SECURITY */
> > >  #endif /* CONFIG_BPF_SYSCALL */
> > > 
> > > diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> > > index 1cf31ddd7616..b144181d3f3a 100644
> > > --- a/kernel/bpf/syscall.c
> > > +++ b/kernel/bpf/syscall.c
> > > @@ -313,7 +313,7 @@ static ssize_t bpf_dummy_write(struct file
> > > *filp,
> > > const char __user *buf,
> > >       return -EINVAL;
> > >  }
> > > 
> > > -static const struct file_operations bpf_map_fops = {
> > > +const struct file_operations bpf_map_fops = {
> > >  #ifdef CONFIG_PROC_FS
> > >       .show_fdinfo    = bpf_map_show_fdinfo,
> > >  #endif
> > > @@ -964,7 +964,7 @@ static void bpf_prog_show_fdinfo(struct
> > > seq_file
> > > *m, struct file *filp)
> > >  }
> > >  #endif
> > > 
> > > -static const struct file_operations bpf_prog_fops = {
> > > +const struct file_operations bpf_prog_fops = {
> > >  #ifdef CONFIG_PROC_FS
> > >       .show_fdinfo    = bpf_prog_show_fdinfo,
> > >  #endif
> > > diff --git a/security/security.c b/security/security.c
> > > index 1cd8526cb0b7..dacf649b8cfa 100644
> > > --- a/security/security.c
> > > +++ b/security/security.c
> > > @@ -1734,4 +1734,12 @@ void security_bpf_prog_free(struct
> > > bpf_prog_aux *aux)
> > >  {
> > >       call_void_hook(bpf_prog_free_security, aux);
> > >  }
> > > +void security_bpf_map_file(struct bpf_map *map, struct file
> > > *file)
> > > +{
> > > +     call_void_hook(bpf_map_file, map, file);
> > > +}
> > > +void security_bpf_prog_file(struct bpf_prog_aux *aux, struct
> > > file
> > > *file)
> > > +{
> > > +     call_void_hook(bpf_prog_file, aux, file);
> > > +}
> > >  #endif /* CONFIG_BPF_SYSCALL */
> > > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> > > index 41aba4e3d57c..fea88655e0ee 100644
> > > --- a/security/selinux/hooks.c
> > > +++ b/security/selinux/hooks.c
> > > @@ -1815,6 +1815,10 @@ static inline int file_path_has_perm(const
> > > struct cred *cred,
> > >       return inode_has_perm(cred, file_inode(file), av, &ad);
> > >  }
> > > 
> > > +#ifdef CONFIG_BPF_SYSCALL
> > > +static int bpf_file_check(struct file *file, u32 sid);
> > > +#endif
> > > +
> > >  /* Check whether a task can use an open file descriptor to
> > >     access an inode in a given way.  Check access to the
> > >     descriptor itself, and then use dentry_has_perm to
> > > @@ -1845,6 +1849,12 @@ static int file_has_perm(const struct cred
> > > *cred,
> > >                       goto out;
> > >       }
> > > 
> > > +#ifdef CONFIG_BPF_SYSCALL
> > > +     rc = bpf_file_check(file, cred_sid(cred));
> > > +     if (rc)
> > > +             goto out;
> > > +#endif
> > > +
> > >       /* av is zero if only checking access to the descriptor. */
> > >       rc = 0;
> > >       if (av)
> > > @@ -2165,6 +2175,12 @@ static int
> > > selinux_binder_transfer_file(struct
> > > task_struct *from,
> > >                       return rc;
> > >       }
> > > 
> > > +#ifdef CONFIG_BPF_SYSCALL
> > > +     rc = bpf_file_check(file, sid);
> > > +     if (rc)
> > > +             return rc;
> > > +#endif
> > > +
> > >       if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> > >               return 0;
> > > 
> > > @@ -6288,6 +6304,33 @@ static u32 bpf_map_fmode_to_av(fmode_t
> > > fmode)
> > >       return av;
> > >  }
> > > 
> > > +/* This function will check the file pass through unix socket or
> > > binder to see
> > > + * if it is a bpf related object. And apply correspinding checks
> > > on
> > > the bpf
> > > + * object based on the type. The bpf maps and programs, not like
> > > other files and
> > > + * socket, are using a shared anonymous inode inside the kernel
> > > as
> > > their inode.
> > > + * So checking that inode cannot identify if the process have
> > > privilege to
> > > + * access the bpf object and that's why we have to add this
> > > additional check in
> > > + * selinux_file_receive and selinux_binder_transfer_files.
> > > + */
> > > +static int bpf_file_check(struct file *file, u32 sid)
> > > +{
> > > +     struct file_security_struct *fsec = file->f_security;
> > > +     int ret;
> > > +
> > > +     if (file->f_op == &bpf_map_fops) {
> > > +             ret = avc_has_perm(sid, fsec->sid,
> > > SECCLASS_BPF_MAP,
> > > +                                bpf_map_fmode_to_av(file-
> > > > f_mode), NULL);
> > > 
> > > +             if (ret)
> > > +                     return ret;
> > > +     } else if (file->f_op == &bpf_prog_fops) {
> > > +             ret = avc_has_perm(sid, fsec->sid,
> > > SECCLASS_BPF_PROG,
> > > +                                BPF_PROG__USE, NULL);
> > > +             if (ret)
> > > +                     return ret;
> > > +     }
> > > +     return 0;
> > > +}
> > > +
> > >  static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
> > >  {
> > >       u32 sid = current_sid();
> > > @@ -6351,6 +6394,22 @@ static void selinux_bpf_prog_free(struct
> > > bpf_prog_aux *aux)
> > >       aux->security = NULL;
> > >       kfree(bpfsec);
> > >  }
> > > +
> > > +static void selinux_bpf_map_file(struct bpf_map *map, struct
> > > file
> > > *file)
> > > +{
> > > +     struct bpf_security_struct *bpfsec = map->security;
> > > +     struct file_security_struct *fsec = file->f_security;
> > > +
> > > +     fsec->sid = bpfsec->sid;
> > > +}
> > > +
> > > +static void selinux_bpf_prog_file(struct bpf_prog_aux *aux,
> > > struct
> > > file *file)
> > > +{
> > > +     struct bpf_security_struct *bpfsec = aux->security;
> > > +     struct file_security_struct *fsec = file->f_security;
> > > +
> > > +     fsec->sid = bpfsec->sid;
> > 
> > I could be wrong, but isn't it the case that fsec->sid already will
> > equal bpfsec->sid, because they are both created by the same thread
> > during the same system call, and they each inherit the SID of the
> > current task?
> > 
> 
> This is true when bpf object is created by the same process that
> obtains the fd. But there are other ways of getting a bpf object fd
> from the kernel such as bpf_obj_get and bpf_get_obj_fd_by_id. These
> action will ask the kernel to allocate a new file for the bpf object
> and the file sid would be the process ask for fd while the bpfsec-
> >sid
> is the sid when bpf object get created. These two could be different.

Oh, in that case you shouldn't change the fsec->sid; you'll need to use
the bpfsec->sid in your checks instead.  But you can still do what I
described below.

> > What I expected you to do was to add and set a flags field in the
> > file_security_struct to indicate that this is a bpf map or prog,
> > and
> > then test for that in your bpf_file_check() function instead of
> > having
> > to export and test the fops structures.
> > 
> > 
> > > +}
> > >  #endif
> > > 
> > >  static struct security_hook_list selinux_hooks[]
> > > __lsm_ro_after_init
> > > = {
> > > @@ -6581,6 +6640,8 @@ static struct security_hook_list
> > > selinux_hooks[] __lsm_ro_after_init = {
> > >       LSM_HOOK_INIT(bpf_prog_alloc_security,
> > > selinux_bpf_prog_alloc),
> > >       LSM_HOOK_INIT(bpf_map_free_security, selinux_bpf_map_free),
> > >       LSM_HOOK_INIT(bpf_prog_free_security,
> > > selinux_bpf_prog_free),
> > > +     LSM_HOOK_INIT(bpf_map_file, selinux_bpf_map_file),
> > > +     LSM_HOOK_INIT(bpf_prog_file, selinux_bpf_prog_file),
> > >  #endif
> > >  };
> > > 

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re:  Re: [PATCH net-next v2 5/5] selinux: bpf: Add addtional check for bpf object file receive
@ 2017-10-10 19:23         ` Stephen Smalley
  0 siblings, 0 replies; 39+ messages in thread
From: Stephen Smalley @ 2017-10-10 19:23 UTC (permalink / raw)
  To: Chenbo Feng
  Cc: Chenbo Feng, linux-security-module, netdev, SELinux,
	Daniel Borkmann, Alexei Starovoitov, Lorenzo Colitti

On Tue, 2017-10-10 at 10:48 -0700, Chenbo Feng wrote:
> On Tue, Oct 10, 2017 at 7:24 AM, Stephen Smalley <sds@tycho.nsa.gov>
> wrote:
> > On Mon, 2017-10-09 at 15:20 -0700, Chenbo Feng wrote:
> > > From: Chenbo Feng <fengc@google.com>
> > > 
> > > Introduce a bpf object related check when sending and receiving
> > > files
> > > through unix domain socket as well as binder. It checks if the
> > > receiving
> > > process have privilege to read/write the bpf map or use the bpf
> > > program.
> > > This check is necessary because the bpf maps and programs are
> > > using a
> > > anonymous inode as their shared inode so the normal way of
> > > checking
> > > the
> > > files and sockets when passing between processes cannot work
> > > properly
> > > on
> > > eBPF object. This check only works when the BPF_SYSCALL is
> > > configured.
> > > The information stored inside the file security struct is the
> > > same as
> > > the information in bpf object security struct.
> > > 
> > > Signed-off-by: Chenbo Feng <fengc@google.com>
> > > ---
> > >  include/linux/bpf.h       |  3 +++
> > >  include/linux/lsm_hooks.h | 17 +++++++++++++
> > >  include/linux/security.h  |  9 +++++++
> > >  kernel/bpf/syscall.c      |  4 ++--
> > >  security/security.c       |  8 +++++++
> > >  security/selinux/hooks.c  | 61
> > > +++++++++++++++++++++++++++++++++++++++++++++++
> > >  6 files changed, 100 insertions(+), 2 deletions(-)
> > > 
> > > diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> > > index 225740688ab7..81d6c01b8825 100644
> > > --- a/include/linux/bpf.h
> > > +++ b/include/linux/bpf.h
> > > @@ -285,6 +285,9 @@ int bpf_prog_array_copy_to_user(struct
> > > bpf_prog_array __rcu *progs,
> > >  #ifdef CONFIG_BPF_SYSCALL
> > >  DECLARE_PER_CPU(int, bpf_prog_active);
> > > 
> > > +extern const struct file_operations bpf_map_fops;
> > > +extern const struct file_operations bpf_prog_fops;
> > > +
> > >  #define BPF_PROG_TYPE(_id, _ops) \
> > >       extern const struct bpf_verifier_ops _ops;
> > >  #define BPF_MAP_TYPE(_id, _ops) \
> > > diff --git a/include/linux/lsm_hooks.h
> > > b/include/linux/lsm_hooks.h
> > > index 7161d8e7ee79..517dea60b87b 100644
> > > --- a/include/linux/lsm_hooks.h
> > > +++ b/include/linux/lsm_hooks.h
> > > @@ -1385,6 +1385,19 @@
> > >   * @bpf_prog_free_security:
> > >   *   Clean up the security information stored inside bpf prog.
> > >   *
> > > + * @bpf_map_file:
> > > + *   When creating a bpf map fd, set up the file security
> > > information with
> > > + *   the bpf security information stored in the map struct. So
> > > when the map
> > > + *   fd is passed between processes, the security module can
> > > directly read
> > > + *   the security information from file security struct rather
> > > than the bpf
> > > + *   security struct.
> > > + *
> > > + * @bpf_prog_file:
> > > + *   When creating a bpf prog fd, set up the file security
> > > information with
> > > + *   the bpf security information stored in the prog struct. So
> > > when the prog
> > > + *   fd is passed between processes, the security module can
> > > directly read
> > > + *   the security information from file security struct rather
> > > than the bpf
> > > + *   security struct.
> > >   */
> > >  union security_list_options {
> > >       int (*binder_set_context_mgr)(struct task_struct *mgr);
> > > @@ -1726,6 +1739,8 @@ union security_list_options {
> > >       void (*bpf_map_free_security)(struct bpf_map *map);
> > >       int (*bpf_prog_alloc_security)(struct bpf_prog_aux *aux);
> > >       void (*bpf_prog_free_security)(struct bpf_prog_aux *aux);
> > > +     void (*bpf_map_file)(struct bpf_map *map, struct file
> > > *file);
> > > +     void (*bpf_prog_file)(struct bpf_prog_aux *aux, struct file
> > > *file);
> > >  #endif /* CONFIG_BPF_SYSCALL */
> > >  };
> > > 
> > > @@ -1954,6 +1969,8 @@ struct security_hook_heads {
> > >       struct list_head bpf_map_free_security;
> > >       struct list_head bpf_prog_alloc_security;
> > >       struct list_head bpf_prog_free_security;
> > > +     struct list_head bpf_map_file;
> > > +     struct list_head bpf_prog_file;
> > >  #endif /* CONFIG_BPF_SYSCALL */
> > >  } __randomize_layout;
> > > 
> > > diff --git a/include/linux/security.h b/include/linux/security.h
> > > index 18800b0911e5..57573b794e2d 100644
> > > --- a/include/linux/security.h
> > > +++ b/include/linux/security.h
> > > @@ -1740,6 +1740,8 @@ extern int security_bpf_map_alloc(struct
> > > bpf_map *map);
> > >  extern void security_bpf_map_free(struct bpf_map *map);
> > >  extern int security_bpf_prog_alloc(struct bpf_prog_aux *aux);
> > >  extern void security_bpf_prog_free(struct bpf_prog_aux *aux);
> > > +extern void security_bpf_map_file(struct bpf_map *map, struct
> > > file
> > > *file);
> > > +extern void security_bpf_prog_file(struct bpf_prog_aux *aux,
> > > struct
> > > file *file);
> > >  #else
> > >  static inline int security_bpf(int cmd, union bpf_attr *attr,
> > >                                            unsigned int size)
> > > @@ -1772,6 +1774,13 @@ static inline int
> > > security_bpf_prog_alloc(struct bpf_prog_aux *aux)
> > > 
> > >  static inline void security_bpf_prog_free(struct bpf_prog_aux
> > > *aux)
> > >  { }
> > > +
> > > +static inline void security_bpf_map_file(struct bpf_map *map,
> > > struct
> > > file *file)
> > > +{ }
> > > +
> > > +static inline void security_bpf_prog_file(struct bpf_prog_aux
> > > *aux,
> > > +                                       struct file *file)
> > > +{ }
> > >  #endif /* CONFIG_SECURITY */
> > >  #endif /* CONFIG_BPF_SYSCALL */
> > > 
> > > diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> > > index 1cf31ddd7616..b144181d3f3a 100644
> > > --- a/kernel/bpf/syscall.c
> > > +++ b/kernel/bpf/syscall.c
> > > @@ -313,7 +313,7 @@ static ssize_t bpf_dummy_write(struct file
> > > *filp,
> > > const char __user *buf,
> > >       return -EINVAL;
> > >  }
> > > 
> > > -static const struct file_operations bpf_map_fops = {
> > > +const struct file_operations bpf_map_fops = {
> > >  #ifdef CONFIG_PROC_FS
> > >       .show_fdinfo    = bpf_map_show_fdinfo,
> > >  #endif
> > > @@ -964,7 +964,7 @@ static void bpf_prog_show_fdinfo(struct
> > > seq_file
> > > *m, struct file *filp)
> > >  }
> > >  #endif
> > > 
> > > -static const struct file_operations bpf_prog_fops = {
> > > +const struct file_operations bpf_prog_fops = {
> > >  #ifdef CONFIG_PROC_FS
> > >       .show_fdinfo    = bpf_prog_show_fdinfo,
> > >  #endif
> > > diff --git a/security/security.c b/security/security.c
> > > index 1cd8526cb0b7..dacf649b8cfa 100644
> > > --- a/security/security.c
> > > +++ b/security/security.c
> > > @@ -1734,4 +1734,12 @@ void security_bpf_prog_free(struct
> > > bpf_prog_aux *aux)
> > >  {
> > >       call_void_hook(bpf_prog_free_security, aux);
> > >  }
> > > +void security_bpf_map_file(struct bpf_map *map, struct file
> > > *file)
> > > +{
> > > +     call_void_hook(bpf_map_file, map, file);
> > > +}
> > > +void security_bpf_prog_file(struct bpf_prog_aux *aux, struct
> > > file
> > > *file)
> > > +{
> > > +     call_void_hook(bpf_prog_file, aux, file);
> > > +}
> > >  #endif /* CONFIG_BPF_SYSCALL */
> > > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> > > index 41aba4e3d57c..fea88655e0ee 100644
> > > --- a/security/selinux/hooks.c
> > > +++ b/security/selinux/hooks.c
> > > @@ -1815,6 +1815,10 @@ static inline int file_path_has_perm(const
> > > struct cred *cred,
> > >       return inode_has_perm(cred, file_inode(file), av, &ad);
> > >  }
> > > 
> > > +#ifdef CONFIG_BPF_SYSCALL
> > > +static int bpf_file_check(struct file *file, u32 sid);
> > > +#endif
> > > +
> > >  /* Check whether a task can use an open file descriptor to
> > >     access an inode in a given way.  Check access to the
> > >     descriptor itself, and then use dentry_has_perm to
> > > @@ -1845,6 +1849,12 @@ static int file_has_perm(const struct cred
> > > *cred,
> > >                       goto out;
> > >       }
> > > 
> > > +#ifdef CONFIG_BPF_SYSCALL
> > > +     rc = bpf_file_check(file, cred_sid(cred));
> > > +     if (rc)
> > > +             goto out;
> > > +#endif
> > > +
> > >       /* av is zero if only checking access to the descriptor. */
> > >       rc = 0;
> > >       if (av)
> > > @@ -2165,6 +2175,12 @@ static int
> > > selinux_binder_transfer_file(struct
> > > task_struct *from,
> > >                       return rc;
> > >       }
> > > 
> > > +#ifdef CONFIG_BPF_SYSCALL
> > > +     rc = bpf_file_check(file, sid);
> > > +     if (rc)
> > > +             return rc;
> > > +#endif
> > > +
> > >       if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> > >               return 0;
> > > 
> > > @@ -6288,6 +6304,33 @@ static u32 bpf_map_fmode_to_av(fmode_t
> > > fmode)
> > >       return av;
> > >  }
> > > 
> > > +/* This function will check the file pass through unix socket or
> > > binder to see
> > > + * if it is a bpf related object. And apply correspinding checks
> > > on
> > > the bpf
> > > + * object based on the type. The bpf maps and programs, not like
> > > other files and
> > > + * socket, are using a shared anonymous inode inside the kernel
> > > as
> > > their inode.
> > > + * So checking that inode cannot identify if the process have
> > > privilege to
> > > + * access the bpf object and that's why we have to add this
> > > additional check in
> > > + * selinux_file_receive and selinux_binder_transfer_files.
> > > + */
> > > +static int bpf_file_check(struct file *file, u32 sid)
> > > +{
> > > +     struct file_security_struct *fsec = file->f_security;
> > > +     int ret;
> > > +
> > > +     if (file->f_op == &bpf_map_fops) {
> > > +             ret = avc_has_perm(sid, fsec->sid,
> > > SECCLASS_BPF_MAP,
> > > +                                bpf_map_fmode_to_av(file-
> > > > f_mode), NULL);
> > > 
> > > +             if (ret)
> > > +                     return ret;
> > > +     } else if (file->f_op == &bpf_prog_fops) {
> > > +             ret = avc_has_perm(sid, fsec->sid,
> > > SECCLASS_BPF_PROG,
> > > +                                BPF_PROG__USE, NULL);
> > > +             if (ret)
> > > +                     return ret;
> > > +     }
> > > +     return 0;
> > > +}
> > > +
> > >  static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
> > >  {
> > >       u32 sid = current_sid();
> > > @@ -6351,6 +6394,22 @@ static void selinux_bpf_prog_free(struct
> > > bpf_prog_aux *aux)
> > >       aux->security = NULL;
> > >       kfree(bpfsec);
> > >  }
> > > +
> > > +static void selinux_bpf_map_file(struct bpf_map *map, struct
> > > file
> > > *file)
> > > +{
> > > +     struct bpf_security_struct *bpfsec = map->security;
> > > +     struct file_security_struct *fsec = file->f_security;
> > > +
> > > +     fsec->sid = bpfsec->sid;
> > > +}
> > > +
> > > +static void selinux_bpf_prog_file(struct bpf_prog_aux *aux,
> > > struct
> > > file *file)
> > > +{
> > > +     struct bpf_security_struct *bpfsec = aux->security;
> > > +     struct file_security_struct *fsec = file->f_security;
> > > +
> > > +     fsec->sid = bpfsec->sid;
> > 
> > I could be wrong, but isn't it the case that fsec->sid already will
> > equal bpfsec->sid, because they are both created by the same thread
> > during the same system call, and they each inherit the SID of the
> > current task?
> > 
> 
> This is true when bpf object is created by the same process that
> obtains the fd. But there are other ways of getting a bpf object fd
> from the kernel such as bpf_obj_get and bpf_get_obj_fd_by_id. These
> action will ask the kernel to allocate a new file for the bpf object
> and the file sid would be the process ask for fd while the bpfsec-
> >sid
> is the sid when bpf object get created. These two could be different.

Oh, in that case you shouldn't change the fsec->sid; you'll need to use
the bpfsec->sid in your checks instead.  But you can still do what I
described below.

> > What I expected you to do was to add and set a flags field in the
> > file_security_struct to indicate that this is a bpf map or prog,
> > and
> > then test for that in your bpf_file_check() function instead of
> > having
> > to export and test the fops structures.
> > 
> > 
> > > +}
> > >  #endif
> > > 
> > >  static struct security_hook_list selinux_hooks[]
> > > __lsm_ro_after_init
> > > = {
> > > @@ -6581,6 +6640,8 @@ static struct security_hook_list
> > > selinux_hooks[] __lsm_ro_after_init = {
> > >       LSM_HOOK_INIT(bpf_prog_alloc_security,
> > > selinux_bpf_prog_alloc),
> > >       LSM_HOOK_INIT(bpf_map_free_security, selinux_bpf_map_free),
> > >       LSM_HOOK_INIT(bpf_prog_free_security,
> > > selinux_bpf_prog_free),
> > > +     LSM_HOOK_INIT(bpf_map_file, selinux_bpf_map_file),
> > > +     LSM_HOOK_INIT(bpf_prog_file, selinux_bpf_prog_file),
> > >  #endif
> > >  };
> > > 

^ permalink raw reply	[flat|nested] 39+ messages in thread

* [Non-DoD Source] Re: [PATCH net-next v2 5/5] selinux: bpf: Add addtional check for bpf object file receive
@ 2017-10-10 19:23         ` Stephen Smalley
  0 siblings, 0 replies; 39+ messages in thread
From: Stephen Smalley @ 2017-10-10 19:23 UTC (permalink / raw)
  To: linux-security-module

On Tue, 2017-10-10 at 10:48 -0700, Chenbo Feng wrote:
> On Tue, Oct 10, 2017 at 7:24 AM, Stephen Smalley <sds@tycho.nsa.gov>
> wrote:
> > On Mon, 2017-10-09 at 15:20 -0700, Chenbo Feng wrote:
> > > From: Chenbo Feng <fengc@google.com>
> > > 
> > > Introduce a bpf object related check when sending and receiving
> > > files
> > > through unix domain socket as well as binder. It checks if the
> > > receiving
> > > process have privilege to read/write the bpf map or use the bpf
> > > program.
> > > This check is necessary because the bpf maps and programs are
> > > using a
> > > anonymous inode as their shared inode so the normal way of
> > > checking
> > > the
> > > files and sockets when passing between processes cannot work
> > > properly
> > > on
> > > eBPF object. This check only works when the BPF_SYSCALL is
> > > configured.
> > > The information stored inside the file security struct is the
> > > same as
> > > the information in bpf object security struct.
> > > 
> > > Signed-off-by: Chenbo Feng <fengc@google.com>
> > > ---
> > > ?include/linux/bpf.h???????|??3 +++
> > > ?include/linux/lsm_hooks.h | 17 +++++++++++++
> > > ?include/linux/security.h??|??9 +++++++
> > > ?kernel/bpf/syscall.c??????|??4 ++--
> > > ?security/security.c???????|??8 +++++++
> > > ?security/selinux/hooks.c??| 61
> > > +++++++++++++++++++++++++++++++++++++++++++++++
> > > ?6 files changed, 100 insertions(+), 2 deletions(-)
> > > 
> > > diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> > > index 225740688ab7..81d6c01b8825 100644
> > > --- a/include/linux/bpf.h
> > > +++ b/include/linux/bpf.h
> > > @@ -285,6 +285,9 @@ int bpf_prog_array_copy_to_user(struct
> > > bpf_prog_array __rcu *progs,
> > > ?#ifdef CONFIG_BPF_SYSCALL
> > > ?DECLARE_PER_CPU(int, bpf_prog_active);
> > > 
> > > +extern const struct file_operations bpf_map_fops;
> > > +extern const struct file_operations bpf_prog_fops;
> > > +
> > > ?#define BPF_PROG_TYPE(_id, _ops) \
> > > ??????extern const struct bpf_verifier_ops _ops;
> > > ?#define BPF_MAP_TYPE(_id, _ops) \
> > > diff --git a/include/linux/lsm_hooks.h
> > > b/include/linux/lsm_hooks.h
> > > index 7161d8e7ee79..517dea60b87b 100644
> > > --- a/include/linux/lsm_hooks.h
> > > +++ b/include/linux/lsm_hooks.h
> > > @@ -1385,6 +1385,19 @@
> > > ? * @bpf_prog_free_security:
> > > ? *???Clean up the security information stored inside bpf prog.
> > > ? *
> > > + * @bpf_map_file:
> > > + *???When creating a bpf map fd, set up the file security
> > > information with
> > > + *???the bpf security information stored in the map struct. So
> > > when the map
> > > + *???fd is passed between processes, the security module can
> > > directly read
> > > + *???the security information from file security struct rather
> > > than the bpf
> > > + *???security struct.
> > > + *
> > > + * @bpf_prog_file:
> > > + *???When creating a bpf prog fd, set up the file security
> > > information with
> > > + *???the bpf security information stored in the prog struct. So
> > > when the prog
> > > + *???fd is passed between processes, the security module can
> > > directly read
> > > + *???the security information from file security struct rather
> > > than the bpf
> > > + *???security struct.
> > > ? */
> > > ?union security_list_options {
> > > ??????int (*binder_set_context_mgr)(struct task_struct *mgr);
> > > @@ -1726,6 +1739,8 @@ union security_list_options {
> > > ??????void (*bpf_map_free_security)(struct bpf_map *map);
> > > ??????int (*bpf_prog_alloc_security)(struct bpf_prog_aux *aux);
> > > ??????void (*bpf_prog_free_security)(struct bpf_prog_aux *aux);
> > > +?????void (*bpf_map_file)(struct bpf_map *map, struct file
> > > *file);
> > > +?????void (*bpf_prog_file)(struct bpf_prog_aux *aux, struct file
> > > *file);
> > > ?#endif /* CONFIG_BPF_SYSCALL */
> > > ?};
> > > 
> > > @@ -1954,6 +1969,8 @@ struct security_hook_heads {
> > > ??????struct list_head bpf_map_free_security;
> > > ??????struct list_head bpf_prog_alloc_security;
> > > ??????struct list_head bpf_prog_free_security;
> > > +?????struct list_head bpf_map_file;
> > > +?????struct list_head bpf_prog_file;
> > > ?#endif /* CONFIG_BPF_SYSCALL */
> > > ?} __randomize_layout;
> > > 
> > > diff --git a/include/linux/security.h b/include/linux/security.h
> > > index 18800b0911e5..57573b794e2d 100644
> > > --- a/include/linux/security.h
> > > +++ b/include/linux/security.h
> > > @@ -1740,6 +1740,8 @@ extern int security_bpf_map_alloc(struct
> > > bpf_map *map);
> > > ?extern void security_bpf_map_free(struct bpf_map *map);
> > > ?extern int security_bpf_prog_alloc(struct bpf_prog_aux *aux);
> > > ?extern void security_bpf_prog_free(struct bpf_prog_aux *aux);
> > > +extern void security_bpf_map_file(struct bpf_map *map, struct
> > > file
> > > *file);
> > > +extern void security_bpf_prog_file(struct bpf_prog_aux *aux,
> > > struct
> > > file *file);
> > > ?#else
> > > ?static inline int security_bpf(int cmd, union bpf_attr *attr,
> > > ???????????????????????????????????????????unsigned int size)
> > > @@ -1772,6 +1774,13 @@ static inline int
> > > security_bpf_prog_alloc(struct bpf_prog_aux *aux)
> > > 
> > > ?static inline void security_bpf_prog_free(struct bpf_prog_aux
> > > *aux)
> > > ?{ }
> > > +
> > > +static inline void security_bpf_map_file(struct bpf_map *map,
> > > struct
> > > file *file)
> > > +{ }
> > > +
> > > +static inline void security_bpf_prog_file(struct bpf_prog_aux
> > > *aux,
> > > +???????????????????????????????????????struct file *file)
> > > +{ }
> > > ?#endif /* CONFIG_SECURITY */
> > > ?#endif /* CONFIG_BPF_SYSCALL */
> > > 
> > > diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> > > index 1cf31ddd7616..b144181d3f3a 100644
> > > --- a/kernel/bpf/syscall.c
> > > +++ b/kernel/bpf/syscall.c
> > > @@ -313,7 +313,7 @@ static ssize_t bpf_dummy_write(struct file
> > > *filp,
> > > const char __user *buf,
> > > ??????return -EINVAL;
> > > ?}
> > > 
> > > -static const struct file_operations bpf_map_fops = {
> > > +const struct file_operations bpf_map_fops = {
> > > ?#ifdef CONFIG_PROC_FS
> > > ??????.show_fdinfo????= bpf_map_show_fdinfo,
> > > ?#endif
> > > @@ -964,7 +964,7 @@ static void bpf_prog_show_fdinfo(struct
> > > seq_file
> > > *m, struct file *filp)
> > > ?}
> > > ?#endif
> > > 
> > > -static const struct file_operations bpf_prog_fops = {
> > > +const struct file_operations bpf_prog_fops = {
> > > ?#ifdef CONFIG_PROC_FS
> > > ??????.show_fdinfo????= bpf_prog_show_fdinfo,
> > > ?#endif
> > > diff --git a/security/security.c b/security/security.c
> > > index 1cd8526cb0b7..dacf649b8cfa 100644
> > > --- a/security/security.c
> > > +++ b/security/security.c
> > > @@ -1734,4 +1734,12 @@ void security_bpf_prog_free(struct
> > > bpf_prog_aux *aux)
> > > ?{
> > > ??????call_void_hook(bpf_prog_free_security, aux);
> > > ?}
> > > +void security_bpf_map_file(struct bpf_map *map, struct file
> > > *file)
> > > +{
> > > +?????call_void_hook(bpf_map_file, map, file);
> > > +}
> > > +void security_bpf_prog_file(struct bpf_prog_aux *aux, struct
> > > file
> > > *file)
> > > +{
> > > +?????call_void_hook(bpf_prog_file, aux, file);
> > > +}
> > > ?#endif /* CONFIG_BPF_SYSCALL */
> > > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> > > index 41aba4e3d57c..fea88655e0ee 100644
> > > --- a/security/selinux/hooks.c
> > > +++ b/security/selinux/hooks.c
> > > @@ -1815,6 +1815,10 @@ static inline int file_path_has_perm(const
> > > struct cred *cred,
> > > ??????return inode_has_perm(cred, file_inode(file), av, &ad);
> > > ?}
> > > 
> > > +#ifdef CONFIG_BPF_SYSCALL
> > > +static int bpf_file_check(struct file *file, u32 sid);
> > > +#endif
> > > +
> > > ?/* Check whether a task can use an open file descriptor to
> > > ????access an inode in a given way.??Check access to the
> > > ????descriptor itself, and then use dentry_has_perm to
> > > @@ -1845,6 +1849,12 @@ static int file_has_perm(const struct cred
> > > *cred,
> > > ??????????????????????goto out;
> > > ??????}
> > > 
> > > +#ifdef CONFIG_BPF_SYSCALL
> > > +?????rc = bpf_file_check(file, cred_sid(cred));
> > > +?????if (rc)
> > > +?????????????goto out;
> > > +#endif
> > > +
> > > ??????/* av is zero if only checking access to the descriptor. */
> > > ??????rc = 0;
> > > ??????if (av)
> > > @@ -2165,6 +2175,12 @@ static int
> > > selinux_binder_transfer_file(struct
> > > task_struct *from,
> > > ??????????????????????return rc;
> > > ??????}
> > > 
> > > +#ifdef CONFIG_BPF_SYSCALL
> > > +?????rc = bpf_file_check(file, sid);
> > > +?????if (rc)
> > > +?????????????return rc;
> > > +#endif
> > > +
> > > ??????if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> > > ??????????????return 0;
> > > 
> > > @@ -6288,6 +6304,33 @@ static u32 bpf_map_fmode_to_av(fmode_t
> > > fmode)
> > > ??????return av;
> > > ?}
> > > 
> > > +/* This function will check the file pass through unix socket or
> > > binder to see
> > > + * if it is a bpf related object. And apply correspinding checks
> > > on
> > > the bpf
> > > + * object based on the type. The bpf maps and programs, not like
> > > other files and
> > > + * socket, are using a shared anonymous inode inside the kernel
> > > as
> > > their inode.
> > > + * So checking that inode cannot identify if the process have
> > > privilege to
> > > + * access the bpf object and that's why we have to add this
> > > additional check in
> > > + * selinux_file_receive and selinux_binder_transfer_files.
> > > + */
> > > +static int bpf_file_check(struct file *file, u32 sid)
> > > +{
> > > +?????struct file_security_struct *fsec = file->f_security;
> > > +?????int ret;
> > > +
> > > +?????if (file->f_op == &bpf_map_fops) {
> > > +?????????????ret = avc_has_perm(sid, fsec->sid,
> > > SECCLASS_BPF_MAP,
> > > +????????????????????????????????bpf_map_fmode_to_av(file-
> > > > f_mode), NULL);
> > > 
> > > +?????????????if (ret)
> > > +?????????????????????return ret;
> > > +?????} else if (file->f_op == &bpf_prog_fops) {
> > > +?????????????ret = avc_has_perm(sid, fsec->sid,
> > > SECCLASS_BPF_PROG,
> > > +????????????????????????????????BPF_PROG__USE, NULL);
> > > +?????????????if (ret)
> > > +?????????????????????return ret;
> > > +?????}
> > > +?????return 0;
> > > +}
> > > +
> > > ?static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
> > > ?{
> > > ??????u32 sid = current_sid();
> > > @@ -6351,6 +6394,22 @@ static void selinux_bpf_prog_free(struct
> > > bpf_prog_aux *aux)
> > > ??????aux->security = NULL;
> > > ??????kfree(bpfsec);
> > > ?}
> > > +
> > > +static void selinux_bpf_map_file(struct bpf_map *map, struct
> > > file
> > > *file)
> > > +{
> > > +?????struct bpf_security_struct *bpfsec = map->security;
> > > +?????struct file_security_struct *fsec = file->f_security;
> > > +
> > > +?????fsec->sid = bpfsec->sid;
> > > +}
> > > +
> > > +static void selinux_bpf_prog_file(struct bpf_prog_aux *aux,
> > > struct
> > > file *file)
> > > +{
> > > +?????struct bpf_security_struct *bpfsec = aux->security;
> > > +?????struct file_security_struct *fsec = file->f_security;
> > > +
> > > +?????fsec->sid = bpfsec->sid;
> > 
> > I could be wrong, but isn't it the case that fsec->sid already will
> > equal bpfsec->sid, because they are both created by the same thread
> > during the same system call, and they each inherit the SID of the
> > current task?
> > 
> 
> This is true when bpf object is created by the same process that
> obtains the fd. But there are other ways of getting a bpf object fd
> from the kernel such as bpf_obj_get and bpf_get_obj_fd_by_id. These
> action will ask the kernel to allocate a new file for the bpf object
> and the file sid would be the process ask for fd while the bpfsec-
> >sid
> is the sid when bpf object get created. These two could be different.

Oh, in that case you shouldn't change the fsec->sid; you'll need to use
the bpfsec->sid in your checks instead.  But you can still do what I
described below.

> > What I expected you to do was to add and set a flags field in the
> > file_security_struct to indicate that this is a bpf map or prog,
> > and
> > then test for that in your bpf_file_check() function instead of
> > having
> > to export and test the fops structures.
> > 
> > 
> > > +}
> > > ?#endif
> > > 
> > > ?static struct security_hook_list selinux_hooks[]
> > > __lsm_ro_after_init
> > > = {
> > > @@ -6581,6 +6640,8 @@ static struct security_hook_list
> > > selinux_hooks[] __lsm_ro_after_init = {
> > > ??????LSM_HOOK_INIT(bpf_prog_alloc_security,
> > > selinux_bpf_prog_alloc),
> > > ??????LSM_HOOK_INIT(bpf_map_free_security, selinux_bpf_map_free),
> > > ??????LSM_HOOK_INIT(bpf_prog_free_security,
> > > selinux_bpf_prog_free),
> > > +?????LSM_HOOK_INIT(bpf_map_file, selinux_bpf_map_file),
> > > +?????LSM_HOOK_INIT(bpf_prog_file, selinux_bpf_prog_file),
> > > ?#endif
> > > ?};
> > > 
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Non-DoD Source] Re: [PATCH net-next v2 5/5] selinux: bpf: Add addtional check for bpf object file receive
  2017-10-10 19:23         ` Stephen Smalley
  (?)
@ 2017-10-10 19:42           ` Chenbo Feng
  -1 siblings, 0 replies; 39+ messages in thread
From: Chenbo Feng @ 2017-10-10 19:42 UTC (permalink / raw)
  To: Stephen Smalley
  Cc: Chenbo Feng, linux-security-module, netdev, SELinux,
	Daniel Borkmann, Alexei Starovoitov, Lorenzo Colitti

On Tue, Oct 10, 2017 at 12:23 PM, Stephen Smalley <sds@tycho.nsa.gov> wrote:
> On Tue, 2017-10-10 at 10:48 -0700, Chenbo Feng wrote:
>> On Tue, Oct 10, 2017 at 7:24 AM, Stephen Smalley <sds@tycho.nsa.gov>
>> wrote:
>> > On Mon, 2017-10-09 at 15:20 -0700, Chenbo Feng wrote:
>> > > From: Chenbo Feng <fengc@google.com>
>> > >
>> > > Introduce a bpf object related check when sending and receiving
>> > > files
>> > > through unix domain socket as well as binder. It checks if the
>> > > receiving
>> > > process have privilege to read/write the bpf map or use the bpf
>> > > program.
>> > > This check is necessary because the bpf maps and programs are
>> > > using a
>> > > anonymous inode as their shared inode so the normal way of
>> > > checking
>> > > the
>> > > files and sockets when passing between processes cannot work
>> > > properly
>> > > on
>> > > eBPF object. This check only works when the BPF_SYSCALL is
>> > > configured.
>> > > The information stored inside the file security struct is the
>> > > same as
>> > > the information in bpf object security struct.
>> > >
>> > > Signed-off-by: Chenbo Feng <fengc@google.com>
>> > > ---
>> > >  include/linux/bpf.h       |  3 +++
>> > >  include/linux/lsm_hooks.h | 17 +++++++++++++
>> > >  include/linux/security.h  |  9 +++++++
>> > >  kernel/bpf/syscall.c      |  4 ++--
>> > >  security/security.c       |  8 +++++++
>> > >  security/selinux/hooks.c  | 61
>> > > +++++++++++++++++++++++++++++++++++++++++++++++
>> > >  6 files changed, 100 insertions(+), 2 deletions(-)
>> > >
>> > > diff --git a/include/linux/bpf.h b/include/linux/bpf.h
>> > > index 225740688ab7..81d6c01b8825 100644
>> > > --- a/include/linux/bpf.h
>> > > +++ b/include/linux/bpf.h
>> > > @@ -285,6 +285,9 @@ int bpf_prog_array_copy_to_user(struct
>> > > bpf_prog_array __rcu *progs,
>> > >  #ifdef CONFIG_BPF_SYSCALL
>> > >  DECLARE_PER_CPU(int, bpf_prog_active);
>> > >
>> > > +extern const struct file_operations bpf_map_fops;
>> > > +extern const struct file_operations bpf_prog_fops;
>> > > +
>> > >  #define BPF_PROG_TYPE(_id, _ops) \
>> > >       extern const struct bpf_verifier_ops _ops;
>> > >  #define BPF_MAP_TYPE(_id, _ops) \
>> > > diff --git a/include/linux/lsm_hooks.h
>> > > b/include/linux/lsm_hooks.h
>> > > index 7161d8e7ee79..517dea60b87b 100644
>> > > --- a/include/linux/lsm_hooks.h
>> > > +++ b/include/linux/lsm_hooks.h
>> > > @@ -1385,6 +1385,19 @@
>> > >   * @bpf_prog_free_security:
>> > >   *   Clean up the security information stored inside bpf prog.
>> > >   *
>> > > + * @bpf_map_file:
>> > > + *   When creating a bpf map fd, set up the file security
>> > > information with
>> > > + *   the bpf security information stored in the map struct. So
>> > > when the map
>> > > + *   fd is passed between processes, the security module can
>> > > directly read
>> > > + *   the security information from file security struct rather
>> > > than the bpf
>> > > + *   security struct.
>> > > + *
>> > > + * @bpf_prog_file:
>> > > + *   When creating a bpf prog fd, set up the file security
>> > > information with
>> > > + *   the bpf security information stored in the prog struct. So
>> > > when the prog
>> > > + *   fd is passed between processes, the security module can
>> > > directly read
>> > > + *   the security information from file security struct rather
>> > > than the bpf
>> > > + *   security struct.
>> > >   */
>> > >  union security_list_options {
>> > >       int (*binder_set_context_mgr)(struct task_struct *mgr);
>> > > @@ -1726,6 +1739,8 @@ union security_list_options {
>> > >       void (*bpf_map_free_security)(struct bpf_map *map);
>> > >       int (*bpf_prog_alloc_security)(struct bpf_prog_aux *aux);
>> > >       void (*bpf_prog_free_security)(struct bpf_prog_aux *aux);
>> > > +     void (*bpf_map_file)(struct bpf_map *map, struct file
>> > > *file);
>> > > +     void (*bpf_prog_file)(struct bpf_prog_aux *aux, struct file
>> > > *file);
>> > >  #endif /* CONFIG_BPF_SYSCALL */
>> > >  };
>> > >
>> > > @@ -1954,6 +1969,8 @@ struct security_hook_heads {
>> > >       struct list_head bpf_map_free_security;
>> > >       struct list_head bpf_prog_alloc_security;
>> > >       struct list_head bpf_prog_free_security;
>> > > +     struct list_head bpf_map_file;
>> > > +     struct list_head bpf_prog_file;
>> > >  #endif /* CONFIG_BPF_SYSCALL */
>> > >  } __randomize_layout;
>> > >
>> > > diff --git a/include/linux/security.h b/include/linux/security.h
>> > > index 18800b0911e5..57573b794e2d 100644
>> > > --- a/include/linux/security.h
>> > > +++ b/include/linux/security.h
>> > > @@ -1740,6 +1740,8 @@ extern int security_bpf_map_alloc(struct
>> > > bpf_map *map);
>> > >  extern void security_bpf_map_free(struct bpf_map *map);
>> > >  extern int security_bpf_prog_alloc(struct bpf_prog_aux *aux);
>> > >  extern void security_bpf_prog_free(struct bpf_prog_aux *aux);
>> > > +extern void security_bpf_map_file(struct bpf_map *map, struct
>> > > file
>> > > *file);
>> > > +extern void security_bpf_prog_file(struct bpf_prog_aux *aux,
>> > > struct
>> > > file *file);
>> > >  #else
>> > >  static inline int security_bpf(int cmd, union bpf_attr *attr,
>> > >                                            unsigned int size)
>> > > @@ -1772,6 +1774,13 @@ static inline int
>> > > security_bpf_prog_alloc(struct bpf_prog_aux *aux)
>> > >
>> > >  static inline void security_bpf_prog_free(struct bpf_prog_aux
>> > > *aux)
>> > >  { }
>> > > +
>> > > +static inline void security_bpf_map_file(struct bpf_map *map,
>> > > struct
>> > > file *file)
>> > > +{ }
>> > > +
>> > > +static inline void security_bpf_prog_file(struct bpf_prog_aux
>> > > *aux,
>> > > +                                       struct file *file)
>> > > +{ }
>> > >  #endif /* CONFIG_SECURITY */
>> > >  #endif /* CONFIG_BPF_SYSCALL */
>> > >
>> > > diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
>> > > index 1cf31ddd7616..b144181d3f3a 100644
>> > > --- a/kernel/bpf/syscall.c
>> > > +++ b/kernel/bpf/syscall.c
>> > > @@ -313,7 +313,7 @@ static ssize_t bpf_dummy_write(struct file
>> > > *filp,
>> > > const char __user *buf,
>> > >       return -EINVAL;
>> > >  }
>> > >
>> > > -static const struct file_operations bpf_map_fops = {
>> > > +const struct file_operations bpf_map_fops = {
>> > >  #ifdef CONFIG_PROC_FS
>> > >       .show_fdinfo    = bpf_map_show_fdinfo,
>> > >  #endif
>> > > @@ -964,7 +964,7 @@ static void bpf_prog_show_fdinfo(struct
>> > > seq_file
>> > > *m, struct file *filp)
>> > >  }
>> > >  #endif
>> > >
>> > > -static const struct file_operations bpf_prog_fops = {
>> > > +const struct file_operations bpf_prog_fops = {
>> > >  #ifdef CONFIG_PROC_FS
>> > >       .show_fdinfo    = bpf_prog_show_fdinfo,
>> > >  #endif
>> > > diff --git a/security/security.c b/security/security.c
>> > > index 1cd8526cb0b7..dacf649b8cfa 100644
>> > > --- a/security/security.c
>> > > +++ b/security/security.c
>> > > @@ -1734,4 +1734,12 @@ void security_bpf_prog_free(struct
>> > > bpf_prog_aux *aux)
>> > >  {
>> > >       call_void_hook(bpf_prog_free_security, aux);
>> > >  }
>> > > +void security_bpf_map_file(struct bpf_map *map, struct file
>> > > *file)
>> > > +{
>> > > +     call_void_hook(bpf_map_file, map, file);
>> > > +}
>> > > +void security_bpf_prog_file(struct bpf_prog_aux *aux, struct
>> > > file
>> > > *file)
>> > > +{
>> > > +     call_void_hook(bpf_prog_file, aux, file);
>> > > +}
>> > >  #endif /* CONFIG_BPF_SYSCALL */
>> > > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
>> > > index 41aba4e3d57c..fea88655e0ee 100644
>> > > --- a/security/selinux/hooks.c
>> > > +++ b/security/selinux/hooks.c
>> > > @@ -1815,6 +1815,10 @@ static inline int file_path_has_perm(const
>> > > struct cred *cred,
>> > >       return inode_has_perm(cred, file_inode(file), av, &ad);
>> > >  }
>> > >
>> > > +#ifdef CONFIG_BPF_SYSCALL
>> > > +static int bpf_file_check(struct file *file, u32 sid);
>> > > +#endif
>> > > +
>> > >  /* Check whether a task can use an open file descriptor to
>> > >     access an inode in a given way.  Check access to the
>> > >     descriptor itself, and then use dentry_has_perm to
>> > > @@ -1845,6 +1849,12 @@ static int file_has_perm(const struct cred
>> > > *cred,
>> > >                       goto out;
>> > >       }
>> > >
>> > > +#ifdef CONFIG_BPF_SYSCALL
>> > > +     rc = bpf_file_check(file, cred_sid(cred));
>> > > +     if (rc)
>> > > +             goto out;
>> > > +#endif
>> > > +
>> > >       /* av is zero if only checking access to the descriptor. */
>> > >       rc = 0;
>> > >       if (av)
>> > > @@ -2165,6 +2175,12 @@ static int
>> > > selinux_binder_transfer_file(struct
>> > > task_struct *from,
>> > >                       return rc;
>> > >       }
>> > >
>> > > +#ifdef CONFIG_BPF_SYSCALL
>> > > +     rc = bpf_file_check(file, sid);
>> > > +     if (rc)
>> > > +             return rc;
>> > > +#endif
>> > > +
>> > >       if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
>> > >               return 0;
>> > >
>> > > @@ -6288,6 +6304,33 @@ static u32 bpf_map_fmode_to_av(fmode_t
>> > > fmode)
>> > >       return av;
>> > >  }
>> > >
>> > > +/* This function will check the file pass through unix socket or
>> > > binder to see
>> > > + * if it is a bpf related object. And apply correspinding checks
>> > > on
>> > > the bpf
>> > > + * object based on the type. The bpf maps and programs, not like
>> > > other files and
>> > > + * socket, are using a shared anonymous inode inside the kernel
>> > > as
>> > > their inode.
>> > > + * So checking that inode cannot identify if the process have
>> > > privilege to
>> > > + * access the bpf object and that's why we have to add this
>> > > additional check in
>> > > + * selinux_file_receive and selinux_binder_transfer_files.
>> > > + */
>> > > +static int bpf_file_check(struct file *file, u32 sid)
>> > > +{
>> > > +     struct file_security_struct *fsec = file->f_security;
>> > > +     int ret;
>> > > +
>> > > +     if (file->f_op == &bpf_map_fops) {
>> > > +             ret = avc_has_perm(sid, fsec->sid,
>> > > SECCLASS_BPF_MAP,
>> > > +                                bpf_map_fmode_to_av(file-
>> > > > f_mode), NULL);
>> > >
>> > > +             if (ret)
>> > > +                     return ret;
>> > > +     } else if (file->f_op == &bpf_prog_fops) {
>> > > +             ret = avc_has_perm(sid, fsec->sid,
>> > > SECCLASS_BPF_PROG,
>> > > +                                BPF_PROG__USE, NULL);
>> > > +             if (ret)
>> > > +                     return ret;
>> > > +     }
>> > > +     return 0;
>> > > +}
>> > > +
>> > >  static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
>> > >  {
>> > >       u32 sid = current_sid();
>> > > @@ -6351,6 +6394,22 @@ static void selinux_bpf_prog_free(struct
>> > > bpf_prog_aux *aux)
>> > >       aux->security = NULL;
>> > >       kfree(bpfsec);
>> > >  }
>> > > +
>> > > +static void selinux_bpf_map_file(struct bpf_map *map, struct
>> > > file
>> > > *file)
>> > > +{
>> > > +     struct bpf_security_struct *bpfsec = map->security;
>> > > +     struct file_security_struct *fsec = file->f_security;
>> > > +
>> > > +     fsec->sid = bpfsec->sid;
>> > > +}
>> > > +
>> > > +static void selinux_bpf_prog_file(struct bpf_prog_aux *aux,
>> > > struct
>> > > file *file)
>> > > +{
>> > > +     struct bpf_security_struct *bpfsec = aux->security;
>> > > +     struct file_security_struct *fsec = file->f_security;
>> > > +
>> > > +     fsec->sid = bpfsec->sid;
>> >
>> > I could be wrong, but isn't it the case that fsec->sid already will
>> > equal bpfsec->sid, because they are both created by the same thread
>> > during the same system call, and they each inherit the SID of the
>> > current task?
>> >
>>
>> This is true when bpf object is created by the same process that
>> obtains the fd. But there are other ways of getting a bpf object fd
>> from the kernel such as bpf_obj_get and bpf_get_obj_fd_by_id. These
>> action will ask the kernel to allocate a new file for the bpf object
>> and the file sid would be the process ask for fd while the bpfsec-
>> >sid
>> is the sid when bpf object get created. These two could be different.
>
> Oh, in that case you shouldn't change the fsec->sid; you'll need to use
> the bpfsec->sid in your checks instead.  But you can still do what I
> described below.
>
Okay, I will add a bpf flag and a bpf sid in the file security struct
to store the flag and sid for selinux checking when fd get transfered.
>> > What I expected you to do was to add and set a flags field in the
>> > file_security_struct to indicate that this is a bpf map or prog,
>> > and
>> > then test for that in your bpf_file_check() function instead of
>> > having
>> > to export and test the fops structures.
>> >
>> >
>> > > +}
>> > >  #endif
>> > >
>> > >  static struct security_hook_list selinux_hooks[]
>> > > __lsm_ro_after_init
>> > > = {
>> > > @@ -6581,6 +6640,8 @@ static struct security_hook_list
>> > > selinux_hooks[] __lsm_ro_after_init = {
>> > >       LSM_HOOK_INIT(bpf_prog_alloc_security,
>> > > selinux_bpf_prog_alloc),
>> > >       LSM_HOOK_INIT(bpf_map_free_security, selinux_bpf_map_free),
>> > >       LSM_HOOK_INIT(bpf_prog_free_security,
>> > > selinux_bpf_prog_free),
>> > > +     LSM_HOOK_INIT(bpf_map_file, selinux_bpf_map_file),
>> > > +     LSM_HOOK_INIT(bpf_prog_file, selinux_bpf_prog_file),
>> > >  #endif
>> > >  };
>> > >

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re:  Re: [PATCH net-next v2 5/5] selinux: bpf: Add addtional check for bpf object file receive
@ 2017-10-10 19:42           ` Chenbo Feng
  0 siblings, 0 replies; 39+ messages in thread
From: Chenbo Feng @ 2017-10-10 19:42 UTC (permalink / raw)
  To: Stephen Smalley
  Cc: Chenbo Feng, linux-security-module, netdev, SELinux,
	Daniel Borkmann, Alexei Starovoitov, Lorenzo Colitti

On Tue, Oct 10, 2017 at 12:23 PM, Stephen Smalley <sds@tycho.nsa.gov> wrote:
> On Tue, 2017-10-10 at 10:48 -0700, Chenbo Feng wrote:
>> On Tue, Oct 10, 2017 at 7:24 AM, Stephen Smalley <sds@tycho.nsa.gov>
>> wrote:
>> > On Mon, 2017-10-09 at 15:20 -0700, Chenbo Feng wrote:
>> > > From: Chenbo Feng <fengc@google.com>
>> > >
>> > > Introduce a bpf object related check when sending and receiving
>> > > files
>> > > through unix domain socket as well as binder. It checks if the
>> > > receiving
>> > > process have privilege to read/write the bpf map or use the bpf
>> > > program.
>> > > This check is necessary because the bpf maps and programs are
>> > > using a
>> > > anonymous inode as their shared inode so the normal way of
>> > > checking
>> > > the
>> > > files and sockets when passing between processes cannot work
>> > > properly
>> > > on
>> > > eBPF object. This check only works when the BPF_SYSCALL is
>> > > configured.
>> > > The information stored inside the file security struct is the
>> > > same as
>> > > the information in bpf object security struct.
>> > >
>> > > Signed-off-by: Chenbo Feng <fengc@google.com>
>> > > ---
>> > >  include/linux/bpf.h       |  3 +++
>> > >  include/linux/lsm_hooks.h | 17 +++++++++++++
>> > >  include/linux/security.h  |  9 +++++++
>> > >  kernel/bpf/syscall.c      |  4 ++--
>> > >  security/security.c       |  8 +++++++
>> > >  security/selinux/hooks.c  | 61
>> > > +++++++++++++++++++++++++++++++++++++++++++++++
>> > >  6 files changed, 100 insertions(+), 2 deletions(-)
>> > >
>> > > diff --git a/include/linux/bpf.h b/include/linux/bpf.h
>> > > index 225740688ab7..81d6c01b8825 100644
>> > > --- a/include/linux/bpf.h
>> > > +++ b/include/linux/bpf.h
>> > > @@ -285,6 +285,9 @@ int bpf_prog_array_copy_to_user(struct
>> > > bpf_prog_array __rcu *progs,
>> > >  #ifdef CONFIG_BPF_SYSCALL
>> > >  DECLARE_PER_CPU(int, bpf_prog_active);
>> > >
>> > > +extern const struct file_operations bpf_map_fops;
>> > > +extern const struct file_operations bpf_prog_fops;
>> > > +
>> > >  #define BPF_PROG_TYPE(_id, _ops) \
>> > >       extern const struct bpf_verifier_ops _ops;
>> > >  #define BPF_MAP_TYPE(_id, _ops) \
>> > > diff --git a/include/linux/lsm_hooks.h
>> > > b/include/linux/lsm_hooks.h
>> > > index 7161d8e7ee79..517dea60b87b 100644
>> > > --- a/include/linux/lsm_hooks.h
>> > > +++ b/include/linux/lsm_hooks.h
>> > > @@ -1385,6 +1385,19 @@
>> > >   * @bpf_prog_free_security:
>> > >   *   Clean up the security information stored inside bpf prog.
>> > >   *
>> > > + * @bpf_map_file:
>> > > + *   When creating a bpf map fd, set up the file security
>> > > information with
>> > > + *   the bpf security information stored in the map struct. So
>> > > when the map
>> > > + *   fd is passed between processes, the security module can
>> > > directly read
>> > > + *   the security information from file security struct rather
>> > > than the bpf
>> > > + *   security struct.
>> > > + *
>> > > + * @bpf_prog_file:
>> > > + *   When creating a bpf prog fd, set up the file security
>> > > information with
>> > > + *   the bpf security information stored in the prog struct. So
>> > > when the prog
>> > > + *   fd is passed between processes, the security module can
>> > > directly read
>> > > + *   the security information from file security struct rather
>> > > than the bpf
>> > > + *   security struct.
>> > >   */
>> > >  union security_list_options {
>> > >       int (*binder_set_context_mgr)(struct task_struct *mgr);
>> > > @@ -1726,6 +1739,8 @@ union security_list_options {
>> > >       void (*bpf_map_free_security)(struct bpf_map *map);
>> > >       int (*bpf_prog_alloc_security)(struct bpf_prog_aux *aux);
>> > >       void (*bpf_prog_free_security)(struct bpf_prog_aux *aux);
>> > > +     void (*bpf_map_file)(struct bpf_map *map, struct file
>> > > *file);
>> > > +     void (*bpf_prog_file)(struct bpf_prog_aux *aux, struct file
>> > > *file);
>> > >  #endif /* CONFIG_BPF_SYSCALL */
>> > >  };
>> > >
>> > > @@ -1954,6 +1969,8 @@ struct security_hook_heads {
>> > >       struct list_head bpf_map_free_security;
>> > >       struct list_head bpf_prog_alloc_security;
>> > >       struct list_head bpf_prog_free_security;
>> > > +     struct list_head bpf_map_file;
>> > > +     struct list_head bpf_prog_file;
>> > >  #endif /* CONFIG_BPF_SYSCALL */
>> > >  } __randomize_layout;
>> > >
>> > > diff --git a/include/linux/security.h b/include/linux/security.h
>> > > index 18800b0911e5..57573b794e2d 100644
>> > > --- a/include/linux/security.h
>> > > +++ b/include/linux/security.h
>> > > @@ -1740,6 +1740,8 @@ extern int security_bpf_map_alloc(struct
>> > > bpf_map *map);
>> > >  extern void security_bpf_map_free(struct bpf_map *map);
>> > >  extern int security_bpf_prog_alloc(struct bpf_prog_aux *aux);
>> > >  extern void security_bpf_prog_free(struct bpf_prog_aux *aux);
>> > > +extern void security_bpf_map_file(struct bpf_map *map, struct
>> > > file
>> > > *file);
>> > > +extern void security_bpf_prog_file(struct bpf_prog_aux *aux,
>> > > struct
>> > > file *file);
>> > >  #else
>> > >  static inline int security_bpf(int cmd, union bpf_attr *attr,
>> > >                                            unsigned int size)
>> > > @@ -1772,6 +1774,13 @@ static inline int
>> > > security_bpf_prog_alloc(struct bpf_prog_aux *aux)
>> > >
>> > >  static inline void security_bpf_prog_free(struct bpf_prog_aux
>> > > *aux)
>> > >  { }
>> > > +
>> > > +static inline void security_bpf_map_file(struct bpf_map *map,
>> > > struct
>> > > file *file)
>> > > +{ }
>> > > +
>> > > +static inline void security_bpf_prog_file(struct bpf_prog_aux
>> > > *aux,
>> > > +                                       struct file *file)
>> > > +{ }
>> > >  #endif /* CONFIG_SECURITY */
>> > >  #endif /* CONFIG_BPF_SYSCALL */
>> > >
>> > > diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
>> > > index 1cf31ddd7616..b144181d3f3a 100644
>> > > --- a/kernel/bpf/syscall.c
>> > > +++ b/kernel/bpf/syscall.c
>> > > @@ -313,7 +313,7 @@ static ssize_t bpf_dummy_write(struct file
>> > > *filp,
>> > > const char __user *buf,
>> > >       return -EINVAL;
>> > >  }
>> > >
>> > > -static const struct file_operations bpf_map_fops = {
>> > > +const struct file_operations bpf_map_fops = {
>> > >  #ifdef CONFIG_PROC_FS
>> > >       .show_fdinfo    = bpf_map_show_fdinfo,
>> > >  #endif
>> > > @@ -964,7 +964,7 @@ static void bpf_prog_show_fdinfo(struct
>> > > seq_file
>> > > *m, struct file *filp)
>> > >  }
>> > >  #endif
>> > >
>> > > -static const struct file_operations bpf_prog_fops = {
>> > > +const struct file_operations bpf_prog_fops = {
>> > >  #ifdef CONFIG_PROC_FS
>> > >       .show_fdinfo    = bpf_prog_show_fdinfo,
>> > >  #endif
>> > > diff --git a/security/security.c b/security/security.c
>> > > index 1cd8526cb0b7..dacf649b8cfa 100644
>> > > --- a/security/security.c
>> > > +++ b/security/security.c
>> > > @@ -1734,4 +1734,12 @@ void security_bpf_prog_free(struct
>> > > bpf_prog_aux *aux)
>> > >  {
>> > >       call_void_hook(bpf_prog_free_security, aux);
>> > >  }
>> > > +void security_bpf_map_file(struct bpf_map *map, struct file
>> > > *file)
>> > > +{
>> > > +     call_void_hook(bpf_map_file, map, file);
>> > > +}
>> > > +void security_bpf_prog_file(struct bpf_prog_aux *aux, struct
>> > > file
>> > > *file)
>> > > +{
>> > > +     call_void_hook(bpf_prog_file, aux, file);
>> > > +}
>> > >  #endif /* CONFIG_BPF_SYSCALL */
>> > > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
>> > > index 41aba4e3d57c..fea88655e0ee 100644
>> > > --- a/security/selinux/hooks.c
>> > > +++ b/security/selinux/hooks.c
>> > > @@ -1815,6 +1815,10 @@ static inline int file_path_has_perm(const
>> > > struct cred *cred,
>> > >       return inode_has_perm(cred, file_inode(file), av, &ad);
>> > >  }
>> > >
>> > > +#ifdef CONFIG_BPF_SYSCALL
>> > > +static int bpf_file_check(struct file *file, u32 sid);
>> > > +#endif
>> > > +
>> > >  /* Check whether a task can use an open file descriptor to
>> > >     access an inode in a given way.  Check access to the
>> > >     descriptor itself, and then use dentry_has_perm to
>> > > @@ -1845,6 +1849,12 @@ static int file_has_perm(const struct cred
>> > > *cred,
>> > >                       goto out;
>> > >       }
>> > >
>> > > +#ifdef CONFIG_BPF_SYSCALL
>> > > +     rc = bpf_file_check(file, cred_sid(cred));
>> > > +     if (rc)
>> > > +             goto out;
>> > > +#endif
>> > > +
>> > >       /* av is zero if only checking access to the descriptor. */
>> > >       rc = 0;
>> > >       if (av)
>> > > @@ -2165,6 +2175,12 @@ static int
>> > > selinux_binder_transfer_file(struct
>> > > task_struct *from,
>> > >                       return rc;
>> > >       }
>> > >
>> > > +#ifdef CONFIG_BPF_SYSCALL
>> > > +     rc = bpf_file_check(file, sid);
>> > > +     if (rc)
>> > > +             return rc;
>> > > +#endif
>> > > +
>> > >       if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
>> > >               return 0;
>> > >
>> > > @@ -6288,6 +6304,33 @@ static u32 bpf_map_fmode_to_av(fmode_t
>> > > fmode)
>> > >       return av;
>> > >  }
>> > >
>> > > +/* This function will check the file pass through unix socket or
>> > > binder to see
>> > > + * if it is a bpf related object. And apply correspinding checks
>> > > on
>> > > the bpf
>> > > + * object based on the type. The bpf maps and programs, not like
>> > > other files and
>> > > + * socket, are using a shared anonymous inode inside the kernel
>> > > as
>> > > their inode.
>> > > + * So checking that inode cannot identify if the process have
>> > > privilege to
>> > > + * access the bpf object and that's why we have to add this
>> > > additional check in
>> > > + * selinux_file_receive and selinux_binder_transfer_files.
>> > > + */
>> > > +static int bpf_file_check(struct file *file, u32 sid)
>> > > +{
>> > > +     struct file_security_struct *fsec = file->f_security;
>> > > +     int ret;
>> > > +
>> > > +     if (file->f_op == &bpf_map_fops) {
>> > > +             ret = avc_has_perm(sid, fsec->sid,
>> > > SECCLASS_BPF_MAP,
>> > > +                                bpf_map_fmode_to_av(file-
>> > > > f_mode), NULL);
>> > >
>> > > +             if (ret)
>> > > +                     return ret;
>> > > +     } else if (file->f_op == &bpf_prog_fops) {
>> > > +             ret = avc_has_perm(sid, fsec->sid,
>> > > SECCLASS_BPF_PROG,
>> > > +                                BPF_PROG__USE, NULL);
>> > > +             if (ret)
>> > > +                     return ret;
>> > > +     }
>> > > +     return 0;
>> > > +}
>> > > +
>> > >  static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
>> > >  {
>> > >       u32 sid = current_sid();
>> > > @@ -6351,6 +6394,22 @@ static void selinux_bpf_prog_free(struct
>> > > bpf_prog_aux *aux)
>> > >       aux->security = NULL;
>> > >       kfree(bpfsec);
>> > >  }
>> > > +
>> > > +static void selinux_bpf_map_file(struct bpf_map *map, struct
>> > > file
>> > > *file)
>> > > +{
>> > > +     struct bpf_security_struct *bpfsec = map->security;
>> > > +     struct file_security_struct *fsec = file->f_security;
>> > > +
>> > > +     fsec->sid = bpfsec->sid;
>> > > +}
>> > > +
>> > > +static void selinux_bpf_prog_file(struct bpf_prog_aux *aux,
>> > > struct
>> > > file *file)
>> > > +{
>> > > +     struct bpf_security_struct *bpfsec = aux->security;
>> > > +     struct file_security_struct *fsec = file->f_security;
>> > > +
>> > > +     fsec->sid = bpfsec->sid;
>> >
>> > I could be wrong, but isn't it the case that fsec->sid already will
>> > equal bpfsec->sid, because they are both created by the same thread
>> > during the same system call, and they each inherit the SID of the
>> > current task?
>> >
>>
>> This is true when bpf object is created by the same process that
>> obtains the fd. But there are other ways of getting a bpf object fd
>> from the kernel such as bpf_obj_get and bpf_get_obj_fd_by_id. These
>> action will ask the kernel to allocate a new file for the bpf object
>> and the file sid would be the process ask for fd while the bpfsec-
>> >sid
>> is the sid when bpf object get created. These two could be different.
>
> Oh, in that case you shouldn't change the fsec->sid; you'll need to use
> the bpfsec->sid in your checks instead.  But you can still do what I
> described below.
>
Okay, I will add a bpf flag and a bpf sid in the file security struct
to store the flag and sid for selinux checking when fd get transfered.
>> > What I expected you to do was to add and set a flags field in the
>> > file_security_struct to indicate that this is a bpf map or prog,
>> > and
>> > then test for that in your bpf_file_check() function instead of
>> > having
>> > to export and test the fops structures.
>> >
>> >
>> > > +}
>> > >  #endif
>> > >
>> > >  static struct security_hook_list selinux_hooks[]
>> > > __lsm_ro_after_init
>> > > = {
>> > > @@ -6581,6 +6640,8 @@ static struct security_hook_list
>> > > selinux_hooks[] __lsm_ro_after_init = {
>> > >       LSM_HOOK_INIT(bpf_prog_alloc_security,
>> > > selinux_bpf_prog_alloc),
>> > >       LSM_HOOK_INIT(bpf_map_free_security, selinux_bpf_map_free),
>> > >       LSM_HOOK_INIT(bpf_prog_free_security,
>> > > selinux_bpf_prog_free),
>> > > +     LSM_HOOK_INIT(bpf_map_file, selinux_bpf_map_file),
>> > > +     LSM_HOOK_INIT(bpf_prog_file, selinux_bpf_prog_file),
>> > >  #endif
>> > >  };
>> > >

^ permalink raw reply	[flat|nested] 39+ messages in thread

* [Non-DoD Source] Re: [PATCH net-next v2 5/5] selinux: bpf: Add addtional check for bpf object file receive
@ 2017-10-10 19:42           ` Chenbo Feng
  0 siblings, 0 replies; 39+ messages in thread
From: Chenbo Feng @ 2017-10-10 19:42 UTC (permalink / raw)
  To: linux-security-module

On Tue, Oct 10, 2017 at 12:23 PM, Stephen Smalley <sds@tycho.nsa.gov> wrote:
> On Tue, 2017-10-10 at 10:48 -0700, Chenbo Feng wrote:
>> On Tue, Oct 10, 2017 at 7:24 AM, Stephen Smalley <sds@tycho.nsa.gov>
>> wrote:
>> > On Mon, 2017-10-09 at 15:20 -0700, Chenbo Feng wrote:
>> > > From: Chenbo Feng <fengc@google.com>
>> > >
>> > > Introduce a bpf object related check when sending and receiving
>> > > files
>> > > through unix domain socket as well as binder. It checks if the
>> > > receiving
>> > > process have privilege to read/write the bpf map or use the bpf
>> > > program.
>> > > This check is necessary because the bpf maps and programs are
>> > > using a
>> > > anonymous inode as their shared inode so the normal way of
>> > > checking
>> > > the
>> > > files and sockets when passing between processes cannot work
>> > > properly
>> > > on
>> > > eBPF object. This check only works when the BPF_SYSCALL is
>> > > configured.
>> > > The information stored inside the file security struct is the
>> > > same as
>> > > the information in bpf object security struct.
>> > >
>> > > Signed-off-by: Chenbo Feng <fengc@google.com>
>> > > ---
>> > >  include/linux/bpf.h       |  3 +++
>> > >  include/linux/lsm_hooks.h | 17 +++++++++++++
>> > >  include/linux/security.h  |  9 +++++++
>> > >  kernel/bpf/syscall.c      |  4 ++--
>> > >  security/security.c       |  8 +++++++
>> > >  security/selinux/hooks.c  | 61
>> > > +++++++++++++++++++++++++++++++++++++++++++++++
>> > >  6 files changed, 100 insertions(+), 2 deletions(-)
>> > >
>> > > diff --git a/include/linux/bpf.h b/include/linux/bpf.h
>> > > index 225740688ab7..81d6c01b8825 100644
>> > > --- a/include/linux/bpf.h
>> > > +++ b/include/linux/bpf.h
>> > > @@ -285,6 +285,9 @@ int bpf_prog_array_copy_to_user(struct
>> > > bpf_prog_array __rcu *progs,
>> > >  #ifdef CONFIG_BPF_SYSCALL
>> > >  DECLARE_PER_CPU(int, bpf_prog_active);
>> > >
>> > > +extern const struct file_operations bpf_map_fops;
>> > > +extern const struct file_operations bpf_prog_fops;
>> > > +
>> > >  #define BPF_PROG_TYPE(_id, _ops) \
>> > >       extern const struct bpf_verifier_ops _ops;
>> > >  #define BPF_MAP_TYPE(_id, _ops) \
>> > > diff --git a/include/linux/lsm_hooks.h
>> > > b/include/linux/lsm_hooks.h
>> > > index 7161d8e7ee79..517dea60b87b 100644
>> > > --- a/include/linux/lsm_hooks.h
>> > > +++ b/include/linux/lsm_hooks.h
>> > > @@ -1385,6 +1385,19 @@
>> > >   * @bpf_prog_free_security:
>> > >   *   Clean up the security information stored inside bpf prog.
>> > >   *
>> > > + * @bpf_map_file:
>> > > + *   When creating a bpf map fd, set up the file security
>> > > information with
>> > > + *   the bpf security information stored in the map struct. So
>> > > when the map
>> > > + *   fd is passed between processes, the security module can
>> > > directly read
>> > > + *   the security information from file security struct rather
>> > > than the bpf
>> > > + *   security struct.
>> > > + *
>> > > + * @bpf_prog_file:
>> > > + *   When creating a bpf prog fd, set up the file security
>> > > information with
>> > > + *   the bpf security information stored in the prog struct. So
>> > > when the prog
>> > > + *   fd is passed between processes, the security module can
>> > > directly read
>> > > + *   the security information from file security struct rather
>> > > than the bpf
>> > > + *   security struct.
>> > >   */
>> > >  union security_list_options {
>> > >       int (*binder_set_context_mgr)(struct task_struct *mgr);
>> > > @@ -1726,6 +1739,8 @@ union security_list_options {
>> > >       void (*bpf_map_free_security)(struct bpf_map *map);
>> > >       int (*bpf_prog_alloc_security)(struct bpf_prog_aux *aux);
>> > >       void (*bpf_prog_free_security)(struct bpf_prog_aux *aux);
>> > > +     void (*bpf_map_file)(struct bpf_map *map, struct file
>> > > *file);
>> > > +     void (*bpf_prog_file)(struct bpf_prog_aux *aux, struct file
>> > > *file);
>> > >  #endif /* CONFIG_BPF_SYSCALL */
>> > >  };
>> > >
>> > > @@ -1954,6 +1969,8 @@ struct security_hook_heads {
>> > >       struct list_head bpf_map_free_security;
>> > >       struct list_head bpf_prog_alloc_security;
>> > >       struct list_head bpf_prog_free_security;
>> > > +     struct list_head bpf_map_file;
>> > > +     struct list_head bpf_prog_file;
>> > >  #endif /* CONFIG_BPF_SYSCALL */
>> > >  } __randomize_layout;
>> > >
>> > > diff --git a/include/linux/security.h b/include/linux/security.h
>> > > index 18800b0911e5..57573b794e2d 100644
>> > > --- a/include/linux/security.h
>> > > +++ b/include/linux/security.h
>> > > @@ -1740,6 +1740,8 @@ extern int security_bpf_map_alloc(struct
>> > > bpf_map *map);
>> > >  extern void security_bpf_map_free(struct bpf_map *map);
>> > >  extern int security_bpf_prog_alloc(struct bpf_prog_aux *aux);
>> > >  extern void security_bpf_prog_free(struct bpf_prog_aux *aux);
>> > > +extern void security_bpf_map_file(struct bpf_map *map, struct
>> > > file
>> > > *file);
>> > > +extern void security_bpf_prog_file(struct bpf_prog_aux *aux,
>> > > struct
>> > > file *file);
>> > >  #else
>> > >  static inline int security_bpf(int cmd, union bpf_attr *attr,
>> > >                                            unsigned int size)
>> > > @@ -1772,6 +1774,13 @@ static inline int
>> > > security_bpf_prog_alloc(struct bpf_prog_aux *aux)
>> > >
>> > >  static inline void security_bpf_prog_free(struct bpf_prog_aux
>> > > *aux)
>> > >  { }
>> > > +
>> > > +static inline void security_bpf_map_file(struct bpf_map *map,
>> > > struct
>> > > file *file)
>> > > +{ }
>> > > +
>> > > +static inline void security_bpf_prog_file(struct bpf_prog_aux
>> > > *aux,
>> > > +                                       struct file *file)
>> > > +{ }
>> > >  #endif /* CONFIG_SECURITY */
>> > >  #endif /* CONFIG_BPF_SYSCALL */
>> > >
>> > > diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
>> > > index 1cf31ddd7616..b144181d3f3a 100644
>> > > --- a/kernel/bpf/syscall.c
>> > > +++ b/kernel/bpf/syscall.c
>> > > @@ -313,7 +313,7 @@ static ssize_t bpf_dummy_write(struct file
>> > > *filp,
>> > > const char __user *buf,
>> > >       return -EINVAL;
>> > >  }
>> > >
>> > > -static const struct file_operations bpf_map_fops = {
>> > > +const struct file_operations bpf_map_fops = {
>> > >  #ifdef CONFIG_PROC_FS
>> > >       .show_fdinfo    = bpf_map_show_fdinfo,
>> > >  #endif
>> > > @@ -964,7 +964,7 @@ static void bpf_prog_show_fdinfo(struct
>> > > seq_file
>> > > *m, struct file *filp)
>> > >  }
>> > >  #endif
>> > >
>> > > -static const struct file_operations bpf_prog_fops = {
>> > > +const struct file_operations bpf_prog_fops = {
>> > >  #ifdef CONFIG_PROC_FS
>> > >       .show_fdinfo    = bpf_prog_show_fdinfo,
>> > >  #endif
>> > > diff --git a/security/security.c b/security/security.c
>> > > index 1cd8526cb0b7..dacf649b8cfa 100644
>> > > --- a/security/security.c
>> > > +++ b/security/security.c
>> > > @@ -1734,4 +1734,12 @@ void security_bpf_prog_free(struct
>> > > bpf_prog_aux *aux)
>> > >  {
>> > >       call_void_hook(bpf_prog_free_security, aux);
>> > >  }
>> > > +void security_bpf_map_file(struct bpf_map *map, struct file
>> > > *file)
>> > > +{
>> > > +     call_void_hook(bpf_map_file, map, file);
>> > > +}
>> > > +void security_bpf_prog_file(struct bpf_prog_aux *aux, struct
>> > > file
>> > > *file)
>> > > +{
>> > > +     call_void_hook(bpf_prog_file, aux, file);
>> > > +}
>> > >  #endif /* CONFIG_BPF_SYSCALL */
>> > > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
>> > > index 41aba4e3d57c..fea88655e0ee 100644
>> > > --- a/security/selinux/hooks.c
>> > > +++ b/security/selinux/hooks.c
>> > > @@ -1815,6 +1815,10 @@ static inline int file_path_has_perm(const
>> > > struct cred *cred,
>> > >       return inode_has_perm(cred, file_inode(file), av, &ad);
>> > >  }
>> > >
>> > > +#ifdef CONFIG_BPF_SYSCALL
>> > > +static int bpf_file_check(struct file *file, u32 sid);
>> > > +#endif
>> > > +
>> > >  /* Check whether a task can use an open file descriptor to
>> > >     access an inode in a given way.  Check access to the
>> > >     descriptor itself, and then use dentry_has_perm to
>> > > @@ -1845,6 +1849,12 @@ static int file_has_perm(const struct cred
>> > > *cred,
>> > >                       goto out;
>> > >       }
>> > >
>> > > +#ifdef CONFIG_BPF_SYSCALL
>> > > +     rc = bpf_file_check(file, cred_sid(cred));
>> > > +     if (rc)
>> > > +             goto out;
>> > > +#endif
>> > > +
>> > >       /* av is zero if only checking access to the descriptor. */
>> > >       rc = 0;
>> > >       if (av)
>> > > @@ -2165,6 +2175,12 @@ static int
>> > > selinux_binder_transfer_file(struct
>> > > task_struct *from,
>> > >                       return rc;
>> > >       }
>> > >
>> > > +#ifdef CONFIG_BPF_SYSCALL
>> > > +     rc = bpf_file_check(file, sid);
>> > > +     if (rc)
>> > > +             return rc;
>> > > +#endif
>> > > +
>> > >       if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
>> > >               return 0;
>> > >
>> > > @@ -6288,6 +6304,33 @@ static u32 bpf_map_fmode_to_av(fmode_t
>> > > fmode)
>> > >       return av;
>> > >  }
>> > >
>> > > +/* This function will check the file pass through unix socket or
>> > > binder to see
>> > > + * if it is a bpf related object. And apply correspinding checks
>> > > on
>> > > the bpf
>> > > + * object based on the type. The bpf maps and programs, not like
>> > > other files and
>> > > + * socket, are using a shared anonymous inode inside the kernel
>> > > as
>> > > their inode.
>> > > + * So checking that inode cannot identify if the process have
>> > > privilege to
>> > > + * access the bpf object and that's why we have to add this
>> > > additional check in
>> > > + * selinux_file_receive and selinux_binder_transfer_files.
>> > > + */
>> > > +static int bpf_file_check(struct file *file, u32 sid)
>> > > +{
>> > > +     struct file_security_struct *fsec = file->f_security;
>> > > +     int ret;
>> > > +
>> > > +     if (file->f_op == &bpf_map_fops) {
>> > > +             ret = avc_has_perm(sid, fsec->sid,
>> > > SECCLASS_BPF_MAP,
>> > > +                                bpf_map_fmode_to_av(file-
>> > > > f_mode), NULL);
>> > >
>> > > +             if (ret)
>> > > +                     return ret;
>> > > +     } else if (file->f_op == &bpf_prog_fops) {
>> > > +             ret = avc_has_perm(sid, fsec->sid,
>> > > SECCLASS_BPF_PROG,
>> > > +                                BPF_PROG__USE, NULL);
>> > > +             if (ret)
>> > > +                     return ret;
>> > > +     }
>> > > +     return 0;
>> > > +}
>> > > +
>> > >  static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
>> > >  {
>> > >       u32 sid = current_sid();
>> > > @@ -6351,6 +6394,22 @@ static void selinux_bpf_prog_free(struct
>> > > bpf_prog_aux *aux)
>> > >       aux->security = NULL;
>> > >       kfree(bpfsec);
>> > >  }
>> > > +
>> > > +static void selinux_bpf_map_file(struct bpf_map *map, struct
>> > > file
>> > > *file)
>> > > +{
>> > > +     struct bpf_security_struct *bpfsec = map->security;
>> > > +     struct file_security_struct *fsec = file->f_security;
>> > > +
>> > > +     fsec->sid = bpfsec->sid;
>> > > +}
>> > > +
>> > > +static void selinux_bpf_prog_file(struct bpf_prog_aux *aux,
>> > > struct
>> > > file *file)
>> > > +{
>> > > +     struct bpf_security_struct *bpfsec = aux->security;
>> > > +     struct file_security_struct *fsec = file->f_security;
>> > > +
>> > > +     fsec->sid = bpfsec->sid;
>> >
>> > I could be wrong, but isn't it the case that fsec->sid already will
>> > equal bpfsec->sid, because they are both created by the same thread
>> > during the same system call, and they each inherit the SID of the
>> > current task?
>> >
>>
>> This is true when bpf object is created by the same process that
>> obtains the fd. But there are other ways of getting a bpf object fd
>> from the kernel such as bpf_obj_get and bpf_get_obj_fd_by_id. These
>> action will ask the kernel to allocate a new file for the bpf object
>> and the file sid would be the process ask for fd while the bpfsec-
>> >sid
>> is the sid when bpf object get created. These two could be different.
>
> Oh, in that case you shouldn't change the fsec->sid; you'll need to use
> the bpfsec->sid in your checks instead.  But you can still do what I
> described below.
>
Okay, I will add a bpf flag and a bpf sid in the file security struct
to store the flag and sid for selinux checking when fd get transfered.
>> > What I expected you to do was to add and set a flags field in the
>> > file_security_struct to indicate that this is a bpf map or prog,
>> > and
>> > then test for that in your bpf_file_check() function instead of
>> > having
>> > to export and test the fops structures.
>> >
>> >
>> > > +}
>> > >  #endif
>> > >
>> > >  static struct security_hook_list selinux_hooks[]
>> > > __lsm_ro_after_init
>> > > = {
>> > > @@ -6581,6 +6640,8 @@ static struct security_hook_list
>> > > selinux_hooks[] __lsm_ro_after_init = {
>> > >       LSM_HOOK_INIT(bpf_prog_alloc_security,
>> > > selinux_bpf_prog_alloc),
>> > >       LSM_HOOK_INIT(bpf_map_free_security, selinux_bpf_map_free),
>> > >       LSM_HOOK_INIT(bpf_prog_free_security,
>> > > selinux_bpf_prog_free),
>> > > +     LSM_HOOK_INIT(bpf_map_file, selinux_bpf_map_file),
>> > > +     LSM_HOOK_INIT(bpf_prog_file, selinux_bpf_prog_file),
>> > >  #endif
>> > >  };
>> > >
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH net-next v2 4/5] selinux: bpf: Add selinux check for eBPF syscall operations
  2017-10-09 22:20   ` Chenbo Feng
@ 2017-10-10 21:30     ` kbuild test robot
  -1 siblings, 0 replies; 39+ messages in thread
From: kbuild test robot @ 2017-10-10 21:30 UTC (permalink / raw)
  To: Chenbo Feng
  Cc: kbuild-all, linux-security-module, netdev, SELinux,
	Jeffrey Vander Stoep, Alexei Starovoitov, lorenzo,
	Daniel Borkmann, Stephen Smalley, Chenbo Feng

[-- Attachment #1: Type: text/plain, Size: 1945 bytes --]

Hi Chenbo,

[auto build test WARNING on net-next/master]

url:    https://github.com/0day-ci/linux/commits/Chenbo-Feng/bpf-security-New-file-mode-and-LSM-hooks-for-eBPF-object-permission-control/20171011-010349
config: x86_64-randconfig-u0-10110310 (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All warnings (new ones prefixed by >>):

   In file included from include/linux/init.h:4:0,
                    from security/selinux/hooks.c:27:
   security/selinux/hooks.c: In function 'bpf_map_fmode_to_av':
   security/selinux/hooks.c:6284:6: error: 'f_mode' undeclared (first use in this function)
     if (f_mode & FMODE_READ)
         ^
   include/linux/compiler.h:156:30: note: in definition of macro '__trace_if'
     if (__builtin_constant_p(!!(cond)) ? !!(cond) :   \
                                 ^~~~
>> security/selinux/hooks.c:6284:2: note: in expansion of macro 'if'
     if (f_mode & FMODE_READ)
     ^~
   security/selinux/hooks.c:6284:6: note: each undeclared identifier is reported only once for each function it appears in
     if (f_mode & FMODE_READ)
         ^
   include/linux/compiler.h:156:30: note: in definition of macro '__trace_if'
     if (__builtin_constant_p(!!(cond)) ? !!(cond) :   \
                                 ^~~~
>> security/selinux/hooks.c:6284:2: note: in expansion of macro 'if'
     if (f_mode & FMODE_READ)
     ^~

vim +/if +6284 security/selinux/hooks.c

  6279	
  6280	static u32 bpf_map_fmode_to_av(fmode_t fmode)
  6281	{
  6282		u32 av = 0;
  6283	
> 6284		if (f_mode & FMODE_READ)
  6285			av |= BPF_MAP__READ;
  6286		if (f_mode & FMODE_WRITE)
  6287			av |= BPF_MAP__WRITE;
  6288		return av;
  6289	}
  6290	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 31549 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH net-next v2 4/5] selinux: bpf: Add selinux check for eBPF syscall operations
@ 2017-10-10 21:30     ` kbuild test robot
  0 siblings, 0 replies; 39+ messages in thread
From: kbuild test robot @ 2017-10-10 21:30 UTC (permalink / raw)
  To: linux-security-module

Hi Chenbo,

[auto build test WARNING on net-next/master]

url:    https://github.com/0day-ci/linux/commits/Chenbo-Feng/bpf-security-New-file-mode-and-LSM-hooks-for-eBPF-object-permission-control/20171011-010349
config: x86_64-randconfig-u0-10110310 (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All warnings (new ones prefixed by >>):

   In file included from include/linux/init.h:4:0,
                    from security/selinux/hooks.c:27:
   security/selinux/hooks.c: In function 'bpf_map_fmode_to_av':
   security/selinux/hooks.c:6284:6: error: 'f_mode' undeclared (first use in this function)
     if (f_mode & FMODE_READ)
         ^
   include/linux/compiler.h:156:30: note: in definition of macro '__trace_if'
     if (__builtin_constant_p(!!(cond)) ? !!(cond) :   \
                                 ^~~~
>> security/selinux/hooks.c:6284:2: note: in expansion of macro 'if'
     if (f_mode & FMODE_READ)
     ^~
   security/selinux/hooks.c:6284:6: note: each undeclared identifier is reported only once for each function it appears in
     if (f_mode & FMODE_READ)
         ^
   include/linux/compiler.h:156:30: note: in definition of macro '__trace_if'
     if (__builtin_constant_p(!!(cond)) ? !!(cond) :   \
                                 ^~~~
>> security/selinux/hooks.c:6284:2: note: in expansion of macro 'if'
     if (f_mode & FMODE_READ)
     ^~

vim +/if +6284 security/selinux/hooks.c

  6279	
  6280	static u32 bpf_map_fmode_to_av(fmode_t fmode)
  6281	{
  6282		u32 av = 0;
  6283	
> 6284		if (f_mode & FMODE_READ)
  6285			av |= BPF_MAP__READ;
  6286		if (f_mode & FMODE_WRITE)
  6287			av |= BPF_MAP__WRITE;
  6288		return av;
  6289	}
  6290	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH net-next v2 4/5] selinux: bpf: Add selinux check for eBPF syscall operations
  2017-10-10 17:54           ` Chenbo Feng
@ 2017-10-11 13:00             ` Stephen Smalley
  -1 siblings, 0 replies; 39+ messages in thread
From: Stephen Smalley @ 2017-10-11 13:00 UTC (permalink / raw)
  To: Chenbo Feng
  Cc: Daniel Borkmann, netdev, Chenbo Feng, linux-security-module,
	SELinux, Alexei Starovoitov, Lorenzo Colitti

On Tue, 2017-10-10 at 10:54 -0700, Chenbo Feng via Selinux wrote:
> On Tue, Oct 10, 2017 at 7:52 AM, Stephen Smalley <sds@tycho.nsa.gov>
> wrote:
> > On Tue, 2017-10-10 at 10:18 -0400, Stephen Smalley wrote:
> > > On Mon, 2017-10-09 at 15:20 -0700, Chenbo Feng wrote:
> > > > From: Chenbo Feng <fengc@google.com>
> > > > 
> > > > Implement the actual checks introduced to eBPF related
> > > > syscalls.
> > > > This
> > > > implementation use the security field inside bpf object to
> > > > store a
> > > > sid that
> > > > identify the bpf object. And when processes try to access the
> > > > object,
> > > > selinux will check if processes have the right privileges. The
> > > > creation
> > > > of eBPF object are also checked at the general bpf check hook
> > > > and
> > > > new
> > > > cmd introduced to eBPF domain can also be checked there.
> > > > 
> > > > Signed-off-by: Chenbo Feng <fengc@google.com>
> > > > Acked-by: Alexei Starovoitov <ast@kernel.org>
> > > > ---
> > > >  security/selinux/hooks.c            | 111
> > > > ++++++++++++++++++++++++++++++++++++
> > > >  security/selinux/include/classmap.h |   2 +
> > > >  security/selinux/include/objsec.h   |   4 ++
> > > >  3 files changed, 117 insertions(+)
> > > > 
> > > > diff --git a/security/selinux/hooks.c
> > > > b/security/selinux/hooks.c
> > > > index f5d304736852..41aba4e3d57c 100644
> > > > --- a/security/selinux/hooks.c
> > > > +++ b/security/selinux/hooks.c
> > > > @@ -85,6 +85,7 @@
> > > >  #include <linux/export.h>
> > > >  #include <linux/msg.h>
> > > >  #include <linux/shm.h>
> > > > +#include <linux/bpf.h>
> > > > 
> > > >  #include "avc.h"
> > > >  #include "objsec.h"
> > > > @@ -6252,6 +6253,106 @@ static void
> > > > selinux_ib_free_security(void
> > > > *ib_sec)
> > > >  }
> > > >  #endif
> > > > 
> > > > +#ifdef CONFIG_BPF_SYSCALL
> > > > +static int selinux_bpf(int cmd, union bpf_attr *attr,
> > > > +                                unsigned int size)
> > > > +{
> > > > +   u32 sid = current_sid();
> > > > +   int ret;
> > > > +
> > > > +   switch (cmd) {
> > > > +   case BPF_MAP_CREATE:
> > > > +           ret = avc_has_perm(sid, sid, SECCLASS_BPF_MAP,
> > > > BPF_MAP__CREATE,
> > > > +                              NULL);
> > > > +           break;
> > > > +   case BPF_PROG_LOAD:
> > > > +           ret = avc_has_perm(sid, sid, SECCLASS_BPF_PROG,
> > > > BPF_PROG__LOAD,
> > > > +                              NULL);
> > > > +           break;
> > > > +   default:
> > > > +           ret = 0;
> > > > +           break;
> > > > +   }
> > > > +
> > > > +   return ret;
> > > > +}
> > > > +
> > > > +static u32 bpf_map_fmode_to_av(fmode_t fmode)
> > > > +{
> > > > +   u32 av = 0;
> > > > +
> > > > +   if (f_mode & FMODE_READ)
> > > > +           av |= BPF_MAP__READ;
> > > > +   if (f_mode & FMODE_WRITE)
> > > > +           av |= BPF_MAP__WRITE;
> > > > +   return av;
> > > > +}
> > > > +
> > > > +static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
> > > > +{
> > > > +   u32 sid = current_sid();
> > > > +   struct bpf_security_struct *bpfsec;
> > > > +
> > > > +   bpfsec = map->security;
> > > > +   return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF_MAP,
> > > > +                       bpf_map_fmode_to_av(fmode), NULL);
> > > > +}
> > > > +
> > > > +static int selinux_bpf_prog(struct bpf_prog *prog)
> > > > +{
> > > > +   u32 sid = current_sid();
> > > > +   struct bpf_security_struct *bpfsec;
> > > > +
> > > > +   bpfsec = prog->aux->security;
> > > > +   return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF_PROG,
> > > > +                       BPF_PROG__USE, NULL);
> > > > +}
> > > > +
> > > > +static int selinux_bpf_map_alloc(struct bpf_map *map)
> > > > +{
> > > > +   struct bpf_security_struct *bpfsec;
> > > > +
> > > > +   bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> > > > +   if (!bpfsec)
> > > > +           return -ENOMEM;
> > > > +
> > > > +   bpfsec->sid = current_sid();
> > > > +   map->security = bpfsec;
> > > > +
> > > > +   return 0;
> > > > +}
> > > > +
> > > > +static void selinux_bpf_map_free(struct bpf_map *map)
> > > > +{
> > > > +   struct bpf_security_struct *bpfsec = map->security;
> > > > +
> > > > +   map->security = NULL;
> > > > +   kfree(bpfsec);
> > > > +}
> > > > +
> > > > +static int selinux_bpf_prog_alloc(struct bpf_prog_aux *aux)
> > > > +{
> > > > +   struct bpf_security_struct *bpfsec;
> > > > +
> > > > +   bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> > > > +   if (!bpfsec)
> > > > +           return -ENOMEM;
> > > > +
> > > > +   bpfsec->sid = current_sid();
> > > > +   aux->security = bpfsec;
> > > > +
> > > > +   return 0;
> > > > +}
> > > > +
> > > > +static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
> > > > +{
> > > > +   struct bpf_security_struct *bpfsec = aux->security;
> > > > +
> > > > +   aux->security = NULL;
> > > > +   kfree(bpfsec);
> > > > +}
> > > > +#endif
> > > > +
> > > >  static struct security_hook_list selinux_hooks[]
> > > > __lsm_ro_after_init
> > > > = {
> > > >     LSM_HOOK_INIT(binder_set_context_mgr,
> > > > selinux_binder_set_context_mgr),
> > > >     LSM_HOOK_INIT(binder_transaction,
> > > > selinux_binder_transaction),
> > > > @@ -6471,6 +6572,16 @@ static struct security_hook_list
> > > > selinux_hooks[] __lsm_ro_after_init = {
> > > >     LSM_HOOK_INIT(audit_rule_match, selinux_audit_rule_match),
> > > >     LSM_HOOK_INIT(audit_rule_free, selinux_audit_rule_free),
> > > >  #endif
> > > > +
> > > > +#ifdef CONFIG_BPF_SYSCALL
> > > > +   LSM_HOOK_INIT(bpf, selinux_bpf),
> > > > +   LSM_HOOK_INIT(bpf_map, selinux_bpf_map),
> > > > +   LSM_HOOK_INIT(bpf_prog, selinux_bpf_prog),
> > > > +   LSM_HOOK_INIT(bpf_map_alloc_security,
> > > > selinux_bpf_map_alloc),
> > > > +   LSM_HOOK_INIT(bpf_prog_alloc_security,
> > > > selinux_bpf_prog_alloc),
> > > > +   LSM_HOOK_INIT(bpf_map_free_security,
> > > > selinux_bpf_map_free),
> > > > +   LSM_HOOK_INIT(bpf_prog_free_security,
> > > > selinux_bpf_prog_free),
> > > > +#endif
> > > >  };
> > > > 
> > > >  static __init int selinux_init(void)
> > > > diff --git a/security/selinux/include/classmap.h
> > > > b/security/selinux/include/classmap.h
> > > > index 35ffb29a69cb..7253c5eea59c 100644
> > > > --- a/security/selinux/include/classmap.h
> > > > +++ b/security/selinux/include/classmap.h
> > > > @@ -237,6 +237,8 @@ struct security_class_mapping
> > > > secclass_map[] =
> > > > {
> > > >       { "access", NULL } },
> > > >     { "infiniband_endport",
> > > >       { "manage_subnet", NULL } },
> > > > +   { "bpf_map", {"create", "read", "write"} },
> > > > +   { "bpf_prog", {"load", "use"} },
> > > 
> > > Again I have to ask: do you truly need/want two separate classes,
> > > or
> > > would a single class with distinct permissions suffice, ala:
> > >         { "bpf", { "create_map", "read_map", "write_map",
> > > "prog_load",
> > > "prog_use" } },
> > > 
> > > and then allow A self:bpf { create_map read_map write_map
> > > prog_load
> > > prog_use }; would be stored in a single policy avtab rule, and be
> > > cached in a single AVC entry.
> 
> Sorry I missed to reply on this, I keep it that way because sometimes
> we need to grant the permission of accessing eBPF maps and programs
> separately. But keep them in a single class definitely works for me.

If we anticipated a large number of permissions being defined for
either bpf maps or programs, or if we were labeling them differently
(e.g. inheriting from creator for one, while using type transitions for
another), then it might make more sense to split the class (so that we
can support up to 32 distinct permissions for each one, or because we'd
never end up allowing both map and prog permissions to the same target
SID/context).  But in this case I can't see any benefit to using two
classes, and it would consume more memory to do so.

BTW, please be sure to cc Paul Moore on these patches if you aren't
already since he is the selinux kernel tree maintainer.

> > I guess for consistency in naming it should be either:
> >         { "bpf", { "map_create", "map_read", "map_write",
> > "prog_load",
> > "prog_use" } },
> > 
> > or:
> > 
> >         { "bpf", { "create_map", "read_map", "write_map",
> > "load_prog",
> > "use_prog" } },
> > 
> > 
> > > >     { NULL }
> > > >    };
> > > > 
> > > > diff --git a/security/selinux/include/objsec.h
> > > > b/security/selinux/include/objsec.h
> > > > index 1649cd18eb0b..3d54468ce334 100644
> > > > --- a/security/selinux/include/objsec.h
> > > > +++ b/security/selinux/include/objsec.h
> > > > @@ -150,6 +150,10 @@ struct pkey_security_struct {
> > > >     u32     sid;    /* SID of pkey */
> > > >  };
> > > > 
> > > > +struct bpf_security_struct {
> > > > +   u32 sid;  /*SID of bpf obj creater*/
> > > > +};
> > > > +
> > > >  extern unsigned int selinux_checkreqprot;
> > > > 
> > > >  #endif /* _SELINUX_OBJSEC_H_ */

^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH net-next v2 4/5] selinux: bpf: Add selinux check for eBPF syscall operations
@ 2017-10-11 13:00             ` Stephen Smalley
  0 siblings, 0 replies; 39+ messages in thread
From: Stephen Smalley @ 2017-10-11 13:00 UTC (permalink / raw)
  To: linux-security-module

On Tue, 2017-10-10 at 10:54 -0700, Chenbo Feng via Selinux wrote:
> On Tue, Oct 10, 2017 at 7:52 AM, Stephen Smalley <sds@tycho.nsa.gov>
> wrote:
> > On Tue, 2017-10-10 at 10:18 -0400, Stephen Smalley wrote:
> > > On Mon, 2017-10-09 at 15:20 -0700, Chenbo Feng wrote:
> > > > From: Chenbo Feng <fengc@google.com>
> > > > 
> > > > Implement the actual checks introduced to eBPF related
> > > > syscalls.
> > > > This
> > > > implementation use the security field inside bpf object to
> > > > store a
> > > > sid that
> > > > identify the bpf object. And when processes try to access the
> > > > object,
> > > > selinux will check if processes have the right privileges. The
> > > > creation
> > > > of eBPF object are also checked at the general bpf check hook
> > > > and
> > > > new
> > > > cmd introduced to eBPF domain can also be checked there.
> > > > 
> > > > Signed-off-by: Chenbo Feng <fengc@google.com>
> > > > Acked-by: Alexei Starovoitov <ast@kernel.org>
> > > > ---
> > > > ?security/selinux/hooks.c????????????| 111
> > > > ++++++++++++++++++++++++++++++++++++
> > > > ?security/selinux/include/classmap.h |???2 +
> > > > ?security/selinux/include/objsec.h???|???4 ++
> > > > ?3 files changed, 117 insertions(+)
> > > > 
> > > > diff --git a/security/selinux/hooks.c
> > > > b/security/selinux/hooks.c
> > > > index f5d304736852..41aba4e3d57c 100644
> > > > --- a/security/selinux/hooks.c
> > > > +++ b/security/selinux/hooks.c
> > > > @@ -85,6 +85,7 @@
> > > > ?#include <linux/export.h>
> > > > ?#include <linux/msg.h>
> > > > ?#include <linux/shm.h>
> > > > +#include <linux/bpf.h>
> > > > 
> > > > ?#include "avc.h"
> > > > ?#include "objsec.h"
> > > > @@ -6252,6 +6253,106 @@ static void
> > > > selinux_ib_free_security(void
> > > > *ib_sec)
> > > > ?}
> > > > ?#endif
> > > > 
> > > > +#ifdef CONFIG_BPF_SYSCALL
> > > > +static int selinux_bpf(int cmd, union bpf_attr *attr,
> > > > +????????????????????????????????unsigned int size)
> > > > +{
> > > > +???u32 sid = current_sid();
> > > > +???int ret;
> > > > +
> > > > +???switch (cmd) {
> > > > +???case BPF_MAP_CREATE:
> > > > +???????????ret = avc_has_perm(sid, sid, SECCLASS_BPF_MAP,
> > > > BPF_MAP__CREATE,
> > > > +??????????????????????????????NULL);
> > > > +???????????break;
> > > > +???case BPF_PROG_LOAD:
> > > > +???????????ret = avc_has_perm(sid, sid, SECCLASS_BPF_PROG,
> > > > BPF_PROG__LOAD,
> > > > +??????????????????????????????NULL);
> > > > +???????????break;
> > > > +???default:
> > > > +???????????ret = 0;
> > > > +???????????break;
> > > > +???}
> > > > +
> > > > +???return ret;
> > > > +}
> > > > +
> > > > +static u32 bpf_map_fmode_to_av(fmode_t fmode)
> > > > +{
> > > > +???u32 av = 0;
> > > > +
> > > > +???if (f_mode & FMODE_READ)
> > > > +???????????av |= BPF_MAP__READ;
> > > > +???if (f_mode & FMODE_WRITE)
> > > > +???????????av |= BPF_MAP__WRITE;
> > > > +???return av;
> > > > +}
> > > > +
> > > > +static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
> > > > +{
> > > > +???u32 sid = current_sid();
> > > > +???struct bpf_security_struct *bpfsec;
> > > > +
> > > > +???bpfsec = map->security;
> > > > +???return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF_MAP,
> > > > +???????????????????????bpf_map_fmode_to_av(fmode), NULL);
> > > > +}
> > > > +
> > > > +static int selinux_bpf_prog(struct bpf_prog *prog)
> > > > +{
> > > > +???u32 sid = current_sid();
> > > > +???struct bpf_security_struct *bpfsec;
> > > > +
> > > > +???bpfsec = prog->aux->security;
> > > > +???return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF_PROG,
> > > > +???????????????????????BPF_PROG__USE, NULL);
> > > > +}
> > > > +
> > > > +static int selinux_bpf_map_alloc(struct bpf_map *map)
> > > > +{
> > > > +???struct bpf_security_struct *bpfsec;
> > > > +
> > > > +???bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> > > > +???if (!bpfsec)
> > > > +???????????return -ENOMEM;
> > > > +
> > > > +???bpfsec->sid = current_sid();
> > > > +???map->security = bpfsec;
> > > > +
> > > > +???return 0;
> > > > +}
> > > > +
> > > > +static void selinux_bpf_map_free(struct bpf_map *map)
> > > > +{
> > > > +???struct bpf_security_struct *bpfsec = map->security;
> > > > +
> > > > +???map->security = NULL;
> > > > +???kfree(bpfsec);
> > > > +}
> > > > +
> > > > +static int selinux_bpf_prog_alloc(struct bpf_prog_aux *aux)
> > > > +{
> > > > +???struct bpf_security_struct *bpfsec;
> > > > +
> > > > +???bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> > > > +???if (!bpfsec)
> > > > +???????????return -ENOMEM;
> > > > +
> > > > +???bpfsec->sid = current_sid();
> > > > +???aux->security = bpfsec;
> > > > +
> > > > +???return 0;
> > > > +}
> > > > +
> > > > +static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
> > > > +{
> > > > +???struct bpf_security_struct *bpfsec = aux->security;
> > > > +
> > > > +???aux->security = NULL;
> > > > +???kfree(bpfsec);
> > > > +}
> > > > +#endif
> > > > +
> > > > ?static struct security_hook_list selinux_hooks[]
> > > > __lsm_ro_after_init
> > > > = {
> > > > ????LSM_HOOK_INIT(binder_set_context_mgr,
> > > > selinux_binder_set_context_mgr),
> > > > ????LSM_HOOK_INIT(binder_transaction,
> > > > selinux_binder_transaction),
> > > > @@ -6471,6 +6572,16 @@ static struct security_hook_list
> > > > selinux_hooks[] __lsm_ro_after_init = {
> > > > ????LSM_HOOK_INIT(audit_rule_match, selinux_audit_rule_match),
> > > > ????LSM_HOOK_INIT(audit_rule_free, selinux_audit_rule_free),
> > > > ?#endif
> > > > +
> > > > +#ifdef CONFIG_BPF_SYSCALL
> > > > +???LSM_HOOK_INIT(bpf, selinux_bpf),
> > > > +???LSM_HOOK_INIT(bpf_map, selinux_bpf_map),
> > > > +???LSM_HOOK_INIT(bpf_prog, selinux_bpf_prog),
> > > > +???LSM_HOOK_INIT(bpf_map_alloc_security,
> > > > selinux_bpf_map_alloc),
> > > > +???LSM_HOOK_INIT(bpf_prog_alloc_security,
> > > > selinux_bpf_prog_alloc),
> > > > +???LSM_HOOK_INIT(bpf_map_free_security,
> > > > selinux_bpf_map_free),
> > > > +???LSM_HOOK_INIT(bpf_prog_free_security,
> > > > selinux_bpf_prog_free),
> > > > +#endif
> > > > ?};
> > > > 
> > > > ?static __init int selinux_init(void)
> > > > diff --git a/security/selinux/include/classmap.h
> > > > b/security/selinux/include/classmap.h
> > > > index 35ffb29a69cb..7253c5eea59c 100644
> > > > --- a/security/selinux/include/classmap.h
> > > > +++ b/security/selinux/include/classmap.h
> > > > @@ -237,6 +237,8 @@ struct security_class_mapping
> > > > secclass_map[] =
> > > > {
> > > > ??????{ "access", NULL } },
> > > > ????{ "infiniband_endport",
> > > > ??????{ "manage_subnet", NULL } },
> > > > +???{ "bpf_map", {"create", "read", "write"} },
> > > > +???{ "bpf_prog", {"load", "use"} },
> > > 
> > > Again I have to ask: do you truly need/want two separate classes,
> > > or
> > > would a single class with distinct permissions suffice, ala:
> > > ????????{ "bpf", { "create_map", "read_map", "write_map",
> > > "prog_load",
> > > "prog_use" } },
> > > 
> > > and then allow A self:bpf { create_map read_map write_map
> > > prog_load
> > > prog_use }; would be stored in a single policy avtab rule, and be
> > > cached in a single AVC entry.
> 
> Sorry I missed to reply on this, I keep it that way because sometimes
> we need to grant the permission of accessing eBPF maps and programs
> separately. But keep them in a single class definitely works for me.

If we anticipated a large number of permissions being defined for
either bpf maps or programs, or if we were labeling them differently
(e.g. inheriting from creator for one, while using type transitions for
another), then it might make more sense to split the class (so that we
can support up to 32 distinct permissions for each one, or because we'd
never end up allowing both map and prog permissions to the same target
SID/context).  But in this case I can't see any benefit to using two
classes, and it would consume more memory to do so.

BTW, please be sure to cc Paul Moore on these patches if you aren't
already since he is the selinux kernel tree maintainer.

> > I guess for consistency in naming it should be either:
> > ????????{ "bpf", { "map_create", "map_read", "map_write",
> > "prog_load",
> > "prog_use" } },
> > 
> > or:
> > 
> > ????????{ "bpf", { "create_map", "read_map", "write_map",
> > "load_prog",
> > "use_prog" } },
> > 
> > 
> > > > ????{ NULL }
> > > > ???};
> > > > 
> > > > diff --git a/security/selinux/include/objsec.h
> > > > b/security/selinux/include/objsec.h
> > > > index 1649cd18eb0b..3d54468ce334 100644
> > > > --- a/security/selinux/include/objsec.h
> > > > +++ b/security/selinux/include/objsec.h
> > > > @@ -150,6 +150,10 @@ struct pkey_security_struct {
> > > > ????u32?????sid;????/* SID of pkey */
> > > > ?};
> > > > 
> > > > +struct bpf_security_struct {
> > > > +???u32 sid;??/*SID of bpf obj creater*/
> > > > +};
> > > > +
> > > > ?extern unsigned int selinux_checkreqprot;
> > > > 
> > > > ?#endif /* _SELINUX_OBJSEC_H_ */
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 39+ messages in thread

end of thread, other threads:[~2017-10-11 13:00 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-10-09 22:20 [PATCH net-next v2 0/5] bpf: security: New file mode and LSM hooks for eBPF object permission control Chenbo Feng
2017-10-09 22:20 ` Chenbo Feng
2017-10-09 22:20 ` [PATCH net-next v2 1/5] bpf: Add file mode configuration into bpf maps Chenbo Feng
2017-10-09 22:20   ` Chenbo Feng
2017-10-09 23:07   ` Alexei Starovoitov
2017-10-09 23:07     ` Alexei Starovoitov
2017-10-09 23:31     ` Chenbo Feng
2017-10-09 23:31       ` Chenbo Feng
2017-10-09 22:20 ` [PATCH net-next v2 2/5] bpf: Add tests for eBPF file mode Chenbo Feng
2017-10-09 22:20   ` Chenbo Feng
2017-10-09 22:20 ` [PATCH net-next v2 3/5] security: bpf: Add LSM hooks for bpf object related syscall Chenbo Feng
2017-10-09 22:20   ` Chenbo Feng
2017-10-09 22:20 ` [PATCH net-next v2 4/5] selinux: bpf: Add selinux check for eBPF syscall operations Chenbo Feng
2017-10-09 22:20   ` Chenbo Feng
2017-10-10 14:18   ` Stephen Smalley
2017-10-10 14:18     ` Stephen Smalley
     [not found]     ` <1507645097.30616.6.camel-+05T5uksL2qpZYMLLGbcSA@public.gmane.org>
2017-10-10 14:52       ` Stephen Smalley
2017-10-10 14:52         ` Stephen Smalley
2017-10-10 14:52         ` Stephen Smalley
2017-10-10 17:54         ` Chenbo Feng
2017-10-10 17:54           ` Chenbo Feng
2017-10-11 13:00           ` Stephen Smalley
2017-10-11 13:00             ` Stephen Smalley
2017-10-10 17:59   ` kbuild test robot
2017-10-10 17:59     ` kbuild test robot
2017-10-10 21:30   ` kbuild test robot
2017-10-10 21:30     ` kbuild test robot
2017-10-09 22:20 ` [PATCH net-next v2 5/5] selinux: bpf: Add addtional check for bpf object file receive Chenbo Feng
2017-10-09 22:20   ` Chenbo Feng
2017-10-10 14:24   ` Stephen Smalley
2017-10-10 14:24     ` Stephen Smalley
2017-10-10 17:48     ` Chenbo Feng
2017-10-10 17:48       ` Chenbo Feng
2017-10-10 19:23       ` [Non-DoD Source] " Stephen Smalley
2017-10-10 19:23         ` Stephen Smalley
2017-10-10 19:23         ` Stephen Smalley
2017-10-10 19:42         ` [Non-DoD Source] " Chenbo Feng
2017-10-10 19:42           ` Chenbo Feng
2017-10-10 19:42           ` Chenbo Feng

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.