]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sysext: validate that they do not contain os-release 27044/head
authorLuca Boccassi <bluca@debian.org>
Tue, 28 Mar 2023 22:16:20 +0000 (23:16 +0100)
committerLuca Boccassi <bluca@debian.org>
Thu, 30 Mar 2023 10:28:12 +0000 (11:28 +0100)
sysexts are not supposed to ship os-release files, enforce this
when loading them

src/shared/discover-image.c
src/shared/dissect-image.c
src/shared/extension-util.c
src/shared/extension-util.h
src/sysext/sysext.c
test/units/testsuite-50.sh

index 5873741c8c6f0deff08fedaf4814ce02c6f664e9..6aff9fbb30b52b8e7bdd82f08c263e7f5af5a3a7 100644 (file)
@@ -22,6 +22,7 @@
 #include "dissect-image.h"
 #include "env-file.h"
 #include "env-util.h"
+#include "extension-util.h"
 #include "fd-util.h"
 #include "fs-util.h"
 #include "hashmap.h"
@@ -1151,6 +1152,16 @@ int image_read_metadata(Image *i) {
                 _cleanup_free_ char *hostname = NULL;
                 _cleanup_free_ char *path = NULL;
 
+                if (i->class == IMAGE_EXTENSION) {
+                        r = extension_has_forbidden_content(i->path);
+                        if (r < 0)
+                                return r;
+                        if (r > 0)
+                                return log_debug_errno(SYNTHETIC_ERRNO(ENOMEDIUM),
+                                                       "Conflicting content found in image %s, refusing.",
+                                                       i->name);
+                }
+
                 r = chase("/etc/hostname", i->path, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &path, NULL);
                 if (r < 0 && r != -ENOENT)
                         log_debug_errno(r, "Failed to chase /etc/hostname in image %s: %m", i->name);
index 32f33274532d82a04e22146baf3f0e4dbbb24c76..6000af0ce05a9b78926856279fb941baf60c6788 100644 (file)
@@ -1787,11 +1787,16 @@ int dissected_image_mount(
                                         ok = true;
                         }
                         if (!ok && FLAGS_SET(flags, DISSECT_IMAGE_VALIDATE_OS_EXT)) {
-                                r = path_is_extension_tree(where, m->image_name, FLAGS_SET(flags, DISSECT_IMAGE_RELAX_SYSEXT_CHECK));
+                                r = extension_has_forbidden_content(where);
                                 if (r < 0)
                                         return r;
-                                if (r > 0)
-                                        ok = true;
+                                if (r == 0) {
+                                        r = path_is_extension_tree(where, m->image_name, FLAGS_SET(flags, DISSECT_IMAGE_RELAX_SYSEXT_CHECK));
+                                        if (r < 0)
+                                                return r;
+                                        if (r > 0)
+                                                ok = true;
+                                }
                         }
 
                         if (!ok)
index 2df7abc6da406d4cade734c184b24a9405a0106b..fa83f6b6fdc7549d6fba1e932f29e12eb187bf1f 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "alloc-util.h"
 #include "architecture.h"
+#include "chase.h"
 #include "env-util.h"
 #include "extension-util.h"
 #include "log.h"
@@ -135,3 +136,20 @@ int parse_env_extension_hierarchies(char ***ret_hierarchies) {
         *ret_hierarchies = TAKE_PTR(l);
         return 0;
 }
+
+int extension_has_forbidden_content(const char *root) {
+        int r;
+
+        /* Insist that extension images do not overwrite the underlying OS release file (it's fine if
+         * they place one in /etc/os-release, i.e. where things don't matter, as they aren't
+         * merged.) */
+        r = chase("/usr/lib/os-release", root, CHASE_PREFIX_ROOT, NULL, NULL);
+        if (r > 0) {
+                log_debug("Extension contains '/usr/lib/os-release', which is not allowed, refusing.");
+                return 1;
+        }
+        if (r < 0 && r != -ENOENT)
+                return log_debug_errno(r, "Failed to determine whether '/usr/lib/os-release' exists in the extension: %m");
+
+        return 0;
+}
index 5c3fee24bef99319cc410409d69a03a6a382c340..fba8acaf199c74490b9009ed5acb2c9d68824aef 100644 (file)
@@ -14,3 +14,7 @@ int extension_release_validate(
 
 /* Parse SYSTEMD_SYSEXT_HIERARCHIES and if not set, return "/usr /opt" */
 int parse_env_extension_hierarchies(char ***ret_hierarchies);
+
+/* Insist that extension images do not overwrite the underlying OS release file (it's fine if they place one
+ * in /etc/os-release, i.e. where things don't matter, as they aren't merged.) */
+int extension_has_forbidden_content(const char *root);
index 449602fc4e5fb7f2c5645efdb0a17ccc34c86606..5632b72f3d2adee9024ec6e4dc97130b471237a7 100644 (file)
@@ -408,47 +408,6 @@ static int strverscmp_improvedp(char *const* a, char *const* b) {
         return strverscmp_improved(*a, *b);
 }
 
-static int validate_version(
-                const char *root,
-                const Image *img,
-                const char *host_os_release_id,
-                const char *host_os_release_version_id,
-                const char *host_os_release_sysext_level) {
-
-        int r;
-
-        assert(root);
-        assert(img);
-
-        if (arg_force) {
-                log_debug("Force mode enabled, skipping version validation.");
-                return 1;
-        }
-
-        /* Insist that extension images do not overwrite the underlying OS release file (it's fine if
-         * they place one in /etc/os-release, i.e. where things don't matter, as they aren't
-         * merged.) */
-        r = chase("/usr/lib/os-release", root, CHASE_PREFIX_ROOT, NULL, NULL);
-        if (r < 0) {
-                if (r != -ENOENT)
-                        return log_error_errno(r, "Failed to determine whether /usr/lib/os-release exists in the extension image: %m");
-        } else
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Extension image contains /usr/lib/os-release file, which is not allowed (it may carry /etc/os-release), refusing.");
-
-        r = extension_release_validate(
-                        img->name,
-                        host_os_release_id,
-                        host_os_release_version_id,
-                        host_os_release_sysext_level,
-                        in_initrd() ? "initrd" : "system",
-                        img->extension_release);
-        if (r < 0)
-                return log_error_errno(r, "Failed to validate extension release information: %m");
-
-        return r;
-}
-
 static int merge_subprocess(Hashmap *images, const char *workspace) {
         _cleanup_free_ char *host_os_release_id = NULL, *host_os_release_version_id = NULL, *host_os_release_sysext_level = NULL,
                 *buf = NULL;
@@ -505,6 +464,17 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
                 switch (img->type) {
                 case IMAGE_DIRECTORY:
                 case IMAGE_SUBVOLUME:
+
+                        if (!arg_force) {
+                                r = extension_has_forbidden_content(p);
+                                if (r < 0)
+                                        return r;
+                                if (r > 0) {
+                                        n_ignored++;
+                                        continue;
+                                }
+                        }
+
                         r = mount_nofollow_verbose(LOG_ERR, img->path, p, NULL, MS_BIND, NULL);
                         if (r < 0)
                                 return r;
@@ -537,6 +507,9 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
                         if (verity_settings.data_path)
                                 flags |= DISSECT_IMAGE_NO_PARTITION_TABLE;
 
+                        if (!arg_force)
+                                flags |= DISSECT_IMAGE_VALIDATE_OS_EXT;
+
                         r = loop_device_make_by_path(
                                         img->path,
                                         O_RDONLY,
@@ -576,8 +549,12 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
                                         UID_INVALID,
                                         UID_INVALID,
                                         flags);
-                        if (r < 0)
+                        if (r < 0 && r != -ENOMEDIUM)
                                 return r;
+                        if (r == -ENOMEDIUM && !arg_force) {
+                                n_ignored++;
+                                continue;
+                        }
 
                         r = dissected_image_relinquish(m);
                         if (r < 0)
@@ -588,17 +565,22 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
                         assert_not_reached();
                 }
 
-                r = validate_version(
-                                p,
-                                img,
-                                host_os_release_id,
-                                host_os_release_version_id,
-                                host_os_release_sysext_level);
-                if (r < 0)
-                        return r;
-                if (r == 0) {
-                        n_ignored++;
-                        continue;
+                if (arg_force)
+                        log_debug("Force mode enabled, skipping version validation.");
+                else {
+                        r = extension_release_validate(
+                                        img->name,
+                                        host_os_release_id,
+                                        host_os_release_version_id,
+                                        host_os_release_sysext_level,
+                                        in_initrd() ? "initrd" : "system",
+                                        img->extension_release);
+                        if (r < 0)
+                                return r;
+                        if (r == 0) {
+                                n_ignored++;
+                                continue;
+                        }
                 }
 
                 /* Nice! This one is an extension we want. */
@@ -612,7 +594,7 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
         /* Nothing left? Then shortcut things */
         if (n_extensions == 0) {
                 if (n_ignored > 0)
-                        log_info("No suitable extensions found (%u ignored due to incompatible version).", n_ignored);
+                        log_info("No suitable extensions found (%u ignored due to incompatible image(s)).", n_ignored);
                 else
                         log_info("No extensions found.");
                 return 0;
index af7d3511546a7ae0d9ecc03879ebb5d569ea4a57..546a915a2e62adb797e551285c1ff9461e646c7e 100755 (executable)
@@ -413,6 +413,17 @@ systemd-sysext merge
 test ! -e /usr/lib/systemd/system/some_file
 systemd-sysext unmerge
 rmdir /etc/extensions/app-nodistro
+
+# Check that extensions cannot contain os-release
+mkdir -p /run/extensions/app-reject/usr/lib/{extension-release.d/,systemd/system}
+echo "ID=_any" >/run/extensions/app-reject/usr/lib/extension-release.d/extension-release.app-reject
+echo "ID=_any" >/run/extensions/app-reject/usr/lib/os-release
+touch /run/extensions/app-reject/usr/lib/systemd/system/other_file
+systemd-sysext merge && { echo 'unexpected success'; exit 1; }
+test ! -e /usr/lib/systemd/system/some_file
+test ! -e /usr/lib/systemd/system/other_file
+systemd-sysext unmerge
+rm -rf /run/extensions/app-reject
 rm /var/lib/extensions/app-nodistro.raw
 
 mkdir -p /run/machines /run/portables /run/extensions