portable: pin attached image via image-policy (#40152)
When attaching images generate a policy in the portable drop-in that matches the partition types and content found while dissecting, so that it can no longer be changed later without a reattach
This commit is contained in:
@@ -141,6 +141,11 @@
|
||||
immediately started (blocking operation unless <option>--no-block</option> is passed) and/or enabled after
|
||||
attaching the image.</para>
|
||||
|
||||
<para>When images are attached, an image policy will be generated that pins the attached image by
|
||||
the exact content that was found while attaching, so that it cannot be swapped downgrading security
|
||||
(e.g.: removing dm-verity protection) without a full reinstallation. For more details on policies, see
|
||||
<citerefentry><refentrytitle>systemd.image-policy</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
|
||||
|
||||
<xi:include href="vpick.xml" xpointer="image"/>
|
||||
<xi:include href="vpick.xml" xpointer="directory"/>
|
||||
<xi:include href="version-info.xml" xpointer="v239"/>
|
||||
|
||||
+109
-3
@@ -27,6 +27,7 @@
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "glyph-util.h"
|
||||
#include "image-policy.h"
|
||||
#include "install.h"
|
||||
#include "iovec-util.h"
|
||||
#include "libmount-util.h"
|
||||
@@ -364,10 +365,12 @@ static int portable_extract_by_path(
|
||||
const ImagePolicy *image_policy,
|
||||
PortableMetadata **ret_os_release,
|
||||
Hashmap **ret_unit_files,
|
||||
ImagePolicy **ret_pinned_image_policy,
|
||||
sd_bus_error *error) {
|
||||
|
||||
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
|
||||
_cleanup_(portable_metadata_unrefp) PortableMetadata* os_release = NULL;
|
||||
_cleanup_(image_policy_freep) ImagePolicy *pinned_image_policy = NULL;
|
||||
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
|
||||
int r;
|
||||
|
||||
@@ -397,6 +400,7 @@ static int portable_extract_by_path(
|
||||
} else if (r < 0)
|
||||
return log_debug_errno(r, "Failed to set up loopback device for %s: %m", path);
|
||||
else {
|
||||
_cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
|
||||
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
|
||||
_cleanup_(rmdir_and_freep) char *tmpdir = NULL;
|
||||
_cleanup_close_pair_ int seq[2] = EBADF_PAIR;
|
||||
@@ -449,6 +453,24 @@ static int portable_extract_by_path(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = verity_settings_load(&verity, path, NULL, NULL);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to load root hash: %m");
|
||||
|
||||
r = dissected_image_load_verity_sig_partition(m, d->fd, &verity);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dissected_image_guess_verity_roothash(m, &verity);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret_pinned_image_policy) {
|
||||
pinned_image_policy = image_policy_new_from_dissected(m, &verity);
|
||||
if (!pinned_image_policy)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, seq) < 0)
|
||||
return log_debug_errno(errno, "Failed to allocated SOCK_SEQPACKET socket: %m");
|
||||
|
||||
@@ -558,6 +580,9 @@ static int portable_extract_by_path(
|
||||
if (ret_os_release)
|
||||
*ret_os_release = TAKE_PTR(os_release);
|
||||
|
||||
if (ret_pinned_image_policy)
|
||||
*ret_pinned_image_policy = TAKE_PTR(pinned_image_policy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -575,9 +600,12 @@ static int extract_image_and_extensions(
|
||||
PortableMetadata **ret_os_release,
|
||||
Hashmap **ret_unit_files,
|
||||
char ***ret_valid_prefixes,
|
||||
ImagePolicy **ret_pinned_root_image_policy,
|
||||
ImagePolicy **ret_pinned_ext_image_policy,
|
||||
sd_bus_error *error) {
|
||||
|
||||
_cleanup_free_ char *id = NULL, *id_like = NULL, *version_id = NULL, *sysext_level = NULL, *confext_level = NULL;
|
||||
_cleanup_(image_policy_freep) ImagePolicy *pinned_root_image_policy = NULL, *pinned_ext_image_policy = NULL;
|
||||
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
|
||||
_cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL, *extension_releases = NULL;
|
||||
_cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
|
||||
@@ -669,6 +697,7 @@ static int extract_image_and_extensions(
|
||||
image_policy,
|
||||
&os_release,
|
||||
&unit_files,
|
||||
&pinned_root_image_policy,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -700,6 +729,7 @@ static int extract_image_and_extensions(
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(ext, extension_images) {
|
||||
_cleanup_(portable_metadata_unrefp) PortableMetadata *extension_release_meta = NULL;
|
||||
_cleanup_(image_policy_freep) ImagePolicy *policy = NULL;
|
||||
_cleanup_hashmap_free_ Hashmap *extra_unit_files = NULL;
|
||||
_cleanup_strv_free_ char **extension_release = NULL;
|
||||
const char *e;
|
||||
@@ -713,6 +743,7 @@ static int extract_image_and_extensions(
|
||||
image_policy,
|
||||
&extension_release_meta,
|
||||
&extra_unit_files,
|
||||
&policy,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -721,6 +752,19 @@ static int extract_image_and_extensions(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!pinned_ext_image_policy && policy)
|
||||
pinned_ext_image_policy = TAKE_PTR(policy);
|
||||
else if (policy) {
|
||||
_cleanup_(image_policy_freep) ImagePolicy *intersected_policy = NULL;
|
||||
|
||||
/* There is a single policy for all extension images, so we need a union */
|
||||
r = image_policy_union(pinned_ext_image_policy, policy, &intersected_policy);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to merge extension image policies: %m");
|
||||
|
||||
free_and_replace(pinned_ext_image_policy, intersected_policy);
|
||||
}
|
||||
|
||||
if (!validate_extension && !ret_valid_prefixes && !ret_extension_releases)
|
||||
continue;
|
||||
|
||||
@@ -768,6 +812,10 @@ static int extract_image_and_extensions(
|
||||
*ret_unit_files = TAKE_PTR(unit_files);
|
||||
if (ret_valid_prefixes)
|
||||
*ret_valid_prefixes = TAKE_PTR(valid_prefixes);
|
||||
if (ret_pinned_root_image_policy)
|
||||
*ret_pinned_root_image_policy = TAKE_PTR(pinned_root_image_policy);
|
||||
if (ret_pinned_ext_image_policy)
|
||||
*ret_pinned_ext_image_policy = TAKE_PTR(pinned_ext_image_policy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -808,6 +856,8 @@ int portable_extract(
|
||||
&os_release,
|
||||
&unit_files,
|
||||
ret_valid_prefixes ? &valid_prefixes : NULL,
|
||||
/* pinned_root_image_policy= */ NULL,
|
||||
/* pinned_ext_image_policy= */ NULL,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -1110,6 +1160,8 @@ static int install_chroot_dropin(
|
||||
ImageType type,
|
||||
OrderedHashmap *extension_images,
|
||||
OrderedHashmap *extension_releases,
|
||||
const ImagePolicy *pinned_root_image_policy,
|
||||
const ImagePolicy *pinned_ext_image_policy,
|
||||
const PortableMetadata *m,
|
||||
const PortableMetadata *os_release,
|
||||
const char *dropin_dir,
|
||||
@@ -1152,6 +1204,18 @@ static int install_chroot_dropin(
|
||||
"LogExtraFields=PORTABLE=", base_name, "\n"))
|
||||
return -ENOMEM;
|
||||
|
||||
if (pinned_root_image_policy) {
|
||||
_cleanup_free_ char *policy_str = NULL;
|
||||
|
||||
r = image_policy_to_string(pinned_root_image_policy, /* simplify= */ true, &policy_str);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to serialize pinned image policy: %m");
|
||||
|
||||
if (!strextend(&text,
|
||||
"RootImagePolicy=", policy_str, "\n"))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* If we have a single image then PORTABLE= will point to it, so we add
|
||||
* PORTABLE_NAME_AND_VERSION= with the os-release fields and we are done. But if we have
|
||||
* extensions, PORTABLE= will point to the image where the current unit was found in. So we
|
||||
@@ -1201,6 +1265,18 @@ static int install_chroot_dropin(
|
||||
"LogExtraFields=PORTABLE_EXTENSION=", extension_base_name, "\n"))
|
||||
return -ENOMEM;
|
||||
|
||||
if (pinned_ext_image_policy) {
|
||||
_cleanup_free_ char *policy_str = NULL;
|
||||
|
||||
r = image_policy_to_string(pinned_ext_image_policy, /* simplify= */ true, &policy_str);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to serialize pinned image policy: %m");
|
||||
|
||||
if (!strextend(&text,
|
||||
"ExtensionImagePolicy=", policy_str, "\n"))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Look for image/version identifiers in the extension release files. We
|
||||
* look for all possible IDs, but typically only 1 or 2 will be set, so
|
||||
* the number of fields added shouldn't be too large. We prefix the DDI
|
||||
@@ -1317,6 +1393,8 @@ static int attach_unit_file(
|
||||
ImageType type,
|
||||
OrderedHashmap *extension_images,
|
||||
OrderedHashmap *extension_releases,
|
||||
const ImagePolicy *pinned_root_image_policy,
|
||||
const ImagePolicy *pinned_ext_image_policy,
|
||||
const PortableMetadata *m,
|
||||
const PortableMetadata *os_release,
|
||||
const char *profile,
|
||||
@@ -1362,7 +1440,20 @@ static int attach_unit_file(
|
||||
* is reloaded while we are creating things here: as long as only the drop-ins exist the unit doesn't exist at
|
||||
* all for PID 1. */
|
||||
|
||||
r = install_chroot_dropin(image_path, type, extension_images, extension_releases, m, os_release, dropin_dir, flags, &chroot_dropin, changes, n_changes);
|
||||
r = install_chroot_dropin(
|
||||
image_path,
|
||||
type,
|
||||
extension_images,
|
||||
extension_releases,
|
||||
pinned_root_image_policy,
|
||||
pinned_ext_image_policy,
|
||||
m,
|
||||
os_release,
|
||||
dropin_dir,
|
||||
flags,
|
||||
&chroot_dropin,
|
||||
changes,
|
||||
n_changes);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -1631,6 +1722,7 @@ int portable_attach(
|
||||
size_t *n_changes,
|
||||
sd_bus_error *error) {
|
||||
|
||||
_cleanup_(image_policy_freep) ImagePolicy *pinned_root_image_policy = NULL, *pinned_ext_image_policy = NULL;
|
||||
_cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL, *extension_releases = NULL;
|
||||
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
|
||||
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
|
||||
@@ -1656,6 +1748,8 @@ int portable_attach(
|
||||
&os_release,
|
||||
&unit_files,
|
||||
&valid_prefixes,
|
||||
&pinned_root_image_policy,
|
||||
&pinned_ext_image_policy,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -1720,8 +1814,20 @@ int portable_attach(
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH(item, unit_files) {
|
||||
r = attach_unit_file(&paths, image->path, image->type, extension_images, extension_releases,
|
||||
item, os_release, profile, flags, changes, n_changes);
|
||||
r = attach_unit_file(
|
||||
&paths,
|
||||
image->path,
|
||||
image->type,
|
||||
extension_images,
|
||||
extension_releases,
|
||||
pinned_root_image_policy,
|
||||
pinned_ext_image_policy,
|
||||
item,
|
||||
os_release,
|
||||
profile,
|
||||
flags,
|
||||
changes,
|
||||
n_changes);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to attach unit '%s': %m", item->name);
|
||||
}
|
||||
|
||||
+113
-26
@@ -1,6 +1,7 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dissect-image.h"
|
||||
#include "extract-word.h"
|
||||
#include "image-policy.h"
|
||||
#include "log.h"
|
||||
@@ -176,6 +177,28 @@ PartitionPolicyFlags image_policy_get_exhaustively(const ImagePolicy *policy, Pa
|
||||
return flags;
|
||||
}
|
||||
|
||||
static PartitionPolicyFlags policy_flag_from_fstype(const char *s) {
|
||||
if (!s)
|
||||
return _PARTITION_POLICY_FLAGS_INVALID;
|
||||
|
||||
if (streq(s, "btrfs"))
|
||||
return PARTITION_POLICY_BTRFS;
|
||||
if (streq(s, "erofs"))
|
||||
return PARTITION_POLICY_EROFS;
|
||||
if (streq(s, "ext4"))
|
||||
return PARTITION_POLICY_EXT4;
|
||||
if (streq(s, "f2fs"))
|
||||
return PARTITION_POLICY_F2FS;
|
||||
if (streq(s, "squashfs"))
|
||||
return PARTITION_POLICY_SQUASHFS;
|
||||
if (streq(s, "vfat"))
|
||||
return PARTITION_POLICY_VFAT;
|
||||
if (streq(s, "xfs"))
|
||||
return PARTITION_POLICY_XFS;
|
||||
|
||||
return _PARTITION_POLICY_FLAGS_INVALID;
|
||||
}
|
||||
|
||||
static PartitionPolicyFlags policy_flag_from_string_one(const char *s) {
|
||||
assert(s);
|
||||
|
||||
@@ -207,22 +230,8 @@ static PartitionPolicyFlags policy_flag_from_string_one(const char *s) {
|
||||
return PARTITION_POLICY_GROWFS_ON;
|
||||
if (streq(s, "growfs-off"))
|
||||
return PARTITION_POLICY_GROWFS_OFF;
|
||||
if (streq(s, "btrfs"))
|
||||
return PARTITION_POLICY_BTRFS;
|
||||
if (streq(s, "erofs"))
|
||||
return PARTITION_POLICY_EROFS;
|
||||
if (streq(s, "ext4"))
|
||||
return PARTITION_POLICY_EXT4;
|
||||
if (streq(s, "f2fs"))
|
||||
return PARTITION_POLICY_F2FS;
|
||||
if (streq(s, "squashfs"))
|
||||
return PARTITION_POLICY_SQUASHFS;
|
||||
if (streq(s, "vfat"))
|
||||
return PARTITION_POLICY_VFAT;
|
||||
if (streq(s, "xfs"))
|
||||
return PARTITION_POLICY_XFS;
|
||||
|
||||
return _PARTITION_POLICY_FLAGS_INVALID;
|
||||
return policy_flag_from_fstype(s);
|
||||
}
|
||||
|
||||
PartitionPolicyFlags partition_policy_flags_from_string(const char *s, bool graceful) {
|
||||
@@ -275,6 +284,60 @@ static ImagePolicy* image_policy_new(size_t n_policies) {
|
||||
return p;
|
||||
}
|
||||
|
||||
ImagePolicy* image_policy_new_from_dissected(const DissectedImage *image, const VeritySettings *verity) {
|
||||
assert(image);
|
||||
|
||||
ImagePolicy *image_policy = image_policy_new(_PARTITION_DESIGNATOR_MAX);
|
||||
if (!image_policy)
|
||||
return NULL;
|
||||
|
||||
/* Default to 'absent', only what we find is allowed to be used */
|
||||
image_policy->default_flags = PARTITION_POLICY_ABSENT;
|
||||
|
||||
for (PartitionDesignator pd = 0; pd < _PARTITION_DESIGNATOR_MAX; pd++) {
|
||||
PartitionPolicyFlags f = 0;
|
||||
|
||||
if (!image->partitions[pd].found)
|
||||
f |= PARTITION_POLICY_ABSENT;
|
||||
else {
|
||||
if (streq_ptr(image->partitions[pd].fstype, "crypto_LUKS"))
|
||||
f |= PARTITION_POLICY_ENCRYPTED;
|
||||
else {
|
||||
PartitionPolicyFlags fstype_flag = policy_flag_from_fstype(image->partitions[pd].fstype);
|
||||
if (fstype_flag >= 0)
|
||||
f |= fstype_flag;
|
||||
}
|
||||
|
||||
if (!verity || verity->designator < 0 || verity->designator == pd) {
|
||||
if (image->verity_sig_ready || (verity && iovec_is_set(&verity->root_hash_sig)))
|
||||
f |= PARTITION_POLICY_SIGNED;
|
||||
else if (image->verity_ready || (verity && iovec_is_set(&verity->root_hash)))
|
||||
f |= PARTITION_POLICY_VERITY;
|
||||
}
|
||||
|
||||
if (!image->single_file_system) {
|
||||
if (image->partitions[pd].growfs)
|
||||
f |= PARTITION_POLICY_GROWFS_ON;
|
||||
else
|
||||
f |= PARTITION_POLICY_GROWFS_OFF;
|
||||
|
||||
if (image->partitions[pd].rw)
|
||||
f |= PARTITION_POLICY_READ_ONLY_OFF;
|
||||
else
|
||||
f |= PARTITION_POLICY_READ_ONLY_ON;
|
||||
}
|
||||
}
|
||||
|
||||
image_policy->policies[pd] = (PartitionPolicy) {
|
||||
.designator = pd,
|
||||
.flags = f,
|
||||
};
|
||||
image_policy->n_policies++;
|
||||
}
|
||||
|
||||
return image_policy;
|
||||
}
|
||||
|
||||
int image_policy_from_string(const char *s, bool graceful, ImagePolicy **ret) {
|
||||
_cleanup_free_ ImagePolicy *p = NULL;
|
||||
uint64_t dmask = 0;
|
||||
@@ -785,21 +848,35 @@ static bool partition_policy_flags_has_unspecified(PartitionPolicyFlags flags) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int image_policy_intersect(const ImagePolicy *a, const ImagePolicy *b, ImagePolicy **ret) {
|
||||
static PartitionPolicyFlags policy_flags_or(PartitionPolicyFlags a, PartitionPolicyFlags b) {
|
||||
return a | b;
|
||||
}
|
||||
|
||||
static PartitionPolicyFlags policy_flags_and(PartitionPolicyFlags a, PartitionPolicyFlags b) {
|
||||
return a & b;
|
||||
}
|
||||
|
||||
static int policy_intersect_or_union(
|
||||
const ImagePolicy *a,
|
||||
const ImagePolicy *b,
|
||||
PartitionPolicyFlags (*op)(PartitionPolicyFlags a, PartitionPolicyFlags b),
|
||||
ImagePolicy **ret) {
|
||||
|
||||
_cleanup_(image_policy_freep) ImagePolicy *p = NULL;
|
||||
|
||||
/* Calculates the intersection of the specified policies, i.e. only what is permitted in both. This
|
||||
* might fail with -ENAVAIL if the intersection is an "impossible policy". For example, if a root
|
||||
* partition my neither be used, nor be absent, nor be unused then this is considered
|
||||
* "impossible". */
|
||||
assert(op);
|
||||
|
||||
/* Calculates the intersection or union of the specified policies, i.e. only what is permitted in
|
||||
* both or either. This might fail with -ENAVAIL if the intersection is an "impossible policy". For
|
||||
* example, if a root partition my neither be used, nor be absent, nor be unused then this is
|
||||
* considered "impossible". */
|
||||
|
||||
p = image_policy_new(_PARTITION_DESIGNATOR_MAX);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
p->default_flags =
|
||||
partition_policy_flags_extend(image_policy_default(a)) &
|
||||
partition_policy_flags_extend(image_policy_default(b));
|
||||
p->default_flags = op(partition_policy_flags_extend(image_policy_default(a)),
|
||||
partition_policy_flags_extend(image_policy_default(b)));
|
||||
|
||||
if (partition_policy_flags_has_unspecified(p->default_flags)) /* Intersection empty? */
|
||||
return -ENAVAIL;
|
||||
@@ -824,10 +901,12 @@ int image_policy_intersect(const ImagePolicy *a, const ImagePolicy *b, ImagePoli
|
||||
return y;
|
||||
|
||||
/* Mask it */
|
||||
z = x & y;
|
||||
z = op(x, y);
|
||||
|
||||
/* Check if the intersection is empty for this partition. If so, generate a clear error */
|
||||
if (partition_policy_flags_has_unspecified(z))
|
||||
/* Check if the intersection is empty for this partition. If so, generate a clear error.
|
||||
* If the partition has to be absent, then it won't have read-only/growfs flags, as
|
||||
* image_policy_get_exhaustively() intentionally strips them, so skip the check. */
|
||||
if (z != PARTITION_POLICY_ABSENT && partition_policy_flags_has_unspecified(z))
|
||||
return -ENAVAIL;
|
||||
|
||||
df = partition_policy_normalized_flags(
|
||||
@@ -857,6 +936,14 @@ int image_policy_intersect(const ImagePolicy *a, const ImagePolicy *b, ImagePoli
|
||||
return 0;
|
||||
}
|
||||
|
||||
int image_policy_intersect(const ImagePolicy *a, const ImagePolicy *b, ImagePolicy **ret) {
|
||||
return policy_intersect_or_union(a, b, policy_flags_and, ret);
|
||||
}
|
||||
|
||||
int image_policy_union(const ImagePolicy *a, const ImagePolicy *b, ImagePolicy **ret) {
|
||||
return policy_intersect_or_union(a, b, policy_flags_or, ret);
|
||||
}
|
||||
|
||||
ImagePolicy* image_policy_free(ImagePolicy *p) {
|
||||
return mfree(p);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "conf-parser-forward.h"
|
||||
#include "dissect-image.h"
|
||||
#include "shared-forward.h"
|
||||
#include "gpt.h"
|
||||
|
||||
@@ -111,7 +112,9 @@ bool image_policy_equal(const ImagePolicy *a, const ImagePolicy *b); /* ch
|
||||
int image_policy_equivalent(const ImagePolicy *a, const ImagePolicy *b); /* checks if the outcome is the same, i.e. for all partitions results in the same decisions. */
|
||||
|
||||
int image_policy_intersect(const ImagePolicy *a, const ImagePolicy *b, ImagePolicy **ret);
|
||||
int image_policy_union(const ImagePolicy *a, const ImagePolicy *b, ImagePolicy **ret);
|
||||
|
||||
ImagePolicy* image_policy_new_from_dissected(const DissectedImage *image, const VeritySettings *verity);
|
||||
ImagePolicy* image_policy_free(ImagePolicy *p);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(ImagePolicy*, image_policy_free);
|
||||
|
||||
+405
-12
@@ -142,14 +142,17 @@ TEST(extend) {
|
||||
assert_se(partition_policy_flags_extend(PARTITION_POLICY_GROWFS_ON) == (PARTITION_POLICY_GROWFS_ON|_PARTITION_POLICY_USE_MASK|_PARTITION_POLICY_READ_ONLY_MASK));
|
||||
}
|
||||
|
||||
static void test_policy_intersect_one(const char *a, const char *b, const char *c) {
|
||||
static void test_policy_intersect_one(const char *a, const char *b, const char *c, bool intersect) {
|
||||
_cleanup_(image_policy_freep) ImagePolicy *x = NULL, *y = NULL, *z = NULL, *t = NULL;
|
||||
|
||||
assert_se(image_policy_from_string(a, /* graceful= */ false, &x) >= 0);
|
||||
assert_se(image_policy_from_string(b, /* graceful= */ false, &y) >= 0);
|
||||
assert_se(image_policy_from_string(c, /* graceful= */ false, &z) >= 0);
|
||||
|
||||
assert_se(image_policy_intersect(x, y, &t) >= 0);
|
||||
if (intersect)
|
||||
assert_se(image_policy_intersect(x, y, &t) >= 0);
|
||||
else
|
||||
assert_se(image_policy_union(x, y, &t) >= 0);
|
||||
|
||||
_cleanup_free_ char *s1 = NULL, *s2 = NULL, *s3 = NULL, *s4 = NULL;
|
||||
assert_se(image_policy_to_string(x, false, &s1) >= 0);
|
||||
@@ -157,21 +160,33 @@ static void test_policy_intersect_one(const char *a, const char *b, const char *
|
||||
assert_se(image_policy_to_string(z, false, &s3) >= 0);
|
||||
assert_se(image_policy_to_string(t, false, &s4) >= 0);
|
||||
|
||||
log_info("%s ^ %s → %s vs. %s", s1, s2, s3, s4);
|
||||
log_info("%s %s %s → %s vs. %s", s1, intersect ? "^" : "U", s2, s3, s4);
|
||||
|
||||
assert_se(image_policy_equivalent(z, t) > 0);
|
||||
}
|
||||
|
||||
TEST(image_policy_intersect) {
|
||||
test_policy_intersect_one("", "", "");
|
||||
test_policy_intersect_one("-", "-", "-");
|
||||
test_policy_intersect_one("*", "*", "*");
|
||||
test_policy_intersect_one("~", "~", "~");
|
||||
test_policy_intersect_one("root=verity+signed", "root=signed+verity", "root=verity+signed");
|
||||
test_policy_intersect_one("root=verity+signed", "root=signed", "root=signed");
|
||||
test_policy_intersect_one("root=verity+signed", "root=verity", "root=verity");
|
||||
test_policy_intersect_one("root=open", "root=verity", "root=verity");
|
||||
test_policy_intersect_one("root=open", "=verity+ignore", "root=verity+ignore:=ignore");
|
||||
test_policy_intersect_one("", "", "", /* intersect= */ true);
|
||||
test_policy_intersect_one("-", "-", "-", /* intersect= */ true);
|
||||
test_policy_intersect_one("*", "*", "*", /* intersect= */ true);
|
||||
test_policy_intersect_one("~", "~", "~", /* intersect= */ true);
|
||||
test_policy_intersect_one("root=verity+signed", "root=signed+verity", "root=verity+signed", /* intersect= */ true);
|
||||
test_policy_intersect_one("root=verity+signed", "root=signed", "root=signed", /* intersect= */ true);
|
||||
test_policy_intersect_one("root=verity+signed", "root=verity", "root=verity", /* intersect= */ true);
|
||||
test_policy_intersect_one("root=open", "root=verity", "root=verity", /* intersect= */ true);
|
||||
test_policy_intersect_one("root=open", "=verity+ignore", "root=verity+ignore:=ignore", /* intersect= */ true);
|
||||
}
|
||||
|
||||
TEST(image_policy_union) {
|
||||
test_policy_intersect_one("root=verity+signed", "root=signed+verity", "root=verity+signed", /* intersect= */ false);
|
||||
test_policy_intersect_one("root=verity+signed", "root=signed", "root=verity+signed", /* intersect= */ false);
|
||||
test_policy_intersect_one("root=verity+signed", "root=verity", "root=verity+signed", /* intersect= */ false);
|
||||
test_policy_intersect_one("root=signed", "root=verity", "root=verity+signed", /* intersect= */ false);
|
||||
test_policy_intersect_one("root=signed:=absent", "root=verity:=unused", "root=verity+signed", /* intersect= */ false);
|
||||
test_policy_intersect_one("root=open", "root=verity", "root=open", /* intersect= */ false);
|
||||
test_policy_intersect_one("root=open", "=verity+ignore", "root=open:=verity+ignore", /* intersect= */ false);
|
||||
test_policy_intersect_one("root=open:usr=absent", "root=open:usr=absent", "root=open:usr=absent", /* intersect= */ false);
|
||||
|
||||
}
|
||||
|
||||
static void test_policy_ignore_designators_one(const char *a, const PartitionDesignator array[], size_t n, const char *b) {
|
||||
@@ -265,4 +280,382 @@ TEST(partition_policy_determine_fstype) {
|
||||
ASSERT_FALSE(encrypted);
|
||||
}
|
||||
|
||||
TEST(image_policy_new_from_dissected) {
|
||||
_cleanup_(image_policy_freep) ImagePolicy *policy = NULL;
|
||||
DissectedImage image;
|
||||
VeritySettings verity;
|
||||
uint8_t dummy_data[4];
|
||||
|
||||
/* Test 1: Empty image - all partitions should be absent */
|
||||
image = (DissectedImage) {};
|
||||
|
||||
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
|
||||
ASSERT_NOT_NULL(policy);
|
||||
ASSERT_EQ(image_policy_default(policy), PARTITION_POLICY_ABSENT);
|
||||
ASSERT_EQ(image_policy_n_entries(policy), (size_t) _PARTITION_DESIGNATOR_MAX);
|
||||
|
||||
/* All partitions should have PARTITION_POLICY_ABSENT */
|
||||
for (PartitionDesignator pd = 0; pd < _PARTITION_DESIGNATOR_MAX; pd++)
|
||||
ASSERT_EQ(policy->policies[pd].flags, (PartitionPolicyFlags) PARTITION_POLICY_ABSENT);
|
||||
|
||||
policy = image_policy_free(policy);
|
||||
|
||||
/* Test 2: Image with a single ext4 root partition */
|
||||
image = (DissectedImage) {
|
||||
.partitions = {
|
||||
[PARTITION_ROOT] = {
|
||||
.found = true,
|
||||
.fstype = (char*) "ext4",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
|
||||
ASSERT_NOT_NULL(policy);
|
||||
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_EXT4 | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
|
||||
ASSERT_EQ(policy->policies[PARTITION_USR].flags, (PartitionPolicyFlags) PARTITION_POLICY_ABSENT);
|
||||
|
||||
policy = image_policy_free(policy);
|
||||
|
||||
/* Test 3: Image with encrypted root partition (LUKS) */
|
||||
image = (DissectedImage) {
|
||||
.partitions = {
|
||||
[PARTITION_ROOT] = {
|
||||
.found = true,
|
||||
.fstype = (char*) "crypto_LUKS",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
|
||||
ASSERT_NOT_NULL(policy);
|
||||
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_ENCRYPTED | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
|
||||
|
||||
policy = image_policy_free(policy);
|
||||
|
||||
/* Test 4: Image with verity ready (without signature) */
|
||||
image = (DissectedImage) {
|
||||
.verity_ready = true,
|
||||
.partitions = {
|
||||
[PARTITION_ROOT] = {
|
||||
.found = true,
|
||||
.fstype = (char*) "squashfs",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
|
||||
ASSERT_NOT_NULL(policy);
|
||||
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_SQUASHFS | PARTITION_POLICY_VERITY | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
|
||||
|
||||
policy = image_policy_free(policy);
|
||||
|
||||
/* Test 5: Image with verity signature ready */
|
||||
image = (DissectedImage) {
|
||||
.verity_sig_ready = true,
|
||||
.partitions = {
|
||||
[PARTITION_ROOT] = {
|
||||
.found = true,
|
||||
.fstype = (char*) "erofs",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
|
||||
ASSERT_NOT_NULL(policy);
|
||||
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_EROFS | PARTITION_POLICY_SIGNED | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
|
||||
|
||||
policy = image_policy_free(policy);
|
||||
|
||||
/* Test 6: Image with growfs enabled */
|
||||
image = (DissectedImage) {
|
||||
.partitions = {
|
||||
[PARTITION_ROOT] = {
|
||||
.found = true,
|
||||
.fstype = (char*) "btrfs",
|
||||
.growfs = true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
|
||||
ASSERT_NOT_NULL(policy);
|
||||
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_BTRFS | PARTITION_POLICY_GROWFS_ON | PARTITION_POLICY_READ_ONLY_ON));
|
||||
|
||||
policy = image_policy_free(policy);
|
||||
|
||||
/* Test 7: Multiple partitions with different filesystems */
|
||||
image = (DissectedImage) {
|
||||
.partitions = {
|
||||
[PARTITION_ROOT] = {
|
||||
.found = true,
|
||||
.fstype = (char*) "ext4",
|
||||
},
|
||||
[PARTITION_USR] = {
|
||||
.found = true,
|
||||
.fstype = (char*) "xfs",
|
||||
},
|
||||
[PARTITION_HOME] = {
|
||||
.found = true,
|
||||
.fstype = (char*) "btrfs",
|
||||
.growfs = true,
|
||||
},
|
||||
[PARTITION_ESP] = {
|
||||
.found = true,
|
||||
.fstype = (char*) "vfat",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
|
||||
ASSERT_NOT_NULL(policy);
|
||||
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_EXT4 | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
|
||||
ASSERT_EQ(policy->policies[PARTITION_USR].flags, (PartitionPolicyFlags) (PARTITION_POLICY_XFS | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
|
||||
ASSERT_EQ(policy->policies[PARTITION_HOME].flags, (PartitionPolicyFlags) (PARTITION_POLICY_BTRFS | PARTITION_POLICY_GROWFS_ON | PARTITION_POLICY_READ_ONLY_ON));
|
||||
ASSERT_EQ(policy->policies[PARTITION_ESP].flags, (PartitionPolicyFlags) (PARTITION_POLICY_VFAT | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
|
||||
ASSERT_EQ(policy->policies[PARTITION_SWAP].flags, (PartitionPolicyFlags) PARTITION_POLICY_ABSENT);
|
||||
|
||||
policy = image_policy_free(policy);
|
||||
|
||||
/* Test 8: VeritySettings with root_hash set (no signature) */
|
||||
dummy_data[0] = 0xde; dummy_data[1] = 0xad; dummy_data[2] = 0xbe; dummy_data[3] = 0xef;
|
||||
verity = (VeritySettings) {
|
||||
.designator = _PARTITION_DESIGNATOR_INVALID,
|
||||
.root_hash = IOVEC_MAKE(dummy_data, sizeof(dummy_data)),
|
||||
};
|
||||
image = (DissectedImage) {
|
||||
.partitions = {
|
||||
[PARTITION_ROOT] = {
|
||||
.found = true,
|
||||
.fstype = (char*) "squashfs",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
policy = image_policy_new_from_dissected(&image, &verity);
|
||||
ASSERT_NOT_NULL(policy);
|
||||
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_SQUASHFS | PARTITION_POLICY_VERITY | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
|
||||
|
||||
policy = image_policy_free(policy);
|
||||
|
||||
/* Test 9: VeritySettings with root_hash_sig set */
|
||||
dummy_data[0] = 0x01; dummy_data[1] = 0x02; dummy_data[2] = 0x03; dummy_data[3] = 0x04;
|
||||
verity = (VeritySettings) {
|
||||
.designator = _PARTITION_DESIGNATOR_INVALID,
|
||||
.root_hash_sig = IOVEC_MAKE(dummy_data, sizeof(dummy_data)),
|
||||
};
|
||||
image = (DissectedImage) {
|
||||
.partitions = {
|
||||
[PARTITION_ROOT] = {
|
||||
.found = true,
|
||||
.fstype = (char*) "erofs",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
policy = image_policy_new_from_dissected(&image, &verity);
|
||||
ASSERT_NOT_NULL(policy);
|
||||
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_EROFS | PARTITION_POLICY_SIGNED | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
|
||||
|
||||
policy = image_policy_free(policy);
|
||||
|
||||
/* Test 10: VeritySettings with designator targeting specific partition */
|
||||
dummy_data[0] = 0xab; dummy_data[1] = 0xcd;
|
||||
verity = (VeritySettings) {
|
||||
.designator = PARTITION_USR,
|
||||
.root_hash = IOVEC_MAKE(dummy_data, 2),
|
||||
};
|
||||
image = (DissectedImage) {
|
||||
.partitions = {
|
||||
[PARTITION_ROOT] = {
|
||||
.found = true,
|
||||
.fstype = (char*) "ext4",
|
||||
},
|
||||
[PARTITION_USR] = {
|
||||
.found = true,
|
||||
.fstype = (char*) "squashfs",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
policy = image_policy_new_from_dissected(&image, &verity);
|
||||
ASSERT_NOT_NULL(policy);
|
||||
/* Root should NOT have verity since verity targets USR */
|
||||
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_EXT4 | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
|
||||
/* USR should have verity */
|
||||
ASSERT_EQ(policy->policies[PARTITION_USR].flags, (PartitionPolicyFlags) (PARTITION_POLICY_SQUASHFS | PARTITION_POLICY_VERITY | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
|
||||
|
||||
policy = image_policy_free(policy);
|
||||
|
||||
/* Test 11: Unknown filesystem type (should have no fstype flag) */
|
||||
image = (DissectedImage) {
|
||||
.partitions = {
|
||||
[PARTITION_ROOT] = {
|
||||
.found = true,
|
||||
.fstype = (char*) "unknown_fs",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
|
||||
ASSERT_NOT_NULL(policy);
|
||||
/* Should only have GROWFS_OFF and READ_ONLY_ON for unknown filesystem */
|
||||
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
|
||||
|
||||
policy = image_policy_free(policy);
|
||||
|
||||
/* Test 12: NULL filesystem type (should have no fstype flag) */
|
||||
image = (DissectedImage) {
|
||||
.partitions = {
|
||||
[PARTITION_ROOT] = {
|
||||
.found = true,
|
||||
.fstype = NULL,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
|
||||
ASSERT_NOT_NULL(policy);
|
||||
/* Should only have GROWFS_OFF and READ_ONLY_ON for NULL fstype */
|
||||
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
|
||||
|
||||
policy = image_policy_free(policy);
|
||||
|
||||
/* Test 13: Combination of verity_ready from image and verity settings - image takes precedence for sig */
|
||||
dummy_data[0] = 0x11; dummy_data[1] = 0x22;
|
||||
verity = (VeritySettings) {
|
||||
.designator = _PARTITION_DESIGNATOR_INVALID,
|
||||
.root_hash = IOVEC_MAKE(dummy_data, 2),
|
||||
};
|
||||
image = (DissectedImage) {
|
||||
.verity_sig_ready = true, /* This should take precedence */
|
||||
.partitions = {
|
||||
[PARTITION_ROOT] = {
|
||||
.found = true,
|
||||
.fstype = (char*) "squashfs",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
policy = image_policy_new_from_dissected(&image, &verity);
|
||||
ASSERT_NOT_NULL(policy);
|
||||
/* verity_sig_ready should result in SIGNED, not just VERITY */
|
||||
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_SQUASHFS | PARTITION_POLICY_SIGNED | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
|
||||
|
||||
policy = image_policy_free(policy);
|
||||
|
||||
/* Test 14: All known filesystem types */
|
||||
image = (DissectedImage) {
|
||||
.partitions = {
|
||||
[PARTITION_ROOT] = { .found = true, .fstype = (char*) "ext4" },
|
||||
[PARTITION_USR] = { .found = true, .fstype = (char*) "btrfs" },
|
||||
[PARTITION_HOME] = { .found = true, .fstype = (char*) "xfs" },
|
||||
[PARTITION_SRV] = { .found = true, .fstype = (char*) "f2fs" },
|
||||
[PARTITION_VAR] = { .found = true, .fstype = (char*) "erofs" },
|
||||
[PARTITION_TMP] = { .found = true, .fstype = (char*) "squashfs" },
|
||||
[PARTITION_ESP] = { .found = true, .fstype = (char*) "vfat" },
|
||||
},
|
||||
};
|
||||
|
||||
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
|
||||
ASSERT_NOT_NULL(policy);
|
||||
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_EXT4 | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
|
||||
ASSERT_EQ(policy->policies[PARTITION_USR].flags, (PartitionPolicyFlags) (PARTITION_POLICY_BTRFS | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
|
||||
ASSERT_EQ(policy->policies[PARTITION_HOME].flags, (PartitionPolicyFlags) (PARTITION_POLICY_XFS | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
|
||||
ASSERT_EQ(policy->policies[PARTITION_SRV].flags, (PartitionPolicyFlags) (PARTITION_POLICY_F2FS | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
|
||||
ASSERT_EQ(policy->policies[PARTITION_VAR].flags, (PartitionPolicyFlags) (PARTITION_POLICY_EROFS | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
|
||||
ASSERT_EQ(policy->policies[PARTITION_TMP].flags, (PartitionPolicyFlags) (PARTITION_POLICY_SQUASHFS | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
|
||||
ASSERT_EQ(policy->policies[PARTITION_ESP].flags, (PartitionPolicyFlags) (PARTITION_POLICY_VFAT | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
|
||||
|
||||
policy = image_policy_free(policy);
|
||||
|
||||
/* Test 15: Encrypted partition with verity (LUKS takes precedence, no verity flag) */
|
||||
image = (DissectedImage) {
|
||||
.verity_ready = true,
|
||||
.partitions = {
|
||||
[PARTITION_ROOT] = {
|
||||
.found = true,
|
||||
.fstype = (char*) "crypto_LUKS",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
|
||||
ASSERT_NOT_NULL(policy);
|
||||
/* crypto_LUKS check happens first, then verity is added */
|
||||
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_ENCRYPTED | PARTITION_POLICY_VERITY | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_ON));
|
||||
|
||||
policy = image_policy_free(policy);
|
||||
|
||||
/* Test 16: Multiple flags combined - encrypted + growfs */
|
||||
image = (DissectedImage) {
|
||||
.partitions = {
|
||||
[PARTITION_HOME] = {
|
||||
.found = true,
|
||||
.fstype = (char*) "crypto_LUKS",
|
||||
.growfs = true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
|
||||
ASSERT_NOT_NULL(policy);
|
||||
ASSERT_EQ(policy->policies[PARTITION_HOME].flags, (PartitionPolicyFlags) (PARTITION_POLICY_ENCRYPTED | PARTITION_POLICY_GROWFS_ON | PARTITION_POLICY_READ_ONLY_ON));
|
||||
|
||||
policy = image_policy_free(policy);
|
||||
|
||||
/* Test 17: single_file_system=true should NOT set growfs or read-only flags */
|
||||
image = (DissectedImage) {
|
||||
.single_file_system = true,
|
||||
.partitions = {
|
||||
[PARTITION_ROOT] = {
|
||||
.found = true,
|
||||
.fstype = (char*) "ext4",
|
||||
.rw = true,
|
||||
.growfs = true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
|
||||
ASSERT_NOT_NULL(policy);
|
||||
/* single_file_system=true means no growfs/read-only flags */
|
||||
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) PARTITION_POLICY_EXT4);
|
||||
|
||||
policy = image_policy_free(policy);
|
||||
|
||||
/* Test 18: rw=true should set READ_ONLY_OFF instead of READ_ONLY_ON */
|
||||
image = (DissectedImage) {
|
||||
.partitions = {
|
||||
[PARTITION_ROOT] = {
|
||||
.found = true,
|
||||
.fstype = (char*) "ext4",
|
||||
.rw = true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
|
||||
ASSERT_NOT_NULL(policy);
|
||||
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_EXT4 | PARTITION_POLICY_GROWFS_OFF | PARTITION_POLICY_READ_ONLY_OFF));
|
||||
|
||||
policy = image_policy_free(policy);
|
||||
|
||||
/* Test 19: rw=true with growfs=true */
|
||||
image = (DissectedImage) {
|
||||
.partitions = {
|
||||
[PARTITION_ROOT] = {
|
||||
.found = true,
|
||||
.fstype = (char*) "btrfs",
|
||||
.rw = true,
|
||||
.growfs = true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
policy = image_policy_new_from_dissected(&image, /* verity= */ NULL);
|
||||
ASSERT_NOT_NULL(policy);
|
||||
ASSERT_EQ(policy->policies[PARTITION_ROOT].flags, (PartitionPolicyFlags) (PARTITION_POLICY_BTRFS | PARTITION_POLICY_GROWFS_ON | PARTITION_POLICY_READ_ONLY_OFF));
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_INFO);
|
||||
|
||||
@@ -25,6 +25,10 @@ systemctl is-active minimal-app0.service
|
||||
systemctl is-active minimal-app0-foo.service
|
||||
systemctl is-active minimal-app0-bar.service && exit 1
|
||||
|
||||
# Ensure pinning by policy works
|
||||
cat /run/systemd/system.attached/minimal-app0-foo.service.d/20-portable.conf
|
||||
grep -q -F 'root=signed+squashfs:' /run/systemd/system.attached/minimal-app0-foo.service.d/20-portable.conf
|
||||
|
||||
portablectl "${ARGS[@]}" reattach --now --runtime /usr/share/minimal_1.raw minimal-app0
|
||||
|
||||
portablectl is-attached minimal-app0
|
||||
@@ -91,6 +95,9 @@ status="$(portablectl is-attached --extension app0 minimal_0)"
|
||||
grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
|
||||
grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
|
||||
grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf
|
||||
# Ensure pinning by policy works
|
||||
grep -q -F 'RootImagePolicy=root=signed+squashfs:' /run/systemd/system.attached/app0.service.d/20-portable.conf >/dev/null
|
||||
grep -q -F 'ExtensionImagePolicy=root=signed+squashfs:' /run/systemd/system.attached/app0.service.d/20-portable.conf >/dev/null
|
||||
|
||||
portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_1.raw app0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user