bpf: security: New file mode and LSM hooks for eBPF object permission control
Subsystems (10)
v6 cover bpf: security: New file mode and LSM hooks for eBPF object permission control
From: Chenbo Feng <[email protected]>
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.
Change since V2:
- Fixed the line break problem for map flags check
- Fixed the typo in selinux check of file mode.
- Merge bpf_map and bpf_prog into one selinux class
- Added bpf_type and bpf_sid into file security struct to store the security information when generate fd.
- Add the hook to bpf_map_new_fd and bpf_prog_new_fd.
Change since V3:
- Return the actual error from security check instead of -EPERM
- Move the hooks into anon_inode_getfd() to avoid get file again after bpf object file is installed with fd.
- Removed the bpf_sid field inside file_scerity_struct to reduce the cache size.
Change since V4:
- Rename bpf av prog_use to prog_run to distinguish from fd_use.
- Remove the bpf_type field inside file_scerity_struct and use bpf fops to indentify bpf object instead.
Change since v5:
- Fixed the incorrect selinux class name for SECCLASS_BPF
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 | 54 +++++++++++ include/linux/security.h | 45 +++++++++ include/uapi/linux/bpf.h | 6 ++ kernel/bpf/arraymap.c | 6 +- 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 | 118 ++++++++++++++++++++--- security/security.c | 32 +++++++ security/selinux/hooks.c | 160 ++++++++++++++++++++++++++++++++ security/selinux/include/classmap.h | 2 + security/selinux/include/objsec.h | 4 + tools/testing/selftests/bpf/test_maps.c | 48 ++++++++++ 17 files changed, 502 insertions(+), 26 deletions(-)
Signature · 6 lines
2.15.0.rc0.271.g36b669edcc-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
PATCH v6 1/5 bpf: Add file mode configuration into bpf maps
From: Chenbo Feng <[email protected]>
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.
--- include/linux/bpf.h | 6 ++-- include/uapi/linux/bpf.h | 6 ++++ kernel/bpf/arraymap.c | 6 +++- 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(+), 22 deletions(-)
include/linux/bpf.h +4 -2
@@ -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
include/uapi/linux/bpf.h +6 -0
@@ -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 */
kernel/bpf/arraymap.c +5 -1
@@ -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,7 +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 || + attr->value_size ==0 || + attr->map_flags & ~ARRAY_CREATE_FLAG_MASK || (percpu && numa_node !=NUMA_NO_NODE ))return ERR_PTR (-EINVAL );
kernel/bpf/devmap.c +4 -1
@@ -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 );
kernel/bpf/hashtab.c +3 -2
@@ -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;
kernel/bpf/inode.c +10 -5
@@ -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;
kernel/bpf/lpm_trie.c +2 -1
@@ -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) {
kernel/bpf/sockmap.c +4 -1
@@ -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 )
kernel/bpf/stackmap.c +4 -1
@@ -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 */
kernel/bpf/syscall.c +72 -8
181 lines not on this page · raw message
Signature · 6 lines
2.15.0.rc0.271.g36b669edcc-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
Re: [PATCH net-next v6 1/5] bpf: Add file mode configuration into bpf maps
Quoted 10 lines · Chenbo Feng
From: Chenbo Feng <[email protected]>
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_flagsis set toO_RDWRif the flag passed by syscall is 0. Otherwise it should beO_RDONLYorO_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.
Quoted 2 lines
Signed-off-by Chenbo Feng <[email protected]>
--
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
Daniel Borkmann
maintainerRe: [PATCH net-next v6 1/5] bpf: Add file mode configuration into bpf maps
In reply to Chenbo FengHey Chenbo,
there's still one thing I noticed later one; would have sent a follow-up, but as you need to respin anyway for the build issue, here's what is still missing uapi-wise:
On 10/16/2017 09:11 PM, Chenbo Feng wrote:
[...]
Quoted 10 lines
kernel/bpf/syscall.c +9
-0+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 ; }
[...]
kernel/bpf/syscall.c static int bpf_obj_get(const union bpf_attr *attr) · line 1184
Quoted 12 lines
kernel/bpf/syscall.c +2
-2-#define BPF_OBJ_LAST_FIELD bpf_fd +#define BPF_OBJ_LAST_FIELD file_flagsstatic 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 )
Here, we also need to check and bail out on ...
attr->file_flags & ~(BPF_F_RDONLY | BPF_F_WRONLY)
... otherwise we cannot extend it with more flags in future. Basically same principle for mask check you do on map creation, but not yet here.
The same is needed in bpf_map_get_fd_by_id(), too.
The bpf_prog_get_fd_by_id() is covered since BPF_PROG_GET_FD_BY_ID_LAST_FIELD still points to prog_id, so ok.
kernel/bpf/syscall.c static int bpf_obj_get(const union bpf_attr *attr) · line 1190
Quoted 6 lines
kernel/bpf/syscall.c +2
-1return -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); }
--
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
PATCH v6 2/5 bpf: Add tests for eBPF file mode
In reply to Chenbo FengFrom: Chenbo Feng <[email protected]>
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.
--- tools/testing/selftests/bpf/test_maps.c | 48 +++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+)
tools/testing/selftests/bpf/test_maps.c +48 -0
@@ -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 )
Signature · 6 lines
2.15.0.rc0.271.g36b669edcc-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
Re: [PATCH net-next v6 2/5] bpf: Add tests for eBPF file mode
Quoted 7 lines · Chenbo Feng
From: Chenbo Feng <[email protected]>
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 <[email protected]>
--
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
PATCH v6 3/5 security: bpf: Add LSM hooks for bpf object related syscall
In reply to Chenbo FengFrom: Chenbo Feng <[email protected]>
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.
--- include/linux/bpf.h | 6 ++++++ include/linux/lsm_hooks.h | 54 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/security.h | 45 +++++++++++++++++++++++++++++++++++++++ kernel/bpf/syscall.c | 34 +++++++++++++++++++++++++++-- security/security.c | 32 ++++++++++++++++++++++++++++ 5 files changed, 169 insertions(+), 2 deletions(-)
include/linux/bpf.h +6 -0
@@ -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;
include/linux/lsm_hooks.h +54 -0
72 lines not on this page · raw message
include/linux/security.h +45 -0
57 lines not on this page · raw message
kernel/bpf/syscall.c +32 -2
@@ -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,12 @@ static const struct file_operations bpf_map_fops = {int bpf_map_new_fd (struct bpf_map *map,int flags) { +int ret; + + ret =security_bpf_map (map,OPEN_FMODE (flags)); +if (ret <0 ) +return ret; +return anon_inode_getfd ("bpf-map" , &bpf_map_fops, map, flags |O_CLOEXEC ); }
@@ -403,10 +410,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 +439,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 +919,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 +978,12 @@ static const struct file_operations bpf_prog_fops = {int bpf_prog_new_fd (struct bpf_prog *prog) { +int ret; + + ret =security_bpf_prog (prog); +if (ret <0 ) +return ret; +return anon_inode_getfd ("bpf-prog" , &bpf_prog_fops, prog,O_RDWR |O_CLOEXEC ); }
@@ -1103,10 +1123,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 +1188,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 +1656,10 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, sizif (copy_from_user (&attr, uattr, size) !=0 )return -EFAULT ; + err =security_bpf (cmd, &attr, size); +if (err <0 ) +return err; +switch (cmd) {case BPF_MAP_CREATE : err =map_create (&attr);
security/security.c +32 -0
@@ -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 */
Signature · 6 lines
2.15.0.rc0.271.g36b669edcc-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
PATCH v6 4/5 selinux: bpf: Add selinux check for eBPF syscall operations
In reply to Chenbo FengFrom: Chenbo Feng <[email protected]>
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.
--- security/selinux/hooks.c | 111 ++++++++++++++++++++++++++++++++++++ security/selinux/include/classmap.h | 2 + security/selinux/include/objsec.h | 4 ++ 3 files changed, 117 insertions(+)
security/selinux/hooks.c +111 -0
@@ -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 ,BPF__MAP_CREATE , +NULL ); +break ; +case BPF_PROG_LOAD : + ret =avc_has_perm (sid, sid,SECCLASS_BPF ,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 (fmode &FMODE_READ ) + av |=BPF__MAP_READ ; +if (fmode &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 , +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 , +BPF__PROG_RUN ,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 )
security/selinux/include/classmap.h +2 -0
@@ -237,6 +237,8 @@ struct security_class_mapping secclass_map[] = { { "access", NULL } }, {"infiniband_endport" , { "manage_subnet", NULL } }, + {"bpf" , + {"map_create", "map_read", "map_write", "prog_load", "prog_run"} }, { NULL } };
security/selinux/include/objsec.h +4 -0
@@ -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_ */
Signature · 6 lines
2.15.0.rc0.271.g36b669edcc-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
PATCH v6 5/5 selinux: bpf: Add addtional check for bpf object file receive
In reply to Chenbo FengFrom: Chenbo Feng <[email protected]>
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.
--- include/linux/bpf.h | 3 +++ kernel/bpf/syscall.c | 4 ++-- security/selinux/hooks.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-)
include/linux/bpf.h +3 -0
@@ -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) \
kernel/bpf/syscall.c +2 -2
@@ -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
@@ -967,7 +967,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
security/selinux/hooks.c +49 -0
73 lines not on this page · raw message
Signature · 6 lines
2.15.0.rc0.271.g36b669edcc-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
David Miller
maintainerFrom: Chenbo Feng <[email protected]>
Date: Mon, 16 Oct 2017 12:11:30 -0700
Quoted 40 lines
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 onlyCAP_SYS_ADMINprocesses. The CAP_SYS_ADMIN-only mode is not ideal because most users do not have this capability and granting a userCAP_SYS_ADMINgrants too many other security-sensitive permissions. It also unnecessarily allows allCAP_SYS_ADMINprocesses 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.
Quoted 5 lines
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.
Series applied.
--
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
David Miller
maintainerFrom: David Miller <[email protected]>
Date: Wed, 18 Oct 2017 13:47:29 +0100 (WEST)
Quoted 1 line
Series applied.
I had to revert as this breaks the build:
net/netfilter/xt_bpf.c: In function ?__bpf_mt_check_path?:net/netfilter/xt_bpf.c:59:7: error: too few arguments to function ?bpf_obj_get_user?
fd = bpf_obj_get_user(path);
^~~~~~~~~~~~~~~~????{.n?+???????+%?????????w??{.n?+????{??????????v?^?)????w*jg??????????j????G?????????j:+v???w?j?m????????w?????f???h?????????
Quoted 1 line · David Miller
Series applied.
I hadn't gotten to reviewing this patchset yet.
Please wait for more acks/reviews from LSM folk for things touching security/, next time.
Signature · 7 lines
James Morris <[email protected]> -- 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
Daniel Borkmann
maintainerQuoted 8 lines · James Morris
Quoted 1 line · David Miller
Series applied.
I hadn't gotten to reviewing this patchset yet.
Please wait for more acks/reviews from LSM folk for things touching
security/, next time.
It didn't get applied yet due to a build error, v7 is pending here if you want to take a look (the series is already on the lists with multiple feedback rounds since beginning of Oct):
http://patchwork.ozlabs.org/project/netdev/list/?series=8967
Thanks,
Daniel
--
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
David Miller
maintainerFrom: James Morris <[email protected]>
Date: Thu, 19 Oct 2017 12:49:59 +1100 (AEDT)
Quoted 8 lines · David Miller
Quoted 2 lines
Series applied.
I hadn't gotten to reviewing this patchset yet.
Please wait for more acks/reviews from LSM folk for things touching
security/, next time.
Well it got reverted and respun in order to deal with build failures. So you have some time.
However...
Please review the changes more expediantly. I don't like paches rotting in my backlog for more than a day or two, weekends not withstanding. So if I don't see anything after two work week days, I'll use my best judgment and apply if I can't find a major flaw in the change(s).
--
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