407 lines
12 KiB
Diff
407 lines
12 KiB
Diff
From ed40333a23ae8f20ac0360df444d10db369fa6d9 Mon Sep 17 00:00:00 2001
|
|
From: "Richard W.M. Jones" <rjones@redhat.com>
|
|
Date: Tue, 12 Aug 2025 12:22:42 +0100
|
|
Subject: [PATCH] daemon: Reimplement guestfs_selinux_relabel in OCaml
|
|
|
|
No change, just reimplement the existing C implementation in OCaml.
|
|
---
|
|
.gitignore | 1 +
|
|
daemon/Makefile.am | 4 +-
|
|
daemon/selinux-relabel.c | 169 --------------------------------------
|
|
daemon/selinux.c | 7 ++
|
|
daemon/selinux.ml | 101 +++++++++++++++++++++++
|
|
docs/C_SOURCE_FILES | 1 -
|
|
generator/actions_core.ml | 1 +
|
|
po/POTFILES | 1 -
|
|
8 files changed, 113 insertions(+), 172 deletions(-)
|
|
delete mode 100644 daemon/selinux-relabel.c
|
|
create mode 100644 daemon/selinux.ml
|
|
|
|
diff --git a/.gitignore b/.gitignore
|
|
index 81cd278cc..02160caff 100644
|
|
--- a/.gitignore
|
|
+++ b/.gitignore
|
|
@@ -108,6 +108,7 @@ Makefile.in
|
|
/daemon/parted.mli
|
|
/daemon/realpath.mli
|
|
/daemon/rpm.mli
|
|
+/daemon/selinux.mli
|
|
/daemon/sfdisk.mli
|
|
/daemon/stamp-guestfsd.pod
|
|
/daemon/statvfs.mli
|
|
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
|
|
index 6d7492013..c644d9881 100644
|
|
--- a/daemon/Makefile.am
|
|
+++ b/daemon/Makefile.am
|
|
@@ -59,6 +59,7 @@ generator_built = \
|
|
parted.mli \
|
|
realpath.mli \
|
|
rpm.mli \
|
|
+ selinux.mli \
|
|
sfdisk.mli \
|
|
statvfs.mli \
|
|
structs.ml \
|
|
@@ -173,7 +174,6 @@ guestfsd_SOURCES = \
|
|
rsync.c \
|
|
scrub.c \
|
|
selinux.c \
|
|
- selinux-relabel.c \
|
|
sfdisk.c \
|
|
sh.c \
|
|
sleep.c \
|
|
@@ -307,6 +307,7 @@ SOURCES_MLI = \
|
|
parted.mli \
|
|
realpath.mli \
|
|
rpm.mli \
|
|
+ selinux.mli \
|
|
sfdisk.mli \
|
|
statvfs.mli \
|
|
structs.mli \
|
|
@@ -345,6 +346,7 @@ SOURCES_ML = \
|
|
listfs.ml \
|
|
realpath.ml \
|
|
statvfs.ml \
|
|
+ selinux.ml \
|
|
inspect_types.ml \
|
|
inspect_utils.ml \
|
|
inspect_fs_unix_fstab.ml \
|
|
diff --git a/daemon/selinux-relabel.c b/daemon/selinux-relabel.c
|
|
deleted file mode 100644
|
|
index cfc5a31d9..000000000
|
|
--- a/daemon/selinux-relabel.c
|
|
+++ /dev/null
|
|
@@ -1,169 +0,0 @@
|
|
-/* libguestfs - the guestfsd daemon
|
|
- * Copyright (C) 2016 Red Hat Inc.
|
|
- *
|
|
- * This program is free software; you can redistribute it and/or modify
|
|
- * it under the terms of the GNU General Public License as published by
|
|
- * the Free Software Foundation; either version 2 of the License, or
|
|
- * (at your option) any later version.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * You should have received a copy of the GNU General Public License
|
|
- * along with this program; if not, write to the Free Software
|
|
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
- */
|
|
-
|
|
-#include <config.h>
|
|
-
|
|
-#include <stdio.h>
|
|
-#include <stdlib.h>
|
|
-#include <string.h>
|
|
-#include <sys/stat.h>
|
|
-
|
|
-#include "guestfs_protocol.h"
|
|
-#include "daemon.h"
|
|
-#include "actions.h"
|
|
-#include "optgroups.h"
|
|
-
|
|
-#include "ignore-value.h"
|
|
-
|
|
-#define MAX_ARGS 64
|
|
-
|
|
-int
|
|
-optgroup_selinuxrelabel_available (void)
|
|
-{
|
|
- return prog_exists ("setfiles");
|
|
-}
|
|
-
|
|
-static int
|
|
-dir_exists (const char *dir)
|
|
-{
|
|
- struct stat statbuf;
|
|
-
|
|
- if (stat (dir, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
|
|
- return 1;
|
|
- else
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int
|
|
-setfiles_has_option (int *flag, char opt_char)
|
|
-{
|
|
- CLEANUP_FREE char *err = NULL;
|
|
-
|
|
- if (*flag == -1) {
|
|
- char option[] = { '-', opt_char, '\0' }; /* "-X" */
|
|
- char err_opt[32]; /* "invalid option -- 'X'" */
|
|
-
|
|
- snprintf(err_opt, sizeof(err_opt), "invalid option -- '%c'", opt_char);
|
|
- ignore_value (command (NULL, &err, "setfiles", option, NULL));
|
|
- *flag = err && strstr (err, /* "invalid option -- " */ err_opt) == NULL;
|
|
- }
|
|
-
|
|
- return *flag;
|
|
-}
|
|
-
|
|
-/* Takes optional arguments, consult optargs_bitmask. */
|
|
-int
|
|
-do_selinux_relabel (const char *specfile, const char *path,
|
|
- int force)
|
|
-{
|
|
- static int flag_m = -1;
|
|
- static int flag_C = -1;
|
|
- static int flag_T = -1;
|
|
- const char *argv[MAX_ARGS];
|
|
- CLEANUP_FREE char *s_dev = NULL, *s_proc = NULL, *s_selinux = NULL,
|
|
- *s_sys = NULL, *s_specfile = NULL, *s_path = NULL;
|
|
- CLEANUP_FREE char *err = NULL;
|
|
- size_t i = 0;
|
|
- int setfiles_status;
|
|
-
|
|
- s_dev = sysroot_path ("/dev");
|
|
- if (!s_dev) {
|
|
- malloc_error:
|
|
- reply_with_perror ("malloc");
|
|
- return -1;
|
|
- }
|
|
- s_proc = sysroot_path ("/proc"); if (!s_proc) goto malloc_error;
|
|
- s_selinux = sysroot_path ("/selinux"); if (!s_selinux) goto malloc_error;
|
|
- s_sys = sysroot_path ("/sys"); if (!s_sys) goto malloc_error;
|
|
- s_specfile = sysroot_path (specfile); if (!s_specfile) goto malloc_error;
|
|
- s_path = sysroot_path (path); if (!s_path) goto malloc_error;
|
|
-
|
|
- /* Default settings if not selected. */
|
|
- if (!(optargs_bitmask & GUESTFS_SELINUX_RELABEL_FORCE_BITMASK))
|
|
- force = 0;
|
|
-
|
|
- /* If setfiles takes an excessively long time to run (but still
|
|
- * completes) then removing .../contexts/files/file_contexts.bin
|
|
- * appears to help. If you find any such cases, please add
|
|
- * observations to the bug report:
|
|
- * https://bugzilla.redhat.com/show_bug.cgi?id=1396297
|
|
- */
|
|
- ADD_ARG (argv, i, "setfiles");
|
|
- if (force)
|
|
- ADD_ARG (argv, i, "-F");
|
|
-
|
|
- /* Exclude some directories that should never be relabelled in
|
|
- * ordinary Linux guests. These won't be mounted anyway. We have
|
|
- * to prefix all these with the sysroot path.
|
|
- */
|
|
- ADD_ARG (argv, i, "-e"); ADD_ARG (argv, i, s_dev);
|
|
- ADD_ARG (argv, i, "-e"); ADD_ARG (argv, i, s_proc);
|
|
- ADD_ARG (argv, i, "-e"); ADD_ARG (argv, i, s_sys);
|
|
- if (dir_exists (s_selinux)) {
|
|
- ADD_ARG (argv, i, "-e"); ADD_ARG (argv, i, s_selinux);
|
|
- }
|
|
-
|
|
- /* You have to use the -m option (where available) otherwise
|
|
- * setfiles puts all the mountpoints on the excludes list for no
|
|
- * useful reason (RHBZ#1433577).
|
|
- */
|
|
- if (setfiles_has_option (&flag_m, 'm'))
|
|
- ADD_ARG (argv, i, "-m");
|
|
-
|
|
- /* Not only do we want setfiles to trudge through individual relabeling
|
|
- * errors, we also want the setfiles exit status to differentiate a fatal
|
|
- * error from "relabeling errors only". See RHBZ#1794518.
|
|
- */
|
|
- if (setfiles_has_option (&flag_C, 'C'))
|
|
- ADD_ARG (argv, i, "-C");
|
|
-
|
|
- /* If the appliance is being run with multiple vCPUs, running setfiles
|
|
- * in multithreading mode might speeds up the process. Option "-T" was
|
|
- * introduced in SELinux userspace v3.4, and we need to check whether it's
|
|
- * supported. Passing "-T 0" creates as many threads as there're available
|
|
- * vCPU cores.
|
|
- * https://github.com/SELinuxProject/selinux/releases/tag/3.4
|
|
- */
|
|
- if (setfiles_has_option (&flag_T, 'T')) {
|
|
- ADD_ARG (argv, i, "-T"); ADD_ARG (argv, i, "0");
|
|
- }
|
|
-
|
|
- /* Relabelling in a chroot. */
|
|
- if (STRNEQ (sysroot, "/")) {
|
|
- ADD_ARG (argv, i, "-r");
|
|
- ADD_ARG (argv, i, sysroot);
|
|
- }
|
|
-
|
|
- if (verbose)
|
|
- ADD_ARG (argv, i, "-v");
|
|
- else
|
|
- /* Suppress non-error output. */
|
|
- ADD_ARG (argv, i, "-q");
|
|
-
|
|
- /* Add parameters. */
|
|
- ADD_ARG (argv, i, s_specfile);
|
|
- ADD_ARG (argv, i, s_path);
|
|
- ADD_ARG (argv, i, NULL);
|
|
-
|
|
- setfiles_status = commandrv (NULL, &err, argv);
|
|
- if ((setfiles_status == 0) || (setfiles_status == 1 && flag_C))
|
|
- return 0;
|
|
-
|
|
- reply_with_error ("%s", err);
|
|
- return -1;
|
|
-}
|
|
diff --git a/daemon/selinux.c b/daemon/selinux.c
|
|
index f4d839c19..4500d0096 100644
|
|
--- a/daemon/selinux.c
|
|
+++ b/daemon/selinux.c
|
|
@@ -39,6 +39,13 @@ optgroup_selinux_available (void)
|
|
return 1;
|
|
}
|
|
|
|
+/* For historical reasons, this is really "is setfiles available" */
|
|
+int
|
|
+optgroup_selinuxrelabel_available (void)
|
|
+{
|
|
+ return prog_exists ("setfiles");
|
|
+}
|
|
+
|
|
/* setcon is only valid under the following circumstances:
|
|
* - single threaded
|
|
* - enforcing=0
|
|
diff --git a/daemon/selinux.ml b/daemon/selinux.ml
|
|
new file mode 100644
|
|
index 000000000..d954fdead
|
|
--- /dev/null
|
|
+++ b/daemon/selinux.ml
|
|
@@ -0,0 +1,101 @@
|
|
+(* SELinux functions.
|
|
+ * Copyright (C) 2009-2025 Red Hat Inc.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License along
|
|
+ * with this program; if not, write to the Free Software Foundation, Inc.,
|
|
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
+ *)
|
|
+
|
|
+open Printf
|
|
+
|
|
+open Std_utils
|
|
+
|
|
+open Sysroot
|
|
+open Utils
|
|
+
|
|
+(* Test if setfiles has various options.
|
|
+ *
|
|
+ * The only way to do this is to run setfiles with the option alone, and
|
|
+ * test for the stderr message [invalid option -- 'X'].
|
|
+ *)
|
|
+let setfiles_has_option_m,
|
|
+ setfiles_has_option_C,
|
|
+ setfiles_has_option_T =
|
|
+ let setfiles_has_option flag =
|
|
+ let err_msg = sprintf "invalid option -- '%c'" flag in
|
|
+ let opt = sprintf "-%c" flag in
|
|
+ let _, _, err = commandr "setfiles" [opt] in
|
|
+ String.find err err_msg = -1
|
|
+ in
|
|
+ let setfiles_has_option_m = lazy (setfiles_has_option 'm')
|
|
+ and setfiles_has_option_C = lazy (setfiles_has_option 'C')
|
|
+ and setfiles_has_option_T = lazy (setfiles_has_option 'T') in
|
|
+ (fun () -> Lazy.force setfiles_has_option_m),
|
|
+ (fun () -> Lazy.force setfiles_has_option_C),
|
|
+ (fun () -> Lazy.force setfiles_has_option_T)
|
|
+
|
|
+let selinux_relabel ?(force = false) specfile path =
|
|
+ (* Prefix /sysroot on all paths. *)
|
|
+ let ignored_paths =
|
|
+ [ "/dev"; "/proc"; "/selinux"; "/sys" ] |>
|
|
+ List.map sysroot_path in
|
|
+ let specfile = sysroot_path specfile in
|
|
+ let path = sysroot_path path in
|
|
+
|
|
+ let args = ref [] in
|
|
+ if force then List.push_back args "-F";
|
|
+ List.iter (
|
|
+ fun ignored_path ->
|
|
+ List.push_back_list args [ "-e"; ignored_path ]
|
|
+ ) ignored_paths;
|
|
+
|
|
+ (* You have to use the -m option (where available) otherwise
|
|
+ * setfiles puts all the mountpoints on the excludes list for no
|
|
+ * useful reason (RHBZ#1433577).
|
|
+ *)
|
|
+ if setfiles_has_option_m () then List.push_back args "-m";
|
|
+
|
|
+ (* Not only do we want setfiles to trudge through individual relabeling
|
|
+ * errors, we also want the setfiles exit status to differentiate a fatal
|
|
+ * error from "relabeling errors only". See RHBZ#1794518.
|
|
+ *)
|
|
+ if setfiles_has_option_C () then List.push_back args "-C";
|
|
+
|
|
+ (* If the appliance is being run with multiple vCPUs, running setfiles
|
|
+ * in multithreading mode might speeds up the process. Option "-T" was
|
|
+ * introduced in SELinux userspace v3.4, and we need to check whether it's
|
|
+ * supported. Passing "-T 0" creates as many threads as there're available
|
|
+ * vCPU cores.
|
|
+ * https://github.com/SELinuxProject/selinux/releases/tag/3.4
|
|
+ *)
|
|
+ if setfiles_has_option_T () then
|
|
+ List.push_back_list args [ "-T"; "0" ];
|
|
+
|
|
+ (* Relabelling in a chroot. *)
|
|
+ if sysroot () <> "/" then
|
|
+ List.push_back_list args [ "-r"; sysroot () ];
|
|
+
|
|
+ if verbose () then
|
|
+ List.push_back args "-v"
|
|
+ else
|
|
+ (* Suppress non-error output. *)
|
|
+ List.push_back args "-q";
|
|
+
|
|
+ (* Add parameters. *)
|
|
+ List.push_back_list args [ specfile; path ];
|
|
+
|
|
+ let args = !args in
|
|
+ let r, _, err = commandr "setfiles" args in
|
|
+
|
|
+ let ok = r = 0 || r = 1 && setfiles_has_option_C () in
|
|
+ if not ok then failwithf "setfiles: %s" err
|
|
diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES
|
|
index cdfb1d615..5270667bf 100644
|
|
--- a/docs/C_SOURCE_FILES
|
|
+++ b/docs/C_SOURCE_FILES
|
|
@@ -132,7 +132,6 @@ daemon/rename.c
|
|
daemon/rpm-c.c
|
|
daemon/rsync.c
|
|
daemon/scrub.c
|
|
-daemon/selinux-relabel.c
|
|
daemon/selinux.c
|
|
daemon/sfdisk.c
|
|
daemon/sh.c
|
|
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
|
|
index 108494ece..128cbe0e9 100644
|
|
--- a/generator/actions_core.ml
|
|
+++ b/generator/actions_core.ml
|
|
@@ -9359,6 +9359,7 @@ fails and the C<errno> is set to C<ENODEV>." };
|
|
{ defaults with
|
|
name = "selinux_relabel"; added = (1, 33, 43);
|
|
style = RErr, [String (PlainString, "specfile"); String (Pathname, "path")], [OBool "force"];
|
|
+ impl = OCaml "Selinux.selinux_relabel";
|
|
optional = Some "selinuxrelabel";
|
|
test_excuse = "tests are in the tests/relabel directory";
|
|
shortdesc = "relabel parts of the filesystem";
|
|
diff --git a/po/POTFILES b/po/POTFILES
|
|
index acf3a68d7..fbe0a7fe2 100644
|
|
--- a/po/POTFILES
|
|
+++ b/po/POTFILES
|
|
@@ -110,7 +110,6 @@ daemon/rename.c
|
|
daemon/rpm-c.c
|
|
daemon/rsync.c
|
|
daemon/scrub.c
|
|
-daemon/selinux-relabel.c
|
|
daemon/selinux.c
|
|
daemon/sfdisk.c
|
|
daemon/sh.c
|
|
--
|
|
2.47.1
|
|
|