Re: [PATCH 1/2] fs: add loopback/bind mount specific security hook

Shervin Oloumi
2025-01-10T04:11:46Z

Thanks for the feedback Paul, the latest patch should include the
recommendations now.

On Thu, Jan 2, 2025 at 9:11 PM Paul Moore <paul@paul-moore.com> wrote:
>
> On Mon, Dec 30, 2024 at 8:46 PM Shervin Oloumi <enlightened@chromium.org> wrote:
> >
> > The main mount security hook (security_sb_mount) is called early in the
> > process before the mount type is determined and the arguments are
> > validated and converted to the appropriate format. Specifically, the
> > source path is surfaced as a string, which is not appropriate for
> > checking bind mount requests. For bind mounts the source should be
> > validated and passed as a path struct (same as destination), after the
> > mount type is determined. This allows the hook users to evaluate the
> > mount attributes without the need to perform any validations or
> > conversions out of band, which can introduce a TOCTOU race condition.
> >
> > The newly introduced hook is invoked only if the security_sb_mount hook
> > passes, and only if the MS_BIND flag is detected. At this point the
> > source of the mount has been successfully converted to a path struct
> > using the kernel's kern_path API. This allows LSMs to target bind mount
> > requests at the right stage, and evaluate the attributes in the right
> > format, based on the type of mount.
> >
> > This does not affect the functionality of the existing mount security
> > hooks, including security_sb_mount. The new hook, can be utilized as a
> > supplement to the main hook for further analyzing bind mount requests.
> > This means that there is still the option of only using the main hook
> > function, if all one wants to do is indiscriminately reject all bind
> > mount requests, regardless of the source and destination arguments.
> > However, if one needs to evaluate the source and destination of a bind
> > mount request before making a decision, this hook function should be
> > preferred. Of course, if a bind mount request does not make it past the
> > security_sb_mount check, the bind mount hook function is never invoked.
> >
> > Signed-off-by: Shervin Oloumi <enlightened@chromium.org>
> > ---
> > fs/namespace.c | 4 ++++
> > include/linux/lsm_hook_defs.h | 1 +
> > include/linux/security.h | 1 +
> > security/security.c | 16 ++++++++++++++++
> > 4 files changed, 22 insertions(+)
>
> Like Casey I'm not really excited about such a specific LSM hook, but
> unfortunately we can't simply call kern_path() in the existing
> security_sb_mount() callback as that could end up resolving to
> something different than the call in do_loopback() which would be bad.
> Moving the kern_path() call up into path_mount() just looks like a bad
> idea anyway you look at it. Unfortunately I don't really see an
> alternative to what you're proposing, so I guess we're kinda stuck
> with this as a solution, unless someone can think of something better.
>
> I'm going to need to see an ACK from the VFS folks on this before I
> merge the new hook.
>
> I'd also stick with the security_sb_bindmount() name as opposed to the
> XXX_path() suggestion from Casey simply to help distinguish it from
> the pathname based LSM hooks. Yes, this is operating on the pathname,
> but bind mounts are a bit of a special case.
>
> Unrelated, but I just noticed that we are calling security_sb_mount()
> *before* may_mount(); that's the opposite order for most LSM hook
> placements where we do the discretionary/capabilities checks first and
> the LSM checks. That's something we should look at, perhaps there is
> a good reason for the ordering being different, perhaps it's a
> mistake.
>
> > diff --git a/fs/namespace.c b/fs/namespace.c
> > index 23e81c2a1e3f..c902608c9759 100644
> > --- a/fs/namespace.c
> > +++ b/fs/namespace.c
> > @@ -2765,6 +2765,10 @@ static int do_loopback(struct path *path, const char *old_name,
> > if (err)
> > return err;
> >
> > + err = security_sb_bindmount(&old_path, path);
> > + if (err)
> > + goto out;
>
> I might make a mention in the commit description that the
> do_reconfigure_mnt() case (MS_REMOUNT|MS_BIND) should be able to be
> handled using the existing security_sb_mount() hook.
>
> > err = -EINVAL;
> > if (mnt_ns_loop(old_path.dentry))
> > goto out;
>
> ...
>
> > diff --git a/security/security.c b/security/security.c
> > index 09664e09fec9..bd7cb3df16f4 100644
> > --- a/security/security.c
> > +++ b/security/security.c
> > @@ -1564,6 +1564,22 @@ int security_sb_mount(const char *dev_name, const struct path *path,
> > return call_int_hook(sb_mount, dev_name, path, type, flags, data);
> > }
> >
> > +/**
> > + * security_sb_bindmount() - Loopback/bind mount specific permission check
> > + * @old_path: source of loopback/bind mount
> > + * @path: mount point
> > + *
> > + * This check is performed in addition to security_sb_mount and only if the
> > + * mount type is determined to be loopback/bind mount (flags & MS_BIND). It
> > + * surfaces the mount source as a path struct.
>
> I wouldn't mention security_sb_mount() above as that makes the comment
> somewhat fragile in the face of changing hooks. I would suggest
> something along these lines:
>
> "Beyond any general mounting hooks, this check is performed on an
> initial loopback/bind mount (MS_BIND) with the mount source presented
> as a path struct in @old_path."
>
> > + * Return: Returns 0 if permission is granted.
> > + */
> > +int security_sb_bindmount(const struct path *old_path, const struct path *path)
> > +{
> > + return call_int_hook(sb_bindmount, old_path, path);
> > +}
> > +
> > /**
> > * security_sb_umount() - Check permission for unmounting a filesystem
> > * @mnt: mounted filesystem
> >
> > base-commit: fc033cf25e612e840e545f8d5ad2edd6ba613ed5
> > --
> > 2.47.1.613.gc27f4b7a9f-goog
>
> --
> paul-moore.com