From: Stephen Smalley <stephen.smalley.work@gmail.com>
To: Rahul Sandhu <nvraxn@gmail.com>
Cc: jwcart2@gmail.com, lautrbach@redhat.com, selinux@vger.kernel.org
Subject: Re: [PATCH v7] seunshare: fix the frail tmpdir cleanup
Date: Tue, 7 Oct 2025 14:20:41 -0400 [thread overview]
Message-ID: <CAEjxPJ6rpJNpFviGHDiJsx_A_APdMTOS9Nyr0mcfT5YCWUtfvQ@mail.gmail.com> (raw)
In-Reply-To: <20251007180906.507115-1-nvraxn@gmail.com>
On Tue, Oct 7, 2025 at 2:09 PM Rahul Sandhu <nvraxn@gmail.com> wrote:
>
> For some reason, rm is invoked via system (3) to cleanup the runtime
> temp directory. This really isn't all that robust, *especially* given
> that seunshare is supposed to be a security boundary. Instead do this
> using libc, the API designed to be used within C programs.
>
> Also make sure that we don't follow symbolic links; the input being
> deleted is untrusted, and hence a malicious symbolic link may be placed
> outside of the sandbox.
>
> Signed-off-by: Rahul Sandhu <nvraxn@gmail.com>
7th time's the charm ;)
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
> ---
> sandbox/seunshare.c | 78 ++++++++++++++++++++++++++++++++++++++-------
> 1 file changed, 66 insertions(+), 12 deletions(-)
>
> v2: don't use else after return
> v3: don't follow symlinks in rm_rf (). This is pretty important as we
> we are operating on an untrusted directory, which may have symlinks
> pointed to privileged content. However, as we only really need to
> operate on the contents of the tmpdir, we can ignore symlinks.
> v4: fix spelling in commit message
> v5: fix spelling in comment
> v6: fix the error checking for the rm_rf () function.
> v7: include stdbool.h
>
> diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
> index 106f625f..01ed9d8e 100644
> --- a/sandbox/seunshare.c
> +++ b/sandbox/seunshare.c
> @@ -4,6 +4,7 @@
> */
>
> #define _GNU_SOURCE
> +#include <stdbool.h>
> #include <signal.h>
> #include <sys/fsuid.h>
> #include <sys/stat.h>
> @@ -403,6 +404,66 @@ err:
> return rc;
> }
>
> +/*
> + * Recursively delete a directory.
> + * SAFETY: This function will NOT follow symbolic links (AT_SYMLINK_NOFOLLOW).
> + * As a result, this function can be run safely on a directory owned by
> + * a non-root user: symbolic links to root paths (such as /root) will
> + * not be followed.
> + */
> +static bool rm_rf(int targetfd, const char *path) {
> + struct stat statbuf;
> +
> + if (fstatat(targetfd, path, &statbuf, AT_SYMLINK_NOFOLLOW) < 0) {
> + if (errno == ENOENT) {
> + return true;
> + }
> + perror("fstatat");
> + return false;
> + }
> +
> + if (S_ISDIR(statbuf.st_mode)) {
> + const int newfd = openat(targetfd, path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
> + if (newfd < 0) {
> + perror("openat");
> + return false;
> + }
> +
> + DIR *dir = fdopendir(newfd);
> + if (!dir) {
> + perror("fdopendir");
> + close(newfd);
> + return false;
> + }
> +
> + struct dirent *entry;
> + int rc = true;
> + while ((entry = readdir(dir)) != NULL) {
> + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
> + continue;
> + }
> +
> + if (!rm_rf(dirfd(dir), entry->d_name)) {
> + rc = false;
> + }
> + }
> +
> + closedir(dir);
> +
> + if (unlinkat(targetfd, path, AT_REMOVEDIR) < 0) {
> + perror("unlinkat");
> + rc = false;
> + }
> +
> + return rc;
> + }
> + if (unlinkat(targetfd, path, 0) < 0) {
> + perror("unlinkat");
> + return false;
> + }
> + return true;
> +}
> +
> /**
> * Clean up runtime temporary directory. Returns 0 if no problem was detected,
> * >0 if some error was detected, but errors here are treated as non-fatal and
> @@ -428,24 +489,17 @@ static int cleanup_tmpdir(const char *tmpdir, const char *src,
> free(cmdbuf); cmdbuf = NULL;
> }
>
> - /* remove files from the runtime temporary directory */
> - if (asprintf(&cmdbuf, "/bin/rm -r '%s/' 2>/dev/null", tmpdir) == -1) {
> - fprintf(stderr, _("Out of memory\n"));
> - cmdbuf = NULL;
> + if ((uid_t)setfsuid(0) != 0) {
> + /* setfsuid does not return error, but this check makes code checkers happy */
> rc++;
> }
> - /* this may fail if there's root-owned file left in the runtime tmpdir */
> - if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) rc++;
> - free(cmdbuf); cmdbuf = NULL;
>
> - /* remove runtime temporary directory */
> - if ((uid_t)setfsuid(0) != 0) {
> - /* setfsuid does not return error, but this check makes code checkers happy */
> + /* Recursively remove the runtime temp directory. */
> + if (!rm_rf(AT_FDCWD, tmpdir)) {
> + fprintf(stderr, _("Failed to recursively remove directory %s\n"), tmpdir);
> rc++;
> }
>
> - if (pwd->pw_uid != 0 && rmdir(tmpdir) == -1)
> - fprintf(stderr, _("Failed to remove directory %s: %s\n"), tmpdir, strerror(errno));
> if ((uid_t)setfsuid(pwd->pw_uid) != 0) {
> fprintf(stderr, _("unable to switch back to user after clearing tmp dir\n"));
> rc++;
> --
> 2.50.1
>
next prev parent reply other threads:[~2025-10-07 18:20 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <CAEjxPJ4wxMi0kXc7wDi qwboAcz1Y0UvzDoZMZrpUgcNH_cNRg@mail.gmail.com>
2025-10-07 17:02 ` [PATCH v5] seunshare: fix the frail tmpdir cleanup Rahul Sandhu
2025-10-07 17:06 ` [PATCH v6] " Rahul Sandhu
2025-10-07 17:42 ` Stephen Smalley
2025-10-07 18:01 ` Stephen Smalley
2025-10-07 18:09 ` [PATCH v7] " Rahul Sandhu
2025-10-07 18:20 ` Stephen Smalley [this message]
2025-10-08 13:16 ` Stephen Smalley
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=CAEjxPJ6rpJNpFviGHDiJsx_A_APdMTOS9Nyr0mcfT5YCWUtfvQ@mail.gmail.com \
--to=stephen.smalley.work@gmail.com \
--cc=jwcart2@gmail.com \
--cc=lautrbach@redhat.com \
--cc=nvraxn@gmail.com \
--cc=selinux@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).