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)