policycoreutils-3.10-3
- Several sandbox and seunshare security improvements Resolves: RHEL-175829
This commit is contained in:
parent
d5da37c390
commit
ef4edd927a
@ -0,0 +1,63 @@
|
||||
From 0edfc7cd42fdded9c697370d6932eecad2aa7449 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.54.0
|
||||
|
||||
30
0010-sandbox-seunshare-pass-O_NOFOLLOW-to-openat.patch
Normal file
30
0010-sandbox-seunshare-pass-O_NOFOLLOW-to-openat.patch
Normal file
@ -0,0 +1,30 @@
|
||||
From 8d2c7360f5749b3ccacd348c495399f24318d7b4 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.54.0
|
||||
|
||||
@ -0,0 +1,52 @@
|
||||
From e964a574c2ca8ec1bcd3172dc185ada8044dceee 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.54.0
|
||||
|
||||
33
0012-sandbox-seunshare-fix-error-checking-for-setfsuid.patch
Normal file
33
0012-sandbox-seunshare-fix-error-checking-for-setfsuid.patch
Normal file
@ -0,0 +1,33 @@
|
||||
From 9c5320c6d4a1eb91b57e4e12737882ef131c2faa 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.54.0
|
||||
|
||||
@ -0,0 +1,86 @@
|
||||
From f78708ea5d6d3d6da4fa20527b459fa55075d8a6 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.54.0
|
||||
|
||||
@ -0,0 +1,75 @@
|
||||
From dd620e8af44e1cb59d70837e80a5d55a8d3e789f 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.54.0
|
||||
|
||||
44
0015-sandbox-seunshare-fix-getopt-flags.patch
Normal file
44
0015-sandbox-seunshare-fix-getopt-flags.patch
Normal file
@ -0,0 +1,44 @@
|
||||
From 9c29653dc6b0f4e9a74cd119d60a84ed8c4a6ec6 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.54.0
|
||||
|
||||
47
0016-sandbox-seunshare-prevent-path-traversal-via-W-P.patch
Normal file
47
0016-sandbox-seunshare-prevent-path-traversal-via-W-P.patch
Normal file
@ -0,0 +1,47 @@
|
||||
From 5da4d90480164c13a5bc8f8c820e701790499ecc 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.54.0
|
||||
|
||||
38
0017-sandbox-seunshare-verify-RUNTIME_DIR-before-use.patch
Normal file
38
0017-sandbox-seunshare-verify-RUNTIME_DIR-before-use.patch
Normal file
@ -0,0 +1,38 @@
|
||||
From cceeb1def62ef78fed802684e9500cfa1aefa6b3 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.54.0
|
||||
|
||||
48
0018-sandbox-seunshare-drop-unused-runuserdir_r.patch
Normal file
48
0018-sandbox-seunshare-drop-unused-runuserdir_r.patch
Normal file
@ -0,0 +1,48 @@
|
||||
From c2f54aff134bcba8ae9701d26d7711530d72be76 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.54.0
|
||||
|
||||
@ -0,0 +1,95 @@
|
||||
From 47a8bd5d29455f1b8496a2bc7ba95f3f535c2ba7 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.54.0
|
||||
|
||||
606
0020-sandbox-seunshare-rewrite-to-pin-directories-before-.patch
Normal file
606
0020-sandbox-seunshare-rewrite-to-pin-directories-before-.patch
Normal file
@ -0,0 +1,606 @@
|
||||
From 9c44bb929a7937897a787790ece679464345c5d9 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.54.0
|
||||
|
||||
158
0021-sandbox-seunshare-fully-check-setfsuid-calls.patch
Normal file
158
0021-sandbox-seunshare-fully-check-setfsuid-calls.patch
Normal file
@ -0,0 +1,158 @@
|
||||
From 59cfed3bf23511f792bde3085759ab44ed643a4a 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.54.0
|
||||
|
||||
@ -0,0 +1,85 @@
|
||||
From 8a3de70622d252f1ee070d6d9e9b019b9a6edd08 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.54.0
|
||||
|
||||
29
0023-sandbox-seunshare-fix-fd_tmpdir_r-check.patch
Normal file
29
0023-sandbox-seunshare-fix-fd_tmpdir_r-check.patch
Normal file
@ -0,0 +1,29 @@
|
||||
From d99b5a313a2790b1ca703023112f548cbe65617a 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.54.0
|
||||
|
||||
30
0024-restorecond-Do-not-unlink-pidfile-if-not-used.patch
Normal file
30
0024-restorecond-Do-not-unlink-pidfile-if-not-used.patch
Normal file
@ -0,0 +1,30 @@
|
||||
From a063bc28044a23dd7aa0c496c52a9c9ff4abd349 Mon Sep 17 00:00:00 2001
|
||||
From: Petr Lautrbach <lautrbach@redhat.com>
|
||||
Date: Mon, 25 May 2026 18:11:21 +0200
|
||||
Subject: [PATCH] restorecond: Do not unlink pidfile if not used
|
||||
Content-type: text/plain
|
||||
|
||||
With -F or -d, restorecond does not create nor use pidfile and therefore
|
||||
it should not try to remove it.
|
||||
|
||||
Signed-off-by: Petr Lautrbach <lautrbach@redhat.com>
|
||||
---
|
||||
restorecond/restorecond.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
diff --git a/restorecond/restorecond.c b/restorecond/restorecond.c
|
||||
index 36f82ae5e9cb..6353b3b72798 100644
|
||||
--- a/restorecond/restorecond.c
|
||||
+++ b/restorecond/restorecond.c
|
||||
@@ -217,6 +217,8 @@ int main(int argc, char **argv)
|
||||
if (daemon(0, 0) < 0)
|
||||
exitApp("daemon");
|
||||
write_pid_file();
|
||||
+ } else {
|
||||
+ pidfile = 0;
|
||||
}
|
||||
|
||||
while (watch(master_fd, watch_file) == 0) {
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
* Mon May 25 2026 Petr Lautrbach <lautrbach@redhat.com> - 3.10-3
|
||||
- Several sandbox and seunshare security improvements
|
||||
|
||||
* Tue Apr 07 2026 Petr Lautrbach <lautrbach@redhat.com> - 3.10-2
|
||||
- restorecond.service: Use Type=simple
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
Summary: SELinux policy core utilities
|
||||
Name: policycoreutils
|
||||
Version: 3.10
|
||||
Release: 2%{?dist}
|
||||
Release: 3%{?dist}
|
||||
License: GPL-2.0-or-later
|
||||
# https://github.com/SELinuxProject/selinux/wiki/Releases
|
||||
Source0: https://github.com/SELinuxProject/selinux/releases/download/%{version}/selinux-%{version}.tar.gz
|
||||
@ -48,6 +48,22 @@ Patch0005: 0005-python-sepolicy-Fix-spec-file-dependencies.patch
|
||||
Patch0006: 0006-sepolicy-Fix-detection-of-writeable-locations.patch
|
||||
Patch0007: 0007-restorecond-Add-F-for-run-in-foreground.patch
|
||||
Patch0008: 0008-restorecond.service-Use-Type-simple.patch
|
||||
Patch0009: 0009-seunshare-guard-fallible-function-calls-by-checking-.patch
|
||||
Patch0010: 0010-sandbox-seunshare-pass-O_NOFOLLOW-to-openat.patch
|
||||
Patch0011: 0011-sandbox-seunshare-switch-seunshare_mount_file-to-use.patch
|
||||
Patch0012: 0012-sandbox-seunshare-fix-error-checking-for-setfsuid.patch
|
||||
Patch0013: 0013-sandbox-seunshare-remount-tmp-and-var-tmp-with-the-p.patch
|
||||
Patch0014: 0014-sandbox-seunshare-prevent-rsync-from-interpreting-pa.patch
|
||||
Patch0015: 0015-sandbox-seunshare-fix-getopt-flags.patch
|
||||
Patch0016: 0016-sandbox-seunshare-prevent-path-traversal-via-W-P.patch
|
||||
Patch0017: 0017-sandbox-seunshare-verify-RUNTIME_DIR-before-use.patch
|
||||
Patch0018: 0018-sandbox-seunshare-drop-unused-runuserdir_r.patch
|
||||
Patch0019: 0019-sandbox-seunshare-fix-killall-realloc-and-missing-ty.patch
|
||||
Patch0020: 0020-sandbox-seunshare-rewrite-to-pin-directories-before-.patch
|
||||
Patch0021: 0021-sandbox-seunshare-fully-check-setfsuid-calls.patch
|
||||
Patch0022: 0022-sandbox-seunshare-check-owner-in-seunshare_mount_fil.patch
|
||||
Patch0023: 0023-sandbox-seunshare-fix-fd_tmpdir_r-check.patch
|
||||
Patch0024: 0024-restorecond-Do-not-unlink-pidfile-if-not-used.patch
|
||||
# Patch list end
|
||||
|
||||
# gen_changelog
|
||||
|
||||
Loading…
Reference in New Issue
Block a user