Commit e1cef620 authored by Andrii Nakryiko's avatar Andrii Nakryiko Committed by Alexei Starovoitov

bpf: add BPF token support to BPF_PROG_LOAD command

Add basic support of BPF token to BPF_PROG_LOAD. Wire through a set of
allowed BPF program types and attach types, derived from BPF FS at BPF
token creation time. Then make sure we perform bpf_token_capable()
checks everywhere where it's relevant.
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/r/20231130185229.2688956-7-andrii@kernel.orgSigned-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent ee54b1a9
...@@ -1461,6 +1461,7 @@ struct bpf_prog_aux { ...@@ -1461,6 +1461,7 @@ struct bpf_prog_aux {
#ifdef CONFIG_SECURITY #ifdef CONFIG_SECURITY
void *security; void *security;
#endif #endif
struct bpf_token *token;
struct bpf_prog_offload *offload; struct bpf_prog_offload *offload;
struct btf *btf; struct btf *btf;
struct bpf_func_info *func_info; struct bpf_func_info *func_info;
...@@ -1601,6 +1602,8 @@ struct bpf_token { ...@@ -1601,6 +1602,8 @@ struct bpf_token {
struct user_namespace *userns; struct user_namespace *userns;
u64 allowed_cmds; u64 allowed_cmds;
u64 allowed_maps; u64 allowed_maps;
u64 allowed_progs;
u64 allowed_attachs;
}; };
struct bpf_struct_ops_value; struct bpf_struct_ops_value;
...@@ -2238,6 +2241,9 @@ struct bpf_token *bpf_token_get_from_fd(u32 ufd); ...@@ -2238,6 +2241,9 @@ struct bpf_token *bpf_token_get_from_fd(u32 ufd);
bool bpf_token_allow_cmd(const struct bpf_token *token, enum bpf_cmd cmd); bool bpf_token_allow_cmd(const struct bpf_token *token, enum bpf_cmd cmd);
bool bpf_token_allow_map_type(const struct bpf_token *token, enum bpf_map_type type); bool bpf_token_allow_map_type(const struct bpf_token *token, enum bpf_map_type type);
bool bpf_token_allow_prog_type(const struct bpf_token *token,
enum bpf_prog_type prog_type,
enum bpf_attach_type attach_type);
int bpf_obj_pin_user(u32 ufd, int path_fd, const char __user *pathname); int bpf_obj_pin_user(u32 ufd, int path_fd, const char __user *pathname);
int bpf_obj_get_user(int path_fd, const char __user *pathname, int flags); int bpf_obj_get_user(int path_fd, const char __user *pathname, int flags);
......
...@@ -1028,6 +1028,7 @@ enum bpf_prog_type { ...@@ -1028,6 +1028,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_SK_LOOKUP, BPF_PROG_TYPE_SK_LOOKUP,
BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */ BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */
BPF_PROG_TYPE_NETFILTER, BPF_PROG_TYPE_NETFILTER,
__MAX_BPF_PROG_TYPE
}; };
enum bpf_attach_type { enum bpf_attach_type {
...@@ -1504,6 +1505,7 @@ union bpf_attr { ...@@ -1504,6 +1505,7 @@ union bpf_attr {
* truncated), or smaller (if log buffer wasn't filled completely). * truncated), or smaller (if log buffer wasn't filled completely).
*/ */
__u32 log_true_size; __u32 log_true_size;
__u32 prog_token_fd;
}; };
struct { /* anonymous struct used by BPF_OBJ_* commands */ struct { /* anonymous struct used by BPF_OBJ_* commands */
......
...@@ -2751,6 +2751,7 @@ void bpf_prog_free(struct bpf_prog *fp) ...@@ -2751,6 +2751,7 @@ void bpf_prog_free(struct bpf_prog *fp)
if (aux->dst_prog) if (aux->dst_prog)
bpf_prog_put(aux->dst_prog); bpf_prog_put(aux->dst_prog);
bpf_token_put(aux->token);
INIT_WORK(&aux->work, bpf_prog_free_deferred); INIT_WORK(&aux->work, bpf_prog_free_deferred);
schedule_work(&aux->work); schedule_work(&aux->work);
} }
......
...@@ -619,12 +619,14 @@ static int bpf_show_options(struct seq_file *m, struct dentry *root) ...@@ -619,12 +619,14 @@ static int bpf_show_options(struct seq_file *m, struct dentry *root)
else if (opts->delegate_maps) else if (opts->delegate_maps)
seq_printf(m, ",delegate_maps=0x%llx", opts->delegate_maps); seq_printf(m, ",delegate_maps=0x%llx", opts->delegate_maps);
if (opts->delegate_progs == ~0ULL) mask = (1ULL << __MAX_BPF_PROG_TYPE) - 1;
if ((opts->delegate_progs & mask) == mask)
seq_printf(m, ",delegate_progs=any"); seq_printf(m, ",delegate_progs=any");
else if (opts->delegate_progs) else if (opts->delegate_progs)
seq_printf(m, ",delegate_progs=0x%llx", opts->delegate_progs); seq_printf(m, ",delegate_progs=0x%llx", opts->delegate_progs);
if (opts->delegate_attachs == ~0ULL) mask = (1ULL << __MAX_BPF_ATTACH_TYPE) - 1;
if ((opts->delegate_attachs & mask) == mask)
seq_printf(m, ",delegate_attachs=any"); seq_printf(m, ",delegate_attachs=any");
else if (opts->delegate_attachs) else if (opts->delegate_attachs)
seq_printf(m, ",delegate_attachs=0x%llx", opts->delegate_attachs); seq_printf(m, ",delegate_attachs=0x%llx", opts->delegate_attachs);
......
...@@ -2608,13 +2608,15 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type) ...@@ -2608,13 +2608,15 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)
} }
/* last field in 'union bpf_attr' used by this command */ /* last field in 'union bpf_attr' used by this command */
#define BPF_PROG_LOAD_LAST_FIELD log_true_size #define BPF_PROG_LOAD_LAST_FIELD prog_token_fd
static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
{ {
enum bpf_prog_type type = attr->prog_type; enum bpf_prog_type type = attr->prog_type;
struct bpf_prog *prog, *dst_prog = NULL; struct bpf_prog *prog, *dst_prog = NULL;
struct btf *attach_btf = NULL; struct btf *attach_btf = NULL;
struct bpf_token *token = NULL;
bool bpf_cap;
int err; int err;
char license[128]; char license[128];
...@@ -2631,10 +2633,31 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) ...@@ -2631,10 +2633,31 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
BPF_F_TEST_REG_INVARIANTS)) BPF_F_TEST_REG_INVARIANTS))
return -EINVAL; return -EINVAL;
bpf_prog_load_fixup_attach_type(attr);
if (attr->prog_token_fd) {
token = bpf_token_get_from_fd(attr->prog_token_fd);
if (IS_ERR(token))
return PTR_ERR(token);
/* if current token doesn't grant prog loading permissions,
* then we can't use this token, so ignore it and rely on
* system-wide capabilities checks
*/
if (!bpf_token_allow_cmd(token, BPF_PROG_LOAD) ||
!bpf_token_allow_prog_type(token, attr->prog_type,
attr->expected_attach_type)) {
bpf_token_put(token);
token = NULL;
}
}
bpf_cap = bpf_token_capable(token, CAP_BPF);
err = -EPERM;
if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) &&
(attr->prog_flags & BPF_F_ANY_ALIGNMENT) && (attr->prog_flags & BPF_F_ANY_ALIGNMENT) &&
!bpf_capable()) !bpf_cap)
return -EPERM; goto put_token;
/* Intent here is for unprivileged_bpf_disabled to block BPF program /* Intent here is for unprivileged_bpf_disabled to block BPF program
* creation for unprivileged users; other actions depend * creation for unprivileged users; other actions depend
...@@ -2643,21 +2666,23 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) ...@@ -2643,21 +2666,23 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
* capability checks are still carried out for these * capability checks are still carried out for these
* and other operations. * and other operations.
*/ */
if (sysctl_unprivileged_bpf_disabled && !bpf_capable()) if (sysctl_unprivileged_bpf_disabled && !bpf_cap)
return -EPERM; goto put_token;
if (attr->insn_cnt == 0 || if (attr->insn_cnt == 0 ||
attr->insn_cnt > (bpf_capable() ? BPF_COMPLEXITY_LIMIT_INSNS : BPF_MAXINSNS)) attr->insn_cnt > (bpf_cap ? BPF_COMPLEXITY_LIMIT_INSNS : BPF_MAXINSNS)) {
return -E2BIG; err = -E2BIG;
goto put_token;
}
if (type != BPF_PROG_TYPE_SOCKET_FILTER && if (type != BPF_PROG_TYPE_SOCKET_FILTER &&
type != BPF_PROG_TYPE_CGROUP_SKB && type != BPF_PROG_TYPE_CGROUP_SKB &&
!bpf_capable()) !bpf_cap)
return -EPERM; goto put_token;
if (is_net_admin_prog_type(type) && !bpf_net_capable()) if (is_net_admin_prog_type(type) && !bpf_token_capable(token, CAP_NET_ADMIN))
return -EPERM; goto put_token;
if (is_perfmon_prog_type(type) && !perfmon_capable()) if (is_perfmon_prog_type(type) && !bpf_token_capable(token, CAP_PERFMON))
return -EPERM; goto put_token;
/* attach_prog_fd/attach_btf_obj_fd can specify fd of either bpf_prog /* attach_prog_fd/attach_btf_obj_fd can specify fd of either bpf_prog
* or btf, we need to check which one it is * or btf, we need to check which one it is
...@@ -2667,27 +2692,33 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) ...@@ -2667,27 +2692,33 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
if (IS_ERR(dst_prog)) { if (IS_ERR(dst_prog)) {
dst_prog = NULL; dst_prog = NULL;
attach_btf = btf_get_by_fd(attr->attach_btf_obj_fd); attach_btf = btf_get_by_fd(attr->attach_btf_obj_fd);
if (IS_ERR(attach_btf)) if (IS_ERR(attach_btf)) {
return -EINVAL; err = -EINVAL;
goto put_token;
}
if (!btf_is_kernel(attach_btf)) { if (!btf_is_kernel(attach_btf)) {
/* attaching through specifying bpf_prog's BTF /* attaching through specifying bpf_prog's BTF
* objects directly might be supported eventually * objects directly might be supported eventually
*/ */
btf_put(attach_btf); btf_put(attach_btf);
return -ENOTSUPP; err = -ENOTSUPP;
goto put_token;
} }
} }
} else if (attr->attach_btf_id) { } else if (attr->attach_btf_id) {
/* fall back to vmlinux BTF, if BTF type ID is specified */ /* fall back to vmlinux BTF, if BTF type ID is specified */
attach_btf = bpf_get_btf_vmlinux(); attach_btf = bpf_get_btf_vmlinux();
if (IS_ERR(attach_btf)) if (IS_ERR(attach_btf)) {
return PTR_ERR(attach_btf); err = PTR_ERR(attach_btf);
if (!attach_btf) goto put_token;
return -EINVAL; }
if (!attach_btf) {
err = -EINVAL;
goto put_token;
}
btf_get(attach_btf); btf_get(attach_btf);
} }
bpf_prog_load_fixup_attach_type(attr);
if (bpf_prog_load_check_attach(type, attr->expected_attach_type, if (bpf_prog_load_check_attach(type, attr->expected_attach_type,
attach_btf, attr->attach_btf_id, attach_btf, attr->attach_btf_id,
dst_prog)) { dst_prog)) {
...@@ -2695,7 +2726,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) ...@@ -2695,7 +2726,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
bpf_prog_put(dst_prog); bpf_prog_put(dst_prog);
if (attach_btf) if (attach_btf)
btf_put(attach_btf); btf_put(attach_btf);
return -EINVAL; err = -EINVAL;
goto put_token;
} }
/* plain bpf_prog allocation */ /* plain bpf_prog allocation */
...@@ -2705,7 +2737,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) ...@@ -2705,7 +2737,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
bpf_prog_put(dst_prog); bpf_prog_put(dst_prog);
if (attach_btf) if (attach_btf)
btf_put(attach_btf); btf_put(attach_btf);
return -ENOMEM; err = -EINVAL;
goto put_token;
} }
prog->expected_attach_type = attr->expected_attach_type; prog->expected_attach_type = attr->expected_attach_type;
...@@ -2716,6 +2749,10 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) ...@@ -2716,6 +2749,10 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
prog->aux->sleepable = attr->prog_flags & BPF_F_SLEEPABLE; prog->aux->sleepable = attr->prog_flags & BPF_F_SLEEPABLE;
prog->aux->xdp_has_frags = attr->prog_flags & BPF_F_XDP_HAS_FRAGS; prog->aux->xdp_has_frags = attr->prog_flags & BPF_F_XDP_HAS_FRAGS;
/* move token into prog->aux, reuse taken refcnt */
prog->aux->token = token;
token = NULL;
err = security_bpf_prog_alloc(prog->aux); err = security_bpf_prog_alloc(prog->aux);
if (err) if (err)
goto free_prog; goto free_prog;
...@@ -2817,6 +2854,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) ...@@ -2817,6 +2854,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
if (prog->aux->attach_btf) if (prog->aux->attach_btf)
btf_put(prog->aux->attach_btf); btf_put(prog->aux->attach_btf);
bpf_prog_free(prog); bpf_prog_free(prog);
put_token:
bpf_token_put(token);
return err; return err;
} }
...@@ -3806,7 +3845,7 @@ static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog, ...@@ -3806,7 +3845,7 @@ static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog,
case BPF_PROG_TYPE_SK_LOOKUP: case BPF_PROG_TYPE_SK_LOOKUP:
return attach_type == prog->expected_attach_type ? 0 : -EINVAL; return attach_type == prog->expected_attach_type ? 0 : -EINVAL;
case BPF_PROG_TYPE_CGROUP_SKB: case BPF_PROG_TYPE_CGROUP_SKB:
if (!bpf_net_capable()) if (!bpf_token_capable(prog->aux->token, CAP_NET_ADMIN))
/* cg-skb progs can be loaded by unpriv user. /* cg-skb progs can be loaded by unpriv user.
* check permissions at attach time. * check permissions at attach time.
*/ */
......
...@@ -79,6 +79,20 @@ static void bpf_token_show_fdinfo(struct seq_file *m, struct file *filp) ...@@ -79,6 +79,20 @@ static void bpf_token_show_fdinfo(struct seq_file *m, struct file *filp)
seq_printf(m, "allowed_maps:\tany\n"); seq_printf(m, "allowed_maps:\tany\n");
else else
seq_printf(m, "allowed_maps:\t0x%llx\n", token->allowed_maps); seq_printf(m, "allowed_maps:\t0x%llx\n", token->allowed_maps);
BUILD_BUG_ON(__MAX_BPF_PROG_TYPE >= 64);
mask = (1ULL << __MAX_BPF_PROG_TYPE) - 1;
if ((token->allowed_progs & mask) == mask)
seq_printf(m, "allowed_progs:\tany\n");
else
seq_printf(m, "allowed_progs:\t0x%llx\n", token->allowed_progs);
BUILD_BUG_ON(__MAX_BPF_ATTACH_TYPE >= 64);
mask = (1ULL << __MAX_BPF_ATTACH_TYPE) - 1;
if ((token->allowed_attachs & mask) == mask)
seq_printf(m, "allowed_attachs:\tany\n");
else
seq_printf(m, "allowed_attachs:\t0x%llx\n", token->allowed_attachs);
} }
#define BPF_TOKEN_INODE_NAME "bpf-token" #define BPF_TOKEN_INODE_NAME "bpf-token"
...@@ -169,6 +183,8 @@ int bpf_token_create(union bpf_attr *attr) ...@@ -169,6 +183,8 @@ int bpf_token_create(union bpf_attr *attr)
mnt_opts = path.dentry->d_sb->s_fs_info; mnt_opts = path.dentry->d_sb->s_fs_info;
token->allowed_cmds = mnt_opts->delegate_cmds; token->allowed_cmds = mnt_opts->delegate_cmds;
token->allowed_maps = mnt_opts->delegate_maps; token->allowed_maps = mnt_opts->delegate_maps;
token->allowed_progs = mnt_opts->delegate_progs;
token->allowed_attachs = mnt_opts->delegate_attachs;
fd = get_unused_fd_flags(O_CLOEXEC); fd = get_unused_fd_flags(O_CLOEXEC);
if (fd < 0) { if (fd < 0) {
...@@ -228,3 +244,14 @@ bool bpf_token_allow_map_type(const struct bpf_token *token, enum bpf_map_type t ...@@ -228,3 +244,14 @@ bool bpf_token_allow_map_type(const struct bpf_token *token, enum bpf_map_type t
return token->allowed_maps & (1ULL << type); return token->allowed_maps & (1ULL << type);
} }
bool bpf_token_allow_prog_type(const struct bpf_token *token,
enum bpf_prog_type prog_type,
enum bpf_attach_type attach_type)
{
if (!token || prog_type >= __MAX_BPF_PROG_TYPE || attach_type >= __MAX_BPF_ATTACH_TYPE)
return false;
return (token->allowed_progs & (1ULL << prog_type)) &&
(token->allowed_attachs & (1ULL << attach_type));
}
...@@ -1028,6 +1028,7 @@ enum bpf_prog_type { ...@@ -1028,6 +1028,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_SK_LOOKUP, BPF_PROG_TYPE_SK_LOOKUP,
BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */ BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */
BPF_PROG_TYPE_NETFILTER, BPF_PROG_TYPE_NETFILTER,
__MAX_BPF_PROG_TYPE
}; };
enum bpf_attach_type { enum bpf_attach_type {
...@@ -1504,6 +1505,7 @@ union bpf_attr { ...@@ -1504,6 +1505,7 @@ union bpf_attr {
* truncated), or smaller (if log buffer wasn't filled completely). * truncated), or smaller (if log buffer wasn't filled completely).
*/ */
__u32 log_true_size; __u32 log_true_size;
__u32 prog_token_fd;
}; };
struct { /* anonymous struct used by BPF_OBJ_* commands */ struct { /* anonymous struct used by BPF_OBJ_* commands */
......
...@@ -30,6 +30,8 @@ void test_libbpf_probe_prog_types(void) ...@@ -30,6 +30,8 @@ void test_libbpf_probe_prog_types(void)
if (prog_type == BPF_PROG_TYPE_UNSPEC) if (prog_type == BPF_PROG_TYPE_UNSPEC)
continue; continue;
if (strcmp(prog_type_name, "__MAX_BPF_PROG_TYPE") == 0)
continue;
if (!test__start_subtest(prog_type_name)) if (!test__start_subtest(prog_type_name))
continue; continue;
......
...@@ -189,6 +189,9 @@ static void test_libbpf_bpf_prog_type_str(void) ...@@ -189,6 +189,9 @@ static void test_libbpf_bpf_prog_type_str(void)
const char *prog_type_str; const char *prog_type_str;
char buf[256]; char buf[256];
if (prog_type == __MAX_BPF_PROG_TYPE)
continue;
prog_type_name = btf__str_by_offset(btf, e->name_off); prog_type_name = btf__str_by_offset(btf, e->name_off);
prog_type_str = libbpf_bpf_prog_type_str(prog_type); prog_type_str = libbpf_bpf_prog_type_str(prog_type);
ASSERT_OK_PTR(prog_type_str, prog_type_name); ASSERT_OK_PTR(prog_type_str, prog_type_name);
......
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