SELinux userspace 3.11 release

Resolves: RHEL-179724
This commit is contained in:
Petr Lautrbach 2026-07-01 21:56:45 +02:00 committed by Vit Mojzis
parent a5900bb459
commit 254d17b34a
30 changed files with 143 additions and 1713 deletions

2
.gitignore vendored
View File

@ -369,3 +369,5 @@ policycoreutils-2.0.83.tgz
/selinux-3.9.tar.gz.asc
/selinux-3.10.tar.gz
/selinux-3.10.tar.gz.asc
/selinux-3.11.tar.gz
/selinux-3.11.tar.gz.asc

View File

@ -1,14 +1,15 @@
From 966537fe43b84f19153592ad946384b80deda37e Mon Sep 17 00:00:00 2001
From e559f98a6d73a623ff735cab43d45b5c98403970 Mon Sep 17 00:00:00 2001
From: Dan Walsh <dwalsh@redhat.com>
Date: Fri, 14 Feb 2014 12:32:12 -0500
Subject: [PATCH] Don't be verbose if you are not on a tty
Content-type: text/plain
---
policycoreutils/scripts/fixfiles | 1 +
1 file changed, 1 insertion(+)
diff --git a/policycoreutils/scripts/fixfiles b/policycoreutils/scripts/fixfiles
index b7cd765c..f2518e96 100755
index b7cd765c15e4..f2518e96e34c 100755
--- a/policycoreutils/scripts/fixfiles
+++ b/policycoreutils/scripts/fixfiles
@@ -108,6 +108,7 @@ exclude_dirs_from_relabelling() {
@ -20,5 +21,5 @@ index b7cd765c..f2518e96 100755
THREADS=""
RPMFILES=""
--
2.52.0
2.54.0

View File

@ -1,7 +1,8 @@
From 3bdc320b86f6694e3716941899a6fca0aff8f9ae Mon Sep 17 00:00:00 2001
From 6be99263017c169d0f0bc1ed247e22a696583bad Mon Sep 17 00:00:00 2001
From: Masatake YAMATO <yamato@redhat.com>
Date: Thu, 14 Dec 2017 15:57:58 +0900
Subject: [PATCH] sepolicy-generate: Handle more reserved port types
Content-type: text/plain
Currently only reserved_port_t, port_t and hi_reserved_port_t are
handled as special when making a ports-dictionary. However, as fas as
@ -52,7 +53,7 @@ https://lore.kernel.org/selinux/20150610.190635.1866127952891120915.yamato@redha
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/python/sepolicy/sepolicy/generate.py b/python/sepolicy/sepolicy/generate.py
index 780a56b2..cde6a840 100644
index 0edda54ad5a1..02d9066c8a42 100644
--- a/python/sepolicy/sepolicy/generate.py
+++ b/python/sepolicy/sepolicy/generate.py
@@ -100,7 +100,9 @@ def get_all_ports():
@ -67,5 +68,5 @@ index 780a56b2..cde6a840 100644
dict[(p['low'], p['high'], p['protocol'])] = (p['type'], p.get('range'))
return dict
--
2.52.0
2.54.0

View File

@ -1,7 +1,8 @@
From d1b75aa6f0cd9c45f025bb619821183446ebfe28 Mon Sep 17 00:00:00 2001
From 211d44c2e7b629f3ac073147a4c31449bcbe4478 Mon Sep 17 00:00:00 2001
From: Petr Lautrbach <plautrba@redhat.com>
Date: Wed, 18 Jul 2018 09:09:35 +0200
Subject: [PATCH] sandbox: Use matchbox-window-manager instead of openbox
Content-type: text/plain
---
sandbox/sandbox | 4 ++--
@ -10,10 +11,10 @@ Subject: [PATCH] sandbox: Use matchbox-window-manager instead of openbox
3 files changed, 3 insertions(+), 17 deletions(-)
diff --git a/sandbox/sandbox b/sandbox/sandbox
index e3fd6119..e01425f0 100644
index 4e7d56fdedfd..5f79c5367bda 100644
--- a/sandbox/sandbox
+++ b/sandbox/sandbox
@@ -270,7 +270,7 @@ class Sandbox:
@@ -266,7 +266,7 @@ class Sandbox:
copyfile(f, "/tmp", self.__tmpdir)
copyfile(f, "/var/tmp", self.__tmpdir)
@ -22,7 +23,7 @@ index e3fd6119..e01425f0 100644
execfile = self.__homedir + "/.sandboxrc"
fd = open(execfile, "w+")
if self.__options.session:
@@ -370,7 +370,7 @@ sandbox [-h] [-l level ] [-[X|M] [-H homedir] [-T tempdir]] [-I includefile ] [-
@@ -366,7 +366,7 @@ sandbox [-h] [-l level ] [-[X|M] [-H homedir] [-T tempdir]] [-I includefile ] [-
parser.add_option("-W", "--windowmanager", dest="wm",
type="string",
@ -32,7 +33,7 @@ index e3fd6119..e01425f0 100644
parser.add_option("-l", "--level", dest="level",
diff --git a/sandbox/sandbox.8 b/sandbox/sandbox.8
index 095b9e27..1c187019 100644
index 095b9e27042d..1c1870190e51 100644
--- a/sandbox/sandbox.8
+++ b/sandbox/sandbox.8
@@ -80,7 +80,7 @@ Specifies the windowsize when creating an X based Sandbox. The default windowsiz
@ -45,7 +46,7 @@ index 095b9e27..1c187019 100644
\fB\-X\fR
Create an X based Sandbox for gui apps, temporary files for
diff --git a/sandbox/sandboxX.sh b/sandbox/sandboxX.sh
index 28169182..e2a7ad9b 100644
index 28169182ce42..e2a7ad9b2ac7 100644
--- a/sandbox/sandboxX.sh
+++ b/sandbox/sandboxX.sh
@@ -7,20 +7,6 @@ export TITLE="Sandbox $context -- `grep ^#TITLE: ~/.sandboxrc | /usr/bin/cut -b8
@ -70,5 +71,5 @@ index 28169182..e2a7ad9b 100644
if [ -z "$WAYLAND_DISPLAY" ]; then
DISPLAY_COMMAND='/usr/bin/Xephyr -resizeable -title "$TITLE" -terminate -screen $SCREENSIZE -dpi $DPI -nolisten tcp -displayfd 5 5>&1 2>/dev/null'
--
2.52.0
2.54.0

View File

@ -1,7 +1,8 @@
From 74f566f16de93662e022d37d616a2f5f47f27f45 Mon Sep 17 00:00:00 2001
From cbaf1040c655bd544eedca3b8e070720dfc3b2be Mon Sep 17 00:00:00 2001
From: Petr Lautrbach <plautrba@redhat.com>
Date: Fri, 30 Jul 2021 14:14:37 +0200
Subject: [PATCH] Use SHA-2 instead of SHA-1
Content-type: text/plain
The use of SHA-1 in RHEL9 is deprecated
---
@ -12,7 +13,7 @@ The use of SHA-1 in RHEL9 is deprecated
4 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/policycoreutils/setfiles/restorecon.8 b/policycoreutils/setfiles/restorecon.8
index b7ff9715..66b9c800 100644
index b7ff9715a49b..66b9c800dc73 100644
--- a/policycoreutils/setfiles/restorecon.8
+++ b/policycoreutils/setfiles/restorecon.8
@@ -103,14 +103,14 @@ display usage information and exit.
@ -52,7 +53,7 @@ index b7ff9715..66b9c800 100644
and provided the
.B \-n
diff --git a/policycoreutils/setfiles/restorecon_xattr.8 b/policycoreutils/setfiles/restorecon_xattr.8
index 51d12a4d..09bfd8c4 100644
index 51d12a4dbb80..09bfd8c40ab4 100644
--- a/policycoreutils/setfiles/restorecon_xattr.8
+++ b/policycoreutils/setfiles/restorecon_xattr.8
@@ -23,7 +23,7 @@ or
@ -89,7 +90,7 @@ index 51d12a4d..09bfd8c4 100644
.I security.sehash
directory digest entries, and is shown for reference only).
diff --git a/policycoreutils/setfiles/restorecon_xattr.c b/policycoreutils/setfiles/restorecon_xattr.c
index 31fb82fd..bc22d3fd 100644
index e8604ec63c29..738319edb4a4 100644
--- a/policycoreutils/setfiles/restorecon_xattr.c
+++ b/policycoreutils/setfiles/restorecon_xattr.c
@@ -38,7 +38,7 @@ int main(int argc, char **argv)
@ -101,7 +102,7 @@ index 31fb82fd..bc22d3fd 100644
unsigned char *fc_digest = NULL;
size_t i, fc_digest_len = 0, num_specfiles;
@@ -133,8 +133,8 @@ int main(int argc, char **argv)
@@ -134,8 +134,8 @@ int main(int argc, char **argv)
exit(-1);
}
@ -109,9 +110,9 @@ index 31fb82fd..bc22d3fd 100644
- if (!sha1_buf) {
+ sha256_buf = malloc(fc_digest_len * 2 + 1);
+ if (!sha256_buf) {
fprintf(stderr,
"Error allocating digest buffer: %s\n",
strerror(errno));
fprintf(stderr, "Error allocating digest buffer: %s\n",
strerror(errno));
selabel_close(hnd);
@@ -143,16 +143,16 @@ int main(int argc, char **argv)
}
@ -133,7 +134,7 @@ index 31fb82fd..bc22d3fd 100644
}
diff --git a/policycoreutils/setfiles/setfiles.8 b/policycoreutils/setfiles/setfiles.8
index d43e4ad2..458f3f9f 100644
index d43e4ad20f54..458f3f9f1693 100644
--- a/policycoreutils/setfiles/setfiles.8
+++ b/policycoreutils/setfiles/setfiles.8
@@ -105,14 +105,14 @@ display usage information and exit.
@ -173,5 +174,5 @@ index d43e4ad2..458f3f9f 100644
and provided the
.B \-n
--
2.52.0
2.54.0

View File

@ -1,7 +1,8 @@
From 4d7b6cd22b09eec515e73bc26a640d888a222a9c Mon Sep 17 00:00:00 2001
From 33b90b3f2e7904c37003e525cb0a5459dea3db38 Mon Sep 17 00:00:00 2001
From: Vit Mojzis <vmojzis@redhat.com>
Date: Tue, 30 May 2023 09:07:28 +0200
Subject: [PATCH] python/sepolicy: Fix spec file dependencies
Content-type: text/plain
semanage is part of policycoreutils-python-utils package, selinuxenabled
is part of libselinux-utils (required by ^^^) and restorecon/load_policy
@ -13,7 +14,7 @@ Signed-off-by: Vit Mojzis <vmojzis@redhat.com>
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/python/sepolicy/sepolicy/templates/spec.py b/python/sepolicy/sepolicy/templates/spec.py
index 433c298a..a6d4508b 100644
index 433c298a17e0..a6d4508bb670 100644
--- a/python/sepolicy/sepolicy/templates/spec.py
+++ b/python/sepolicy/sepolicy/templates/spec.py
@@ -11,18 +11,20 @@ Version: 1.0
@ -43,5 +44,5 @@ index 433c298a..a6d4508b 100644
mid_section="""\
--
2.52.0
2.54.0

View File

@ -1,7 +1,8 @@
From 7bc3e35c8c8e158e8cba30f7dd42e6a68a8378b3 Mon Sep 17 00:00:00 2001
From b5d8719fdcaa7a7209bacb6e0d2f57f0f428706d Mon Sep 17 00:00:00 2001
From: Petr Lautrbach <lautrbach@redhat.com>
Date: Mon, 5 May 2025 18:28:40 +0200
Subject: [PATCH] sepolicy: Fix detection of writeable locations
Content-type: text/plain
- update substitutions from /etc/dnf/var
@ -25,17 +26,20 @@ Fixes:
Signed-off-by: Petr Lautrbach <lautrbach@redhat.com>
---
python/sepolicy/sepolicy/generate.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
python/sepolicy/sepolicy/generate.py | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/python/sepolicy/sepolicy/generate.py b/python/sepolicy/sepolicy/generate.py
index cde6a840..9b360e96 100644
index 02d9066c8a42..9b360e96c9d1 100644
--- a/python/sepolicy/sepolicy/generate.py
+++ b/python/sepolicy/sepolicy/generate.py
@@ -1324,15 +1324,15 @@ allow %s_t %s_t:%s_socket name_%s;
@@ -1324,18 +1324,15 @@ allow %s_t %s_t:%s_socket name_%s;
import dnf
with dnf.Base() as base:
- if base.conf.substitutions.get('releasever') is None:
- base.conf.substitutions['releasever'] = ''
-
+ base.conf.substitutions.update_from_etc('/')
base.read_all_repos()
base.fill_sack(load_system_repo=True)
@ -51,7 +55,7 @@ index cde6a840..9b360e96 100644
self.rpms.append(pkg.name)
for fname in pkg.files:
for b in self.DEFAULT_DIRS:
@@ -1345,7 +1345,7 @@ allow %s_t %s_t:%s_socket name_%s;
@@ -1348,7 +1345,7 @@ allow %s_t %s_t:%s_socket name_%s;
self.add_dir(fname)
sq = query.available()
sq = sq.filter(provides=pkg.source_name)
@ -61,5 +65,5 @@ index cde6a840..9b360e96 100644
for b in self.DEFAULT_DIRS:
if b == "/etc":
--
2.52.0
2.54.0

View File

@ -0,0 +1,27 @@
From eeb5dbe746492dd4722745c5cf7ba2b46ac0a805 Mon Sep 17 00:00:00 2001
From: Vit Mojzis <vmojzis@redhat.com>
Date: Fri, 10 Jul 2026 13:20:24 +0200
Subject: [PATCH] Revert "libselinux,python: Use system Python3 build module"
This reverts commit e11e59675f6981e8b7cf990478887c9147b2b20a.
python3-build is not available in RHEL-10
---
python/sepolicy/Makefile | 1 +
1 file changed, 1 insertion(+)
diff --git a/python/sepolicy/Makefile b/python/sepolicy/Makefile
index 51812980..4a4a3512 100644
--- a/python/sepolicy/Makefile
+++ b/python/sepolicy/Makefile
@@ -14,6 +14,7 @@ BASHCOMPLETIONS=sepolicy-bash-completion.sh
all: python-build
python-build:
+ $(PYTHON) -m pip install build wheel setuptools
$(PYTHON) -m build --no-isolation --wheel .
clean:
--
2.53.0

View File

@ -1,94 +0,0 @@
From 8f63d68747c38b2d2301afbdaeca88c1cf020552 Mon Sep 17 00:00:00 2001
From: Petr Lautrbach <lautrbach@redhat.com>
Date: Thu, 2 Apr 2026 17:17:12 +0200
Subject: [PATCH] restorecond: Add -F for run in foreground
Content-type: text/plain
Signed-off-by: Petr Lautrbach <lautrbach@redhat.com>
---
restorecond/restorecond.8 | 5 ++++-
restorecond/restorecond.c | 13 ++++++++-----
2 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/restorecond/restorecond.8 b/restorecond/restorecond.8
index bf8ec87562f8..bb99b24a2d69 100644
--- a/restorecond/restorecond.8
+++ b/restorecond/restorecond.8
@@ -3,7 +3,7 @@
restorecond \- daemon that watches for file creation and then sets the default SELinux file context
.SH "SYNOPSIS"
-.B restorecond [\-d] [-h] [\-f restorecond_file ] [\-u] [\-v]
+.B restorecond [\-d] [-h] [\-f restorecond_file ] [\-F] [\-u] [\-v]
.P
.SH "DESCRIPTION"
@@ -26,6 +26,9 @@ Print usage statement.
.B \-f restorecond_file
Use alternative restorecond.conf file.
.TP
+.B \-F
+Run in foreground, do not become a daemon.
+.TP
.B \-u
Turns on user mode. Runs restorecond in the user session and reads /etc/selinux/restorecond_user.conf. Uses dbus to make sure only one restorecond is running per user session.
.TP
diff --git a/restorecond/restorecond.c b/restorecond/restorecond.c
index d5f70fc2e2c1..36f82ae5e9cb 100644
--- a/restorecond/restorecond.c
+++ b/restorecond/restorecond.c
@@ -76,6 +76,7 @@ int debug_mode = 0;
int terminate = 0;
int master_wd = -1;
int run_as_user = 0;
+int foreground_mode = 0;
static void done(void) {
watch_list_free(master_fd);
@@ -124,7 +125,7 @@ static void term_handler(int s __attribute__ ((unused)))
static void usage(char *program)
{
- printf("%s [-d] [-f restorecond_file ] [-u] [-v] \n", program);
+ printf("%s [-d] [-f restorecond_file ] [-F] [-n] [-u] [-v] \n", program);
}
void exitApp(const char *msg)
@@ -165,7 +166,7 @@ int main(int argc, char **argv)
sigaction(SIGTERM, &sa, NULL);
atexit( done );
- while ((opt = getopt(argc, argv, "hdf:uv")) > 0) {
+ while ((opt = getopt(argc, argv, "hdf:Fuv")) > 0) {
switch (opt) {
case 'd':
debug_mode = 1;
@@ -173,6 +174,9 @@ int main(int argc, char **argv)
case 'f':
watch_file = optarg;
break;
+ case 'F':
+ foreground_mode = 1;
+ break;
case 'u':
run_as_user = 1;
break;
@@ -209,13 +213,12 @@ int main(int argc, char **argv)
read_config(master_fd, watch_file);
- if (!debug_mode) {
+ if (!debug_mode && !foreground_mode) {
if (daemon(0, 0) < 0)
exitApp("daemon");
+ write_pid_file();
}
- write_pid_file();
-
while (watch(master_fd, watch_file) == 0) {
}
--
2.53.0

View File

@ -0,0 +1,56 @@
From 11d3ceaac388023652935bae245b19d490350472 Mon Sep 17 00:00:00 2001
From: Vit Mojzis <vmojzis@redhat.com>
Date: Fri, 10 Jul 2026 13:28:31 +0200
Subject: [PATCH] Revert "python/sepolicy: update for modern python builds"
This reverts commit 3aac92b7250ed57fd4ab674ffcf03d9c499b3188.
'pip install build' fails on systems without network and python3-build
is not available in RHEL-10.
---
python/sepolicy/.gitignore | 1 -
python/sepolicy/Makefile | 10 +++++-----
2 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/python/sepolicy/.gitignore b/python/sepolicy/.gitignore
index 80824997..32fb47f9 100644
--- a/python/sepolicy/.gitignore
+++ b/python/sepolicy/.gitignore
@@ -2,4 +2,3 @@ build
tmp
*.bak
sepolicy.egg-info/
-dist/
diff --git a/python/sepolicy/Makefile b/python/sepolicy/Makefile
index 4a4a3512..ccb22fe7 100644
--- a/python/sepolicy/Makefile
+++ b/python/sepolicy/Makefile
@@ -14,11 +14,11 @@ BASHCOMPLETIONS=sepolicy-bash-completion.sh
all: python-build
python-build:
- $(PYTHON) -m pip install build wheel setuptools
- $(PYTHON) -m build --no-isolation --wheel .
+ $(PYTHON) setup.py build
clean:
- -rm -rf build dist *.egg-info *~ \#* *pyc .#*
+ $(PYTHON) setup.py clean
+ -rm -rf build *~ \#* *pyc .#* sepolicy.egg-info/
sepolgen:
ln -sf sepolicy sepolgen
@@ -26,8 +26,8 @@ sepolgen:
test:
@$(PYTHON) test_sepolicy.py -v
-install: python-build
- $(PYTHON) -m pip install --no-build-isolation --force-reinstall --prefix=$(PREFIX) `test -n "$(DESTDIR)" && echo --root $(DESTDIR) --ignore-installed --no-deps` dist/*.whl
+install:
+ $(PYTHON) -m pip install --no-build-isolation --prefix=$(PREFIX) `test -n "$(DESTDIR)" && echo --root $(DESTDIR) --ignore-installed --no-deps` $(PYTHON_SETUP_ARGS) .
[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
install -m 755 sepolicy.py $(DESTDIR)$(BINDIR)/sepolicy
(cd $(DESTDIR)$(BINDIR); ln -sf sepolicy sepolgen)
--
2.53.0

View File

@ -1,39 +0,0 @@
From 9a2d20910e4f6f35a76513627b68bfbfda7f6fd0 Mon Sep 17 00:00:00 2001
From: Petr Lautrbach <lautrbach@redhat.com>
Date: Thu, 2 Apr 2026 17:19:05 +0200
Subject: [PATCH] restorecond.service: Use Type=simple
Content-type: text/plain
There's a race condition when daemon() in parent process immediately
exits while pid file is not created or updated. Using Type=forking it
can confuse systemd which tries to open non-existing pid file. It's
better to run restocond in foreground and use Type=simple
Fixes:
openat(80</run>, "restorecond.pid", O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH) = -1 ENOENT (No such file or directory) <0.000006>
Signed-off-by: Petr Lautrbach <lautrbach@redhat.com>
---
restorecond/restorecond.service | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/restorecond/restorecond.service b/restorecond/restorecond.service
index 0e4ea72d08ae..56f970707d93 100644
--- a/restorecond/restorecond.service
+++ b/restorecond/restorecond.service
@@ -5,9 +5,8 @@ ConditionPathExists=/etc/selinux/restorecond.conf
ConditionSecurity=selinux
[Service]
-Type=forking
-ExecStart=/usr/sbin/restorecond
-PIDFile=/run/restorecond.pid
+Type=simple
+ExecStart=/usr/sbin/restorecond -F
[Install]
WantedBy=multi-user.target
--
2.53.0

View File

@ -1,63 +0,0 @@
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

View File

@ -1,30 +0,0 @@
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

View File

@ -1,52 +0,0 @@
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

View File

@ -1,33 +0,0 @@
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

View File

@ -1,86 +0,0 @@
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

View File

@ -1,75 +0,0 @@
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

View File

@ -1,44 +0,0 @@
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

View File

@ -1,47 +0,0 @@
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

View File

@ -1,38 +0,0 @@
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

View File

@ -1,48 +0,0 @@
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

View File

@ -1,95 +0,0 @@
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

View File

@ -1,606 +0,0 @@
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

View File

@ -1,158 +0,0 @@
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

View File

@ -1,85 +0,0 @@
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

View File

@ -1,29 +0,0 @@
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

View File

@ -1,30 +0,0 @@
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

View File

@ -1,3 +1,6 @@
* Wed Jul 01 2026 Petr Lautrbach <lautrbach@redhat.com> - 3.11-1
- SELinux userspace 3.11 release
* Mon May 25 2026 Petr Lautrbach <lautrbach@redhat.com> - 3.10-3
- Several sandbox and seunshare security improvements

View File

@ -1,7 +1,7 @@
%global libauditver 4.0
%global libsepolver 3.10-1
%global libsemanagever 3.10-1
%global libselinuxver 3.10-1
%global libsepolver 3.11-1
%global libsemanagever 3.11-1
%global libselinuxver 3.11-1
%global generatorsdir %{_prefix}/lib/systemd/system-generators
@ -10,13 +10,13 @@
Summary: SELinux policy core utilities
Name: policycoreutils
Version: 3.10
Release: 3%{?dist}
Version: 3.11
Release: 1%{?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
Source1: https://github.com/SELinuxProject/selinux/releases/download/%{version}/selinux-%{version}.tar.gz.asc
Source2: https://github.com/perfinion.gpg
Source2: https://github.com/bachradsusi.gpg
Source3: changelog
Source4: macros
URL: https://github.com/SELinuxProject/selinux
@ -37,7 +37,7 @@ Source22: selinux-gui.zip
# wlc --key <apikey> --url https://translate.fedoraproject.org/api/ download selinux/sandbox --output ./
Source23: selinux-sandbox.zip
# https://github.com/fedora-selinux/selinux
# $ git format-patch -N 3.10 -- policycoreutils python gui sandbox dbus semodule-utils restorecond
# $ git format-patch -N 3.11 -- policycoreutils python gui sandbox dbus semodule-utils restorecond
# $ for j in [0-9]*.patch; do printf "Patch%s: %s\n" ${j/-*/} $j; done
# Patch list start
Patch0001: 0001-Don-t-be-verbose-if-you-are-not-on-a-tty.patch
@ -46,24 +46,8 @@ Patch0003: 0003-sandbox-Use-matchbox-window-manager-instead-of-openb.patch
Patch0004: 0004-Use-SHA-2-instead-of-SHA-1.patch
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
Patch0007: 0007-Revert-libselinux-python-Use-system-Python3-build-mo.patch
Patch0008: 0008-Revert-python-sepolicy-update-for-modern-python-buil.patch
# Patch list end
# gen_changelog
@ -79,7 +63,8 @@ Provides: /sbin/restorecon
BuildRequires: gcc make
BuildRequires: pam-devel libsepol-static >= %{libsepolver} libsemanage-devel >= %{libsemanagever} libselinux-devel >= %{libselinuxver} libcap-devel audit-libs-devel >= %{libauditver} gettext
BuildRequires: desktop-file-utils dbus-devel glib2-devel
BuildRequires: python3-devel python3-setuptools python3-wheel python3-pip
BuildRequires: python3-devel python3-setuptools python3-pip
BuildRequires: (python3-wheel if python3-setuptools < 71)
BuildRequires: systemd
BuildRequires: git-core
BuildRequires: gnupg2

View File

@ -2,5 +2,5 @@ SHA512 (selinux-policycoreutils.zip) = 0df9dc274e0d1a2e4e2467f95a18a5bf7b6de2428
SHA512 (selinux-python.zip) = 35d209f8bcff498f66465499fcc4cef0780781276a4ba060b2d1d56eed1dd72d253f6b0eae5f679d46cf426b967a7aadac909363513be5d483c95a31249eacdd
SHA512 (selinux-sandbox.zip) = ecbc0c8280eb6c013b039a2e63ee5a361cd84807613962a012ac0a98092357e9809bea23c3c71bd8ae4745b1dd12a4fce43db5e1cab31614f386a2a8db88b733
SHA512 (selinux-gui.zip) = 3ae41eba5dd6d34e10dfdb97f4194d170ace2f3044e984077db7d26d05bdaad86625e48e5694e3e8680487ad99a50861d4bea30c4bf08e2820e3b7a8671270c7
SHA512 (selinux-3.10.tar.gz) = 40ada85709ad23c368579bc661661ac77f4ada6d9a65243bc25de8e46202baed264d1b49de1765d5dd21ebc8d94d872b86d2b0e7b56a7269f1942d609ff7278e
SHA512 (selinux-3.10.tar.gz.asc) = 5efaecb97be2da435c7ece88e213dd753a4c6ee6f6a2726f25b82b0e30c77a5136efb593fb9590c0395f0c63e9536abf407dcec95b3d62027defbeafaa5d4b91
SHA512 (selinux-3.11.tar.gz) = 67a460e0ee8c652ee88b1defb56ae2cb9d46c45a3a92d4add631cb7073377a846f31901a8fa659a664e7c3190531f58d774c99321d7870b542a99e033a16df32
SHA512 (selinux-3.11.tar.gz.asc) = 866acdc35ff2e6450cefe1a5350c537aa5877c1ee6c39b985e3bfde8f12558b9df890450ce34b8463590abe54033052474267453d6820675f9b1d0e36dd43fcf