]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/boot/measure.c
tree-wide: use memstream-util
[thirdparty/systemd.git] / src / boot / measure.c
index b0bcfe0f983f8b023d71db6dbca15a519751bee3..bd7cc783996d6aaa7c41d94f31e7f3fc12fe5aee 100644 (file)
@@ -11,6 +11,7 @@
 #include "hexdecoct.h"
 #include "json.h"
 #include "main-func.h"
+#include "memstream-util.h"
 #include "openssl-util.h"
 #include "parse-argument.h"
 #include "parse-util.h"
@@ -83,6 +84,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "     --initrd=PATH       Path to initrd image file              %7$s .initrd\n"
                "     --splash=PATH       Path to splash bitmap file             %7$s .splash\n"
                "     --dtb=PATH          Path to Devicetree file                %7$s .dtb\n"
+               "     --uname=PATH        Path to 'uname -r' file                %7$s .uname\n"
                "     --pcrpkey=PATH      Path to public key for PCR signatures  %7$s .pcrpkey\n"
                "\nSee the %2$s for details.\n",
                program_invocation_short_name,
@@ -122,6 +124,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_INITRD,
                 ARG_SPLASH,
                 ARG_DTB,
+                ARG_UNAME,
                 _ARG_PCRSIG, /* the .pcrsig section is not input for signing, hence not actually an argument here */
                 _ARG_SECTION_LAST,
                 ARG_PCRPKEY = _ARG_SECTION_LAST,
@@ -144,6 +147,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "initrd",      required_argument, NULL, ARG_INITRD      },
                 { "splash",      required_argument, NULL, ARG_SPLASH      },
                 { "dtb",         required_argument, NULL, ARG_DTB         },
+                { "uname",       required_argument, NULL, ARG_UNAME       },
                 { "pcrpkey",     required_argument, NULL, ARG_PCRPKEY     },
                 { "current",     no_argument,       NULL, 'c'             },
                 { "bank",        required_argument, NULL, ARG_BANK        },
@@ -286,7 +290,8 @@ static int parse_argv(int argc, char *argv[]) {
         if (arg_current)
                 for (UnifiedSection us = 0; us < _UNIFIED_SECTION_MAX; us++)
                         if (arg_sections[us])
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The --current switch cannot be used in combination with --linux= and related switches.");
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "The --current switch cannot be used in combination with --linux= and related switches.");
 
         if (strv_isempty(arg_phase)) {
                 /* If no phases are specifically selected, pick everything from the beginning of the initrd
@@ -448,7 +453,8 @@ static int measure_kernel(PcrState *pcr_states, size_t n) {
                                 return log_oom();
 
                         if (EVP_DigestInit_ex(mdctx[i], pcr_states[i].md, NULL) != 1)
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize data %s context.", pcr_states[i].bank);
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "Failed to initialize data %s context.", pcr_states[i].bank);
                 }
 
                 for (;;) {
@@ -630,13 +636,14 @@ static void pcr_states_restore(PcrState *pcr_states, size_t n) {
 static int verb_calculate(int argc, char *argv[], void *userdata) {
         _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
         _cleanup_(pcr_state_free_all) PcrState *pcr_states = NULL;
-        size_t n;
         int r;
 
         if (!arg_sections[UNIFIED_SECTION_LINUX] && !arg_current)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Either --linux= or --current must be specified, refusing.");
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Either --linux= or --current must be specified, refusing.");
         if (arg_append)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The --append= switch is only supported for 'sign', not 'calculate'.");
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "The --append= switch is only supported for 'sign', not 'calculate'.");
 
         assert(!strv_isempty(arg_banks));
         assert(!strv_isempty(arg_phase));
@@ -645,7 +652,7 @@ static int verb_calculate(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
-        n = (size_t) r;
+        size_t n = r;
 
         r = measure_kernel(pcr_states, n);
         if (r < 0)
@@ -726,18 +733,17 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
         _cleanup_(pcr_state_free_all) PcrState *pcr_states = NULL;
         _cleanup_(EVP_PKEY_freep) EVP_PKEY *privkey = NULL, *pubkey = NULL;
-        _cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
         _cleanup_fclose_ FILE *privkeyf = NULL;
-        ESYS_TR session_handle = ESYS_TR_NONE;
-        TSS2_RC rc;
         size_t n;
         int r;
 
         if (!arg_sections[UNIFIED_SECTION_LINUX] && !arg_current)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Either --linux= or --current must be specified, refusing.");
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Either --linux= or --current must be specified, refusing.");
 
         if (!arg_private_key)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No private key specified, use --private-key=.");
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "No private key specified, use --private-key=.");
 
         assert(!strv_isempty(arg_banks));
         assert(!strv_isempty(arg_phase));
@@ -748,7 +754,8 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
                         return log_error_errno(r, "Failed to parse '%s': %m", arg_append);
 
                 if (!json_variant_is_object(v))
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File '%s' is not a valid JSON object, refusing.", arg_append);
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "File '%s' is not a valid JSON object, refusing.", arg_append);
         }
 
         /* When signing we only support JSON output */
@@ -773,24 +780,25 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
                 if (!pubkey)
                         return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse public key '%s'.", arg_public_key);
         } else {
-                _cleanup_free_ char *data = NULL;
-                _cleanup_fclose_ FILE *tf = NULL;
-                size_t sz;
+                _cleanup_(memstream_done) MemStream m = {};
+                FILE *tf;
 
                 /* No public key was specified, let's derive it automatically, if we can */
 
-                tf = open_memstream_unlocked(&data, &sz);
+                tf = memstream_init(&m);
                 if (!tf)
                         return log_oom();
 
                 if (i2d_PUBKEY_fp(tf, privkey) != 1)
-                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to extract public key from private key file '%s'.", arg_private_key);
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                               "Failed to extract public key from private key file '%s'.", arg_private_key);
 
                 fflush(tf);
                 rewind(tf);
 
                 if (!d2i_PUBKEY_fp(tf, &pubkey))
-                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse extracted public key of private key file '%s'.", arg_private_key);
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                               "Failed to parse extracted public key of private key file '%s'.", arg_private_key);
         }
 
         r = pcr_states_allocate(&pcr_states);
@@ -807,14 +815,6 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
-        r = dlopen_tpm2();
-        if (r < 0)
-                return r;
-
-        r = tpm2_context_init(arg_tpm2_device, &c);
-        if (r < 0)
-                return r;
-
         STRV_FOREACH(phase, arg_phase) {
 
                 r = measure_phase(pcr_states, n, *phase);
@@ -822,154 +822,88 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
                         return r;
 
                 for (size_t i = 0; i < n; i++) {
-                        static const TPMT_SYM_DEF symmetric = {
-                                .algorithm = TPM2_ALG_AES,
-                                .keyBits.aes = 128,
-                                .mode.aes = TPM2_ALG_CFB,
-                        };
                         PcrState *p = pcr_states + i;
 
-                        rc = sym_Esys_StartAuthSession(
-                                        c.esys_context,
-                                        ESYS_TR_NONE,
-                                        ESYS_TR_NONE,
-                                        ESYS_TR_NONE,
-                                        ESYS_TR_NONE,
-                                        ESYS_TR_NONE,
-                                        NULL,
-                                        TPM2_SE_TRIAL,
-                                        &symmetric,
-                                        TPM2_ALG_SHA256,
-                                        &session_handle);
-                        if (rc != TSS2_RC_SUCCESS) {
-                                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                                    "Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc));
-                                goto finish;
-                        }
-
-                        /* Generate a single hash value from the PCRs included in our policy. Given that that's
-                         * exactly one, the calculation is trivial. */
-                        TPM2B_DIGEST intermediate_digest = {
-                                .size = SHA256_DIGEST_SIZE,
-                        };
-                        assert(sizeof(intermediate_digest.buffer) >= SHA256_DIGEST_SIZE);
-                        sha256_direct(p->value, p->value_size, intermediate_digest.buffer);
-
                         int tpmalg = tpm2_hash_alg_from_string(EVP_MD_name(p->md));
-                        if (tpmalg < 0) {
-                                log_error_errno(tpmalg, "Unsupported PCR bank");
-                                goto finish;
-                        }
+                        if (tpmalg < 0)
+                                return log_error_errno(tpmalg, "Unsupported PCR bank");
 
                         TPML_PCR_SELECTION pcr_selection;
-                        tpm2_pcr_mask_to_selection(1 << TPM_PCR_INDEX_KERNEL_IMAGE, tpmalg, &pcr_selection);
-
-                        rc = sym_Esys_PolicyPCR(
-                                        c.esys_context,
-                                        session_handle,
-                                        ESYS_TR_NONE,
-                                        ESYS_TR_NONE,
-                                        ESYS_TR_NONE,
-                                        &intermediate_digest,
-                                        &pcr_selection);
-                        if (rc != TSS2_RC_SUCCESS) {
-                                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                                    "Failed to push PCR policy into TPM: %s", sym_Tss2_RC_Decode(rc));
-                                goto finish;
-                        }
+                        tpm2_tpml_pcr_selection_from_mask(1 << TPM_PCR_INDEX_KERNEL_IMAGE,
+                                                          tpmalg,
+                                                          &pcr_selection);
 
-                        _cleanup_(Esys_Freep) TPM2B_DIGEST *pcr_policy_digest = NULL;
-                        rc = sym_Esys_PolicyGetDigest(
-                                        c.esys_context,
-                                        session_handle,
-                                        ESYS_TR_NONE,
-                                        ESYS_TR_NONE,
-                                        ESYS_TR_NONE,
-                                        &pcr_policy_digest);
-                        if (rc != TSS2_RC_SUCCESS) {
-                                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                                    "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc));
-                                goto finish;
-                        }
+                        TPM2B_DIGEST pcr_values = {
+                                .size = p->value_size,
+                        };
+                        assert(sizeof(pcr_values.buffer) >= p->value_size);
+                        memcpy_safe(pcr_values.buffer, p->value, p->value_size);
+
+                        TPM2B_DIGEST pcr_policy_digest;
+                        r = tpm2_digest_init(TPM2_ALG_SHA256, &pcr_policy_digest);
+                        if (r < 0)
+                                return r;
 
-                        session_handle = tpm2_flush_context_verbose(c.esys_context, session_handle);
+                        r = tpm2_calculate_policy_pcr(&pcr_selection, &pcr_values, 1, &pcr_policy_digest);
+                        if (r < 0)
+                                return r;
 
                         _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX* mdctx = NULL;
                         mdctx = EVP_MD_CTX_new();
-                        if (!mdctx) {
-                                r = log_oom();
-                                goto finish;
-                        }
+                        if (!mdctx)
+                                return log_oom();
 
-                        if (EVP_DigestSignInit(mdctx, NULL, p->md, NULL, privkey) != 1) {
-                                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                                    "Failed to initialize signature context.");
-                                goto finish;
-                        }
+                        if (EVP_DigestSignInit(mdctx, NULL, p->md, NULL, privkey) != 1)
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                                       "Failed to initialize signature context.");
 
-                        if (EVP_DigestSignUpdate(mdctx, pcr_policy_digest->buffer, pcr_policy_digest->size) != 1) {
-                                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                                    "Failed to sign data.");
-                                goto finish;
-                        }
+                        if (EVP_DigestSignUpdate(mdctx, pcr_policy_digest.buffer, pcr_policy_digest.size) != 1)
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                                       "Failed to sign data.");
 
                         size_t ss;
-                        if (EVP_DigestSignFinal(mdctx, NULL, &ss) != 1) {
-                                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                                    "Failed to finalize signature");
-                                goto finish;
-                        }
+                        if (EVP_DigestSignFinal(mdctx, NULL, &ss) != 1)
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                                       "Failed to finalize signature");
 
                         _cleanup_free_ void *sig = malloc(ss);
-                        if (!sig) {
-                                r = log_oom();
-                                goto finish;
-                        }
+                        if (!sig)
+                                return log_oom();
 
-                        if (EVP_DigestSignFinal(mdctx, sig, &ss) != 1) {
-                                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                                    "Failed to acquire signature data");
-                                goto finish;
-                        }
+                        if (EVP_DigestSignFinal(mdctx, sig, &ss) != 1)
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                                       "Failed to acquire signature data");
 
                         _cleanup_free_ void *pubkey_fp = NULL;
                         size_t pubkey_fp_size = 0;
                         r = pubkey_fingerprint(pubkey, EVP_sha256(), &pubkey_fp, &pubkey_fp_size);
                         if (r < 0)
-                                goto finish;
+                                return r;
 
                         _cleanup_(json_variant_unrefp) JsonVariant *a = NULL;
                         r = tpm2_make_pcr_json_array(UINT64_C(1) << TPM_PCR_INDEX_KERNEL_IMAGE, &a);
-                        if (r < 0) {
-                                log_error_errno(r, "Failed to build JSON PCR mask array: %m");
-                                goto finish;
-                        }
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to build JSON PCR mask array: %m");
 
                         _cleanup_(json_variant_unrefp) JsonVariant *bv = NULL;
                         r = json_build(&bv, JSON_BUILD_OBJECT(
                                                        JSON_BUILD_PAIR("pcrs", JSON_BUILD_VARIANT(a)),                                             /* PCR mask */
                                                        JSON_BUILD_PAIR("pkfp", JSON_BUILD_HEX(pubkey_fp, pubkey_fp_size)),                         /* SHA256 fingerprint of public key (DER) used for the signature */
-                                                       JSON_BUILD_PAIR("pol", JSON_BUILD_HEX(pcr_policy_digest->buffer, pcr_policy_digest->size)), /* TPM2 policy hash that is signed */
+                                                       JSON_BUILD_PAIR("pol", JSON_BUILD_HEX(pcr_policy_digest.buffer, pcr_policy_digest.size)),   /* TPM2 policy hash that is signed */
                                                        JSON_BUILD_PAIR("sig", JSON_BUILD_BASE64(sig, ss))));                                       /* signature data */
-                        if (r < 0) {
-                                log_error_errno(r, "Failed to build JSON object: %m");
-                                goto finish;
-                        }
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to build JSON object: %m");
 
                         _cleanup_(json_variant_unrefp) JsonVariant *av = NULL;
                         av = json_variant_ref(json_variant_by_key(v, p->bank));
 
                         r = json_variant_append_array_nodup(&av, bv);
-                        if (r < 0) {
-                                log_error_errno(r, "Failed to append JSON object: %m");
-                                goto finish;
-                        }
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to append JSON object: %m");
 
                         r = json_variant_set_field(&v, p->bank, av);
-                        if (r < 0) {
-                                log_error_errno(r, "Failed to add JSON field: %m");
-                                goto finish;
-                        }
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to add JSON field: %m");
                 }
 
                 /* Return to the original kernel measurement for the next phase calculation */
@@ -980,11 +914,8 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
                 pager_open(arg_pager_flags);
 
         json_variant_dump(v, arg_json_format_flags, stdout, NULL);
-        r = 0;
 
-finish:
-        session_handle = tpm2_flush_context_verbose(c.esys_context, session_handle);
-        return r;
+        return 0;
 }
 
 static int compare_reported_pcr_nr(uint32_t pcr, const char *varname, const char *description) {