Re: [PATCH 1/2] libselinux: selinux_restorecon: fix error message on intermediate symlinks

James Carter <[email protected]>
Newsgroups org.kernel.vger.selinux
Message-ID <CAP+JOzQjGEDuxcS8BAzbCvUNzisDUF9KMsQv=Ay99DB8_NQTLg@mail.gmail.com>
On Fri, Aug 14, 2026 at 9:35 AM Stephen Smalley
<[email protected]> wrote:
>
> selinux_restorecon(3) was changed by commit 67bc978bfaf9 ("libselinux:
> restorecon: revisit pinning files to avoid TOCTOU issues") to avoid
> following intermediate symlinks on the caller-supplied pathname unless
> the SELINUX_RESTORECON_REALPATH flag was set in the restorecon_flags
> parameter. However, because it calls openat(2) with both O_NOFOLLOW
> and O_DIRECTORY, the errno was being set to ENOTDIR and a message was
> logged with the pathname being "Not a directory" even if the terminal
> node of the path was in fact a directory. Fix the error handling logic
> and log message to correctly reflect the intermediate symlink-induced
> failure.
>
> Reported-by: Krzesimir Nowak <[email protected]>
> Fixes: 67bc978bfaf9 ("libselinux: restorecon: revisit pinning files to avoid TOCTOU issues")
> Fixes: https://github.com/SELinuxProject/selinux/issues/540
> Signed-off-by: Stephen Smalley <[email protected]>

For these two patches:
Acked-by: James Carter <[email protected]>

> ---
>  libselinux/src/selinux_restorecon.c | 31 ++++++++++++++++++++++++++---
>  1 file changed, 28 insertions(+), 3 deletions(-)
>
> diff --git a/libselinux/src/selinux_restorecon.c b/libselinux/src/selinux_restorecon.c
> index 49b8ff7e..fbadf367 100644
> --- a/libselinux/src/selinux_restorecon.c
> +++ b/libselinux/src/selinux_restorecon.c
> @@ -1167,11 +1167,30 @@ static int safe_open(const char *path, struct stat *sb)
>                                 nfd = openat(dfd, cur,
>                                              O_PATH | O_NOFOLLOW | O_DIRECTORY |
>                                                      O_CLOEXEC);
> -                               close(dfd);
>                                 if (nfd < 0) {
> +                                       int err = errno;
> +                                       struct stat st;
> +
> +                                       /*
> +                                        * O_NOFOLLOW | O_DIRECTORY on a
> +                                        * symlink yields ENOTDIR; report
> +                                        * ELOOP so the caller can tell an
> +                                        * unfollowed intermediate symlink
> +                                        * from a genuine non-directory, and
> +                                        * so the fallback matches openat2's
> +                                        * RESOLVE_NO_SYMLINKS.
> +                                        */
> +                                       if (err == ENOTDIR &&
> +                                           fstatat(dfd, cur, &st,
> +                                                   AT_SYMLINK_NOFOLLOW) == 0 &&
> +                                           S_ISLNK(st.st_mode))
> +                                               err = ELOOP;
> +                                       close(dfd);
>                                         free(copy);
> +                                       errno = err;
>                                         return -1;
>                                 }
> +                               close(dfd);
>                                 dfd = nfd;
>                                 cur = next;
>                                 continue;
> @@ -1680,8 +1699,14 @@ static int selinux_restorecon_common(const char *pathname_orig,
>                         free(pathname);
>                         return 0;
>                 } else {
> -                       selinux_log(SELINUX_ERROR, "open(%s) failed: %m\n",
> -                                   pathname);
> +                       if (errno == ELOOP)
> +                               selinux_log(
> +                                       SELINUX_ERROR,
> +                                       "%s: an intermediate path component is a symbolic link (not followed)\n",
> +                                       pathname);
> +                       else
> +                               selinux_log(SELINUX_ERROR,
> +                                           "open(%s) failed: %m\n", pathname);
>                         error = -1;
>                         goto cleanup;
>                 }
> --
> 2.55.0
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.