policycoreutils-3.6-9

- Several sandbox and seunshare security improvements
- https://github.com/SELinuxProject/selinux/security/advisories/GHSA-h4m3-rc99-c7m5

Resolves: RHEL-212364
This commit is contained in:
Petr Lautrbach 2026-08-04 17:19:57 +02:00
parent 4dab9f244f
commit 400e2f2208
20 changed files with 1947 additions and 1 deletions

View File

@ -0,0 +1,232 @@
From 78af7de720215c5697f4303e7317a104fe088bf4 Mon Sep 17 00:00:00 2001
From: Petr Lautrbach <lautrbach@redhat.com>
Date: Tue, 14 May 2024 12:56:50 +0200
Subject: [PATCH] seunshare: Add [ -P pipewiresocket ] [ -W waylandsocket ]
options
Content-type: text/plain
Mount /run/user/UID/<waylandsocket> or /run/user/UID/<pipewiresocket>
inside unshared /run/user/UID directory
Signed-off-by: Petr Lautrbach <lautrbach@redhat.com>
---
sandbox/seunshare.c | 120 +++++++++++++++++++++++++++++++++++++++++---
1 file changed, 113 insertions(+), 7 deletions(-)
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index b49fc2cd8050..2512a18c7b52 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -53,7 +53,8 @@
#define BUF_SIZE 1024
#define DEFAULT_PATH "/usr/bin:/bin"
-#define USAGE_STRING _("USAGE: seunshare [ -v ] [ -C ] [ -k ] [ -t tmpdir ] [ -h homedir ] [ -r runuserdir ] [ -Z CONTEXT ] -- executable [args] ")
+#define USAGE_STRING _("USAGE: seunshare [ -v ] [ -C ] [ -k ] [ -t tmpdir ] [ -h homedir ] \
+[ -r runuserdir ] [ -P pipewiresocket ] [ -W waylandsocket ] [ -Z CONTEXT ] -- executable [args] ")
#define strdup_or_err(args, index, src) do { \
args[index] = strdup(src); \
@@ -270,6 +271,10 @@ static int seunshare_mount(const char *src, const char *dst, struct stat *src_st
is_tmp = 1;
}
+ if (strncmp("/run/user", dst, 9) == 0) {
+ flags = flags | MS_REC;
+ }
+
/* mount directory */
if (mount(src, dst, NULL, MS_BIND | flags, NULL) < 0) {
fprintf(stderr, _("Failed to mount %s on %s: %s\n"), src, dst, strerror(errno));
@@ -294,6 +299,31 @@ static int seunshare_mount(const char *src, const char *dst, struct stat *src_st
}
+/**
+ * Mount directory and check that we mounted the right directory.
+ */
+static int seunshare_mount_file(const char *src, const char *dst)
+{
+ int flags = 0;
+
+ if (verbose)
+ printf(_("Mounting %s on %s\n"), src, dst);
+
+ if (access(dst, F_OK) == -1) {
+ FILE *fptr;
+ fptr = fopen(dst, "w");
+ fclose(fptr);
+ }
+ /* mount file */
+ if (mount(src, dst, NULL, MS_BIND | flags, NULL) < 0) {
+ fprintf(stderr, _("Failed to mount %s on %s: %s\n"), src, dst, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+
+}
+
/*
If path is empty or ends with "/." or "/.. return -1 else return 0;
*/
@@ -696,6 +726,8 @@ killall (const char *execcon)
int main(int argc, char **argv) {
int status = -1;
const char *execcon = NULL;
+ const char *pipewire_socket = NULL;
+ const char *wayland_display = NULL;
int clflag; /* holds codes for command line flags */
int kill_all = 0;
@@ -721,6 +753,8 @@ int main(int argc, char **argv) {
{"verbose", 1, 0, 'v'},
{"context", 1, 0, 'Z'},
{"capabilities", 1, 0, 'C'},
+ {"wayland", 1, 0, 'W'},
+ {"pipewire", 1, 0, 'P'},
{NULL, 0, 0, 0}
};
@@ -750,7 +784,7 @@ int main(int argc, char **argv) {
}
while (1) {
- clflag = getopt_long(argc, argv, "Ccvh:r:t:Z:", long_options, NULL);
+ clflag = getopt_long(argc, argv, "Ccvh:r:t:W:Z:", long_options, NULL);
if (clflag == -1)
break;
@@ -773,6 +807,12 @@ int main(int argc, char **argv) {
case 'C':
cap_set = CAPNG_SELECT_CAPS;
break;
+ case 'P':
+ pipewire_socket = optarg;
+ break;
+ case 'W':
+ wayland_display = optarg;
+ break;
case 'Z':
execcon = optarg;
break;
@@ -847,8 +887,14 @@ int main(int argc, char **argv) {
char *display = NULL;
char *LANG = NULL;
char *RUNTIME_DIR = NULL;
+ char *XDG_SESSION_TYPE = NULL;
int rc = -1;
char *resolved_path = NULL;
+ char *wayland_path_s = NULL; /* /tmp/.../wayland-0 */
+ char *wayland_path = NULL; /* /run/user/UID/wayland-0 */
+ char *pipewire_path_s = NULL; /* /tmp/.../pipewire-0 */
+ char *pipewire_path = NULL; /* /run/user/UID/pipewire-0 */
+
if (unshare(CLONE_NEWNS) < 0) {
perror(_("Failed to unshare"));
@@ -885,6 +931,42 @@ int main(int argc, char **argv) {
}
}
+ if ((XDG_SESSION_TYPE = getenv("XDG_SESSION_TYPE")) != NULL) {
+ if ((XDG_SESSION_TYPE = strdup(XDG_SESSION_TYPE)) == NULL) {
+ perror(_("Out of memory"));
+ goto childerr;
+ }
+ }
+
+ if (runuserdir_s && (wayland_display || pipewire_socket)) {
+ if (wayland_display) {
+ if (asprintf(&wayland_path_s, "%s/%s", runuserdir_s, wayland_display) == -1) {
+ perror(_("Out of memory"));
+ goto childerr;
+ }
+
+ if (asprintf(&wayland_path, "%s/%s", RUNTIME_DIR, wayland_display) == -1) {
+ perror(_("Out of memory"));
+ goto childerr;
+ }
+
+ if (seunshare_mount_file(wayland_path, wayland_path_s) == -1)
+ goto childerr;
+ }
+
+ if (pipewire_socket) {
+ if (asprintf(&pipewire_path_s, "%s/%s", runuserdir_s, pipewire_socket) == -1) {
+ perror(_("Out of memory"));
+ goto childerr;
+ }
+ if (asprintf(&pipewire_path, "%s/pipewire-0", RUNTIME_DIR) == -1) {
+ perror(_("Out of memory"));
+ goto childerr;
+ }
+ seunshare_mount_file(pipewire_path, pipewire_path_s);
+ }
+ }
+
/* mount homedir, runuserdir and tmpdir, in this order */
if (runuserdir_s && seunshare_mount(runuserdir_s, RUNTIME_DIR,
&st_runuserdir_s) != 0) goto childerr;
@@ -896,10 +978,21 @@ int main(int argc, char **argv) {
if (drop_privs(uid) != 0) goto childerr;
/* construct a new environment */
- if ((display = getenv("DISPLAY")) != NULL) {
- if ((display = strdup(display)) == NULL) {
- perror(_("Out of memory"));
- goto childerr;
+
+ if (XDG_SESSION_TYPE && strcmp(XDG_SESSION_TYPE, "wayland") == 0) {
+ if (wayland_display == NULL && (wayland_display = getenv("WAYLAND_DISPLAY")) != NULL) {
+ if ((wayland_display = strdup(wayland_display)) == NULL) {
+ perror(_("Out of memory"));
+ goto childerr;
+ }
+ }
+ }
+ else {
+ if ((display = getenv("DISPLAY")) != NULL) {
+ if ((display = strdup(display)) == NULL) {
+ perror(_("Out of memory"));
+ goto childerr;
+ }
}
}
@@ -915,8 +1008,16 @@ int main(int argc, char **argv) {
perror(_("Failed to clear environment"));
goto childerr;
}
- if (display)
+ if (display) {
rc |= setenv("DISPLAY", display, 1);
+ }
+ if (wayland_display) {
+ rc |= setenv("WAYLAND_DISPLAY", wayland_display, 1);
+ }
+
+ if (XDG_SESSION_TYPE)
+ rc |= setenv("XDG_SESSION_TYPE", XDG_SESSION_TYPE, 1);
+
if (LANG)
rc |= setenv("LANG", LANG, 1);
if (RUNTIME_DIR)
@@ -954,9 +1055,14 @@ int main(int argc, char **argv) {
fprintf(stderr, _("Failed to execute command %s: %s\n"), argv[optind], strerror(errno));
childerr:
free(resolved_path);
+ free(wayland_path);
+ free(wayland_path_s);
+ free(pipewire_path);
+ free(pipewire_path_s);
free(display);
free(LANG);
free(RUNTIME_DIR);
+ free(XDG_SESSION_TYPE);
exit(-1);
}
--
2.55.0

View File

@ -0,0 +1,63 @@
From 53a21672ecff0f4c55956d7594194703c4e2bceb Mon Sep 17 00:00:00 2001
From: Rahul Sandhu <nvraxn@posteo.uk>
Date: Tue, 14 Apr 2026 18:40:00 +0000
Subject: [PATCH] seunshare: guard fallible function calls by checking retval
Content-type: text/plain
seunshare currently segfaults is passed --kill and an invalid context
via -Z.
Signed-off-by: Rahul Sandhu <nvraxn@posteo.uk>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
---
sandbox/seunshare.c | 26 +++++++++++++++++++-------
1 file changed, 19 insertions(+), 7 deletions(-)
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index 2512a18c7b52..e89a9fecf9f6 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -679,9 +679,19 @@ killall (const char *execcon)
return -1;
}
pids = 0;
- context_t con;
- con = context_new(execcon);
- const char *mcs = context_range_get(con);
+ context_t con = context_new(execcon);
+ if (!con) {
+ free(pid_table);
+ (void)closedir(dir);
+ return -1;
+ }
+ const char *const mcs = context_range_get(con);
+ if (!mcs) {
+ context_free(con);
+ free(pid_table);
+ (void)closedir(dir);
+ return -1;
+ }
printf("mcs=%s\n", mcs);
while ((de = readdir (dir)) != NULL) {
if (!(pid = (pid_t)atoi(de->d_name)) || pid == self)
@@ -708,11 +718,13 @@ killall (const char *execcon)
if (getpidcon(id, &scon) == 0) {
context_t pidcon = context_new(scon);
- /* Attempt to kill remaining processes */
- if (strcmp(context_range_get(pidcon), mcs) == 0)
- kill(id, SIGKILL);
+ if (pidcon) {
+ /* Attempt to kill remaining processes */
+ if (strcmp(context_range_get(pidcon), mcs) == 0)
+ kill(id, SIGKILL);
- context_free(pidcon);
+ context_free(pidcon);
+ }
freecon(scon);
}
running++;
--
2.55.0

View File

@ -0,0 +1,30 @@
From 42b2ea57ba96d1e4f25d6b43f3ecac29e6780ff6 Mon Sep 17 00:00:00 2001
From: Stephen Smalley <stephen.smalley.work@gmail.com>
Date: Tue, 12 May 2026 14:34:02 -0400
Subject: [PATCH] sandbox/seunshare: pass O_NOFOLLOW to openat()
Content-type: text/plain
We do not want to follow symlinks here.
Acked-by: James Carter <jwcart2@gmail.com>
Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
---
sandbox/seunshare.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index e89a9fecf9f6..a1900eaa7600 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -428,7 +428,7 @@ static bool rm_rf(int targetfd, const char *path) {
}
if (S_ISDIR(statbuf.st_mode)) {
- const int newfd = openat(targetfd, path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+ const int newfd = openat(targetfd, path, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
if (newfd < 0) {
perror("openat");
return false;
--
2.55.0

View File

@ -0,0 +1,52 @@
From 28fb4e58c3b51b55227596fb2b1d90a5a2f7fa1f Mon Sep 17 00:00:00 2001
From: Stephen Smalley <stephen.smalley.work@gmail.com>
Date: Tue, 12 May 2026 15:26:31 -0400
Subject: [PATCH] sandbox/seunshare: switch seunshare_mount_file() to use
open()
Content-type: text/plain
seunshare_mount_file() currently uses fopen() to create the dst
if it doesn't already exist. Switch to using open() with
explicitly specified flags including O_NOFOLLOW and an explicitly
specified mode for the new file.
Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Acked-by: Petr Lautrbach <lautrbach@redhat.com>
---
sandbox/seunshare.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index a1900eaa7600..17a727e78b5f 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -304,18 +304,20 @@ static int seunshare_mount(const char *src, const char *dst, struct stat *src_st
*/
static int seunshare_mount_file(const char *src, const char *dst)
{
- int flags = 0;
-
if (verbose)
printf(_("Mounting %s on %s\n"), src, dst);
if (access(dst, F_OK) == -1) {
- FILE *fptr;
- fptr = fopen(dst, "w");
- fclose(fptr);
+ int fd = open(dst, O_WRONLY | O_CREAT | O_NOFOLLOW | O_CLOEXEC, 0600);
+ if (fd < 0) {
+ fprintf(stderr, _("Failed to create mount point %s: %m\n"), dst);
+ return -1;
+ }
+ close(fd);
}
+
/* mount file */
- if (mount(src, dst, NULL, MS_BIND | flags, NULL) < 0) {
+ if (mount(src, dst, NULL, MS_BIND, NULL) < 0) {
fprintf(stderr, _("Failed to mount %s on %s: %s\n"), src, dst, strerror(errno));
return -1;
}
--
2.55.0

View File

@ -0,0 +1,33 @@
From db3b5ed011a2a675971e9416825f80359f97e4b8 Mon Sep 17 00:00:00 2001
From: Stephen Smalley <stephen.smalley.work@gmail.com>
Date: Tue, 12 May 2026 15:35:53 -0400
Subject: [PATCH] sandbox/seunshare: fix error checking for setfsuid()
Content-type: text/plain
setfsuid() doesn't reliably set errno or return anything indicating
an error.
Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Acked-by: Petr Lautrbach <lautrbach@redhat.com>
---
sandbox/seunshare.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index 17a727e78b5f..b9c85bf20f85 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -858,8 +858,8 @@ int main(int argc, char **argv) {
/* Changing fsuid is usually required when user-specified directory is
* on an NFS mount. It's also desired to avoid leaking info about
* existence of the files not accessible to the user. */
- if (((uid_t)setfsuid(uid) != 0) && (errno != 0)) {
- fprintf(stderr, _("Error: unable to setfsuid %m\n"));
+ if ((uid_t)setfsuid(uid) != 0) {
+ fprintf(stderr, _("Error: unable to setfsuid\n"));
return -1;
}
--
2.55.0

View File

@ -0,0 +1,86 @@
From d3734b8867f147e8aacd54f319fda357f7aaeed6 Mon Sep 17 00:00:00 2001
From: Stephen Smalley <stephen.smalley.work@gmail.com>
Date: Tue, 12 May 2026 16:06:05 -0400
Subject: [PATCH] sandbox/seunshare: remount /tmp and /var/tmp with the proper
flags
Content-type: text/plain
mount(2) with MS_BIND ignores any nosuid/nodev/noexec flags, so
seunshare_mount() was never setting those on the /tmp and
/var/tmp mounts. Fix seunshare_mount() to remount them
with those flags after the bind mount, which does
set them properly.
Test:
mkdir tmp
seunshare -t tmp /bin/bash
cp /bin/bash /tmp
/tmp/bash
Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Acked-by: Petr Lautrbach <lautrbach@redhat.com>
---
sandbox/seunshare.c | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index b9c85bf20f85..985e0cfba199 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -260,26 +260,32 @@ static int verify_shell(const char *shell_name)
*/
static int seunshare_mount(const char *src, const char *dst, struct stat *src_st)
{
- int flags = 0;
+ int bind_flags = MS_BIND;
+ int sec_flags = 0;
int is_tmp = 0;
if (verbose)
printf(_("Mounting %s on %s\n"), src, dst);
if (strcmp("/tmp", dst) == 0) {
- flags = flags | MS_NODEV | MS_NOSUID | MS_NOEXEC;
+ sec_flags = MS_NODEV | MS_NOSUID | MS_NOEXEC;
is_tmp = 1;
}
if (strncmp("/run/user", dst, 9) == 0) {
- flags = flags | MS_REC;
+ bind_flags |= MS_REC;
}
/* mount directory */
- if (mount(src, dst, NULL, MS_BIND | flags, NULL) < 0) {
+ if (mount(src, dst, NULL, bind_flags, NULL) < 0) {
fprintf(stderr, _("Failed to mount %s on %s: %s\n"), src, dst, strerror(errno));
return -1;
}
+ /* remount with security flags, ignored on original bind mount */
+ if (sec_flags && mount(NULL, dst, NULL, MS_BIND | MS_REMOUNT | sec_flags, NULL) < 0) {
+ fprintf(stderr, _("Failed to remount %s: %m\n"), dst);
+ return -1;
+ }
/* verify whether we mounted what we expected to mount */
if (verify_directory(dst, src_st, NULL) < 0) return -1;
@@ -289,10 +295,15 @@ static int seunshare_mount(const char *src, const char *dst, struct stat *src_st
if (verbose)
printf(_("Mounting /tmp on /var/tmp\n"));
- if (mount("/tmp", "/var/tmp", NULL, MS_BIND | flags, NULL) < 0) {
+ if (mount("/tmp", "/var/tmp", NULL, MS_BIND, NULL) < 0) {
fprintf(stderr, _("Failed to mount /tmp on /var/tmp: %s\n"), strerror(errno));
return -1;
}
+ /* remount with security flags, ignored on original bind mount */
+ if (mount(NULL, "/var/tmp", NULL, MS_BIND | MS_REMOUNT | sec_flags, NULL) < 0) {
+ fprintf(stderr, _("Failed to remount /var/tmp: %m\n"));
+ return -1;
+ }
}
return 0;
--
2.55.0

View File

@ -0,0 +1,75 @@
From 245f370d7c2b039fe7e971d0a418650a94eea847 Mon Sep 17 00:00:00 2001
From: Stephen Smalley <stephen.smalley.work@gmail.com>
Date: Wed, 13 May 2026 09:47:50 -0400
Subject: [PATCH] sandbox/seunshare: prevent rsync from interpreting paths as
options
Content-type: text/plain
Insert a "--" after all legitimate options before any glob or
pathnames to prevent rsync from interpreting them as options.
Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
---
sandbox/seunshare.c | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index 985e0cfba199..a48c88036088 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -391,8 +391,8 @@ static int rsynccmd(const char * src, const char *dst, char ***cmd) {
free(buf); buf = NULL;
- /* rsync -trlHDq + <glob list> + dst + NULL */
- *cmd = calloc(2 + fglob.gl_pathc + 2, sizeof(char *));
+ /* rsync -trlHDq -- <glob list> dst NULL */
+ *cmd = calloc(3 + fglob.gl_pathc + 2, sizeof(char *));
if (! *cmd) {
fprintf(stderr, _("Out of memory\n"));
return -1;
@@ -401,8 +401,9 @@ static int rsynccmd(const char * src, const char *dst, char ***cmd) {
args = *cmd;
strdup_or_err(args, 0, "/usr/bin/rsync");
strdup_or_err(args, 1, "-trlHDq");
+ strdup_or_err(args, 2, "--");
- for ( i=0, index = 2; i < fglob.gl_pathc; i++) {
+ for ( i=0, index = 3; i < fglob.gl_pathc; i++) {
const char *path = fglob.gl_pathv[i];
if (bad_path(path)) continue;
strdup_or_err(args, index, path);
@@ -495,7 +496,7 @@ static int cleanup_tmpdir(const char *tmpdir, const char *src,
/* rsync files back */
if (copy_content) {
- args = calloc(7, sizeof(char *));
+ args = calloc(8, sizeof(char *));
if (! args) {
fprintf(stderr, _("Out of memory\n"));
return 1;
@@ -505,17 +506,18 @@ static int cleanup_tmpdir(const char *tmpdir, const char *src,
strdup_or_err(args, 1, "--exclude=.X11-unix");
strdup_or_err(args, 2, "-utrlHDq");
strdup_or_err(args, 3, "--delete");
- if (asprintf(&args[4], "%s/", tmpdir) == -1) {
+ strdup_or_err(args, 4, "--");
+ if (asprintf(&args[5], "%s/", tmpdir) == -1) {
fprintf(stderr, _("Out of memory\n"));
free_args(args);
return 1;
}
- if (asprintf(&args[5], "%s/", src) == -1) {
+ if (asprintf(&args[6], "%s/", src) == -1) {
fprintf(stderr, _("Out of memory\n"));
free_args(args);
return 1;
}
- args[6] = NULL;
+ args[7] = NULL;
if (spawn_command(args, pwd->pw_uid) != 0) {
fprintf(stderr, _("Failed to copy files from the runtime temporary directory\n"));
--
2.55.0

View File

@ -0,0 +1,44 @@
From 59013df444e0806d614b327ce5579ae4c1c7e285 Mon Sep 17 00:00:00 2001
From: Stephen Smalley <stephen.smalley.work@gmail.com>
Date: Wed, 13 May 2026 09:59:08 -0400
Subject: [PATCH] sandbox/seunshare: fix getopt flags
Content-type: text/plain
-k, -v, and -C do NOT accept an argument, and the optstring
was not correct.
Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
---
sandbox/seunshare.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index a48c88036088..0dfc5cb2b820 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -776,10 +776,10 @@ int main(int argc, char **argv) {
{"homedir", 1, 0, 'h'},
{"tmpdir", 1, 0, 't'},
{"runuserdir", 1, 0, 'r'},
- {"kill", 1, 0, 'k'},
- {"verbose", 1, 0, 'v'},
+ {"kill", 0, 0, 'k'},
+ {"verbose", 0, 0, 'v'},
{"context", 1, 0, 'Z'},
- {"capabilities", 1, 0, 'C'},
+ {"capabilities", 0, 0, 'C'},
{"wayland", 1, 0, 'W'},
{"pipewire", 1, 0, 'P'},
{NULL, 0, 0, 0}
@@ -811,7 +811,7 @@ int main(int argc, char **argv) {
}
while (1) {
- clflag = getopt_long(argc, argv, "Ccvh:r:t:W:Z:", long_options, NULL);
+ clflag = getopt_long(argc, argv, "Ckvh:r:t:W:P:Z:", long_options, NULL);
if (clflag == -1)
break;
--
2.55.0

View File

@ -0,0 +1,47 @@
From d7335b75dccf033a56c34b99ee82d70c43057b5b Mon Sep 17 00:00:00 2001
From: Stephen Smalley <stephen.smalley.work@gmail.com>
Date: Wed, 13 May 2026 10:13:17 -0400
Subject: [PATCH] sandbox/seunshare: prevent path traversal via -W/-P
Content-type: text/plain
The -W/-P options specify the wayland or pipewire socket name respectively.
The option argument is then concatenated with the runuserdir to create
a pathname. We don't want to allow it to be used to construct arbitrary
paths outside of the runuserdir. Also, check and handle
errors when bind mounting the pipewire socket file.
Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
---
sandbox/seunshare.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index 0dfc5cb2b820..945a3b21c1cc 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -849,6 +849,12 @@ int main(int argc, char **argv) {
}
}
+ if ((wayland_display && (strchr(wayland_display, '/') || strstr(wayland_display, ".."))) ||
+ (pipewire_socket && (strchr(pipewire_socket, '/') || strstr(pipewire_socket, "..")))) {
+ fprintf(stderr, _("Error: -W/-P must be a socket name, not a path\n"));
+ return -1;
+ }
+
if (! homedir_s && ! tmpdir_s) {
fprintf(stderr, _("Error: tmpdir and/or homedir required\n %s\n"), USAGE_STRING);
return -1;
@@ -990,7 +996,8 @@ int main(int argc, char **argv) {
perror(_("Out of memory"));
goto childerr;
}
- seunshare_mount_file(pipewire_path, pipewire_path_s);
+ if (seunshare_mount_file(pipewire_path, pipewire_path_s) == -1)
+ goto childerr;
}
}
--
2.55.0

View File

@ -0,0 +1,38 @@
From 89acb9dc3f7648b75583b0fb724fbfa00724ba08 Mon Sep 17 00:00:00 2001
From: Stephen Smalley <stephen.smalley.work@gmail.com>
Date: Wed, 13 May 2026 10:30:20 -0400
Subject: [PATCH] sandbox/seunshare: verify RUNTIME_DIR before use
Content-type: text/plain
RUNTIME_DIR can be inherited from XDG_RUNTIME_DIR or set to a default
path. Regardless, we should verify it the same way as the other
user-supplied directories before first use.
Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
---
sandbox/seunshare.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index 945a3b21c1cc..89180d0aa1ed 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -964,6 +964,15 @@ int main(int argc, char **argv) {
}
}
+ if (runuserdir_s) {
+ struct stat sb;
+
+ if (verify_directory(RUNTIME_DIR, NULL, &sb) < 0 ||
+ check_owner_uid(uid, RUNTIME_DIR, &sb) < 0)
+ goto childerr;
+
+ }
+
if ((XDG_SESSION_TYPE = getenv("XDG_SESSION_TYPE")) != NULL) {
if ((XDG_SESSION_TYPE = strdup(XDG_SESSION_TYPE)) == NULL) {
perror(_("Out of memory"));
--
2.55.0

View File

@ -0,0 +1,48 @@
From 7ed6a7aef9c5aa878d0f4edd904585f52df0b265 Mon Sep 17 00:00:00 2001
From: Stephen Smalley <stephen.smalley.work@gmail.com>
Date: Wed, 13 May 2026 10:37:56 -0400
Subject: [PATCH] sandbox/seunshare: drop unused runuserdir_r
Content-type: text/plain
runuserdir_r is created but never used nor deleted. Get rid of it.
Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
---
sandbox/seunshare.c | 8 --------
1 file changed, 8 deletions(-)
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index 89180d0aa1ed..2eef0e2f800e 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -763,14 +763,12 @@ int main(int argc, char **argv) {
char *tmpdir_s = NULL; /* tmpdir spec'd by user in argv[] */
char *tmpdir_r = NULL; /* tmpdir created by seunshare */
char *runuserdir_s = NULL; /* /var/run/user/UID spec'd by user in argv[] */
- char *runuserdir_r = NULL; /* /var/run/user/UID created by seunshare */
struct stat st_curhomedir;
struct stat st_homedir;
struct stat st_tmpdir_s;
struct stat st_tmpdir_r;
struct stat st_runuserdir_s;
- struct stat st_runuserdir_r;
const struct option long_options[] = {
{"homedir", 1, 0, 'h'},
@@ -902,12 +900,6 @@ int main(int argc, char **argv) {
fprintf(stderr, _("Failed to create runtime temporary directory\n"));
return -1;
}
- /* create runtime runuserdir */
- if (runuserdir_s && (runuserdir_r = create_tmpdir(runuserdir_s, &st_runuserdir_s,
- &st_runuserdir_r, pwd, execcon)) == NULL) {
- fprintf(stderr, _("Failed to create runtime $XDG_RUNTIME_DIR directory\n"));
- return -1;
- }
/* spawn child process */
child = fork();
--
2.55.0

View File

@ -0,0 +1,95 @@
From 549aa82c2ac5827283dcbecd697030889fa10be0 Mon Sep 17 00:00:00 2001
From: Stephen Smalley <stephen.smalley.work@gmail.com>
Date: Wed, 13 May 2026 12:21:47 -0400
Subject: [PATCH] sandbox/seunshare: fix killall() realloc and missing type
comparison
Content-type: text/plain
The killall() realloc() can produce an integer overflow.
Check and handle this correctly.
The killall() logic also only compares the MCS category set when
deciding whether to kill the process. We should at least also compare
the type to avoid incorrectly killing an unrelated process with
the same category set (e.g. if multiple applications are independently
assigning category sets on the same system). Check the type as well.
killall() still seems error prone and I couldn't find any actual users
of the -k/--kill option for seunshare. Can we just drop this?
Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
---
sandbox/seunshare.c | 26 +++++++++++++++++++-------
1 file changed, 19 insertions(+), 7 deletions(-)
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index 2eef0e2f800e..e20c0f8723aa 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -680,8 +680,8 @@ killall (const char *execcon)
char *scon;
struct dirent *de;
pid_t *pid_table, pid, self;
- int i;
- int pids, max_pids;
+ unsigned int i;
+ unsigned int pids, max_pids;
int running = 0;
self = getpid();
if (!(dir = opendir(PROC_BASE))) {
@@ -701,26 +701,34 @@ killall (const char *execcon)
return -1;
}
const char *const mcs = context_range_get(con);
- if (!mcs) {
+ const char *const type = context_type_get(con);
+ if (!mcs || !type) {
context_free(con);
free(pid_table);
(void)closedir(dir);
return -1;
}
- printf("mcs=%s\n", mcs);
+ if (verbose)
+ printf("mcs=%s type=%s\n", mcs, type);
while ((de = readdir (dir)) != NULL) {
if (!(pid = (pid_t)atoi(de->d_name)) || pid == self)
continue;
if (pids == max_pids) {
- pid_t *new_pid_table = realloc(pid_table, 2*pids*sizeof(pid_t));
+ max_pids *= 2;
+ if (max_pids <= pids)
+ {
+ free(pid_table);
+ (void)closedir(dir);
+ return -1;
+ }
+ pid_t *new_pid_table = reallocarray(pid_table, max_pids, sizeof(pid_t));
if (!new_pid_table) {
free(pid_table);
(void)closedir(dir);
return -1;
}
pid_table = new_pid_table;
- max_pids *= 2;
}
pid_table[pids++] = pid;
}
@@ -734,8 +742,12 @@ killall (const char *execcon)
context_t pidcon = context_new(scon);
if (pidcon) {
+ const char *const pmcs = context_range_get(pidcon);
+ const char *const ptype = context_type_get(pidcon);
+
/* Attempt to kill remaining processes */
- if (strcmp(context_range_get(pidcon), mcs) == 0)
+ if (pmcs && ptype && !strcmp(pmcs, mcs) &&
+ !strcmp(ptype, type))
kill(id, SIGKILL);
context_free(pidcon);
--
2.55.0

View File

@ -0,0 +1,606 @@
From 9906f53d850fe4a1ec272bb90eaae952b9728023 Mon Sep 17 00:00:00 2001
From: Stephen Smalley <stephen.smalley.work@gmail.com>
Date: Thu, 14 May 2026 10:32:13 -0400
Subject: [PATCH] sandbox/seunshare: rewrite to pin directories before use
Content-type: text/plain
To fully eliminate TOCTOU issues in seunshare, we need to pin the
directories and pass them via /proc/self/fd/N to mount(2).
This is complicated further by the unsharing of the mount namespace
in the child process and the need to remount in order to apply
nosuid/nodev/noexec flags. Do the best we can with the legacy
mount(2) API for compatibility on old kernels and revisit if/when
we make kernels with the new mount API the minimum required for
SELinux userspace.
Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
---
sandbox/seunshare.c | 406 ++++++++++++++++++++++++++------------------
1 file changed, 241 insertions(+), 165 deletions(-)
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index e20c0f8723aa..611bfe80d030 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -197,35 +197,33 @@ static int check_owner_gid(gid_t gid, const char *file, struct stat *st) {
return 0;
}
-#define equal_stats(one,two) \
- ((one)->st_dev == (two)->st_dev && (one)->st_ino == (two)->st_ino && \
- (one)->st_uid == (two)->st_uid && (one)->st_gid == (two)->st_gid && \
- (one)->st_mode == (two)->st_mode)
-
/**
- * Sanity check specified directory. Store stat info for future comparison, or
- * compare with previously saved info to detect replaced directories.
- * Note: This function does not perform owner checks.
+ * Open directory with O_DIRECTORY|O_NOFOLLOW and return its fd
+ * and fstat() results. The returned fd and its /proc/self/fd/N
+ * path can be used for all subsequent operations on the directory.
+ * NB Any non-final components in the @dir pathname are resolved
+ * as usual but will be checked against the fsuid of the caller.
*/
-static int verify_directory(const char *dir, struct stat *st_in, struct stat *st_out) {
+static int pin_dir(const char *dir, struct stat *st_out)
+{
+ int fd;
struct stat sb;
- if (st_out == NULL) st_out = &sb;
-
- if (lstat(dir, st_out) == -1) {
- fprintf(stderr, _("Failed to stat %s: %s\n"), dir, strerror(errno));
+ fd = open(dir, O_RDONLY|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
+ if (fd < 0) {
+ fprintf(stderr, _("Failed to open %s: %m\n"), dir);
return -1;
}
- if (! S_ISDIR(st_out->st_mode)) {
- fprintf(stderr, _("Error: %s is not a directory: %s\n"), dir, strerror(errno));
- return -1;
- }
- if (st_in && !equal_stats(st_in, st_out)) {
- fprintf(stderr, _("Error: %s was replaced by a different directory\n"), dir);
+
+ if (fstat(fd, &sb) < 0) {
+ fprintf(stderr, _("Failed to stat %s: %m\n"), dir);
+ close(fd);
return -1;
}
- return 0;
+ if (st_out)
+ *st_out = sb;
+ return fd;
}
/**
@@ -256,85 +254,90 @@ static int verify_shell(const char *shell_name)
}
/**
- * Mount directory and check that we mounted the right directory.
+ * Bind-mount directory @src (using @src_fd) on directory @dst (using @dst_fd),
+ * applying @bind_flags for the initial bind mount and @sec_flags if
+ * non-zero via remount.
*/
-static int seunshare_mount(const char *src, const char *dst, struct stat *src_st)
+static int seunshare_mount(const char *src, int src_fd,
+ const char *dst, int dst_fd,
+ int bind_flags, int sec_flags)
{
- int bind_flags = MS_BIND;
- int sec_flags = 0;
- int is_tmp = 0;
+ char srcprocfd[32], dstprocfd[32];
+
+ bind_flags |= MS_BIND;
if (verbose)
printf(_("Mounting %s on %s\n"), src, dst);
- if (strcmp("/tmp", dst) == 0) {
- sec_flags = MS_NODEV | MS_NOSUID | MS_NOEXEC;
- is_tmp = 1;
- }
+ snprintf(srcprocfd, sizeof(srcprocfd), "/proc/self/fd/%d", src_fd);
+ snprintf(dstprocfd, sizeof(dstprocfd), "/proc/self/fd/%d", dst_fd);
- if (strncmp("/run/user", dst, 9) == 0) {
- bind_flags |= MS_REC;
- }
-
- /* mount directory */
- if (mount(src, dst, NULL, bind_flags, NULL) < 0) {
- fprintf(stderr, _("Failed to mount %s on %s: %s\n"), src, dst, strerror(errno));
+ /* bind mount directory */
+ if (mount(srcprocfd, dstprocfd, NULL, bind_flags, NULL) < 0) {
+ fprintf(stderr, _("Failed to mount %s on %s: %m\n"), src, dst);
return -1;
}
- /* remount with security flags, ignored on original bind mount */
- if (sec_flags && mount(NULL, dst, NULL, MS_BIND | MS_REMOUNT | sec_flags, NULL) < 0) {
+
+ /*
+ * Remount with security flags set - requires use of dst path again.
+ * Revisit when we migrate to open_tree()/move_mount().
+ */
+ if (sec_flags &&
+ mount(NULL, dst, NULL, MS_BIND | MS_REMOUNT | sec_flags, NULL) < 0) {
fprintf(stderr, _("Failed to remount %s: %m\n"), dst);
return -1;
}
- /* verify whether we mounted what we expected to mount */
- if (verify_directory(dst, src_st, NULL) < 0) return -1;
-
- /* bind mount /tmp on /var/tmp too */
- if (is_tmp) {
- if (verbose)
- printf(_("Mounting /tmp on /var/tmp\n"));
-
- if (mount("/tmp", "/var/tmp", NULL, MS_BIND, NULL) < 0) {
- fprintf(stderr, _("Failed to mount /tmp on /var/tmp: %s\n"), strerror(errno));
- return -1;
- }
- /* remount with security flags, ignored on original bind mount */
- if (mount(NULL, "/var/tmp", NULL, MS_BIND | MS_REMOUNT | sec_flags, NULL) < 0) {
- fprintf(stderr, _("Failed to remount /var/tmp: %m\n"));
- return -1;
- }
- }
-
return 0;
-
}
/**
- * Mount directory and check that we mounted the right directory.
+ * Bind-mount a file named @src_name in directory @src_dirfd on
+ * a file named @dst_name in directory @dst_dirfd, creating @dst_name
+ * if it doesn't already exist.
*/
-static int seunshare_mount_file(const char *src, const char *dst)
+static int seunshare_mount_file(int src_dirfd, const char *src_name,
+ int dst_dirfd, const char *dst_name)
{
+ char srcprocfd[32], dstprocfd[32];
+ int src_fd = -1, dst_fd = -1, rc = -1;
+
if (verbose)
- printf(_("Mounting %s on %s\n"), src, dst);
+ printf(_("Mounting %s on %s\n"), src_name, dst_name);
- if (access(dst, F_OK) == -1) {
- int fd = open(dst, O_WRONLY | O_CREAT | O_NOFOLLOW | O_CLOEXEC, 0600);
- if (fd < 0) {
- fprintf(stderr, _("Failed to create mount point %s: %m\n"), dst);
- return -1;
- }
- close(fd);
+ src_fd = openat(src_dirfd, src_name, O_PATH | O_NOFOLLOW | O_CLOEXEC);
+ if (src_fd < 0) {
+ fprintf(stderr, _("Failed to open %s: %m\n"), src_name);
+ goto out;
}
- /* mount file */
- if (mount(src, dst, NULL, MS_BIND, NULL) < 0) {
- fprintf(stderr, _("Failed to mount %s on %s: %s\n"), src, dst, strerror(errno));
- return -1;
+ dst_fd = openat(dst_dirfd, dst_name, O_PATH | O_NOFOLLOW | O_CLOEXEC);
+ if (dst_fd < 0 && errno == ENOENT)
+ dst_fd = openat(dst_dirfd, dst_name,
+ O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW |
+ O_CLOEXEC, 0600);
+ if (dst_fd < 0) {
+ fprintf(stderr, _("Failed to open/create %s: %m\n"), dst_name);
+ goto out;
}
- return 0;
+ snprintf(srcprocfd, sizeof(srcprocfd), "/proc/self/fd/%d", src_fd);
+ snprintf(dstprocfd, sizeof(dstprocfd), "/proc/self/fd/%d", dst_fd);
+ /* mount file */
+ if (mount(srcprocfd, dstprocfd, NULL, MS_BIND, NULL) < 0) {
+ fprintf(stderr, _("Failed to mount %s on %s: %m\n"), src_name,
+ dst_name);
+ goto out;
+ }
+
+ rc = 0;
+out:
+ if (src_fd >= 0)
+ close(src_fd);
+ if (dst_fd >= 0)
+ close(dst_fd);
+ return rc;
}
/*
@@ -556,7 +559,8 @@ err:
* to clean it up.
*/
static char *create_tmpdir(const char *src, struct stat *src_st,
- struct stat *out_st, struct passwd *pwd, const char *execcon)
+ struct stat *out_st, struct passwd *pwd,
+ const char *execcon)
{
char *tmpdir = NULL;
char **cmd = NULL;
@@ -564,29 +568,23 @@ static char *create_tmpdir(const char *src, struct stat *src_st,
struct stat tmp_st;
char *con = NULL;
- /* get selinux context */
+ /* get selinux context of source directory */
if (execcon) {
if ((uid_t)setfsuid(pwd->pw_uid) != 0)
goto err;
-
- if ((fd_s = open(src, O_RDONLY)) < 0) {
- fprintf(stderr, _("Failed to open directory %s: %s\n"), src, strerror(errno));
- goto err;
- }
- if (fstat(fd_s, &tmp_st) == -1) {
- fprintf(stderr, _("Failed to stat directory %s: %s\n"), src, strerror(errno));
+ if ((fd_s = pin_dir(src, &tmp_st)) < 0)
goto err;
- }
- if (!equal_stats(src_st, &tmp_st)) {
- fprintf(stderr, _("Error: %s was replaced by a different directory\n"), src);
+ if (tmp_st.st_dev != src_st->st_dev ||
+ tmp_st.st_ino != src_st->st_ino) {
+ fprintf(stderr,
+ _("%s was replaced by a different directory\n"),
+ src);
goto err;
}
if (fgetfilecon(fd_s, &con) == -1) {
- fprintf(stderr, _("Failed to get context of the directory %s: %s\n"), src, strerror(errno));
+ fprintf(stderr, _("Failed to get context of the directory %s: %m\n"), src);
goto err;
}
-
- /* ok to not reach this if there is an error */
if ((uid_t)setfsuid(0) != pwd->pw_uid)
goto err;
}
@@ -602,9 +600,9 @@ static char *create_tmpdir(const char *src, struct stat *src_st,
}
/* temporary directory must be owned by root:user */
- if (verify_directory(tmpdir, NULL, out_st) < 0) {
+ fd_t = pin_dir(tmpdir, out_st);
+ if (fd_t < 0)
goto err;
- }
if (check_owner_uid(0, tmpdir, out_st) < 0)
goto err;
@@ -613,18 +611,6 @@ static char *create_tmpdir(const char *src, struct stat *src_st,
goto err;
/* change permissions of the temporary directory */
- if ((fd_t = open(tmpdir, O_RDONLY)) < 0) {
- fprintf(stderr, _("Failed to open directory %s: %s\n"), tmpdir, strerror(errno));
- goto err;
- }
- if (fstat(fd_t, &tmp_st) == -1) {
- fprintf(stderr, _("Failed to stat directory %s: %s\n"), tmpdir, strerror(errno));
- goto err;
- }
- if (!equal_stats(out_st, &tmp_st)) {
- fprintf(stderr, _("Error: %s was replaced by a different directory\n"), tmpdir);
- goto err;
- }
if (fchmod(fd_t, 01770) == -1) {
fprintf(stderr, _("Unable to change mode on %s: %s\n"), tmpdir, strerror(errno));
goto err;
@@ -664,10 +650,10 @@ static char *create_tmpdir(const char *src, struct stat *src_st,
err:
free(tmpdir); tmpdir = NULL;
good:
- free_args(cmd);
- freecon(con); con = NULL;
if (fd_t >= 0) close(fd_t);
if (fd_s >= 0) close(fd_s);
+ free_args(cmd);
+ freecon(con); con = NULL;
return tmpdir;
}
@@ -776,12 +762,13 @@ int main(int argc, char **argv) {
char *tmpdir_r = NULL; /* tmpdir created by seunshare */
char *runuserdir_s = NULL; /* /var/run/user/UID spec'd by user in argv[] */
- struct stat st_curhomedir;
struct stat st_homedir;
struct stat st_tmpdir_s;
struct stat st_tmpdir_r;
struct stat st_runuserdir_s;
+ int fd;
+
const struct option long_options[] = {
{"homedir", 1, 0, 'h'},
{"tmpdir", 1, 0, 't'},
@@ -893,24 +880,49 @@ int main(int argc, char **argv) {
return -1;
}
- /* verify homedir and tmpdir */
- if (homedir_s && (
- verify_directory(homedir_s, NULL, &st_homedir) < 0 ||
- check_owner_uid(uid, homedir_s, &st_homedir))) return -1;
- if (tmpdir_s && (
- verify_directory(tmpdir_s, NULL, &st_tmpdir_s) < 0 ||
- check_owner_uid(uid, tmpdir_s, &st_tmpdir_s))) return -1;
- if (runuserdir_s && (
- verify_directory(runuserdir_s, NULL, &st_runuserdir_s) < 0 ||
- check_owner_uid(uid, runuserdir_s, &st_runuserdir_s))) return -1;
+ /*
+ * Perform early validation of the caller-provided directories so we
+ * can fail fast, but we unfortunately have to redo this after
+ * unsharing the mount namespace in the child so that it can use
+ * the descriptors for subsequent mount(2) calls. Otherwise,
+ * they end up with a different mount namespace and mount(2) fails
+ * with errno EINVAL.
+ */
+ if (homedir_s) {
+ fd = pin_dir(homedir_s, &st_homedir);
+ if (fd < 0)
+ return -1;
+ if (check_owner_uid(uid, homedir_s, &st_homedir))
+ return -1;
+ close(fd);
+ }
+ if (tmpdir_s) {
+ fd = pin_dir(tmpdir_s, &st_tmpdir_s);
+ if (fd < 0)
+ return -1;
+ if (check_owner_uid(uid, tmpdir_s, &st_tmpdir_s))
+ return -1;
+ close(fd);
+ }
+ if (runuserdir_s) {
+ fd = pin_dir(runuserdir_s, &st_runuserdir_s);
+ if (fd < 0)
+ return -1;
+ if (check_owner_uid(uid, runuserdir_s, &st_runuserdir_s))
+ return -1;
+ close(fd);
+ }
if ((uid_t)setfsuid(0) != uid) return -1;
/* create runtime tmpdir */
- if (tmpdir_s && (tmpdir_r = create_tmpdir(tmpdir_s, &st_tmpdir_s,
- &st_tmpdir_r, pwd, execcon)) == NULL) {
- fprintf(stderr, _("Failed to create runtime temporary directory\n"));
- return -1;
+ if (tmpdir_s) {
+ tmpdir_r = create_tmpdir(tmpdir_s, &st_tmpdir_s, &st_tmpdir_r,
+ pwd, execcon);
+ if (!tmpdir_r) {
+ fprintf(stderr, _("Failed to create runtime temporary directory\n"));
+ return -1;
+ }
}
/* spawn child process */
@@ -927,11 +939,10 @@ int main(int argc, char **argv) {
char *XDG_SESSION_TYPE = NULL;
int rc = -1;
char *resolved_path = NULL;
- char *wayland_path_s = NULL; /* /tmp/.../wayland-0 */
- char *wayland_path = NULL; /* /run/user/UID/wayland-0 */
- char *pipewire_path_s = NULL; /* /tmp/.../pipewire-0 */
- char *pipewire_path = NULL; /* /run/user/UID/pipewire-0 */
-
+ int fd_homedir_s = -1, fd_curhomedir = -1;
+ int fd_runuserdir_s = -1, fd_runtime_dir = -1;
+ int fd_tmpdir_r = -1, fd_tmp = -1, fd_var_tmp = -1;
+ struct stat sb;
if (unshare(CLONE_NEWNS) < 0) {
perror(_("Failed to unshare"));
@@ -941,19 +952,67 @@ int main(int argc, char **argv) {
/* Remount / as SLAVE so that nothing mounted in the namespace
shows up in the parent */
if (mount("none", "/", NULL, MS_SLAVE | MS_REC , NULL) < 0) {
- perror(_("Failed to make / a SLAVE mountpoint\n"));
+
goto childerr;
}
/* assume fsuid==ruid after this point */
if ((uid_t)setfsuid(uid) != 0) goto childerr;
+ /*
+ * Now we can pin the source directories in this namespace
+ * for later use by mount(2). We recheck that each
+ * directory is the same inode and still has the
+ * expected ownership as the early validation.
+ */
+ if (homedir_s) {
+ fd_homedir_s = pin_dir(homedir_s, &sb);
+ if (fd_homedir_s < 0)
+ goto childerr;
+ if (sb.st_dev != st_homedir.st_dev ||
+ sb.st_ino != st_homedir.st_ino)
+ goto childerr;
+ if (check_owner_uid(uid, homedir_s, &sb))
+ goto childerr;
+ }
+ /*
+ * NB We don't need to re-pin tmpdir_s, just tmpdir_r,
+ * since the child never uses tmpdir_s.
+ */
+ if (tmpdir_r) {
+ fd_tmpdir_r = pin_dir(tmpdir_r, &sb);
+ if (fd < 0)
+ goto childerr;
+ /*
+ * tmpdir_r checks differ in that it is
+ * root-owned and we also want to validate
+ * that the mode is still correct.
+ */
+ if (sb.st_dev != st_tmpdir_r.st_dev ||
+ sb.st_ino != st_tmpdir_r.st_ino ||
+ sb.st_mode != st_tmpdir_r.st_mode)
+ goto childerr;
+ if (check_owner_uid(0, tmpdir_r, &sb))
+ goto childerr;
+ }
+ if (runuserdir_s) {
+ fd_runuserdir_s = pin_dir(runuserdir_s, &sb);
+ if (fd_runuserdir_s < 0)
+ goto childerr;
+ if (sb.st_dev != st_runuserdir_s.st_dev ||
+ sb.st_ino != st_runuserdir_s.st_ino)
+ goto childerr;
+ if (check_owner_uid(uid, runuserdir_s, &sb))
+ goto childerr;
+ }
+
resolved_path = realpath(pwd->pw_dir,NULL);
if (! resolved_path) goto childerr;
- if (verify_directory(resolved_path, NULL, &st_curhomedir) < 0)
+ fd_curhomedir = pin_dir(resolved_path, &sb);
+ if (fd_curhomedir < 0)
goto childerr;
- if (check_owner_uid(uid, resolved_path, &st_curhomedir) < 0)
+ if (check_owner_uid(uid, resolved_path, &sb) < 0)
goto childerr;
if ((RUNTIME_DIR = getenv("XDG_RUNTIME_DIR")) != NULL) {
@@ -969,12 +1028,11 @@ int main(int argc, char **argv) {
}
if (runuserdir_s) {
- struct stat sb;
-
- if (verify_directory(RUNTIME_DIR, NULL, &sb) < 0 ||
- check_owner_uid(uid, RUNTIME_DIR, &sb) < 0)
+ fd_runtime_dir = pin_dir(RUNTIME_DIR, &sb);
+ if (fd_runtime_dir < 0)
+ goto childerr;
+ if (check_owner_uid(uid, RUNTIME_DIR, &sb) < 0)
goto childerr;
-
}
if ((XDG_SESSION_TYPE = getenv("XDG_SESSION_TYPE")) != NULL) {
@@ -985,42 +1043,57 @@ int main(int argc, char **argv) {
}
if (runuserdir_s && (wayland_display || pipewire_socket)) {
- if (wayland_display) {
- if (asprintf(&wayland_path_s, "%s/%s", runuserdir_s, wayland_display) == -1) {
- perror(_("Out of memory"));
+ if (wayland_display &&
+ seunshare_mount_file(fd_runtime_dir,
+ wayland_display,
+ fd_runuserdir_s,
+ wayland_display) == -1)
goto childerr;
- }
- if (asprintf(&wayland_path, "%s/%s", RUNTIME_DIR, wayland_display) == -1) {
- perror(_("Out of memory"));
- goto childerr;
- }
+ if (pipewire_socket &&
+ seunshare_mount_file(fd_runtime_dir,
+ "pipewire-0",
+ fd_runuserdir_s,
+ pipewire_socket) == -1)
+ goto childerr;
+ }
- if (seunshare_mount_file(wayland_path, wayland_path_s) == -1)
- goto childerr;
+ /* mount homedir, runuserdir and tmpdir, in this order */
+ if (runuserdir_s &&
+ seunshare_mount(runuserdir_s, fd_runuserdir_s,
+ RUNTIME_DIR, fd_runtime_dir,
+ MS_REC, 0) != 0)
+ goto childerr;
+ if (homedir_s &&
+ seunshare_mount(homedir_s, fd_homedir_s,
+ resolved_path, fd_curhomedir,
+ 0, 0) != 0)
+ goto childerr;
+ if (tmpdir_s) {
+ fd_tmp = open("/tmp", O_RDONLY | O_DIRECTORY |
+ O_NOFOLLOW | O_CLOEXEC);
+ if (fd_tmp < 0) {
+ perror(_("Failed to open /tmp"));
+ goto childerr;
}
- if (pipewire_socket) {
- if (asprintf(&pipewire_path_s, "%s/%s", runuserdir_s, pipewire_socket) == -1) {
- perror(_("Out of memory"));
- goto childerr;
- }
- if (asprintf(&pipewire_path, "%s/pipewire-0", RUNTIME_DIR) == -1) {
- perror(_("Out of memory"));
- goto childerr;
- }
- if (seunshare_mount_file(pipewire_path, pipewire_path_s) == -1)
- goto childerr;
+ if (seunshare_mount(tmpdir_r, fd_tmpdir_r,
+ "/tmp", fd_tmp, 0,
+ MS_NODEV|MS_NOSUID|MS_NOEXEC) < 0)
+ goto childerr;
+
+ fd_var_tmp = open("/var/tmp", O_RDONLY | O_DIRECTORY |
+ O_NOFOLLOW | O_CLOEXEC);
+ if (fd_var_tmp < 0) {
+ perror(_("Failed to open /var/tmp"));
+ goto childerr;
}
- }
- /* mount homedir, runuserdir and tmpdir, in this order */
- if (runuserdir_s && seunshare_mount(runuserdir_s, RUNTIME_DIR,
- &st_runuserdir_s) != 0) goto childerr;
- if (homedir_s && seunshare_mount(homedir_s, resolved_path,
- &st_homedir) != 0) goto childerr;
- if (tmpdir_s && seunshare_mount(tmpdir_r, "/tmp",
- &st_tmpdir_r) != 0) goto childerr;
+ if (seunshare_mount("/tmp", fd_tmpdir_r,
+ "/var/tmp", fd_var_tmp, 0,
+ MS_NODEV|MS_NOSUID|MS_NOEXEC) < 0)
+ goto childerr;
+ }
if (drop_privs(uid) != 0) goto childerr;
@@ -1101,11 +1174,14 @@ int main(int argc, char **argv) {
execv(argv[optind], argv + optind);
fprintf(stderr, _("Failed to execute command %s: %s\n"), argv[optind], strerror(errno));
childerr:
+ if (fd_homedir_s >= 0) close(fd_homedir_s);
+ if (fd_curhomedir >= 0) close(fd_curhomedir);
+ if (fd_runuserdir_s >= 0) close(fd_runuserdir_s);
+ if (fd_runtime_dir >= 0) close(fd_runtime_dir);
+ if (fd_tmpdir_r >= 0) close(fd_tmpdir_r);
+ if (fd_tmp >= 0) close(fd_tmp);
+ if (fd_var_tmp >= 0) close(fd_var_tmp);
free(resolved_path);
- free(wayland_path);
- free(wayland_path_s);
- free(pipewire_path);
- free(pipewire_path_s);
free(display);
free(LANG);
free(RUNTIME_DIR);
--
2.55.0

View File

@ -0,0 +1,158 @@
From 0f8787a80194d38746f2d7280c961882982fc688 Mon Sep 17 00:00:00 2001
From: Stephen Smalley <stephen.smalley.work@gmail.com>
Date: Thu, 14 May 2026 11:03:40 -0400
Subject: [PATCH] sandbox/seunshare: fully check setfsuid() calls
Content-type: text/plain
setfsuid() returns the old fsuid value and doesn't always
set errno. The existing code was checking that it did
successfully return the old fsuid value but not explicitly
checking that the new one was set, which can be done by
a second call to setfsuid() with -1 and checking its
return value. Add a wrapper for setfsuid() and use it
throughout to ensure complete checking.
Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
---
sandbox/seunshare.c | 60 ++++++++++++++++++++++++++++++++++++---------
1 file changed, 48 insertions(+), 12 deletions(-)
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index 611bfe80d030..98fcef326853 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -486,6 +486,44 @@ static bool rm_rf(int targetfd, const char *path) {
return true;
}
+/*
+ * setfsuid() returns the previous fsuid value,
+ * and does not reliably set errno on errors.
+ * Let's do better.
+ */
+static int setfsuid_checked(uid_t old, uid_t new)
+{
+ int rc;
+
+ rc = setfsuid(new);
+ if ((uid_t)rc != old) {
+ int save_errno = errno;
+ fprintf(stderr,
+ "setfsuid(%u): Returned unexpected old uid %u\n",
+ new, (uid_t)rc);
+ if (save_errno)
+ errno = save_errno;
+ else
+ errno = EPERM;
+ return -1;
+ }
+
+ rc = setfsuid(-1);
+ if ((uid_t)rc != new) {
+ int save_errno = errno;
+ fprintf(stderr,
+ "setfsuid(%u): Produced unexpected new uid %u\n",
+ new,(uid_t)rc);
+ if (save_errno)
+ errno = save_errno;
+ else
+ errno = EPERM;
+ return -1;
+ }
+
+ return 0;
+}
+
/**
* 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
@@ -529,10 +567,8 @@ static int cleanup_tmpdir(const char *tmpdir, const char *src,
free_args(args);
}
- if ((uid_t)setfsuid(0) != 0) {
- /* setfsuid does not return error, but this check makes code checkers happy */
+ if (setfsuid_checked(0, 0) < 0)
rc++;
- }
/* Recursively remove the runtime temp directory. */
if (!rm_rf(AT_FDCWD, tmpdir)) {
@@ -540,7 +576,7 @@ static int cleanup_tmpdir(const char *tmpdir, const char *src,
rc++;
}
- if ((uid_t)setfsuid(pwd->pw_uid) != 0) {
+ if (setfsuid_checked(0, pwd->pw_uid) < 0) {
fprintf(stderr, _("unable to switch back to user after clearing tmp dir\n"));
rc++;
}
@@ -570,7 +606,7 @@ static char *create_tmpdir(const char *src, struct stat *src_st,
/* get selinux context of source directory */
if (execcon) {
- if ((uid_t)setfsuid(pwd->pw_uid) != 0)
+ if (setfsuid_checked(0, pwd->pw_uid))
goto err;
if ((fd_s = pin_dir(src, &tmp_st)) < 0)
goto err;
@@ -585,7 +621,7 @@ static char *create_tmpdir(const char *src, struct stat *src_st,
fprintf(stderr, _("Failed to get context of the directory %s: %m\n"), src);
goto err;
}
- if ((uid_t)setfsuid(0) != pwd->pw_uid)
+ if (setfsuid_checked(pwd->pw_uid, 0) < 0)
goto err;
}
@@ -629,7 +665,7 @@ static char *create_tmpdir(const char *src, struct stat *src_st,
}
}
- if ((uid_t)setfsuid(pwd->pw_uid) != 0)
+ if (setfsuid_checked(0, pwd->pw_uid) < 0)
goto err;
if (rsynccmd(src, tmpdir, &cmd) < 0) {
@@ -637,7 +673,7 @@ static char *create_tmpdir(const char *src, struct stat *src_st,
}
/* ok to not reach this if there is an error */
- if ((uid_t)setfsuid(0) != pwd->pw_uid)
+ if (setfsuid_checked(pwd->pw_uid, 0) < 0)
goto err;
if (spawn_command(cmd, pwd->pw_uid) != 0) {
@@ -874,9 +910,8 @@ int main(int argc, char **argv) {
/* Changing fsuid is usually required when user-specified directory is
* on an NFS mount. It's also desired to avoid leaking info about
* existence of the files not accessible to the user. */
- if ((uid_t)setfsuid(uid) != 0) {
+ if (setfsuid_checked(0, uid) < 0) {
fprintf(stderr, _("Error: unable to setfsuid\n"));
-
return -1;
}
@@ -913,7 +948,8 @@ int main(int argc, char **argv) {
close(fd);
}
- if ((uid_t)setfsuid(0) != uid) return -1;
+ if (setfsuid_checked(uid, 0) < 0)
+ return -1;
/* create runtime tmpdir */
if (tmpdir_s) {
@@ -957,7 +993,7 @@ int main(int argc, char **argv) {
}
/* assume fsuid==ruid after this point */
- if ((uid_t)setfsuid(uid) != 0) goto childerr;
+ if (setfsuid_checked(0, uid) < 0) goto childerr;
/*
* Now we can pin the source directories in this namespace
--
2.55.0

View File

@ -0,0 +1,85 @@
From 8bc3312574ea56d0895d0e15699197f4aa327cdd Mon Sep 17 00:00:00 2001
From: Stephen Smalley <stephen.smalley.work@gmail.com>
Date: Fri, 15 May 2026 11:11:43 -0400
Subject: [PATCH] sandbox/seunshare: check owner in seunshare_mount_file()
Content-type: text/plain
We currently apply an owner check on directories prior to
calling seunshare_mount() on them. Do the same for
seunshare_mount_file(), but this has to be done inside
of the function since we do not open the source and
destination files until then.
Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
---
sandbox/seunshare.c | 23 ++++++++++++++++++++---
1 file changed, 20 insertions(+), 3 deletions(-)
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index 98fcef326853..3afe1554ae56 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -296,11 +296,12 @@ static int seunshare_mount(const char *src, int src_fd,
* a file named @dst_name in directory @dst_dirfd, creating @dst_name
* if it doesn't already exist.
*/
-static int seunshare_mount_file(int src_dirfd, const char *src_name,
+static int seunshare_mount_file(uid_t uid, int src_dirfd, const char *src_name,
int dst_dirfd, const char *dst_name)
{
char srcprocfd[32], dstprocfd[32];
int src_fd = -1, dst_fd = -1, rc = -1;
+ struct stat sb;
if (verbose)
printf(_("Mounting %s on %s\n"), src_name, dst_name);
@@ -311,6 +312,14 @@ static int seunshare_mount_file(int src_dirfd, const char *src_name,
goto out;
}
+ if (fstat(src_fd, &sb) < 0) {
+ fprintf(stderr, _("Failed to stat %s: %m\n"), src_name);
+ goto out;
+
+ }
+ if (check_owner_uid(uid, src_name, &sb))
+ goto out;
+
dst_fd = openat(dst_dirfd, dst_name, O_PATH | O_NOFOLLOW | O_CLOEXEC);
if (dst_fd < 0 && errno == ENOENT)
dst_fd = openat(dst_dirfd, dst_name,
@@ -321,6 +330,14 @@ static int seunshare_mount_file(int src_dirfd, const char *src_name,
goto out;
}
+ if (fstat(dst_fd, &sb) < 0) {
+ fprintf(stderr, _("Failed to stat %s: %m\n"), dst_name);
+ goto out;
+
+ }
+ if (check_owner_uid(uid, dst_name, &sb))
+ goto out;
+
snprintf(srcprocfd, sizeof(srcprocfd), "/proc/self/fd/%d", src_fd);
snprintf(dstprocfd, sizeof(dstprocfd), "/proc/self/fd/%d", dst_fd);
@@ -1080,14 +1097,14 @@ int main(int argc, char **argv) {
if (runuserdir_s && (wayland_display || pipewire_socket)) {
if (wayland_display &&
- seunshare_mount_file(fd_runtime_dir,
+ seunshare_mount_file(uid, fd_runtime_dir,
wayland_display,
fd_runuserdir_s,
wayland_display) == -1)
goto childerr;
if (pipewire_socket &&
- seunshare_mount_file(fd_runtime_dir,
+ seunshare_mount_file(uid, fd_runtime_dir,
"pipewire-0",
fd_runuserdir_s,
pipewire_socket) == -1)
--
2.55.0

View File

@ -0,0 +1,29 @@
From 730f8d1ceeabf00461773e01a6b19b14320bce24 Mon Sep 17 00:00:00 2001
From: Stephen Smalley <stephen.smalley.work@gmail.com>
Date: Tue, 19 May 2026 08:13:40 -0400
Subject: [PATCH] sandbox/seunshare: fix fd_tmpdir_r check
Content-type: text/plain
Check fd_tmpdir_r not fd for a failed pin_dir() here.
Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
---
sandbox/seunshare.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index 3afe1554ae56..7a4233fbee00 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -1034,7 +1034,7 @@ int main(int argc, char **argv) {
*/
if (tmpdir_r) {
fd_tmpdir_r = pin_dir(tmpdir_r, &sb);
- if (fd < 0)
+ if (fd_tmpdir_r < 0)
goto childerr;
/*
* tmpdir_r checks differ in that it is
--
2.55.0

View File

@ -0,0 +1,31 @@
From 757801f1b140117729ebc0283d43893350733602 Mon Sep 17 00:00:00 2001
From: Stephen Smalley <stephen.smalley.work@gmail.com>
Date: Thu, 21 May 2026 11:31:33 -0400
Subject: [PATCH] sandbox/seunshare: check for errors from parent drop_caps()
Content-type: text/plain
Make sure we don't continue processing in the parent if
drop_caps() failed.
Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
---
sandbox/seunshare.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index 7a4233fbee00..1d13d2612f29 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -1242,7 +1242,8 @@ childerr:
exit(-1);
}
- drop_caps();
+ if (drop_caps() == -1)
+ goto err;
/* parent waits for child exit to do the cleanup */
waitpid(child, &status, 0);
--
2.55.0

View File

@ -0,0 +1,41 @@
From 878680ecffa57f66894412a3cdbd0696ce414563 Mon Sep 17 00:00:00 2001
From: Stephen Smalley <stephen.smalley.work@gmail.com>
Date: Thu, 21 May 2026 11:45:26 -0400
Subject: [PATCH] sandbox/seunshare: fix undefined behavior for child
Content-type: text/plain
The global child variable is written in main() and read
by the SIGINT handler. To avoid undefined behavior,
declare child to be volatile sig_atomic_t so that it
is async-signal-safe.
Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
---
sandbox/seunshare.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index 1d13d2612f29..ac749d4ca69c 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -63,7 +63,7 @@
} while(0)
static int verbose = 0;
-static int child = 0;
+static volatile sig_atomic_t child = 0;
static capng_select_t cap_set = CAPNG_SELECT_CAPS;
@@ -98,7 +98,7 @@ static int drop_privs(uid_t uid)
* If the user sends a siginto to seunshare, kill the child's session
*/
static void handler(int sig) {
- if (child > 0) kill(-child,sig);
+ if (child > 0) kill(-(pid_t)child,sig);
}
/**
--
2.55.0

View File

@ -0,0 +1,130 @@
From 6ee3f8a59640483ec04cc74e44c7b88f631c700e Mon Sep 17 00:00:00 2001
From: Stephen Smalley <stephen.smalley.work@gmail.com>
Date: Thu, 21 May 2026 11:54:44 -0400
Subject: [PATCH] sandbox/seunshare: drop -k/--kill support
Content-type: text/plain
Drop the -k/--kill support; it is fundamentally racy, redundant
with killall -Z, and seemingly unused by sandbox itself.
Provide an error message to the user telling them how to achieve
the same effect via killall.
Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
---
sandbox/seunshare.c | 93 +--------------------------------------------
1 file changed, 1 insertion(+), 92 deletions(-)
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index ac749d4ca69c..814d003d310f 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -710,97 +710,6 @@ good:
return tmpdir;
}
-#define PROC_BASE "/proc"
-
-static int
-killall (const char *execcon)
-{
- DIR *dir;
- char *scon;
- struct dirent *de;
- pid_t *pid_table, pid, self;
- unsigned int i;
- unsigned int pids, max_pids;
- int running = 0;
- self = getpid();
- if (!(dir = opendir(PROC_BASE))) {
- return -1;
- }
- max_pids = 256;
- pid_table = malloc(max_pids * sizeof (pid_t));
- if (!pid_table) {
- (void)closedir(dir);
- return -1;
- }
- pids = 0;
- context_t con = context_new(execcon);
- if (!con) {
- free(pid_table);
- (void)closedir(dir);
- return -1;
- }
- const char *const mcs = context_range_get(con);
- const char *const type = context_type_get(con);
- if (!mcs || !type) {
- context_free(con);
- free(pid_table);
- (void)closedir(dir);
- return -1;
- }
- if (verbose)
- printf("mcs=%s type=%s\n", mcs, type);
- while ((de = readdir (dir)) != NULL) {
- if (!(pid = (pid_t)atoi(de->d_name)) || pid == self)
- continue;
-
- if (pids == max_pids) {
- max_pids *= 2;
- if (max_pids <= pids)
- {
- free(pid_table);
- (void)closedir(dir);
- return -1;
- }
- pid_t *new_pid_table = reallocarray(pid_table, max_pids, sizeof(pid_t));
- if (!new_pid_table) {
- free(pid_table);
- (void)closedir(dir);
- return -1;
- }
- pid_table = new_pid_table;
- }
- pid_table[pids++] = pid;
- }
-
- (void)closedir(dir);
-
- for (i = 0; i < pids; i++) {
- pid_t id = pid_table[i];
-
- if (getpidcon(id, &scon) == 0) {
-
- context_t pidcon = context_new(scon);
- if (pidcon) {
- const char *const pmcs = context_range_get(pidcon);
- const char *const ptype = context_type_get(pidcon);
-
- /* Attempt to kill remaining processes */
- if (pmcs && ptype && !strcmp(pmcs, mcs) &&
- !strcmp(ptype, type))
- kill(id, SIGKILL);
-
- context_free(pidcon);
- }
- freecon(scon);
- }
- running++;
- }
-
- context_free(con);
- free(pid_table);
- return running;
-}
-
int main(int argc, char **argv) {
int status = -1;
const char *execcon = NULL;
@@ -1253,7 +1162,7 @@ childerr:
kill(-child,SIGTERM);
if (execcon && kill_all)
- killall(execcon);
+ fprintf(stderr, "-k/--kill no longer supported; run killall -Z %s\n", execcon);
if (tmpdir_r) cleanup_tmpdir(tmpdir_r, tmpdir_s, pwd, 1);
--
2.55.0

View File

@ -11,7 +11,7 @@
Summary: SELinux policy core utilities
Name: policycoreutils
Version: 3.6
Release: 8%{?dist}
Release: 9%{?dist}
License: GPL-2.0-or-later
# https://github.com/SELinuxProject/selinux/wiki/Releases
Source0: https://github.com/SELinuxProject/selinux/releases/download/3.6/selinux-3.6.tar.gz
@ -67,6 +67,25 @@ Patch0028: 0028-semanage-fcontext-8-improve-e-documentation.patch
Patch0029: 0029-restorecond-Add-F-for-run-in-foreground.patch
Patch0030: 0030-restorecond.service-Use-Type-simple.patch
Patch0031: 0031-sepolicy-Fix-detection-of-writeable-locations.patch
Patch0032: 0032-seunshare-Add-P-pipewiresocket-W-waylandsocket-optio.patch
Patch0033: 0033-seunshare-guard-fallible-function-calls-by-checking-.patch
Patch0034: 0034-sandbox-seunshare-pass-O_NOFOLLOW-to-openat.patch
Patch0035: 0035-sandbox-seunshare-switch-seunshare_mount_file-to-use.patch
Patch0036: 0036-sandbox-seunshare-fix-error-checking-for-setfsuid.patch
Patch0037: 0037-sandbox-seunshare-remount-tmp-and-var-tmp-with-the-p.patch
Patch0038: 0038-sandbox-seunshare-prevent-rsync-from-interpreting-pa.patch
Patch0039: 0039-sandbox-seunshare-fix-getopt-flags.patch
Patch0040: 0040-sandbox-seunshare-prevent-path-traversal-via-W-P.patch
Patch0041: 0041-sandbox-seunshare-verify-RUNTIME_DIR-before-use.patch
Patch0042: 0042-sandbox-seunshare-drop-unused-runuserdir_r.patch
Patch0043: 0043-sandbox-seunshare-fix-killall-realloc-and-missing-ty.patch
Patch0044: 0044-sandbox-seunshare-rewrite-to-pin-directories-before-.patch
Patch0045: 0045-sandbox-seunshare-fully-check-setfsuid-calls.patch
Patch0046: 0046-sandbox-seunshare-check-owner-in-seunshare_mount_fil.patch
Patch0047: 0047-sandbox-seunshare-fix-fd_tmpdir_r-check.patch
Patch0048: 0048-sandbox-seunshare-check-for-errors-from-parent-drop_.patch
Patch0049: 0049-sandbox-seunshare-fix-undefined-behavior-for-child.patch
Patch0050: 0050-sandbox-seunshare-drop-k-kill-support.patch
# Patch list end
Obsoletes: policycoreutils < 2.0.61-2
Conflicts: filesystem < 3, selinux-policy-base < 3.13.1-138
@ -476,6 +495,10 @@ The policycoreutils-restorecond package contains the restorecond service.
%systemd_postun_with_restart restorecond.service
%changelog
* Tue Aug 04 2026 Petr Lautrbach <lautrbach@redhat.com> - 3.6-9
- Several sandbox and seunshare security improvements
- https://github.com/SELinuxProject/selinux/security/advisories/GHSA-h4m3-rc99-c7m5
* Fri Jul 17 2026 Vit Mojzis <vmojzis@redhat.com> - 3.6-8
- sepolicy: Fix detection of writeable locations (RHEL-141295)