bpf: Add BPF token support to BPF_PROG_LOAD command
authorAndrii Nakryiko <andrii@kernel.org>
Wed, 24 Jan 2024 02:21:03 +0000 (18:21 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Thu, 25 Jan 2024 00:21:01 +0000 (16:21 -0800)
Add basic support of BPF token to BPF_PROG_LOAD. BPF_F_TOKEN_FD flag
should be set in prog_flags field when providing prog_token_fd.

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: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20240124022127.2379740-7-andrii@kernel.org
include/linux/bpf.h
include/uapi/linux/bpf.h
kernel/bpf/core.c
kernel/bpf/inode.c
kernel/bpf/syscall.c
kernel/bpf/token.c
tools/include/uapi/linux/bpf.h
tools/testing/selftests/bpf/prog_tests/libbpf_probes.c
tools/testing/selftests/bpf/prog_tests/libbpf_str.c

index 8252452d0c4daa1887c0fdb1caccb25bc7211b6f..d0bf37e3f166af1df355e543058ca66042d8582b 100644 (file)
@@ -1489,6 +1489,7 @@ struct bpf_prog_aux {
 #ifdef CONFIG_SECURITY
        void *security;
 #endif
+       struct bpf_token *token;
        struct bpf_prog_offload *offload;
        struct btf *btf;
        struct bpf_func_info *func_info;
@@ -1631,6 +1632,8 @@ struct bpf_token {
        struct user_namespace *userns;
        u64 allowed_cmds;
        u64 allowed_maps;
+       u64 allowed_progs;
+       u64 allowed_attachs;
 };
 
 struct bpf_struct_ops_value;
@@ -2299,6 +2302,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_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_get_user(int path_fd, const char __user *pathname, int flags);
index cb2c888e3bb4dee2337229a264083f43cca4e756..d96708380e523ebc6eff70798babfc39527b5ad4 100644 (file)
@@ -1028,6 +1028,7 @@ enum bpf_prog_type {
        BPF_PROG_TYPE_SK_LOOKUP,
        BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */
        BPF_PROG_TYPE_NETFILTER,
+       __MAX_BPF_PROG_TYPE
 };
 
 enum bpf_attach_type {
@@ -1520,6 +1521,10 @@ union bpf_attr {
                 * truncated), or smaller (if log buffer wasn't filled completely).
                 */
                __u32           log_true_size;
+               /* BPF token FD to use with BPF_PROG_LOAD operation.
+                * If provided, prog_flags should have BPF_F_TOKEN_FD flag set.
+                */
+               __s32           prog_token_fd;
        };
 
        struct { /* anonymous struct used by BPF_OBJ_* commands */
index fbb1d95a9b446d13f1f57bc8298c4dbc6fc494e0..00dccba2976980b6449b61b8c2fa991c30efd143 100644 (file)
@@ -2779,6 +2779,7 @@ void bpf_prog_free(struct bpf_prog *fp)
 
        if (aux->dst_prog)
                bpf_prog_put(aux->dst_prog);
+       bpf_token_put(aux->token);
        INIT_WORK(&aux->work, bpf_prog_free_deferred);
        schedule_work(&aux->work);
 }
index 034b7e4d8f192284594d5858f50d712a81c0a899..5fb10da5717f9f4a17860b3469c6df71316bda1d 100644 (file)
@@ -626,12 +626,14 @@ static int bpf_show_options(struct seq_file *m, struct dentry *root)
        else if (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");
        else if (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");
        else if (opts->delegate_attachs)
                seq_printf(m, ",delegate_attachs=0x%llx", opts->delegate_attachs);
index 45b3a55896eba5ecaa6b8fa931cdfdfc8d7ee0bc..61b4bf4cc2870c2559dbe3f4089a74fb1c40e649 100644 (file)
@@ -2626,13 +2626,15 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)
 }
 
 /* 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)
 {
        enum bpf_prog_type type = attr->prog_type;
        struct bpf_prog *prog, *dst_prog = NULL;
        struct btf *attach_btf = NULL;
+       struct bpf_token *token = NULL;
+       bool bpf_cap;
        int err;
        char license[128];
 
@@ -2646,13 +2648,35 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
                                 BPF_F_TEST_RND_HI32 |
                                 BPF_F_XDP_HAS_FRAGS |
                                 BPF_F_XDP_DEV_BOUND_ONLY |
-                                BPF_F_TEST_REG_INVARIANTS))
+                                BPF_F_TEST_REG_INVARIANTS |
+                                BPF_F_TOKEN_FD))
                return -EINVAL;
 
+       bpf_prog_load_fixup_attach_type(attr);
+
+       if (attr->prog_flags & BPF_F_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) &&
            (attr->prog_flags & BPF_F_ANY_ALIGNMENT) &&
-           !bpf_capable())
-               return -EPERM;
+           !bpf_cap)
+               goto put_token;
 
        /* Intent here is for unprivileged_bpf_disabled to block BPF program
         * creation for unprivileged users; other actions depend
@@ -2661,21 +2685,23 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
         * capability checks are still carried out for these
         * and other operations.
         */
-       if (sysctl_unprivileged_bpf_disabled && !bpf_capable())
-               return -EPERM;
+       if (sysctl_unprivileged_bpf_disabled && !bpf_cap)
+               goto put_token;
 
        if (attr->insn_cnt == 0 ||
-           attr->insn_cnt > (bpf_capable() ? BPF_COMPLEXITY_LIMIT_INSNS : BPF_MAXINSNS))
-               return -E2BIG;
+           attr->insn_cnt > (bpf_cap ? BPF_COMPLEXITY_LIMIT_INSNS : BPF_MAXINSNS)) {
+               err = -E2BIG;
+               goto put_token;
+       }
        if (type != BPF_PROG_TYPE_SOCKET_FILTER &&
            type != BPF_PROG_TYPE_CGROUP_SKB &&
-           !bpf_capable())
-               return -EPERM;
+           !bpf_cap)
+               goto put_token;
 
-       if (is_net_admin_prog_type(type) && !bpf_net_capable())
-               return -EPERM;
-       if (is_perfmon_prog_type(type) && !perfmon_capable())
-               return -EPERM;
+       if (is_net_admin_prog_type(type) && !bpf_token_capable(token, CAP_NET_ADMIN))
+               goto put_token;
+       if (is_perfmon_prog_type(type) && !bpf_token_capable(token, CAP_PERFMON))
+               goto put_token;
 
        /* attach_prog_fd/attach_btf_obj_fd can specify fd of either bpf_prog
         * or btf, we need to check which one it is
@@ -2685,27 +2711,33 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
                if (IS_ERR(dst_prog)) {
                        dst_prog = NULL;
                        attach_btf = btf_get_by_fd(attr->attach_btf_obj_fd);
-                       if (IS_ERR(attach_btf))
-                               return -EINVAL;
+                       if (IS_ERR(attach_btf)) {
+                               err = -EINVAL;
+                               goto put_token;
+                       }
                        if (!btf_is_kernel(attach_btf)) {
                                /* attaching through specifying bpf_prog's BTF
                                 * objects directly might be supported eventually
                                 */
                                btf_put(attach_btf);
-                               return -ENOTSUPP;
+                               err = -ENOTSUPP;
+                               goto put_token;
                        }
                }
        } else if (attr->attach_btf_id) {
                /* fall back to vmlinux BTF, if BTF type ID is specified */
                attach_btf = bpf_get_btf_vmlinux();
-               if (IS_ERR(attach_btf))
-                       return PTR_ERR(attach_btf);
-               if (!attach_btf)
-                       return -EINVAL;
+               if (IS_ERR(attach_btf)) {
+                       err = PTR_ERR(attach_btf);
+                       goto put_token;
+               }
+               if (!attach_btf) {
+                       err = -EINVAL;
+                       goto put_token;
+               }
                btf_get(attach_btf);
        }
 
-       bpf_prog_load_fixup_attach_type(attr);
        if (bpf_prog_load_check_attach(type, attr->expected_attach_type,
                                       attach_btf, attr->attach_btf_id,
                                       dst_prog)) {
@@ -2713,7 +2745,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
                        bpf_prog_put(dst_prog);
                if (attach_btf)
                        btf_put(attach_btf);
-               return -EINVAL;
+               err = -EINVAL;
+               goto put_token;
        }
 
        /* plain bpf_prog allocation */
@@ -2723,7 +2756,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
                        bpf_prog_put(dst_prog);
                if (attach_btf)
                        btf_put(attach_btf);
-               return -ENOMEM;
+               err = -EINVAL;
+               goto put_token;
        }
 
        prog->expected_attach_type = attr->expected_attach_type;
@@ -2734,6 +2768,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->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);
        if (err)
                goto free_prog;
@@ -2851,6 +2889,8 @@ free_prog:
        if (prog->aux->attach_btf)
                btf_put(prog->aux->attach_btf);
        bpf_prog_free(prog);
+put_token:
+       bpf_token_put(token);
        return err;
 }
 
@@ -3858,7 +3898,7 @@ static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog,
        case BPF_PROG_TYPE_SK_LOOKUP:
                return attach_type == prog->expected_attach_type ? 0 : -EINVAL;
        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.
                         * check permissions at attach time.
                         */
index bc86be4ca5679013943879f8effacb728401c431..c13c73788d8c34fe879f678aa4cf7e26634791fb 100644 (file)
@@ -80,6 +80,20 @@ static void bpf_token_show_fdinfo(struct seq_file *m, struct file *filp)
                seq_printf(m, "allowed_maps:\tany\n");
        else
                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"
@@ -176,6 +190,8 @@ int bpf_token_create(union bpf_attr *attr)
        mnt_opts = path.dentry->d_sb->s_fs_info;
        token->allowed_cmds = mnt_opts->delegate_cmds;
        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);
        if (fd < 0) {
@@ -231,3 +247,14 @@ bool bpf_token_allow_map_type(const struct bpf_token *token, enum bpf_map_type t
 
        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));
+}
index cb2c888e3bb4dee2337229a264083f43cca4e756..d96708380e523ebc6eff70798babfc39527b5ad4 100644 (file)
@@ -1028,6 +1028,7 @@ enum bpf_prog_type {
        BPF_PROG_TYPE_SK_LOOKUP,
        BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */
        BPF_PROG_TYPE_NETFILTER,
+       __MAX_BPF_PROG_TYPE
 };
 
 enum bpf_attach_type {
@@ -1520,6 +1521,10 @@ union bpf_attr {
                 * truncated), or smaller (if log buffer wasn't filled completely).
                 */
                __u32           log_true_size;
+               /* BPF token FD to use with BPF_PROG_LOAD operation.
+                * If provided, prog_flags should have BPF_F_TOKEN_FD flag set.
+                */
+               __s32           prog_token_fd;
        };
 
        struct { /* anonymous struct used by BPF_OBJ_* commands */
index 573249a2814dc2741f7b43ba39ae078cfb87c095..4ed46ed58a7b098259572581bcc87ee85a9f9013 100644 (file)
@@ -30,6 +30,8 @@ void test_libbpf_probe_prog_types(void)
 
                if (prog_type == BPF_PROG_TYPE_UNSPEC)
                        continue;
+               if (strcmp(prog_type_name, "__MAX_BPF_PROG_TYPE") == 0)
+                       continue;
 
                if (!test__start_subtest(prog_type_name))
                        continue;
index 1f328c0d8aff690d62f91fa0d73b0312451ab8c8..62ea855ec4d04a8947a5aefcb0ed973bdf686af9 100644 (file)
@@ -189,6 +189,9 @@ static void test_libbpf_bpf_prog_type_str(void)
                const char *prog_type_str;
                char buf[256];
 
+               if (prog_type == __MAX_BPF_PROG_TYPE)
+                       continue;
+
                prog_type_name = btf__str_by_offset(btf, e->name_off);
                prog_type_str = libbpf_bpf_prog_type_str(prog_type);
                ASSERT_OK_PTR(prog_type_str, prog_type_name);