On Sun, May 8, 2022 at 3:19 PM Christian Göttsche <cgzones@xxxxxxxxxxxxxx> wrote: > > Operating on a file descriptor avoids TOCTOU issues and one opened via > O_PATH avoids the requirement of having read access to the file. Since > Linux does not natively support file descriptors opened via O_PATH in > fgetxattr(2) and at least glibc and musl does not emulate O_PATH support > in their implementations, fgetfilecon(3) and fsetfilecon(3) also do not > currently support file descriptors opened with O_PATH. > > Inspired by CVE-2013-4392: https://github.com/systemd/systemd/pull/8583 > Implementation adapted from: https://android.googlesource.com/platform/bionic/+/2825f10b7f61558c264231a536cf3affc0d84204%5E%21/ > > Signed-off-by: Christian Göttsche <cgzones@xxxxxxxxxxxxxx> Works as advertised. Acked-by: James Carter <jwcart2@xxxxxxxxx> > --- > libselinux/man/man3/getfilecon.3 | 4 +++- > libselinux/man/man3/setfilecon.3 | 4 +++- > libselinux/src/fgetfilecon.c | 28 +++++++++++++++++++++++++--- > libselinux/src/fsetfilecon.c | 23 ++++++++++++++++++++++- > 4 files changed, 53 insertions(+), 6 deletions(-) > > diff --git a/libselinux/man/man3/getfilecon.3 b/libselinux/man/man3/getfilecon.3 > index 5bb575b5..c3e92ba1 100644 > --- a/libselinux/man/man3/getfilecon.3 > +++ b/libselinux/man/man3/getfilecon.3 > @@ -33,7 +33,9 @@ is identical to > .BR getfilecon (), > only the open file pointed to by filedes (as returned by > .BR open (2)) > -is interrogated in place of path. > +is interrogated in place of path. Since libselinux 3.4 a file opened via > +.I O_PATH > +is supported. > > .BR getfilecon_raw (), > .BR lgetfilecon_raw () > diff --git a/libselinux/man/man3/setfilecon.3 b/libselinux/man/man3/setfilecon.3 > index 0e9a383f..61f9a806 100644 > --- a/libselinux/man/man3/setfilecon.3 > +++ b/libselinux/man/man3/setfilecon.3 > @@ -29,7 +29,9 @@ link itself has it's context set, not the file that it refers to. > is identical to setfilecon, only the open file pointed to by filedes (as > returned by > .BR open (2)) > -has it's context set in place of path. > +has it's context set in place of path. Since libselinux 3.4 a file opened via > +.I O_PATH > +is supported. > > .BR setfilecon_raw (), > .BR lsetfilecon_raw (), > diff --git a/libselinux/src/fgetfilecon.c b/libselinux/src/fgetfilecon.c > index 8c748f8a..baf38ec1 100644 > --- a/libselinux/src/fgetfilecon.c > +++ b/libselinux/src/fgetfilecon.c > @@ -3,10 +3,32 @@ > #include <string.h> > #include <stdlib.h> > #include <errno.h> > +#include <stdio.h> > #include <sys/xattr.h> > #include "selinux_internal.h" > #include "policy.h" > > +static ssize_t fgetxattr_wrapper(int fd, const char *name, void *value, size_t size) { > + char buf[40]; > + int fd_flag, saved_errno = errno; > + ssize_t ret; > + > + ret = fgetxattr(fd, name, value, size); > + if (ret != -1 || errno != EBADF) > + return ret; > + > + /* Emulate O_PATH support */ > + fd_flag = fcntl(fd, F_GETFL); > + if (fd_flag == -1 || (fd_flag & O_PATH) == 0) { > + errno = EBADF; > + return -1; > + } > + > + snprintf(buf, sizeof(buf), "/proc/self/fd/%d", fd); > + errno = saved_errno; > + return getxattr(buf, name, value, size); > +} > + > int fgetfilecon_raw(int fd, char ** context) > { > char *buf; > @@ -19,11 +41,11 @@ int fgetfilecon_raw(int fd, char ** context) > return -1; > memset(buf, 0, size); > > - ret = fgetxattr(fd, XATTR_NAME_SELINUX, buf, size - 1); > + ret = fgetxattr_wrapper(fd, XATTR_NAME_SELINUX, buf, size - 1); > if (ret < 0 && errno == ERANGE) { > char *newbuf; > > - size = fgetxattr(fd, XATTR_NAME_SELINUX, NULL, 0); > + size = fgetxattr_wrapper(fd, XATTR_NAME_SELINUX, NULL, 0); > if (size < 0) > goto out; > > @@ -34,7 +56,7 @@ int fgetfilecon_raw(int fd, char ** context) > > buf = newbuf; > memset(buf, 0, size); > - ret = fgetxattr(fd, XATTR_NAME_SELINUX, buf, size - 1); > + ret = fgetxattr_wrapper(fd, XATTR_NAME_SELINUX, buf, size - 1); > } > out: > if (ret == 0) { > diff --git a/libselinux/src/fsetfilecon.c b/libselinux/src/fsetfilecon.c > index 5cf34e3f..be821c7a 100644 > --- a/libselinux/src/fsetfilecon.c > +++ b/libselinux/src/fsetfilecon.c > @@ -3,13 +3,34 @@ > #include <string.h> > #include <stdlib.h> > #include <errno.h> > +#include <stdio.h> > #include <sys/xattr.h> > #include "selinux_internal.h" > #include "policy.h" > > +static int fsetxattr_wrapper(int fd, const char* name, const void* value, size_t size, int flags) { > + char buf[40]; > + int rc, fd_flag, saved_errno = errno; > + > + rc = fsetxattr(fd, name, value, size, flags); > + if (rc == 0 || errno != EBADF) > + return rc; > + > + /* Emulate O_PATH support */ > + fd_flag = fcntl(fd, F_GETFL); > + if (fd_flag == -1 || (fd_flag & O_PATH) == 0) { > + errno = EBADF; > + return -1; > + } > + > + snprintf(buf, sizeof(buf), "/proc/self/fd/%d", fd); > + errno = saved_errno; > + return setxattr(buf, name, value, size, flags); > +} > + > int fsetfilecon_raw(int fd, const char * context) > { > - int rc = fsetxattr(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1, > + int rc = fsetxattr_wrapper(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1, > 0); > if (rc < 0 && errno == ENOTSUP) { > char * ccontext = NULL; > -- > 2.36.0 >