From: Lennart Poettering Date: Thu, 9 Sep 2021 11:46:01 +0000 (+0200) Subject: dissect-image: load embedded verity signature info from image X-Git-Tag: v250-rc1~606^2~6 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=88b3300fdc64d5320fb50d0f369d3fc0885e15e8;p=thirdparty%2Fsystemd.git dissect-image: load embedded verity signature info from image This adds support for actually using embedded signature data from partitions. --- diff --git a/src/core/namespace.c b/src/core/namespace.c index 0d49f52217e..038da6c554b 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -1913,6 +1913,13 @@ int setup_namespace( if (r < 0) return log_debug_errno(r, "Failed to dissect image: %m"); + r = dissected_image_load_verity_sig_partition( + dissected_image, + loop_device->fd, + &verity); + if (r < 0) + return r; + r = dissected_image_decrypt( dissected_image, NULL, diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index d6db939dcbb..c145c276114 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -805,6 +805,13 @@ static int run(int argc, char *argv[]) { if (r < 0) return r; + r = dissected_image_load_verity_sig_partition( + m, + d->fd, + &arg_verity_settings); + if (r < 0) + return r; + switch (arg_action) { case ACTION_DISSECT: diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 03e1467f2d0..3694841ba84 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -5710,6 +5710,13 @@ static int run(int argc, char *argv[]) { if (r < 0) goto finish; + r = dissected_image_load_verity_sig_partition( + dissected_image, + loop->fd, + &arg_verity_settings); + if (r < 0) + goto finish; + if (dissected_image->has_verity && !arg_verity_settings.root_hash && !dissected_image->has_verity_sig) log_notice("Note: image %s contains verity information, but no root hash specified and no embedded " "root hash signature found! Proceeding without integrity checking.", arg_image); diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index af8878f3b8e..af65d15306a 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -27,6 +27,7 @@ #include "dissect-image.h" #include "dm-util.h" #include "env-file.h" +#include "env-util.h" #include "extension-release.h" #include "fd-util.h" #include "fileio.h" @@ -826,6 +827,8 @@ int dissect_image( .fstype = TAKE_PTR(t), .node = TAKE_PTR(n), .mount_options = TAKE_PTR(o), + .offset = 0, + .size = UINT64_MAX, }; *ret = TAKE_PTR(m); @@ -869,6 +872,7 @@ int dissect_image( for (int i = 0; i < n_partitions; i++) { _cleanup_(sd_device_unrefp) sd_device *q = NULL; unsigned long long pflags; + blkid_loff_t start, size; blkid_partition pp; const char *node; int nr; @@ -893,6 +897,20 @@ int dissect_image( if (nr < 0) return errno_or_else(EIO); + errno = 0; + start = blkid_partition_get_start(pp); + if (start < 0) + return errno_or_else(EIO); + + assert((uint64_t) start < UINT64_MAX/512); + + errno = 0; + size = blkid_partition_get_size(pp); + if (size < 0) + return errno_or_else(EIO); + + assert((uint64_t) size < UINT64_MAX/512); + if (is_gpt) { PartitionDesignator designator = _PARTITION_DESIGNATOR_INVALID; int architecture = _ARCHITECTURE_INVALID; @@ -1337,6 +1355,8 @@ int dissect_image( .label = TAKE_PTR(l), .uuid = id, .mount_options = TAKE_PTR(o), + .offset = (uint64_t) start * 512, + .size = (uint64_t) size * 512, }; } @@ -1395,6 +1415,8 @@ int dissect_image( .node = TAKE_PTR(n), .uuid = id, .mount_options = TAKE_PTR(o), + .offset = (uint64_t) start * 512, + .size = (uint64_t) size * 512, }; break; @@ -1515,6 +1537,8 @@ int dissect_image( .node = TAKE_PTR(generic_node), .uuid = generic_uuid, .mount_options = TAKE_PTR(o), + .offset = UINT64_MAX, + .size = UINT64_MAX, }; } } @@ -2741,6 +2765,109 @@ int verity_settings_load( return 1; } +int dissected_image_load_verity_sig_partition( + DissectedImage *m, + int fd, + VeritySettings *verity) { + + _cleanup_free_ void *root_hash = NULL, *root_hash_sig = NULL; + _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; + size_t root_hash_size, root_hash_sig_size; + _cleanup_free_ char *buf = NULL; + PartitionDesignator d; + DissectedPartition *p; + JsonVariant *rh, *sig; + ssize_t n; + char *e; + int r; + + assert(m); + assert(fd >= 0); + assert(verity); + + if (verity->root_hash && verity->root_hash_sig) /* Already loaded? */ + return 0; + + r = getenv_bool_secure("SYSTEMD_DISSECT_VERITY_EMBEDDED"); + if (r < 0 && r != -ENXIO) + log_debug_errno(r, "Failed to parse $SYSTEMD_DISSECT_VERITY_EMBEDDED, ignoring: %m"); + if (r == 0) + return 0; + + d = PARTITION_VERITY_SIG_OF(verity->designator < 0 ? PARTITION_ROOT : verity->designator); + assert(d >= 0); + + p = m->partitions + d; + if (!p->found) + return 0; + if (p->offset == UINT64_MAX || p->size == UINT64_MAX) + return -EINVAL; + + if (p->size > 4*1024*1024) /* Signature data cannot possible be larger than 4M, refuse that */ + return -EFBIG; + + buf = new(char, p->size+1); + if (!buf) + return -ENOMEM; + + n = pread(fd, buf, p->size, p->offset); + if (n < 0) + return -ENOMEM; + if ((uint64_t) n != p->size) + return -EIO; + + e = memchr(buf, 0, p->size); + if (e) { + /* If we found a NUL byte then the rest of the data must be NUL too */ + if (!memeqzero(e, p->size - (e - buf))) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Signature data contains embedded NUL byte."); + } else + buf[p->size] = 0; + + r = json_parse(buf, 0, &v, NULL, NULL); + if (r < 0) + return log_debug_errno(r, "Failed to parse signature JSON data: %m"); + + rh = json_variant_by_key(v, "rootHash"); + if (!rh) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Signature JSON object lacks 'rootHash' field."); + if (!json_variant_is_string(rh)) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "'rootHash' field of signature JSON object is not a string."); + + r = unhexmem(json_variant_string(rh), SIZE_MAX, &root_hash, &root_hash_size); + if (r < 0) + return log_debug_errno(r, "Failed to parse root hash field: %m"); + + /* Check if specified root hash matches if it is specified */ + if (verity->root_hash && + memcmp_nn(verity->root_hash, verity->root_hash_size, root_hash, root_hash_size) != 0) { + _cleanup_free_ char *a = NULL, *b = NULL; + + a = hexmem(root_hash, root_hash_size); + b = hexmem(verity->root_hash, verity->root_hash_size); + + return log_debug_errno(r, "Root hash in signature JSON data (%s) doesn't match configured hash (%s).", strna(a), strna(b)); + } + + sig = json_variant_by_key(v, "signature"); + if (!sig) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Signature JSON object lacks 'signature' field."); + if (!json_variant_is_string(sig)) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "'signature' field of signature JSON object is not a string."); + + r = unbase64mem(json_variant_string(sig), SIZE_MAX, &root_hash_sig, &root_hash_sig_size); + if (r < 0) + return log_debug_errno(r, "Failed to parse signature field: %m"); + + free_and_replace(verity->root_hash, root_hash); + verity->root_hash_size = root_hash_size; + + free_and_replace(verity->root_hash_sig, root_hash_sig); + verity->root_hash_sig_size = root_hash_sig_size; + + return 1; +} + int dissected_image_acquire_metadata(DissectedImage *m) { enum { @@ -3131,6 +3258,10 @@ int mount_image_privately_interactively( if (r < 0) return r; + r = dissected_image_load_verity_sig_partition(dissected_image, d->fd, &verity); + if (r < 0) + return r; + r = dissected_image_decrypt_interactively(dissected_image, NULL, &verity, flags, &decrypted_image); if (r < 0) return r; @@ -3241,6 +3372,10 @@ int verity_dissect_and_mount( if (r < 0) return log_debug_errno(r, "Failed to dissect image: %m"); + r = dissected_image_load_verity_sig_partition(dissected_image, loop_device->fd, &verity); + if (r < 0) + return r; + r = dissected_image_decrypt( dissected_image, NULL, diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 84b14cae804..635ca5b76c1 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -28,6 +28,8 @@ struct DissectedPartition { char *decrypted_node; char *decrypted_fstype; char *mount_options; + uint64_t size; + uint64_t offset; }; typedef enum PartitionDesignator { @@ -217,6 +219,8 @@ PartitionDesignator partition_designator_from_string(const char *name) _pure_; int verity_settings_load(VeritySettings *verity, const char *image, const char *root_hash_path, const char *root_hash_sig_path); void verity_settings_done(VeritySettings *verity); +int dissected_image_load_verity_sig_partition(DissectedImage *m, int fd, VeritySettings *verity); + bool dissected_image_verity_candidate(const DissectedImage *image, PartitionDesignator d); bool dissected_image_verity_ready(const DissectedImage *image, PartitionDesignator d); bool dissected_image_verity_sig_ready(const DissectedImage *image, PartitionDesignator d); diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index f15d4dd61da..67112c4e14d 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -540,6 +540,13 @@ static int merge_subprocess(Hashmap *images, const char *workspace) { if (r < 0) return r; + r = dissected_image_load_verity_sig_partition( + m, + d->fd, + &verity_settings); + if (r < 0) + return r; + r = dissected_image_decrypt_interactively( m, NULL, &verity_settings,