#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"
" --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,
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,
{ "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 },
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
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 (;;) {
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));
if (r < 0)
return r;
- n = (size_t) r;
+ size_t n = r;
r = measure_kernel(pcr_states, n);
if (r < 0)
_cleanup_(pcr_state_free_all) PcrState *pcr_states = NULL;
_cleanup_(EVP_PKEY_freep) EVP_PKEY *privkey = NULL, *pubkey = NULL;
_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));
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 */
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);
if (r < 0)
return r;
- r = dlopen_tpm2();
- if (r < 0)
- return r;
-
- _cleanup_tpm2_context_ Tpm2Context *c = NULL;
- r = tpm2_context_new(arg_tpm2_device, &c);
- if (r < 0)
- return r;
-
STRV_FOREACH(phase, arg_phase) {
r = measure_phase(pcr_states, n, *phase);
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 */
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) {