import CS policycoreutils-3.6-5.el9

This commit is contained in:
AlmaLinux RelEng Bot 2026-03-30 10:53:55 -04:00
parent a6d2837367
commit 493fb87dad
5 changed files with 579 additions and 1 deletions

View File

@ -0,0 +1,98 @@
From da6f2522a8de5f2f5dc5970283298ec37445e759 Mon Sep 17 00:00:00 2001
From: Vit Mojzis <vmojzis@redhat.com>
Date: Mon, 1 Sep 2025 18:17:10 +0200
Subject: [PATCH] semanage: Reset active value when deleting boolean
customizations
Currently, removal of boolean local customizations leaves their current
(active) value untouched.
After the removal is complete, semanage_bool_query will return the
default value. But it needs to be called in a separate transaction.
This makes the fix a bit awkward, but I have not found a way to query
the default value before the first transation is committed.
Fixes:
# getsebool smbd_anon_write
smbd_anon_write --> off
# semanage boolean -m1 smbd_anon_write
# semanage boolean -D
# getsebool smbd_anon_write
smbd_anon_write --> on
# manage boolean -l isemanage boolean --list | grep smbd_anon_write
smbd_anon_write (on , off) Allow smbd to anon write
Signed-off-by: Vit Mojzis <vmojzis@redhat.com>
---
python/semanage/seobject.py | 43 +++++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/python/semanage/seobject.py b/python/semanage/seobject.py
index 10963e81..6d6188fd 100644
--- a/python/semanage/seobject.py
+++ b/python/semanage/seobject.py
@@ -2886,7 +2886,15 @@ class booleanRecords(semanageRecords):
self.__delete(name)
self.commit()
+ # New transaction to reset the boolean to its default value.
+ # Calling __reset_value in the same transaction as the removal of
+ # local customizations does nothing
+ self.begin()
+ self.__reset_value(name)
+ self.commit()
+
def deleteall(self):
+ deleted = []
(rc, self.blist) = semanage_bool_list_local(self.sh)
if rc < 0:
raise ValueError(_("Could not list booleans"))
@@ -2895,10 +2903,45 @@ class booleanRecords(semanageRecords):
for boolean in self.blist:
name = semanage_bool_get_name(boolean)
+ deleted.append(name)
self.__delete(name)
self.commit()
+ # New transaction to reset all affected booleans to their default values.
+ # Calling __reset_value in the same transaction as the removal of
+ # local customizations does nothing
+ self.begin()
+
+ for boolean in deleted:
+ self.__reset_value(boolean)
+
+ self.commit()
+
+ # Set active value to default
+ # Note: this needs to be called in a new transaction after removing local customizations
+ # in order for semanage_bool_query to fetch the default value
+ # (as opposed to the current one -- set by the local customizations)
+ def __reset_value(self, name):
+ name = selinux.selinux_boolean_sub(name)
+
+ (rc, k) = semanage_bool_key_create(self.sh, name)
+ if rc < 0:
+ raise ValueError(_("Could not create a key for %s") % name)
+
+ (rc, b) = semanage_bool_query(self.sh, k)
+ if rc < 0:
+ raise ValueError(_("Could not query boolean %s") % name)
+
+ semanage_bool_set_value(b, semanage_bool_get_value(b))
+
+ rc = semanage_bool_set_active(self.sh, k, b)
+ if rc < 0:
+ raise ValueError(_("Could not set active value of boolean %s") % name)
+
+ semanage_bool_key_free(k)
+ semanage_bool_free(b)
+
def get_all(self, locallist=0):
ddict = {}
if locallist:
--
2.52.0

View File

@ -0,0 +1,79 @@
From fe5706639f4a4c8e331274ad47cf0c5a36371f76 Mon Sep 17 00:00:00 2001
From: Rahul Sandhu <nvraxn@gmail.com>
Date: Thu, 31 Jul 2025 18:17:06 +0100
Subject: [PATCH] seunshare: always use translations when printing
Content-type: text/plain
Some errors previously were not using gettext for translations, hence
wrap them with the _ macro.
Signed-off-by: Rahul Sandhu <nvraxn@gmail.com>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
---
sandbox/seunshare.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index 1d38ea92b9ae..9bce167aeac7 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -102,7 +102,7 @@ static int set_signal_handles(void)
/* Empty the signal mask in case someone is blocking a signal */
if (sigemptyset(&empty)) {
- fprintf(stderr, "Unable to obtain empty signal set\n");
+ fprintf(stderr, _("Unable to obtain empty signal set\n"));
return -1;
}
@@ -110,12 +110,12 @@ static int set_signal_handles(void)
/* Terminate on SIGHUP */
if (signal(SIGHUP, SIG_DFL) == SIG_ERR) {
- perror("Unable to set SIGHUP handler");
+ perror(_("Unable to set SIGHUP handler"));
return -1;
}
if (signal(SIGINT, handler) == SIG_ERR) {
- perror("Unable to set SIGINT handler");
+ perror(_("Unable to set SIGINT handler"));
return -1;
}
@@ -323,7 +323,7 @@ static int rsynccmd(const char * src, const char *dst, char **cmdbuf)
/* match glob for all files in src dir */
if (asprintf(&buf, "%s/*", src) == -1) {
- fprintf(stderr, "Out of memory\n");
+ fprintf(stderr, _("Out of memory\n"));
return -1;
}
@@ -341,12 +341,12 @@ static int rsynccmd(const char * src, const char *dst, char **cmdbuf)
if (!buf) {
if (asprintf(&newbuf, "\'%s\'", path) == -1) {
- fprintf(stderr, "Out of memory\n");
+ fprintf(stderr, _("Out of memory\n"));
goto err;
}
} else {
if (asprintf(&newbuf, "%s \'%s\'", buf, path) == -1) {
- fprintf(stderr, "Out of memory\n");
+ fprintf(stderr, _("Out of memory\n"));
goto err;
}
}
@@ -357,7 +357,7 @@ static int rsynccmd(const char * src, const char *dst, char **cmdbuf)
if (buf) {
if (asprintf(&newbuf, "/usr/bin/rsync -trlHDq %s '%s'", buf, dst) == -1) {
- fprintf(stderr, "Out of memory\n");
+ fprintf(stderr, _("Out of memory\n"));
goto err;
}
*cmdbuf=newbuf;
--
2.52.0

View File

@ -0,0 +1,133 @@
From ea48acce4ff2bb6267b038c0127d9cc8b2c5b021 Mon Sep 17 00:00:00 2001
From: Rahul Sandhu <nvraxn@gmail.com>
Date: Tue, 7 Oct 2025 19:09:06 +0100
Subject: [PATCH] seunshare: fix the frail tmpdir cleanup
Content-type: text/plain
For some reason, rm is invoked via system (3) to cleanup the runtime
temp directory. This really isn't all that robust, *especially* given
that seunshare is supposed to be a security boundary. Instead do this
using libc, the API designed to be used within C programs.
Also make sure that we don't follow symbolic links; the input being
deleted is untrusted, and hence a malicious symbolic link may be placed
outside of the sandbox.
Signed-off-by: Rahul Sandhu <nvraxn@gmail.com>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
---
sandbox/seunshare.c | 78 ++++++++++++++++++++++++++++++++++++++-------
1 file changed, 66 insertions(+), 12 deletions(-)
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index 9bce167aeac7..f0f22f797f84 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -4,6 +4,7 @@
*/
#define _GNU_SOURCE
+#include <stdbool.h>
#include <signal.h>
#include <sys/fsuid.h>
#include <sys/stat.h>
@@ -373,6 +374,66 @@ err:
return rc;
}
+/*
+ * Recursively delete a directory.
+ * SAFETY: This function will NOT follow symbolic links (AT_SYMLINK_NOFOLLOW).
+ * As a result, this function can be run safely on a directory owned by
+ * a non-root user: symbolic links to root paths (such as /root) will
+ * not be followed.
+ */
+static bool rm_rf(int targetfd, const char *path) {
+ struct stat statbuf;
+
+ if (fstatat(targetfd, path, &statbuf, AT_SYMLINK_NOFOLLOW) < 0) {
+ if (errno == ENOENT) {
+ return true;
+ }
+ perror("fstatat");
+ return false;
+ }
+
+ if (S_ISDIR(statbuf.st_mode)) {
+ const int newfd = openat(targetfd, path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+ if (newfd < 0) {
+ perror("openat");
+ return false;
+ }
+
+ DIR *dir = fdopendir(newfd);
+ if (!dir) {
+ perror("fdopendir");
+ close(newfd);
+ return false;
+ }
+
+ struct dirent *entry;
+ int rc = true;
+ while ((entry = readdir(dir)) != NULL) {
+ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
+ continue;
+ }
+
+ if (!rm_rf(dirfd(dir), entry->d_name)) {
+ rc = false;
+ }
+ }
+
+ closedir(dir);
+
+ if (unlinkat(targetfd, path, AT_REMOVEDIR) < 0) {
+ perror("unlinkat");
+ rc = false;
+ }
+
+ return rc;
+ }
+ if (unlinkat(targetfd, path, 0) < 0) {
+ perror("unlinkat");
+ return false;
+ }
+ return true;
+}
+
/**
* Clean up runtime temporary directory. Returns 0 if no problem was detected,
* >0 if some error was detected, but errors here are treated as non-fatal and
@@ -398,24 +459,17 @@ static int cleanup_tmpdir(const char *tmpdir, const char *src,
free(cmdbuf); cmdbuf = NULL;
}
- /* remove files from the runtime temporary directory */
- if (asprintf(&cmdbuf, "/bin/rm -r '%s/' 2>/dev/null", tmpdir) == -1) {
- fprintf(stderr, _("Out of memory\n"));
- cmdbuf = NULL;
+ if ((uid_t)setfsuid(0) != 0) {
+ /* setfsuid does not return error, but this check makes code checkers happy */
rc++;
}
- /* this may fail if there's root-owned file left in the runtime tmpdir */
- if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) rc++;
- free(cmdbuf); cmdbuf = NULL;
- /* remove runtime temporary directory */
- if ((uid_t)setfsuid(0) != 0) {
- /* setfsuid does not return error, but this check makes code checkers happy */
+ /* Recursively remove the runtime temp directory. */
+ if (!rm_rf(AT_FDCWD, tmpdir)) {
+ fprintf(stderr, _("Failed to recursively remove directory %s\n"), tmpdir);
rc++;
}
- if (pwd->pw_uid != 0 && rmdir(tmpdir) == -1)
- fprintf(stderr, _("Failed to remove directory %s: %s\n"), tmpdir, strerror(errno));
if ((uid_t)setfsuid(pwd->pw_uid) != 0) {
fprintf(stderr, _("unable to switch back to user after clearing tmp dir\n"));
rc++;
--
2.52.0

View File

@ -0,0 +1,257 @@
From 9ad5e7f2b8d219c1ae187f8b3c664458ce7de32f Mon Sep 17 00:00:00 2001
From: Petr Lautrbach <lautrbach@redhat.com>
Date: Wed, 7 Jan 2026 17:58:34 +0100
Subject: [PATCH] sandbox/seunshare: Replace system() with execv() to prevent
shell injection
Content-type: text/plain
Refactor spawn_command() to use execv() instead of system() to eliminate
shell injection vulnerabilities.
Reported-By: Rahul Sandhu <nvraxn@gmail.com>
Signed-off-by: Petr Lautrbach <lautrbach@redhat.com>
Acked-by: James Carter <jwcart2@gmail.com>
Signed-off-by: Jason Zaman <jason@perfinion.com>
---
sandbox/seunshare.c | 132 ++++++++++++++++++++++++++------------------
1 file changed, 79 insertions(+), 53 deletions(-)
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index f0f22f797f84..b49fc2cd8050 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -55,6 +55,12 @@
#define DEFAULT_PATH "/usr/bin:/bin"
#define USAGE_STRING _("USAGE: seunshare [ -v ] [ -C ] [ -k ] [ -t tmpdir ] [ -h homedir ] [ -r runuserdir ] [ -Z CONTEXT ] -- executable [args] ")
+#define strdup_or_err(args, index, src) do { \
+ args[index] = strdup(src); \
+ if (! args[index]) \
+ goto err; \
+ } while(0)
+
static int verbose = 0;
static int child = 0;
@@ -135,15 +141,14 @@ static int set_signal_handles(void)
} while(0)
/**
- * Spawn external command using system() with dropped privileges.
- * TODO: avoid system() and use exec*() instead
+ * Spawn external command with dropped privileges.
*/
-static int spawn_command(const char *cmd, uid_t uid){
+static int spawn_command(char **cmd, uid_t uid){
int childpid;
int status = -1;
if (verbose > 1)
- printf("spawn_command: %s\n", cmd);
+ printf("spawn_command: %s\n", cmd[0]);
childpid = fork();
if (childpid == -1) {
@@ -154,8 +159,7 @@ static int spawn_command(const char *cmd, uid_t uid){
if (childpid == 0) {
if (drop_privs(uid) != 0) exit(-1);
- status = system(cmd);
- status_to_retval(status, status);
+ status = execv(cmd[0], cmd);
exit(status);
}
@@ -312,15 +316,24 @@ static int bad_path(const char *path) {
return 0;
}
-static int rsynccmd(const char * src, const char *dst, char **cmdbuf)
-{
+static void free_args(char **args) {
+ char **args_p = args;
+ if (! args)
+ return;
+ while (*args_p != NULL) {
+ free(*args_p);
+ args_p++;
+ }
+ free(args);
+}
+
+static int rsynccmd(const char * src, const char *dst, char ***cmd) {
+ char **args;
char *buf = NULL;
- char *newbuf = NULL;
glob_t fglob;
fglob.gl_offs = 0;
int flags = GLOB_PERIOD;
- unsigned int i = 0;
- int rc = -1;
+ unsigned int i = 0, index;
/* match glob for all files in src dir */
if (asprintf(&buf, "%s/*", src) == -1) {
@@ -335,43 +348,35 @@ static int rsynccmd(const char * src, const char *dst, char **cmdbuf)
free(buf); buf = NULL;
- for ( i=0; i < fglob.gl_pathc; i++) {
- const char *path = fglob.gl_pathv[i];
-
- if (bad_path(path)) continue;
-
- if (!buf) {
- if (asprintf(&newbuf, "\'%s\'", path) == -1) {
- fprintf(stderr, _("Out of memory\n"));
- goto err;
- }
- } else {
- if (asprintf(&newbuf, "%s \'%s\'", buf, path) == -1) {
- fprintf(stderr, _("Out of memory\n"));
- goto err;
- }
- }
-
- free(buf); buf = newbuf;
- newbuf = NULL;
+ /* rsync -trlHDq + <glob list> + dst + NULL */
+ *cmd = calloc(2 + fglob.gl_pathc + 2, sizeof(char *));
+ if (! *cmd) {
+ fprintf(stderr, _("Out of memory\n"));
+ return -1;
}
- if (buf) {
- if (asprintf(&newbuf, "/usr/bin/rsync -trlHDq %s '%s'", buf, dst) == -1) {
- fprintf(stderr, _("Out of memory\n"));
- goto err;
- }
- *cmdbuf=newbuf;
- }
- else {
- *cmdbuf=NULL;
- }
- rc = 0;
+ args = *cmd;
+ strdup_or_err(args, 0, "/usr/bin/rsync");
+ strdup_or_err(args, 1, "-trlHDq");
+ for ( i=0, index = 2; i < fglob.gl_pathc; i++) {
+ const char *path = fglob.gl_pathv[i];
+ if (bad_path(path)) continue;
+ strdup_or_err(args, index, path);
+ index++;
+ }
+ strdup_or_err(args, index, dst);
+ index++;
+ args[index] = NULL;
+ globfree(&fglob);
+ return 0;
err:
- free(buf); buf = NULL;
globfree(&fglob);
- return rc;
+ if (args) {
+ free_args(args);
+ *cmd = NULL;
+ }
+ return -1;
}
/*
@@ -442,21 +447,38 @@ static bool rm_rf(int targetfd, const char *path) {
static int cleanup_tmpdir(const char *tmpdir, const char *src,
struct passwd *pwd, int copy_content)
{
- char *cmdbuf = NULL;
+ char **args;
int rc = 0;
/* rsync files back */
if (copy_content) {
- if (asprintf(&cmdbuf, "/usr/bin/rsync --exclude=.X11-unix -utrlHDq --delete '%s/' '%s/'", tmpdir, src) == -1) {
+ args = calloc(7, sizeof(char *));
+ if (! args) {
fprintf(stderr, _("Out of memory\n"));
- cmdbuf = NULL;
- rc++;
+ return 1;
+ }
+
+ strdup_or_err(args, 0, "/usr/bin/rsync");
+ 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) {
+ fprintf(stderr, _("Out of memory\n"));
+ free_args(args);
+ return 1;
}
- if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) {
+ if (asprintf(&args[5], "%s/", src) == -1) {
+ fprintf(stderr, _("Out of memory\n"));
+ free_args(args);
+ return 1;
+ }
+ args[6] = NULL;
+
+ if (spawn_command(args, pwd->pw_uid) != 0) {
fprintf(stderr, _("Failed to copy files from the runtime temporary directory\n"));
rc++;
}
- free(cmdbuf); cmdbuf = NULL;
+ free_args(args);
}
if ((uid_t)setfsuid(0) != 0) {
@@ -476,6 +498,10 @@ static int cleanup_tmpdir(const char *tmpdir, const char *src,
}
return rc;
+err:
+ if (args)
+ free_args(args);
+ return 1;
}
/**
@@ -488,7 +514,7 @@ static char *create_tmpdir(const char *src, struct stat *src_st,
struct stat *out_st, struct passwd *pwd, const char *execcon)
{
char *tmpdir = NULL;
- char *cmdbuf = NULL;
+ char **cmd = NULL;
int fd_t = -1, fd_s = -1;
struct stat tmp_st;
char *con = NULL;
@@ -575,7 +601,7 @@ static char *create_tmpdir(const char *src, struct stat *src_st,
if ((uid_t)setfsuid(pwd->pw_uid) != 0)
goto err;
- if (rsynccmd(src, tmpdir, &cmdbuf) < 0) {
+ if (rsynccmd(src, tmpdir, &cmd) < 0) {
goto err;
}
@@ -583,7 +609,7 @@ static char *create_tmpdir(const char *src, struct stat *src_st,
if ((uid_t)setfsuid(0) != pwd->pw_uid)
goto err;
- if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) {
+ if (spawn_command(cmd, pwd->pw_uid) != 0) {
fprintf(stderr, _("Failed to populate runtime temporary directory\n"));
cleanup_tmpdir(tmpdir, src, pwd, 0);
goto err;
@@ -593,7 +619,7 @@ static char *create_tmpdir(const char *src, struct stat *src_st,
err:
free(tmpdir); tmpdir = NULL;
good:
- free(cmdbuf); cmdbuf = NULL;
+ free_args(cmd);
freecon(con); con = NULL;
if (fd_t >= 0) close(fd_t);
if (fd_s >= 0) close(fd_s);
--
2.52.0

View File

@ -11,7 +11,7 @@
Summary: SELinux policy core utilities
Name: policycoreutils
Version: 3.6
Release: 3%{?dist}
Release: 5%{?dist}
License: GPL-2.0-or-later
# https://github.com/SELinuxProject/selinux/wiki/Releases
Source0: https://github.com/SELinuxProject/selinux/releases/download/3.6/selinux-3.6.tar.gz
@ -57,6 +57,10 @@ Patch0018: 0018-python-semanage-Allow-modifying-records-on-add.patch
Patch0019: 0019-python-semanage-Do-not-sort-local-fcontext-definitio.patch
Patch0020: 0020-fixfiles-drop-unnecessary-line-endings.patch
Patch0021: 0021-restorecond-always-add-0-to-ut_user.patch
Patch0022: 0022-semanage-Reset-active-value-when-deleting-boolean-cu.patch
Patch0023: 0023-seunshare-always-use-translations-when-printing.patch
Patch0024: 0024-seunshare-fix-the-frail-tmpdir-cleanup.patch
Patch0025: 0025-sandbox-seunshare-Replace-system-with-execv-to-preve.patch
# Patch list end
Obsoletes: policycoreutils < 2.0.61-2
Conflicts: filesystem < 3, selinux-policy-base < 3.13.1-138
@ -466,6 +470,13 @@ The policycoreutils-restorecond package contains the restorecond service.
%systemd_postun_with_restart restorecond.service
%changelog
* Tue Feb 03 2026 Petr Lautrbach <lautrbach@redhat.com> - 3.6-5
- sandbox/seunshare: Replace system() with execv() to prevent shell injection
- seunshare: fix the frail tmpdir cleanup
* Thu Jan 15 2026 Veronika Syncakova <vsyncako@redhat.com> - 3.6-4
- semanage: Reset active value when deleting boolean customizations
* Mon Apr 28 2025 Petr Lautrbach <lautrbach@redhat.com> - 3.6-3
- restorecond: always add '\0' to ut_user