Message ID | 20240402141145.2685631-1-roberto.sassu@huaweicloud.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [GIT,PULL] security changes for v6.9-rc3 | expand |
On Tue, 2 Apr 2024 at 07:12, Roberto Sassu <roberto.sassu@huaweicloud.com> wrote: > A single bug fix to address a kernel panic in the newly introduced function > security_path_post_mknod. So I've pulled from you before, but I still don't have a signature chain for your key (not that I can even find the key itself, much less a signature chain). Last time I pulled, it was after having everybody else just verify the actual commit. This time, the commit looks like a valid "avoid NULL", but I have to say that I also think the security layer code in question is ENTIRELY WRONG. IOW, as far as I can tell, the mknod() system call may indeed leave the dentry unhashed, and rely on anybody who then wants to use the new special file to just do a "lookup()" to actually use it. HOWEVER. That also means that the whole notion of "post_path_mknod() is complete and utter hoghwash. There is not anything that the security layer can possibly validly do. End result: instead of checking the 'inode' for NULL, I think the right fix is to remove that meaningless security hook. It cannot do anything sane, since one option is always 'the inode hasn't been initialized yet". Put another way: any security hook that checks inode in security_path_post_mknod() seems simply buggy. But if we really want to do this ("if mknod creates a positive dentry, I won't see it in lookup, so I want to appraise it now"), then we should just deal with this in the generic layer with some hack like this: --- a/security/security.c +++ b/security/security.c @@ -1801,7 +1801,8 @@ EXPORT_SYMBOL(security_path_mknod); */ void security_path_post_mknod(struct mnt_idmap *idmap, struct dentry *dentry) { - if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) + struct inode *inode = d_backing_inode(dentry); + if (unlikely(!inode || IS_PRIVATE(inode))) return; call_void_hook(path_post_mknod, idmap, dentry); } and IMA and EVM would have to do any validation at lookup() time for the cases where the dentry wasn't hashed by ->mknod. Anyway, all of this is to say that I don't feel like I can pull this without (a) more acks by people and (b) explanations for why the simpler fix to just security_path_post_mknod() isn't the right fix. Linus
On Tue, 2 Apr 2024 at 12:39, Linus Torvalds <torvalds@linux-foundation.org> wrote: > > void security_path_post_mknod(struct mnt_idmap *idmap, struct dentry *dentry) > { > - if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) > + struct inode *inode = d_backing_inode(dentry); > + if (unlikely(!inode || IS_PRIVATE(inode))) > return; > call_void_hook(path_post_mknod, idmap, dentry); Hmm. We do have other hooks that get called for this case. For fsnotify_create() we actually have a comment about this: * fsnotify_create - 'name' was linked in * * Caller must make sure that dentry->d_name is stable. * Note: some filesystems (e.g. kernfs) leave @dentry negative and instantiate * ->d_inode later and audit_inode_child() ends up having a if (inode) handle_one(inode); in it. So in other cases we do handle the NULL, but it does seem like the other cases actually do validaly want to deal with this (ie the fsnotify case will say "the directory that mknod was done in was changed" even if it doesn't know what the change is. But for the security case, it really doesn't seem to make much sense to check a mknod() that you don't know the result of. I do wonder if that "!inode" test might also be more specific with "d_unhashed(dentry)". But that would only make sense if we moved this test from security_path_post_mknod() into the caller itself, ie we could possibly do something like this instead (or in addition to): - if (error) - goto out2; - security_path_post_mknod(idmap, dentry); + if (!error && !d_unhashed(dentry)) + security_path_post_mknod(idmap, dentry); which might also be sensible. Al? Anybody? Linus
On Tue, Apr 2, 2024 at 3:39 PM Linus Torvalds <torvalds@linux-foundation.org> wrote: > ... > But if we really want to do this ("if mknod creates a positive dentry, > I won't see it in lookup, so I want to appraise it now"), then we > should just deal with this in the generic layer with some hack like > this: > > --- a/security/security.c > +++ b/security/security.c > @@ -1801,7 +1801,8 @@ EXPORT_SYMBOL(security_path_mknod); > */ > void security_path_post_mknod(struct mnt_idmap *idmap, struct dentry *dentry) > { > - if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) > + struct inode *inode = d_backing_inode(dentry); > + if (unlikely(!inode || IS_PRIVATE(inode))) > return; > call_void_hook(path_post_mknod, idmap, dentry); > } Other than your snippet wrapping both the inode/NULL and inode/IS_PRIVATE checks with an unlikely(), that's what Roberto submitted (his patch only wrapped the inode/IS_PRIVATE with unlikely).
On Tue, Apr 2, 2024 at 4:27 PM Paul Moore <paul@paul-moore.com> wrote: > On Tue, Apr 2, 2024 at 3:39 PM Linus Torvalds > <torvalds@linux-foundation.org> wrote: > > > > ... > > > But if we really want to do this ("if mknod creates a positive dentry, > > I won't see it in lookup, so I want to appraise it now"), then we > > should just deal with this in the generic layer with some hack like > > this: > > > > --- a/security/security.c > > +++ b/security/security.c > > @@ -1801,7 +1801,8 @@ EXPORT_SYMBOL(security_path_mknod); > > */ > > void security_path_post_mknod(struct mnt_idmap *idmap, struct dentry *dentry) > > { > > - if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) > > + struct inode *inode = d_backing_inode(dentry); > > + if (unlikely(!inode || IS_PRIVATE(inode))) > > return; > > call_void_hook(path_post_mknod, idmap, dentry); > > } > > Other than your snippet wrapping both the inode/NULL and > inode/IS_PRIVATE checks with an unlikely(), that's what Roberto > submitted (his patch only wrapped the inode/IS_PRIVATE with unlikely). Nevermind, I missed the obvious OR / AND diff ... sorry for the noise.
On Tue, Apr 02, 2024 at 12:57:28PM -0700, Linus Torvalds wrote: > So in other cases we do handle the NULL, but it does seem like the > other cases actually do validaly want to deal with this (ie the > fsnotify case will say "the directory that mknod was done in was > changed" even if it doesn't know what the change is. > > But for the security case, it really doesn't seem to make much sense > to check a mknod() that you don't know the result of. > > I do wonder if that "!inode" test might also be more specific with > "d_unhashed(dentry)". But that would only make sense if we moved this > test from security_path_post_mknod() into the caller itself, ie we > could possibly do something like this instead (or in addition to): > > - if (error) > - goto out2; > - security_path_post_mknod(idmap, dentry); > + if (!error && !d_unhashed(dentry)) > + security_path_post_mknod(idmap, dentry); > > which might also be sensible. > > Al? Anybody? Several things here: 1) location of that hook is wrong. It's really "how do we catch file creation that does not come through open() - yes, you can use mknod(2) for that". It should've been after the call of vfs_create(), not the entire switch. LSM folks have a disturbing fondness of inserting hooks in various places, but IMO this one has no business being where they'd placed it. Bikeshedding regarding the name/arguments/etc. for that thing is, IMO, not interesting... 2) the only ->mknod() instance in the tree that tries to leave dentry unhashed negative on success is CIFS (and only one case in it). From conversation with CIFS folks it's actually cheaper to instantiate in that case as well - leaving instantiation to the next lookup will cost several extra roundtrips for no good reason. 3) documentation (in vfs.rst) is way too vague. The actual rules are * ->create() must instantiate on success * ->mkdir() is allowed to return unhashed negative on success and it might be forced to do so in some cases. If a caller of vfs_mkdir() wants the damn thing positive, it should account for such possibility and do a lookup. Normal callers don't care; see e.g. nfsd and overlayfs for example of those that do. * ->mknod() is interesting - historically it had been "may leave unhashed negative", but e.g. unix_bind() expected that it won't do so; the reason it didn't blow up for CIFS is that this case (SFU) of their mknod() does not support FIFOs and sockets anyway. Considering how few instances try to make use of that option and how it doesn't actually save them anything, I would prefer to declare that ->mknod() should act as ->create(). * ->symlink() - not sure; there are instances that make use of that option (coda and hostfs). OTOH, the only callers of vfs_symlink() that care either way are nfsd and overlayfs, and neither is usable with coda or hostfs... Could go either way, but we need to say it clearly in the docs, whichever way we choose.
On Tue, 2 Apr 2024 at 14:00, Al Viro <viro@zeniv.linux.org.uk> wrote: > > 1) location of that hook is wrong. It's really "how do we catch > file creation that does not come through open() - yes, you can use > mknod(2) for that". It should've been after the call of vfs_create(), > not the entire switch. LSM folks have a disturbing fondness of inserting > hooks in various places, but IMO this one has no business being where > they'd placed it. Bikeshedding regarding the name/arguments/etc. for > that thing is, IMO, not interesting... Hmm. I guess that's right - for a non-file node, there's nothing that the security layer can really check after-the-fact anyway. It's not like you can attest the contents of a character device or whatever... > 2) the only ->mknod() instance in the tree that tries to leave > dentry unhashed negative on success is CIFS (and only one case in it). > From conversation with CIFS folks it's actually cheaper to instantiate > in that case as well - leaving instantiation to the next lookup will > cost several extra roundtrips for no good reason. Ack. > 3) documentation (in vfs.rst) is way too vague. The actual > rules are > * ->create() must instantiate on success > * ->mkdir() is allowed to return unhashed negative on success and > it might be forced to do so in some cases. If a caller of vfs_mkdir() > wants the damn thing positive, it should account for such possibility and do > a lookup. Normal callers don't care; see e.g. nfsd and overlayfs for example > of those that do. > * ->mknod() is interesting - historically it had been "may leave > unhashed negative", but e.g. unix_bind() expected that it won't do so; > the reason it didn't blow up for CIFS is that this case (SFU) of their mknod() > does not support FIFOs and sockets anyway. Considering how few instances > try to make use of that option and how it doesn't actually save them > anything, I would prefer to declare that ->mknod() should act as ->create(). > * ->symlink() - not sure; there are instances that make use of that > option (coda and hostfs). OTOH, the only callers of vfs_symlink() that > care either way are nfsd and overlayfs, and neither is usable with coda > or hostfs... Could go either way, but we need to say it clearly in the > docs, whichever way we choose. Fair enough. Anyway, it does sound like maybe the minimal fix would be just that "move it into the case 0: case S_IFREG: path". Although if somebody already has the cifs patch to just do the d_instantiate() for mknod, that might be even better. I will leave this in more competent hands for now. Let the bike-shedding commence, Linus
On Tue, Apr 2, 2024 at 5:00 PM Al Viro <viro@zeniv.linux.org.uk> wrote: > On Tue, Apr 02, 2024 at 12:57:28PM -0700, Linus Torvalds wrote: > > > So in other cases we do handle the NULL, but it does seem like the > > other cases actually do validaly want to deal with this (ie the > > fsnotify case will say "the directory that mknod was done in was > > changed" even if it doesn't know what the change is. > > > > But for the security case, it really doesn't seem to make much sense > > to check a mknod() that you don't know the result of. > > > > I do wonder if that "!inode" test might also be more specific with > > "d_unhashed(dentry)". But that would only make sense if we moved this > > test from security_path_post_mknod() into the caller itself, ie we > > could possibly do something like this instead (or in addition to): > > > > - if (error) > > - goto out2; > > - security_path_post_mknod(idmap, dentry); > > + if (!error && !d_unhashed(dentry)) > > + security_path_post_mknod(idmap, dentry); > > > > which might also be sensible. > > > > Al? Anybody? > > Several things here: > > 1) location of that hook is wrong. It's really "how do we catch > file creation that does not come through open() - yes, you can use > mknod(2) for that". It should've been after the call of vfs_create(), > not the entire switch. LSM folks have a disturbing fondness of inserting > hooks in various places, but IMO this one has no business being where > they'd placed it. I know it's everyone's favorite hobby to bash the LSM and LSM devs, but it's important to note that we don't add hooks without working with the associated subsystem devs to get approval. In the cases where we don't get an explicit ACK, there is an on-list approval, or several ignored on-list attempts over weeks/months/years. We want to be good neighbors. Roberto's original patch which converted from the IMA/EVM hook to the LSM hook was ACK'd by the VFS folks. Regardless, Roberto if it isn't obvious by now, just move the hook back to where it was prior to v6.9-rc1.
On Tue, Apr 02, 2024 at 05:36:30PM -0400, Paul Moore wrote: > > 1) location of that hook is wrong. It's really "how do we catch > > file creation that does not come through open() - yes, you can use > > mknod(2) for that". It should've been after the call of vfs_create(), > > not the entire switch. LSM folks have a disturbing fondness of inserting > > hooks in various places, but IMO this one has no business being where > > they'd placed it. > > I know it's everyone's favorite hobby to bash the LSM and LSM devs, > but it's important to note that we don't add hooks without working > with the associated subsystem devs to get approval. In the cases > where we don't get an explicit ACK, there is an on-list approval, or > several ignored on-list attempts over weeks/months/years. We want to > be good neighbors. > > Roberto's original patch which converted from the IMA/EVM hook to the > LSM hook was ACK'd by the VFS folks. > > Regardless, Roberto if it isn't obvious by now, just move the hook > back to where it was prior to v6.9-rc1. The root cause is in the too vague documentation - it's very easy to misread as "->mknod() must call d_instantiate()", so the authors of that patchset and reviewers of the same had missed the subtlety involved. No arguments about that. Unkind comments about the LSM folks' tendency to shove hooks in places where they make no sense had been brought by many things, the most recent instance being this: However, I thought, since we were promoting it as an LSM hook, we should be as generic possible, and support more usages than what was needed for IMA. (https://lore.kernel.org/all/3441a4a1140944f5b418b70f557bca72@huawei.com/) I'm not blaming Roberto - that really seems to be the general attitude around LSM; I've seen a _lot_ of "it doesn't matter if it makes any sense, somebody might figure out some use for the data we have at that point in control flow, eventually if not now" kind of responses over the years. IME asking what this or that hook is for and what it expects from the objects passed to it gets treated as invalid question. Which invites treating hooks as black boxes...
On Tue, Apr 2, 2024 at 6:42 PM Al Viro <viro@zeniv.linux.org.uk> wrote: > On Tue, Apr 02, 2024 at 05:36:30PM -0400, Paul Moore wrote: > > > > 1) location of that hook is wrong. It's really "how do we catch > > > file creation that does not come through open() - yes, you can use > > > mknod(2) for that". It should've been after the call of vfs_create(), > > > not the entire switch. LSM folks have a disturbing fondness of inserting > > > hooks in various places, but IMO this one has no business being where > > > they'd placed it. > > > > I know it's everyone's favorite hobby to bash the LSM and LSM devs, > > but it's important to note that we don't add hooks without working > > with the associated subsystem devs to get approval. In the cases > > where we don't get an explicit ACK, there is an on-list approval, or > > several ignored on-list attempts over weeks/months/years. We want to > > be good neighbors. > > > > Roberto's original patch which converted from the IMA/EVM hook to the > > LSM hook was ACK'd by the VFS folks. > > > > Regardless, Roberto if it isn't obvious by now, just move the hook > > back to where it was prior to v6.9-rc1. > > The root cause is in the too vague documentation - it's very easy to > misread as "->mknod() must call d_instantiate()", so the authors of > that patchset and reviewers of the same had missed the subtlety > involved. No arguments about that. > > Unkind comments about the LSM folks' tendency to shove hooks in > places where they make no sense had been brought by many things, > the most recent instance being this: > However, I thought, since we were promoting it as an LSM hook, > we should be as generic possible, and support more usages than > what was needed for IMA. > (https://lore.kernel.org/all/3441a4a1140944f5b418b70f557bca72@huawei.com/) > > I'm not blaming Roberto - that really seems to be the general attitude > around LSM; I've seen a _lot_ of "it doesn't matter if it makes any sense, > somebody might figure out some use for the data we have at that point in > control flow, eventually if not now" kind of responses over the years. > IME asking what this or that hook is for and what it expects from the objects > passed to it gets treated as invalid question. It's rather common for subsystems to push back on the number LSM hooks, which ends up resulting in patterns where LSM hooks are placed in as wide a scope as possible both to satisfy the requirements of the individual subsystems as well as the LSM's requirements on coverage. Clearly documenting hooks, their inputs, return values, constraints, etc. is important and we need to have those discussions as part of the hook. This is a big part of why we CC the subsystems when adding new hooks and why I make sure we get an ACK or some other approval for a subsystem maintainer before we merge a new hook. Is the system perfect, no, clearly not, but I don't believe it is for a lack of trying or any ill intent on the part of the LSM devs. We recently restored the LSM hook comment blocks in security/security.c (long story), I would gladly welcome any comments/edits/suggestions you, or anyone else may have, about the docs there - I will be the first to admit those docs have rotted quite a bit (once again, long story). If you have corrections, notes, or constraints that should be added please let me know and/or send patches. Similarly, if you're aware of any hooks which are ill advised and/or poorly placed, let us know so we can work together to fix things. I'm serious Al. These aren't just words in an email. I realize you don't have a lot of free cycles, but if you do have feedback on any of those things above, I'm listening. I *really* want to see better collaboration between various subsystems and the LSMs; that's part of why I get annoyed with LSM bashing, leaving the LSM devs out of security/LSM related threads, etc. it only helps keep the divide up between the groups which is bad for all of us.
Paul Moore <paul@paul-moore.com> writes: > I know it's everyone's favorite hobby to bash the LSM and LSM devs, > but it's important to note that we don't add hooks without working > with the associated subsystem devs to get approval. Hah!!!! > In the cases > where we don't get an explicit ACK, there is an on-list approval, or > several ignored on-list attempts over weeks/months/years. We want to > be good neighbors. Hah!!!! You merged a LSM hook that is only good for breaking chrome's sandbox, over my expressed objections. After I tested and verified that is what it does. I asked for testing. None was done. It was claimed that no security sensitive code would ever fail to check and deal with all return codes, so no testing was necessary. Then later a whole bunch of security sensitive code that didn't was found. The only redeeming grace has been that no-one ever actually uses that misbegotten security hook. P.S. Sorry for this off topic rant but sheesh. At least from my perspective you deserve plenty of bashing. Eric
On Tue, Apr 9, 2024 at 1:38 PM Eric W. Biederman <ebiederm@xmission.com> wrote: > Paul Moore <paul@paul-moore.com> writes: > > > I know it's everyone's favorite hobby to bash the LSM and LSM devs, > > but it's important to note that we don't add hooks without working > > with the associated subsystem devs to get approval. > > Hah!!!! > > > In the cases > > where we don't get an explicit ACK, there is an on-list approval, or > > several ignored on-list attempts over weeks/months/years. We want to > > be good neighbors. > > Hah!!!! > > You merged a LSM hook that is only good for breaking chrome's sandbox, > over my expressed objections. After I tested and verified that > is what it does. > > I asked for testing. None was done. It was claimed that no > security sensitive code would ever fail to check and deal with > all return codes, so no testing was necessary. Then later a > whole bunch of security sensitive code that didn't was found. > > The only redeeming grace has been that no-one ever actually uses > that misbegotten security hook. > > P.S. Sorry for this off topic rant but sheesh. At least from > my perspective you deserve plenty of bashing. Just in case people are reading this email and don't recall the security_create_user_ns() hook discussions from 2022, I would suggest reading those old threads and drawing your own conclusions. A lore link is below: https://lore.kernel.org/linux-security-module/?q=s%3Asecurity_create_user_ns
From: Roberto Sassu <roberto.sassu@huawei.com> Hi Linus, A single bug fix to address a kernel panic in the newly introduced function security_path_post_mknod. PS: sorry for the email mismatch, @huawei.com emails resent from the mailing list are classified by Gmail as spam, we are working on fixing it. Thanks, Roberto The following changes since commit 39cd87c4eb2b893354f3b850f916353f2658ae6f: Linux 6.9-rc2 (2024-03-31 14:32:39 -0700) are available in the Git repository at: https://github.com/linux-integrity/linux.git tags/security-mknod-6.9-rc3 for you to fetch changes up to 991c999d8dc76261623c44f9076e427045053427: security: Handle dentries without inode in security_path_post_mknod() (2024-04-02 15:27:46 +0200) ---------------------------------------------------------------- security-mknod-6.9-rc3 Fixes a kernel panic in the newly introduced function security_path_post_mknod(). (Not all dentries have an inode attached to them.) ---------------------------------------------------------------- Roberto Sassu (1): security: Handle dentries without inode in security_path_post_mknod() security/integrity/evm/evm_main.c | 6 ++++-- security/integrity/ima/ima_main.c | 5 +++-- security/security.c | 5 ++++- 3 files changed, 11 insertions(+), 5 deletions(-)