Message ID | 20191212163904.159893-33-dgilbert@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | virtiofs daemon [all] | expand |
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
* 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 (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
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
* 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 --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 {