Hi Andy,

Thanks for these detailed analysis. 

> On Jun 30, 2019, at 8:12 AM, Andy Lutomirski <l...@kernel.org> wrote:
> 
> On Fri, Jun 28, 2019 at 12:05 PM Song Liu <songliubrav...@fb.com> wrote:
>> 
>> Hi Andy,
>> 
>>> On Jun 27, 2019, at 4:40 PM, Andy Lutomirski <l...@kernel.org> wrote:
>>> 
>>> On 6/27/19 1:19 PM, Song Liu wrote:
>>>> This patch introduce unprivileged BPF access. The access control is
>>>> achieved via device /dev/bpf. Users with write access to /dev/bpf are able
>>>> to call sys_bpf().
>>>> Two ioctl command are added to /dev/bpf:
>>>> The two commands enable/disable permission to call sys_bpf() for current
>>>> task. This permission is noted by bpf_permitted in task_struct. This
>>>> permission is inherited during clone(CLONE_THREAD).
>>>> Helper function bpf_capable() is added to check whether the task has got
>>>> permission via /dev/bpf.
>>> 
>>>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
>>>> index 0e079b2298f8..79dc4d641cf3 100644
>>>> --- a/kernel/bpf/verifier.c
>>>> +++ b/kernel/bpf/verifier.c
>>>> @@ -9134,7 +9134,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr 
>>>> *attr,
>>>>             env->insn_aux_data[i].orig_idx = i;
>>>>     env->prog = *prog;
>>>>     env->ops = bpf_verifier_ops[env->prog->type];
>>>> -    is_priv = capable(CAP_SYS_ADMIN);
>>>> +    is_priv = bpf_capable(CAP_SYS_ADMIN);
>>> 
>>> Huh?  This isn't a hardening measure -- the "is_priv" verifier mode allows 
>>> straight-up leaks of private kernel state to user mode.
>>> 
>>> (For that matter, the pending lockdown stuff should possibly consider this 
>>> a "confidentiality" issue.)
>>> 
>>> 
>>> I have a bigger issue with this patch, though: it's a really awkward way to 
>>> pretend to have capabilities. For bpf, it seems like you could make this be 
>>> a *real* capability without too much pain since there's only one syscall 
>>> there.  Just find a way to pass an fd to /dev/bpf into the syscall.  If 
>>> this means you need a new bpf_with_cap() syscall that takes an extra 
>>> argument, so be it.  The old bpf() syscall can just translate to 
>>> bpf_with_cap(..., -1).
>>> 
>>> For a while, I've considered a scheme I call "implicit rights".  There 
>>> would be a directory in /dev called /dev/implicit_rights.  This would 
>>> either be part of devtmpfs or a whole new filesystem -- it would *not* be 
>>> any other filesystem.  The contents would be files that can't be read or 
>>> written and exist only in memory. You create them with a privileged 
>>> syscall.  Certain actions that are sensitive but not at the level of 
>>> CAP_SYS_ADMIN (use of large-attack-surface bpf stuff, creation of user 
>>> namespaces, profiling the kernel, etc) could require an "implicit right".  
>>> When you do them, if you don't have CAP_SYS_ADMIN, the kernel would do a 
>>> path walk for, say, /dev/implicit_rights/bpf and, if the object exists, can 
>>> be opened, and actually refers to the "bpf" rights object, then the action 
>>> is allowed.  Otherwise it's denied.
>>> 
>>> This is extensible, and it doesn't require the rather ugly per-task state 
>>> of whether it's enabled.
>>> 
>>> For things like creation of user namespaces, there's an existing API, and 
>>> the default is that it works without privilege.  Switching it to an 
>>> implicit right has the benefit of not requiring code changes to programs 
>>> that already work as non-root.
>>> 
>>> But, for BPF in particular, this type of compatibility issue doesn't exist 
>>> now.  You already can't use most eBPF functionality without privilege.  New 
>>> bpf-using programs meant to run without privilege are *new*, so they can 
>>> use a new improved API.  So, rather than adding this obnoxious ioctl, just 
>>> make the API explicit, please.
>>> 
>>> Also, please cc: linux-abi next time.
>> 
>> Thanks for your inputs.
>> 
>> I think we need to clarify the use case here. In this case, we are NOT
>> thinking about creating new tools for unprivileged users. Instead, we
>> would like to use existing tools without root.
> 
> I read patch 4, and I interpret it very differently.  Patches 2-4 are
> creating a new version of libbpf and a new version of bpftool.  Given
> this, I see no real justification for adding a new in-kernel per-task
> state instead of just pushing the complexity into libbpf.

I am not sure whether we are on the same page. Let me try an example, 
say we have application A, which calls sys_bpf(). 

Before the series: we have to run A with root; 
After the series:  we add a special user with access to /dev/bpf, and 
                   run A with this special user. 

If we look at the whole system, I would say we are more secure after 
the series. 

I am not trying to make an extreme example here, because this use case
is the motivation here. 

To stay safe, we have to properly manage the permission of /dev/bpf. 
This is just like we need to properly manage access to /etc/sudoers and 
/dev/mem. 

Does this make sense? 

Thanks,
Song

Reply via email to