From 5855a79ebadb472582772a347c1b74848a1b61ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20=C5=A0karvada?= Date: Wed, 6 May 2026 22:52:38 +0200 Subject: [PATCH] bootloader: initrd can be generated from the /tmp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes regression introduced by 74c3d35d7a8afb391803076d826027d61a7fd5a3. Added safety check, that the custom directory from which the initrd is generated has to owned by the same user who is running the TuneD. Also dropped redundant "error:" prefix from some related error messages. Signed-off-by: Jaroslav Škarvada --- tuned/consts.py | 1 + tuned/plugins/base.py | 15 +++++++++++++-- tuned/plugins/plugin_bootloader.py | 25 ++++++++++++++++++++----- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/tuned/consts.py b/tuned/consts.py index abed0cc..13c96a1 100644 --- a/tuned/consts.py +++ b/tuned/consts.py @@ -26,6 +26,7 @@ PLUGIN_VARIABLES_UNIT_NAME = "variables" MAGIC_HEADER_NAME = "this_is_some_magic_section_header_because_of_compatibility" RECOMMEND_DIRECTORIES = ["/usr/lib/tuned/recommend.d", "/etc/tuned/recommend.d"] +TMP_DIR = "/tmp" TMP_FILE_SUFFIX = ".tmp" # max. number of consecutive errors to give up ERROR_THRESHOLD = 3 diff --git a/tuned/plugins/base.py b/tuned/plugins/base.py index b6ac8dd..589a079 100644 --- a/tuned/plugins/base.py +++ b/tuned/plugins/base.py @@ -213,11 +213,22 @@ class Plugin(object): def _instance_post_static(self, instance, enabling): pass - def _safe_script_path(self, path): + def _safe_script_path(self, path, tmp_allowed = False): + """ + Checks if the path is within profile directories or TMP directory. + TMP directory is configured in the consts.py. + + :param path: the path to check + :param tmp_allowed: optional parameter, default False, True allows paths within TMP + :returns: True if the path is OK + """ path = os.path.realpath(path) + # os.path.join() to add trailing slash + if tmp_allowed and path.startswith(os.path.join(consts.TMP_DIR, "")): + return True profile_paths = self._global_cfg.get_list(consts.CFG_PROFILE_DIRS, consts.CFG_DEF_PROFILE_DIRS) for p in profile_paths: - if path.startswith(p): + if path.startswith(os.path.join(p, "")): return True return False diff --git a/tuned/plugins/plugin_bootloader.py b/tuned/plugins/plugin_bootloader.py index eda2ef0..211e961 100644 --- a/tuned/plugins/plugin_bootloader.py +++ b/tuned/plugins/plugin_bootloader.py @@ -134,7 +134,12 @@ class BootloaderPlugin(base.Plugin): If the `DIR` directory name begins with '/', the absolute path is used. Otherwise, the current profile directory is used as the base directory for the `DIR`. For safety reasons, `DIR` has to be - a subdirectory within the defined profile directories. + a subdirectory within the defined profile directories or within the + `TMP` directory (`TMP` directory is configured in the consts.py) + and owned by the same user who is running the TuneD process. + If `TMP` directory is used be careful about possible security concerns, + because `TMP` is usually writeable by any user. See e.g. the + `cpu-partitioning` profile for inspiration how to handle it securely. The [option]`initrd_dst_img=PATHNAME` sets the name and location of the resulting initrd image. Typically, it is not necessary to use this @@ -594,16 +599,26 @@ class BootloaderPlugin(base.Plugin): if src_dir == "": return False if not os.path.isdir(src_dir): - log.error("error: cannot create initrd image, source directory '%s' doesn't exist" % src_dir) + log.error("cannot create initrd image, source directory '%s' doesn't exist" % src_dir) return False - if not self._safe_script_path(src_dir): - log.error("error: paths outside of the profile directories cannot be used: '%s'" % src_dir) + if not self._safe_script_path(src_dir, tmp_allowed = True): + log.error("paths outside of the profile directories or '%s' cannot be used, path: '%s'" + % (consts.TMP_DIR, src_dir)) return False + try: + euid = os.geteuid() + fuid = os.stat(src_dir).st_uid + if euid != fuid: + log.error("directory '%s' to be included in the initrd is owned by UID '%d' which is different " + "from the TuneD process EUID '%d', it is suspicious, initrd will not be created" % (src_dir, fuid, euid)) + return False + except (FileNotFoundError, PermissionError) as e: + log.error("unable to stat directory '%s': '%s'" % (src_dir, e)) try: os.chmod(src_dir, 0o755) log.debug("setting permissions of directory '%s'" % src_dir) except Exception as e: - log.error("error: failed to change permissions of directory '%s': %s" % (src_dir, e)) + log.error("failed to change permissions of directory '%s': %s" % (src_dir, e)) return False log.info("generating initrd image from directory '%s'" % src_dir)