Commit afdb09c7 authored by Chenbo Feng's avatar Chenbo Feng Committed by David S. Miller

security: bpf: Add LSM hooks for bpf object related syscall

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: default avatarChenbo Feng <fengc@google.com>
Acked-by: default avatarJames Morris <james.l.morris@oracle.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e043325b
...@@ -57,6 +57,9 @@ struct bpf_map { ...@@ -57,6 +57,9 @@ struct bpf_map {
atomic_t usercnt; atomic_t usercnt;
struct bpf_map *inner_map_meta; struct bpf_map *inner_map_meta;
char name[BPF_OBJ_NAME_LEN]; char name[BPF_OBJ_NAME_LEN];
#ifdef CONFIG_SECURITY
void *security;
#endif
}; };
/* function argument constraints */ /* function argument constraints */
...@@ -193,6 +196,9 @@ struct bpf_prog_aux { ...@@ -193,6 +196,9 @@ struct bpf_prog_aux {
struct user_struct *user; struct user_struct *user;
u64 load_time; /* ns since boottime */ u64 load_time; /* ns since boottime */
char name[BPF_OBJ_NAME_LEN]; char name[BPF_OBJ_NAME_LEN];
#ifdef CONFIG_SECURITY
void *security;
#endif
union { union {
struct work_struct work; struct work_struct work;
struct rcu_head rcu; struct rcu_head rcu;
......
...@@ -1351,6 +1351,40 @@ ...@@ -1351,6 +1351,40 @@
* @inode we wish to get the security context of. * @inode we wish to get the security context of.
* @ctx is a pointer in which to place the allocated security context. * @ctx is a pointer in which to place the allocated security context.
* @ctxlen points to the place to put the length of @ctx. * @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 { union security_list_options {
int (*binder_set_context_mgr)(struct task_struct *mgr); int (*binder_set_context_mgr)(struct task_struct *mgr);
...@@ -1682,6 +1716,17 @@ union security_list_options { ...@@ -1682,6 +1716,17 @@ union security_list_options {
struct audit_context *actx); struct audit_context *actx);
void (*audit_rule_free)(void *lsmrule); void (*audit_rule_free)(void *lsmrule);
#endif /* CONFIG_AUDIT */ #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 { struct security_hook_heads {
...@@ -1901,6 +1946,15 @@ struct security_hook_heads { ...@@ -1901,6 +1946,15 @@ struct security_hook_heads {
struct list_head audit_rule_match; struct list_head audit_rule_match;
struct list_head audit_rule_free; struct list_head audit_rule_free;
#endif /* CONFIG_AUDIT */ #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; } __randomize_layout;
/* /*
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/bpf.h>
struct linux_binprm; struct linux_binprm;
struct cred; struct cred;
...@@ -1730,6 +1731,50 @@ static inline void securityfs_remove(struct dentry *dentry) ...@@ -1730,6 +1731,50 @@ static inline void securityfs_remove(struct dentry *dentry)
#endif #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 #ifdef CONFIG_SECURITY
static inline char *alloc_secdata(void) static inline char *alloc_secdata(void)
......
...@@ -212,6 +212,7 @@ static void bpf_map_free_deferred(struct work_struct *work) ...@@ -212,6 +212,7 @@ static void bpf_map_free_deferred(struct work_struct *work)
struct bpf_map *map = container_of(work, struct bpf_map, work); struct bpf_map *map = container_of(work, struct bpf_map, work);
bpf_map_uncharge_memlock(map); bpf_map_uncharge_memlock(map);
security_bpf_map_free(map);
/* implementation dependent freeing */ /* implementation dependent freeing */
map->ops->map_free(map); map->ops->map_free(map);
} }
...@@ -325,6 +326,12 @@ static const struct file_operations bpf_map_fops = { ...@@ -325,6 +326,12 @@ static const struct file_operations bpf_map_fops = {
int bpf_map_new_fd(struct bpf_map *map, int flags) 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, return anon_inode_getfd("bpf-map", &bpf_map_fops, map,
flags | O_CLOEXEC); flags | O_CLOEXEC);
} }
...@@ -405,10 +412,14 @@ static int map_create(union bpf_attr *attr) ...@@ -405,10 +412,14 @@ static int map_create(union bpf_attr *attr)
atomic_set(&map->refcnt, 1); atomic_set(&map->refcnt, 1);
atomic_set(&map->usercnt, 1); atomic_set(&map->usercnt, 1);
err = bpf_map_charge_memlock(map); err = security_bpf_map_alloc(map);
if (err) if (err)
goto free_map_nouncharge; goto free_map_nouncharge;
err = bpf_map_charge_memlock(map);
if (err)
goto free_map_sec;
err = bpf_map_alloc_id(map); err = bpf_map_alloc_id(map);
if (err) if (err)
goto free_map; goto free_map;
...@@ -430,6 +441,8 @@ static int map_create(union bpf_attr *attr) ...@@ -430,6 +441,8 @@ static int map_create(union bpf_attr *attr)
free_map: free_map:
bpf_map_uncharge_memlock(map); bpf_map_uncharge_memlock(map);
free_map_sec:
security_bpf_map_free(map);
free_map_nouncharge: free_map_nouncharge:
map->ops->map_free(map); map->ops->map_free(map);
return err; return err;
...@@ -914,6 +927,7 @@ static void __bpf_prog_put_rcu(struct rcu_head *rcu) ...@@ -914,6 +927,7 @@ static void __bpf_prog_put_rcu(struct rcu_head *rcu)
free_used_maps(aux); free_used_maps(aux);
bpf_prog_uncharge_memlock(aux->prog); bpf_prog_uncharge_memlock(aux->prog);
security_bpf_prog_free(aux);
bpf_prog_free(aux->prog); bpf_prog_free(aux->prog);
} }
...@@ -972,6 +986,12 @@ static const struct file_operations bpf_prog_fops = { ...@@ -972,6 +986,12 @@ static const struct file_operations bpf_prog_fops = {
int bpf_prog_new_fd(struct bpf_prog *prog) 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, return anon_inode_getfd("bpf-prog", &bpf_prog_fops, prog,
O_RDWR | O_CLOEXEC); O_RDWR | O_CLOEXEC);
} }
...@@ -1111,10 +1131,14 @@ static int bpf_prog_load(union bpf_attr *attr) ...@@ -1111,10 +1131,14 @@ static int bpf_prog_load(union bpf_attr *attr)
if (!prog) if (!prog)
return -ENOMEM; return -ENOMEM;
err = bpf_prog_charge_memlock(prog); err = security_bpf_prog_alloc(prog->aux);
if (err) if (err)
goto free_prog_nouncharge; goto free_prog_nouncharge;
err = bpf_prog_charge_memlock(prog);
if (err)
goto free_prog_sec;
prog->len = attr->insn_cnt; prog->len = attr->insn_cnt;
err = -EFAULT; err = -EFAULT;
...@@ -1172,6 +1196,8 @@ static int bpf_prog_load(union bpf_attr *attr) ...@@ -1172,6 +1196,8 @@ static int bpf_prog_load(union bpf_attr *attr)
free_used_maps(prog->aux); free_used_maps(prog->aux);
free_prog: free_prog:
bpf_prog_uncharge_memlock(prog); bpf_prog_uncharge_memlock(prog);
free_prog_sec:
security_bpf_prog_free(prog->aux);
free_prog_nouncharge: free_prog_nouncharge:
bpf_prog_free(prog); bpf_prog_free(prog);
return err; return err;
...@@ -1640,6 +1666,10 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz ...@@ -1640,6 +1666,10 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
if (copy_from_user(&attr, uattr, size) != 0) if (copy_from_user(&attr, uattr, size) != 0)
return -EFAULT; return -EFAULT;
err = security_bpf(cmd, &attr, size);
if (err < 0)
return err;
switch (cmd) { switch (cmd) {
case BPF_MAP_CREATE: case BPF_MAP_CREATE:
err = map_create(&attr); err = map_create(&attr);
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
* (at your option) any later version. * (at your option) any later version.
*/ */
#include <linux/bpf.h>
#include <linux/capability.h> #include <linux/capability.h>
#include <linux/dcache.h> #include <linux/dcache.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -1703,3 +1704,34 @@ int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule, ...@@ -1703,3 +1704,34 @@ int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule,
actx); actx);
} }
#endif /* CONFIG_AUDIT */ #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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment