diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 5a27620244..1162982f71 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -568,6 +568,83 @@ static void dissected_partition_done(DissectedPartition *p) { *p = DISSECTED_PARTITION_NULL; } +static int acquire_sig_for_roothash( + int fd, + uint64_t partition_offset, + uint64_t partition_size, + void **ret_root_hash, + size_t *ret_root_hash_size, + void **ret_root_hash_sig, + size_t *ret_root_hash_sig_size) { + + int r; + + assert(fd >= 0); + assert(!!ret_root_hash == !!ret_root_hash_size); + assert(!!ret_root_hash_sig == !!ret_root_hash_sig_size); + + if (partition_offset == UINT64_MAX || partition_size == UINT64_MAX) + return -EINVAL; + + if (partition_size > 4*1024*1024) /* Signature data cannot possible be larger than 4M, refuse that */ + return log_debug_errno(SYNTHETIC_ERRNO(EFBIG), "Verity signature partition is larger than 4M, refusing."); + + _cleanup_free_ char *buf = new(char, partition_size+1); + if (!buf) + return -ENOMEM; + + ssize_t n = pread(fd, buf, partition_size, partition_offset); + if (n < 0) + return -ENOMEM; + if ((uint64_t) n != partition_size) + return -EIO; + + const char *e = memchr(buf, 0, partition_size); + if (e) { + /* If we found a NUL byte then the rest of the data must be NUL too */ + if (!memeqzero(e, partition_size - (e - buf))) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Signature data contains embedded NUL byte."); + } else + buf[partition_size] = 0; + + _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; + r = sd_json_parse(buf, 0, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL); + if (r < 0) + return log_debug_errno(r, "Failed to parse signature JSON data: %m"); + + sd_json_variant *rh = sd_json_variant_by_key(v, "rootHash"); + if (!rh) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Signature JSON object lacks 'rootHash' field."); + + _cleanup_free_ void *root_hash = NULL; + size_t root_hash_size; + r = sd_json_variant_unhex(rh, &root_hash, &root_hash_size); + if (r < 0) + return log_debug_errno(r, "Failed to parse root hash field: %m"); + + sd_json_variant *sig = sd_json_variant_by_key(v, "signature"); + if (!sig) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Signature JSON object lacks 'signature' field."); + + _cleanup_free_ void *root_hash_sig = NULL; + size_t root_hash_sig_size; + r = sd_json_variant_unbase64(sig, &root_hash_sig, &root_hash_sig_size); + if (r < 0) + return log_debug_errno(r, "Failed to parse signature field: %m"); + + if (ret_root_hash) { + *ret_root_hash = TAKE_PTR(root_hash); + *ret_root_hash_size = root_hash_size; + } + + if (ret_root_hash_sig) { + *ret_root_hash_sig = TAKE_PTR(root_hash_sig); + *ret_root_hash_sig_size = root_hash_sig_size; + } + + return 0; +} + #if HAVE_BLKID static int diskseq_should_be_used( const char *whole_devname, @@ -1159,6 +1236,32 @@ static int dissect_image( rw = false; } else if (type.designator == PARTITION_ROOT_VERITY_SIG) { + if (verity && verity->root_hash) { + _cleanup_free_ void *root_hash = NULL; + size_t root_hash_size; + + r = acquire_sig_for_roothash( + fd, + start * 512, + size * 512, + &root_hash, + &root_hash_size, + /* ret_root_hash_sig= */ NULL, + /* ret_root_hash_sig_size= */ NULL); + if (r < 0) + return r; + if (memcmp_nn(verity->root_hash, verity->root_hash_size, root_hash, root_hash_size) != 0) { + if (DEBUG_LOGGING) { + _cleanup_free_ char *found = NULL, *expected = NULL; + + found = hexmem(root_hash, root_hash_size); + expected = hexmem(verity->root_hash, verity->root_hash_size); + + log_debug("Root hash in signature JSON data (%s) doesn't match configured hash (%s).", strna(found), strna(expected)); + } + continue; + } + } check_partition_flags(node, pflags, SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY); @@ -1202,6 +1305,32 @@ static int dissect_image( rw = false; } else if (type.designator == PARTITION_USR_VERITY_SIG) { + if (verity && verity->root_hash) { + _cleanup_free_ void *root_hash = NULL; + size_t root_hash_size; + + r = acquire_sig_for_roothash( + fd, + start * 512, + size * 512, + &root_hash, + &root_hash_size, + /* ret_root_hash_sig= */ NULL, + /* ret_root_hash_sig_size= */ NULL); + if (r < 0) + return r; + if (memcmp_nn(verity->root_hash, verity->root_hash_size, root_hash, root_hash_size) != 0) { + if (DEBUG_LOGGING) { + _cleanup_free_ char *found = NULL, *expected = NULL; + + found = hexmem(root_hash, root_hash_size); + expected = hexmem(verity->root_hash, verity->root_hash_size); + + log_debug("Root hash in signature JSON data (%s) doesn't match configured hash (%s).", strna(found), strna(expected)); + } + continue; + } + } check_partition_flags(node, pflags, SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY); @@ -3661,44 +3790,13 @@ int dissected_image_load_verity_sig_partition( DissectedPartition *p = m->partitions + ds; 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 log_debug_errno(SYNTHETIC_ERRNO(EFBIG), "Verity signature partition is larger than 4M, refusing."); + _cleanup_free_ void *root_hash = NULL, *root_hash_sig = NULL; + size_t root_hash_size, root_hash_sig_size; - _cleanup_free_ char *buf = new(char, p->size+1); - if (!buf) - return -ENOMEM; - - ssize_t n = pread(fd, buf, p->size, p->offset); - if (n < 0) - return -ENOMEM; - if ((uint64_t) n != p->size) - return -EIO; - - const char *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; - - _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; - r = sd_json_parse(buf, 0, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL); + r = acquire_sig_for_roothash(fd, p->offset, p->size, &root_hash, &root_hash_size, &root_hash_sig, &root_hash_sig_size); if (r < 0) - return log_debug_errno(r, "Failed to parse signature JSON data: %m"); - - sd_json_variant *rh = sd_json_variant_by_key(v, "rootHash"); - if (!rh) - return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Signature JSON object lacks 'rootHash' field."); - - _cleanup_free_ void *root_hash = NULL; - size_t root_hash_size; - r = sd_json_variant_unhex(rh, &root_hash, &root_hash_size); - if (r < 0) - return log_debug_errno(r, "Failed to parse root hash field: %m"); + return r; /* Check if specified root hash matches if it is specified */ if (verity->root_hash && @@ -3711,16 +3809,6 @@ int dissected_image_load_verity_sig_partition( return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Root hash in signature JSON data (%s) doesn't match configured hash (%s).", strna(a), strna(b)); } - sd_json_variant *sig = sd_json_variant_by_key(v, "signature"); - if (!sig) - return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Signature JSON object lacks 'signature' field."); - - _cleanup_free_ void *root_hash_sig = NULL; - size_t root_hash_sig_size; - r = sd_json_variant_unbase64(sig, &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;