mirror of
https://github.com/systemd/systemd
synced 2024-07-21 18:24:38 +00:00
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:
commit
dd37963aff
36
NEWS
36
NEWS
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue