From: Song Liu <songliubraving@meta.com>
To: Christian Brauner <brauner@kernel.org>
Cc: Song Liu <songliubraving@meta.com>, 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 07:18:40 +0000 [thread overview]
Message-ID: <8DFC3BD2-84DC-4A0C-A997-AA9F57771D92@fb.com> (raw)
In-Reply-To: <20240729-zollfrei-verteidigen-cf359eb36601@brauner>
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?
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).
To summary my thoughts here. I think we need:
1. Change security_inode_permission to take dentry instead of inode.
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.
Does this make sense?
Thanks,
Song
next prev parent reply other threads:[~2024-08-19 7:18 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 [this message]
2024-08-19 11:16 ` Christian Brauner
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=8DFC3BD2-84DC-4A0C-A997-AA9F57771D92@fb.com \
--to=songliubraving@meta.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=brauner@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=shankaran@meta.com \
--cc=song@kernel.org \
--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