_cleanup_free_ char *h = NULL;
uint32_t nv_index = 0;
+ uint64_t priority = 0;
if (c) {
if (!*c) {
r = tpm2_context_new_or_warn(/* device= */ NULL, c);
}
_cleanup_(iovec_done) struct iovec digest = {};
- r = tpm2_nvpcr_read(*c, /* session= */ NULL, name, &digest, &nv_index);
+ r = tpm2_nvpcr_read(*c, /* session= */ NULL, name, &digest, &nv_index, &priority);
if (r < 0)
return log_error_errno(r, "Failed to read NvPCR '%s': %m", name);
if (r > 0) { /* set? */
return log_oom();
}
} else {
- r = tpm2_nvpcr_get_index(name, &nv_index);
+ r = tpm2_nvpcr_get_index(name, &nv_index, &priority);
if (r < 0)
return log_error_errno(r, "Failed to get NV index of NvPCR '%s': %m", name);
}
char *name;
uint16_t algorithm;
uint32_t nv_index;
+ uint64_t priority;
} NvPCRData;
static void nvpcr_data_done(NvPCRData *d) {
{ "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(NvPCRData, name), SD_JSON_MANDATORY },
{ "algorithm", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_tpm2_algorithm, offsetof(NvPCRData, algorithm), 0 },
{ "nvIndex", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32, offsetof(NvPCRData, nv_index), SD_JSON_MANDATORY },
+ { "priority", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(NvPCRData, priority), 0 },
{},
};
_cleanup_(nvpcr_data_done) NvPCRData p = {
.algorithm = TPM2_ALG_SHA256,
+ .priority = TPM2_NVPCR_PRIORITY_DEFAULT,
};
r = sd_json_dispatch(v, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
if (r < 0)
return 0;
}
-int tpm2_nvpcr_get_index(const char *name, uint32_t *ret) {
+int tpm2_nvpcr_get_index(const char *name, uint32_t *ret_nv_index, uint64_t *ret_priority) {
int r;
_cleanup_(nvpcr_data_done) NvPCRData p = {};
if (r < 0)
return r;
- if (ret)
- *ret = p.nv_index;
+ if (ret_nv_index)
+ *ret_nv_index = p.nv_index;
+ if (ret_priority)
+ *ret_priority = p.priority;
return 0;
}
const Tpm2Handle *session,
const char *name,
struct iovec *ret_value,
- uint32_t *ret_nv_index) {
+ uint32_t *ret_nv_index,
+ uint64_t *ret_priority) {
#if HAVE_OPENSSL
int r;
*ret_value = (struct iovec) {};
if (ret_nv_index)
*ret_nv_index = p.nv_index;
+ if (ret_priority)
+ *ret_priority = p.priority;
return 0;
}
if (ret_nv_index)
*ret_nv_index = p.nv_index;
+ if (ret_priority)
+ *ret_priority = p.priority;
return r;
#else /* HAVE_OPENSSL */
DECLARE_STRING_TABLE_LOOKUP(tpm2_userspace_event_type, Tpm2UserspaceEventType);
int tpm2_pcr_extend_bytes(Tpm2Context *c, char **banks, unsigned pcr_index, const struct iovec *data, const struct iovec *secret, Tpm2UserspaceEventType event_type, const char *description);
-int tpm2_nvpcr_get_index(const char *name, uint32_t *ret);
+
+/* Default allocation priority for NvPCRs that do not specify one explicitly. Lower values are more
+ * important and are allocated first when the TPM's NV index space is constrained. */
+#define TPM2_NVPCR_PRIORITY_DEFAULT UINT64_C(1000)
+
+int tpm2_nvpcr_get_index(const char *name, uint32_t *ret_nv_index, uint64_t *ret_priority);
int tpm2_nvpcr_extend_bytes(Tpm2Context *c, const Tpm2Handle *session, const char *name, const struct iovec *data, const struct iovec *secret, Tpm2UserspaceEventType event_type, const char *description);
int tpm2_nvpcr_acquire_anchor_secret(struct iovec *ret, bool sync_secondary);
int tpm2_nvpcr_initialize(Tpm2Context *c, const Tpm2Handle *session, const char *name, const struct iovec *anchor_secret);
-int tpm2_nvpcr_read(Tpm2Context *c, const Tpm2Handle *session, const char *name, struct iovec *ret, uint32_t *ret_nv_index);
+int tpm2_nvpcr_read(Tpm2Context *c, const Tpm2Handle *session, const char *name, struct iovec *ret, uint32_t *ret_nv_index, uint64_t *ret_priority);
uint32_t tpm2_tpms_pcr_selection_to_mask(const TPMS_PCR_SELECTION *s);
void tpm2_tpms_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash, TPMS_PCR_SELECTION *ret);
#include "parse-util.h"
#include "pretty-print.h"
#include "set.h"
+#include "sort-util.h"
#include "string-util.h"
#include "strv.h"
#include "tmpfile-util.h"
return 0;
}
+typedef struct NvPCREntry {
+ const char *name; /* points into the strv 'l' in setup_nvpcr() */
+ uint64_t priority;
+} NvPCREntry;
+
+static int nvpcr_entry_compare(const NvPCREntry *a, const NvPCREntry *b) {
+ int r;
+
+ assert(a);
+ assert(b);
+
+ /* Lower priority value means more important, hence allocate it first. Break ties by name
+ * (ascending) so the resulting order is fully deterministic. */
+ r = CMP(a->priority, b->priority);
+ if (r != 0)
+ return r;
+
+ return strcmp(a->name, b->name);
+}
+
static int setup_nvpcr(void) {
_cleanup_(setup_nvpcr_context_done) SetupNvPCRContext c = {};
int r;
if (r < 0)
return log_error_errno(r, "Failed to find .nvpcr files: %m");
+ /* Pair each NvPCR name with its allocation priority, then sort, so that we allocate the most
+ * important NvPCRs first. This matters when the TPM's NV index space is too small to fit all of
+ * them: we then degrade gracefully, skipping the least important NvPCRs. */
+ size_t n = strv_length(l);
+ _cleanup_free_ NvPCREntry *entries = new(NvPCREntry, n);
+ if (!entries)
+ return log_oom();
+
+ for (size_t i = 0; i < n; i++) {
+ uint64_t priority = TPM2_NVPCR_PRIORITY_DEFAULT;
+
+ r = tpm2_nvpcr_get_index(l[i], /* ret_nv_index= */ NULL, &priority);
+ if (r < 0)
+ /* If we can't read the definition, assume the default priority and let the
+ * per-NvPCR error path in setup_nvpcr_one() report the actual problem. */
+ log_warning_errno(r, "Failed to read priority of NvPCR '%s', assuming default priority %" PRIu64 ": %m", l[i], priority);
+
+ entries[i] = (NvPCREntry) {
+ .name = l[i],
+ .priority = priority,
+ };
+ }
+
+ typesafe_qsort(entries, n, nvpcr_entry_compare);
+
int ret = 0;
- STRV_FOREACH(i, l) {
+ FOREACH_ARRAY(e, entries, n) {
if (!c.tpm2_context) {
/* Inability to contact the TPM shall be fatal for us */
r = tpm2_context_new_or_warn(arg_tpm2_device, &c.tpm2_context);
return r;
}
+ log_debug("Setting up NvPCR '%s' (priority %" PRIu64 ").", e->name, e->priority);
+
/* But if we fail to initialize some NvPCR, we go on */
- RET_GATHER(ret, setup_nvpcr_one(&c, *i));
+ RET_GATHER(ret, setup_nvpcr_one(&c, e->name));
}
if (c.n_already > 0 && c.n_anchored == 0 && !arg_early)