[PATCH net-next 4/4] selinux: bpf: Add addtional check for bpf object file receive

Chenbo Feng fengc at google.com
Fri Oct 6 21:10:11 UTC 2017


On Thu, Oct 5, 2017 at 11:26 AM, Stephen Smalley <sds at tycho.nsa.gov> wrote:
> On Thu, 2017-10-05 at 09:37 -0400, Stephen Smalley wrote:
>> On Wed, 2017-10-04 at 11:29 -0700, Chenbo Feng wrote:
>> > From: Chenbo Feng <fengc at 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.
>> >
>> > Signed-off-by: Chenbo Feng <fengc at google.com>
>> > ---
>> >  include/linux/bpf.h      |  3 +++
>> >  kernel/bpf/syscall.c     |  4 ++--
>> >  security/selinux/hooks.c | 57
>> > +++++++++++++++++++++++++++++++++++++++++++++++-
>> >  3 files changed, 61 insertions(+), 3 deletions(-)
>> >
>> > diff --git a/include/linux/bpf.h b/include/linux/bpf.h
>> > index d757ea3f2228..ac8428a36d56 100644
>> > --- a/include/linux/bpf.h
>> > +++ b/include/linux/bpf.h
>> > @@ -250,6 +250,9 @@ int bpf_prog_test_run_skb(struct bpf_prog
>> > *prog,
>> > const union bpf_attr *kattr,
>> >  #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/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
>> > index 58ff769d58ab..5789a5359f0a 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
>> > @@ -965,7 +965,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/selinux/hooks.c b/security/selinux/hooks.c
>> > index 41aba4e3d57c..381474ce3216 100644
>> > --- a/security/selinux/hooks.c
>> > +++ b/security/selinux/hooks.c
>> > @@ -1847,6 +1847,7 @@ static int file_has_perm(const struct cred
>> > *cred,
>> >
>> >     /* av is zero if only checking access to the descriptor.
>> > */
>> >     rc = 0;
>> > +
>> >     if (av)
>> >             rc = inode_has_perm(cred, inode, av, &ad);
>> >
>> > @@ -2142,6 +2143,10 @@ static int
>> > selinux_binder_transfer_binder(struct task_struct *from,
>> >                         NULL);
>> >  }
>> >
>> > +#ifdef CONFIG_BPF_SYSCALL
>> > +static int bpf_fd_pass(struct file *file, u32 sid);
>> > +#endif
>> > +
>> >  static int selinux_binder_transfer_file(struct task_struct *from,
>> >                                     struct task_struct *to,
>> >                                     struct file *file)
>> > @@ -2165,6 +2170,12 @@ static int
>> > selinux_binder_transfer_file(struct
>> > task_struct *from,
>> >                     return rc;
>> >     }
>> >
>> > +#ifdef CONFIG_BPF_SYSCALL
>> > +   rc = bpf_fd_pass(file, sid);
>> > +   if (rc)
>> > +           return rc;
>> > +#endif
>> > +
>> >     if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
>> >             return 0;
>> >
>> > @@ -3735,8 +3746,18 @@ static int
>> > selinux_file_send_sigiotask(struct
>> > task_struct *tsk,
>> >  static int selinux_file_receive(struct file *file)
>> >  {
>> >     const struct cred *cred = current_cred();
>> > +   int rc;
>> > +
>> > +   rc = file_has_perm(cred, file, file_to_av(file));
>> > +   if (rc)
>> > +           goto out;
>> > +
>> > +#ifdef CONFIG_BPF_SYSCALL
>> > +   rc = bpf_fd_pass(file, cred_sid(sid));
>> > +#endif
>> >
>> > -   return file_has_perm(cred, file, file_to_av(file));
>> > +out:
>> > +   return rc;
>> >  }
>> >
>> >  static int selinux_file_open(struct file *file, const struct cred
>> > *cred)
>> > @@ -6288,6 +6309,40 @@ 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_fd_pass(struct file *file, u32 sid)
>> > +{
>> > +   struct bpf_security_struct *bpfsec;
>> > +   u32 sid = cred_sid(cred);
>> > +   struct bpf_prog *prog;
>> > +   struct bpf_map *map;
>> > +   int ret;
>> > +
>> > +   if (file->f_op == &bpf_map_fops) {
>> > +           map = file->private_data;
>> > +           bpfsec = map->security;
>> > +           ret = avc_has_perm(sid, bpfsec->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) {
>> > +           prog = file->private_data;
>> > +           bpfsec = prog->aux->security;
>> > +           ret = avc_has_perm(sid, bpfsec->sid,
>> > SECCLASS_BPF_PROG,
>> > +                              BPF_PROG__USE, NULL);
>> > +           if (ret)
>> > +                   return ret;
>> > +   }
>> > +   return 0;
>> > +}
>>
>> When the struct file is allocated for the bpf map and/or prog, you
>> could call a hook at that time passing both, and note the fact that
>> it
>> is a bpf map/prog in the file_security_struct.  Then, on
>> file_receive/binder_transfer_file, you could apply the appropriate
>> checking.  Further, if we know that the file is always allocated at
>> the
>> same point as the bpf map/prog, then they should have the same SID
>> (i.e
>> fsec->sid should be the same as bpfsec->sid), so we shouldn't even
>> need
>> to dereference the bpf map/prog.  Unless I'm missing something.
>>
>> Also, are we concerned about doing the same in
>> flush_unauthorized_files(), for inheriting descriptors across a
>> context-changing execve?  Should this checking actually go into
>> file_has_perm() itself so it is always applied on any use of the
>> struct
>> file?
>>
>> Lastly, do we need/want these checks if sid == bpfsec->sid?  We skip
>> FD__USE in the case where sid == fsec->sid, for example.
>
> BTW, the prog use check seems slightly redundant in that we will
> already check fd use permission.  So we only really need it if you want
> to allow fd use but deny prog use.  The map read/write checks are more
> granular than fd use, so I guess we can't get rid of those.
>
But it seems fd use doesn't check what object is behind that fd. So if
we don't want a process to run the eBPF program, then we still need a
additional check for it. Maybe use prog_run instead of prog_use should
be more appropriate.
>>
>> > +
>> >  static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
>> >  {
>> >     u32 sid = current_sid();
--
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



More information about the Linux-security-module-archive mailing list