Re: [PATCH bpf-next 03/29] bpf: introduce BPF token object

Linus Torvalds
2024-01-05T21:45:54Z

Ok, I've gone through the whole series now, and I don't find anything
objectionable.

Which may only mean that I didn't notice something, of course, but at
least there's nothing I'd consider obvious.

I keep coming back to this 03/29 patch, because it's kind of the heart
of it, and I have one more small nit, but it's also purely stylistic:

On Wed, 3 Jan 2024 at 14:21, Andrii Nakryiko <andrii@kernel.org> wrote:
>
> +bool bpf_token_capable(const struct bpf_token *token, int cap)
> +{
> + /* BPF token allows ns_capable() level of capabilities, but only if
> + * token's userns is *exactly* the same as current user's userns
> + */
> + if (token && current_user_ns() == token->userns) {
> + if (ns_capable(token->userns, cap))
> + return true;
> + if (cap != CAP_SYS_ADMIN && ns_capable(token->userns, CAP_SYS_ADMIN))
> + return true;
> + }
> + /* otherwise fallback to capable() checks */
> + return capable(cap) || (cap != CAP_SYS_ADMIN && capable(CAP_SYS_ADMIN));
> +}

This *feels* like it should be written as

bool bpf_token_capable(const struct bpf_token *token, int cap)
{
struct user_namespace *ns = &init_ns;

/* BPF token allows ns_capable() level of capabilities, but only if
* token's userns is *exactly* the same as current user's userns
*/
if (token && current_user_ns() == token->userns)
ns = token->userns;
return ns_capable(ns, cap) ||
(cap != CAP_SYS_ADMIN && capable(CAP_SYS_ADMIN));
}

And yes, I realize that the function will end up later growing a

security_bpf_token_capable(token, cap)

test inside that 'if (token ..)' statement, and this would change the
order of that test so that the LSM hook would now be done before the
capability checks are done, but that all still seems just more of an
argument for the simplification.

So the end result would be something like

bool bpf_token_capable(const struct bpf_token *token, int cap)
{
struct user_namespace *ns = &init_ns;

if (token && current_user_ns() == token->userns) {
if (security_bpf_token_capable(token, cap) < 0)
return false;
ns = token->userns;
}
return ns_capable(ns, cap) ||
(cap != CAP_SYS_ADMIN && capable(CAP_SYS_ADMIN));
}

although I feel that with that LSM hook, maybe this all should return
the error code (zero or negative), not a bool for success?

Also, should "current_user_ns() != token->userns" perhaps be an error
condition, rather than a "fall back to init_ns" condition?

Again, none of this is a big deal. I do think you're dropping the LSM
error code on the floor, and are duplicating the "ns_capable()" vs
"capable()" logic as-is, but none of this is a deal breaker, just more
of my commentary on the patch and about the logic here.

And yeah, I don't exactly love how you say "ok, if there's a token and
it doesn't match, I'll not use it" rather than "if the token namespace
doesn't match, it's an error", but maybe there's some usability issue
here?

Linus