From: Christian Brauner <brauner@kernel.org>
To: "Song Liu" <songliubraving@meta.com>, "Mickaël Salaün" <mic@digikod.net>
Cc: Song Liu <song@kernel.org>, bpf <bpf@vger.kernel.org>,
Linux-Fsdevel <linux-fsdevel@vger.kernel.org>,
LKML <linux-kernel@vger.kernel.org>,
Kernel Team <kernel-team@meta.com>,
"andrii@kernel.org" <andrii@kernel.org>,
"eddyz87@gmail.com" <eddyz87@gmail.com>,
"ast@kernel.org" <ast@kernel.org>,
"daniel@iogearbox.net" <daniel@iogearbox.net>,
"martin.lau@linux.dev" <martin.lau@linux.dev>,
"viro@zeniv.linux.org.uk" <viro@zeniv.linux.org.uk>,
"jack@suse.cz" <jack@suse.cz>,
"kpsingh@kernel.org" <kpsingh@kernel.org>,
"mattbobrowski@google.com" <mattbobrowski@google.com>,
Liam Wisehart <liamwisehart@meta.com>,
Liang Tang <lltang@meta.com>,
Shankaran Gnanashanmugam <shankaran@meta.com>,
LSM List <linux-security-module@vger.kernel.org>
Subject: Re: [PATCH bpf-next 2/2] selftests/bpf: Add tests for bpf_get_dentry_xattr
Date: Mon, 19 Aug 2024 13:16:04 +0200 [thread overview]
Message-ID: <20240819-keilen-urlaub-2875ef909760@brauner> (raw)
In-Reply-To: <8DFC3BD2-84DC-4A0C-A997-AA9F57771D92@fb.com>
On Mon, Aug 19, 2024 at 07:18:40AM GMT, Song Liu wrote:
> Hi Christian,
>
> Thanks again for your suggestions here. I have got more questions on
> this work.
>
> > On Jul 29, 2024, at 6:46 AM, Christian Brauner <brauner@kernel.org> wrote:
>
> [...]
>
> >> I am not sure I follow the suggestion to implement this with
> >> security_inode_permission()? Could you please share more details about
> >> this idea?
> >
> > Given a path like /bin/gcc-6.9/gcc what that code currently does is:
> >
> > * walk down to /bin/gcc-6.9/gcc
> > * walk up from /bin/gcc-6.9/gcc and then checking xattr labels for:
> > gcc
> > gcc-6.9/
> > bin/
> > /
> >
> > That's broken because someone could've done
> > mv /bin/gcc-6.9/gcc /attack/ and when this walks back and it checks xattrs on
> > /attack even though the path lookup was for /bin/gcc-6.9. IOW, the
> > security_file_open() checks have nothing to do with the permission checks that
> > were done during path lookup.
> >
> > Why isn't that logic:
> >
> > * walk down to /bin/gcc-6.9/gcc and check for each component:
> >
> > security_inode_permission(/)
> > security_inode_permission(gcc-6.9/)
> > security_inode_permission(bin/)
> > security_inode_permission(gcc)
> > security_file_open(gcc)
>
> I am trying to implement this approach. The idea, IIUC, is:
>
> 1. For each open/openat, as we walk the path in do_filp_open=>path_openat,
> check xattr for "/", "gcc-6.9/", "bin/" for all given flags.
> 2. Save the value of the flag somewhere (for BPF, we can use inode local
> storage). This is needed, because openat(dfd, ..) will not start from
> root again.
> 3. Propagate these flag to children. All the above are done at
> security_inode_permission.
> 4. Finally, at security_file_open, check the xattr with the file, which
> is probably propagated from some parents.
>
> Did I get this right?
>
> IIUC, there are a few issues with this approach.
>
> 1. security_inode_permission takes inode as parameter. However, we need
> dentry to get the xattr. Shall we change security_inode_permission
> to take dentry instead?
> PS: Maybe we should change most/all inode hooks to take dentry instead?
security_inode_permission() is called in generic_permission() which in
turn is called from inode_permission() which in turn is called from
inode->i_op->permission() for various filesystems. So to make
security_inode_permission() take a dentry argument one would need to
change all inode->i_op->permission() to take a dentry argument for all
filesystems. NAK on that.
That's ignoring that it's just plain wrong to pass a dentry to
**inode**_permission() or security_**inode**_permission(). It's
permissions on the inode, not the dentry.
>
> 2. There is no easy way to propagate data from parent. Assuming we already
> changed security_inode_permission to take dentry, we still need some
> mechanism to look up xattr from the parent, which is probably still
> something like bpf_dget_parent(). Or maybe we should add another hook
> with both parent and child dentry as input?
>
> 3. Given we save the flag from parents in children's inode local storage,
> we may consume non-trivial extra memory. BPF inode local storage will
> be freed as the inode gets freed, so we will not leak any memory or
> overflow some hash map. However, this will probably increase the
> memory consumption of inode by a few percents. I think a "walk-up"
> based approach will not have this problem, as we don't need the extra
> storage. Of course, this means more xattr lookups in some cases.
>
> >
> > I think that dget_parent() logic also wouldn't make sense for relative path
> > lookups:
> >
> > dfd = open("/bin/gcc-6.9", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
> >
> > This walks down to /bin/gcc-6.9 and then walks back up (subject to the
> > same problem mentioned earlier) and check xattrs for:
> >
> > gcc-6.9
> > bin/
> > /
> >
> > then that dfd is passed to openat() to open "gcc":
> >
> > fd = openat(dfd, "gcc", O_RDONLY);
> >
> > which again walks up to /bin/gcc-6.9 and checks xattrs for:
> > gcc
> > gcc-6.9
> > bin/
> > /
> >
> > Which means this code ends up charging relative lookups twice. Even if one
> > irons that out in the program this encourages really bad patterns.
> > Path lookup is iterative top down. One can't just randomly walk back up and
> > assume that's equivalent.
>
> I understand that walk-up is not equivalent to walk down. But it is probably
> accurate enough for some security policies. For example, LSM LandLock uses
> similar logic in the file_open hook (file security/landlock/fs.c, function
> is_access_to_paths_allowed).
I'm not well-versed in landlock so I'll let Mickaël comment on this with
more details but there's very important restrictions and differences
here.
Landlock expresses security policies with file hierarchies and
security_inode_permission() doesn't and cannot have access to that.
Landlock is subject to the same problem that the BPF is here. Namely
that the VFS permission checking could have been done on a path walk
completely different from the path walk that is checked when walking
back up from security_file_open().
But because landlock works with a deny-by-default security policy this
is ok and it takes overmounts into account etc.
>
> To summary my thoughts here. I think we need:
>
> 1. Change security_inode_permission to take dentry instead of inode.
Sorry, no.
> 2. Still add bpf_dget_parent. We will use it with security_inode_permission
> so that we can propagate flags from parents to children. We will need
> a bpf_dput as well.
> 3. There are pros and cons with different approaches to implement this
> policy (tags on directory work for all files in it). We probably need
> the policy writer to decide with one to use. From BPF's POV, dget_parent
> is "safe", because it won't crash the system. It may encourage some bad
> patterns, but it appears to be required in some use cases.
You cannot just walk a path upwards and check permissions and assume
that this is safe unless you have a clear idea what makes it safe in
this scenario. Landlock has afaict. But so far you only have a vague
sketch of checking permissions walking upwards and retrieving xattrs
without any notion of the problems involved.
If you provide a bpf_get_parent() api for userspace to consume you'll
end up providing them with an api that is extremly easy to misuse.
next prev parent reply other threads:[~2024-08-19 11:16 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-07-25 23:47 [PATCH bpf-next 0/2] Add kfuncs to support reading xattr from dentry Song Liu
2024-07-25 23:47 ` [PATCH bpf-next 1/2] bpf: Add kfunc bpf_get_dentry_xattr() to read " Song Liu
2024-07-26 5:34 ` Al Viro
2024-07-26 7:01 ` Song Liu
2024-07-25 23:47 ` [PATCH bpf-next 2/2] selftests/bpf: Add tests for bpf_get_dentry_xattr Song Liu
2024-07-26 7:06 ` Christian Brauner
2024-07-26 9:19 ` Song Liu
2024-07-26 11:51 ` Christian Brauner
2024-07-26 19:43 ` Song Liu
2024-07-29 13:46 ` Christian Brauner
2024-07-30 5:58 ` Song Liu
2024-07-30 8:59 ` Christian Brauner
2024-08-19 7:18 ` Song Liu
2024-08-19 11:16 ` Christian Brauner [this message]
2024-08-19 13:12 ` Mickaël Salaün
2024-08-19 20:35 ` Song Liu
2024-08-20 12:45 ` Mickaël Salaün
2024-08-20 17:42 ` Song Liu
2024-08-20 21:11 ` Paul Moore
2024-08-21 3:43 ` Song Liu
2024-08-23 10:38 ` Mickaël Salaün
2024-08-19 20:25 ` Song Liu
2024-08-20 5:42 ` Song Liu
2024-08-20 6:29 ` Al Viro
2024-08-20 7:23 ` Song Liu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240819-keilen-urlaub-2875ef909760@brauner \
--to=brauner@kernel.org \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=jack@suse.cz \
--cc=kernel-team@meta.com \
--cc=kpsingh@kernel.org \
--cc=liamwisehart@meta.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=lltang@meta.com \
--cc=martin.lau@linux.dev \
--cc=mattbobrowski@google.com \
--cc=mic@digikod.net \
--cc=shankaran@meta.com \
--cc=song@kernel.org \
--cc=songliubraving@meta.com \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®