mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
KEYS: changes for v7.4-rc1
HIGHLIGHTS
==========
A patch that Kyle Zeng submitted addresses a critical bug and complements
the ongoing rhashtable migration. The commit in question is de891c73c1
("keys: make keyring indexes independent of kernel addresses").
The index value for the associative array has been calculated with the
following formula:
mult_64x32_and_fold(description + type address + domain address)
By creating a sequence of dummy keys (e.g., "A", "B", "C", ...), it is
trivial to form a group of equations with a predictable hash function, and
ultimately break KASLR.
Kyle's solution is based on an ephemeral SipHash key and an atomic 64-bit
counter, which is encapsulated to a key_index_id(). This function is then
used to assign identifiers for types and domains, and since the IDs are
unique for each power on, they are as unique as memory addresses are.
With these ingredients the index value calculation can be changed into
form, which is not leaking any kernel addresses:
SipHash(description + type id + domain id)
This is exactly what the Kyle's commit does.
FEATURES AND IMPROVEMENTS
=========================
- TPM fields were moved from trusted_key_options to the TPM backend.
They cost memory for non-TPM backends.
- A TPM trusted key now caches tpm_is_tpm2() at the root of each call
chain, and passes the result as a parameter. This decreases the amount
of I/O traffic given that tpm_is_tpm2() starts and stops the chip via
tpm_try_get_ops() and tpm_put_ops().
BUGS
====
- A TPM trusted key corrupted the return value of a failed key unsealing
operation if the pcrlock feature was used.
- A TPM 2 trusted key allocated an off-by-four too large buffer for the
decoded ASN.1 blob coming from keyctl add trusted.
- Sometimes a TPM 2 trusted key rejected created keys based on the
TPM2_Create response size, treating them as too large. Now it correctly
checks the extracted TPM2 key blob size.
- The keyctl reject operation raced with keyctl restrict_keyring because
keyring->sem was not held in key_reject_and_link() before accessing
keyring->restrict_link.
- keyctl_describe_key() was not holding key->sem while accessing its
fields, which can lead to use-after-free of a retired key type.
- find_keyring_by_name() and procfs code were reading the UID from
key->user->uid instead of key->uid. The latter is the cached value and
is meant to protect against a use-after-free caused by keyctl chown.
- search_process_keyrings_rcu() did not properly RCU-dereference the
authorization key payload, which could cause a null dereference if the
payload was revoked.
- keyctl_read_key() checked key->type->read without holding key->sem,
which can lead to use-after-free if the type is retired. Based on my
suggestion, this was addressed in __keyctl_read_key(), which already
did take key->sem.
-----BEGIN PGP SIGNATURE-----
iHUEABYKAB0WIQRE6pSOnaBC00OEHEIaerohdGur0gUCatjKPgAKCRAaerohdGur
0iVIAP9N2OG7/93a2WbH14jxbEQv2zWEZlV0lz4b4NdV0qyhBwEArta0xov2A57X
nGFNGEYOjtOS1WFJlvBetu0G9Uz/TAA=
=Xkuf
-----END PGP SIGNATURE-----
Merge tag 'keys-v7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd
Pull keys updates from Jarkko Sakkinen:
"Highlight:
- Don't leak local kernel addresses (and complement the ongoing
rhashtable migration) (Kyle Zeng)
The index value for the associative array has been calculated with
the following formula:
mult_64x32_and_fold(description + type address + domain address)
By creating a sequence of dummy keys (e.g., "A", "B", "C", ...), it
is trivial to form a group of equations with a predictable hash
function, and ultimately break KASLR.
Kyle's solution is based on an ephemeral SipHash key and an atomic
64-bit counter, which is encapsulated to a key_index_id(). This
function is then used to assign identifiers for types and domains,
and since the IDs are unique for each power on, they are as unique
as memory addresses are.
With these ingredients the index value calculation can be changed
into form, which is not leaking any kernel addresses:
SipHash(description + type id + domain id)
Features and improvements:
- TPM fields were moved from trusted_key_options to the TPM backend.
They cost memory for non-TPM backends.
- A TPM trusted key now caches tpm_is_tpm2() at the root of each call
chain, and passes the result as a parameter. This decreases the
amount of I/O traffic given that tpm_is_tpm2() starts and stops the
chip via tpm_try_get_ops() and tpm_put_ops().
Bugfixes:
- A TPM trusted key corrupted the return value of a failed key
unsealing operation if the pcrlock feature was used.
- A TPM 2 trusted key allocated an off-by-four too large buffer for
the decoded ASN.1 blob coming from keyctl add trusted.
- Sometimes a TPM 2 trusted key rejected created keys based on the
TPM2_Create response size, treating them as too large. Now it
correctly checks the extracted TPM2 key blob size.
- The keyctl reject operation raced with keyctl restrict_keyring
because keyring->sem was not held in key_reject_and_link() before
accessing keyring->restrict_link.
- keyctl_describe_key() was not holding key->sem while accessing its
fields, which can lead to use-after-free of a retired key type.
- find_keyring_by_name() and procfs code were reading the UID from
key->user->uid instead of key->uid. The latter is the cached value
and is meant to protect against a use-after-free caused by keyctl
chown.
- search_process_keyrings_rcu() did not properly RCU-dereference the
authorization key payload, which could cause a null dereference if
the payload was revoked.
- keyctl_read_key() checked key->type->read without holding key->sem,
which can lead to use-after-free if the type is retired. Based on
my suggestion, this was addressed in __keyctl_read_key(), which
already did take key->sem"
* tag 'keys-v7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd:
keys/trusted_keys: reuse TPM version in option handling
security: keys: Fix comment of search_process_keyrings_rcu()
keys/trusted/tpm2: Validate TPM2_Create object sizes separately
keys/trusted_keys: move TPM-specific fields into struct trusted_key_tpm
keys: Protect the read method check against type removal
keys: Avoid owner account dereferences in /proc/keys
keys: make keyring indexes independent of kernel addresses
keys: Check the authorization payload in process keyring searches
keys: Avoid the owner account dereference in named keyring lookup
keys: Protect the type name while describing a key
KEYS: Fix key_reject_and_link() race with keyring restriction
KEYS: trusted: Fix blob allocation size in tpm2_key_decode()
keys/trusted_keys: return immediately after TPM unseal failure
This commit is contained in:
commit
14785eff40
|
|
@ -39,17 +39,6 @@ struct trusted_key_payload {
|
|||
|
||||
struct trusted_key_options {
|
||||
uint16_t keytype;
|
||||
uint32_t keyhandle;
|
||||
unsigned char keyauth[TPM_DIGEST_SIZE];
|
||||
uint32_t blobauth_len;
|
||||
unsigned char blobauth[TPM_DIGEST_SIZE];
|
||||
uint32_t pcrinfo_len;
|
||||
unsigned char pcrinfo[MAX_PCRINFO_SIZE];
|
||||
int pcrlock;
|
||||
uint32_t hash;
|
||||
uint32_t policydigest_len;
|
||||
unsigned char policydigest[MAX_DIGEST_SIZE];
|
||||
uint32_t policyhandle;
|
||||
void *private;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,20 @@
|
|||
|
||||
extern struct trusted_key_ops trusted_key_tpm_ops;
|
||||
|
||||
struct trusted_key_tpm {
|
||||
u32 keyhandle;
|
||||
unsigned char keyauth[TPM_DIGEST_SIZE];
|
||||
u32 blobauth_len;
|
||||
unsigned char blobauth[TPM_DIGEST_SIZE];
|
||||
u32 pcrinfo_len;
|
||||
unsigned char pcrinfo[MAX_PCRINFO_SIZE];
|
||||
int pcrlock;
|
||||
u32 hash;
|
||||
u32 policydigest_len;
|
||||
unsigned char policydigest[MAX_DIGEST_SIZE];
|
||||
u32 policyhandle;
|
||||
};
|
||||
|
||||
int tpm2_seal_trusted(struct tpm_chip *chip,
|
||||
struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options);
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ struct key_type {
|
|||
const void *in, const void *in2);
|
||||
|
||||
/* internal fields */
|
||||
atomic64_t index_id; /* Opaque keyring index ID */
|
||||
struct list_head link; /* link in types list */
|
||||
struct lock_class_key lock_class; /* key->sem lock class */
|
||||
} __randomize_layout;
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ struct keyring_name;
|
|||
|
||||
struct key_tag {
|
||||
struct rcu_head rcu;
|
||||
atomic64_t index_id; /* Opaque keyring index ID */
|
||||
refcount_t usage;
|
||||
bool removed; /* T when subject removed */
|
||||
};
|
||||
|
|
@ -129,6 +130,8 @@ struct keyring_index_key {
|
|||
struct key_type *type;
|
||||
struct key_tag *domain_tag; /* Domain of operation */
|
||||
const char *description;
|
||||
u64 type_id; /* Cached index IDs */
|
||||
u64 domain_id;
|
||||
};
|
||||
|
||||
union key_payload {
|
||||
|
|
@ -251,6 +254,8 @@ struct key {
|
|||
struct key_type *type; /* type of key */
|
||||
struct key_tag *domain_tag; /* Domain of operation */
|
||||
char *description;
|
||||
u64 type_id;
|
||||
u64 domain_id;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -588,11 +588,18 @@ int key_reject_and_link(struct key *key,
|
|||
ret = -EBUSY;
|
||||
|
||||
if (keyring) {
|
||||
if (keyring->restrict_link)
|
||||
return -EPERM;
|
||||
|
||||
link_ret = __key_link_lock(keyring, &key->index_key);
|
||||
if (link_ret == 0) {
|
||||
/*
|
||||
* Check the restriction under the keyring semaphore.
|
||||
* keyring_restrict() installs it while holding the
|
||||
* semaphore, so testing it beforehand races with a
|
||||
* concurrent restriction install.
|
||||
*/
|
||||
if (keyring->restrict_link) {
|
||||
__key_link_end(keyring, &key->index_key, edit);
|
||||
return -EPERM;
|
||||
}
|
||||
link_ret = __key_link_begin(keyring, &key->index_key, &edit);
|
||||
if (link_ret < 0)
|
||||
__key_link_end(keyring, &key->index_key, edit);
|
||||
|
|
|
|||
|
|
@ -677,12 +677,14 @@ long keyctl_describe_key(key_serial_t keyid,
|
|||
|
||||
/* calculate how much information we're going to return */
|
||||
ret = -ENOMEM;
|
||||
down_read(&key->sem);
|
||||
infobuf = kasprintf(GFP_KERNEL,
|
||||
"%s;%d;%d;%08x;",
|
||||
key->type->name,
|
||||
from_kuid_munged(current_user_ns(), key->uid),
|
||||
from_kgid_munged(current_user_ns(), key->gid),
|
||||
key->perm);
|
||||
up_read(&key->sem);
|
||||
if (!infobuf)
|
||||
goto error2;
|
||||
infolen = strlen(infobuf);
|
||||
|
|
@ -806,8 +808,12 @@ static long __keyctl_read_key(struct key *key, char *buffer, size_t buflen)
|
|||
|
||||
down_read(&key->sem);
|
||||
ret = key_validate(key);
|
||||
if (ret == 0)
|
||||
ret = key->type->read(key, buffer, buflen);
|
||||
if (ret == 0) {
|
||||
if (key->type->read)
|
||||
ret = key->type->read(key, buffer, buflen);
|
||||
else
|
||||
ret = -EOPNOTSUPP;
|
||||
}
|
||||
up_read(&key->sem);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -861,11 +867,6 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
|
|||
|
||||
/* the key is probably readable - now try to read it */
|
||||
can_read_key:
|
||||
if (!key->type->read) {
|
||||
ret = -EOPNOTSUPP;
|
||||
goto key_put_out;
|
||||
}
|
||||
|
||||
if (!buffer || !buflen) {
|
||||
/* Get the key length from the read method */
|
||||
ret = __keyctl_read_key(key, NULL, 0);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/user_namespace.h>
|
||||
#include <linux/nsproxy.h>
|
||||
#include <linux/once.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/siphash.h>
|
||||
#include <keys/keyring-type.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <linux/assoc_array_priv.h>
|
||||
|
|
@ -147,49 +150,46 @@ static int keyring_instantiate(struct key *keyring,
|
|||
}
|
||||
|
||||
/*
|
||||
* Multiply 64-bits by 32-bits to 96-bits and fold back to 64-bit. Ideally we'd
|
||||
* fold the carry back too, but that requires inline asm.
|
||||
* Give key types and domains address-independent identities. Zero means that
|
||||
* an ID has not yet been allocated. As with inode sequence numbers used by
|
||||
* futexes, this relies on the 64-bit counter not wrapping in a machine's
|
||||
* lifetime.
|
||||
*/
|
||||
static u64 mult_64x32_and_fold(u64 x, u32 y)
|
||||
static u64 key_index_id(atomic64_t *id)
|
||||
{
|
||||
u64 hi = (u64)(u32)(x >> 32) * y;
|
||||
u64 lo = (u64)(u32)(x) * y;
|
||||
return lo + ((u64)(u32)hi << 32) + (u32)(hi >> 32);
|
||||
static atomic64_t next_id;
|
||||
u64 old, new;
|
||||
|
||||
old = atomic64_read(id);
|
||||
if (likely(old))
|
||||
return old;
|
||||
|
||||
do {
|
||||
new = atomic64_inc_return(&next_id);
|
||||
} while (WARN_ON_ONCE(!new));
|
||||
|
||||
old = 0;
|
||||
if (!atomic64_try_cmpxchg_relaxed(id, &old, new))
|
||||
return old;
|
||||
return new;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hash a key type and description.
|
||||
* Hash a key type, domain and description. KEYCTL_READ exposes the tree's
|
||||
* traversal order, so no part of the index may contain kernel addresses.
|
||||
*/
|
||||
static void hash_key_type_and_desc(struct keyring_index_key *index_key)
|
||||
{
|
||||
const unsigned level_shift = ASSOC_ARRAY_LEVEL_STEP;
|
||||
const unsigned long fan_mask = ASSOC_ARRAY_FAN_MASK;
|
||||
const char *description = index_key->description;
|
||||
unsigned long hash, type;
|
||||
u32 piece;
|
||||
static siphash_key_t hash_key __read_mostly;
|
||||
unsigned long hash;
|
||||
u64 acc;
|
||||
int n, desc_len = index_key->desc_len;
|
||||
|
||||
type = (unsigned long)index_key->type;
|
||||
acc = mult_64x32_and_fold(type, desc_len + 13);
|
||||
acc = mult_64x32_and_fold(acc, 9207);
|
||||
piece = (unsigned long)index_key->domain_tag;
|
||||
acc = mult_64x32_and_fold(acc, piece);
|
||||
acc = mult_64x32_and_fold(acc, 9207);
|
||||
|
||||
for (;;) {
|
||||
n = desc_len;
|
||||
if (n <= 0)
|
||||
break;
|
||||
if (n > 4)
|
||||
n = 4;
|
||||
piece = 0;
|
||||
memcpy(&piece, description, n);
|
||||
description += n;
|
||||
desc_len -= n;
|
||||
acc = mult_64x32_and_fold(acc, piece);
|
||||
acc = mult_64x32_and_fold(acc, 9207);
|
||||
}
|
||||
get_random_once(&hash_key, sizeof(hash_key));
|
||||
acc = siphash(index_key->description, index_key->desc_len, &hash_key);
|
||||
acc = siphash_3u64(index_key->type_id, index_key->domain_id, acc,
|
||||
&hash_key);
|
||||
|
||||
/* Fold the hash down to 32 bits if need be. */
|
||||
hash = acc;
|
||||
|
|
@ -209,7 +209,7 @@ static void hash_key_type_and_desc(struct keyring_index_key *index_key)
|
|||
|
||||
/*
|
||||
* Finalise an index key to include a part of the description actually in the
|
||||
* index key, to set the domain tag and to calculate the hash.
|
||||
* index key, to set the domain tag and index IDs, and to calculate the hash.
|
||||
*/
|
||||
void key_set_index_key(struct keyring_index_key *index_key)
|
||||
{
|
||||
|
|
@ -225,6 +225,14 @@ void key_set_index_key(struct keyring_index_key *index_key)
|
|||
index_key->domain_tag = &default_domain_tag;
|
||||
}
|
||||
|
||||
/* Preserve the IDs when copying an existing key's index. In
|
||||
* particular, its type may already have been unregistered.
|
||||
*/
|
||||
if (!index_key->type_id)
|
||||
index_key->type_id = key_index_id(&index_key->type->index_id);
|
||||
if (!index_key->domain_id)
|
||||
index_key->domain_id = key_index_id(&index_key->domain_tag->index_id);
|
||||
|
||||
hash_key_type_and_desc(index_key);
|
||||
}
|
||||
|
||||
|
|
@ -263,15 +271,19 @@ void key_remove_domain(struct key_tag *domain_tag)
|
|||
/*
|
||||
* Build the next index key chunk.
|
||||
*
|
||||
* The hash and inline description are followed by the 64-bit type and domain
|
||||
* IDs, low word first, and then by the rest of the description.
|
||||
* We return it one word-sized chunk at a time.
|
||||
*/
|
||||
static unsigned long keyring_get_key_chunk(const void *data, int level)
|
||||
{
|
||||
const unsigned int id_chunks = sizeof(u64) / sizeof(unsigned long);
|
||||
const struct keyring_index_key *index_key = data;
|
||||
int desc_len = index_key->desc_len;
|
||||
unsigned long chunk = 0;
|
||||
const u8 *d;
|
||||
int desc_len = index_key->desc_len, n = sizeof(chunk);
|
||||
int n = sizeof(chunk);
|
||||
unsigned int offset;
|
||||
const u8 *d;
|
||||
|
||||
level /= ASSOC_ARRAY_KEY_CHUNK_SIZE;
|
||||
switch (level) {
|
||||
|
|
@ -279,27 +291,30 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
|
|||
return index_key->hash;
|
||||
case 1:
|
||||
return index_key->x;
|
||||
case 2:
|
||||
return (unsigned long)index_key->type;
|
||||
case 3:
|
||||
return (unsigned long)index_key->domain_tag;
|
||||
default:
|
||||
level -= 4;
|
||||
offset = sizeof(index_key->desc) + level * sizeof(long);
|
||||
if (desc_len <= offset)
|
||||
return 0;
|
||||
|
||||
d = index_key->description + offset;
|
||||
desc_len -= offset;
|
||||
if (desc_len > n)
|
||||
desc_len = n;
|
||||
d += desc_len;
|
||||
do {
|
||||
chunk <<= 8;
|
||||
chunk |= *--d;
|
||||
} while (--desc_len > 0);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
level -= 2;
|
||||
if (level < id_chunks)
|
||||
return index_key->type_id >> (level * ASSOC_ARRAY_KEY_CHUNK_SIZE);
|
||||
level -= id_chunks;
|
||||
if (level < id_chunks)
|
||||
return index_key->domain_id >> (level * ASSOC_ARRAY_KEY_CHUNK_SIZE);
|
||||
level -= id_chunks;
|
||||
|
||||
offset = sizeof(index_key->desc) + level * sizeof(long);
|
||||
if (desc_len <= offset)
|
||||
return 0;
|
||||
|
||||
d = index_key->description + offset;
|
||||
desc_len -= offset;
|
||||
if (desc_len > n)
|
||||
desc_len = n;
|
||||
d += desc_len;
|
||||
do {
|
||||
chunk <<= 8;
|
||||
chunk |= *--d;
|
||||
} while (--desc_len > 0);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
static unsigned long keyring_get_object_key_chunk(const void *object, int level)
|
||||
|
|
@ -331,6 +346,7 @@ static int keyring_diff_objects(const void *object, const void *data)
|
|||
const struct keyring_index_key *b = data;
|
||||
unsigned long seg_a, seg_b;
|
||||
int level, i;
|
||||
u64 diff;
|
||||
|
||||
level = 0;
|
||||
seg_a = a->hash;
|
||||
|
|
@ -339,28 +355,22 @@ static int keyring_diff_objects(const void *object, const void *data)
|
|||
goto differ;
|
||||
level += ASSOC_ARRAY_KEY_CHUNK_SIZE / 8;
|
||||
|
||||
/* The number of bits contributed by the hash is controlled by a
|
||||
* constant in the assoc_array headers. Everything else thereafter we
|
||||
* can deal with as being machine word-size dependent.
|
||||
*/
|
||||
/* The inline description contributes one machine-word-sized chunk. */
|
||||
seg_a = a->x;
|
||||
seg_b = b->x;
|
||||
if ((seg_a ^ seg_b) != 0)
|
||||
goto differ;
|
||||
level += sizeof(unsigned long);
|
||||
|
||||
/* The next bit may not work on big endian */
|
||||
seg_a = (unsigned long)a->type;
|
||||
seg_b = (unsigned long)b->type;
|
||||
if ((seg_a ^ seg_b) != 0)
|
||||
goto differ;
|
||||
level += sizeof(unsigned long);
|
||||
diff = a->type_id ^ b->type_id;
|
||||
if (diff)
|
||||
return level * 8 + __ffs64(diff);
|
||||
level += sizeof(u64);
|
||||
|
||||
seg_a = (unsigned long)a->domain_tag;
|
||||
seg_b = (unsigned long)b->domain_tag;
|
||||
if ((seg_a ^ seg_b) != 0)
|
||||
goto differ;
|
||||
level += sizeof(unsigned long);
|
||||
diff = a->domain_id ^ b->domain_id;
|
||||
if (diff)
|
||||
return level * 8 + __ffs64(diff);
|
||||
level += sizeof(u64);
|
||||
|
||||
i = sizeof(a->desc);
|
||||
if (a->desc_len <= i)
|
||||
|
|
@ -1158,7 +1168,7 @@ struct key *find_keyring_by_name(const char *name, bool uid_keyring)
|
|||
* grants Search permission and that hasn't been revoked
|
||||
*/
|
||||
list_for_each_entry(keyring, &ns->keyring_name_list, name_link) {
|
||||
if (!kuid_has_mapping(ns, keyring->user->uid))
|
||||
if (!kuid_has_mapping(ns, keyring->uid))
|
||||
continue;
|
||||
|
||||
if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ static struct rb_node *key_serial_next(struct seq_file *p, struct rb_node *n)
|
|||
n = rb_next(n);
|
||||
while (n) {
|
||||
struct key *key = rb_entry(n, struct key, serial_node);
|
||||
if (kuid_has_mapping(user_ns, key->user->uid))
|
||||
if (kuid_has_mapping(user_ns, key->uid))
|
||||
break;
|
||||
n = rb_next(n);
|
||||
}
|
||||
|
|
@ -100,7 +100,7 @@ static struct key *find_ge_key(struct seq_file *p, key_serial_t id)
|
|||
return NULL;
|
||||
|
||||
for (;;) {
|
||||
if (kuid_has_mapping(user_ns, minkey->user->uid))
|
||||
if (kuid_has_mapping(user_ns, minkey->uid))
|
||||
return minkey;
|
||||
n = rb_next(&minkey->serial_node);
|
||||
if (!n)
|
||||
|
|
|
|||
|
|
@ -528,7 +528,7 @@ key_ref_t search_cred_keyrings_rcu(struct keyring_search_context *ctx)
|
|||
|
||||
/*
|
||||
* Search the process keyrings attached to the supplied cred for the first
|
||||
* matching key in the manner of search_my_process_keyrings(), but also search
|
||||
* matching key in the manner of search_cred_keyrings_rcu(), but also search
|
||||
* the keys attached to the assumed authorisation key using its credentials if
|
||||
* one is available.
|
||||
*
|
||||
|
|
@ -556,9 +556,8 @@ key_ref_t search_process_keyrings_rcu(struct keyring_search_context *ctx)
|
|||
) {
|
||||
const struct cred *cred = ctx->cred;
|
||||
|
||||
if (key_validate(cred->request_key_auth) == 0) {
|
||||
rka = ctx->cred->request_key_auth->payload.data[0];
|
||||
|
||||
rka = dereference_key_rcu(cred->request_key_auth);
|
||||
if (rka && key_validate(cred->request_key_auth) == 0) {
|
||||
//// was search_process_keyrings() [ie. recursive]
|
||||
ctx->cred = rka->cred;
|
||||
key_ref = search_cred_keyrings_rcu(ctx);
|
||||
|
|
|
|||
|
|
@ -48,15 +48,17 @@ enum {
|
|||
#ifdef CONFIG_TRUSTED_KEYS_DEBUG
|
||||
static inline void dump_options(struct trusted_key_options *o)
|
||||
{
|
||||
struct trusted_key_tpm *private = o->private;
|
||||
|
||||
if (!trusted_debug)
|
||||
return;
|
||||
|
||||
pr_debug("sealing key type %d\n", o->keytype);
|
||||
pr_debug("sealing key handle %0X\n", o->keyhandle);
|
||||
pr_debug("pcrlock %d\n", o->pcrlock);
|
||||
pr_debug("pcrinfo %d\n", o->pcrinfo_len);
|
||||
pr_debug("sealing key handle %0X\n", private->keyhandle);
|
||||
pr_debug("pcrlock %d\n", private->pcrlock);
|
||||
pr_debug("pcrinfo %d\n", private->pcrinfo_len);
|
||||
print_hex_dump_debug("pcrinfo ", DUMP_PREFIX_NONE,
|
||||
16, 1, o->pcrinfo, o->pcrinfo_len, 0);
|
||||
16, 1, private->pcrinfo, private->pcrinfo_len, 0);
|
||||
}
|
||||
|
||||
static inline void dump_sess(struct osapsess *s)
|
||||
|
|
@ -626,6 +628,7 @@ static int tpm_unseal(struct tpm_buf *tb,
|
|||
static int key_seal(struct trusted_key_payload *p,
|
||||
struct trusted_key_options *o)
|
||||
{
|
||||
struct trusted_key_tpm *private = o->private;
|
||||
int ret;
|
||||
|
||||
struct tpm_buf *tb __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
|
||||
|
|
@ -637,9 +640,10 @@ static int key_seal(struct trusted_key_payload *p,
|
|||
/* include migratable flag at end of sealed key */
|
||||
p->key[p->key_len] = p->migratable;
|
||||
|
||||
ret = tpm_seal(tb, o->keytype, o->keyhandle, o->keyauth,
|
||||
ret = tpm_seal(tb, o->keytype, private->keyhandle, private->keyauth,
|
||||
p->key, p->key_len + 1, p->blob, &p->blob_len,
|
||||
o->blobauth, o->pcrinfo, o->pcrinfo_len);
|
||||
private->blobauth, private->pcrinfo,
|
||||
private->pcrinfo_len);
|
||||
if (ret < 0)
|
||||
pr_info("srkseal failed (%d)\n", ret);
|
||||
|
||||
|
|
@ -652,6 +656,7 @@ static int key_seal(struct trusted_key_payload *p,
|
|||
static int key_unseal(struct trusted_key_payload *p,
|
||||
struct trusted_key_options *o)
|
||||
{
|
||||
struct trusted_key_tpm *private = o->private;
|
||||
int ret;
|
||||
|
||||
struct tpm_buf *tb __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
|
||||
|
|
@ -660,8 +665,8 @@ static int key_unseal(struct trusted_key_payload *p,
|
|||
|
||||
tpm_buf_init(tb, TPM_BUFSIZE);
|
||||
|
||||
ret = tpm_unseal(tb, o->keyhandle, o->keyauth, p->blob, p->blob_len,
|
||||
o->blobauth, p->key, &p->key_len);
|
||||
ret = tpm_unseal(tb, private->keyhandle, private->keyauth, p->blob,
|
||||
p->blob_len, private->blobauth, p->key, &p->key_len);
|
||||
if (ret < 0)
|
||||
pr_info("srkunseal failed (%d)\n", ret);
|
||||
else
|
||||
|
|
@ -695,8 +700,9 @@ static const match_table_t key_tokens = {
|
|||
|
||||
/* can have zero or more token= options */
|
||||
static int getoptions(char *c, struct trusted_key_payload *pay,
|
||||
struct trusted_key_options *opt)
|
||||
struct trusted_key_options *opt, bool is_tpm2)
|
||||
{
|
||||
struct trusted_key_tpm *private = opt->private;
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
char *p = c;
|
||||
int token;
|
||||
|
|
@ -706,13 +712,8 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
|
|||
unsigned long token_mask = 0;
|
||||
unsigned int digest_len;
|
||||
int i;
|
||||
int tpm2;
|
||||
|
||||
tpm2 = tpm_is_tpm2(chip);
|
||||
if (tpm2 < 0)
|
||||
return tpm2;
|
||||
|
||||
opt->hash = tpm2 ? HASH_ALGO_SHA256 : HASH_ALGO_SHA1;
|
||||
private->hash = is_tpm2 ? HASH_ALGO_SHA256 : HASH_ALGO_SHA1;
|
||||
|
||||
if (!c)
|
||||
return 0;
|
||||
|
|
@ -726,11 +727,11 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
|
|||
|
||||
switch (token) {
|
||||
case Opt_pcrinfo:
|
||||
opt->pcrinfo_len = strlen(args[0].from) / 2;
|
||||
if (opt->pcrinfo_len > MAX_PCRINFO_SIZE)
|
||||
private->pcrinfo_len = strlen(args[0].from) / 2;
|
||||
if (private->pcrinfo_len > MAX_PCRINFO_SIZE)
|
||||
return -EINVAL;
|
||||
res = hex2bin(opt->pcrinfo, args[0].from,
|
||||
opt->pcrinfo_len);
|
||||
res = hex2bin(private->pcrinfo, args[0].from,
|
||||
private->pcrinfo_len);
|
||||
if (res < 0)
|
||||
return -EINVAL;
|
||||
break;
|
||||
|
|
@ -739,12 +740,12 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
|
|||
if (res < 0)
|
||||
return -EINVAL;
|
||||
opt->keytype = SEAL_keytype;
|
||||
opt->keyhandle = handle;
|
||||
private->keyhandle = handle;
|
||||
break;
|
||||
case Opt_keyauth:
|
||||
if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE)
|
||||
return -EINVAL;
|
||||
res = hex2bin(opt->keyauth, args[0].from,
|
||||
res = hex2bin(private->keyauth, args[0].from,
|
||||
SHA1_DIGEST_SIZE);
|
||||
if (res < 0)
|
||||
return -EINVAL;
|
||||
|
|
@ -755,21 +756,23 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
|
|||
* hex strings. TPM 2.0 authorizations are simple
|
||||
* passwords (although it can take a hash as well)
|
||||
*/
|
||||
opt->blobauth_len = strlen(args[0].from);
|
||||
private->blobauth_len = strlen(args[0].from);
|
||||
|
||||
if (opt->blobauth_len == 2 * TPM_DIGEST_SIZE) {
|
||||
res = hex2bin(opt->blobauth, args[0].from,
|
||||
if (private->blobauth_len == 2 * TPM_DIGEST_SIZE) {
|
||||
res = hex2bin(private->blobauth, args[0].from,
|
||||
TPM_DIGEST_SIZE);
|
||||
if (res < 0)
|
||||
return -EINVAL;
|
||||
|
||||
opt->blobauth_len = TPM_DIGEST_SIZE;
|
||||
private->blobauth_len = TPM_DIGEST_SIZE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (tpm2 && opt->blobauth_len <= sizeof(opt->blobauth)) {
|
||||
memcpy(opt->blobauth, args[0].from,
|
||||
opt->blobauth_len);
|
||||
if (is_tpm2 &&
|
||||
private->blobauth_len <=
|
||||
sizeof(private->blobauth)) {
|
||||
memcpy(private->blobauth, args[0].from,
|
||||
private->blobauth_len);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -787,41 +790,42 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
|
|||
res = kstrtoul(args[0].from, 10, &lock);
|
||||
if (res < 0)
|
||||
return -EINVAL;
|
||||
opt->pcrlock = lock;
|
||||
private->pcrlock = lock;
|
||||
break;
|
||||
case Opt_hash:
|
||||
if (test_bit(Opt_policydigest, &token_mask))
|
||||
return -EINVAL;
|
||||
for (i = 0; i < HASH_ALGO__LAST; i++) {
|
||||
if (!strcmp(args[0].from, hash_algo_name[i])) {
|
||||
opt->hash = i;
|
||||
private->hash = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == HASH_ALGO__LAST)
|
||||
return -EINVAL;
|
||||
if (!tpm2 && i != HASH_ALGO_SHA1) {
|
||||
if (!is_tpm2 && i != HASH_ALGO_SHA1) {
|
||||
pr_info("TPM 1.x only supports SHA-1.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case Opt_policydigest:
|
||||
digest_len = hash_digest_size[opt->hash];
|
||||
if (!tpm2 || strlen(args[0].from) != (2 * digest_len))
|
||||
digest_len = hash_digest_size[private->hash];
|
||||
if (!is_tpm2 ||
|
||||
strlen(args[0].from) != (2 * digest_len))
|
||||
return -EINVAL;
|
||||
res = hex2bin(opt->policydigest, args[0].from,
|
||||
res = hex2bin(private->policydigest, args[0].from,
|
||||
digest_len);
|
||||
if (res < 0)
|
||||
return -EINVAL;
|
||||
opt->policydigest_len = digest_len;
|
||||
private->policydigest_len = digest_len;
|
||||
break;
|
||||
case Opt_policyhandle:
|
||||
if (!tpm2)
|
||||
if (!is_tpm2)
|
||||
return -EINVAL;
|
||||
res = kstrtoul(args[0].from, 16, &handle);
|
||||
if (res < 0)
|
||||
return -EINVAL;
|
||||
opt->policyhandle = handle;
|
||||
private->policyhandle = handle;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
|
@ -830,29 +834,33 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct trusted_key_options *trusted_options_alloc(void)
|
||||
static struct trusted_key_options *trusted_options_alloc(bool is_tpm2)
|
||||
{
|
||||
struct trusted_key_tpm *private;
|
||||
struct trusted_key_options *options;
|
||||
int tpm2;
|
||||
|
||||
tpm2 = tpm_is_tpm2(chip);
|
||||
if (tpm2 < 0)
|
||||
return NULL;
|
||||
|
||||
options = kzalloc_obj(*options);
|
||||
if (options) {
|
||||
/* set any non-zero defaults */
|
||||
options->keytype = SRK_keytype;
|
||||
|
||||
if (!tpm2)
|
||||
options->keyhandle = SRKHANDLE;
|
||||
private = kzalloc_obj(*private);
|
||||
if (!private) {
|
||||
kfree_sensitive(options);
|
||||
options = NULL;
|
||||
} else {
|
||||
if (!is_tpm2)
|
||||
private->keyhandle = SRKHANDLE;
|
||||
options->private = private;
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
static int trusted_tpm_seal(struct trusted_key_payload *p, char *datablob)
|
||||
{
|
||||
struct trusted_key_options *options = NULL;
|
||||
struct trusted_key_options *options __free(kfree_sensitive) = NULL;
|
||||
struct trusted_key_tpm *private __free(kfree_sensitive) = NULL;
|
||||
int ret = 0;
|
||||
int tpm2;
|
||||
|
||||
|
|
@ -860,19 +868,19 @@ static int trusted_tpm_seal(struct trusted_key_payload *p, char *datablob)
|
|||
if (tpm2 < 0)
|
||||
return tpm2;
|
||||
|
||||
options = trusted_options_alloc();
|
||||
options = trusted_options_alloc(tpm2);
|
||||
if (!options)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = getoptions(datablob, p, options);
|
||||
private = options->private;
|
||||
|
||||
ret = getoptions(datablob, p, options, tpm2);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
return ret;
|
||||
dump_options(options);
|
||||
|
||||
if (!options->keyhandle && !tpm2) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (!private->keyhandle && !tpm2)
|
||||
return -EINVAL;
|
||||
|
||||
if (tpm2)
|
||||
ret = tpm2_seal_trusted(chip, p, options);
|
||||
|
|
@ -880,24 +888,24 @@ static int trusted_tpm_seal(struct trusted_key_payload *p, char *datablob)
|
|||
ret = key_seal(p, options);
|
||||
if (ret < 0) {
|
||||
pr_info("key_seal failed (%d)\n", ret);
|
||||
goto out;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (options->pcrlock) {
|
||||
ret = pcrlock(options->pcrlock);
|
||||
if (private->pcrlock) {
|
||||
ret = pcrlock(private->pcrlock);
|
||||
if (ret < 0) {
|
||||
pr_info("pcrlock failed (%d)\n", ret);
|
||||
goto out;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
out:
|
||||
kfree_sensitive(options);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int trusted_tpm_unseal(struct trusted_key_payload *p, char *datablob)
|
||||
{
|
||||
struct trusted_key_options *options = NULL;
|
||||
struct trusted_key_options *options __free(kfree_sensitive) = NULL;
|
||||
struct trusted_key_tpm *private __free(kfree_sensitive) = NULL;
|
||||
int ret = 0;
|
||||
int tpm2;
|
||||
|
||||
|
|
@ -905,36 +913,36 @@ static int trusted_tpm_unseal(struct trusted_key_payload *p, char *datablob)
|
|||
if (tpm2 < 0)
|
||||
return tpm2;
|
||||
|
||||
options = trusted_options_alloc();
|
||||
options = trusted_options_alloc(tpm2);
|
||||
if (!options)
|
||||
return -ENOMEM;
|
||||
private = options->private;
|
||||
|
||||
ret = getoptions(datablob, p, options);
|
||||
ret = getoptions(datablob, p, options, tpm2);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
return ret;
|
||||
dump_options(options);
|
||||
|
||||
if (!options->keyhandle && !tpm2) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (!private->keyhandle && !tpm2)
|
||||
return -EINVAL;
|
||||
|
||||
if (tpm2)
|
||||
ret = tpm2_unseal_trusted(chip, p, options);
|
||||
else
|
||||
ret = key_unseal(p, options);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
pr_info("key_unseal failed (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (options->pcrlock) {
|
||||
ret = pcrlock(options->pcrlock);
|
||||
if (private->pcrlock) {
|
||||
ret = pcrlock(private->pcrlock);
|
||||
if (ret < 0) {
|
||||
pr_info("pcrlock failed (%d)\n", ret);
|
||||
goto out;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
out:
|
||||
kfree_sensitive(options);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,36 +23,48 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
|
|||
struct trusted_key_options *options,
|
||||
u8 *src, u32 len)
|
||||
{
|
||||
struct trusted_key_tpm *private = options->private;
|
||||
const int SCRATCH_SIZE = PAGE_SIZE;
|
||||
u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
|
||||
u8 *work = scratch, *work1;
|
||||
u8 *end_work = scratch + SCRATCH_SIZE;
|
||||
u8 *scratch __free(kfree) = NULL;
|
||||
u8 *work, *work1;
|
||||
u8 *end_work;
|
||||
u8 *priv, *pub;
|
||||
u16 priv_len, pub_len;
|
||||
u32 priv_len, pub_len;
|
||||
int ret;
|
||||
|
||||
priv_len = get_unaligned_be16(src) + 2;
|
||||
priv = src;
|
||||
if (len < 4)
|
||||
return -EINVAL;
|
||||
|
||||
priv_len = get_unaligned_be16(src) + 2;
|
||||
if (priv_len + 2 > len)
|
||||
return -EIO;
|
||||
|
||||
priv = src;
|
||||
src += priv_len;
|
||||
|
||||
pub_len = get_unaligned_be16(src) + 2;
|
||||
if (pub_len + priv_len > len)
|
||||
return -EIO;
|
||||
|
||||
pub = src;
|
||||
|
||||
scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
|
||||
if (!scratch)
|
||||
return -ENOMEM;
|
||||
|
||||
work = scratch;
|
||||
end_work = scratch + SCRATCH_SIZE;
|
||||
|
||||
work = asn1_encode_oid(work, end_work, tpm2key_oid,
|
||||
asn1_oid_len(tpm2key_oid));
|
||||
|
||||
if (options->blobauth_len == 0) {
|
||||
if (private->blobauth_len == 0) {
|
||||
unsigned char bool[3], *w = bool;
|
||||
/* tag 0 is emptyAuth */
|
||||
w = asn1_encode_boolean(w, w + sizeof(bool), true);
|
||||
if (WARN(IS_ERR(w), "BUG: Boolean failed to encode")) {
|
||||
ret = PTR_ERR(w);
|
||||
goto err;
|
||||
}
|
||||
if (WARN(IS_ERR(w), "BUG: Boolean failed to encode"))
|
||||
return PTR_ERR(w);
|
||||
|
||||
work = asn1_encode_tag(work, end_work, 0, bool, w - bool);
|
||||
}
|
||||
|
||||
|
|
@ -64,11 +76,10 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
|
|||
*/
|
||||
if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE,
|
||||
"BUG: scratch buffer is too small")) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
work = asn1_encode_integer(work, end_work, options->keyhandle);
|
||||
work = asn1_encode_integer(work, end_work, private->keyhandle);
|
||||
work = asn1_encode_octet_string(work, end_work, pub, pub_len);
|
||||
work = asn1_encode_octet_string(work, end_work, priv, priv_len);
|
||||
|
||||
|
|
@ -78,15 +89,10 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
|
|||
if (IS_ERR(work1)) {
|
||||
ret = PTR_ERR(work1);
|
||||
pr_err("BUG: ASN.1 encoder failed with %d\n", ret);
|
||||
goto err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
kfree(scratch);
|
||||
return work1 - payload->blob;
|
||||
|
||||
err:
|
||||
kfree(scratch);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct tpm2_key_context {
|
||||
|
|
@ -101,6 +107,7 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
|
|||
struct trusted_key_options *options,
|
||||
u8 **buf, unsigned int *blob_len)
|
||||
{
|
||||
struct trusted_key_tpm *private = options->private;
|
||||
int ret;
|
||||
struct tpm2_key_context ctx;
|
||||
u8 *blob;
|
||||
|
|
@ -115,13 +122,13 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
|
|||
if (ctx.priv_len + ctx.pub_len > MAX_BLOB_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
blob = kmalloc(ctx.priv_len + ctx.pub_len + 4, GFP_KERNEL);
|
||||
blob = kmalloc(ctx.priv_len + ctx.pub_len, GFP_KERNEL);
|
||||
if (!blob)
|
||||
return -ENOMEM;
|
||||
|
||||
*buf = blob;
|
||||
*blob_len = ctx.priv_len + ctx.pub_len;
|
||||
options->keyhandle = ctx.parent;
|
||||
private->keyhandle = ctx.parent;
|
||||
|
||||
memcpy(blob, ctx.priv, ctx.priv_len);
|
||||
blob += ctx.priv_len;
|
||||
|
|
@ -233,6 +240,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
|
|||
struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options)
|
||||
{
|
||||
struct trusted_key_tpm *private = options->private;
|
||||
off_t offset = TPM_HEADER_SIZE;
|
||||
struct tpm_buf *buf __free(kfree) = NULL;
|
||||
struct tpm_buf *sized __free(kfree) = NULL;
|
||||
|
|
@ -241,11 +249,11 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
|
|||
u32 flags;
|
||||
int rc;
|
||||
|
||||
hash = tpm2_find_hash_alg(options->hash);
|
||||
hash = tpm2_find_hash_alg(private->hash);
|
||||
if (hash < 0)
|
||||
return hash;
|
||||
|
||||
if (!options->keyhandle)
|
||||
if (!private->keyhandle)
|
||||
return -EINVAL;
|
||||
|
||||
rc = tpm_try_get_ops(chip);
|
||||
|
|
@ -275,18 +283,18 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
|
|||
|
||||
tpm_buf_init_sized(sized, TPM_BUFSIZE);
|
||||
|
||||
rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
|
||||
rc = tpm_buf_append_name(chip, buf, private->keyhandle, NULL);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
tpm_buf_append_hmac_session(chip, buf, TPM2_SA_DECRYPT,
|
||||
options->keyauth, TPM_DIGEST_SIZE);
|
||||
private->keyauth, TPM_DIGEST_SIZE);
|
||||
|
||||
/* sensitive */
|
||||
tpm_buf_append_u16(sized, options->blobauth_len);
|
||||
tpm_buf_append_u16(sized, private->blobauth_len);
|
||||
|
||||
if (options->blobauth_len)
|
||||
tpm_buf_append(sized, options->blobauth, options->blobauth_len);
|
||||
if (private->blobauth_len)
|
||||
tpm_buf_append(sized, private->blobauth, private->blobauth_len);
|
||||
|
||||
tpm_buf_append_u16(sized, payload->key_len);
|
||||
tpm_buf_append(sized, payload->key, payload->key_len);
|
||||
|
|
@ -299,14 +307,15 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
|
|||
|
||||
/* key properties */
|
||||
flags = 0;
|
||||
flags |= options->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH;
|
||||
flags |= private->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH;
|
||||
flags |= payload->migratable ? 0 : (TPM2_OA_FIXED_TPM | TPM2_OA_FIXED_PARENT);
|
||||
tpm_buf_append_u32(sized, flags);
|
||||
|
||||
/* policy */
|
||||
tpm_buf_append_u16(sized, options->policydigest_len);
|
||||
if (options->policydigest_len)
|
||||
tpm_buf_append(sized, options->policydigest, options->policydigest_len);
|
||||
tpm_buf_append_u16(sized, private->policydigest_len);
|
||||
if (private->policydigest_len)
|
||||
tpm_buf_append(sized, private->policydigest,
|
||||
private->policydigest_len);
|
||||
|
||||
/* public parameters */
|
||||
tpm_buf_append_u16(sized, TPM_ALG_NULL);
|
||||
|
|
@ -336,10 +345,11 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
|
|||
goto out;
|
||||
|
||||
blob_len = tpm_buf_read_u32(buf, &offset);
|
||||
if (blob_len > MAX_BLOB_SIZE || buf->flags & TPM_BUF_INVALID) {
|
||||
if (buf->flags & TPM_BUF_INVALID) {
|
||||
rc = -E2BIG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (buf->length - offset < blob_len) {
|
||||
rc = -EFAULT;
|
||||
goto out;
|
||||
|
|
@ -377,6 +387,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
|
|||
u32 *blob_handle)
|
||||
{
|
||||
u8 *blob_ref __free(kfree) = NULL;
|
||||
struct trusted_key_tpm *private = options->private;
|
||||
struct tpm_buf *buf __free(kfree) = NULL;
|
||||
unsigned int private_len;
|
||||
unsigned int public_len;
|
||||
|
|
@ -397,7 +408,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
|
|||
}
|
||||
|
||||
/* new format carries keyhandle but old format doesn't */
|
||||
if (!options->keyhandle)
|
||||
if (!private->keyhandle)
|
||||
return -EINVAL;
|
||||
|
||||
/* must be big enough for at least the two be16 size counts */
|
||||
|
|
@ -441,11 +452,11 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
|
|||
tpm_buf_init(buf, TPM_BUFSIZE);
|
||||
tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
|
||||
|
||||
rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
|
||||
rc = tpm_buf_append_name(chip, buf, private->keyhandle, NULL);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
tpm_buf_append_hmac_session(chip, buf, 0, options->keyauth,
|
||||
tpm_buf_append_hmac_session(chip, buf, 0, private->keyauth,
|
||||
TPM_DIGEST_SIZE);
|
||||
|
||||
tpm_buf_append(buf, blob, blob_len);
|
||||
|
|
@ -485,6 +496,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
|
|||
struct trusted_key_options *options,
|
||||
u32 blob_handle)
|
||||
{
|
||||
struct trusted_key_tpm *private = options->private;
|
||||
struct tpm_header *head;
|
||||
struct tpm_buf *buf __free(kfree) = NULL;
|
||||
u16 data_len;
|
||||
|
|
@ -509,10 +521,10 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
|
|||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!options->policyhandle) {
|
||||
if (!private->policyhandle) {
|
||||
tpm_buf_append_hmac_session(chip, buf, TPM2_SA_ENCRYPT,
|
||||
options->blobauth,
|
||||
options->blobauth_len);
|
||||
private->blobauth,
|
||||
private->blobauth_len);
|
||||
} else {
|
||||
/*
|
||||
* FIXME: The policy session was generated outside the
|
||||
|
|
@ -525,9 +537,9 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
|
|||
* could repeat our actions with the exfiltrated
|
||||
* password.
|
||||
*/
|
||||
tpm2_buf_append_auth(buf, options->policyhandle,
|
||||
tpm2_buf_append_auth(buf, private->policyhandle,
|
||||
NULL /* nonce */, 0, 0,
|
||||
options->blobauth, options->blobauth_len);
|
||||
private->blobauth, private->blobauth_len);
|
||||
if (tpm2_chip_auth(chip)) {
|
||||
tpm_buf_append_hmac_session(chip, buf, TPM2_SA_ENCRYPT,
|
||||
NULL, 0);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user