From: Alberto Planas Date: Tue, 2 Apr 2024 14:18:30 +0000 (+0200) Subject: pcrlock: add make_pcrlock_record_from_stream X-Git-Tag: v256-rc1~103^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bb6fe2afbc5ff163a6b8189cb3806ef2d2de881a;p=thirdparty%2Fsystemd.git pcrlock: add make_pcrlock_record_from_stream To hash long files (like initrd) add the funcion make_pcrlock_record_from_stream, that will read a long file (or stdin) to generate the digests of multiple hashes, redading block by block. Use this new function in verb_lock_raw and verb_lock_kernel_initrd. Signed-off-by: Alberto Planas --- diff --git a/src/pcrlock/pcrlock.c b/src/pcrlock/pcrlock.c index 2b2720978a7..5045ad93e88 100644 --- a/src/pcrlock/pcrlock.c +++ b/src/pcrlock/pcrlock.c @@ -2679,7 +2679,7 @@ static int make_pcrlock_record( assert_se(a = tpm2_hash_alg_to_string(*pa)); assert_se(md = EVP_get_digestbyname(a)); hash_ssize = EVP_MD_size(md); - assert_se(hash_ssize > 0); + assert(hash_ssize > 0); hash_usize = hash_ssize; hash = malloc(hash_usize); @@ -2708,6 +2708,101 @@ static int make_pcrlock_record( return 0; } +static void evp_md_ctx_free_all(EVP_MD_CTX *(*md)[TPM2_N_HASH_ALGORITHMS]) { + assert(md); + FOREACH_ARRAY(alg, *md, TPM2_N_HASH_ALGORITHMS) + if (*alg) + EVP_MD_CTX_free(*alg); +} + +static int make_pcrlock_record_from_stream( + uint32_t pcr_mask, + FILE *f, + JsonVariant **ret_records) { + + _cleanup_(json_variant_unrefp) JsonVariant *digests = NULL; + _cleanup_(evp_md_ctx_free_all) EVP_MD_CTX *mdctx[TPM2_N_HASH_ALGORITHMS] = {}; + int r; + + assert(f); + assert(ret_records); + + for (size_t i = 0; i < TPM2_N_HASH_ALGORITHMS; i++) { + const char *a; + const EVP_MD *md; + + assert_se(a = tpm2_hash_alg_to_string(tpm2_hash_algorithms[i])); + assert_se(md = EVP_get_digestbyname(a)); + + mdctx[i] = EVP_MD_CTX_new(); + if (!mdctx[i]) + return log_oom(); + + if (EVP_DigestInit_ex(mdctx[i], md, NULL) != 1) + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Failed to initialize message digest for %s.", a); + } + + for (;;) { + uint8_t buffer[64*1024]; + size_t n; + + n = fread(buffer, 1, sizeof(buffer), f); + if (ferror(f)) + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to read file: %m"); + if (n == 0 && feof(f)) + break; + + for (size_t i = 0; i < TPM2_N_HASH_ALGORITHMS; i++) + if (EVP_DigestUpdate(mdctx[i], buffer, n) != 1) + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Unable to hash data."); + } + + for (size_t i = 0; i < TPM2_N_HASH_ALGORITHMS; i++) { + const char *a; + int hash_ssize; + unsigned hash_usize; + + assert_se(a = tpm2_hash_alg_to_string(tpm2_hash_algorithms[i])); + hash_ssize = EVP_MD_CTX_size(mdctx[i]); + assert(hash_ssize > 0 && hash_ssize <= EVP_MAX_MD_SIZE); + hash_usize = hash_ssize; + unsigned char hash[hash_usize]; + + if (EVP_DigestFinal_ex(mdctx[i], hash, &hash_usize) != 1) + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Failed to finalize hash context for algorithn '%s'.", a); + + r = json_variant_append_arrayb( + &digests, + JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("hashAlg", JSON_BUILD_STRING(a)), + JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(hash, hash_usize)))); + if (r < 0) + return log_error_errno(r, "Failed to build JSON digest object: %m"); + } + + for (uint32_t i = 0; i < TPM2_PCRS_MAX; i++) { + _cleanup_(json_variant_unrefp) JsonVariant *record = NULL; + + if (!FLAGS_SET(pcr_mask, UINT32_C(1) << i)) + continue; + + r = json_build(&record, + JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("pcr", JSON_BUILD_UNSIGNED(i)), + JSON_BUILD_PAIR("digests", JSON_BUILD_VARIANT(digests)))); + if (r < 0) + return log_error_errno(r, "Failed to build record object: %m"); + + r = json_variant_append_array(ret_records, record); + if (r < 0) + return log_error_errno(r, "Failed to append to JSON array: %m"); + } + + return 0; +} + static const char *pcrlock_path(const char *default_pcrlock_path) { return arg_pcrlock_path ?: arg_pcrlock_auto ? default_pcrlock_path : NULL; } @@ -2771,10 +2866,8 @@ static int unlink_pcrlock(const char *default_pcrlock_path) { } static int verb_lock_raw(int argc, char *argv[], void *userdata) { - _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; - _cleanup_free_ char *data = NULL; + _cleanup_(json_variant_unrefp) JsonVariant *records = NULL; _cleanup_fclose_ FILE *f = NULL; - size_t size; int r; if (arg_pcr_mask == 0) @@ -2786,26 +2879,11 @@ static int verb_lock_raw(int argc, char *argv[], void *userdata) { return log_error_errno(errno, "Failed to open '%s': %m", argv[1]); } - r = read_full_stream(f ?: stdin, &data, &size); + r = make_pcrlock_record_from_stream(arg_pcr_mask, f ?: stdin, &records); if (r < 0) - return log_error_errno(r, "Failed to read data from stdin: %m"); - - for (uint32_t i = 0; i < TPM2_PCRS_MAX; i++) { - _cleanup_(json_variant_unrefp) JsonVariant *record = NULL; - - if (!FLAGS_SET(arg_pcr_mask, UINT32_C(1) << i)) - continue; - - r = make_pcrlock_record(i, data, size, &record); - if (r < 0) - return r; - - r = json_variant_append_array(&array, record); - if (r < 0) - return log_error_errno(r, "Failed to append to JSON array: %m"); - } + return r; - return write_pcrlock(array, NULL); + return write_pcrlock(records, NULL); } static int verb_unlock_simple(int argc, char *argv[], void *userdata) { @@ -3813,10 +3891,9 @@ static int verb_unlock_kernel_cmdline(int argc, char *argv[], void *userdata) { } static int verb_lock_kernel_initrd(int argc, char *argv[], void *userdata) { - _cleanup_(json_variant_unrefp) JsonVariant *record = NULL, *array = NULL; - _cleanup_free_ void *data = NULL; + _cleanup_(json_variant_unrefp) JsonVariant *records = NULL; _cleanup_fclose_ FILE *f = NULL; - size_t size; + uint32_t pcr_mask = UINT32_C(1) << TPM2_PCR_KERNEL_INITRD; int r; if (argc >= 2) { @@ -3825,19 +3902,11 @@ static int verb_lock_kernel_initrd(int argc, char *argv[], void *userdata) { return log_error_errno(errno, "Failed to open '%s': %m", argv[1]); } - r = read_full_stream(f ?: stdin, (char**) &data, &size); - if (r < 0) - return log_error_errno(r, "Failed to read data from stdin: %m"); - - r = make_pcrlock_record(TPM2_PCR_KERNEL_INITRD /* = 9 */, data, size, &record); + r = make_pcrlock_record_from_stream(pcr_mask, f ?: stdin, &records); if (r < 0) return r; - r = json_variant_new_array(&array, &record, 1); - if (r < 0) - return log_error_errno(r, "Failed to create record array: %m"); - - r = write_pcrlock(array, PCRLOCK_KERNEL_INITRD_PATH); + r = write_pcrlock(records, PCRLOCK_KERNEL_INITRD_PATH); if (r < 0) return r;