diff mbox series

[v3,3/6] fuse: setattr should set FATTR_KILL_PRIV upon size change

Message ID 20201009181512.65496-4-vgoyal@redhat.com (mailing list archive)
State New, archived
Headers show
Series fuse: Implement FUSE_HANDLE_KILLPRIV_V2 and enable SB_NOSEC | expand

Commit Message

Vivek Goyal Oct. 9, 2020, 6:15 p.m. UTC
If fc->handle_killpriv_v2 is enabled, we expect file server to clear
suid/sgid/security.capbility upon chown/truncate/write as appropriate.

Upon truncate (ATTR_SIZE), suid/sgid is cleared only if caller does
not have CAP_FSETID. File server does not know whether caller has
CAP_FSETID or not. Hence set FATTR_KILL_PRIV upon truncate to let
file server know that caller does not have CAP_FSETID and it should
kill suid/sgid as appropriate.

We don't have to send this information for chown (ATTR_UID/ATTR_GID)
as that always clears suid/sgid irrespective of capabilities of
calling process.

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
 fs/fuse/dir.c             | 2 ++
 include/uapi/linux/fuse.h | 1 +
 2 files changed, 3 insertions(+)

Comments

Miklos Szeredi Nov. 6, 2020, 2:39 p.m. UTC | #1
On Fri, Oct 9, 2020 at 8:16 PM Vivek Goyal <vgoyal@redhat.com> wrote:
>
> If fc->handle_killpriv_v2 is enabled, we expect file server to clear
> suid/sgid/security.capbility upon chown/truncate/write as appropriate.
>
> Upon truncate (ATTR_SIZE), suid/sgid is cleared only if caller does
> not have CAP_FSETID. File server does not know whether caller has
> CAP_FSETID or not. Hence set FATTR_KILL_PRIV upon truncate to let
> file server know that caller does not have CAP_FSETID and it should
> kill suid/sgid as appropriate.
>
> We don't have to send this information for chown (ATTR_UID/ATTR_GID)
> as that always clears suid/sgid irrespective of capabilities of
> calling process.

I'm  undecided on this.   Would it hurt to set it on chown?  That
might make the logic in some servers simpler, no?

What would be the drawback of setting FATTR_KILL_PRIV for chown as well?

Thanks,
Miklos
Vivek Goyal Nov. 6, 2020, 5:18 p.m. UTC | #2
On Fri, Nov 06, 2020 at 03:39:29PM +0100, Miklos Szeredi wrote:
> On Fri, Oct 9, 2020 at 8:16 PM Vivek Goyal <vgoyal@redhat.com> wrote:
> >
> > If fc->handle_killpriv_v2 is enabled, we expect file server to clear
> > suid/sgid/security.capbility upon chown/truncate/write as appropriate.
> >
> > Upon truncate (ATTR_SIZE), suid/sgid is cleared only if caller does
> > not have CAP_FSETID. File server does not know whether caller has
> > CAP_FSETID or not. Hence set FATTR_KILL_PRIV upon truncate to let
> > file server know that caller does not have CAP_FSETID and it should
> > kill suid/sgid as appropriate.
> >
> > We don't have to send this information for chown (ATTR_UID/ATTR_GID)
> > as that always clears suid/sgid irrespective of capabilities of
> > calling process.
> 
> I'm  undecided on this.   Would it hurt to set it on chown?  That
> might make the logic in some servers simpler, no?
> 
> What would be the drawback of setting FATTR_KILL_PRIV for chown as well?

Hi Miklos,

Thinking loud.

So these are the rules we expect from VFS point of view.

- caps are always cleared on chown/write/truncate
- suid is always cleared on chown, while for truncate/write it is cleared
  only if caller does not have CAP_FSETID.
- sgid is always cleared on chown, while for truncate/write it is cleared
  only if caller does not have CAP_FSETID as well as file has group execute
  permission.

From server point of view, these rules become.

- caps are always cleared on chown/write/truncate
- suid is always cleared on chown, while for truncate/write it is cleared
  only if client set appropriate flag.
  	- For truncate, this flag will either be FUSE_OPEN_KILL_PRIV or
	  FATTR_KILL_PRIV.
	- For write, FUSE_WRITE_KILL_PRIV will be set.
- sgid is always cleared on chown, while for truncate/write it is cleared
  only if caller has set a flag as well as file has group execute permission.
  	- For truncate, this flag will either be FUSE_OPEN_KILL_PRIV or
	  FATTR_KILL_PRIV.
	- For write, FUSE_WRITE_KILL_PRIV will be set.

Above rules assumes that chown() will always clear caps/suid/sgid and
server does not have to rely on any flags.

I think it does not hurt to start passing FATTR_KILL_PRIV for chown()
as well. In that case, server will always clear caps on chown but
clear suid/sgid only if FATTR_KILL_PRIV is set. (Which will always
be set).

So anything is fine. We just need to document it well. I think I will
write it very clearly in qemu patch depending on what goes in kernel.

Thanks
Vivek
Miklos Szeredi Nov. 11, 2020, 1:54 p.m. UTC | #3
On Fri, Nov 6, 2020 at 6:18 PM Vivek Goyal <vgoyal@redhat.com> wrote:

> I think it does not hurt to start passing FATTR_KILL_PRIV for chown()
> as well. In that case, server will always clear caps on chown but
> clear suid/sgid only if FATTR_KILL_PRIV is set. (Which will always
> be set).

Okay.

More thoughts for FUSE_HANDLE_KILLPRIV_V2:

 - clear "security.capability" on write, truncate and chown unconditionally
 - clear suid/sgid if
    o setattr has FATTR_SIZE and  FATTR_KILL_PRIV
    o setattr has FATTR_UID or FATTR_GID
    o open has O_TRUNC and FUSE_OPEN_KILL_PRIV
    o write has FUSE_WRITE_KILL_PRIV

Kernel has:
ATTR_KILL_PRIV -> clear "security.capability"
ATTR_KILL_SUID -> clear S_ISUID
ATTR_KILL_SGID -> clear S_ISGID if executable

Fuse has:
FUSE_*KILL_PRIV -> clear S_ISUID and S_ISGID if executable

So the fuse meaning of FUSE_*KILL_PRIV has a complementary meaning to
that of ATTR_KILL_PRIV, which is somewhat confusing.  Also "PRIV"
implies all privileges, including "security.capability" but the fuse
ones relate to suid/sgid only.

How about FUSE_*KILL_SUIDGID (FUSE_WRITE_KILL_SUIDGID being an alias
for FUSE_WRITE_KILL_PRIV)?

Thanks,
Miklos




>
> So anything is fine. We just need to document it well. I think I will
> write it very clearly in qemu patch depending on what goes in kernel.
>
> Thanks
> Vivek
>
Miklos Szeredi Nov. 11, 2020, 4:24 p.m. UTC | #4
On Wed, Nov 11, 2020 at 2:54 PM Miklos Szeredi <miklos@szeredi.hu> wrote:

> How about FUSE_*KILL_SUIDGID (FUSE_WRITE_KILL_SUIDGID being an alias
> for FUSE_WRITE_KILL_PRIV)?

Series pushed to #for-next with these changes.  Please take a look and test.

Thanks,
Miklos
Vivek Goyal Nov. 11, 2020, 7:16 p.m. UTC | #5
On Wed, Nov 11, 2020 at 02:54:43PM +0100, Miklos Szeredi wrote:
> On Fri, Nov 6, 2020 at 6:18 PM Vivek Goyal <vgoyal@redhat.com> wrote:
> 
> > I think it does not hurt to start passing FATTR_KILL_PRIV for chown()
> > as well. In that case, server will always clear caps on chown but
> > clear suid/sgid only if FATTR_KILL_PRIV is set. (Which will always
> > be set).
> 
> Okay.
> 
> More thoughts for FUSE_HANDLE_KILLPRIV_V2:
> 
>  - clear "security.capability" on write, truncate and chown unconditionally
>  - clear suid/sgid if
>     o setattr has FATTR_SIZE and  FATTR_KILL_PRIV
>     o setattr has FATTR_UID or FATTR_GID
>     o open has O_TRUNC and FUSE_OPEN_KILL_PRIV
>     o write has FUSE_WRITE_KILL_PRIV
> 
> Kernel has:
> ATTR_KILL_PRIV -> clear "security.capability"
> ATTR_KILL_SUID -> clear S_ISUID
> ATTR_KILL_SGID -> clear S_ISGID if executable
> 
> Fuse has:
> FUSE_*KILL_PRIV -> clear S_ISUID and S_ISGID if executable
> 
> So the fuse meaning of FUSE_*KILL_PRIV has a complementary meaning to
> that of ATTR_KILL_PRIV, which is somewhat confusing.  Also "PRIV"
> implies all privileges, including "security.capability" but the fuse
> ones relate to suid/sgid only.
> 
> How about FUSE_*KILL_SUIDGID (FUSE_WRITE_KILL_SUIDGID being an alias
> for FUSE_WRITE_KILL_PRIV)?

Hi Miklos,

Renaming FUSE_*KILL_PRIV to FUSE_*KILL_SUIDSGID sounds good. For a
breif moment I was also thinking that these FUSE_*KILL_PRIV and
and ATTR_KILL_PRIV are not exactly mapping. Glad you caught it
and made the situation better.

Thanks
Vivek
Vivek Goyal Nov. 11, 2020, 10:09 p.m. UTC | #6
On Wed, Nov 11, 2020 at 05:24:22PM +0100, Miklos Szeredi wrote:
> On Wed, Nov 11, 2020 at 2:54 PM Miklos Szeredi <miklos@szeredi.hu> wrote:
> 
> > How about FUSE_*KILL_SUIDGID (FUSE_WRITE_KILL_SUIDGID being an alias
> > for FUSE_WRITE_KILL_PRIV)?
> 
> Series pushed to #for-next with these changes.  Please take a look and test.

Thanks Miklos. I looked at the patches quickly and they look good. I am
now fixing qemu virtiofsd side and will test.

Thanks
Vivek
diff mbox series

Patch

diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index c4a01290aec6..ecdb7895c156 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1575,6 +1575,8 @@  int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
 		/* For mandatory locking in truncate */
 		inarg.valid |= FATTR_LOCKOWNER;
 		inarg.lock_owner = fuse_lock_owner_id(fc, current->files);
+		if (fc->handle_killpriv_v2 && !capable(CAP_FSETID))
+			inarg.valid |= FATTR_KILL_PRIV;
 	}
 	fuse_setattr_fill(fc, &args, inode, &inarg, &outarg);
 	err = fuse_simple_request(fc, &args);
diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h
index 3ae3f222a0ed..7b8da0a2de0d 100644
--- a/include/uapi/linux/fuse.h
+++ b/include/uapi/linux/fuse.h
@@ -269,6 +269,7 @@  struct fuse_file_lock {
 #define FATTR_MTIME_NOW	(1 << 8)
 #define FATTR_LOCKOWNER	(1 << 9)
 #define FATTR_CTIME	(1 << 10)
+#define FATTR_KILL_PRIV	(1 << 14) /* Matches ATTR_KILL_PRIV */
 
 /**
  * Flags returned by the OPEN request