Kernel trusted keys don't require userspace knowledge of the raw key material and instead export a sealed blob, which can be persisted to unencrypted storage. Userspace can then load this blob into the kernel, where it's unsealed and from there on usable for kernel crypto. This is incompatible with fscrypt, where userspace is supposed to supply the raw key material. For TPMs, a work around is to do key unsealing in userspace, but this may not be feasible for other trusted key backends. Make it possible to benefit from both fscrypt and trusted key sealing by extending fscrypt_add_key_arg::key_id to hold either the ID of a fscrypt-provisioning or a trusted key. A non fscrypt-provisioning key_id was so far prohibited, so additionally allowing trusted keys won't break backwards compatibility. Signed-off-by: Ahmad Fatoum <a.fatoum@xxxxxxxxxxxxxx> --- Tested with: https://github.com/google/fscryptctl/pull/23 v1 here: https://lore.kernel.org/linux-fscrypt/20210727144349.11215-1-a.fatoum@xxxxxxxxxxxxxx/T/#u v1 -> v2: - Drop encrypted key support and key_extract_material - Use key_id instead of repurposing raw (Eric) - Shift focus to trusted key sealing for non-TPM as a rationale why this integration is worthwhile (Eric) - Extend documentation with rationale on why one would use trusted keys and warn about trusted key reuse To: "Theodore Y. Ts'o" <tytso@xxxxxxx> To: Jaegeuk Kim <jaegeuk@xxxxxxxxxx> To: Eric Biggers <ebiggers@xxxxxxxxxx> Cc: Jarkko Sakkinen <jarkko@xxxxxxxxxx> Cc: James Morris <jmorris@xxxxxxxxx> Cc: "Serge E. Hallyn" <serge@xxxxxxxxxx> Cc: James Bottomley <jejb@xxxxxxxxxxxxx> Cc: Mimi Zohar <zohar@xxxxxxxxxxxxx> Cc: Sumit Garg <sumit.garg@xxxxxxxxxx> Cc: David Howells <dhowells@xxxxxxxxxx> Cc: linux-fscrypt@xxxxxxxxxxxxxxx Cc: linux-crypto@xxxxxxxxxxxxxxx Cc: linux-integrity@xxxxxxxxxxxxxxx Cc: linux-security-module@xxxxxxxxxxxxxxx Cc: keyrings@xxxxxxxxxxxxxxx Cc: linux-kernel@xxxxxxxxxxxxxxx --- Documentation/filesystems/fscrypt.rst | 31 ++++++++++++++----- fs/crypto/keyring.c | 43 +++++++++++++++++++-------- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst index 44b67ebd6e40..c1811fa4285a 100644 --- a/Documentation/filesystems/fscrypt.rst +++ b/Documentation/filesystems/fscrypt.rst @@ -734,23 +734,40 @@ as follows: - ``key_id`` is 0 if the raw key is given directly in the ``raw`` field. Otherwise ``key_id`` is the ID of a Linux keyring key of - type "fscrypt-provisioning" whose payload is + type "fscrypt-provisioning" or "trusted": + "fscrypt-provisioning" keys have a payload of struct fscrypt_provisioning_key_payload whose ``raw`` field contains the raw key and whose ``type`` field matches ``key_spec.type``. Since ``raw`` is variable-length, the total size of this key's payload must be ``sizeof(struct fscrypt_provisioning_key_payload)`` - plus the raw key size. The process must have Search permission on - this key. - - Most users should leave this 0 and specify the raw key directly. - The support for specifying a Linux keyring key is intended mainly to - allow re-adding keys after a filesystem is unmounted and re-mounted, + plus the raw key size. + For "trusted" keys, the payload is directly taken as the raw key. + + The process must have Search permission on this key. + + Most users leave this 0 and specify the raw key directly. + "trusted" keys are useful to leverage kernel support for sealing + and unsealing key material. Sealed keys can be persisted to + unencrypted storage and later be used to decrypt the file system + without requiring userspace to have knowledge of the raw key + material. + "fscrypt-provisioning" key support is intended mainly to allow + re-adding keys after a filesystem is unmounted and re-mounted, without having to store the raw keys in userspace memory. - ``raw`` is a variable-length field which must contain the actual key, ``raw_size`` bytes long. Alternatively, if ``key_id`` is nonzero, then this field is unused. +.. note:: + + Users should take care not to reuse the fscrypt key material with + different ciphers or in multiple contexts as this may make it + easier to deduce the key. + This also applies when the key material is supplied indirectly + via a kernel trusted key. In this case, the trusted key should + perferably be used only in a single context. + For v2 policy keys, the kernel keeps track of which user (identified by effective user ID) added the key, and only allows the key to be removed by that user --- or by "root", if they use diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index 0b3ffbb4faf4..721f5da51416 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -20,6 +20,7 @@ #include <crypto/skcipher.h> #include <linux/key-type.h> +#include <keys/trusted-type.h> #include <linux/random.h> #include <linux/seq_file.h> @@ -577,28 +578,44 @@ static int get_keyring_key(u32 key_id, u32 type, key_ref_t ref; struct key *key; const struct fscrypt_provisioning_key_payload *payload; - int err; + int err = 0; ref = lookup_user_key(key_id, 0, KEY_NEED_SEARCH); if (IS_ERR(ref)) return PTR_ERR(ref); key = key_ref_to_ptr(ref); - if (key->type != &key_type_fscrypt_provisioning) - goto bad_key; - payload = key->payload.data[0]; + if (key->type == &key_type_fscrypt_provisioning) { + payload = key->payload.data[0]; - /* Don't allow fscrypt v1 keys to be used as v2 keys and vice versa. */ - if (payload->type != type) - goto bad_key; + /* Don't allow fscrypt v1 keys to be used as v2 keys and vice versa. */ + if (payload->type != type) { + err = -EKEYREJECTED; + goto out_put; + } - secret->size = key->datalen - sizeof(*payload); - memcpy(secret->raw, payload->raw, secret->size); - err = 0; - goto out_put; + secret->size = key->datalen - sizeof(*payload); + memcpy(secret->raw, payload->raw, secret->size); + } else if (IS_REACHABLE(CONFIG_TRUSTED_KEYS) && key->type == &key_type_trusted) { + struct trusted_key_payload *tkp; + + /* avoid reseal changing payload while we memcpy key */ + down_read(&key->sem); + tkp = key->payload.data[0]; + if (!tkp || tkp->key_len < FSCRYPT_MIN_KEY_SIZE || + tkp->key_len > FSCRYPT_MAX_KEY_SIZE) { + up_read(&key->sem); + err = -EINVAL; + goto out_put; + } + + secret->size = tkp->key_len; + memcpy(secret->raw, tkp->key, secret->size); + up_read(&key->sem); + } else { + err = -EKEYREJECTED; + } -bad_key: - err = -EKEYREJECTED; out_put: key_ref_put(ref); return err; -- 2.30.2