Merge pull request #31790 from poettering/pcrlock-policy-fix

Replace PolicyAuthValue by PolicySigned as access policy for pcrlock policy nvindex
This commit is contained in:
Lennart Poettering 2024-04-18 21:11:27 +02:00 committed by GitHub
commit dd37963aff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 329 additions and 71 deletions

36
NEWS
View file

@ -461,20 +461,8 @@ CHANGES WITH 256-rc1:
* confexts are loaded by systemd-stub from the ESP as well.
* The pcrlock policy is saved in an unencrypted credential file
"pcrlock.<entry-token>.cred" under XBOOTLDR/ESP in the
/loader/credentials/ directory. It will be picked up at boot by
systemd-stub and passed to the initrd, where it can be used to unlock
the root file system.
* kernel-install gained support for --root= for the 'list' verb.
* systemd-pcrlock gained an --entry-token= option to configure the
entry-token.
* systemd-pcrlock now provides a basic Varlink interface and can be run
as a daemon via a template unit.
* bootctl now provides a basic Varlink interface and can be run as a
daemon via a template unit.
@ -498,6 +486,30 @@ CHANGES WITH 256-rc1:
for enrolling "dbx" too (Previously, only db/KEK/PK enrollment was
supported). It also now supports UEFI "Custom" mode.
* The pcrlock policy is saved in an unencrypted credential file
"pcrlock.<entry-token>.cred" under XBOOTLDR/ESP in the
/loader/credentials/ directory. It will be picked up at boot by
systemd-stub and passed to the initrd, where it can be used to unlock
the root file system.
* systemd-pcrlock gained an --entry-token= option to configure the
entry-token.
* systemd-pcrlock now provides a basic Varlink interface and can be run
as a daemon via a template unit.
* systemd-pcrlock's TPM nvindex access policy has been modified, this
means that previous pcrlock policies stored in nvindexes are
invalidated. They must be removed (systemd-pcrlock remove-policy) and
recreated (systemd-pcrlock make-policy). For the time being
systemd-pcrlock remains an experimental feature, but it is expected
to become stable in the next release, i.e. v257.
* systemd-pcrlock's --recovery-pin= switch now takes three values:
"hide", "show", "query". If "show" is selected the automatically
generated recovery PIN is shown to the user. If "query" is selected
then the PIN is queried from the user.
systemd-run/run0:
* systemd-run is now a multi-call binary. When invoked as 'run0', it

View file

@ -504,13 +504,16 @@
<varlistentry>
<term><option>--recovery-pin=</option></term>
<listitem><para>Takes a boolean. Defaults to false. Honoured by <command>make-policy</command>. If
true, will query the user for a PIN to unlock the TPM2 NV index with. If no policy was created before
this PIN is used to protect the newly allocated NV index. If a policy has been created before the PIN
is used to unlock write access to the NV index. If this option is not used a PIN is automatically
generated. Regardless if user supplied or automatically generated, it is stored in encrypted form in
the policy metadata file. The recovery PIN may be used to regain write access to an NV index in case
the access policy became out of date.</para>
<listitem><para>Takes one of <literal>hide</literal>, <literal>show</literal> or
<literal>query</literal>. Defaults to <literal>hide</literal>. Honoured by
<command>make-policy</command>. If <literal>query</literal>, will query the user for a PIN to unlock
the TPM2 NV index with. If no policy was created before, this PIN is used to protect the newly
allocated NV index. If a policy has been created before, the PIN is used to unlock write access to
the NV index. If either <literal>hide</literal> or <literal>show</literal> is used, a PIN is
automatically generated, and — only in case of <literal>show</literal> — displayed on
screen. Regardless if user supplied or automatically generated, it is stored in encrypted form in the
policy metadata file. The recovery PIN may be used to regain write access to an NV index in case the
access policy became out of date.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>

View file

@ -43,6 +43,7 @@
#include "random-util.h"
#include "recovery-key.h"
#include "sort-util.h"
#include "string-table.h"
#include "terminal-util.h"
#include "tpm2-util.h"
#include "unaligned.h"
@ -52,6 +53,14 @@
#include "varlink-io.systemd.PCRLock.h"
#include "verbs.h"
typedef enum RecoveryPinMode {
RECOVERY_PIN_HIDE, /* generate a recovery PIN automatically, but don't show it (alias: "no") */
RECOVERY_PIN_SHOW, /* generate a recovery PIN automatically, and display it to the user */
RECOVERY_PIN_QUERY, /* asks the user for a PIN to use interactively (alias: "yes") */
_RECOVERY_PIN_MODE_MAX,
_RECOVERY_PIN_MODE_INVALID = -EINVAL,
} RecoveryPinMode;
static PagerFlags arg_pager_flags = 0;
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF|JSON_FORMAT_NEWLINE;
static char **arg_components = NULL;
@ -62,7 +71,7 @@ static bool arg_raw_description = false;
static char *arg_location_start = NULL;
static char *arg_location_end = NULL;
static TPM2_HANDLE arg_nv_index = 0;
static bool arg_recovery_pin = false;
static RecoveryPinMode arg_recovery_pin = RECOVERY_PIN_HIDE;
static char *arg_policy_path = NULL;
static bool arg_force = false;
static BootEntryTokenType arg_entry_token_type = BOOT_ENTRY_TOKEN_AUTO;
@ -104,6 +113,14 @@ STATIC_DESTRUCTOR_REGISTER(arg_entry_token, freep);
(UINT32_C(1) << TPM2_PCR_SHIM_POLICY) | \
(UINT32_C(1) << TPM2_PCR_SYSTEM_IDENTITY))
static const char* recovery_pin_mode_table[_RECOVERY_PIN_MODE_MAX] = {
[RECOVERY_PIN_HIDE] = "hide",
[RECOVERY_PIN_SHOW] = "show",
[RECOVERY_PIN_QUERY] = "query",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(recovery_pin_mode, RecoveryPinMode, RECOVERY_PIN_QUERY);
typedef struct EventLogRecordBank EventLogRecordBank;
typedef struct EventLogRecord EventLogRecord;
typedef struct EventLogRegisterBank EventLogRegisterBank;
@ -4320,7 +4337,7 @@ static int write_boot_policy_file(const char *json_text) {
return 1;
}
static int make_policy(bool force, bool recovery_pin) {
static int make_policy(bool force, RecoveryPinMode recovery_pin_mode) {
int r;
/* Here's how this all works: after predicting all possible PCR values for next boot (with
@ -4444,7 +4461,7 @@ static int make_policy(bool force, bool recovery_pin) {
/* Acquire a recovery PIN, either from the user, or create a randomized one */
_cleanup_(erase_and_freep) char *pin = NULL;
if (recovery_pin) {
if (recovery_pin_mode == RECOVERY_PIN_QUERY) {
r = getenv_steal_erase("PIN", &pin);
if (r < 0)
return log_error_errno(r, "Failed to acquire PIN from environment: %m");
@ -4473,16 +4490,16 @@ static int make_policy(bool force, bool recovery_pin) {
}
} else if (!have_old_policy) {
char rnd[256];
r = crypto_random_bytes(rnd, sizeof(rnd));
r = make_recovery_key(&pin);
if (r < 0)
return log_error_errno(r, "Failed to generate a randomized recovery PIN: %m");
(void) base64mem(rnd, sizeof(rnd), &pin);
explicit_bzero_safe(rnd, sizeof(rnd));
if (!pin)
return log_oom();
if (recovery_pin_mode == RECOVERY_PIN_SHOW)
printf("%s Selected recovery PIN is: %s%s%s\n",
special_glyph(SPECIAL_GLYPH_LOCK_AND_KEY),
ansi_highlight_cyan(),
pin,
ansi_normal());
}
_cleanup_(tpm2_handle_freep) Tpm2Handle *nv_handle = NULL;
@ -4500,7 +4517,7 @@ static int make_policy(bool force, bool recovery_pin) {
CLEANUP_ERASE(auth);
if (pin) {
r = tpm2_get_pin_auth(TPM2_ALG_SHA256, pin, &auth);
r = tpm2_auth_value_from_pin(TPM2_ALG_SHA256, pin, &auth);
if (r < 0)
return log_error_errno(r, "Failed to hash PIN: %m");
} else {
@ -4567,15 +4584,28 @@ static int make_policy(bool force, bool recovery_pin) {
log_info("Retrieved PIN from TPM2 in %s.", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC), pin_start_usec), 1));
}
TPM2B_NV_PUBLIC nv_public = {};
/* Now convert the PIN into an HMAC-SHA256 key that we can use in PolicySigned to protect access to the nvindex with */
_cleanup_(tpm2_handle_freep) Tpm2Handle *pin_handle = NULL;
r = tpm2_hmac_key_from_pin(tc, encryption_session, &auth, &pin_handle);
if (r < 0)
return r;
TPM2B_NV_PUBLIC nv_public = {};
usec_t nv_index_start_usec = now(CLOCK_MONOTONIC);
if (!iovec_is_set(&nv_blob)) {
TPM2B_DIGEST recovery_policy_digest = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
r = tpm2_calculate_policy_auth_value(&recovery_policy_digest);
_cleanup_(Esys_Freep) TPM2B_NAME *pin_name = NULL;
r = tpm2_get_name(
tc,
pin_handle,
&pin_name);
if (r < 0)
return log_error_errno(r, "Failed to calculate authentication value policy: %m");
return log_error_errno(r, "Failed to get name of PIN from TPM2: %m");
TPM2B_DIGEST recovery_policy_digest = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
r = tpm2_calculate_policy_signed(&recovery_policy_digest, pin_name);
if (r < 0)
return log_error_errno(r, "Failed to calculate PolicySigned policy: %m");
log_debug("Allocating NV index to write PCR policy to...");
r = tpm2_define_policy_nv_index(
@ -4583,8 +4613,6 @@ static int make_policy(bool force, bool recovery_pin) {
encryption_session,
arg_nv_index,
&recovery_policy_digest,
pin,
&auth,
&nv_index,
&nv_handle,
&nv_public);
@ -4594,10 +4622,6 @@ static int make_policy(bool force, bool recovery_pin) {
return log_error_errno(r, "Failed to allocate NV index: %m");
}
r = tpm2_set_auth_binary(tc, nv_handle, &auth);
if (r < 0)
return log_error_errno(r, "Failed to set authentication value on NV index: %m");
_cleanup_(tpm2_handle_freep) Tpm2Handle *policy_session = NULL;
r = tpm2_make_policy_session(
tc,
@ -4607,9 +4631,11 @@ static int make_policy(bool force, bool recovery_pin) {
if (r < 0)
return log_error_errno(r, "Failed to allocate policy session: %m");
r = tpm2_policy_auth_value(
r = tpm2_policy_signed_hmac_sha256(
tc,
policy_session,
pin_handle,
&IOVEC_MAKE(auth.buffer, auth.size),
/* ret_policy_digest= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to submit authentication value policy: %m");
@ -5044,9 +5070,9 @@ static int parse_argv(int argc, char *argv[]) {
}
case ARG_RECOVERY_PIN:
r = parse_boolean_argument("--recovery-pin", optarg, &arg_recovery_pin);
if (r < 0)
return r;
arg_recovery_pin = recovery_pin_mode_from_string(optarg);
if (arg_recovery_pin < 0)
return log_error_errno(arg_recovery_pin, "Failed to parse --recovery-pin= mode: %s", optarg);
break;
case ARG_PCRLOCK:
@ -5210,7 +5236,7 @@ static int vl_method_make_policy(Varlink *link, JsonVariant *parameters, Varlink
if (r != 0)
return r;
r = make_policy(p.force, /* recovery_key= */ false);
r = make_policy(p.force, /* recovery_key= */ RECOVERY_PIN_HIDE);
if (r < 0)
return r;
if (r == 0)

View file

@ -29,6 +29,7 @@
#include "recurse-dir.h"
#include "sha256.h"
#include "sort-util.h"
#include "sparse-endian.h"
#include "stat-util.h"
#include "string-table.h"
#include "sync-util.h"
@ -65,6 +66,7 @@ static DLSYM_FUNCTION(Esys_PolicyAuthorizeNV);
static DLSYM_FUNCTION(Esys_PolicyGetDigest);
static DLSYM_FUNCTION(Esys_PolicyOR);
static DLSYM_FUNCTION(Esys_PolicyPCR);
static DLSYM_FUNCTION(Esys_PolicySigned);
static DLSYM_FUNCTION(Esys_ReadPublic);
static DLSYM_FUNCTION(Esys_StartAuthSession);
static DLSYM_FUNCTION(Esys_Startup);
@ -77,6 +79,7 @@ static DLSYM_FUNCTION(Esys_TR_GetTpmHandle);
static DLSYM_FUNCTION(Esys_TR_Serialize);
static DLSYM_FUNCTION(Esys_TR_SetAuth);
static DLSYM_FUNCTION(Esys_TRSess_GetAttributes);
static DLSYM_FUNCTION(Esys_TRSess_GetNonceTPM);
static DLSYM_FUNCTION(Esys_TRSess_SetAttributes);
static DLSYM_FUNCTION(Esys_Unseal);
static DLSYM_FUNCTION(Esys_VerifySignature);
@ -132,6 +135,7 @@ int dlopen_tpm2(void) {
DLSYM_ARG(Esys_PolicyGetDigest),
DLSYM_ARG(Esys_PolicyOR),
DLSYM_ARG(Esys_PolicyPCR),
DLSYM_ARG(Esys_PolicySigned),
DLSYM_ARG(Esys_ReadPublic),
DLSYM_ARG(Esys_StartAuthSession),
DLSYM_ARG(Esys_Startup),
@ -143,6 +147,7 @@ int dlopen_tpm2(void) {
DLSYM_ARG(Esys_TR_Serialize),
DLSYM_ARG(Esys_TR_SetAuth),
DLSYM_ARG(Esys_TRSess_GetAttributes),
DLSYM_ARG(Esys_TRSess_GetNonceTPM),
DLSYM_ARG(Esys_TRSess_SetAttributes),
DLSYM_ARG(Esys_Unseal),
DLSYM_ARG(Esys_VerifySignature));
@ -2238,9 +2243,9 @@ static int tpm2_load_external(
#if HAVE_TSS2_ESYS3
/* tpm2-tss >= 3.0.0 requires a ESYS_TR_RH_* constant specifying the requested
* hierarchy, older versions need TPM2_RH_* instead. */
ESYS_TR_RH_OWNER,
private ? ESYS_TR_RH_NULL : ESYS_TR_RH_OWNER,
#else
TPM2_RH_OWNER,
private ? TPM2_RH_NULL : TPM2_RH_OWNER,
#endif
&handle->esys_handle);
if (rc != TSS2_RC_SUCCESS)
@ -3058,7 +3063,7 @@ static void tpm2_trim_auth_value(TPM2B_AUTH *auth) {
log_debug("authValue ends in 0, trimming as required by the TPM2 specification Part 1 section 'HMAC Computation' authValue Note 2.");
}
int tpm2_get_pin_auth(TPMI_ALG_HASH hash, const char *pin, TPM2B_AUTH *ret_auth) {
int tpm2_auth_value_from_pin(TPMI_ALG_HASH hash, const char *pin, TPM2B_AUTH *ret_auth) {
TPM2B_AUTH auth = {};
int r;
@ -3105,7 +3110,7 @@ int tpm2_set_auth(Tpm2Context *c, const Tpm2Handle *handle, const char *pin) {
CLEANUP_ERASE(auth);
r = tpm2_get_pin_auth(TPM2_ALG_SHA256, pin, &auth);
r = tpm2_auth_value_from_pin(TPM2_ALG_SHA256, pin, &auth);
if (r < 0)
return r;
@ -3392,7 +3397,7 @@ int tpm2_calculate_pubkey_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name)
*
* The handle must reference a key already present in the TPM. It may be either a public key only, or a
* public/private keypair. */
static int tpm2_get_name(
int tpm2_get_name(
Tpm2Context *c,
const Tpm2Handle *handle,
TPM2B_NAME **ret_name) {
@ -3530,6 +3535,150 @@ int tpm2_policy_auth_value(
return tpm2_get_policy_digest(c, session, ret_policy_digest);
}
/* Extend 'digest' with the PolicySigned calculated hash. */
int tpm2_calculate_policy_signed(TPM2B_DIGEST *digest, const TPM2B_NAME *name) {
TPM2_CC command = TPM2_CC_PolicySigned;
TSS2_RC rc;
int r;
assert(digest);
assert(digest->size == SHA256_DIGEST_SIZE);
assert(name);
r = dlopen_tpm2();
if (r < 0)
return log_debug_errno(r, "TPM2 support not installed: %m");
uint8_t buf[sizeof(command)];
size_t offset = 0;
rc = sym_Tss2_MU_TPM2_CC_Marshal(command, buf, sizeof(buf), &offset);
if (rc != TSS2_RC_SUCCESS)
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to marshal PolicySigned command: %s", sym_Tss2_RC_Decode(rc));
if (offset != sizeof(command))
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Offset 0x%zx wrong after marshalling PolicySigned command", offset);
struct iovec data[] = {
IOVEC_MAKE(buf, offset),
IOVEC_MAKE(name->name, name->size),
};
r = tpm2_digest_many(TPM2_ALG_SHA256, digest, data, ELEMENTSOF(data), /* extend= */ true);
if (r < 0)
return r;
const TPM2B_NONCE policyRef = {}; /* For now, we do not make use of the policyRef stuff */
r = tpm2_digest_buffer(TPM2_ALG_SHA256, digest, policyRef.buffer, policyRef.size, /* extend= */ true);
if (r < 0)
return r;
tpm2_log_debug_digest(digest, "PolicySigned calculated digest");
return 0;
}
int tpm2_policy_signed_hmac_sha256(
Tpm2Context *c,
const Tpm2Handle *session,
const Tpm2Handle *hmac_key_handle,
const struct iovec *hmac_key,
TPM2B_DIGEST **ret_policy_digest) {
#if HAVE_OPENSSL
TSS2_RC rc;
int r;
assert(c);
assert(session);
assert(hmac_key_handle);
assert(iovec_is_set(hmac_key));
/* This sends a TPM2_PolicySigned command to the tpm. As signature key we use an HMAC-SHA256 key
* specified in the hmac_key parameter. The secret key must be loaded into the TPM already and
* referenced in hmac_key_handle. */
log_debug("Submitting PolicySigned policy for HMAC-SHA256.");
/* Acquire the nonce from the TPM that we shall sign */
_cleanup_(Esys_Freep) TPM2B_NONCE *nonce = NULL;
rc = sym_Esys_TRSess_GetNonceTPM(
c->esys_context,
session->esys_handle,
&nonce);
if (rc != TSS2_RC_SUCCESS)
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to determine NoneTPM of auth session: %s",
sym_Tss2_RC_Decode(rc));
be32_t expiration = htobe64(0);
const TPM2B_DIGEST cpHashA = {}; /* For now, we do not make use of the cpHashA stuff */
const TPM2B_NONCE policyRef = {}; /* ditto, we do not bother with policyRef */
/* Put together the data to sign, as per TPM2 Spec Part 3, 23.3.1 */
struct iovec data_to_sign[] = {
IOVEC_MAKE(nonce->buffer, nonce->size),
IOVEC_MAKE(&expiration, sizeof(expiration)),
IOVEC_MAKE(cpHashA.buffer, cpHashA.size),
IOVEC_MAKE(policyRef.buffer, policyRef.size),
};
/* Now calculate the digest of the data we put together */
TPM2B_DIGEST digest_to_sign;
r = tpm2_digest_many(TPM2_ALG_SHA256, &digest_to_sign, data_to_sign, ELEMENTSOF(data_to_sign), /* extend= */ false);
if (r < 0)
return r;
unsigned char hmac_signature[SHA256_DIGEST_SIZE];
unsigned hmac_signature_size = sizeof(hmac_signature);
/* And sign this with our key */
if (!HMAC(EVP_sha256(),
hmac_key->iov_base,
hmac_key->iov_len,
digest_to_sign.buffer,
digest_to_sign.size,
hmac_signature,
&hmac_signature_size))
return -ENOTRECOVERABLE;
/* Now bring the signature into a format that the TPM understands */
TPMT_SIGNATURE sig = {
.sigAlg = TPM2_ALG_HMAC,
.signature.hmac.hashAlg = TPM2_ALG_SHA256,
};
assert(hmac_signature_size == sizeof(sig.signature.hmac.digest.sha256));
memcpy(sig.signature.hmac.digest.sha256, hmac_signature, hmac_signature_size);
/* And submit the whole shebang to the TPM */
rc = sym_Esys_PolicySigned(
c->esys_context,
hmac_key_handle->esys_handle,
session->esys_handle,
/* shandle1= */ ESYS_TR_NONE,
/* shandle2= */ ESYS_TR_NONE,
/* shandle3= */ ESYS_TR_NONE,
nonce,
&cpHashA,
&policyRef,
expiration,
&sig,
/* timeout= */ NULL,
/* policyTicket= */ NULL);
if (rc != TSS2_RC_SUCCESS)
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to add PolicySigned policy to TPM: %s",
sym_Tss2_RC_Decode(rc));
return tpm2_get_policy_digest(c, session, ret_policy_digest);
#else /* HAVE_OPENSSL */
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL support is disabled.");
#endif
}
int tpm2_calculate_policy_authorize_nv(
const TPM2B_NV_PUBLIC *public_info,
TPM2B_DIGEST *digest) {
@ -4785,7 +4934,7 @@ static int tpm2_calculate_seal_private(
TPM2B_AUTH auth = {};
if (pin) {
r = tpm2_get_pin_auth(parent->publicArea.nameAlg, pin, &auth);
r = tpm2_auth_value_from_pin(parent->publicArea.nameAlg, pin, &auth);
if (r < 0)
return r;
}
@ -5250,7 +5399,7 @@ int tpm2_seal(Tpm2Context *c,
CLEANUP_ERASE(hmac_sensitive);
if (pin) {
r = tpm2_get_pin_auth(TPM2_ALG_SHA256, pin, &hmac_sensitive.userAuth);
r = tpm2_auth_value_from_pin(TPM2_ALG_SHA256, pin, &hmac_sensitive.userAuth);
if (r < 0)
return r;
}
@ -5622,8 +5771,6 @@ int tpm2_define_policy_nv_index(
const Tpm2Handle *session,
TPM2_HANDLE requested_nv_index,
const TPM2B_DIGEST *write_policy,
const char *pin,
const TPM2B_AUTH *auth,
TPM2_HANDLE *ret_nv_index,
Tpm2Handle **ret_nv_handle,
TPM2B_NV_PUBLIC *ret_nv_public) {
@ -5633,7 +5780,10 @@ int tpm2_define_policy_nv_index(
int r;
assert(c);
assert(pin || auth);
/* Allocates an nvindex to store a policy for use in PolicyAuthorizeNV in. This is where pcrlock then
* stores its predicted PCR policies in. If 'requested_nv_index' will try to allocate the specified
* nvindex, otherwise will find a free one, and use that. */
r = tpm2_handle_new(c, &new_handle);
if (r < 0)
@ -5641,17 +5791,6 @@ int tpm2_define_policy_nv_index(
new_handle->flush = false; /* This is a persistent NV index, don't flush hence */
TPM2B_AUTH _auth = {};
CLEANUP_ERASE(_auth);
if (!auth) {
r = tpm2_get_pin_auth(TPM2_ALG_SHA256, pin, &_auth);
if (r < 0)
return r;
auth = &_auth;
}
for (unsigned try = 0; try < 25U; try++) {
TPM2_HANDLE nv_index;
@ -5679,7 +5818,7 @@ int tpm2_define_policy_nv_index(
/* shandle1= */ session ? session->esys_handle : ESYS_TR_PASSWORD,
/* shandle2= */ ESYS_TR_NONE,
/* shandle3= */ ESYS_TR_NONE,
auth,
/* auth= */ NULL,
&public_info,
&new_handle->esys_handle);
@ -7031,6 +7170,75 @@ int tpm2_load_public_key_file(const char *path, TPM2B_PUBLIC *ret) {
*ret = device_key_public;
return 0;
}
int tpm2_hmac_key_from_pin(Tpm2Context *c, const Tpm2Handle *session, const TPM2B_AUTH *pin, Tpm2Handle **ret) {
int r;
assert(c);
assert(pin);
assert(ret);
log_debug("Converting PIN into TPM2 HMAC-SHA256 object.");
/* Load the PIN (which we have stored in the "auth" TPM2B_AUTH) into the TPM as an HMAC key so that
* we can use it in a TPM2_PolicySigned() to write to the nvindex. For that we'll prep a pair of
* TPM2B_PUBLIC and TPM2B_SENSITIVE that defines an HMAC-SHA256 keyed hash function, and initialize
* it based on on the provided PIN data. */
TPM2B_PUBLIC auth_hmac_public = {
.publicArea = {
.type = TPM2_ALG_KEYEDHASH,
.nameAlg = TPM2_ALG_SHA256,
.objectAttributes = TPMA_OBJECT_SIGN_ENCRYPT,
.parameters.keyedHashDetail.scheme = {
.scheme = TPM2_ALG_HMAC,
.details.hmac.hashAlg = TPM2_ALG_SHA256,
},
.unique.keyedHash.size = SHA256_DIGEST_SIZE,
},
};
TPM2B_SENSITIVE auth_hmac_private = {
.sensitiveArea = {
.sensitiveType = TPM2_ALG_KEYEDHASH,
.sensitive.bits.size = pin->size,
.seedValue.size = SHA256_DIGEST_SIZE,
},
};
/* Copy in the key data */
memcpy_safe(auth_hmac_private.sensitiveArea.sensitive.bits.buffer, pin->buffer, pin->size);
/* NB: We initialize the seed of the TPMT_SENSITIVE structure to all zeroes, since we want a stable
* "name" of the PIN object */
/* Now calculate the "unique" field for the public area, based on the sensitive data, according to
* the algorithm in the TPM2 spec, part 1, Section 27.5.3.2 */
struct iovec sensitive_data[] = {
IOVEC_MAKE(auth_hmac_private.sensitiveArea.seedValue.buffer, auth_hmac_private.sensitiveArea.seedValue.size),
IOVEC_MAKE(auth_hmac_private.sensitiveArea.sensitive.bits.buffer, auth_hmac_private.sensitiveArea.sensitive.bits.size),
};
r = tpm2_digest_many(
auth_hmac_public.publicArea.nameAlg,
&auth_hmac_public.publicArea.unique.keyedHash,
sensitive_data,
ELEMENTSOF(sensitive_data),
/* extend= */ false);
if (r < 0)
return r;
/* And now load the public/private parts into the TPM and get a handle back */
r = tpm2_load_external(
c,
session,
&auth_hmac_public,
&auth_hmac_private,
ret);
if (r < 0)
return log_error_errno(r, "Failed to load PIN into TPM2: %m");
return 0;
}
#endif
char *tpm2_pcr_mask_to_string(uint32_t mask) {

View file

@ -131,6 +131,7 @@ int tpm2_marshal_nv_public(const TPM2B_NV_PUBLIC *nv_public, void **ret, size_t
int tpm2_unmarshal_nv_public(const void *data, size_t size, TPM2B_NV_PUBLIC *ret_nv_public);
int tpm2_marshal_blob(const TPM2B_PUBLIC *public, const TPM2B_PRIVATE *private, const TPM2B_ENCRYPTED_SECRET *seed, void **ret_blob, size_t *ret_blob_size);
int tpm2_unmarshal_blob(const void *blob, size_t blob_size, TPM2B_PUBLIC *ret_public, TPM2B_PRIVATE *ret_private, TPM2B_ENCRYPTED_SECRET *ret_seed);
int tpm2_get_name(Tpm2Context *c, const Tpm2Handle *handle, TPM2B_NAME **ret_name);
bool tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg);
bool tpm2_supports_command(Tpm2Context *c, TPM2_CC command);
@ -256,7 +257,7 @@ int tpm2_index_from_handle(Tpm2Context *c, const Tpm2Handle *handle, TPM2_HANDLE
int tpm2_pcr_read(Tpm2Context *c, const TPML_PCR_SELECTION *pcr_selection, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values);
int tpm2_pcr_read_missing_values(Tpm2Context *c, Tpm2PCRValue *pcr_values, size_t n_pcr_values);
int tpm2_get_pin_auth(TPMI_ALG_HASH hash, const char *pin, TPM2B_AUTH *ret_auth);
int tpm2_auth_value_from_pin(TPMI_ALG_HASH hash, const char *pin, TPM2B_AUTH *ret_auth);
int tpm2_set_auth(Tpm2Context *c, const Tpm2Handle *handle, const char *pin);
int tpm2_set_auth_binary(Tpm2Context *c, const Tpm2Handle *handle, const TPM2B_AUTH *auth);
@ -267,6 +268,7 @@ int tpm2_policy_authorize_nv(Tpm2Context *c, const Tpm2Handle *session, const Tp
int tpm2_policy_pcr(Tpm2Context *c, const Tpm2Handle *session, const TPML_PCR_SELECTION *pcr_selection, TPM2B_DIGEST **ret_policy_digest);
int tpm2_policy_or(Tpm2Context *c, const Tpm2Handle *session, const TPM2B_DIGEST *branches, size_t n_branches, TPM2B_DIGEST **ret_policy_digest);
int tpm2_policy_super_pcr(Tpm2Context *c, const Tpm2Handle *session, const Tpm2PCRPrediction *prediction, uint16_t algorithm);
int tpm2_policy_signed_hmac_sha256(Tpm2Context *c, const Tpm2Handle *session, const Tpm2Handle *hmac_key_handle, const struct iovec *hmac_key, TPM2B_DIGEST **ret_policy_digest);
int tpm2_calculate_pubkey_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name);
int tpm2_calculate_nv_index_name(const TPMS_NV_PUBLIC *nvpublic, TPM2B_NAME *ret_name);
@ -277,6 +279,7 @@ int tpm2_calculate_policy_authorize_nv(const TPM2B_NV_PUBLIC *public, TPM2B_DIGE
int tpm2_calculate_policy_pcr(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, TPM2B_DIGEST *digest);
int tpm2_calculate_policy_or(const TPM2B_DIGEST *branches, size_t n_branches, TPM2B_DIGEST *digest);
int tpm2_calculate_policy_super_pcr(Tpm2PCRPrediction *prediction, uint16_t algorithm, TPM2B_DIGEST *pcr_policy);
int tpm2_calculate_policy_signed(TPM2B_DIGEST *digest, const TPM2B_NAME *name);
int tpm2_calculate_serialize(TPM2_HANDLE handle, const TPM2B_NAME *name, const TPM2B_PUBLIC *public, void **ret_serialized, size_t *ret_serialized_size);
int tpm2_calculate_sealing_policy(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, const TPM2B_PUBLIC *public, bool use_pin, const Tpm2PCRLockPolicy *policy, TPM2B_DIGEST *digest);
int tpm2_calculate_seal(TPM2_HANDLE parent_handle, const TPM2B_PUBLIC *parent_public, const TPMA_OBJECT *attributes, const struct iovec *secret, const TPM2B_DIGEST *policy, const char *pin, struct iovec *ret_secret, struct iovec *ret_blob, struct iovec *ret_serialized_parent);
@ -298,7 +301,7 @@ int tpm2_tpm2b_public_from_openssl_pkey(const EVP_PKEY *pkey, TPM2B_PUBLIC *ret)
int tpm2_tpm2b_public_from_pem(const void *pem, size_t pem_size, TPM2B_PUBLIC *ret);
int tpm2_tpm2b_public_to_fingerprint(const TPM2B_PUBLIC *public, void **ret_fingerprint, size_t *ret_fingerprint_size);
int tpm2_define_policy_nv_index(Tpm2Context *c, const Tpm2Handle *session, TPM2_HANDLE requested_nv_index, const TPM2B_DIGEST *write_policy, const char *pin, const TPM2B_AUTH *auth, TPM2_HANDLE *ret_nv_index, Tpm2Handle **ret_nv_handle, TPM2B_NV_PUBLIC *ret_nv_public);
int tpm2_define_policy_nv_index(Tpm2Context *c, const Tpm2Handle *session, TPM2_HANDLE requested_nv_index, const TPM2B_DIGEST *write_policy, TPM2_HANDLE *ret_nv_index, Tpm2Handle **ret_nv_handle, TPM2B_NV_PUBLIC *ret_nv_public);
int tpm2_write_policy_nv_index(Tpm2Context *c, const Tpm2Handle *policy_session, TPM2_HANDLE nv_index, const Tpm2Handle *nv_handle, const TPM2B_DIGEST *policy_digest);
int tpm2_undefine_policy_nv_index(Tpm2Context *c, const Tpm2Handle *session, TPM2_HANDLE nv_index, const Tpm2Handle *nv_handle);
@ -310,6 +313,8 @@ int tpm2_deserialize(Tpm2Context *c, const void *serialized, size_t serialized_s
int tpm2_load_public_key_file(const char *path, TPM2B_PUBLIC *ret);
int tpm2_hmac_key_from_pin(Tpm2Context *c, const Tpm2Handle *session, const TPM2B_AUTH *pin, Tpm2Handle **ret);
/* The tpm2-tss library has many structs that are simply a combination of an array (or object) and
* size. These macros allow easily initializing or assigning instances of such structs from an existing
* buffer/object and size, while also checking the size for safety with the struct buffer/object size. If the

View file

@ -74,7 +74,7 @@ if [[ -n "$SD_STUB" ]]; then
"$SD_PCRLOCK" lock-uki <"$SD_STUB"
fi
PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=yes
PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=query
# Repeat immediately (this call will have to reuse the nvindex, rather than create it)
"$SD_PCRLOCK" make-policy --pcr="$PCRS"
"$SD_PCRLOCK" make-policy --pcr="$PCRS" --force
@ -102,7 +102,7 @@ systemd-cryptsetup detach pcrlock
# work.
echo -n test70 | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/910-test70.pcrlock --pcr=16
(! "$SD_PCRLOCK" make-policy --pcr="$PCRS")
PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=yes
PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=query
systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless
systemd-cryptsetup detach pcrlock
@ -110,6 +110,10 @@ systemd-cryptsetup detach pcrlock
# And now let's do it the clean way, and generate the right policy ahead of time.
echo -n test70-take-two | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/920-test70.pcrlock --pcr=16
"$SD_PCRLOCK" make-policy --pcr="$PCRS"
# the next one should be skipped because redundant
"$SD_PCRLOCK" make-policy --pcr="$PCRS"
# but this one should not be skipped, even if redundant, because we force it
"$SD_PCRLOCK" make-policy --pcr="$PCRS" --force --recovery-pin=show
"$SD_PCREXTEND" --pcr=16 test70-take-two