]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
pcrlock: add make_pcrlock_record_from_stream 31889/head
authorAlberto Planas <aplanas@suse.com>
Tue, 2 Apr 2024 14:18:30 +0000 (16:18 +0200)
committerAlberto Planas <aplanas@suse.com>
Fri, 19 Apr 2024 14:43:00 +0000 (16:43 +0200)
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 <aplanas@suse.com>
src/pcrlock/pcrlock.c

index 2b2720978a7b65117ef63b68ca3a3c2ccf41c0e1..5045ad93e888b8233b584820a4e4c61dce89540b 100644 (file)
@@ -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;