fstat(2) on file descriptors obtained via O_PATH is supported since Linux 3.6. Fallback on older systems to lstat(2). Fixes: 7e979b56 ("libselinux: restorecon: pin file to avoid TOCTOU issues") Signed-off-by: Christian Göttsche <cgzones@xxxxxxxxxxxxxx> --- libselinux/src/selinux_restorecon.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/libselinux/src/selinux_restorecon.c b/libselinux/src/selinux_restorecon.c index 9dd6be81..1a185ced 100644 --- a/libselinux/src/selinux_restorecon.c +++ b/libselinux/src/selinux_restorecon.c @@ -89,10 +89,22 @@ struct rest_flags { bool count_errors; }; +/* Linux version for availability tests. */ +static struct utsname uts; + static void restorecon_init(void) { struct selabel_handle *sehandle = NULL; + if (uname(&uts) < 0) { + /* + * utsname(2) should never fail, but assume oldest supported + * LTS release as backup + */ + strncpy(uts.release, "4.9", sizeof(uts.release)); + uts.release[sizeof(uts.release) - 1] = '\0'; + } + if (!fc_sehandle) { sehandle = selinux_restorecon_default_handle(); selinux_restorecon_set_sehandle(sehandle); @@ -238,7 +250,6 @@ static uint64_t file_system_count(const char *name) */ static uint64_t exclude_non_seclabel_mounts(void) { - struct utsname uts; FILE *fp; size_t len; int index = 0, found = 0; @@ -247,7 +258,7 @@ static uint64_t exclude_non_seclabel_mounts(void) char *buf = NULL, *item; /* Check to see if the kernel supports seclabel */ - if (uname(&uts) == 0 && strverscmp(uts.release, "2.6.30") < 0) + if (strverscmp(uts.release, "2.6.30") < 0) return 0; if (is_selinux_enabled() <= 0) return 0; @@ -648,9 +659,19 @@ static int restorecon_sb(const char *pathname, struct rest_flags *flags, bool fi if (fd < 0) goto err; + /* + * fstat(2) on file descriptors obtained via O_PATH are supported + * since Linux 3.6, see man:open(2). + * Test fstat(2) first, support might have been backported. + */ rc = fstat(fd, &stat_buf); - if (rc < 0) - goto err; + if (rc < 0) { + if (errno == EBADF && strverscmp(uts.release, "3.6") < 0) + rc = lstat(pathname, &stat_buf); + + if (rc < 0) + goto err; + } if (rootpath != NULL && lookup_path[0] == '\0') /* this is actually the root dir of the alt root. */ -- 2.36.1