From: Lennart Poettering Date: Thu, 9 Sep 2021 09:43:13 +0000 (+0200) Subject: dissect-image: discover verity signature partitions X-Git-Tag: v250-rc1~606^2~7 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=8ee9615e10f449dcabbd5e27c960c26857943832;p=thirdparty%2Fsystemd.git dissect-image: discover verity signature partitions This doesn't make use of the discovered partitions yet, but it finds them at least. --- diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index 3f464f8dade..d6db939dcbb 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -515,7 +515,9 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { if (arg_verity_settings.data_path) r = table_add_cell(t, NULL, TABLE_STRING, "external"); else if (dissected_image_verity_candidate(m, i)) - r = table_add_cell(t, NULL, TABLE_STRING, yes_no(dissected_image_verity_ready(m, i))); + r = table_add_cell(t, NULL, TABLE_STRING, + dissected_image_verity_sig_ready(m, i) ? "signed" : + yes_no(dissected_image_verity_ready(m, i))); else r = table_add_cell(t, NULL, TABLE_EMPTY, NULL); if (r < 0) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 97e55adc48f..03e1467f2d0 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -5710,8 +5710,9 @@ static int run(int argc, char *argv[]) { if (r < 0) goto finish; - if (!arg_verity_settings.root_hash && dissected_image->has_verity) - log_notice("Note: image %s contains verity information, but no root hash specified! Proceeding without integrity checking.", arg_image); + 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); r = dissected_image_decrypt_interactively( dissected_image, diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 1cfbde683d4..af8878f3b8e 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -807,6 +807,10 @@ int dissect_image( verity->root_hash && (verity->designator < 0 || verity->designator == PARTITION_ROOT); + m->has_verity_sig = false; /* signature not embedded, must be specified */ + m->verity_sig_ready = m->verity_ready && + verity->root_hash_sig; + options = mount_options_from_designator(mount_options, PARTITION_ROOT); if (options) { o = strdup(options); @@ -982,14 +986,42 @@ int dissect_image( m->has_verity = true; - /* Ignore verity unless a root hash is specified */ - if (sd_id128_is_null(root_verity_uuid) || !sd_id128_equal(root_verity_uuid, id)) + /* If no verity configuration is specified, then don't do verity */ + if (!verity) + continue; + if (verity->designator >= 0 && verity->designator != PARTITION_ROOT) + continue; + + /* If root hash is specified, then ignore everything but the root id */ + if (!sd_id128_is_null(root_verity_uuid) && !sd_id128_equal(root_verity_uuid, id)) continue; designator = PARTITION_ROOT_VERITY; fstype = "DM_verity_hash"; architecture = native_architecture(); rw = false; + + } else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE_VERITY_SIG)) { + + check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY); + + if (pflags & GPT_FLAG_NO_AUTO) + continue; + + m->has_verity_sig = true; + + /* If root hash is specified explicitly, then ignore any embedded signature */ + if (!verity) + continue; + if (verity->designator >= 0 && verity->designator != PARTITION_ROOT) + continue; + if (verity->root_hash) + continue; + + designator = PARTITION_ROOT_VERITY_SIG; + fstype = "verity_hash_signature"; + architecture = native_architecture(); + rw = false; } #endif #ifdef GPT_ROOT_SECONDARY @@ -1018,14 +1050,42 @@ int dissect_image( m->has_verity = true; - /* Ignore verity unless root has is specified */ - if (sd_id128_is_null(root_verity_uuid) || !sd_id128_equal(root_verity_uuid, id)) + /* Don't do verity if no verity config is passed in */ + if (!verity) + continue; + if (verity->designator >= 0 && verity->designator != PARTITION_ROOT) + continue; + + /* If root hash is specified, then ignore everything but the root id */ + if (!sd_id128_is_null(root_verity_uuid) && !sd_id128_equal(root_verity_uuid, id)) continue; designator = PARTITION_ROOT_SECONDARY_VERITY; fstype = "DM_verity_hash"; architecture = SECONDARY_ARCHITECTURE; rw = false; + + } else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY_VERITY_SIG)) { + + check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY); + + if (pflags & GPT_FLAG_NO_AUTO) + continue; + + m->has_verity_sig = true; + + /* If root hash is specified explicitly, then ignore any embedded signature */ + if (!verity) + continue; + if (verity->designator >= 0 && verity->designator != PARTITION_ROOT) + continue; + if (verity->root_hash) + continue; + + designator = PARTITION_ROOT_SECONDARY_VERITY_SIG; + fstype = "verity_hash_signature"; + architecture = native_architecture(); + rw = false; } #endif #ifdef GPT_USR_NATIVE @@ -1054,14 +1114,41 @@ int dissect_image( m->has_verity = true; - /* Ignore verity unless a usr hash is specified */ - if (sd_id128_is_null(usr_verity_uuid) || !sd_id128_equal(usr_verity_uuid, id)) + if (!verity) + continue; + if (verity->designator >= 0 && verity->designator != PARTITION_USR) + continue; + + /* If usr hash is specified, then ignore everything but the usr id */ + if (!sd_id128_is_null(usr_verity_uuid) && !sd_id128_equal(usr_verity_uuid, id)) continue; designator = PARTITION_USR_VERITY; fstype = "DM_verity_hash"; architecture = native_architecture(); rw = false; + + } else if (sd_id128_equal(type_id, GPT_USR_NATIVE_VERITY_SIG)) { + + check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY); + + if (pflags & GPT_FLAG_NO_AUTO) + continue; + + m->has_verity_sig = true; + + /* If usr hash is specified explicitly, then ignore any embedded signature */ + if (!verity) + continue; + if (verity->designator >= 0 && verity->designator != PARTITION_USR) + continue; + if (verity->root_hash) + continue; + + designator = PARTITION_USR_VERITY_SIG; + fstype = "verity_hash_signature"; + architecture = native_architecture(); + rw = false; } #endif #ifdef GPT_USR_SECONDARY @@ -1090,14 +1177,41 @@ int dissect_image( m->has_verity = true; - /* Ignore verity unless usr has is specified */ - if (sd_id128_is_null(usr_verity_uuid) || !sd_id128_equal(usr_verity_uuid, id)) + if (!verity) + continue; + if (verity->designator >= 0 && verity->designator != PARTITION_USR) + continue; + + /* If usr hash is specified, then ignore everything but the root id */ + if (!sd_id128_is_null(usr_verity_uuid) && !sd_id128_equal(usr_verity_uuid, id)) continue; designator = PARTITION_USR_SECONDARY_VERITY; fstype = "DM_verity_hash"; architecture = SECONDARY_ARCHITECTURE; rw = false; + + } else if (sd_id128_equal(type_id, GPT_USR_SECONDARY_VERITY_SIG)) { + + check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY); + + if (pflags & GPT_FLAG_NO_AUTO) + continue; + + m->has_verity_sig = true; + + /* If usr hash is specified explicitly, then ignore any embedded signature */ + if (!verity) + continue; + if (verity->designator >= 0 && verity->designator != PARTITION_USR) + continue; + if (verity->root_hash) + continue; + + designator = PARTITION_USR_SECONDARY_VERITY_SIG; + fstype = "verity_hash_signature"; + architecture = native_architecture(); + rw = false; } #endif else if (sd_id128_equal(type_id, GPT_SWAP)) { @@ -1293,10 +1407,13 @@ int dissect_image( * since we never want to mount the secondary arch in this case. */ m->partitions[PARTITION_ROOT_SECONDARY].found = false; m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found = false; + m->partitions[PARTITION_ROOT_SECONDARY_VERITY_SIG].found = false; m->partitions[PARTITION_USR_SECONDARY].found = false; m->partitions[PARTITION_USR_SECONDARY_VERITY].found = false; + m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG].found = false; - } else if (m->partitions[PARTITION_ROOT_VERITY].found) + } else if (m->partitions[PARTITION_ROOT_VERITY].found || + m->partitions[PARTITION_ROOT_VERITY_SIG].found) return -EADDRNOTAVAIL; /* Verity found but no matching rootfs? Something is off, refuse. */ else if (m->partitions[PARTITION_ROOT_SECONDARY].found) { @@ -1308,22 +1425,32 @@ int dissect_image( zero(m->partitions[PARTITION_ROOT_SECONDARY]); m->partitions[PARTITION_ROOT_VERITY] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY]; zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]); + m->partitions[PARTITION_ROOT_VERITY_SIG] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY_SIG]; + zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY_SIG]); m->partitions[PARTITION_USR] = m->partitions[PARTITION_USR_SECONDARY]; zero(m->partitions[PARTITION_USR_SECONDARY]); m->partitions[PARTITION_USR_VERITY] = m->partitions[PARTITION_USR_SECONDARY_VERITY]; zero(m->partitions[PARTITION_USR_SECONDARY_VERITY]); + m->partitions[PARTITION_USR_VERITY_SIG] = m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG]; + zero(m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG]); - } else if (m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found) + } else if (m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found || + m->partitions[PARTITION_ROOT_SECONDARY_VERITY_SIG].found) return -EADDRNOTAVAIL; /* as above */ - if (m->partitions[PARTITION_USR].found) { + /* Hmm, we found a signature partition but no Verity data? Something is off. */ + if (m->partitions[PARTITION_ROOT_VERITY_SIG].found && !m->partitions[PARTITION_ROOT_VERITY].found) + return -EADDRNOTAVAIL; + if (m->partitions[PARTITION_USR].found) { /* Invalidate secondary arch /usr/ if we found the primary arch */ m->partitions[PARTITION_USR_SECONDARY].found = false; m->partitions[PARTITION_USR_SECONDARY_VERITY].found = false; + m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG].found = false; - } else if (m->partitions[PARTITION_USR_VERITY].found) + } else if (m->partitions[PARTITION_USR_VERITY].found || + m->partitions[PARTITION_USR_VERITY_SIG].found) return -EADDRNOTAVAIL; /* as above */ else if (m->partitions[PARTITION_USR_SECONDARY].found) { @@ -1333,10 +1460,17 @@ int dissect_image( zero(m->partitions[PARTITION_USR_SECONDARY]); m->partitions[PARTITION_USR_VERITY] = m->partitions[PARTITION_USR_SECONDARY_VERITY]; zero(m->partitions[PARTITION_USR_SECONDARY_VERITY]); + m->partitions[PARTITION_USR_VERITY_SIG] = m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG]; + zero(m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG]); - } else if (m->partitions[PARTITION_USR_SECONDARY_VERITY].found) + } else if (m->partitions[PARTITION_USR_SECONDARY_VERITY].found || + m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG].found) return -EADDRNOTAVAIL; /* as above */ + /* Hmm, we found a signature partition but no Verity data? Something is off. */ + if (m->partitions[PARTITION_USR_VERITY_SIG].found && !m->partitions[PARTITION_USR_VERITY].found) + return -EADDRNOTAVAIL; + /* If root and /usr are combined then insist that the architecture matches */ if (m->partitions[PARTITION_ROOT].found && m->partitions[PARTITION_USR].found && @@ -1407,6 +1541,9 @@ int dissect_image( return -EADDRNOTAVAIL; if (verity->root_hash) { + /* If we have an explicit root hash and found the partitions for it, then we are ready to use + * Verity, set things up for it */ + if (verity->designator < 0 || verity->designator == PARTITION_ROOT) { if (!m->partitions[PARTITION_ROOT_VERITY].found || !m->partitions[PARTITION_ROOT].found) return -EADDRNOTAVAIL; @@ -1424,6 +1561,16 @@ int dissect_image( m->partitions[PARTITION_USR].rw = false; m->verity_ready = true; } + + if (m->verity_ready) + m->verity_sig_ready = !!verity->root_hash_sig; + + } else if (m->partitions[verity->designator == PARTITION_USR ? PARTITION_USR_VERITY_SIG : PARTITION_ROOT_VERITY_SIG].found) { + + /* If we found an embedded signature partition, we are ready, too. */ + + m->verity_ready = m->verity_sig_ready = true; + m->partitions[verity->designator == PARTITION_USR ? PARTITION_USR : PARTITION_ROOT].rw = false; } } @@ -2901,6 +3048,23 @@ bool dissected_image_verity_ready(const DissectedImage *image, PartitionDesignat return k >= 0 && image->partitions[k].found; } +bool dissected_image_verity_sig_ready(const DissectedImage *image, PartitionDesignator partition_designator) { + PartitionDesignator k; + + assert(image); + + /* Checks if this partition has verity signature data available that we can use. */ + + if (!image->verity_sig_ready) + return false; + + if (image->single_file_system) + return partition_designator == PARTITION_ROOT; + + k = PARTITION_VERITY_SIG_OF(partition_designator); + return k >= 0 && image->partitions[k].found; +} + MountOptions* mount_options_free_all(MountOptions *options) { MountOptions *m; @@ -3014,6 +3178,10 @@ static const char *const partition_designator_table[] = { [PARTITION_ROOT_SECONDARY_VERITY] = "root-secondary-verity", [PARTITION_USR_VERITY] = "usr-verity", [PARTITION_USR_SECONDARY_VERITY] = "usr-secondary-verity", + [PARTITION_ROOT_VERITY_SIG] = "root-verity-sig", + [PARTITION_ROOT_SECONDARY_VERITY_SIG] = "root-secondary-verity-sig", + [PARTITION_USR_VERITY_SIG] = "usr-verity-sig", + [PARTITION_USR_SECONDARY_VERITY_SIG] = "usr-secondary-verity-sig", [PARTITION_TMP] = "tmp", [PARTITION_VAR] = "var", }; diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 55c8f29c283..84b14cae804 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -44,6 +44,10 @@ typedef enum PartitionDesignator { PARTITION_ROOT_SECONDARY_VERITY, /* verity data for the PARTITION_ROOT_SECONDARY partition */ PARTITION_USR_VERITY, PARTITION_USR_SECONDARY_VERITY, + PARTITION_ROOT_VERITY_SIG, /* PKCS#7 signature for root hash for the PARTITION_ROOT partition */ + PARTITION_ROOT_SECONDARY_VERITY_SIG, /* ditto for the PARTITION_ROOT_SECONDARY partition */ + PARTITION_USR_VERITY_SIG, + PARTITION_USR_SECONDARY_VERITY_SIG, PARTITION_TMP, PARTITION_VAR, _PARTITION_DESIGNATOR_MAX, @@ -64,7 +68,11 @@ static inline bool PARTITION_DESIGNATOR_VERSIONED(PartitionDesignator d) { PARTITION_ROOT_VERITY, PARTITION_ROOT_SECONDARY_VERITY, PARTITION_USR_VERITY, - PARTITION_USR_SECONDARY_VERITY); + PARTITION_USR_SECONDARY_VERITY, + PARTITION_ROOT_VERITY_SIG, + PARTITION_ROOT_SECONDARY_VERITY_SIG, + PARTITION_USR_VERITY_SIG, + PARTITION_USR_SECONDARY_VERITY_SIG); } static inline PartitionDesignator PARTITION_VERITY_OF(PartitionDesignator p) { @@ -87,6 +95,26 @@ static inline PartitionDesignator PARTITION_VERITY_OF(PartitionDesignator p) { } } +static inline PartitionDesignator PARTITION_VERITY_SIG_OF(PartitionDesignator p) { + switch (p) { + + case PARTITION_ROOT: + return PARTITION_ROOT_VERITY_SIG; + + case PARTITION_ROOT_SECONDARY: + return PARTITION_ROOT_SECONDARY_VERITY_SIG; + + case PARTITION_USR: + return PARTITION_USR_VERITY_SIG; + + case PARTITION_USR_SECONDARY: + return PARTITION_USR_SECONDARY_VERITY_SIG; + + default: + return _PARTITION_DESIGNATOR_INVALID; + } +} + typedef enum DissectImageFlags { DISSECT_IMAGE_DEVICE_READ_ONLY = 1 << 0, /* Make device read-only */ DISSECT_IMAGE_DISCARD_ON_LOOP = 1 << 1, /* Turn on "discard" if on a loop device and file system supports it */ @@ -119,11 +147,14 @@ typedef enum DissectImageFlags { struct DissectedImage { bool encrypted:1; bool has_verity:1; /* verity available in image, but not necessarily used */ + bool has_verity_sig:1; /* pkcs#7 signature embedded in image */ bool verity_ready:1; /* verity available, fully specified and usable */ + bool verity_sig_ready:1; /* verity signature logic, fully specified and usable */ bool single_file_system:1; /* MBR/GPT or single file system */ DissectedPartition partitions[_PARTITION_DESIGNATOR_MAX]; + /* Meta information extracted from /etc/os-release and similar */ char *image_name; char *hostname; sd_id128_t machine_id; @@ -188,6 +219,7 @@ void verity_settings_done(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); int mount_image_privately_interactively(const char *path, DissectImageFlags flags, char **ret_directory, LoopDevice **ret_loop_device, DecryptedImage **ret_decrypted_image);