diff mbox series

[032/104] virtiofsd: passthrough_ll: create new files in caller's context

Message ID 20191212163904.159893-33-dgilbert@redhat.com (mailing list archive)
State New, archived
Headers show
Series virtiofs daemon [all] | expand

Commit Message

Dr. David Alan Gilbert Dec. 12, 2019, 4:37 p.m. UTC
From: Vivek Goyal <vgoyal@redhat.com>

We need to create files in the caller's context. Otherwise after
creating a file, the caller might not be able to do file operations on
that file.

Changed effective uid/gid to caller's uid/gid, create file and then
switch back to uid/gid 0.

Use syscall(setresuid, ...) otherwise glibc does some magic to change EUID
in all threads, which is not what we want.

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
---
 tools/virtiofsd/passthrough_ll.c | 79 ++++++++++++++++++++++++++++++--
 1 file changed, 74 insertions(+), 5 deletions(-)

Comments

Daniel P. Berrangé Jan. 6, 2020, 2:30 p.m. UTC | #1
On Thu, Dec 12, 2019 at 04:37:52PM +0000, Dr. David Alan Gilbert (git) wrote:
> From: Vivek Goyal <vgoyal@redhat.com>
> 
> We need to create files in the caller's context. Otherwise after
> creating a file, the caller might not be able to do file operations on
> that file.
> 
> Changed effective uid/gid to caller's uid/gid, create file and then
> switch back to uid/gid 0.
> 
> Use syscall(setresuid, ...) otherwise glibc does some magic to change EUID
> in all threads, which is not what we want.
> 
> Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
> ---
>  tools/virtiofsd/passthrough_ll.c | 79 ++++++++++++++++++++++++++++++--
>  1 file changed, 74 insertions(+), 5 deletions(-)
> 
> diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c
> index 68bacb6fc5..0188cd9ad6 100644
> --- a/tools/virtiofsd/passthrough_ll.c
> +++ b/tools/virtiofsd/passthrough_ll.c


> +static int lo_change_cred(fuse_req_t req, struct lo_cred *old)
> +{
> +    int res;
> +
> +    old->euid = geteuid();
> +    old->egid = getegid();
> +
> +    res = syscall(SYS_setresgid, -1, fuse_req_ctx(req)->gid, -1);

Do we need to be using  SYS_setres[u,g]id32 instead...

[quote setresgid(2)]
       The original Linux setresuid() and setresgid() system  calls
       supported  only  16-bit  user  and group IDs.  Subsequently,
       Linux 2.4 added setresuid32() and setresgid32(),  supporting
       32-bit  IDs.   The glibc setresuid() and setresgid() wrapper
       functions transparently deal with the variations across ker‐
       nel versions.
[/quote]

> +    if (res == -1) {
> +        return errno;
> +    }
> +
> +    res = syscall(SYS_setresuid, -1, fuse_req_ctx(req)->uid, -1);
> +    if (res == -1) {
> +        int errno_save = errno;
> +
> +        syscall(SYS_setresgid, -1, old->egid, -1);
> +        return errno_save;
> +    }
> +
> +    return 0;
> +}
> +
> +/* Regain Privileges */
> +static void lo_restore_cred(struct lo_cred *old)
> +{
> +    int res;
> +
> +    res = syscall(SYS_setresuid, -1, old->euid, -1);
> +    if (res == -1) {
> +        fuse_log(FUSE_LOG_ERR, "seteuid(%u): %m\n", old->euid);
> +        exit(1);
> +    }
> +
> +    res = syscall(SYS_setresgid, -1, old->egid, -1);
> +    if (res == -1) {
> +        fuse_log(FUSE_LOG_ERR, "setegid(%u): %m\n", old->egid);
> +        exit(1);
> +    }
> +}
> +
>  static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
>                               const char *name, mode_t mode, dev_t rdev,
>                               const char *link)
> @@ -391,12 +443,21 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
>      int saverr;
>      struct lo_inode *dir = lo_inode(req, parent);
>      struct fuse_entry_param e;
> +    struct lo_cred old = {};
>  
>      saverr = ENOMEM;
>  
> +    saverr = lo_change_cred(req, &old);
> +    if (saverr) {
> +        goto out;
> +    }
> +
>      res = mknod_wrapper(dir->fd, name, link, mode, rdev);
>  
>      saverr = errno;
> +
> +    lo_restore_cred(&old);
> +
>      if (res == -1) {
>          goto out;
>      }
> @@ -794,26 +855,34 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
>      struct lo_data *lo = lo_data(req);
>      struct fuse_entry_param e;
>      int err;
> +    struct lo_cred old = {};
>  
>      if (lo_debug(req)) {
>          fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n",
>                   parent, name);
>      }
>  
> +    err = lo_change_cred(req, &old);
> +    if (err) {
> +        goto out;
> +    }
> +
>      fd = openat(lo_fd(req, parent), name, (fi->flags | O_CREAT) & ~O_NOFOLLOW,
>                  mode);
> -    if (fd == -1) {
> -        return (void)fuse_reply_err(req, errno);
> -    }
> +    err = fd == -1 ? errno : 0;
> +    lo_restore_cred(&old);
>  
> -    fi->fh = fd;
> +    if (!err) {
> +        fi->fh = fd;
> +        err = lo_do_lookup(req, parent, name, &e);
> +    }
>      if (lo->cache == CACHE_NEVER) {
>          fi->direct_io = 1;
>      } else if (lo->cache == CACHE_ALWAYS) {
>          fi->keep_cache = 1;
>      }
>  
> -    err = lo_do_lookup(req, parent, name, &e);
> +out:
>      if (err) {
>          fuse_reply_err(req, err);
>      } else {
> -- 
> 2.23.0
> 
> 

Regards,
Daniel
Dr. David Alan Gilbert Jan. 6, 2020, 7 p.m. UTC | #2
* Daniel P. Berrangé (berrange@redhat.com) wrote:
> On Thu, Dec 12, 2019 at 04:37:52PM +0000, Dr. David Alan Gilbert (git) wrote:
> > From: Vivek Goyal <vgoyal@redhat.com>
> > 
> > We need to create files in the caller's context. Otherwise after
> > creating a file, the caller might not be able to do file operations on
> > that file.
> > 
> > Changed effective uid/gid to caller's uid/gid, create file and then
> > switch back to uid/gid 0.
> > 
> > Use syscall(setresuid, ...) otherwise glibc does some magic to change EUID
> > in all threads, which is not what we want.
> > 
> > Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
> > Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
> > ---
> >  tools/virtiofsd/passthrough_ll.c | 79 ++++++++++++++++++++++++++++++--
> >  1 file changed, 74 insertions(+), 5 deletions(-)
> > 
> > diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c
> > index 68bacb6fc5..0188cd9ad6 100644
> > --- a/tools/virtiofsd/passthrough_ll.c
> > +++ b/tools/virtiofsd/passthrough_ll.c
> 
> 
> > +static int lo_change_cred(fuse_req_t req, struct lo_cred *old)
> > +{
> > +    int res;
> > +
> > +    old->euid = geteuid();
> > +    old->egid = getegid();
> > +
> > +    res = syscall(SYS_setresgid, -1, fuse_req_ctx(req)->gid, -1);
> 
> Do we need to be using  SYS_setres[u,g]id32 instead...
> 
> [quote setresgid(2)]
>        The original Linux setresuid() and setresgid() system  calls
>        supported  only  16-bit  user  and group IDs.  Subsequently,
>        Linux 2.4 added setresuid32() and setresgid32(),  supporting
>        32-bit  IDs.   The glibc setresuid() and setresgid() wrapper
>        functions transparently deal with the variations across ker‐
>        nel versions.
> [/quote]

OK, updated.

Dave

> > +    if (res == -1) {
> > +        return errno;
> > +    }
> > +
> > +    res = syscall(SYS_setresuid, -1, fuse_req_ctx(req)->uid, -1);
> > +    if (res == -1) {
> > +        int errno_save = errno;
> > +
> > +        syscall(SYS_setresgid, -1, old->egid, -1);
> > +        return errno_save;
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +/* Regain Privileges */
> > +static void lo_restore_cred(struct lo_cred *old)
> > +{
> > +    int res;
> > +
> > +    res = syscall(SYS_setresuid, -1, old->euid, -1);
> > +    if (res == -1) {
> > +        fuse_log(FUSE_LOG_ERR, "seteuid(%u): %m\n", old->euid);
> > +        exit(1);
> > +    }
> > +
> > +    res = syscall(SYS_setresgid, -1, old->egid, -1);
> > +    if (res == -1) {
> > +        fuse_log(FUSE_LOG_ERR, "setegid(%u): %m\n", old->egid);
> > +        exit(1);
> > +    }
> > +}
> > +
> >  static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
> >                               const char *name, mode_t mode, dev_t rdev,
> >                               const char *link)
> > @@ -391,12 +443,21 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
> >      int saverr;
> >      struct lo_inode *dir = lo_inode(req, parent);
> >      struct fuse_entry_param e;
> > +    struct lo_cred old = {};
> >  
> >      saverr = ENOMEM;
> >  
> > +    saverr = lo_change_cred(req, &old);
> > +    if (saverr) {
> > +        goto out;
> > +    }
> > +
> >      res = mknod_wrapper(dir->fd, name, link, mode, rdev);
> >  
> >      saverr = errno;
> > +
> > +    lo_restore_cred(&old);
> > +
> >      if (res == -1) {
> >          goto out;
> >      }
> > @@ -794,26 +855,34 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
> >      struct lo_data *lo = lo_data(req);
> >      struct fuse_entry_param e;
> >      int err;
> > +    struct lo_cred old = {};
> >  
> >      if (lo_debug(req)) {
> >          fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n",
> >                   parent, name);
> >      }
> >  
> > +    err = lo_change_cred(req, &old);
> > +    if (err) {
> > +        goto out;
> > +    }
> > +
> >      fd = openat(lo_fd(req, parent), name, (fi->flags | O_CREAT) & ~O_NOFOLLOW,
> >                  mode);
> > -    if (fd == -1) {
> > -        return (void)fuse_reply_err(req, errno);
> > -    }
> > +    err = fd == -1 ? errno : 0;
> > +    lo_restore_cred(&old);
> >  
> > -    fi->fh = fd;
> > +    if (!err) {
> > +        fi->fh = fd;
> > +        err = lo_do_lookup(req, parent, name, &e);
> > +    }
> >      if (lo->cache == CACHE_NEVER) {
> >          fi->direct_io = 1;
> >      } else if (lo->cache == CACHE_ALWAYS) {
> >          fi->keep_cache = 1;
> >      }
> >  
> > -    err = lo_do_lookup(req, parent, name, &e);
> > +out:
> >      if (err) {
> >          fuse_reply_err(req, err);
> >      } else {
> > -- 
> > 2.23.0
> > 
> > 
> 
> Regards,
> Daniel
> -- 
> |: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org         -o-            https://fstop138.berrange.com :|
> |: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
Dr. David Alan Gilbert Jan. 6, 2020, 7:08 p.m. UTC | #3
* Dr. David Alan Gilbert (dgilbert@redhat.com) wrote:
> * Daniel P. Berrangé (berrange@redhat.com) wrote:
> > On Thu, Dec 12, 2019 at 04:37:52PM +0000, Dr. David Alan Gilbert (git) wrote:
> > > From: Vivek Goyal <vgoyal@redhat.com>
> > > 
> > > We need to create files in the caller's context. Otherwise after
> > > creating a file, the caller might not be able to do file operations on
> > > that file.
> > > 
> > > Changed effective uid/gid to caller's uid/gid, create file and then
> > > switch back to uid/gid 0.
> > > 
> > > Use syscall(setresuid, ...) otherwise glibc does some magic to change EUID
> > > in all threads, which is not what we want.
> > > 
> > > Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
> > > Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
> > > ---
> > >  tools/virtiofsd/passthrough_ll.c | 79 ++++++++++++++++++++++++++++++--
> > >  1 file changed, 74 insertions(+), 5 deletions(-)
> > > 
> > > diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c
> > > index 68bacb6fc5..0188cd9ad6 100644
> > > --- a/tools/virtiofsd/passthrough_ll.c
> > > +++ b/tools/virtiofsd/passthrough_ll.c
> > 
> > 
> > > +static int lo_change_cred(fuse_req_t req, struct lo_cred *old)
> > > +{
> > > +    int res;
> > > +
> > > +    old->euid = geteuid();
> > > +    old->egid = getegid();
> > > +
> > > +    res = syscall(SYS_setresgid, -1, fuse_req_ctx(req)->gid, -1);
> > 
> > Do we need to be using  SYS_setres[u,g]id32 instead...
> > 
> > [quote setresgid(2)]
> >        The original Linux setresuid() and setresgid() system  calls
> >        supported  only  16-bit  user  and group IDs.  Subsequently,
> >        Linux 2.4 added setresuid32() and setresgid32(),  supporting
> >        32-bit  IDs.   The glibc setresuid() and setresgid() wrapper
> >        functions transparently deal with the variations across ker‐
> >        nel versions.
> > [/quote]
> 
> OK, updated.

Hmm hang on; this is messy.  x86-64 only seems to have setresuid
where as some architectures have both;  If I'm reading this right, all
64 bit machines have setresuid/gid calling the code that takes the
32bit ID; some have compat entries for 32bit syscalls.

I think it's probably more correct to call setresuid here; except
for 32 bit platforms - but how do we tell?

Dave

> Dave
> 
> > > +    if (res == -1) {
> > > +        return errno;
> > > +    }
> > > +
> > > +    res = syscall(SYS_setresuid, -1, fuse_req_ctx(req)->uid, -1);
> > > +    if (res == -1) {
> > > +        int errno_save = errno;
> > > +
> > > +        syscall(SYS_setresgid, -1, old->egid, -1);
> > > +        return errno_save;
> > > +    }
> > > +
> > > +    return 0;
> > > +}
> > > +
> > > +/* Regain Privileges */
> > > +static void lo_restore_cred(struct lo_cred *old)
> > > +{
> > > +    int res;
> > > +
> > > +    res = syscall(SYS_setresuid, -1, old->euid, -1);
> > > +    if (res == -1) {
> > > +        fuse_log(FUSE_LOG_ERR, "seteuid(%u): %m\n", old->euid);
> > > +        exit(1);
> > > +    }
> > > +
> > > +    res = syscall(SYS_setresgid, -1, old->egid, -1);
> > > +    if (res == -1) {
> > > +        fuse_log(FUSE_LOG_ERR, "setegid(%u): %m\n", old->egid);
> > > +        exit(1);
> > > +    }
> > > +}
> > > +
> > >  static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
> > >                               const char *name, mode_t mode, dev_t rdev,
> > >                               const char *link)
> > > @@ -391,12 +443,21 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
> > >      int saverr;
> > >      struct lo_inode *dir = lo_inode(req, parent);
> > >      struct fuse_entry_param e;
> > > +    struct lo_cred old = {};
> > >  
> > >      saverr = ENOMEM;
> > >  
> > > +    saverr = lo_change_cred(req, &old);
> > > +    if (saverr) {
> > > +        goto out;
> > > +    }
> > > +
> > >      res = mknod_wrapper(dir->fd, name, link, mode, rdev);
> > >  
> > >      saverr = errno;
> > > +
> > > +    lo_restore_cred(&old);
> > > +
> > >      if (res == -1) {
> > >          goto out;
> > >      }
> > > @@ -794,26 +855,34 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
> > >      struct lo_data *lo = lo_data(req);
> > >      struct fuse_entry_param e;
> > >      int err;
> > > +    struct lo_cred old = {};
> > >  
> > >      if (lo_debug(req)) {
> > >          fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n",
> > >                   parent, name);
> > >      }
> > >  
> > > +    err = lo_change_cred(req, &old);
> > > +    if (err) {
> > > +        goto out;
> > > +    }
> > > +
> > >      fd = openat(lo_fd(req, parent), name, (fi->flags | O_CREAT) & ~O_NOFOLLOW,
> > >                  mode);
> > > -    if (fd == -1) {
> > > -        return (void)fuse_reply_err(req, errno);
> > > -    }
> > > +    err = fd == -1 ? errno : 0;
> > > +    lo_restore_cred(&old);
> > >  
> > > -    fi->fh = fd;
> > > +    if (!err) {
> > > +        fi->fh = fd;
> > > +        err = lo_do_lookup(req, parent, name, &e);
> > > +    }
> > >      if (lo->cache == CACHE_NEVER) {
> > >          fi->direct_io = 1;
> > >      } else if (lo->cache == CACHE_ALWAYS) {
> > >          fi->keep_cache = 1;
> > >      }
> > >  
> > > -    err = lo_do_lookup(req, parent, name, &e);
> > > +out:
> > >      if (err) {
> > >          fuse_reply_err(req, err);
> > >      } else {
> > > -- 
> > > 2.23.0
> > > 
> > > 
> > 
> > Regards,
> > Daniel
> > -- 
> > |: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
> > |: https://libvirt.org         -o-            https://fstop138.berrange.com :|
> > |: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|
> --
> Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
Daniel P. Berrangé Jan. 7, 2020, 9:22 a.m. UTC | #4
On Mon, Jan 06, 2020 at 07:08:43PM +0000, Dr. David Alan Gilbert wrote:
> * Dr. David Alan Gilbert (dgilbert@redhat.com) wrote:
> > * Daniel P. Berrangé (berrange@redhat.com) wrote:
> > > On Thu, Dec 12, 2019 at 04:37:52PM +0000, Dr. David Alan Gilbert (git) wrote:
> > > > From: Vivek Goyal <vgoyal@redhat.com>
> > > > 
> > > > We need to create files in the caller's context. Otherwise after
> > > > creating a file, the caller might not be able to do file operations on
> > > > that file.
> > > > 
> > > > Changed effective uid/gid to caller's uid/gid, create file and then
> > > > switch back to uid/gid 0.
> > > > 
> > > > Use syscall(setresuid, ...) otherwise glibc does some magic to change EUID
> > > > in all threads, which is not what we want.
> > > > 
> > > > Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
> > > > Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
> > > > ---
> > > >  tools/virtiofsd/passthrough_ll.c | 79 ++++++++++++++++++++++++++++++--
> > > >  1 file changed, 74 insertions(+), 5 deletions(-)
> > > > 
> > > > diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c
> > > > index 68bacb6fc5..0188cd9ad6 100644
> > > > --- a/tools/virtiofsd/passthrough_ll.c
> > > > +++ b/tools/virtiofsd/passthrough_ll.c
> > > 
> > > 
> > > > +static int lo_change_cred(fuse_req_t req, struct lo_cred *old)
> > > > +{
> > > > +    int res;
> > > > +
> > > > +    old->euid = geteuid();
> > > > +    old->egid = getegid();
> > > > +
> > > > +    res = syscall(SYS_setresgid, -1, fuse_req_ctx(req)->gid, -1);
> > > 
> > > Do we need to be using  SYS_setres[u,g]id32 instead...
> > > 
> > > [quote setresgid(2)]
> > >        The original Linux setresuid() and setresgid() system  calls
> > >        supported  only  16-bit  user  and group IDs.  Subsequently,
> > >        Linux 2.4 added setresuid32() and setresgid32(),  supporting
> > >        32-bit  IDs.   The glibc setresuid() and setresgid() wrapper
> > >        functions transparently deal with the variations across ker‐
> > >        nel versions.
> > > [/quote]
> > 
> > OK, updated.
> 
> Hmm hang on; this is messy.  x86-64 only seems to have setresuid
> where as some architectures have both;  If I'm reading this right, all
> 64 bit machines have setresuid/gid calling the code that takes the
> 32bit ID; some have compat entries for 32bit syscalls.

Oh yuk.

> I think it's probably more correct to call setresuid here; except
> for 32 bit platforms - but how do we tell?

Is it possible to just do an #ifdef SYS_setresgid32 check to see
if the wider variant exists ?


Regards,
Daniel
Dr. David Alan Gilbert Jan. 10, 2020, 1:05 p.m. UTC | #5
* Daniel P. Berrangé (berrange@redhat.com) wrote:
> On Mon, Jan 06, 2020 at 07:08:43PM +0000, Dr. David Alan Gilbert wrote:
> > * Dr. David Alan Gilbert (dgilbert@redhat.com) wrote:
> > > * Daniel P. Berrangé (berrange@redhat.com) wrote:
> > > > On Thu, Dec 12, 2019 at 04:37:52PM +0000, Dr. David Alan Gilbert (git) wrote:
> > > > > From: Vivek Goyal <vgoyal@redhat.com>
> > > > > 
> > > > > We need to create files in the caller's context. Otherwise after
> > > > > creating a file, the caller might not be able to do file operations on
> > > > > that file.
> > > > > 
> > > > > Changed effective uid/gid to caller's uid/gid, create file and then
> > > > > switch back to uid/gid 0.
> > > > > 
> > > > > Use syscall(setresuid, ...) otherwise glibc does some magic to change EUID
> > > > > in all threads, which is not what we want.
> > > > > 
> > > > > Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
> > > > > Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
> > > > > ---
> > > > >  tools/virtiofsd/passthrough_ll.c | 79 ++++++++++++++++++++++++++++++--
> > > > >  1 file changed, 74 insertions(+), 5 deletions(-)
> > > > > 
> > > > > diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c
> > > > > index 68bacb6fc5..0188cd9ad6 100644
> > > > > --- a/tools/virtiofsd/passthrough_ll.c
> > > > > +++ b/tools/virtiofsd/passthrough_ll.c
> > > > 
> > > > 
> > > > > +static int lo_change_cred(fuse_req_t req, struct lo_cred *old)
> > > > > +{
> > > > > +    int res;
> > > > > +
> > > > > +    old->euid = geteuid();
> > > > > +    old->egid = getegid();
> > > > > +
> > > > > +    res = syscall(SYS_setresgid, -1, fuse_req_ctx(req)->gid, -1);
> > > > 
> > > > Do we need to be using  SYS_setres[u,g]id32 instead...
> > > > 
> > > > [quote setresgid(2)]
> > > >        The original Linux setresuid() and setresgid() system  calls
> > > >        supported  only  16-bit  user  and group IDs.  Subsequently,
> > > >        Linux 2.4 added setresuid32() and setresgid32(),  supporting
> > > >        32-bit  IDs.   The glibc setresuid() and setresgid() wrapper
> > > >        functions transparently deal with the variations across ker‐
> > > >        nel versions.
> > > > [/quote]
> > > 
> > > OK, updated.
> > 
> > Hmm hang on; this is messy.  x86-64 only seems to have setresuid
> > where as some architectures have both;  If I'm reading this right, all
> > 64 bit machines have setresuid/gid calling the code that takes the
> > 32bit ID; some have compat entries for 32bit syscalls.
> 
> Oh yuk.
> 
> > I think it's probably more correct to call setresuid here; except
> > for 32 bit platforms - but how do we tell?
> 
> Is it possible to just do an #ifdef SYS_setresgid32 check to see
> if the wider variant exists ?

I've ended up with:

+/*
+ * On some archs, setres*id is limited to 2^16 but they
+ * provide setres*id32 variants that allow 2^32.
+ * Others just let setres*id do 2^32 anyway.
+ */
+#ifdef SYS_setresgid32
+#define OURSYS_setresgid SYS_setresgid32
+#else
+#define OURSYS_setresgid SYS_setresgid
+#endif
+
+#ifdef SYS_setresuid32
+#define OURSYS_setresuid SYS_setresuid32
+#else
+#define OURSYS_setresuid SYS_setresuid
+#endif
+
+/*
+ * Change to uid/gid of caller so that file is created with
+ * ownership of caller.
+ * TODO: What about selinux context?
+ */
+static int lo_change_cred(fuse_req_t req, struct lo_cred *old)
+{
+    int res;
+
+    old->euid = geteuid();
+    old->egid = getegid();
+
+    res = syscall(OURSYS_setresgid, -1, fuse_req_ctx(req)->gid, -1);
+    if (res == -1) {
+        return errno;
+    }
+
+    res = syscall(OURSYS_setresuid, -1, fuse_req_ctx(req)->uid, -1);
+    if (res == -1) {
+        int errno_save = errno;
+
+        syscall(OURSYS_setresgid, -1, old->egid, -1);
+        return errno_save;
+    }

and in the seccomp:

#ifdef __NR_setresgid32
    SCMP_SYS(setresgid32),
#endif
#ifdef __NR_setresuid32
    SCMP_SYS(setresuid32),
#endif

Dave

> 
> Regards,
> Daniel
> -- 
> |: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org         -o-            https://fstop138.berrange.com :|
> |: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
diff mbox series

Patch

diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c
index 68bacb6fc5..0188cd9ad6 100644
--- a/tools/virtiofsd/passthrough_ll.c
+++ b/tools/virtiofsd/passthrough_ll.c
@@ -49,6 +49,7 @@ 
 #include <stdlib.h>
 #include <string.h>
 #include <sys/file.h>
+#include <sys/syscall.h>
 #include <sys/xattr.h>
 #include <unistd.h>
 
@@ -83,6 +84,11 @@  struct lo_inode {
     uint64_t refcount; /* protected by lo->mutex */
 };
 
+struct lo_cred {
+    uid_t euid;
+    gid_t egid;
+};
+
 enum {
     CACHE_NEVER,
     CACHE_NORMAL,
@@ -383,6 +389,52 @@  static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
     }
 }
 
+/*
+ * Change to uid/gid of caller so that file is created with
+ * ownership of caller.
+ * TODO: What about selinux context?
+ */
+static int lo_change_cred(fuse_req_t req, struct lo_cred *old)
+{
+    int res;
+
+    old->euid = geteuid();
+    old->egid = getegid();
+
+    res = syscall(SYS_setresgid, -1, fuse_req_ctx(req)->gid, -1);
+    if (res == -1) {
+        return errno;
+    }
+
+    res = syscall(SYS_setresuid, -1, fuse_req_ctx(req)->uid, -1);
+    if (res == -1) {
+        int errno_save = errno;
+
+        syscall(SYS_setresgid, -1, old->egid, -1);
+        return errno_save;
+    }
+
+    return 0;
+}
+
+/* Regain Privileges */
+static void lo_restore_cred(struct lo_cred *old)
+{
+    int res;
+
+    res = syscall(SYS_setresuid, -1, old->euid, -1);
+    if (res == -1) {
+        fuse_log(FUSE_LOG_ERR, "seteuid(%u): %m\n", old->euid);
+        exit(1);
+    }
+
+    res = syscall(SYS_setresgid, -1, old->egid, -1);
+    if (res == -1) {
+        fuse_log(FUSE_LOG_ERR, "setegid(%u): %m\n", old->egid);
+        exit(1);
+    }
+}
+
 static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
                              const char *name, mode_t mode, dev_t rdev,
                              const char *link)
@@ -391,12 +443,21 @@  static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
     int saverr;
     struct lo_inode *dir = lo_inode(req, parent);
     struct fuse_entry_param e;
+    struct lo_cred old = {};
 
     saverr = ENOMEM;
 
+    saverr = lo_change_cred(req, &old);
+    if (saverr) {
+        goto out;
+    }
+
     res = mknod_wrapper(dir->fd, name, link, mode, rdev);
 
     saverr = errno;
+
+    lo_restore_cred(&old);
+
     if (res == -1) {
         goto out;
     }
@@ -794,26 +855,34 @@  static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
     struct lo_data *lo = lo_data(req);
     struct fuse_entry_param e;
     int err;
+    struct lo_cred old = {};
 
     if (lo_debug(req)) {
         fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n",
                  parent, name);
     }
 
+    err = lo_change_cred(req, &old);
+    if (err) {
+        goto out;
+    }
+
     fd = openat(lo_fd(req, parent), name, (fi->flags | O_CREAT) & ~O_NOFOLLOW,
                 mode);
-    if (fd == -1) {
-        return (void)fuse_reply_err(req, errno);
-    }
+    err = fd == -1 ? errno : 0;
+    lo_restore_cred(&old);
 
-    fi->fh = fd;
+    if (!err) {
+        fi->fh = fd;
+        err = lo_do_lookup(req, parent, name, &e);
+    }
     if (lo->cache == CACHE_NEVER) {
         fi->direct_io = 1;
     } else if (lo->cache == CACHE_ALWAYS) {
         fi->keep_cache = 1;
     }
 
-    err = lo_do_lookup(req, parent, name, &e);
+out:
     if (err) {
         fuse_reply_err(req, err);
     } else {