diff --git a/docs/DISCOVERABLE_PARTITIONS.md b/docs/DISCOVERABLE_PARTITIONS.md index 936eb14065..efd20533a5 100644 --- a/docs/DISCOVERABLE_PARTITIONS.md +++ b/docs/DISCOVERABLE_PARTITIONS.md @@ -280,8 +280,8 @@ size. Currently three fields are defined for the JSON object: in `rootHash` above. 3. The (optional) `certificateFingerprint` field should be a string containing - a SHA256 fingerprint of the X.509 certificate for the key that signed the - root hash, formatted as series of (lowercase) hex characters (no `:` + a SHA256 fingerprint of the X.509 certificate in DER format for the key that + signed the root hash, formatted as series of (lowercase) hex characters (no `:` separators or such). More fields might be added in later revisions of this specification. diff --git a/man/repart.d.xml b/man/repart.d.xml index 280ce6b8ea..eec48fb569 100644 --- a/man/repart.d.xml +++ b/man/repart.d.xml @@ -583,17 +583,21 @@ Verity= - Takes one of off, data or - hash. Defaults to off. If set to off or - data, the partition is populated with content as specified by - CopyBlocks= or CopyFiles=. If set to hash, - the partition will be populated with verity hashes from a matching verity data partition. A matching - data partition is a partition with Verity= set to data and the - same verity match key (as configured with VerityMatchKey=). If not explicitly - configured, the data partition's UUID will be set to the first 128 bits of the verity root hash. - Similarly, if not configured, the hash partition's UUID will be set to the final 128 bits of the - verity root hash. The verity root hash itself will be included in the output of - systemd-repart. + Takes one of off, data, + hash or signature. Defaults to off. If set + to off or data, the partition is populated with content as + specified by CopyBlocks= or CopyFiles=. If set to + hash, the partition will be populated with verity hashes from the matching verity + data partition. If set to signature, The partition will be populated with a JSON + object containing a signature of the verity root hash of the matching verity hash partition. + + A matching verity partition is a partition with the same verity match key (as configured with + VerityMatchKey=). + + If not explicitly configured, the data partition's UUID will be set to the first 128 + bits of the verity root hash. Similarly, if not configured, the hash partition's UUID will be set to + the final 128 bits of the verity root hash. The verity root hash itself will be included in the + output of systemd-repart. This option has no effect if the partition already exists. diff --git a/meson.build b/meson.build index 6022617832..a20e94c55a 100644 --- a/meson.build +++ b/meson.build @@ -3682,7 +3682,8 @@ if conf.get('ENABLE_REPART') == 1 link_with : [libshared], dependencies : [threads, libblkid, - libfdisk], + libfdisk, + libopenssl], install_rpath : rootpkglibdir, install : true, install_dir : rootbindir) diff --git a/src/basic/string-util.c b/src/basic/string-util.c index 128aea99c0..17d35fe1a4 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -521,6 +521,19 @@ char* strshorten(char *s, size_t l) { return s; } +int strgrowpad0(char **s, size_t l) { + assert(s); + + char *q = realloc(*s, l); + if (!q) + return -ENOMEM; + *s = q; + + size_t sz = strlen(*s); + memzero(*s + sz, l - sz); + return 0; +} + char *strreplace(const char *text, const char *old_string, const char *new_string) { size_t l, old_len, new_len; char *t, *ret = NULL; diff --git a/src/basic/string-util.h b/src/basic/string-util.h index 1dd46f7f20..0703c848f0 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -152,6 +152,8 @@ char *cellescape(char *buf, size_t len, const char *s); char* strshorten(char *s, size_t l); +int strgrowpad0(char **s, size_t l); + char *strreplace(const char *text, const char *old_string, const char *new_string); char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]); diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index 4d49bad458..8cdf312f58 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -948,7 +948,7 @@ static int run(int argc, char *argv[]) { d->fd, &arg_verity_settings); if (r < 0) - return r; + return log_error_errno(r, "Failed to load verity signature partition: %m"); switch (arg_action) { diff --git a/src/partition/repart.c b/src/partition/repart.c index 325cdf0b99..0bd19523ec 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -40,6 +40,7 @@ #include "hexdecoct.h" #include "hmac.h" #include "id128-util.h" +#include "io-util.h" #include "json.h" #include "list.h" #include "loop-util.h" @@ -48,6 +49,7 @@ #include "mkfs-util.h" #include "mount-util.h" #include "mountpoint-util.h" +#include "openssl-util.h" #include "parse-argument.h" #include "parse-helpers.h" #include "pretty-print.h" @@ -76,6 +78,9 @@ /* Hard lower limit for new partition sizes */ #define HARD_MIN_SIZE 4096 +/* We know up front we're never going to put more than this in a verity sig partition. */ +#define VERITY_SIG_SIZE (HARD_MIN_SIZE * 4) + /* libfdisk takes off slightly more than 1M of the disk size when creating a GPT disk label */ #define GPT_METADATA_SIZE (1044*1024) @@ -113,6 +118,8 @@ static PagerFlags arg_pager_flags = 0; static bool arg_legend = true; static void *arg_key = NULL; static size_t arg_key_size = 0; +static EVP_PKEY *arg_private_key = NULL; +static X509 *arg_certificate = NULL; static char *arg_tpm2_device = NULL; static uint32_t arg_tpm2_pcr_mask = UINT32_MAX; static char *arg_tpm2_public_key = NULL; @@ -123,6 +130,8 @@ STATIC_DESTRUCTOR_REGISTER(arg_root, freep); STATIC_DESTRUCTOR_REGISTER(arg_image, freep); STATIC_DESTRUCTOR_REGISTER(arg_definitions, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_key, erase_and_freep); +STATIC_DESTRUCTOR_REGISTER(arg_private_key, EVP_PKEY_freep); +STATIC_DESTRUCTOR_REGISTER(arg_certificate, X509_freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep); @@ -143,6 +152,7 @@ typedef enum VerityMode { VERITY_OFF, VERITY_DATA, VERITY_HASH, + VERITY_SIG, _VERITY_MODE_MAX, _VERITY_MODE_INVALID = -EINVAL, } VerityMode; @@ -240,6 +250,7 @@ static const char *verity_mode_table[_VERITY_MODE_MAX] = { [VERITY_OFF] = "off", [VERITY_DATA] = "data", [VERITY_HASH] = "hash", + [VERITY_SIG] = "signature", }; #if HAVE_LIBCRYPTSETUP @@ -515,6 +526,9 @@ static uint64_t partition_min_size(const Context *context, const Partition *p) { return p->current_size; } + if (p->verity == VERITY_SIG) + return VERITY_SIG_SIZE; + sz = p->current_size != UINT64_MAX ? p->current_size : HARD_MIN_SIZE; if (!PARTITION_EXISTS(p)) { @@ -556,6 +570,9 @@ static uint64_t partition_max_size(const Context *context, const Partition *p) { return p->current_size; } + if (p->verity == VERITY_SIG) + return VERITY_SIG_SIZE; + if (p->size_max == UINT64_MAX) return UINT64_MAX; @@ -1548,7 +1565,8 @@ static int partition_read_definition(Partition *p, const char *path, const char "VerityMatchKey= can only be set if Verity= is not \"%s\"", verity_mode_to_string(p->verity)); - if (p->verity == VERITY_HASH && (p->copy_files || p->copy_blocks_path || p->copy_blocks_auto || p->format || p->make_directories)) + if (IN_SET(p->verity, VERITY_HASH, VERITY_SIG) && + (p->copy_files || p->copy_blocks_path || p->copy_blocks_auto || p->format || p->make_directories)) return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), "CopyBlocks=/CopyFiles=/Format=/MakeDirectories= cannot be used with Verity=%s", verity_mode_to_string(p->verity)); @@ -1557,6 +1575,19 @@ static int partition_read_definition(Partition *p, const char *path, const char return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), "Encrypting verity hash/data partitions is not supported"); + if (p->verity == VERITY_SIG && !arg_private_key) + return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), + "Verity signature partition requested but no private key provided (--private-key=)"); + + if (p->verity == VERITY_SIG && !arg_certificate) + return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), + "Verity signature partition requested but no PEM certificate provided (--certificate-file=)"); + + if (p->verity == VERITY_SIG && (p->size_min != UINT64_MAX || p->size_max != UINT64_MAX)) + return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), + "SizeMinBytes=/SizeMaxBytes= cannot be used with Verity=%s", + verity_mode_to_string(p->verity)); + /* Verity partitions are read only, let's imply the RO flag hence, unless explicitly configured otherwise. */ if ((gpt_partition_type_is_root_verity(p->type_uuid) || gpt_partition_type_is_usr_verity(p->type_uuid)) && @@ -1665,7 +1696,7 @@ static int context_read_definitions( continue; for (VerityMode mode = VERITY_OFF + 1; mode < _VERITY_MODE_MAX; mode++) { - Partition *q; + Partition *q = NULL; if (p->verity == mode) continue; @@ -1674,7 +1705,7 @@ static int context_read_definitions( continue; r = find_verity_sibling(context, p, mode, &q); - if (r == -ENXIO) + if (mode != VERITY_SIG && r == -ENXIO) return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL), "Missing verity %s partition for verity %s partition with VerityMatchKey=%s", verity_mode_to_string(mode), verity_mode_to_string(p->verity), p->verity_match_key); @@ -1685,12 +1716,14 @@ static int context_read_definitions( if (r < 0) return r; - if (q->priority != p->priority) - return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL), - "Priority mismatch (%i != %i) for verity sibling partitions with VerityMatchKey=%s", - p->priority, q->priority, p->verity_match_key); + if (q) { + if (q->priority != p->priority) + return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL), + "Priority mismatch (%i != %i) for verity sibling partitions with VerityMatchKey=%s", + p->priority, q->priority, p->verity_match_key); - p->siblings[mode] = q; + p->siblings[mode] = q; + } } } @@ -3577,11 +3610,11 @@ static int do_verity_format( return 0; #else - return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "libcryptsetup is not supported, cannot setup verity: %m"); + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "libcryptsetup is not supported, cannot setup verity hashes: %m"); #endif } -static int context_verity(Context *context) { +static int context_verity_hash(Context *context) { int fd = -1, r; assert(context); @@ -3642,6 +3675,181 @@ static int context_verity(Context *context) { return 0; } +static int parse_x509_certificate(const char *certificate, size_t certificate_size, X509 **ret) { +#if HAVE_OPENSSL + _cleanup_(X509_freep) X509 *cert = NULL; + _cleanup_(BIO_freep) BIO *cb = NULL; + + assert(certificate); + assert(certificate_size > 0); + assert(ret); + + cb = BIO_new_mem_buf(certificate, certificate_size); + if (!cb) + return log_oom(); + + cert = PEM_read_bio_X509(cb, NULL, NULL, NULL); + if (!cert) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse X.509 certificate: %s", + ERR_error_string(ERR_get_error(), NULL)); + + if (ret) + *ret = TAKE_PTR(cert); + + return 0; +#else + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "openssl is not supported, cannot parse X509 certificate."); +#endif +} + +static int parse_private_key(const char *key, size_t key_size, EVP_PKEY **ret) { +#if HAVE_OPENSSL + _cleanup_(BIO_freep) BIO *kb = NULL; + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pk = NULL; + + assert(key); + assert(key_size > 0); + assert(ret); + + kb = BIO_new_mem_buf(key, key_size); + if (!kb) + return log_oom(); + + pk = PEM_read_bio_PrivateKey(kb, NULL, NULL, NULL); + if (!pk) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse PEM private key: %s", + ERR_error_string(ERR_get_error(), NULL)); + + if (ret) + *ret = TAKE_PTR(pk); + + return 0; +#else + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "openssl is not supported, cannot parse private key."); +#endif +} + +static int sign_verity_roothash( + const uint8_t *roothash, + size_t roothash_size, + uint8_t **ret_signature, + size_t *ret_signature_size) { + +#if HAVE_OPENSSL + _cleanup_(BIO_freep) BIO *rb = NULL; + _cleanup_(PKCS7_freep) PKCS7 *p7 = NULL; + _cleanup_free_ char *hex = NULL; + _cleanup_free_ uint8_t *sig = NULL; + int sigsz; + + assert(roothash); + assert(roothash_size > 0); + assert(ret_signature); + assert(ret_signature_size); + + hex = hexmem(roothash, roothash_size); + if (!hex) + return log_oom(); + + rb = BIO_new_mem_buf(hex, -1); + if (!rb) + return log_oom(); + + p7 = PKCS7_sign(arg_certificate, arg_private_key, NULL, rb, PKCS7_DETACHED|PKCS7_NOATTR|PKCS7_BINARY); + if (!p7) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to calculate PKCS7 signature: %s", + ERR_error_string(ERR_get_error(), NULL)); + + sigsz = i2d_PKCS7(p7, &sig); + if (sigsz < 0) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert PKCS7 signature to DER: %s", + ERR_error_string(ERR_get_error(), NULL)); + + *ret_signature = TAKE_PTR(sig); + *ret_signature_size = sigsz; + + return 0; +#else + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "openssl is not supported, cannot setup verity signature: %m"); +#endif +} + +static int context_verity_sig(Context *context) { + int fd = -1, r; + + assert(context); + + LIST_FOREACH(partitions, p, context->partitions) { + _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; + _cleanup_free_ uint8_t *sig = NULL; + _cleanup_free_ char *text = NULL; + Partition *hp; + uint8_t fp[X509_FINGERPRINT_SIZE]; + size_t sigsz, padsz; + + if (p->dropped) + continue; + + if (PARTITION_EXISTS(p)) + continue; + + if (p->verity != VERITY_SIG) + continue; + + assert_se(hp = p->siblings[VERITY_HASH]); + assert(!hp->dropped); + + assert(arg_certificate); + + if (fd < 0) + assert_se((fd = fdisk_get_devfd(context->fdisk_context)) >= 0); + + r = sign_verity_roothash(hp->roothash, hp->roothash_size, &sig, &sigsz); + if (r < 0) + return r; + + r = x509_fingerprint(arg_certificate, fp); + if (r < 0) + return log_error_errno(r, "Unable to calculate X509 certificate fingerprint: %m"); + + r = json_build(&v, + JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("rootHash", JSON_BUILD_HEX(hp->roothash, hp->roothash_size)), + JSON_BUILD_PAIR( + "certificateFingerprint", + JSON_BUILD_HEX(fp, sizeof(fp)) + ), + JSON_BUILD_PAIR("signature", JSON_BUILD_BASE64(sig, sigsz)) + ) + ); + if (r < 0) + return log_error_errno(r, "Failed to build JSON object: %m"); + + r = json_variant_format(v, 0, &text); + if (r < 0) + return log_error_errno(r, "Failed to format JSON object: %m"); + + padsz = round_up_size(strlen(text), 4096); + assert_se(padsz <= p->new_size); + + r = strgrowpad0(&text, padsz); + if (r < 0) + return log_error_errno(r, "Failed to pad string to %s", FORMAT_BYTES(padsz)); + + if (lseek(fd, p->offset, SEEK_SET) == (off_t) -1) + return log_error_errno(errno, "Failed to seek to partition offset: %m"); + + r = loop_write(fd, text, padsz, /*do_poll=*/ false); + if (r < 0) + return log_error_errno(r, "Failed to write verity signature to partition: %m"); + + if (fsync(fd) < 0) + return log_error_errno(errno, "Failed to synchronize verity signature JSON: %m"); + } + + return 0; +} + static int partition_acquire_uuid(Context *context, Partition *p, sd_id128_t *ret) { struct { sd_id128_t type_uuid; @@ -3787,7 +3995,7 @@ static int context_acquire_partition_uuids_and_labels(Context *context) { if (!sd_id128_is_null(p->current_uuid)) p->new_uuid = p->current_uuid; /* Never change initialized UUIDs */ - else if (!p->new_uuid_is_set && p->verity == VERITY_OFF) { + else if (!p->new_uuid_is_set && !IN_SET(p->verity, VERITY_DATA, VERITY_HASH)) { /* Not explicitly set by user! */ r = partition_acquire_uuid(context, p, &p->new_uuid); if (r < 0) @@ -4201,7 +4409,11 @@ static int context_write_partition_table( if (r < 0) return r; - r = context_verity(context); + r = context_verity_hash(context); + if (r < 0) + return r; + + r = context_verity_sig(context); if (r < 0) return r; @@ -4747,6 +4959,10 @@ static int help(void) { " --image=PATH Operate relative to image file\n" " --definitions=DIR Find partition definitions in specified directory\n" " --key-file=PATH Key to use when encrypting partitions\n" + " --private-key=PATH Private key to use when generating verity roothash\n" + " signatures\n" + " --certificate=PATH PEM certificate to use when generating verity\n" + " roothash signatures\n" " --tpm2-device=PATH Path to TPM2 device node to use\n" " --tpm2-pcrs=PCR1+PCR2+PCR3+…\n" " TPM2 PCR indexes to use for TPM2 enrollment\n" @@ -4787,6 +5003,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_SIZE, ARG_JSON, ARG_KEY_FILE, + ARG_PRIVATE_KEY, + ARG_CERTIFICATE, ARG_TPM2_DEVICE, ARG_TPM2_PCRS, ARG_TPM2_PUBLIC_KEY, @@ -4812,6 +5030,8 @@ static int parse_argv(int argc, char *argv[]) { { "size", required_argument, NULL, ARG_SIZE }, { "json", required_argument, NULL, ARG_JSON }, { "key-file", required_argument, NULL, ARG_KEY_FILE }, + { "private-key", required_argument, NULL, ARG_PRIVATE_KEY }, + { "certificate", required_argument, NULL, ARG_CERTIFICATE }, { "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE }, { "tpm2-pcrs", required_argument, NULL, ARG_TPM2_PCRS }, { "tpm2-public-key", required_argument, NULL, ARG_TPM2_PUBLIC_KEY }, @@ -4985,6 +5205,46 @@ static int parse_argv(int argc, char *argv[]) { break; } + case ARG_PRIVATE_KEY: { + _cleanup_(erase_and_freep) char *k = NULL; + size_t n = 0; + + r = read_full_file_full( + AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX, + READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, + NULL, + &k, &n); + if (r < 0) + return log_error_errno(r, "Failed to read key file '%s': %m", optarg); + + EVP_PKEY_free(arg_private_key); + arg_private_key = NULL; + r = parse_private_key(k, n, &arg_private_key); + if (r < 0) + return r; + break; + } + + case ARG_CERTIFICATE: { + _cleanup_free_ char *cert = NULL; + size_t n = 0; + + r = read_full_file_full( + AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX, + READ_FULL_FILE_CONNECT_SOCKET, + NULL, + &cert, &n); + if (r < 0) + return log_error_errno(r, "Failed to read certificate file '%s': %m", optarg); + + X509_free(arg_certificate); + arg_certificate = NULL; + r = parse_x509_certificate(cert, n, &arg_certificate); + if (r < 0) + return r; + break; + } + case ARG_TPM2_DEVICE: { _cleanup_free_ char *device = NULL; diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index a7eec9fa11..2ea053e009 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -603,13 +603,10 @@ int dissect_image( 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; assert_se((architecture = gpt_partition_type_uuid_to_arch(type_id)) >= 0); designator = PARTITION_VERITY_SIG_OF(PARTITION_ROOT_OF_ARCH(architecture)); @@ -667,13 +664,10 @@ int dissect_image( 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; assert_se((architecture = gpt_partition_type_uuid_to_arch(type_id)) >= 0); designator = PARTITION_VERITY_SIG_OF(PARTITION_USR_OF_ARCH(architecture)); @@ -1074,6 +1068,9 @@ int dissect_image( if (verity->designator >= 0 && !m->partitions[verity->designator].found) return -EADDRNOTAVAIL; + bool have_verity_sig_partition = + m->partitions[verity->designator == PARTITION_USR ? PARTITION_USR_VERITY_SIG : PARTITION_ROOT_VERITY_SIG].found; + 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 */ @@ -1097,9 +1094,9 @@ int dissect_image( } if (m->verity_ready) - m->verity_sig_ready = verity->root_hash_sig; + m->verity_sig_ready = verity->root_hash_sig || have_verity_sig_partition; - } else if (m->partitions[verity->designator == PARTITION_USR ? PARTITION_USR_VERITY_SIG : PARTITION_ROOT_VERITY_SIG].found) { + } else if (have_verity_sig_partition) { /* If we found an embedded signature partition, we are ready, too. */ diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c index e4ee09102c..c7fcbd9ea4 100644 --- a/src/shared/openssl-util.c +++ b/src/shared/openssl-util.c @@ -195,3 +195,22 @@ int string_hashsum( } # endif #endif + +int x509_fingerprint(X509 *cert, uint8_t buffer[static SHA256_DIGEST_SIZE]) { +#if HAVE_OPENSSL + _cleanup_free_ uint8_t *der = NULL; + int dersz; + + assert(cert); + + dersz = i2d_X509(cert, &der); + if (dersz < 0) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unable to convert PEM certificate to DER format: %s", + ERR_error_string(ERR_get_error(), NULL)); + + sha256_direct(der, dersz, buffer); + return 0; +#else + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "openssl is not supported, cannot calculate X509 fingerprint: %m"); +#endif +} diff --git a/src/shared/openssl-util.h b/src/shared/openssl-util.h index 3dda19582b..4fa0a95966 100644 --- a/src/shared/openssl-util.h +++ b/src/shared/openssl-util.h @@ -2,6 +2,9 @@ #pragma once #include "macro.h" +#include "sha256.h" + +#define X509_FINGERPRINT_SIZE SHA256_DIGEST_SIZE #if HAVE_OPENSSL # include @@ -20,10 +23,8 @@ # include # endif -DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(X509*, X509_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(X509_NAME*, X509_NAME_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_PKEY_CTX*, EVP_PKEY_CTX_free, NULL); -DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_PKEY*, EVP_PKEY_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_CIPHER_CTX*, EVP_CIPHER_CTX_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EC_POINT*, EC_POINT_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EC_GROUP*, EC_GROUP_free, NULL); @@ -50,8 +51,28 @@ int rsa_pkey_to_suitable_key_size(EVP_PKEY *pkey, size_t *ret_suitable_key_size) int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size); +#else + +typedef struct X509 X509; +typedef struct EVP_PKEY EVP_PKEY; + +static inline void *X509_free(X509 *p) { + assert(p == NULL); + return NULL; +} + +static inline void *EVP_PKEY_free(EVP_PKEY *p) { + assert(p == NULL); + return NULL; +} + #endif +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(X509*, X509_free, NULL); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_PKEY*, EVP_PKEY_free, NULL); + +int x509_fingerprint(X509 *cert, uint8_t buffer[static X509_FINGERPRINT_SIZE]); + #if PREFER_OPENSSL /* The openssl definition */ typedef const EVP_MD* hash_md_t; diff --git a/test/TEST-58-REPART/test.sh b/test/TEST-58-REPART/test.sh index c2920f9c22..31c5e67e6a 100755 --- a/test/TEST-58-REPART/test.sh +++ b/test/TEST-58-REPART/test.sh @@ -10,6 +10,9 @@ TEST_DESCRIPTION="test systemd-repart" test_append_files() { if ! get_bool "${TEST_NO_QEMU:=}"; then install_dmevent + if command -v openssl >/dev/null 2>&1; then + inst_binary openssl + fi instmods dm_verity =md generate_module_dependencies fi diff --git a/test/test-functions b/test/test-functions index fa3ad9fbdc..d3ebfe0f51 100644 --- a/test/test-functions +++ b/test/test-functions @@ -1267,6 +1267,15 @@ install_missing_libraries() { continue fi done + + # Install extra openssl 3 stuff + path="$(pkg-config --variable=libdir libcrypto)" + inst_simple "${path}/ossl-modules/legacy.so" || true + inst_simple "${path}/ossl-modules/fips.so" || true + inst_simple "${path}/engines-3/afalg.so" || true + inst_simple "${path}/engines-3/capi.so" || true + inst_simple "${path}/engines-3/loader_attic.so" || true + inst_simple "${path}/engines-3/padlock.so" || true } cleanup_loopdev() { diff --git a/test/units/testsuite-50.sh b/test/units/testsuite-50.sh index f39383abc5..3ab020d687 100755 --- a/test/units/testsuite-50.sh +++ b/test/units/testsuite-50.sh @@ -214,8 +214,11 @@ losetup -d "${loop}" ROOT_UUID="$(systemd-id128 -u show "$(head -c 32 "${image}.roothash")" -u | tail -n 1 | cut -b 6-)" VERITY_UUID="$(systemd-id128 -u show "$(tail -c 32 "${image}.roothash")" -u | tail -n 1 | cut -b 6-)" -systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q '{"rw":"ro","designator":"root","partition_uuid":"'"$ROOT_UUID"'","partition_label":"Root Partition","fstype":"squashfs","architecture":"'"$architecture"'","verity":"yes",' +systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q '{"rw":"ro","designator":"root","partition_uuid":"'"$ROOT_UUID"'","partition_label":"Root Partition","fstype":"squashfs","architecture":"'"$architecture"'","verity":"signed",' systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q '{"rw":"ro","designator":"root-verity","partition_uuid":"'"$VERITY_UUID"'","partition_label":"Verity Partition","fstype":"DM_verity_hash","architecture":"'"$architecture"'","verity":null,' +if [ "${HAVE_OPENSSL}" -eq 1 ]; then + systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q -E '{"rw":"ro","designator":"root-verity-sig","partition_uuid":"'".*"'","partition_label":"Signature Partition","fstype":"verity_hash_signature","architecture":"'"$architecture"'","verity":null,' +fi systemd-dissect --root-hash "${roothash}" "${image}.gpt" | grep -q -F "MARKER=1" systemd-dissect --root-hash "${roothash}" "${image}.gpt" | grep -q -F -f <(sed 's/"//g' "$os_release") diff --git a/test/units/testsuite-58.sh b/test/units/testsuite-58.sh index c40e410494..f41069ee04 100755 --- a/test/units/testsuite-58.sh +++ b/test/units/testsuite-58.sh @@ -711,7 +711,7 @@ test_verity() { # shellcheck disable=SC2064 trap "rm -rf '$defs' '$imgs'" RETURN - cat >"$defs/root.conf" <"$defs/verity-data.conf" <"$defs/verity.conf" <"$defs/verity-hash.conf" <"$defs/verity-sig.conf" <> "$defs/verity.openssl.cnf" <